diff --git a/gramps2/ChangeLog b/gramps2/ChangeLog index b5db9be58..da7a761c4 100644 --- a/gramps2/ChangeLog +++ b/gramps2/ChangeLog @@ -1,3 +1,8 @@ +2006-01-25 Serge Noiraud + * src/docgen/ODFDoc.py: Add new document generator. + * src/docgen/Makefile.am: Ship new file. + * src/po/fr.po, src/po/template.po: Add new format's name. + 2006-01-25 Kees Bakker * src/po/nl.po: Translation update. diff --git a/gramps2/src/docgen/Makefile.am b/gramps2/src/docgen/Makefile.am index 0a64aaa15..afa5890cc 100644 --- a/gramps2/src/docgen/Makefile.am +++ b/gramps2/src/docgen/Makefile.am @@ -10,6 +10,7 @@ docgen_PYTHON = \ HtmlDoc.py\ KwordDoc.py\ LaTeXDoc.py\ + ODFDoc.py\ OpenOfficeDoc.py\ OpenSpreadSheet.py\ PdfDoc.py\ diff --git a/gramps2/src/docgen/ODFDoc.py b/gramps2/src/docgen/ODFDoc.py new file mode 100644 index 000000000..5b3611164 --- /dev/null +++ b/gramps2/src/docgen/ODFDoc.py @@ -0,0 +1,1149 @@ +# +# Gramps - a GTK+/GNOME based genealogy program +# +# Copyright (C) 2000-2006 Donald N. Allingham +# Copyright (C) 2005-2006 Serge Noiraud +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +# $Id$ + +#------------------------------------------------------------------------- +# +# Standard Python Modules +# +#------------------------------------------------------------------------- +import os +import zipfile +import time +import locale +from cStringIO import StringIO +from math import pi, cos, sin, fabs + +#------------------------------------------------------------------------- +# +# Gramps modules +# +#------------------------------------------------------------------------- +import Errors +import BaseDoc +import const +import PluginMgr +import ImgManip +import FontScale +import GrampsMime +from ReportUtils import pt2cm + +#------------------------------------------------------------------------- +# +# internationalization +# +#------------------------------------------------------------------------- +from gettext import gettext as _ +from xml.sax.saxutils import escape + +_apptype = 'application/vnd.oasis.opendocument.text' + +_esc_map = { + '\x1a' : '', + '\x0c' : '', + '\n' : '', + '<super>' : '', + '</super>' : '', + } + +#------------------------------------------------------------------------- +# +# ODFDoc +# +#------------------------------------------------------------------------- +class ODFDoc(BaseDoc.BaseDoc): + + def __init__(self,styles,type,template,orientation=BaseDoc.PAPER_PORTRAIT): + BaseDoc.BaseDoc.__init__(self,styles,type,template,orientation) + self.cntnt = None + self.filename = None + self.level = 0 + self.time = "0000-00-00T00:00:00" + self.new_page = 0 + self.new_cell = 0 + self.page = 0 + self.first_page = 1 + + def set_mode(self, mode): + self.mode = mode + + def open(self,filename): + t = time.localtime(time.time()) + self.time = "%04d-%02d-%02dT%02d:%02d:%02d" % \ + (t[0],t[1],t[2],t[3],t[4],t[5]) + + if filename[-4:] != ".odt": + self.filename = filename + ".odt" + else: + self.filename = filename + + self.filename = os.path.normpath(os.path.abspath(self.filename)) + self.cntnt = StringIO() + + def init(self): + + assert(self.init_called==False) + self.init_called = True + + current_locale = locale.getlocale() + self.lang = current_locale[0] + if self.lang: + self.lang = self.lang.replace('_','-') + else: + self.lang = "en-US" + + 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') + self.cntnt.write('\n') + self.cntnt.write('') + self.cntnt.write('') + self.cntnt.write('\n') + self.cntnt.write('') + self.cntnt.write('') + self.cntnt.write('\n') + + for style_name in self.draw_styles.keys(): + style = self.draw_styles[style_name] + self.cntnt.write('\n') + self.cntnt.write('\n') + self.cntnt.write('\n') + + self.cntnt.write('\n') + self.cntnt.write('\n') + self.cntnt.write('\n') + + for style_name in self.style_list.keys(): + style = self.style_list[style_name] + + self.cntnt.write('\n') + self.cntnt.write('\n') + + self.cntnt.write('\n') + self.cntnt.write(' 0: + self.cntnt.write('fo:keep-with-next="true" ') + + align = style.get_alignment() + if align == BaseDoc.PARA_ALIGN_LEFT: + self.cntnt.write('fo:text-align="start" ') + elif align == BaseDoc.PARA_ALIGN_RIGHT: + self.cntnt.write('fo:text-align="end" ') + elif align == BaseDoc.PARA_ALIGN_CENTER: + self.cntnt.write('fo:text-align="center" ') + self.cntnt.write('style:justify-single-word="false" ') + else: + self.cntnt.write('fo:text-align="justify" ') + self.cntnt.write('style:justify-single-word="false" ') + font = style.get_font() + if font.get_type_face() == BaseDoc.FONT_SANS_SERIF: + self.cntnt.write('style:font-name="Arial" ') + else: + self.cntnt.write('style:font-name="Times New Roman" ') + self.cntnt.write('fo:font-size="%.2fpt" ' % font.get_size()) + self.cntnt.write('style:font-size-asian="%.2fpt" ' % font.get_size()) + color = font.get_color() + self.cntnt.write('fo:color="#%02x%02x%02x" ' % color) + if font.get_bold(): + self.cntnt.write('fo:font-weight="bold" ') + if font.get_italic(): + self.cntnt.write('fo:font-style="italic" ') + if font.get_underline(): + self.cntnt.write('style:text-underline="single" ') + self.cntnt.write('style:text-underline-color="font-color" ') + self.cntnt.write('fo:text-indent="%.2fcm"\n' % style.get_first_indent()) + self.cntnt.write('fo:margin-right="%.2fcm"\n' % style.get_right_margin()) + self.cntnt.write('fo:margin-left="%.2fcm"\n' % style.get_left_margin()) + self.cntnt.write('fo:margin-top="0.00cm"\n') + self.cntnt.write('fo:margin-bottom="0.212cm" ') + self.cntnt.write('/>\n') + self.cntnt.write('\n') + + self.cntnt.write('\n') + self.cntnt.write(' ' % font.get_size()) + self.cntnt.write('\n') + + for style_name in self.table_styles.keys(): + style = self.table_styles[style_name] + self.cntnt.write('\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 + self.cntnt.write('' % width_str) + self.cntnt.write('\n') + + for cell in self.cell_styles.keys(): + cell_style = self.cell_styles[cell] + 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') + + #end of Photo style edits + + self.cntnt.write('\n') + self.cntnt.write('\n') + self.cntnt.write(' \n') + self.cntnt.write(' \n') + + def close(self): + self.cntnt.write('\n') + self.cntnt.write('\n') + self.cntnt.write('\n') + self._write_styles_file() + self._write_manifest() + self._write_meta_file() + self._write_mimetype_file() + self._write_zip() + if self.print_req: + app = GrampsMime.get_application(_apptype) + os.environ["FILE"] = self.filename + os.system ('%s "$FILE" &' % app[0]) + + def add_media_object(self,name,pos,x_cm,y_cm): + + # try to open the image. If the open fails, it probably wasn't + # a valid image (could be a PDF, or a non-image) + try: + image = ImgManip.ImgManip(name) + (x,y) = image.size() + ratio = float(x_cm)*float(y)/(float(y_cm)*float(x)) + except: + return + + if ratio < 1: + act_width = x_cm + act_height = y_cm*ratio + else: + act_height = y_cm + act_width = x_cm/ratio + + media_list_item = (name,act_width,act_height) + if not media_list_item in self.media_list: + self.media_list.append(media_list_item) + + base = os.path.basename(name) + tag = base.replace('.','_') + + if self.new_cell: + self.cntnt.write('') + if pos == "left": + self.cntnt.write('\n') + if self.new_cell: + self.cntnt.write('\n') + + def start_table(self,name,style_name): + self.cntnt.write('\n' % style_name) + table = self.table_styles[style_name] + for col in range(0,table.get_columns()): + self.cntnt.write('\n') + + def end_table(self): + self.cntnt.write('\n') + + def start_row(self): + self.cntnt.write('\n') + + def end_row(self): + self.cntnt.write('\n') + + def start_cell(self,style_name,span=1): + self.span = span + self.cntnt.write(' 1: + self.cntnt.write(' table:number-columns-spanned="%s">\n' % span) + else: + self.cntnt.write('>\n') + self.new_cell = 1 + + def end_cell(self): + self.cntnt.write('\n') + #for col in range(1,self.span): + # self.cntnt.write('\n') + self.new_cell = 0 + + def start_bold(self): + self.cntnt.write('') + + def end_bold(self): + self.cntnt.write('') + + def _add_zip(self,zfile,name,data,t): + zipinfo = zipfile.ZipInfo(name.encode('latin-1')) + zipinfo.date_time = t + zipinfo.compress_type = zipfile.ZIP_DEFLATED + zfile.writestr(zipinfo,data) + + def _write_zip(self): + zfile = zipfile.ZipFile(self.filename,"w",zipfile.ZIP_DEFLATED) + + t = time.localtime(time.time())[:6] + + self._add_zip(zfile,"META-INF/manifest.xml",self.mfile.getvalue(),t) + self._add_zip(zfile,"content.xml",self.cntnt.getvalue(),t) + self._add_zip(zfile,"meta.xml",self.meta.getvalue(),t) + self._add_zip(zfile,"styles.xml",self.sfile.getvalue(),t) + self._add_zip(zfile,"mimetype",self.mimetype.getvalue(),t) + + self.mfile.close() + self.cntnt.close() + self.meta.close() + self.sfile.close() + self.mimetype.close() + + for image in self.media_list: + try: + ifile = open(image[0]) + base = os.path.basename(image[0]) + self._add_zip(zfile,"Pictures/%s" % base, ifile.read(),t) + ifile.close() + except: + print "Could not open %s" % image[0] + zfile.close() + + def _write_styles_file(self): + 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('') + self.sfile.write(' ') + self.sfile.write(' ') + self.sfile.write('') + 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(' ') + 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('\n') + self.sfile.write('\n') + + for key in self.style_list.keys(): + style = self.style_list[key] + self.sfile.write('\n') + self.sfile.write(' 0: + self.sfile.write('fo:keep-with-next="always" ') + + align = style.get_alignment() + if align == BaseDoc.PARA_ALIGN_LEFT: + self.sfile.write('fo:text-align="start" ') + self.sfile.write('style:justify-single-word="false" ') + elif align == BaseDoc.PARA_ALIGN_RIGHT: + self.sfile.write('fo:text-align="end" ') + elif align == BaseDoc.PARA_ALIGN_CENTER: + self.sfile.write('fo:text-align="center" ') + self.sfile.write('style:justify-single-word="false" ') + else: + self.sfile.write('fo:text-align="justify" ') + self.sfile.write('style:justify-single-word="false" ') + self.sfile.write('fo:text-indent="%.2fcm" ' % style.get_first_indent()) + self.sfile.write('style:auto-text-indent="false"/> ') + self.sfile.write('\n') + self.sfile.write('\n') + + # 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(' ') + self.sfile.write(' ') + self.sfile.write(' ') + 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') + + def page_break(self): + self.new_page = 1 + + def start_page(self): + self.cntnt.write('\n') + + def end_page(self): + self.cntnt.write('\n') + + def start_paragraph(self,style_name,leader=None): + style = self.style_list[style_name] + self.level = style.get_header_level() + if self.new_page == 1: + self.new_page = 0 + name = "NL%s" % style_name + else: + name = style_name + if self.level == 0: + self.cntnt.write('' % name) + else: + self.cntnt.write('\n') + if leader != None: + self.cntnt.write(leader) + self.cntnt.write('\n') + self.new_cell = 0 + + def end_paragraph(self): + if self.level == 0: + self.cntnt.write('\n') + else: + self.cntnt.write('\n') + self.new_cell = 1 + + def write_note(self,text,format,style_name): + if format == 1: + text = text.replace('&','&') # Must be first + text = text.replace('<','<') + text = text.replace('>','>') + # Replace multiple spaces: have to go from the largest number down + for n in range(text.count(' '),1,-1): + text = text.replace(' '*n, ' ' % (n-1) ) + text = text.replace('\n','') + text = text.replace('\t','') + text = text.replace('<super>', + '') + text = text.replace('</super>','') + + self.start_paragraph(style_name) + self.cntnt.write('') + self.cntnt.write(text) + self.cntnt.write('') + self.end_paragraph() + elif format == 0: + for line in text.split('\n\n'): + self.start_paragraph(style_name) + line = line.replace('\n',' ') + line = ' '.join(line.split()) + self.write_text(line) + self.end_paragraph() + + def write_text(self,text): + """ + Uses the xml.sax.saxutils.escape function to convert XML + entities. The _esc_map dictionary allows us to add our own + mappings. + """ + self.cntnt.write(escape(text,_esc_map)) + + def _write_manifest(self): + self.mfile = StringIO() + + self.mfile.write('\n') + self.mfile.write('') + self.mfile.write('') + for image in self.media_list: + i = image[0] + base = os.path.basename(i) + self.mfile.write('') + self.mfile.write('') + self.mfile.write('') + self.mfile.write('') + self.mfile.write('') + self.mfile.write('\n') + + def _write_mimetype_file(self): + self.mimetype = StringIO() + self.mimetype.write('application/vnd.oasis.opendocument.text') + + def _write_meta_file(self): + self.meta = StringIO() + + self.meta.write('\n') + self.meta.write('\n'); + self.meta.write('\n') + self.meta.write('') + self.meta.write(const.progName + ' ' + const.version) + self.meta.write('\n') + self.meta.write('') + # It should be reasonable to have a true document title. but how ? + # self.title ? + #self.meta.write(_("Summary of %s") % self.name) + self.meta.write('\n') + self.meta.write('') + #self.meta.write(_("Summary of %s") % name) + self.meta.write('\n') + self.meta.write('') + self.meta.write('\n') + self.meta.write('') + self.meta.write(self.name) + self.meta.write('\n') + self.meta.write('') + self.meta.write(self.time) + self.meta.write('\n') + self.meta.write('') + self.meta.write(self.name) + self.meta.write('\n') + self.meta.write('') + self.meta.write(self.time) + self.meta.write('\n') + self.meta.write('') + self.meta.write('\n') + self.meta.write('0-00-00T00:00:00\n') + 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('\n') + self.meta.write('\n') + self.meta.write('\n') + self.meta.write('\n') + self.meta.write('\n') + self.meta.write('\n') + + def rotate_text(self,style,text,x,y,angle): + + stype = self.draw_styles[style] + pname = stype.get_paragraph_style() + p = self.style_list[pname] + font = p.get_font() + size = font.get_size() + + height = size*(len(text)) + oneline = (size/72.0)*2.54 + width = 0 + for line in text: + width = max(width,FontScale.string_width(font,line)) + wcm = (width/72.0)*2.54*1.5 # *1.2 + hcm = (height/72.0)*2.54*1.5 # *1.2 + + rangle = -((pi/180.0) * angle) + + self.cntnt.write(' ') + self.cntnt.write('') + self.cntnt.write(' ' % pname) + + self.cntnt.write('\n' % pname) + self.write_text('\n'.join(text)) + self.cntnt.write('\n\n\n') + self.cntnt.write('\n') + + def draw_path(self,style,path): + minx = 9e12 + miny = 9e12 + maxx = 0 + maxy = 0 + + for point in path: + minx = min(point[0],minx) + miny = min(point[1],miny) + maxx = max(point[0],maxx) + maxy = max(point[1],maxy) + + self.cntnt.write('\n') + + def draw_line(self,style,x1,y1,x2,y2): + self.cntnt.write('' % y2) + self.cntnt.write('\n') + self.cntnt.write('\n') + + def draw_text(self,style,text,x,y): + box_style = self.draw_styles[style] + para_name = box_style.get_paragraph_style() + + pstyle = self.style_list[para_name] + font = pstyle.get_font() + if box_style.get_width(): + sw = box_style.get_width()*2.0 # *1.2 + else: + sw = pt2cm(FontScale.string_width(font,text))*1.3 + + self.cntnt.write('' % float(y)) + self.cntnt.write(' ' ) + self.cntnt.write('' % para_name) + self.cntnt.write('' % font.get_size() ) + self.cntnt.write(text) + self.cntnt.write('') + self.cntnt.write('\n') + self.cntnt.write('\n') + + def draw_bar(self,style,x,y,x2,y2): + box_style = self.draw_styles[style] + + self.cntnt.write('' % float(y)) + self.cntnt.write('\n') + + def draw_box(self,style,text,x,y): + box_style = self.draw_styles[style] + para_name = box_style.get_paragraph_style() + shadow_width = box_style.get_shadow_space() + + if box_style.get_shadow(): + self.cntnt.write('\n' % (float(y)+shadow_width)) + self.cntnt.write('\n') + + self.cntnt.write('\n' % float(y)) + if text != "": + self.cntnt.write('' % para_name) + self.cntnt.write('' % para_name) + text = text.replace('\t','') + text = text.replace('\n','') + self.cntnt.write(text) + self.cntnt.write('') + self.cntnt.write('\n') + self.cntnt.write('\n') + + def center_text(self,style,text,x,y): + box_style = self.draw_styles[style] + para_name = box_style.get_paragraph_style() + pstyle = self.style_list[para_name] + font = pstyle.get_font() + + size = 1.5*(FontScale.string_width(font,text)/72.0) * 2.54 + + self.cntnt.write('\n' % float(y)) + + if text != "": + self.cntnt.write('') + self.cntnt.write('' % para_name) + self.cntnt.write('' % para_name) + self.cntnt.write(text) + self.cntnt.write('\n') + self.cntnt.write('\n') + self.cntnt.write('') + self.cntnt.write('\n') + + def write_at(self,style,text,x,y): + pstyle = self.style_list[style] + font = pstyle.get_font() + + size = 2*(FontScale.string_width(font,text)/72.0) * 2.54 + + self.cntnt.write('\n' % float(y)) + + if text != "": + self.cntnt.write('') + self.cntnt.write('' % style) + self.cntnt.write('' % style) + self.cntnt.write(text) + self.cntnt.write('\n') + self.cntnt.write('\n') + self.cntnt.write('') + self.cntnt.write('\n') + +#-------------------------------------------------------------------------- +# +# Register plugins +# +#-------------------------------------------------------------------------- +print_label = None +try: + import Utils + + mprog = GrampsMime.get_application(_apptype) + mtype = GrampsMime.get_description(_apptype) + + if Utils.search_for(mprog[0]): + print_label = _("Open in Open Document Editor") + else: + print_label = None + + PluginMgr.register_text_doc(mtype,ODFDoc,1,1,1,".odt",print_label) + PluginMgr.register_book_doc(mtype,ODFDoc,1,1,1,".odt") + PluginMgr.register_draw_doc(mtype,ODFDoc,1,1, ".odt",print_label); +except: + PluginMgr.register_text_doc(_('Open Document Text'), OpenDocumentDoc,1,1,1,".odt", None) + PluginMgr.register_book_doc(_("Open Document Text"), OpenDocumentDoc,1,1,1,".odt") + PluginMgr.register_draw_doc(_("Open Document Text"), OpenDocumentDoc,1,1,".odt",None); diff --git a/gramps2/src/po/fr.po b/gramps2/src/po/fr.po index 757419819..ff1d76fd9 100644 --- a/gramps2/src/po/fr.po +++ b/gramps2/src/po/fr.po @@ -5269,6 +5269,15 @@ msgstr "Ouvrir avec OpenOffice.org" msgid "OpenOffice.org Writer" msgstr "Traitement de Texte OpenOffice" +#: docgen/OpenDocumentDoc.py:1139 +msgid "Open in Open Document Editor" +msgstr "Ouvrir avec éditeur Open Document" + +#: docgen/OpenDocumentDoc.py:1147 docgen/OpenDocumentDoc.py:1148 +#: docgen/OpenDocumentDoc.py:1149 +msgid "Open Document Text" +msgstr "Texte Open Document" + #: docgen/PSDrawDoc.py:423 msgid "PostScript" msgstr "PostScript" diff --git a/gramps2/src/po/template.po b/gramps2/src/po/template.po index ccc13aec5..1040f96c9 100644 --- a/gramps2/src/po/template.po +++ b/gramps2/src/po/template.po @@ -5206,6 +5206,15 @@ msgstr "" msgid "OpenOffice.org Writer" msgstr "" +#: docgen/OpenDocumentDoc.py:1139 +msgid "Open in Open Document Editor" +msgstr "" + +#: docgen/OpenDocumentDoc.py:1147 docgen/OpenDocumentDoc.py:1148 +#: docgen/OpenDocumentDoc.py:1149 +msgid "Open Document Text" +msgstr "" + #: docgen/PSDrawDoc.py:423 msgid "PostScript" msgstr ""