From 8e7c827c83635b6ee38d5b3b4ad47f2e2afa28d8 Mon Sep 17 00:00:00 2001 From: Serge Noiraud Date: Fri, 21 Aug 2009 20:54:14 +0000 Subject: [PATCH] ODFDoc : More pylint compliant. Duplicate styles and fonts suppression. Multi line now works correctly : tested with multi-lines attribut. svn: r13102 --- src/plugins/docgen/ODFDoc.py | 552 +++++++++++++++++++++++++++-------- 1 file changed, 423 insertions(+), 129 deletions(-) diff --git a/src/plugins/docgen/ODFDoc.py b/src/plugins/docgen/ODFDoc.py index 0b5e60385..5783394cb 100644 --- a/src/plugins/docgen/ODFDoc.py +++ b/src/plugins/docgen/ODFDoc.py @@ -2,7 +2,7 @@ # Gramps - a GTK+/GNOME based genealogy program # # Copyright (C) 2000-2006 Donald N. Allingham -# Copyright (C) 2005-2006 Serge Noiraud +# Copyright (C) 2005-2009 Serge Noiraud # Copyright (C) 2007-2009 Brian G. Matherly # # This program is free software; you can redistribute it and/or modify @@ -22,6 +22,38 @@ # $Id$ +""" +ODFDoc : used to generate Open Office Document +""" + +#------------------------------------------------------------------------- +# +# pylint : disable messages ... +# +#------------------------------------------------------------------------- +# disable-msg=C0302 # Too many lines in module +# pylint: disable-msg=C0302 +# disable-msg # Regular expression which should only match +# pylint: disable-msg=C0103 +# disable-msg=R0902 # Too many instance attributes +# pylint: disable-msg=R0902 +# disable-msg=R0904 # Too many public methods +# pylint: disable-msg=R0904 +# disable-msg=R0912 # Too many branches +# pylint: disable-msg=R0912 +# disable-msg=R0913 # Too many arguments +# pylint: disable-msg=R0913 +# disable-msg=R0914 # Too many local variables +# pylint: disable-msg=R0914 +# disable-msg=R0915 # Too many statements +# pylint: disable-msg=R0915 +# warnings : +# disable-msg=W0613 # Unused argument +# pylint: disable-msg=W0613 +# errors : +# disable-msg=E1101 # has no member +# pylint: disable-msg=E1101 + #------------------------------------------------------------------------- # # Standard Python Modules @@ -38,7 +70,6 @@ import locale from cStringIO import StringIO from math import pi, cos, sin from xml.sax.saxutils import escape -import operator #------------------------------------------------------------------------- # @@ -56,7 +87,6 @@ from libodfbackend import OdfBackend import const from ReportBase import ReportUtils import ImgManip -import Utils import Errors #------------------------------------------------------------------------- @@ -89,13 +119,29 @@ NewStyle = re.compile('style-name="([a-zA-Z0-9]*)__([#a-zA-Z0-9 ]*)__">') # #------------------------------------------------------------------------- class ODFDoc(BaseDoc, TextDoc, DrawDoc): + """ + The ODF document class + """ - def __init__(self, styles, type): - BaseDoc.__init__(self, styles, type) + def __init__(self, styles, ftype): + """ + Class init + """ + BaseDoc.__init__(self, styles, ftype) self.media_list = [] + self.init_called = False self.cntnt = None - self._backend = None + self.cntnt1 = None + self.cntnt2 = None + self.cntntx = None + self.sfile = None + self.mimetype = None + self.meta = None + self.mfile = None self.filename = None + self.lang = None + self._backend = None + self.span = 0 self.level = 0 self.time = "0000-00-00T00:00:00" self.new_page = 0 @@ -105,6 +151,9 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc): self.StyleList = [] # styles to create depending on styled notes. def open(self, filename): + """ + Open the new document + """ t = time.localtime(time.time()) self.time = "%04d-%02d-%02dT%02d:%02d:%02d" % t[:6] @@ -120,6 +169,9 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc): self.cntnt2 = StringIO() def init(self): + """ + Create the document header + """ assert (not self.init_called) self.init_called = True @@ -134,29 +186,44 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc): self.StyleList = [] # styles to create depending on styled notes. self.cntnt1.write('\n') self.cntnt1.write('\n') self.cntnt1.write('\n') self.cntnt1.write('\n') - self.cntnt1.write('\n') + self.cntnt1.write('\n') self.cntnt1.write('\n') self.cntnt2.write('\n') self.cntnt2.write('\n') - self.cntnt2.write('\n') - self.cntnt2.write('\n') + self.cntnt2.write('\n') self.cntnt2.write('\n') - self.cntnt2.write('') - self.cntnt2.write('') + self.cntnt2.write('') + self.cntnt2.write('') self.cntnt2.write('\n') - self.cntnt2.write('') + self.cntnt2.write('') self.cntnt2.write('') self.cntnt2.write('\n') @@ -188,7 +260,8 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc): self.cntnt.write('\n') self.cntnt.write('\n') - self.cntnt.write('\n') self.cntnt.write('\n') self.cntnt.write('\n') @@ -237,7 +315,7 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc): # Graphic style for items with a clear background self.cntnt.write('\n') - self.cntnt.write('\t\n' % style_name) - self.cntnt.write('\n') + self.cntnt.write('\n') self.cntnt.write('\n') self.cntnt.write('\n') @@ -325,7 +408,8 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc): if font.get_italic(): self.cntnt.write('fo:font-style="italic" ') self.cntnt.write('fo:font-size="%.2fpt" ' % font.get_size()) - self.cntnt.write('style:font-size-asian="%.2fpt"/> ' % font.get_size()) + self.cntnt.write('style:font-size-asian="%.2fpt"/> ' % + font.get_size()) self.cntnt.write('\n') for style_name in styles.get_table_style_names(): @@ -334,12 +418,14 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc): self.cntnt.write('style:family="table-properties">\n') table_width = float(self.get_usable_width()) table_width_str = "%.2f" % table_width - self.cntnt.write('\n') self.cntnt.write('\n') for col in range(0, style.get_columns()): self.cntnt.write('') width = table_width * float(style.get_column_width(col)/100.0) width_str = "%.4f" % width @@ -372,10 +458,12 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc): self.cntnt.write('/>\n') self.cntnt.write('\n') - self.cntnt.write('\n') + self.cntnt.write('\n') self.cntnt.write('\n') self.cntnt.write('\n') - self.cntnt.write('\n') + self.cntnt.write('\n') self.cntnt.write('\n') self.cntnt.write('\n') self.cntnt.write('\n') #Begin photo style - self.cntnt.write('') - self.cntnt.write('') self.cntnt.write('\n') - self.cntnt.write('') - self.cntnt.write('') self.cntnt.write('\n') - self.cntnt.write(' ') - self.cntnt.write(' ') self.cntnt.write('\n') self.cntnt.write('') - self.cntnt.write('') self.cntnt.write('\n') @@ -451,8 +552,35 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc): self.cntnt.write('form:automatic-focus="false" ') self.cntnt.write('form:apply-design-mode="false"/>\n') + def uniq(self, List, funct=None): + """ + We want no duplicate in the list + """ + # order preserving + if funct is None: + def funct(x): + """ + function used to compare elements + """ + return x + seen = {} + result = [] + for item in List: + marker = funct(item[0]) + if marker in seen: + continue + seen[marker] = 1 + result.append(item) + return result + def finish_cntnt_creation(self): + """ + We have finished the document. + So me must integrate the new fonts and styles where they should be. + The content.xml file is closed. + """ self.cntntx = StringIO() + self.StyleList = self.uniq(self.StyleList) self.add_styled_notes_fonts() self.add_styled_notes_styles() self.cntntx.write(self.cntnt1.getvalue()) @@ -463,6 +591,9 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc): self.cntnt.close() def close(self): + """ + Close the document and create the odt file + """ self.cntnt.write('\n') self.cntnt.write('\n') self.cntnt.write('\n') @@ -476,41 +607,62 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc): open_file_with_default_application(self.filename) def add_styled_notes_fonts(self): + """ + Add the new fonts for Styled notes in the font-face-decls section. + """ # Need to add new font for styled notes here. for style in self.StyleList: - if ( style[0] == "FontFace" ): - self.cntnt1.write('\n') def add_styled_notes_styles(self): + """ + Add the new styles for Styled notes in the automatic-styles section. + """ # Need to add new style for styled notes here. for style in self.StyleList: - if ( style[0] == "FontSize" ): - self.cntnt2.write('') - self.cntnt2.write(' ' % style[1]) + if ( style[1] == "FontSize" ): + self.cntnt2.write(' ') + self.cntnt2.write('' % + style[2]) self.cntnt2.write('\n') - elif ( style[0] == "FontColor" ): - self.cntnt2.write('') - self.cntnt2.write(' ' % style[1]) + self.cntnt2.write(' ' % + style[2]) self.cntnt2.write('\n') - elif ( style[0] == "FontHighlight" ): - self.cntnt2.write('') - self.cntnt2.write(' ' % style[1]) + elif ( style[1] == "FontHighlight" ): + self.cntnt2.write(' ') + self.cntnt2.write('' % style[2]) self.cntnt2.write('\n') - elif ( style[0] == "FontFace" ): - self.cntnt2.write('') - self.cntnt2.write(' ') + self.cntnt2.write('') self.cntnt2.write('\n') def add_media_object(self, file_name, pos, x_cm, y_cm, alt=''): + """ + Add multi-media documents : photos + """ # try to open the image. If the open fails, it probably wasn't # a valid image (could be a PDF, or a non-image) @@ -561,6 +713,9 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc): self.cntnt.write('\n') def start_table(self, name, style_name): + """ + open a table + """ self.cntnt.write('\n' % style_name) styles = self.get_style_sheet() @@ -570,17 +725,30 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc): self.cntnt.write(style_name + '.' + str(chr(ord('A')+col)) +'"/>\n') def end_table(self): + """ + close a table + """ self.cntnt.write('\n') def start_row(self): + """ + open a row + """ self.cntnt.write('\n') def end_row(self): + """ + close a row + """ self.cntnt.write('\n') def start_cell(self, style_name, span=1): + """ + open a cell + """ self.span = span - self.cntnt.write(' 1: self.cntnt.write(' table:number-columns-spanned="%s">\n' % span) @@ -589,24 +757,42 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc): self.new_cell = 1 def end_cell(self): + """ + close a cell + """ self.cntnt.write('\n') #for col in range(1, self.span): # self.cntnt.write('\n') self.new_cell = 0 def start_bold(self): + """ + open bold + """ self.cntnt.write('') def end_bold(self): + """ + close bold + """ self.cntnt.write('') def start_superscript(self): + """ + open superscript + """ self.cntnt.write('') def end_superscript(self): + """ + close superscript + """ self.cntnt.write('') def _add_zip(self, zfile, name, data, t): + """ + Add a zip file to an archive + """ zipinfo = zipfile.ZipInfo(name.encode('utf-8')) zipinfo.date_time = t zipinfo.compress_type = zipfile.ZIP_DEFLATED @@ -614,8 +800,11 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc): zfile.writestr(zipinfo, data) def _write_zip(self): + """ + Create the odt file. This is a zip file + """ try: - zfile = zipfile.ZipFile(self.filename, "w", zipfile.ZIP_DEFLATED) + zfile = zipfile.ZipFile(self.filename, "w", zipfile.ZIP_DEFLATED) except IOError, msg: errmsg = "%s\n%s" % (_("Could not create %s") % self.filename, msg) raise Errors.ReportError(errmsg) @@ -642,30 +831,48 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc): self._add_zip(zfile, "Pictures/%s" % image[1], ifile.read(), t) ifile.close() except: - print "Could not open %s" % image[0] + errmsg = "%s\n%s" % (_("Could not open %s") % image[0], + msg) + raise Errors.ReportError(errmsg) zfile.close() def _write_styles_file(self): + """ + create the styles.xml file + """ self.sfile = StringIO() self.sfile.write('\n') self.sfile.write('\n') self.sfile.write('\n') self.sfile.write('\n') self.sfile.write('\n') - self.sfile.write('\n') - self.sfile.write('\n') + self.sfile.write('\n') self.sfile.write(' ') self.sfile.write('\n') @@ -803,16 +1018,26 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc): # Current no leading number format for headers #self.sfile.write('\n') - #self.sfile.write('\n') - #self.sfile.write('\n') - #self.sfile.write('\n') - #self.sfile.write('\n') - #self.sfile.write('\n') - #self.sfile.write('\n') - #self.sfile.write('\n') - #self.sfile.write('\n') - #self.sfile.write('\n') - #self.sfile.write('\n') + #self.sfile.write('\n') + #self.sfile.write('\n') + #self.sfile.write('\n') + #self.sfile.write('\n') + #self.sfile.write('\n') + #self.sfile.write('\n') + #self.sfile.write('\n') + #self.sfile.write('\n') + #self.sfile.write('\n') + #self.sfile.write('\n') #self.sfile.write('\n') self.sfile.write(' ') self.sfile.write('\n') self.sfile.write('\n') - self.sfile.write('') self.sfile.write('') self.sfile.write('\n') - self.sfile.write('') self.sfile.write('') self.sfile.write('\n') self.sfile.write('\n') - self.sfile.write('\n') self.sfile.write('') #self.sfile.write('') - #self.sfile.write(' TITRE : %s' % self.title) # How to get the document title here ? + # How to get the document title here ? + #self.sfile.write(' TITRE : %s' % self.title) #self.sfile.write('') #self.sfile.write('') # footer @@ -900,15 +1135,27 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc): self.sfile.write('\n') def page_break(self): + """ + prepare a new page + """ self.new_page = 1 def start_page(self): + """ + create a new page + """ self.cntnt.write('\n') def end_page(self): + """ + close the page + """ self.cntnt.write('\n') def start_paragraph(self, style_name, leader=None): + """ + open a new paragraph + """ style_sheet = self.get_style_sheet() style = style_sheet.get_paragraph_style(style_name) self.level = style.get_header_level() @@ -929,6 +1176,9 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc): self.new_cell = 0 def end_paragraph(self): + """ + close a paragraph + """ if self.level == 0: self.cntnt.write('\n') else: @@ -936,6 +1186,9 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc): self.new_cell = 1 def write_note(self, text, format, style_name): + """ + write a note + """ if format == 1: text = escape(text, _esc_map) # Replace multiple spaces: have to go from the largest number down @@ -965,7 +1218,7 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc): s_tags = styledtext.get_tags() markuptext = self._backend.add_markup_from_styled(text, s_tags) # we need to know if we have new styles to add. - # if markuptext contains : FontColor, FontFace, FontSize or FontHighlight + # if markuptext contains : FontColor, FontFace, FontSize ... # we must prepare the new styles for the styles.xml file. # We are looking for the following format : # style-name="([a-zA-Z0-9]*)__([a-zA-Z0-9 ])"> @@ -975,10 +1228,17 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc): m = NewStyle.search(markuptext, start) if not m: break - self.StyleList.append([m.group(1), m.group(2)]) + self.StyleList.append([m.group(1)+m.group(2), + m.group(1), + m.group(2)]) start = m.end() self.cntnt.write('') - self.cntnt.write(markuptext) + linenb = 1 + for line in markuptext.split('\n'): + if ( linenb > 1 ): + self.cntnt.write('') + self.cntnt.write(line) + linenb += 1 self.cntnt.write('') def write_text(self, text, mark=None): @@ -1000,11 +1260,15 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc): self.cntnt.write(escape(text, _esc_map)) def _write_manifest(self): + """ + create the manifest.xml file + """ self.mfile = StringIO() self.mfile.write('\n') self.mfile.write('') + self.mfile.write('xmlns:manifest="urn:oasis:names:tc:opendocument') + self.mfile.write(':xmlns:manifest:1.0">') self.mfile.write('') @@ -1024,19 +1288,27 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc): self.mfile.write('\n') def _write_mimetype_file(self): + """ + create the mimetype.xml file + """ self.mimetype = StringIO() self.mimetype.write('application/vnd.oasis.opendocument.text') def _write_meta_file(self): + """ + create the meta.xml file + """ self.meta = StringIO() self.meta.write('\n') self.meta.write('\n'); + self.meta.write('xmlns:meta="urn:oasis:names:tc:opendocument') + self.meta.write(':xmlns:meta:1.0" ') + self.meta.write('office:version="1.0">\n') self.meta.write('\n') self.meta.write('') self.meta.write(const.PROGRAM_NAME + ' ' + const.VERSION) @@ -1069,7 +1341,9 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc): self.meta.write('%s\n' % self.lang) self.meta.write('1\n') self.meta.write('PT0S\n') - self.meta.write('http://gramps-project.org') + self.meta.write('') + self.meta.write('http://gramps-project.org') self.meta.write('\n') self.meta.write('\n') self.meta.write('\n') @@ -1078,6 +1352,9 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc): self.meta.write('\n') def rotate_text(self, style, text, x, y, angle): + """ + Used to rotate a text with an angle. + """ style_sheet = self.get_style_sheet() stype = style_sheet.get_draw_style(style) pname = stype.get_paragraph_style() @@ -1112,6 +1389,9 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc): self.cntnt.write('\n') def draw_path(self, style, path): + """ + Draw a path + """ minx = 9e12 miny = 9e12 maxx = 0 @@ -1123,13 +1403,15 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc): maxx = max(point[0], maxx) maxy = max(point[1], maxy) - self.cntnt.write('\n') def draw_line(self, style, x1, y1, x2, y2): + """ + Draw a line + """ self.cntnt.write('\n') def draw_text(self, style, text, x, y): + """ + Draw a text + """ style_sheet = self.get_style_sheet() box_style = style_sheet.get_draw_style(style) para_name = box_style.get_paragraph_style() @@ -1182,6 +1470,9 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc): self.cntnt.write('\n') def draw_box(self, style, text, x, y, w, h): + """ + Draw a box + """ style_sheet = self.get_style_sheet() box_style = style_sheet.get_draw_style(style) para_name = box_style.get_paragraph_style() @@ -1215,6 +1506,9 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc): self.cntnt.write('\n') def center_text(self, style, text, x, y): + """ + Center a text in a cell, a row, a line, ... + """ style_sheet = self.get_style_sheet() box_style = style_sheet.get_draw_style(style) para_name = box_style.get_paragraph_style()