Further pylint improvements to document generators

This commit is contained in:
Nick Hall 2016-06-04 18:29:10 +01:00
parent 36f5a6addc
commit a3fd7c06be
5 changed files with 839 additions and 897 deletions

View File

@ -27,27 +27,8 @@
# Python modules
#
#------------------------------------------------------------------------
from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
from math import radians
#------------------------------------------------------------------------
#
# Gramps modules
#
#------------------------------------------------------------------------
from gramps.gen.plug.docgen import PAPER_PORTRAIT
import gramps.plugins.lib.libcairodoc as libcairodoc
from gramps.gen.errors import UnavailableError
#import constfunc
#------------------------------------------------------------------------
#
# Set up logging
#
#------------------------------------------------------------------------
import logging
log = logging.getLogger(".GtkPrint")
#-------------------------------------------------------------------------
#
@ -61,6 +42,23 @@ try: # the Gramps-Connect server has no DISPLAY
except:
pass
#------------------------------------------------------------------------
#
# Gramps modules
#
#------------------------------------------------------------------------
from gramps.gen.plug.docgen import PAPER_PORTRAIT
import gramps.plugins.lib.libcairodoc as libcairodoc
from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
#------------------------------------------------------------------------
#
# Set up logging
#
#------------------------------------------------------------------------
LOG = logging.getLogger(".GtkPrint")
#------------------------------------------------------------------------
#
# Constants
@ -128,7 +126,7 @@ def paperstyle_to_pagesetup(paper_style):
# are handled as custom format, because we are not intelligent enough.
if gramps_paper_name in gramps_to_gtk:
paper_size = Gtk.PaperSize.new(name=gramps_to_gtk[gramps_paper_name])
log.debug("Selected paper size: %s" % gramps_to_gtk[gramps_paper_name])
LOG.debug("Selected paper size: %s", gramps_to_gtk[gramps_paper_name])
else:
if paper_style.get_orientation() == PAPER_PORTRAIT:
paper_width = gramps_paper_size.get_width() * 10
@ -139,7 +137,7 @@ def paperstyle_to_pagesetup(paper_style):
paper_size = Gtk.PaperSize.new_custom("custom", "Custom Size",
paper_width, paper_height,
Gtk.Unit.MM)
log.debug("Selected paper size: (%f,%f)" % (paper_width, paper_height))
LOG.debug("Selected paper size: (%f,%f)", paper_width, paper_height)
page_setup = Gtk.PageSetup()
page_setup.set_paper_size(paper_size)
@ -234,7 +232,7 @@ class PrintPreview:
##"""
##if page_no >= len(self._page_numbers):
##log.debug("Page number %d doesn't exist." % page_no)
##LOG.debug("Page number %d doesn't exist." % page_no)
##page_no = 0
##if page_no not in self._page_surfaces:

View File

@ -32,28 +32,34 @@
#------------------------------------------------------------------------
#
# python modules
# Python modules
#
#------------------------------------------------------------------------
from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
from bisect import bisect
import re, os, sys
import re
import os
import logging
_LOG = logging.getLogger(".latexdoc")
#----------------------------------------------------------------------- -
#
# gramps modules
#
#------------------------------------------------------------------------
from gramps.gen.plug.docgen import BaseDoc, TextDoc, PAPER_LANDSCAPE, FONT_SANS_SERIF, URL_PATTERN
from gramps.gen.plug.docbackend import DocBackend
HAVE_PIL = False
try:
from PIL import Image
HAVE_PIL = True
except:
except ImportError:
HAVE_PIL = False
#----------------------------------------------------------------------- -
#
# Gramps modules
#
#------------------------------------------------------------------------
from gramps.gen.plug.docgen import (BaseDoc, TextDoc, PAPER_LANDSCAPE,
FONT_SANS_SERIF, URL_PATTERN)
from gramps.gen.plug.docbackend import DocBackend
from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
_LOG = logging.getLogger(".latexdoc")
if not HAVE_PIL:
_LOG.warning(_("PIL (Python Imaging Library) not loaded. "
"Production of jpg images from non-jpg images "
"in LaTeX documents will not be available. "
@ -435,8 +441,8 @@ def map_font_size(fontsize):
TBLFMT_PAT = re.compile(r'({\|?)l(\|?})')
# constants for routing in table construction:
(CELL_BEG, CELL_TEXT, CELL_END,
ROW_BEG, ROW_END, TAB_BEG, TAB_END) = list(range(7))
(CELL_BEG, CELL_TEXT, CELL_END, ROW_BEG, ROW_END, TAB_BEG,
TAB_END) = list(range(7))
FIRST_ROW, SUBSEQ_ROW = list(range(2))
@ -489,22 +495,22 @@ def str_incr(str_counter):
#
#------------------------------------------------------------------------
class Tab_Cell:
class TabCell:
def __init__(self, colchar, span, head, content):
self.colchar = colchar
self.span = span
self.head = head
self.content = content
class Tab_Row:
class TabRow:
def __init__(self):
self.cells =[]
self.cells = []
self.tail = ''
self.addit = '' # for: \\hline, \\cline{}
class Tab_Mem:
class TabMem:
def __init__(self, head):
self.head = head
self.tail =''
self.rows =[]
self.tail = ''
self.rows = []
#------------------------------------------------------------------------
#
@ -513,25 +519,7 @@ class Tab_Mem:
#------------------------------------------------------------------------
def latexescape(text):
"""
change text in text that latex shows correctly
special characters: \& \$ \% \# \_ \{ \}
"""
text = text.replace('&','\\&')
text = text.replace('$','\\$')
text = text.replace('%','\\%')
text = text.replace('#','\\#')
text = text.replace('_','\\_')
text = text.replace('{','\\{')
text = text.replace('}','\\}')
# replace character unknown to LaTeX
text = text.replace('','$\\longrightarrow$')
return text
def latexescapeverbatim(text):
"""
change text in text that latex shows correctly respecting whitespace
special characters: \& \$ \% \# \_ \{ \}
Now also make sure space and newline is respected
Escape the following special characters: & $ % # _ { }
"""
text = text.replace('&', '\\&')
text = text.replace('$', '\\$')
@ -540,14 +528,22 @@ def latexescapeverbatim(text):
text = text.replace('_', '\\_')
text = text.replace('{', '\\{')
text = text.replace('}', '\\}')
# replace character unknown to LaTeX
text = text.replace('', '$\\longrightarrow$')
return text
def latexescapeverbatim(text):
"""
Escape special characters and also make sure that LaTeX respects whitespace
and newlines correctly.
"""
text = latexescape(text)
text = text.replace(' ', '\\ ')
text = text.replace('\n', '~\\newline \n')
#spaces at begin are normally ignored, make sure they are not.
#due to above a space at begin is now \newline\n\
text = text.replace('\\newline\n\\ ',
'\\newline\n\\hspace*{0.1\\grbaseindent}\\ ')
# replace character unknown to LaTeX
text = text.replace('','$\\longrightarrow$')
return text
#------------------------------------------------------------------------
@ -568,7 +564,7 @@ class LateXBackend(DocBackend):
DocBackend.UNDERLINE,
DocBackend.FONTSIZE,
DocBackend.FONTFACE,
DocBackend.SUPERSCRIPT ]
DocBackend.SUPERSCRIPT]
STYLETAG_MARKUP = {
DocBackend.BOLD : ("\\textbf{", "}"),
@ -634,13 +630,13 @@ class TexFont:
if style:
self.font_beg = style.font_beg
self.font_end = style.font_end
self.leftIndent = style.left_indent
self.firstLineIndent = style.firstLineIndent
self.left_indent = style.left_indent
self.first_line_indent = style.first_line_indent
else:
self.font_beg = ""
self.font_end = ""
self.leftIndent = ""
self.firstLineIndent = ""
self.left_indent = ""
self.first_line_indent = ""
#------------------------------------------------------------------
@ -691,9 +687,9 @@ class LaTeXDoc(BaseDoc, TextDoc):
if span > 1: # phantom columns prior to multicolumns
for col in range(self.curcol - span, self.curcol - 1):
col_char = get_charform(col)
phantom = Tab_Cell(col_char, 0, '', '')
phantom = TabCell(col_char, 0, '', '')
self.tabrow.cells.append(phantom)
self.tabcell = Tab_Cell(self.curcol_char, span, text, '')
self.tabcell = TabCell(self.curcol_char, span, text, '')
elif tab_state == CELL_TEXT:
self.textmem.append(text)
elif tab_state == CELL_END: # text == ''
@ -707,7 +703,7 @@ class LaTeXDoc(BaseDoc, TextDoc):
self.tabrow.cells.append(self.tabcell)
self.textmem = []
elif tab_state == ROW_BEG:
self.tabrow = Tab_Row()
self.tabrow = TabRow()
elif tab_state == ROW_END:
self.tabrow.addit = text # text: \\hline, \\cline{}
self.tabrow.tail = ''.join(self.textmem) # \\\\ row-termination
@ -718,7 +714,7 @@ class LaTeXDoc(BaseDoc, TextDoc):
elif tab_state == TAB_BEG: # text: \\begin{longtable}[l]{
self._backend.write(''.join(('\\grinittab{\\textwidth}{',
repr(1.0/self.numcols), '}%\n')))
self.tabmem = Tab_Mem(text)
self.tabmem = TabMem(text)
elif tab_state == TAB_END: # text: \\end{longtable}
self.tabmem.tail = text
@ -740,8 +736,8 @@ class LaTeXDoc(BaseDoc, TextDoc):
# extract cell.contents
bare_contents = [cell.content.strip(SEPARATION_PAT).replace(
'\n', '').split(SEPARATION_PAT)
for cell in self.tabrow.cells]
'\n', '').split(SEPARATION_PAT) for cell in self.tabrow.cells]
# mk equal length & transpose
num_new_rows = max([len(mult_row_cont)
for mult_row_cont in bare_contents])
@ -759,15 +755,18 @@ class LaTeXDoc(BaseDoc, TextDoc):
self.pict = transp_cont[0][-1]
last_cell -= 1
self.numcols -= 1
self._backend.write(''.join(('\\addtolength{\\grtabwidth}{-',
repr(self.pict_width), '\\grbaseindent -2\\tabcolsep}%\n')))
self._backend.write(''.join(
('\\addtolength{\\grtabwidth}{-',
repr(self.pict_width),
'\\grbaseindent -2\\tabcolsep}%\n')))
self.pict_in_table = False
# new row-col structure
for row in range(num_new_rows):
new_row = Tab_Row()
new_row = TabRow()
for i in range(first_cell, last_cell):
new_cell = Tab_Cell(get_charform(i + first_cell),
new_cell = TabCell(
get_charform(i + first_cell),
self.tabrow.cells[i].span, self.tabrow.cells[i].head,
transp_cont[row][i + first_cell])
new_row.cells.append(new_cell)
@ -796,43 +795,50 @@ class LaTeXDoc(BaseDoc, TextDoc):
if cell.span == 0:
continue
if cell.content.startswith('\\grmkpicture'):
self._backend.write(''.join(('\\setlength{\\grpictsize}{',
self._backend.write(
''.join(('\\setlength{\\grpictsize}{',
self.pict_width, '\\grbaseindent}%\n')))
else:
for part in cell.content.split(SEPARATION_PAT):
self._backend.write(''.join(('\\grtextneedwidth{',
part, '}%\n')))
self._backend.write(
''.join(('\\grtextneedwidth{', part, '}%\n')))
row.cells[col_num].content = cell.content.replace(
SEPARATION_PAT, '~\\newline \n')
if cell.span == 1:
self._backend.write(''.join(('\\grsetreqfull%\n')))
elif cell.span > 1:
self._backend.write(''.join(('\\grsetreqpart{\\grcolbeg',
get_charform(get_numform(cell.colchar) - cell.span +1),
self._backend.write(
''.join(('\\grsetreqpart{\\grcolbeg',
get_charform(get_numform(cell.colchar) -
cell.span +1),
'}%\n')))
self._backend.write(''.join(('\\grcolsfirstfix',
self._backend.write(
''.join(('\\grcolsfirstfix',
' {\\grcolbeg', col_char, '}{\\grtempwidth', col_char,
'}{\\grfinalwidth', col_char, '}{\\grpictreq', col_char,
'}{\\grtextreq', col_char, '}%\n')))
'}{\\grfinalwidth', col_char, '}{\\grpictreq',
col_char, '}{\\grtextreq', col_char, '}%\n')))
self._backend.write(''.join(('\\grdividelength%\n')))
for col_char in tabcol_chars:
self._backend.write(''.join(('\\grcolssecondfix',
self._backend.write(
''.join(('\\grcolssecondfix',
' {\\grcolbeg', col_char, '}{\\grtempwidth', col_char,
'}{\\grfinalwidth', col_char, '}{\\grpictreq', col_char,
'}%\n')))
'}{\\grfinalwidth', col_char, '}{\\grpictreq',
col_char, '}%\n')))
self._backend.write(''.join(('\\grdividelength%\n')))
for col_char in tabcol_chars:
self._backend.write(''.join(('\\grcolsthirdfix',
self._backend.write(
''.join(('\\grcolsthirdfix',
' {\\grcolbeg', col_char, '}{\\grtempwidth', col_char,
'}{\\grfinalwidth', col_char, '}%\n')))
self._backend.write(''.join(('\\grdividelength%\n')))
for col_char in tabcol_chars:
self._backend.write(''.join(('\\grcolsfourthfix',
self._backend.write(
''.join(('\\grcolsfourthfix',
' {\\grcolbeg', col_char, '}{\\grtempwidth', col_char,
'}{\\grfinalwidth', col_char, '}%\n')))
@ -841,10 +847,11 @@ class LaTeXDoc(BaseDoc, TextDoc):
for cell in row.cells:
if cell.span > 1:
multcol_alph_id = next(self.multcol_alph_counter)
self._backend.write(''.join(('\\grgetspanwidth{',
self._backend.write(
''.join(('\\grgetspanwidth{',
'\\grspanwidth', multcol_alph_id,
'}{\\grcolbeg', get_charform(get_numform(cell.colchar)-
cell.span + 1),
'}{\\grcolbeg', get_charform(
get_numform(cell.colchar)- cell.span + 1),
'}{\\grcolbeg', cell.colchar,
'}{\\grtempwidth', cell.colchar,
'}%\n')))
@ -852,7 +859,8 @@ class LaTeXDoc(BaseDoc, TextDoc):
def write_table(self):
# Choosing RaggedRight (with hyphenation) in table and
# provide manually adjusting of column widths
self._backend.write(''.join((
self._backend.write(
''.join((
'%\n', self.pict,
'%\n%\n',
'% ==> Comment out one of the two lines ',
@ -862,13 +870,15 @@ class LaTeXDoc(BaseDoc, TextDoc):
'% ==> You may add pos or neg values ',
'to the following ', repr(self.numcols), ' column widths %\n')))
for col_num in range(self.numcols):
self._backend.write(''.join(('\\addtolength{\\grtempwidth',
self._backend.write(
''.join(('\\addtolength{\\grtempwidth',
get_charform(col_num), '}{+0.0cm}%\n')))
self._backend.write('% === %\n')
# adjust & open table':
if self.pict:
self._backend.write(''.join(('%\n\\vspace{\\grtabprepos}%\n',
self._backend.write(
''.join(('%\n\\vspace{\\grtabprepos}%\n',
'\\setlength{\\grtabprepos}{0ex}%\n')))
self.pict = ''
self._backend.write(''.join(self.tabmem.head))
@ -887,7 +897,7 @@ class LaTeXDoc(BaseDoc, TextDoc):
self._backend.write('\\endfoot%\n')
if self.head_line:
self._backend.write('\\hline%\n')
self.head_line= False
self.head_line = False
else:
self._backend.write('%\n')
self._backend.write(complete_row)
@ -902,7 +912,7 @@ class LaTeXDoc(BaseDoc, TextDoc):
self._backend.write(''.join((''.join(self.tabmem.tail), '}%\n\n')))
def mk_splitting_row(self, row):
splitting =[]
splitting = []
add_vdots = '\\grempty'
for cell in row.cells:
if cell.span == 0:
@ -915,12 +925,13 @@ class LaTeXDoc(BaseDoc, TextDoc):
else:
cell_width = ''.join(('\\grspanwidth',
next(self.multcol_alph_counter)))
splitting.append(''.join(('\\grtabpgbreak{', cell.head, '}{',
splitting.append(
''.join(('\\grtabpgbreak{', cell.head, '}{',
cell_width, '}{', add_vdots, '}{+2ex}%\n')))
return ''.join((' & '.join(splitting), '%\n', row.tail))
def mk_complete_row(self, row):
complete =[]
complete = []
for cell in row.cells:
if cell.span == 0:
continue
@ -929,8 +940,9 @@ class LaTeXDoc(BaseDoc, TextDoc):
else:
cell_width = ''.join(('\\grspanwidth',
next(self.multcol_alph_counter)))
complete.append(''.join(('\\grcolpart{%\n ', cell.head,
'}{%\n', cell_width, '}{%\n ', cell.content, '%\n}%\n')))
complete.append(
''.join(('\\grcolpart{%\n ', cell.head, '}{%\n', cell_width,
'}{%\n ', cell.content, '%\n}%\n')))
return ''.join((' & '.join(complete), '%\n', row.tail, row.addit))
# ---------------------------------------------------------------------
@ -1016,8 +1028,8 @@ class LaTeXDoc(BaseDoc, TextDoc):
left = style.get_left_margin()
first = style.get_first_indent() + left
thisstyle.leftIndent = left
thisstyle.firstLineIndent = first
thisstyle.left_indent = left
thisstyle.first_line_indent = first
self.latexstyle[style_name] = thisstyle
@ -1045,10 +1057,10 @@ class LaTeXDoc(BaseDoc, TextDoc):
self.fbeg = ltxstyle.font_beg
self.fend = ltxstyle.font_end
self.indent = ltxstyle.leftIndent
self.FLindent = ltxstyle.firstLineIndent
self.indent = ltxstyle.left_indent
self.first_line_indent = ltxstyle.first_line_indent
if self.indent == 0:
self.indent = self.FLindent
self.indent = self.first_line_indent
# For additional vertical space beneath title line(s)
# i.e. when the first centering ended:
@ -1059,8 +1071,8 @@ class LaTeXDoc(BaseDoc, TextDoc):
self.in_multrow_cell = True
else:
if leader:
self._backend.write(''.join(('\\grprepleader{', leader,
'}%\n')))
self._backend.write(
''.join(('\\grprepleader{', leader, '}%\n')))
else:
self._backend.write('\\grprepnoleader%\n')
@ -1070,8 +1082,9 @@ class LaTeXDoc(BaseDoc, TextDoc):
# there another value might be choosen.
# -------------------------------------------------------------------
if self.indent is not None:
self._backend.write(''.join(('\\grminpghead{',
repr(self.indent), '}{', repr(self.pict_width), '}%\n')))
self._backend.write(
''.join(('\\grminpghead{', repr(self.indent), '}{',
repr(self.pict_width), '}%\n')))
self.fix_indent = True
if leader is not None and not self.in_list:
@ -1116,7 +1129,7 @@ class LaTeXDoc(BaseDoc, TextDoc):
def end_superscript(self):
self.emit('}')
def start_table(self, name,style_name):
def start_table(self, name, style_name):
"""Begin new table"""
self.in_table = True
self.currow = 0
@ -1171,10 +1184,10 @@ class LaTeXDoc(BaseDoc, TextDoc):
# values imported here are used for test '==1' and '!=0'. To get
# local boolean values the tests are now transfered to the import lines
# ------------------------------------------------------------------
self.lborder = 1 == self.cstyle.get_left_border()
self.rborder = 1 == self.cstyle.get_right_border()
self.bborder = 1 == self.cstyle.get_bottom_border()
self.tborder = 0 != self.cstyle.get_top_border()
self.lborder = self.cstyle.get_left_border() == 1
self.rborder = self.cstyle.get_right_border() == 1
self.bborder = self.cstyle.get_bottom_border() == 1
self.tborder = self.cstyle.get_top_border() != 0
# self.llist not needed any longer.
# now column widths are arranged in self.calc_latex_widths()
@ -1207,21 +1220,20 @@ class LaTeXDoc(BaseDoc, TextDoc):
self.emit('', CELL_END)
def add_media(self, infile, pos, x, y, alt='',
style_name=None, crop=None):
def add_media(self, infile, pos, x, y, alt='', style_name=None, crop=None):
"""Add photo to report"""
outfile = os.path.splitext(infile)[0]
pictname = latexescape(os.path.split(outfile)[1])
outfile = ''.join((outfile, '.jpg'))
outfile2 = ''.join((outfile, '.jpeg'))
outfile3 = ''.join((outfile, '.png'))
if HAVE_PIL and infile not in [outfile, outfile2, outfile3] :
if HAVE_PIL and infile not in [outfile, outfile2, outfile3]:
try:
curr_img = Image.open(infile)
curr_img.save(outfile)
px, py = curr_img.size
if py > px:
y = y*py/px
width, height = curr_img.size
if height > width:
y = y*height/width
except IOError:
self.emit(''.join(('%\n *** Error: cannot convert ', infile,
'\n *** to ', outfile,
@ -1239,13 +1251,13 @@ class LaTeXDoc(BaseDoc, TextDoc):
self.pict_width = x
self.pict_height = y
def write_text(self,text,mark=None,links=False):
def write_text(self, text, mark=None, links=False):
"""Write the text to the file"""
if text == '\n':
text = ''
text = latexescape(text)
if links == True:
if links is True:
text = re.sub(URL_PATTERN, _CLICKABLE, text)
#hard coded replace of the underline used for missing names/data
@ -1277,7 +1289,7 @@ class LaTeXDoc(BaseDoc, TextDoc):
markuptext = self._backend.add_markup_from_styled(text, s_tags)
if links == True:
if links is True:
markuptext = re.sub(URL_PATTERN, _CLICKABLE, markuptext)
markuptext = self._backend.add_markup_from_styled(text, s_tags)

View File

@ -28,34 +28,6 @@
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
@ -66,19 +38,20 @@ from hashlib import md5
import zipfile
import time
from io import StringIO
from math import pi, cos, sin, degrees, radians
from math import cos, sin, radians
from xml.sax.saxutils import escape
import re
#-------------------------------------------------------------------------
#
# Gramps modules
#
#-------------------------------------------------------------------------
from gramps.gen.plug.docgen import (BaseDoc, TextDoc, DrawDoc, graphicstyle,
FONT_SANS_SERIF, SOLID, PAPER_PORTRAIT,
INDEX_TYPE_TOC, PARA_ALIGN_CENTER, PARA_ALIGN_LEFT,
INDEX_TYPE_ALP, PARA_ALIGN_RIGHT, URL_PATTERN,
LOCAL_HYPERLINK, LOCAL_TARGET)
from gramps.gen.plug.docgen import (
BaseDoc, TextDoc, DrawDoc, graphicstyle, FONT_SANS_SERIF, SOLID,
PAPER_PORTRAIT, INDEX_TYPE_TOC, PARA_ALIGN_CENTER, PARA_ALIGN_LEFT,
INDEX_TYPE_ALP, PARA_ALIGN_RIGHT, URL_PATTERN, LOCAL_HYPERLINK,
LOCAL_TARGET)
from gramps.gen.plug.docgen.fontscale import string_width
from gramps.plugins.lib.libodfbackend import OdfBackend
from gramps.gen.const import PROGRAM_NAME, URL_HOMEPAGE
@ -96,9 +69,9 @@ from gramps.gen.errors import ReportError
from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
_apptype = 'application/vnd.oasis.opendocument.text'
APP_TYPE = 'application/vnd.oasis.opendocument.text'
_esc_map = {
ESC_MAP = {
'\x1a' : '',
'\x0c' : '',
'\n' : '<text:line-break/>',
@ -110,9 +83,8 @@ _esc_map = {
# regexp for Styled Notes ...
#
#-------------------------------------------------------------------------
import re
# Hyphen is added because it is used to replace spaces in the font name
NewStyle = re.compile('style-name="([a-zA-Z0-9]*)__([#a-zA-Z0-9 -]*)__">')
NEW_STYLE = re.compile('style-name="([a-zA-Z0-9]*)__([#a-zA-Z0-9 -]*)__">')
#-------------------------------------------------------------------------
#
@ -457,15 +429,15 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
self.new_cell = 0
self.page = 0
self.first_page = 1
self.StyleList_notes = [] # styles to create depending on styled notes.
self.StyleList_photos = [] # styles to create depending on clipped images.
self.stylelist_notes = [] # styles to create for styled notes.
self.stylelist_photos = [] # styles to create for clipped images.
def open(self, filename):
"""
Open the new document
"""
t = time.localtime(time.time())
self.time = "%04d-%02d-%02dT%02d:%02d:%02d" % t[:6]
now = time.localtime(time.time())
self.time = "%04d-%02d-%02dT%02d:%02d:%02d" % now[:6]
self.filename = filename
if not filename.endswith("odt"):
@ -482,7 +454,7 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
Create the document header
"""
assert (not self.init_called)
assert not self.init_called
self.init_called = True
wrt = self.cntnt.write
wrt1, wrt2 = self.cntnt1.write, self.cntnt2.write
@ -490,8 +462,8 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
self.lang = glocale.lang
self.lang = self.lang.replace('_', '-') if self.lang else "en-US"
self.StyleList_notes = [] # styles to create depending on styled notes.
wrt1('<?xml version="1.0" encoding="UTF-8"?>\n'
self.stylelist_notes = [] # styles to create depending on styled notes.
wrt1('<?xml version="1.0" encoding="UTF-8"?>\n' +
'<office:document-content\n' +
_XMLNS +
'office:version="1.0">\n' +
@ -500,8 +472,7 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
wrt1('<office:font-face-decls>\n' +
_FONTS
)
wrt2(
'</office:font-face-decls>\n' +
wrt2('</office:font-face-decls>\n' +
'<office:automatic-styles>\n' +
_AUTOMATIC_STYLES
)
@ -510,16 +481,14 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
for style_name in styles.get_draw_style_names():
style = styles.get_draw_style(style_name)
wrt(
'<style:style ' +
wrt('<style:style ' +
'style:name="%s" ' % style_name +
'style:family="graphic">\n'
'<style:graphic-properties '
)
if style.get_line_width():
wrt(
'svg:stroke-width="%.2f" '
wrt('svg:stroke-width="%.2f" '
% (style.get_line_width()*10) +
'draw:marker-start="" '
'draw:marker-start-width="0.0" '
@ -529,17 +498,16 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
)
if style.get_line_style() != SOLID:
#wrt('svg:fill-color="#ff0000" ')
wrt('draw:stroke="dash" draw:stroke-dash="gramps_%s" ' % style.get_dash_style_name())
wrt('draw:stroke="dash" draw:stroke-dash="gramps_%s" ' %
style.get_dash_style_name())
else:
wrt('draw:stroke="solid" ')
else:
wrt(
'draw:stroke="none" '
wrt('draw:stroke="none" '
'draw:stroke-color="#000000" '
)
wrt(
'svg:fill-color="#%02x%02x%02x" '
wrt('svg:fill-color="#%02x%02x%02x" '
% style.get_color() +
'draw:fill-color="#%02x%02x%02x" '
% style.get_fill_color() +
@ -612,24 +580,20 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
elif align == PARA_ALIGN_RIGHT:
wrt('fo:text-align="end" ')
elif align == PARA_ALIGN_CENTER:
wrt(
'fo:text-align="center" '
wrt('fo:text-align="center" '
'style:justify-single-word="false" '
)
else:
wrt(
'fo:text-align="justify" '
wrt('fo:text-align="justify" '
'style:justify-single-word="false" '
)
font = style.get_font()
wrt('style:font-name="%s" ' %
("Arial"
if font.get_type_face() == FONT_SANS_SERIF else
"Times New Roman")
("Arial" if font.get_type_face() == FONT_SANS_SERIF
else "Times New Roman")
)
wrt(
'fo:font-size="%.2fpt" ' % font.get_size() +
wrt('fo:font-size="%.2fpt" ' % font.get_size() +
'style:font-size-asian="%.2fpt" ' % font.get_size() +
'fo:color="#%02x%02x%02x" ' % font.get_color()
)
@ -639,13 +603,11 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
if font.get_italic():
wrt('fo:font-style="italic" ')
if font.get_underline():
wrt(
'style:text-underline="single" '
wrt('style:text-underline="single" '
'style:text-underline-color="font-color" '
)
wrt(
'fo:text-indent="%.2fcm"\n' % style.get_first_indent() +
wrt('fo:text-indent="%.2fcm"\n' % style.get_first_indent() +
'fo:margin-right="%.2fcm"\n' % style.get_right_margin() +
'fo:margin-left="%.2fcm"\n' % style.get_left_margin() +
'fo:margin-top="%.2fcm"\n' % style.get_top_margin() +
@ -654,8 +616,7 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
'</style:style>\n'
)
wrt(
'<style:style style:name="F%s" ' % style_name +
wrt('<style:style style:name="F%s" ' % style_name +
'style:family="text">\n' +
'<style:text-properties '
)
@ -666,16 +627,14 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
elif align == PARA_ALIGN_RIGHT:
wrt('fo:text-align="end" ')
elif align == PARA_ALIGN_CENTER:
wrt(
'fo:text-align="center" '
wrt('fo:text-align="center" '
'style:justify-single-word="false" '
)
font = style.get_font()
wrt('style:font-name="%s" ' %
("Arial"
if font.get_type_face() == FONT_SANS_SERIF else
"Times New Roman")
("Arial" if font.get_type_face() == FONT_SANS_SERIF
else "Times New Roman")
)
color = font.get_color()
@ -685,8 +644,7 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
if font.get_italic():
wrt('fo:font-style="italic" ')
wrt(
'fo:font-size="%.2fpt" ' % font.get_size() +
wrt('fo:font-size="%.2fpt" ' % font.get_size() +
'style:font-size-asian="%.2fpt"/> ' % font.get_size() +
'</style:style>\n'
)
@ -695,8 +653,7 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
style = styles.get_table_style(style_name)
table_width = float(self.get_usable_width())
table_width_str = "%.2f" % table_width
wrt(
'<style:style style:name="%s" ' % style_name +
wrt('<style:style style:name="%s" ' % style_name +
'style:family="table-properties">\n'
'<style:table-properties-properties ' +
'style:width="%scm" ' % table_width_str +
@ -707,8 +664,7 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
for col in range(0, min(style.get_columns(), 50)):
width = table_width * float(style.get_column_width(col) / 100.0)
width_str = "%.4f" % width
wrt(
'<style:style style:name="%s.%s" '
wrt('<style:style style:name="%s.%s" '
% (style_name, chr(ord('A')+col)) +
'style:family="table-column">' +
'<style:table-column-properties ' +
@ -718,38 +674,32 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
for cell in styles.get_cell_style_names():
cell_style = styles.get_cell_style(cell)
wrt(
'<style:style style:name="%s" ' % cell +
wrt('<style:style style:name="%s" ' % cell +
'style:family="table-cell">\n' +
'<style:table-cell-properties' +
' fo:padding="%.2fcm"' % cell_style.get_padding()
)
wrt(' fo:border-top="%s"' %
("0.002cm solid #000000"
if cell_style.get_top_border() else
"none")
("0.002cm solid #000000" if cell_style.get_top_border()
else "none")
)
wrt(' fo:border-bottom="%s"' %
("0.002cm solid #000000"
if cell_style.get_bottom_border() else
"none")
("0.002cm solid #000000" if cell_style.get_bottom_border()
else "none")
)
wrt(' fo:border-left="%s"' %
("0.002cm solid #000000"
if cell_style.get_left_border() else
"none")
("0.002cm solid #000000" if cell_style.get_left_border()
else "none")
)
wrt(' fo:border-right="%s"' %
("0.002cm solid #000000"
if cell_style.get_right_border() else
"none")
("0.002cm solid #000000" if cell_style.get_right_border()
else "none")
)
wrt(
'/>\n'
wrt('/>\n'
'</style:style>\n'
)
@ -771,7 +721,7 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
We want no duplicate in the list
"""
# order preserving
funct = funct or (lambda x:x)
funct = funct or (lambda x: x)
seen = set()
result = []
for item in list_:
@ -789,7 +739,7 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
The content.xml file is closed.
"""
self.cntntx = StringIO()
self.StyleList_notes = self.uniq(self.StyleList_notes)
self.stylelist_notes = self.uniq(self.stylelist_notes)
self.add_styled_notes_fonts()
self.add_styled_notes_styles()
self.add_styled_photo_styles()
@ -823,12 +773,11 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
"""
# Need to add new font for styled notes here.
wrt1 = self.cntnt1.write
for style in self.StyleList_notes:
for style in self.stylelist_notes:
if style[1] == "FontFace":
# Restore any spaces that were replaced by hyphens in
# libodfbackend
wrt1(
'<style:font-face ' +
wrt1('<style:font-face ' +
' style:name="%s"\n' % style[2].replace("-", " ") +
' svg:font-family="&apos;%s&apos;"\n' %
style[2].replace("-", " ") +
@ -841,10 +790,9 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
"""
# Need to add new style for styled notes here.
wrt2 = self.cntnt2.write
for style in self.StyleList_notes:
for style in self.stylelist_notes:
if style[1] == "FontSize":
wrt2(
'<style:style ' +
wrt2('<style:style ' +
'style:name="FontSize__%s__"\n' % style[2] +
' style:family="text">\n' +
' <style:text-properties\n' +
@ -857,8 +805,7 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
elif style[1] == "FontColor":
# Restore the hash at the start that was removed by
# libodfbackend
wrt2(
'<style:style ' +
wrt2('<style:style ' +
'style:name="FontColor__%s__"\n' % style[2] +
' style:family="text">\n' +
' <style:text-properties\n' +
@ -867,8 +814,7 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
)
elif style[1] == "FontHighlight":
wrt2(
'<style:style ' +
wrt2('<style:style ' +
'style:name="FontHighlight__%s__"\n' % style[2] +
' style:family="text">\n' +
' <style:text-properties\n' +
@ -879,8 +825,7 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
elif style[1] == "FontFace":
# Restore any spaces that were replaced by hyphens in
# libodfbackend
wrt2(
'<style:style ' +
wrt2('<style:style ' +
'style:name="FontFace__%s__"\n' % style[2] +
' style:family="text">\n' +
' <style:text-properties\n' +
@ -895,10 +840,9 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
Add the new styles for clipped images in the automatic-styles section.
"""
wrt2 = self.cntnt2.write
for style in self.StyleList_photos:
for style in self.stylelist_photos:
if style[0] == "Left":
wrt2(
'<style:style ' +
wrt2('<style:style ' +
'style:name="Left_%s" ' % str(style[1]) +
'style:family="graphic" ' +
'style:parent-style-name="photo">' +
@ -925,8 +869,7 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
'</style:style>\n'
)
elif style[0] == "Right":
wrt2(
'<style:style ' +
wrt2('<style:style ' +
'style:name="Right_%s" ' % str(style[1]) +
'style:family="graphic" ' +
'style:parent-style-name="photo">' +
@ -953,8 +896,7 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
'</style:style>\n'
)
elif style[0] == "Single":
wrt2(
'<style:style ' +
wrt2('<style:style ' +
'style:name="Single_%s" ' % str(style[1]) +
'style:family="graphic" ' +
'style:parent-style-name="photo">' +
@ -974,8 +916,7 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
'</style:style>\n'
)
else:
wrt2(
'<style:style ' +
wrt2('<style:style ' +
'style:name="Row_%s" ' % str(style[1]) +
'style:family="graphic" ' +
'style:parent-style-name="Graphics">' +
@ -998,7 +939,8 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
'</style:style>\n'
)
def add_media(self, file_name, pos, x_cm, y_cm, alt='', style_name=None, crop=None):
def add_media(self, file_name, pos, x_cm, y_cm, alt='', style_name=None,
crop=None):
"""
Add multi-media documents : photos
"""
@ -1015,7 +957,7 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
odf_name = md5(file_name_hash).hexdigest() + extension
media_list_item = (file_name, odf_name)
if not media_list_item in self.media_list:
if media_list_item not in self.media_list:
self.media_list.append(media_list_item)
base = escape(os.path.basename(file_name))
@ -1027,11 +969,11 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
pos = pos.title() if pos in ['left', 'right', 'single'] else 'Row'
if crop:
(start_x, start_y, end_x, end_y
) = crop_percentage_to_subpixel(x, y, crop)
(start_x, start_y,
end_x, end_y) = crop_percentage_to_subpixel(x, y, crop)
# Need to keep the ratio intact, otherwise scaled images look stretched
# if the dimensions aren't close in size
# Need to keep the ratio intact, otherwise scaled images look
# stretched if the dimensions aren't close in size
(act_width, act_height) = image_actual_size(
x_cm, y_cm, int(end_x-start_x), int(end_y-start_y)
)
@ -1045,7 +987,7 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
bottom = (y - end_y)/dpi[1]
crop = (top, right, bottom, left)
self.StyleList_photos.append(
self.stylelist_photos.append(
[pos, crop]
)
@ -1174,12 +1116,12 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
"""
self.cntnt.write('</text:span>')
def _add_zip(self, zfile, name, data, t):
def _add_zip(self, zfile, name, data, date_time):
"""
Add a zip file to an archive
"""
zipinfo = zipfile.ZipInfo(name)
zipinfo.date_time = t
zipinfo.date_time = date_time
zipinfo.compress_type = zipfile.ZIP_DEFLATED
zipinfo.external_attr = 0o644 << 16
zfile.writestr(zipinfo, data)
@ -1196,14 +1138,15 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
except:
raise ReportError(_("Could not create %s") % self.filename)
t = time.localtime(time.time())[:6]
now = time.localtime(time.time())[:6]
self._add_zip(zfile, "META-INF/manifest.xml", self.mfile.getvalue(), t)
self._add_zip(zfile, "content.xml", self.cntntx.getvalue(), t)
self._add_zip(zfile, "meta.xml", self.meta.getvalue(), t)
self._add_zip(zfile, "settings.xml", self.stfile.getvalue(), t)
self._add_zip(zfile, "styles.xml", self.sfile.getvalue(), t)
self._add_zip(zfile, "mimetype", self.mimetype.getvalue(), t)
self._add_zip(zfile, "META-INF/manifest.xml", self.mfile.getvalue(),
now)
self._add_zip(zfile, "content.xml", self.cntntx.getvalue(), now)
self._add_zip(zfile, "meta.xml", self.meta.getvalue(), now)
self._add_zip(zfile, "settings.xml", self.stfile.getvalue(), now)
self._add_zip(zfile, "styles.xml", self.sfile.getvalue(), now)
self._add_zip(zfile, "mimetype", self.mimetype.getvalue(), now)
self.mfile.close()
self.cntnt.close()
@ -1215,7 +1158,8 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
for image in self.media_list:
try:
with open(image[0], mode='rb') as ifile:
self._add_zip(zfile, "Pictures/%s" % image[1], ifile.read(), t)
self._add_zip(zfile, "Pictures/%s" % image[1],
ifile.read(), now)
except OSError as msg:
errmsg = "%s\n%s" % (_("Could not open %s") % image[0],
msg)
@ -1248,8 +1192,7 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
for style_name in styles.get_paragraph_style_names():
style = styles.get_paragraph_style(style_name)
wrtf(
'<style:style style:name="%s" ' % style_name +
wrtf('<style:style style:name="%s" ' % style_name +
'style:family="paragraph" ' +
'style:parent-style-name="Standard" ' +
'style:class="text">\n' +
@ -1279,18 +1222,15 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
elif align == PARA_ALIGN_RIGHT:
wrtf('fo:text-align="end" ')
elif align == PARA_ALIGN_CENTER:
wrtf(
'fo:text-align="center" '
wrtf('fo:text-align="center" '
'style:justify-single-word="false" '
)
else:
wrtf(
'fo:text-align="justify" '
wrtf('fo:text-align="justify" '
'style:justify-single-word="false" '
)
wrtf(
'fo:text-indent="%.2fcm" ' % style.get_first_indent() +
wrtf('fo:text-indent="%.2fcm" ' % style.get_first_indent() +
'style:auto-text-indent="false"/> ' +
'<style:text-properties '
)
@ -1300,9 +1240,8 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
wrtf('fo:color="#%02x%02x%02x" ' % color)
wrtf('style:font-name="%s" ' %
("Arial"
if font.get_type_face() == FONT_SANS_SERIF else
"Times New Roman")
("Arial" if font.get_type_face() == FONT_SANS_SERIF
else "Times New Roman")
)
wrtf('fo:font-size="%.0fpt" ' % font.get_size())
@ -1312,8 +1251,7 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
wrtf('fo:font-weight="bold" ')
if font.get_underline():
wrtf(
'style:text-underline="single" ' +
wrtf('style:text-underline="single" ' +
'style:text-underline-color="font-color" ' +
'fo:text-indent="%.2fcm" ' % style.get_first_indent() +
'fo:margin-right="%.2fcm" ' % style.get_right_margin() +
@ -1321,8 +1259,7 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
'fo:margin-top="%.2fcm" ' % style.get_top_margin() +
'fo:margin-bottom="%.2fcm"\n' % style.get_bottom_margin()
)
wrtf(
'/>\n'
wrtf('/>\n'
'</style:style>\n'
)
@ -1332,7 +1269,9 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
wrtf('<draw:stroke-dash draw:name="gramps_%s" draw:style="rect" '
'draw:dots1="%d" draw:dots1-length="0.102cm" '
'draw:dots2="%d" draw:dots2-length="0.102cm" '
'draw:distance="%5.3fcm" />\n' % (line_style, dash_array[0], dash_array[0], dash_array[1] * 0.051))
'draw:distance="%5.3fcm" />\n' %
(line_style, dash_array[0], dash_array[0],
dash_array[1] * 0.051))
# Current no leading number format for headers
@ -1360,8 +1299,7 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
#wrtf('text:level="10" style:num-format=""/>\n')
#wrtf('</text:outline-style>\n')
wrtf(
'<text:notes-configuration '
wrtf('<text:notes-configuration '
'text:note-class="footnote" '
'style:num-format="1" '
'text:start-value="0" '
@ -1369,15 +1307,13 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
'text:start-numbering-at="document"/> '
)
wrtf(
'<text:notes-configuration '
wrtf('<text:notes-configuration '
'text:note-class="endnote" '
'style:num-format="i" '
'text:start-value="0"/> '
)
wrtf(
'<text:linenumbering-configuration '
wrtf('<text:linenumbering-configuration '
'text:number-lines="false" '
'text:offset="0.499cm" '
'style:num-format="1" '
@ -1386,8 +1322,7 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
)
wrtf('</office:styles>\n')
wrtf(
'<office:automatic-styles>\n' +
wrtf('<office:automatic-styles>\n' +
_SHEADER_FOOTER +
'<style:page-layout style:name="pm1">\n' +
'<style:page-layout-properties ' +
@ -1399,13 +1334,11 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
)
wrtf('style:print-orientation="%s" ' %
("portrait"
if self.paper.get_orientation() == PAPER_PORTRAIT else
"landscape")
("portrait" if self.paper.get_orientation() == PAPER_PORTRAIT
else "landscape")
)
wrtf(
'fo:margin-top="%.2fcm" '
wrtf('fo:margin-top="%.2fcm" '
% self.paper.get_top_margin() +
'fo:margin-bottom="%.2fcm" '
% self.paper.get_bottom_margin() +
@ -1424,30 +1357,26 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
)
# header
wrtf(
'<style:header-style>\n'
wrtf('<style:header-style>\n'
'<style:header-footer-properties '
'fo:min-height="0cm" fo:margin-bottom="0.499cm"/>\n'
'</style:header-style>\n'
)
# footer
wrtf(
'<style:footer-style>\n'
wrtf('<style:footer-style>\n'
'<style:header-footer-properties '
'fo:min-height="0cm" fo:margin-bottom="0.499cm"/>\n'
'</style:footer-style>\n'
)
# End of page layout
wrtf(
'</style:page-layout>\n'
wrtf('</style:page-layout>\n'
'</office:automatic-styles>\n'
)
# Master Styles
wrtf(
'<office:master-styles>\n'
wrtf('<office:master-styles>\n'
'<style:master-page style:name="Standard" '
'style:page-layout-name="pm1">\n'
# header
@ -1540,7 +1469,7 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
s_tags = styledtext.get_tags()
markuptext = self._backend.add_markup_from_styled(text, s_tags, '\n')
if links == True:
if links is True:
markuptext = re.sub(URL_PATTERN, _CLICKABLE, markuptext)
# we need to know if we have new styles to add.
@ -1551,10 +1480,10 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
# The first element is the StyleType and the second one is the value
start = 0
while 1:
m = NewStyle.search(markuptext, start)
m = NEW_STYLE.search(markuptext, start)
if not m:
break
self.StyleList_notes.append([m.group(1)+m.group(2),
self.stylelist_notes.append([m.group(1)+m.group(2),
m.group(1),
m.group(2)])
start = m.end()
@ -1567,7 +1496,7 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
self.start_paragraph(style_name)
linenb = 1
else:
if ( linenb > 1 ):
if linenb > 1:
self.cntnt.write('<text:line-break/>')
self.cntnt.write(line)
linenb += 1
@ -1576,13 +1505,13 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
def write_text(self, text, mark=None, links=False):
"""
Uses the xml.sax.saxutils.escape function to convert XML
entities. The _esc_map dictionary allows us to add our own
entities. The ESC_MAP dictionary allows us to add our own
mappings.
@param mark: IndexMark to use for indexing
"""
text = escape(text, _esc_map)
text = escape(text, ESC_MAP)
if links == True:
if links is True:
text = re.sub(URL_PATTERN, _CLICKABLE, text)
self._write_mark(mark, text)
@ -1594,7 +1523,7 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
Insert a mark at this point in the document.
"""
if mark:
key = escape(mark.key, _esc_map)
key = escape(mark.key, ESC_MAP)
key = key.replace('"', '&quot;')
if mark.type == INDEX_TYPE_ALP:
self.cntnt.write(
@ -1705,7 +1634,7 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
'xmlns:manifest="urn:oasis:names:tc:opendocument' +
':xmlns:manifest:1.0">' +
'<manifest:file-entry ' +
'manifest:media-type="%s" ' % _apptype +
'manifest:media-type="%s" ' % APP_TYPE +
'manifest:full-path="/"/>'
)
@ -1780,8 +1709,8 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
style_sheet = self.get_style_sheet()
stype = style_sheet.get_draw_style(style)
pname = stype.get_paragraph_style()
p = style_sheet.get_paragraph_style(pname)
font = p.get_font()
para = style_sheet.get_paragraph_style(pname)
font = para.get_font()
size = font.get_size()
height = size * (len(text))
@ -1809,7 +1738,7 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
'<draw:text-box>\n' +
'<text:p text:style-name="X%s">' % pname +
'<text:span text:style-name="F%s">' % pname +
escape('\n'.join(text), _esc_map) +
escape('\n'.join(text), ESC_MAP) +
'</text:span></text:p>\n</draw:text-box>\n' +
'</draw:frame>\n')
@ -1894,7 +1823,7 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
'<text:p text:style-name="F%s">' % para_name +
'<text:span text:style-name="F%s">' % para_name +
#' fo:max-height="%.2f">' % font.get_size() +
escape(text, _esc_map) +
escape(text, ESC_MAP) +
'</text:span>' +
'</text:p>' +
'</draw:text-box>\n' +
@ -1940,7 +1869,7 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
self.cntnt.write(
'<text:p text:style-name="%s">' % para_name +
'<text:span text:style-name="F%s">' % para_name +
escape(text, _esc_map) +
escape(text, ESC_MAP) +
'</text:span>'
'</text:p>\n'
)
@ -1976,7 +1905,7 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
'<draw:text-box>' +
'<text:p text:style-name="X%s">' % para_name +
'<text:span text:style-name="F%s">' % para_name +
escape(text, _esc_map) +
escape(text, ESC_MAP) +
'</text:span>\n' +
'</text:p>\n' +
'</draw:text-box>'
@ -1991,27 +1920,29 @@ def process_spaces(line, format):
If the text is flowed (format==0), then leading spaces (after ignoring XML)
are removed. Embedded multiple spaces are reduced to one by ODF
If the text is pre-formatted (format==1). then all spaces (after ignoring XML)
are replaced by "<text:s/>"
If the text is pre-formatted (format==1), then all spaces (after ignoring
XML) are replaced by "<text:s/>"
Returns the processed text, and the number of significant (i.e. non-white-space) chars.
Returns the processed text, and the number of significant
(i.e. non-white-space) chars.
"""
txt = ""
xml = False
sigcount = 0
# we loop through every character, which is very inefficient, but an attempt to use
# a regex replace didn't always work. This was the code that was replaced.
# We loop through every character, which is very inefficient, but an attempt
# to use a regex replace didn't always work. This was the code that was
# replaced.
# Problem, we may not replace ' ' in xml tags, so we use a regex
# self.cntnt.write(re.sub(' (?=([^(<|>)]*<[^>]*>)*[^>]*$)',
# "<text:s/>", line))
for char in line:
if char == '<' and xml == False:
if char == '<' and xml is False:
xml = True
txt += char
elif char == '>' and xml == True:
elif char == '>' and xml is True:
xml = False
txt += char
elif xml == True:
elif xml is True:
txt += char
elif char == " " or char == "\t":
if format == 0 and sigcount == 0:

View File

@ -24,24 +24,26 @@
#------------------------------------------------------------------------
#
# python modules
# Python modules
#
#------------------------------------------------------------------------
from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
import logging
LOG = logging.getLogger(".rtfdoc")
#------------------------------------------------------------------------
#
# Load the base BaseDoc class
# Gramps modules
#
#------------------------------------------------------------------------
from gramps.gen.plug.docgen import (BaseDoc, TextDoc, FONT_SERIF, PARA_ALIGN_RIGHT,
PARA_ALIGN_CENTER, PARA_ALIGN_JUSTIFY,
URL_PATTERN)
from gramps.gen.utils.image import image_size, image_actual_size, resize_to_jpeg_buffer
from gramps.gen.plug.docgen import (
BaseDoc, TextDoc, FONT_SERIF, PARA_ALIGN_RIGHT, PARA_ALIGN_CENTER,
PARA_ALIGN_JUSTIFY, URL_PATTERN)
from gramps.gen.utils.image import (image_size, image_actual_size,
resize_to_jpeg_buffer)
from gramps.gen.errors import ReportError
from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
LOG = logging.getLogger(".rtfdoc")
#------------------------------------------------------------------------
#
@ -52,15 +54,18 @@ _CLICKABLE = r'''{\\field{\\*\\fldinst HYPERLINK "\1"}{\\fldrslt \1}}'''
#------------------------------------------------------------------------
#
# RTF uses a unit called "twips" for its measurements. According to the
# RTF specification, 1 point is 20 twips. This routines converts
# centimeters to twips
#
# 2.54 cm/inch 72pts/inch, 20twips/pt
# Functions
#
#------------------------------------------------------------------------
def twips(cm):
return int(((cm/2.54)*72)+0.5)*20
def twips(length_cm):
"""
RTF uses a unit called "twips" for its measurements. According to the
RTF specification, 1 point is 20 twips. This routines converts
centimeters to twips
2.54 cm/inch 72pts/inch, 20twips/pt
"""
return int(((length_cm/2.54)*72)+0.5)*20
#------------------------------------------------------------------------
#
@ -68,25 +73,21 @@ def twips(cm):
# use style sheets. Instead it writes raw formatting.
#
#------------------------------------------------------------------------
class RTFDoc(BaseDoc,TextDoc):
#--------------------------------------------------------------------
#
# Opens the file, and writes the header. Builds the color and font
# tables. Fonts are chosen using the MS TrueType fonts, since it
# is assumed that if you are generating RTF, you are probably
# targeting Word. This generator assumes a Western Europe character
# set.
#
#--------------------------------------------------------------------
def open(self,filename):
class RTFDoc(BaseDoc, TextDoc):
"""
Opens the file, and writes the header. Builds the color and font tables.
Fonts are chosen using the MS TrueType fonts, since it is assumed that if
you are generating RTF, you are probably targeting Word. This generator
assumes a Western Europe character set.
"""
def open(self, filename):
if filename[-4:] != ".rtf":
self.filename = filename + ".rtf"
else:
self.filename = filename
try:
self.f = open(self.filename,"w")
self.file = open(self.filename, "w")
except IOError as msg:
errmsg = "%s\n%s" % (_("Could not create %s") % self.filename, msg)
raise ReportError(errmsg)
@ -95,7 +96,7 @@ class RTFDoc(BaseDoc,TextDoc):
style_sheet = self.get_style_sheet()
self.f.write(
self.file.write(
'{\\rtf1\\ansi\\ansicpg1252\\deff0\n'
'{\\fonttbl\n'
'{\\f0\\froman\\fcharset0\\fprq0 Times New Roman;}\n'
@ -105,22 +106,22 @@ class RTFDoc(BaseDoc,TextDoc):
self.color_map = {}
index = 1
self.color_map[(0,0,0)] = 0
self.f.write('\\red0\\green0\\blue0;')
self.color_map[(0, 0, 0)] = 0
self.file.write('\\red0\\green0\\blue0;')
for style_name in style_sheet.get_paragraph_style_names():
style = style_sheet.get_paragraph_style(style_name)
fgcolor = style.get_font().get_color()
bgcolor = style.get_background_color()
if fgcolor not in self.color_map:
self.color_map[fgcolor] = index
self.f.write('\\red%d\\green%d\\blue%d;' % fgcolor)
self.file.write('\\red%d\\green%d\\blue%d;' % fgcolor)
index += 1
if bgcolor not in self.color_map:
self.f.write('\\red%d\\green%d\\blue%d;' % bgcolor)
self.file.write('\\red%d\\green%d\\blue%d;' % bgcolor)
self.color_map[bgcolor] = index
index += 1
self.f.write('}\n')
self.f.write(
self.file.write('}\n')
self.file.write(
'\\kerning0\\cf0\\viewkind1' +
'\\paperw%d' % twips(self.paper.get_size().get_width()) +
'\\paperh%d' % twips(self.paper.get_size().get_height()) +
@ -139,8 +140,8 @@ class RTFDoc(BaseDoc,TextDoc):
#
#--------------------------------------------------------------------
def close(self):
self.f.write('}\n')
self.f.close()
self.file.write('}\n')
self.file.close()
#--------------------------------------------------------------------
#
@ -148,7 +149,7 @@ class RTFDoc(BaseDoc,TextDoc):
#
#--------------------------------------------------------------------
def end_page(self):
self.f.write('\\sbkpage\n')
self.file.write('\\sbkpage\n')
#--------------------------------------------------------------------
#
@ -157,70 +158,70 @@ class RTFDoc(BaseDoc,TextDoc):
# does work.
#
#--------------------------------------------------------------------
def start_paragraph(self,style_name,leader=None):
def start_paragraph(self, style_name, leader=None):
self.opened = 0
style_sheet = self.get_style_sheet()
p = style_sheet.get_paragraph_style(style_name)
para = style_sheet.get_paragraph_style(style_name)
# build font information
f = p.get_font()
size = f.get_size()*2
bgindex = self.color_map[p.get_background_color()]
fgindex = self.color_map[f.get_color()]
if f.get_type_face() == FONT_SERIF:
font = para.get_font()
size = font.get_size()*2
bgindex = self.color_map[para.get_background_color()]
fgindex = self.color_map[font.get_color()]
if font.get_type_face() == FONT_SERIF:
self.font_type = '\\f0'
else:
self.font_type = '\\f1'
self.font_type += '\\fs%d\\cf%d\\cb%d' % (size,fgindex,bgindex)
self.font_type += '\\fs%d\\cf%d\\cb%d' % (size, fgindex, bgindex)
if f.get_bold():
if font.get_bold():
self.font_type += "\\b"
if f.get_underline():
if font.get_underline():
self.font_type += "\\ul"
if f.get_italic():
if font.get_italic():
self.font_type += "\\i"
# build paragraph information
if not self.in_table:
self.f.write('\\pard')
if p.get_alignment() == PARA_ALIGN_RIGHT:
self.f.write('\\qr')
elif p.get_alignment() == PARA_ALIGN_CENTER:
self.f.write('\\qc')
self.f.write(
'\\ri%d' % twips(p.get_right_margin()) +
'\\li%d' % twips(p.get_left_margin()) +
'\\fi%d' % twips(p.get_first_indent())
self.file.write('\\pard')
if para.get_alignment() == PARA_ALIGN_RIGHT:
self.file.write('\\qr')
elif para.get_alignment() == PARA_ALIGN_CENTER:
self.file.write('\\qc')
self.file.write(
'\\ri%d' % twips(para.get_right_margin()) +
'\\li%d' % twips(para.get_left_margin()) +
'\\fi%d' % twips(para.get_first_indent())
)
if p.get_alignment() == PARA_ALIGN_JUSTIFY:
self.f.write('\\qj')
if p.get_padding():
self.f.write('\\sa%d' % twips(p.get_padding()/2.0))
if p.get_top_border():
self.f.write('\\brdrt\\brdrs')
if p.get_bottom_border():
self.f.write('\\brdrb\\brdrs')
if p.get_left_border():
self.f.write('\\brdrl\\brdrs')
if p.get_right_border():
self.f.write('\\brdrr\\brdrs')
if p.get_first_indent():
self.f.write('\\fi%d' % twips(p.get_first_indent()))
if p.get_left_margin():
self.f.write('\\li%d' % twips(p.get_left_margin()))
if p.get_right_margin():
self.f.write('\\ri%d' % twips(p.get_right_margin()))
if para.get_alignment() == PARA_ALIGN_JUSTIFY:
self.file.write('\\qj')
if para.get_padding():
self.file.write('\\sa%d' % twips(para.get_padding()/2.0))
if para.get_top_border():
self.file.write('\\brdrt\\brdrs')
if para.get_bottom_border():
self.file.write('\\brdrb\\brdrs')
if para.get_left_border():
self.file.write('\\brdrl\\brdrs')
if para.get_right_border():
self.file.write('\\brdrr\\brdrs')
if para.get_first_indent():
self.file.write('\\fi%d' % twips(para.get_first_indent()))
if para.get_left_margin():
self.file.write('\\li%d' % twips(para.get_left_margin()))
if para.get_right_margin():
self.file.write('\\ri%d' % twips(para.get_right_margin()))
if leader:
self.opened = 1
self.f.write('\\tx%d' % twips(p.get_left_margin()))
self.f.write('{%s ' % self.font_type)
self.file.write('\\tx%d' % twips(para.get_left_margin()))
self.file.write('{%s ' % self.font_type)
self.write_text(leader)
self.f.write(self.text)
self.file.write(self.text)
self.text = ""
self.f.write('\\tab}')
self.file.write('\\tab}')
self.opened = 0
#--------------------------------------------------------------------
@ -233,20 +234,21 @@ class RTFDoc(BaseDoc,TextDoc):
#--------------------------------------------------------------------
def end_paragraph(self):
# FIXME: I don't understand why no end paragraph marker is output when
# we are inside a table. Since at least version 3.2.2, this seems to mean that
# there is no new paragraph after the first line of a table entry.
# we are inside a table. Since at least version 3.2.2, this seems to
# mean that there is no new paragraph after the first line of a table
# entry.
# For example in the birth cell, the first paragraph should be the
# description (21 Jan 1900 in London); if there is a note following this,
# there is no newline between the description and the note.
# description (21 Jan 1900 in London); if there is a note following
# this, there is no newline between the description and the note.
if not self.in_table:
self.f.write(self.text)
LOG.debug("end_paragraph: opened: %d write: %s" %
(self.opened,
self.text + '}' if self.opened else "" + "newline"))
self.file.write(self.text)
LOG.debug("end_paragraph: opened: %d write: %s",
self.opened,
self.text + '}' if self.opened else "" + "newline")
if self.opened:
self.f.write('}')
self.file.write('}')
self.opened = 0
self.f.write('\n\\par')
self.file.write('\n\\par')
self.text = ""
else:
if self.text == "":
@ -259,7 +261,7 @@ class RTFDoc(BaseDoc,TextDoc):
#
#--------------------------------------------------------------------
def page_break(self):
self.f.write('\\page\n')
self.file.write('\\page\n')
#--------------------------------------------------------------------
#
@ -267,9 +269,8 @@ class RTFDoc(BaseDoc,TextDoc):
#
#--------------------------------------------------------------------
def start_bold(self):
LOG.debug("start_bold: opened: %d saved text: %s" %
(self.opened,
'}' if self.opened else "" + '{%s\\b ' % self.font_type))
LOG.debug("start_bold: opened: %d saved text: %s", self.opened,
'}' if self.opened else "" + '{%s\\b ' % self.font_type)
if self.opened:
self.text += '}'
self.text += '{%s\\b ' % self.font_type
@ -281,9 +282,8 @@ class RTFDoc(BaseDoc,TextDoc):
#
#--------------------------------------------------------------------
def end_bold(self):
LOG.debug("end_bold: opened: %d saved text: %s" %
(self.opened,
self.text + '}'))
LOG.debug("end_bold: opened: %d saved text: %s", self.opened,
self.text + '}')
if not self.opened == 1:
print(self.opened)
raise RuntimeError
@ -304,7 +304,7 @@ class RTFDoc(BaseDoc,TextDoc):
# table, since a table is treated as a bunch of rows.
#
#--------------------------------------------------------------------
def start_table(self, name,style_name):
def start_table(self, name, style_name):
self.in_table = 1
styles = self.get_style_sheet()
self.tbl_style = styles.get_table_style(style_name)
@ -330,7 +330,7 @@ class RTFDoc(BaseDoc,TextDoc):
self.cell = 0
self.prev = 0
self.cell_percent = 0.0
self.f.write('\\trowd\n')
self.file.write('\\trowd\n')
#--------------------------------------------------------------------
#
@ -339,11 +339,11 @@ class RTFDoc(BaseDoc,TextDoc):
#
#--------------------------------------------------------------------
def end_row(self):
self.f.write('{')
self.file.write('{')
for line in self.contents:
self.f.write(line)
self.f.write('\\cell ')
self.f.write('}\\pard\\intbl\\row\n')
self.file.write(line)
self.file.write('\\cell ')
self.file.write('}\\pard\\intbl\\row\n')
#--------------------------------------------------------------------
#
@ -354,23 +354,23 @@ class RTFDoc(BaseDoc,TextDoc):
# previous cells plus its own width.
#
#--------------------------------------------------------------------
def start_cell(self,style_name,span=1):
def start_cell(self, style_name, span=1):
styles = self.get_style_sheet()
s = styles.get_cell_style(style_name)
self.remain = span -1
if s.get_top_border():
self.f.write('\\clbrdrt\\brdrs\\brdrw10\n')
self.file.write('\\clbrdrt\\brdrs\\brdrw10\n')
if s.get_bottom_border():
self.f.write('\\clbrdrb\\brdrs\\brdrw10\n')
self.file.write('\\clbrdrb\\brdrs\\brdrw10\n')
if s.get_left_border():
self.f.write('\\clbrdrl\\brdrs\\brdrw10\n')
self.file.write('\\clbrdrl\\brdrs\\brdrw10\n')
if s.get_right_border():
self.f.write('\\clbrdrr\\brdrs\\brdrw10\n')
self.file.write('\\clbrdrr\\brdrs\\brdrw10\n')
table_width = float(self.paper.get_usable_width())
for cell in range(self.cell,self.cell+span):
for cell in range(self.cell, self.cell+span):
self.cell_percent += float(self.tbl_style.get_column_width(cell))
cell_width = twips((table_width * self.cell_percent)/100.0)
self.f.write('\\cellx%d\\pard\intbl\n' % cell_width)
self.file.write('\\cellx%d\\pard\intbl\n' % cell_width)
self.cell += 1
#--------------------------------------------------------------------
@ -391,35 +391,37 @@ class RTFDoc(BaseDoc,TextDoc):
# dumped as a string of HEX numbers.
#
#--------------------------------------------------------------------
def add_media(self, name, pos, x_cm, y_cm, alt='', style_name=None, crop=None):
def add_media(self, name, pos, x_cm, y_cm, alt='', style_name=None,
crop=None):
nx, ny = image_size(name)
width, height = image_size(name)
if (nx, ny) == (0,0):
if (width, height) == (0, 0):
return
(act_width, act_height) = image_actual_size(x_cm, y_cm, nx, ny)
(act_width, act_height) = image_actual_size(x_cm, y_cm, width, height)
act_width = twips(act_width)
act_height = twips(act_height)
size = [act_width, act_height]
buf = resize_to_jpeg_buffer(name, size, crop=crop)
act_width = size[0] # In case it changed because of cropping or keeping the ratio
# The size may change because of cropping or keeping the ratio
act_width = size[0]
act_height = size[1]
self.f.write('{\*\shppict{\\pict\\jpegblip')
self.f.write('\\picwgoal%d\\pichgoal%d\n' % (act_width,act_height))
self.file.write('{\*\shppict{\\pict\\jpegblip')
self.file.write('\\picwgoal%d\\pichgoal%d\n' % (act_width, act_height))
index = 1
for i in buf:
self.f.write('%02x' % i)
if index%32==0:
self.f.write('\n')
self.file.write('%02x' % i)
if index%32 == 0:
self.file.write('\n')
index = index+1
self.f.write('}}\\par\n')
self.file.write('}}\\par\n')
if len(alt):
self.f.write('%s\n\\par\n' % '\\par'.join(alt))
self.file.write('%s\n\\par\n' % '\\par'.join(alt))
def write_styled_note(self, styledtext, format, style_name,
contains_html=False, links=False):
@ -447,15 +449,15 @@ class RTFDoc(BaseDoc,TextDoc):
self.start_paragraph(style_name)
linenb = 1
else:
if ( linenb > 1 ):
if linenb > 1:
self.write_text('\\line ')
self.write_text(line, links=links)
linenb += 1
# FIXME: I don't understand why these newlines are necessary.
# It may be related to the behaviour of end_paragraph inside tables, and
# write_text converting \n to end paragraph.
# This code prevents the whole document going wrong, but seems to produce an extra
# paragraph mark at the end of each table cell.
# It may be related to the behaviour of end_paragraph inside tables,
# and write_text converting \n to end paragraph.
# This code prevents the whole document going wrong, but seems to
# produce an extra paragraph mark at the end of each table cell.
if self.in_table:
# # Add LF when in table as in indiv_complete report
self.write_text('\n')
@ -472,9 +474,7 @@ class RTFDoc(BaseDoc,TextDoc):
def write_text(self, text, mark=None, links=False):
# Convert to unicode, just in case it's not. Fix of bug 2449.
text = str(text)
LOG.debug("write_text: opened: %d input text: %s" %
(self.opened,
text))
LOG.debug("write_text: opened: %d input text: %s", self.opened, text)
if self.opened == 0:
self.opened = 1
self.text += '{%s ' % self.font_type
@ -488,18 +488,17 @@ class RTFDoc(BaseDoc,TextDoc):
# RTF req valus in decimal, not hex.
self.text += '{\\uc1\\u%d\\uc0}' % ord(i)
elif i == '\n':
self.text += '\n\\par ';
self.text += '\n\\par '
elif i == '{' or i == '}' or i == '\\':
self.text += '\\%s' % i
else:
self.text += i
if links == True:
if links is True:
import re
self.text = re.sub(URL_PATTERN, _CLICKABLE, self.text)
LOG.debug("write_text, exit: opened: %d saved text: %s" %
(self.opened,
self.text))
LOG.debug("write_text, exit: opened: %d saved text: %s", self.opened,
self.text)
def process_spaces(line, format):
"""
@ -511,26 +510,27 @@ def process_spaces(line, format):
are removed, and multiple spaces are reduced to one.
If the text is pre-formatted (format==1). then all spaces are preserved
Note that xml is just treated like any other text,
because it will be from the original note, and it is just printed, not interpreted.
Returns the processed text, and the number of significant (i.e. non-white-space) chars.
Note that xml is just treated like any other text, because it will be from
the original note, and it is just printed, not interpreted.
Returns the processed text, and the number of significant
(i.e. non-white-space) chars.
"""
txt = ""
xml = False
space = False
sigcount = 0
# we loop through every character, which is very inefficient, but an attempt to use
# a regex replace didn't always work.
# we loop through every character, which is very inefficient, but an
# attempt to use a regex replace didn't always work.
for char in line:
if char == " " or char == "\t":
if format == 1:
txt += char
elif format == 0 and sigcount == 0:
pass
elif format == 0 and space == False:
elif format == 0 and space is False:
space = True
txt += char
elif format == 0 and space == True:
elif format == 0 and space is True:
pass
else:
sigcount += 1

View File

@ -27,7 +27,7 @@ SVG document generator.
#-------------------------------------------------------------------------
#
# python modules
# Python modules
#
#-------------------------------------------------------------------------
from io import StringIO
@ -37,12 +37,12 @@ from io import StringIO
# Gramps modules
#
#-------------------------------------------------------------------------
from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
from gramps.gen.plug.docgen import BaseDoc, DrawDoc, SOLID, FONT_SANS_SERIF
from gramps.gen.errors import ReportError
from gramps.gen.plug.menu import EnumeratedListOption
from gramps.gen.plug.report import DocOptions
from gramps.gen.const import GRAMPS_LOCALE as glocale
_ = glocale.translation.gettext
#-------------------------------------------------------------------------
#
@ -53,7 +53,7 @@ class SvgDrawDoc(BaseDoc, DrawDoc):
def __init__(self, styles, type, options=None):
BaseDoc.__init__(self, styles, type)
self.f = None
self.file = None
self.filename = None
self.level = 0
self.time = "0000-00-00T00:00:00"
@ -83,18 +83,18 @@ class SvgDrawDoc(BaseDoc, DrawDoc):
name = "%s.svg" % self.root
try:
self.f = open(name,"w", encoding="utf-8")
self.file = open(name, "w", encoding="utf-8")
except IOError as msg:
raise ReportError(_("Could not create %s") % name, msg)
except:
raise ReportError(_("Could not create %s") % name)
self.t = StringIO()
self.buffer = StringIO()
width = self.paper.get_size().get_width()
height = self.paper.get_size().get_height()
self.f.write(
self.file.write(
'<?xml version="1.0" encoding="UTF-8" standalone="no"?>\n'
'<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.0//EN" '
'"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">\n'
@ -110,8 +110,8 @@ class SvgDrawDoc(BaseDoc, DrawDoc):
style_sheet = self.get_style_sheet()
stype = style_sheet.get_draw_style(style)
pname = stype.get_paragraph_style()
p = style_sheet.get_paragraph_style(pname)
font = p.get_font()
para = style_sheet.get_paragraph_style(pname)
font = para.get_font()
size = font.get_size()
width = height = 0
@ -119,45 +119,45 @@ class SvgDrawDoc(BaseDoc, DrawDoc):
width = max(width, self.string_width(font, line))
height += size
centerx, centery = units(( x+self.paper.get_left_margin(),
y+self.paper.get_top_margin() ))
centerx, centery = units((x+self.paper.get_left_margin(),
y+self.paper.get_top_margin()))
xpos = (centerx - (width/2.0))
ypos = (centery - (height/2.0))
self.t.write(
self.buffer.write(
'<text ' +
'x="%4.2f" y="%4.2f" ' % (xpos, ypos) +
'transform="rotate(%d %4.2f %4.2f)" ' % (angle, centerx, centery) +
'style="fill:#%02x%02x%02x; '% font.get_color()
)
if font.get_bold():
self.t.write('font-weight:bold;')
self.buffer.write('font-weight:bold;')
if font.get_italic():
self.t.write('font-style:italic;')
self.t.write('font-size:%dpt; ' % size)
self.buffer.write('font-style:italic;')
self.buffer.write('font-size:%dpt; ' % size)
if font.get_type_face() == FONT_SANS_SERIF:
self.t.write('font-family:sans-serif;')
self.buffer.write('font-family:sans-serif;')
else:
self.t.write('font-family:serif;')
self.t.write('">')
self.buffer.write('font-family:serif;')
self.buffer.write('">')
for line in text:
# Center this line relative to the rest of the text
linex = xpos + (width - self.string_width(font, line) ) / 2
self.t.write(
linex = xpos + (width - self.string_width(font, line)) / 2
self.buffer.write(
'<tspan x="%4.2f" dy="%d">' % (linex, size) +
line +
'</tspan>'
)
self.t.write('</text>\n')
self.buffer.write('</text>\n')
def end_page(self):
# Print the text last for each page so that it is rendered on top of
# other graphic elements.
self.f.write(self.t.getvalue())
self.t.close()
self.f.write('</svg>\n')
self.f.close()
self.file.write(self.buffer.getvalue())
self.buffer.close()
self.file.write('</svg>\n')
self.file.close()
def draw_line(self, style, x1, y1, x2, y2):
x1 += self.paper.get_left_margin()
@ -166,17 +166,17 @@ class SvgDrawDoc(BaseDoc, DrawDoc):
y2 += self.paper.get_top_margin()
style_sheet = self.get_style_sheet()
s = style_sheet.get_draw_style(style)
draw_style = style_sheet.get_draw_style(style)
line_out = '<line x1="%4.2fcm" y1="%4.2fcm" ' % (x1, y1)
line_out += 'x2="%4.2fcm" y2="%4.2fcm" ' % (x2, y2)
line_out += 'style="stroke:#%02x%02x%02x; ' % s.get_color()
if s.get_line_style() != SOLID:
line_out += 'style="stroke:#%02x%02x%02x; ' % draw_style.get_color()
if draw_style.get_line_style() != SOLID:
line_out += 'stroke-dasharray: %s; ' % (
",".join(map(str, s.get_dash_style()))
",".join(map(str, draw_style.get_dash_style()))
)
line_out += 'stroke-width:%.2fpt;"/>\n' % s.get_line_width()
self.f.write(line_out)
line_out += 'stroke-width:%.2fpt;"/>\n' % draw_style.get_line_width()
self.file.write(line_out)
def draw_path(self, style, path):
style_sheet = self.get_style_sheet()
@ -190,16 +190,17 @@ class SvgDrawDoc(BaseDoc, DrawDoc):
",".join(map(str, stype.get_dash_style()))
)
line_out += ' stroke-width:%.2fpt;"' % stype.get_line_width()
line_out += ' points="%.2f,%.2f' % units((point[0]+self.paper.get_left_margin(),
line_out += ' points="%.2f,%.2f' % units(
(point[0]+self.paper.get_left_margin(),
point[1]+self.paper.get_top_margin()))
self.f.write(line_out)
self.file.write(line_out)
for point in path[1:]:
self.f.write(
self.file.write(
' %.2f,%.2f'
% units((point[0]+self.paper.get_left_margin(),
point[1]+self.paper.get_top_margin()))
)
self.f.write('"/>\n')
self.file.write('"/>\n')
def draw_box(self, style, text, x, y, w, h, mark=None):
""" @param mark: IndexMark to use for indexing (not supported) """
@ -211,7 +212,7 @@ class SvgDrawDoc(BaseDoc, DrawDoc):
shadow_width = box_style.get_shadow_space()
if box_style.get_shadow() and shadow_width > 0:
self.f.write(
self.file.write(
'<rect ' +
'x="%4.2fcm" ' % (x + shadow_width) +
'y="%4.2fcm" ' % (y + shadow_width) +
@ -232,37 +233,37 @@ class SvgDrawDoc(BaseDoc, DrawDoc):
",".join(map(str, box_style.get_dash_style()))
)
line_out += 'stroke-width:%f;"/>\n' % box_style.get_line_width()
self.f.write(line_out)
self.file.write(line_out)
if text:
para_name = box_style.get_paragraph_style()
assert( para_name != '' )
p = style_sheet.get_paragraph_style(para_name)
font = p.get_font()
assert para_name != ''
para = style_sheet.get_paragraph_style(para_name)
font = para.get_font()
font_size = font.get_size()
lines = text.split('\n')
mar = 10/28.35
fs = (font_size/28.35) * 1.2
center = y + (h + fs)/2.0 + (fs*0.2)
ystart = center - (fs/2.0) * len(lines)
fsize = (font_size/28.35) * 1.2
center = y + (h + fsize)/2.0 + (fsize*0.2)
ystart = center - (fsize/2.0) * len(lines)
for i, line in enumerate(lines):
ypos = ystart + (i * fs)
self.t.write(
ypos = ystart + (i * fsize)
self.buffer.write(
'<text ' +
'x="%4.2fcm" ' % (x+mar) +
'y="%4.2fcm" ' % ypos +
'style="fill:#%02x%02x%02x; '% font.get_color()
)
if font.get_bold():
self.t.write(' font-weight:bold;')
self.buffer.write(' font-weight:bold;')
if font.get_italic():
self.t.write(' font-style:italic;')
self.t.write(' font-size:%dpt;' % font_size)
self.buffer.write(' font-style:italic;')
self.buffer.write(' font-size:%dpt;' % font_size)
if font.get_type_face() == FONT_SANS_SERIF:
self.t.write(' font-family:sans-serif;')
self.buffer.write(' font-family:sans-serif;')
else:
self.t.write(' font-family:serif;')
self.t.write(
self.buffer.write(' font-family:serif;')
self.buffer.write(
'">' +
line +
'</text>\n'
@ -276,27 +277,27 @@ class SvgDrawDoc(BaseDoc, DrawDoc):
style_sheet = self.get_style_sheet()
box_style = style_sheet.get_draw_style(style)
para_name = box_style.get_paragraph_style()
p = style_sheet.get_paragraph_style(para_name)
para = style_sheet.get_paragraph_style(para_name)
font = p.get_font()
font = para.get_font()
font_size = font.get_size()
fs = (font_size/28.35) * 1.2
self.t.write(
fsize = (font_size/28.35) * 1.2
self.buffer.write(
'<text ' +
'x="%4.2fcm" ' % x +
'y="%4.2fcm" ' % (y+fs) +
'y="%4.2fcm" ' % (y+fsize) +
'style="fill:#%02x%02x%02x;'% font.get_color()
)
if font.get_bold():
self.t.write('font-weight:bold;')
self.buffer.write('font-weight:bold;')
if font.get_italic():
self.t.write('font-style:italic;')
self.t.write('font-size:%dpt; ' % font_size)
self.buffer.write('font-style:italic;')
self.buffer.write('font-size:%dpt; ' % font_size)
if font.get_type_face() == FONT_SANS_SERIF:
self.t.write('font-family:sans-serif;')
self.buffer.write('font-family:sans-serif;')
else:
self.t.write('font-family:serif;')
self.t.write(
self.buffer.write('font-family:serif;')
self.buffer.write(
'">' +
text +
'</text>\n'
@ -307,8 +308,8 @@ class SvgDrawDoc(BaseDoc, DrawDoc):
style_sheet = self.get_style_sheet()
box_style = style_sheet.get_draw_style(style)
para_name = box_style.get_paragraph_style()
p = style_sheet.get_paragraph_style(para_name)
font = p.get_font()
para = style_sheet.get_paragraph_style(para_name)
font = para.get_font()
width = self.string_width(font, text) / 72
x -= width
self.draw_text(style, text, x, y)
@ -345,6 +346,6 @@ class SvgDrawDocOptions(DocOptions):
('blue', _('blue')),
('cyan', _('cyan')),
('magenta', _('magenta')),
('yellow', _('yellow')) ])
('yellow', _('yellow'))])
background.set_help(_('The color, if any, of the SVG background'))
menu.add_option(category_name, 'svg_background', background)