#
# 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
from PluginUtils import ReportUtils, \
register_text_doc, register_draw_doc, register_book_doc
pt2cm = ReportUtils.pt2cm
import ImgManip
import FontScale
import Mime
#-------------------------------------------------------------------------
#
# 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' % 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 = Mime.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('')
self.cntnt.write('\n')
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],mode='rb')
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.program_name + ' ' + 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 = Mime.get_application(_apptype)
mtype = Mime.get_description(_apptype)
if Utils.search_for(mprog[0]):
print_label = _("Open in %(program_name)s") % { 'program_name':
mprog[1]}
else:
print_label = None
register_text_doc(mtype,ODFDoc,1,1,1,".odt",print_label)
register_book_doc(mtype,ODFDoc,1,1,1,".odt")
register_draw_doc(mtype,ODFDoc,1,1, ".odt",print_label);
except:
register_text_doc(_('Open Document Text'),ODFDoc,1,1,1,".odt", None)
register_book_doc(_("Open Document Text"),ODFDoc,1,1,1,".odt")
register_draw_doc(_("Open Document Text"),ODFDoc,1,1,".odt",None);