1289 lines
47 KiB
Python
1289 lines
47 KiB
Python
#
|
|
# -*- coding: utf-8 -*-
|
|
#
|
|
# Gramps - a GTK+/GNOME based genealogy program
|
|
#
|
|
# Copyright (C) 2000-2006 Donald N. Allingham
|
|
# Copyright (C) 2007-2009 Brian G. Matherly
|
|
# Copyright (C) 2008 Raphael Ackermann
|
|
# 2002-2003 Donald A. Peterson
|
|
# 2003 Alex Roitman
|
|
# 2009 Benny Malengier
|
|
# 2010 Peter Landgren
|
|
# Copyright (C) 2011 Adam Stein <adam@csh.rit.edu>
|
|
# 2011-2012 Harald Rosemann
|
|
#
|
|
# 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$
|
|
|
|
"""LaTeX document generator"""
|
|
|
|
#------------------------------------------------------------------------
|
|
#
|
|
# python modules
|
|
#
|
|
#------------------------------------------------------------------------
|
|
from gen.ggettext import gettext as _
|
|
from bisect import bisect
|
|
import re, os, sys
|
|
|
|
#----------------------------------------------------------------------- -
|
|
#
|
|
# gramps modules
|
|
#
|
|
#------------------------------------------------------------------------
|
|
from gen.plug.docgen import BaseDoc, TextDoc, PAPER_LANDSCAPE, FONT_SANS_SERIF, URL_PATTERN
|
|
from gen.plug.docbackend import DocBackend
|
|
import Image
|
|
import Errors
|
|
import Utils
|
|
|
|
_CLICKABLE = r'''\url{\1}'''
|
|
|
|
#------------------------------------------------------------------------
|
|
#
|
|
# Special settings for LaTeX output
|
|
#
|
|
#------------------------------------------------------------------------
|
|
# For an interim mark e.g. for an intended linebreak I use a special pattern.
|
|
# It shouldn't interfere with normal text. In LaTeX charackter '&' is used
|
|
# for column separation in tables and may occur there in series. The pattern
|
|
# is used here before column separation is set. On the other hand incoming
|
|
# text can't show this pattern for it would have been replaced by '\&\&'.
|
|
# So the choosen pattern will do the job without confusion:
|
|
|
|
SEPARATION_PAT = '&&'
|
|
|
|
#------------------------------------------------------------------------
|
|
#
|
|
# Latex Article Template
|
|
#
|
|
#------------------------------------------------------------------------
|
|
|
|
_LATEX_TEMPLATE_1 = '\\documentclass[%s]{article}\n'
|
|
_LATEX_TEMPLATE = '''%
|
|
%
|
|
\\usepackage[T1]{fontenc}%
|
|
%
|
|
% We use latin1 encoding at a minimum by default.
|
|
% GRAMPS uses unicode UTF-8 encoding for its
|
|
% international support. LaTeX can deal gracefully
|
|
% with unicode encoding by using the ucs style invoked
|
|
% when utf8 is specified as an option to the inputenc
|
|
% package. This package is included by default in some
|
|
% installations, but not in others, so we do not make it
|
|
% the default. Uncomment the first line if you wish to use it
|
|
% (If you do not have ucs.sty, you may obtain it from
|
|
% http://www.tug.org/tex-archive/macros/latex/contrib/supported/unicode/)
|
|
%
|
|
%\\usepackage[latin1]{inputenc}%
|
|
\\usepackage[latin1,utf8]{inputenc}%
|
|
\\usepackage{graphicx}% Extended graphics support
|
|
\\usepackage{longtable}% For multi-page tables
|
|
\\usepackage{calc}% For some calculations
|
|
\\usepackage{ifthen}% For table width calculations
|
|
\\usepackage{ragged2e}% For left aligning with hyphenation
|
|
\\usepackage{wrapfig}% wrap pictures in text
|
|
%
|
|
% Depending on your LaTeX installation, the margins may be too
|
|
% narrow. This can be corrected by uncommenting the following
|
|
% two lines and adjusting the width appropriately. The example
|
|
% removes 0.5in from each margin. (Adds 1 inch to the text)
|
|
%\\addtolength{\\oddsidemargin}{-0.5in}%
|
|
%\\addtolength{\\textwidth}{1.0in}%
|
|
%
|
|
% Vertical spacing between paragraphs:
|
|
% take one of three possibilities or modify to your taste:
|
|
%\\setlength{\\parskip}{1.0ex plus0.2ex minus0.2ex}%
|
|
\\setlength{\\parskip}{1.5ex plus0.3ex minus0.3ex}%
|
|
%\\setlength{\\parskip}{2.0ex plus0.4ex minus0.4ex}%
|
|
%
|
|
% Vertical spacing between lines:
|
|
% take one of three possibilities or modify to your taste:
|
|
\\renewcommand{\\baselinestretch}{1.0}%
|
|
%\\renewcommand{\\baselinestretch}{1.1}%
|
|
%\\renewcommand{\\baselinestretch}{1.2}%
|
|
%
|
|
% Indentation; substitute for '1cm' of gramps, 2.5em is right for 12pt
|
|
% take one of three possibilities or modify to your taste:
|
|
\\newlength{\\grbaseindent}%
|
|
%\\setlength{\\grbaseindent}{3.0em}%
|
|
\\setlength{\\grbaseindent}{2.5em}%
|
|
%\\setlength{\\grbaseindent}{2.0em}%
|
|
%
|
|
%
|
|
% -------------------------------------------------------------
|
|
% New lengths, counters and commands for calculations in tables
|
|
% -------------------------------------------------------------
|
|
%
|
|
\\newlength{\\grtabwidth}%
|
|
\\newlength{\\grtabprepos}%
|
|
\\newlength{\\grreqwidth}%
|
|
\\newlength{\\grtempwd}%
|
|
\\newlength{\\grmaxwidth}%
|
|
\\newlength{\\grprorated}%
|
|
\\newlength{\\grxwd}%
|
|
\\newlength{\\grwidthused}%
|
|
\\newlength{\\grreduce}%
|
|
\\newlength{\\grcurcolend}%
|
|
\\newlength{\\grspanwidth}%
|
|
\\newlength{\\grleadlabelwidth}%
|
|
\\newlength{\\grminpgindent}%
|
|
\\newlength{\\grlistbacksp}%
|
|
\\newlength{\\grpictsize}%
|
|
\\newlength{\\grmaxpictsize}%
|
|
\\newlength{\\grtextsize}%
|
|
\\newlength{\\grmaxtextsize}%
|
|
\\newcounter{grtofixcnt}%
|
|
\\newcounter{grxwdcolcnt}%
|
|
%
|
|
%
|
|
\\newcommand{\\grinitlength}[2]{%
|
|
\\ifthenelse{\\isundefined{#1}}%
|
|
{\\newlength{#1}}{}%
|
|
\\setlength{#1}{#2}%
|
|
}%
|
|
%
|
|
\\newcommand{\\grinittab}[2]{% #1: tabwidth, #2 = 1.0/anz-cols
|
|
\\setlength{\\grtabwidth}{#1}%
|
|
\\setlength{\\grprorated}{#2\\grtabwidth}%
|
|
\\setlength{\\grwidthused}{0em}%
|
|
\\setlength{\\grreqwidth}{0em}%
|
|
\\setlength{\\grmaxwidth }{0em}%
|
|
\\setlength{\\grxwd}{0em}%
|
|
\\setlength{\\grtempwd}{0em}%
|
|
\\setlength{\\grpictsize}{0em}%
|
|
\\setlength{\\grmaxpictsize}{0em}%
|
|
\\setlength{\\grtextsize}{0em}%
|
|
\\setlength{\\grmaxtextsize}{0em}%
|
|
\\setlength{\\grcurcolend}{0em}%
|
|
\\setcounter{grxwdcolcnt}{0}%
|
|
\\setcounter{grtofixcnt}{0}% number of wide cols%
|
|
\\grinitlength{\\grcolbega}{0em}% beg of first col
|
|
}%
|
|
%
|
|
\\newcommand{\\grmaxvaltofirst}[2]{%
|
|
\\ifthenelse{\\lengthtest{#1 < #2}}%
|
|
{\\setlength{#1}{#2}}{}%
|
|
}%
|
|
%
|
|
\\newcommand{\\grsetreqfull}{%
|
|
\\grmaxvaltofirst{\\grmaxpictsize}{\\grpictsize}%
|
|
\\grmaxvaltofirst{\\grmaxtextsize}{\\grtextsize}%
|
|
}%
|
|
%
|
|
\\newcommand{\\grsetreqpart}[1]{%
|
|
\\addtolength{\\grtextsize}{#1 - \\grcurcolend}%
|
|
\\addtolength{\\grpictsize}{#1 - \\grcurcolend}%
|
|
\\grsetreqfull%
|
|
}%
|
|
%
|
|
\\newcommand{\\grdividelength}{%
|
|
\\setlength{\\grtempwd}{\\grtabwidth - \\grwidthused}%
|
|
% rough division of lengths:
|
|
% if 0 < #1 <= 10: \\grxwd = ~\\grtempwd / grtofixcnt
|
|
% otherwise: \\grxwd = \\grprorated
|
|
\\ifthenelse{\\value{grtofixcnt} > 0}%
|
|
{\\ifthenelse{\\value{grtofixcnt}=1}%
|
|
{\\setlength{\\grxwd}{\\grtempwd}}{%
|
|
\\ifthenelse{\\value{grtofixcnt}=2}
|
|
{\\setlength{\\grxwd}{0.5\\grtempwd}}{%
|
|
\\ifthenelse{\\value{grtofixcnt}=3}
|
|
{\\setlength{\\grxwd}{0.333\\grtempwd}}{%
|
|
\\ifthenelse{\\value{grtofixcnt}=4}
|
|
{\\setlength{\\grxwd}{0.25\\grtempwd}}{%
|
|
\\ifthenelse{\\value{grtofixcnt}=5}
|
|
{\\setlength{\\grxwd}{0.2\\grtempwd}}{%
|
|
\\ifthenelse{\\value{grtofixcnt}=6}
|
|
{\\setlength{\\grxwd}{0.166\\grtempwd}}{%
|
|
\\ifthenelse{\\value{grtofixcnt}=7}
|
|
{\\setlength{\\grxwd}{0.143\\grtempwd}}{%
|
|
\\ifthenelse{\\value{grtofixcnt}=8}
|
|
{\\setlength{\\grxwd}{0.125\\grtempwd}}{%
|
|
\\ifthenelse{\\value{grtofixcnt}=9}
|
|
{\\setlength{\\grxwd}{0.111\\grtempwd}}{%
|
|
\\ifthenelse{\\value{grtofixcnt}=10}
|
|
{\\setlength{\\grxwd}{0.1\\grtempwd}}{%
|
|
\\setlength{\\grxwd}{\\grprorated}% give up, take \\grprorated%
|
|
}}}}}}}}}}%
|
|
\\setlength{\\grreduce}{0em}%
|
|
}{\\setlength{\\grxwd}{0em}}%
|
|
}%
|
|
%
|
|
\\newcommand{\\grtextneedwidth}[1]{%
|
|
\\settowidth{\\grtempwd}{#1}%
|
|
\\grmaxvaltofirst{\\grtextsize}{\\grtempwd}%
|
|
}%
|
|
%
|
|
\\newcommand{\\grcolsfirstfix}[5]{%
|
|
\\grinitlength{#1}{\\grcurcolend}%
|
|
\\grinitlength{#3}{0em}%
|
|
\\grinitlength{#4}{\\grmaxpictsize}%
|
|
\\grinitlength{#5}{\\grmaxtextsize}%
|
|
\\grinitlength{#2}{#5}%
|
|
\\grmaxvaltofirst{#2}{#4}%
|
|
\\addtolength{#2}{2\\tabcolsep}%
|
|
\\grmaxvaltofirst{\\grmaxwidth}{#2}%
|
|
\\ifthenelse{\\lengthtest{#2 < #4} \\or \\lengthtest{#2 < \\grprorated}}%
|
|
{ \\setlength{#3}{#2}%
|
|
\\addtolength{\\grwidthused}{#2} }%
|
|
{ \\stepcounter{grtofixcnt} }%
|
|
\\addtolength{\\grcurcolend}{#2}%
|
|
}%
|
|
%
|
|
\\newcommand{\\grcolssecondfix}[4]{%
|
|
\\ifthenelse{\\lengthtest{\\grcurcolend < \\grtabwidth}}%
|
|
{ \\setlength{#3}{#2} }%
|
|
{ \\addtolength{#1}{-\\grreduce}%
|
|
\\ifthenelse{\\lengthtest{#2 = \\grmaxwidth}}%
|
|
{ \\stepcounter{grxwdcolcnt}}%
|
|
{ \\ifthenelse{\\lengthtest{#3 = 0em} \\and %
|
|
\\lengthtest{#4 > 0em}}%
|
|
{ \\setlength{\\grtempwd}{#4}%
|
|
\\grmaxvaltofirst{\\grtempwd}{\\grxwd}%
|
|
\\addtolength{\\grreduce}{#2 - \\grtempwd}%
|
|
\\setlength{#2}{\\grtempwd}%
|
|
\\addtolength{\\grwidthused}{#2}%
|
|
\\addtocounter{grtofixcnt}{-1}%
|
|
\\setlength{#3}{#2}%
|
|
}{}%
|
|
}%
|
|
}%
|
|
}%
|
|
%
|
|
\\newcommand{\\grcolsthirdfix}[3]{%
|
|
\\ifthenelse{\\lengthtest{\\grcurcolend < \\grtabwidth}}%
|
|
{}{ \\addtolength{#1}{-\\grreduce}%
|
|
\\ifthenelse{\\lengthtest{#3 = 0em} \\and %
|
|
\\lengthtest{#2 < \\grmaxwidth}}%
|
|
{ \\ifthenelse{\\lengthtest{#2 < 0.5\\grmaxwidth}}%
|
|
{ \\setlength{\\grtempwd}{0.5\\grxwd}%
|
|
\\grmaxvaltofirst{\\grtempwd}{0.7\\grprorated}}%
|
|
{ \\setlength{\\grtempwd}{\\grxwd}}%
|
|
\\addtolength{\\grreduce}{#2 - \\grtempwd}%
|
|
\\setlength{#2}{\\grtempwd}%
|
|
\\addtolength{\\grwidthused}{#2}%
|
|
\\addtocounter{grtofixcnt}{-1}%
|
|
\\setlength{#3}{#2}%
|
|
}{}%
|
|
}%
|
|
}%
|
|
%
|
|
\\newcommand{\\grcolsfourthfix}[3]{%
|
|
\\ifthenelse{\\lengthtest{\\grcurcolend < \\grtabwidth}}%
|
|
{}{ \\addtolength{#1}{-\\grreduce}%
|
|
\\ifthenelse{\\lengthtest{#3 = 0em}}%
|
|
{ \\addtolength{\\grreduce}{#2 - \\grxwd}%
|
|
\\setlength{#2}{\\grxwd}%
|
|
\\setlength{#3}{#2}%
|
|
}{}%
|
|
}%
|
|
}%
|
|
%
|
|
\\newcommand{\\grgetspanwidth}[4]{%
|
|
\\grinitlength{#1}{#3 - #2 + #4}%
|
|
}%
|
|
%
|
|
\\newcommand{\\tabheadstrutceil}{%
|
|
\\rule[0.0ex]{0.00em}{3.5ex}}%
|
|
\\newcommand{\\tabheadstrutfloor}{%
|
|
\\rule[-2.0ex]{0.00em}{2.5ex}}%
|
|
\\newcommand{\\tabrowstrutceil}{%
|
|
\\rule[0.0ex]{0.00em}{2.9ex}}%
|
|
\\newcommand{\\tabrowstrutfloor}{%
|
|
\\rule[-0.1ex]{0.00em}{2.0ex}}%
|
|
%
|
|
\\newcommand{\\grempty}[1]{}%
|
|
%
|
|
\\newcommand{\\graddvdots}[1]{%
|
|
\\hspace*{\\fill}\\hspace*{\\fill}\\raisebox{#1}{\\vdots}%
|
|
}%
|
|
%
|
|
\\newcommand{\\grtabpgbreak}[4]{%
|
|
#1 { \\parbox[t]{ #2 - 2\\tabcolsep}{\\tabheadstrutceil\\hspace*{\\fill}%
|
|
\\raisebox{#4}{\\vdots} #3{#4} \\hspace*{\\fill}\\tabheadstrutfloor}}%
|
|
}%
|
|
%
|
|
\\newcommand{\\grcolpart}[3]{%
|
|
#1 { \\parbox[t]{ #2 - 2\\tabcolsep}%
|
|
{\\tabrowstrutceil #3~\\\\[-1.6ex]\\tabrowstrutfloor}}%
|
|
}%
|
|
%
|
|
\\newcommand{\\grminpghead}[2]{%
|
|
\\setlength{\\grminpgindent}{#1\\grbaseindent-\\grlistbacksp}%
|
|
\\hspace*{\\grminpgindent}%
|
|
\\ifthenelse{\\not \\lengthtest{#2em > 0em}}%
|
|
{\\begin{minipage}[t]{\\textwidth -\\grminpgindent}}%
|
|
{\\begin{minipage}[t]{\\textwidth -\\grminpgindent%
|
|
-#2\\grbaseindent -4\\tabcolsep}}%
|
|
}%
|
|
%
|
|
\\newcommand{\\grminpgtail}{%
|
|
\\end{minipage}\\parindent0em%
|
|
}%
|
|
%
|
|
\\newcommand{\\grlisthead}[1]{%
|
|
\\begin{list}{#1}%
|
|
{ \\setlength{\\labelsep}{0.5em}%
|
|
\\setlength{\\labelwidth}{\\grleadlabelwidth}%
|
|
\\setlength{\\leftmargin}{\\grlistbacksp}%
|
|
}\\item%
|
|
}%
|
|
%
|
|
\\newcommand{\\grlisttail}{%
|
|
\\end{list}%
|
|
}%
|
|
%
|
|
\\newcommand{\\grprepleader}[1]{%
|
|
\\settowidth{\\grtempwd}{#1}%
|
|
\\ifthenelse{\\lengthtest{\\grtempwd > \\grleadlabelwidth}}%
|
|
{ \\setlength{\\grleadlabelwidth}{\\grtempwd}}{}%
|
|
\\setlength{\\grlistbacksp}{\\grleadlabelwidth + 1.0em}%
|
|
}%
|
|
%
|
|
\\newcommand{\\grprepnoleader}{%
|
|
\\setlength{\\grleadlabelwidth}{0em}%
|
|
\\setlength{\\grlistbacksp}{0em}%
|
|
}%
|
|
%
|
|
\\newcommand{\\grmkpicture}[4]{%
|
|
\\begin{wrapfigure}{r}{#2\\grbaseindent}%
|
|
\\vspace{-6ex}%
|
|
\\begin{center}%
|
|
\\includegraphics[%
|
|
width= #2\\grbaseindent,%
|
|
height= #3\\grbaseindent,%
|
|
keepaspectratio]%
|
|
{#1}\\\\%
|
|
{\\RaggedRight\\footnotesize#4}%
|
|
\\end{center}%
|
|
\\end{wrapfigure}%
|
|
\\settowidth{\\grtempwd}{\\footnotesize#4}%
|
|
\\setlength{\\grxwd}{#2\\grbaseindent}%
|
|
\\ifthenelse{\\lengthtest{\\grtempwd < 0.7\\grxwd}}%
|
|
{\\setlength{\\grxwd}{1ex}}{%
|
|
\\ifthenelse{\\lengthtest{\\grtempwd < 1.2\\grxwd}}%
|
|
{\\setlength{\\grxwd}{2ex}}{%
|
|
\\ifthenelse{\\lengthtest{\\grtempwd < 1.8\\grxwd}}%
|
|
{\\setlength{\\grxwd}{6ex}}{%
|
|
\\ifthenelse{\\lengthtest{\\grtempwd < 2.0\\grxwd}}%
|
|
{\\setlength{\\grxwd}{10ex}}{%
|
|
\\setlength{\\grxwd}{12ex}}%
|
|
}}}%
|
|
\\setlength{\\grtempwd}{#3\\grbaseindent + \\grxwd}%
|
|
\\rule[-\\grtempwd]{0pt}{\\grtempwd}%
|
|
\\setlength{\\grtabprepos}{-\\grtempwd}%
|
|
}%
|
|
%
|
|
%
|
|
\\begin{document}%
|
|
'''
|
|
|
|
|
|
#------------------------------------------------------------------------
|
|
#
|
|
# Font size table and function
|
|
#
|
|
#------------------------------------------------------------------------
|
|
|
|
# These tables correlate font sizes to Latex. The first table contains
|
|
# typical font sizes in points. The second table contains the standard
|
|
# Latex font size names. Since we use bisect to map the first table to the
|
|
# second, we are guaranteed that any font less than 6 points is 'tiny', fonts
|
|
# from 6-7 points are 'script', etc. and fonts greater than or equal to 22
|
|
# are considered 'Huge'. Note that fonts from 12-13 points are not given a
|
|
# Latex font size name but are considered "normal."
|
|
|
|
_FONT_SIZES = [6, 8, 10, 12, 14, 16, 18, 20, 22]
|
|
_FONT_NAMES = ['tiny', 'scriptsize', 'footnotesize', 'small', '',
|
|
'large', 'Large', 'LARGE', 'huge', 'Huge']
|
|
|
|
def map_font_size(fontsize):
|
|
""" Map font size in points to Latex font size """
|
|
return _FONT_NAMES[bisect(_FONT_SIZES, fontsize)]
|
|
|
|
|
|
#------------------------------------------------------------------------
|
|
#
|
|
# auxiliaries to facilitate table construction
|
|
#
|
|
#------------------------------------------------------------------------
|
|
|
|
# patterns for regular expressions, module re:
|
|
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) = range(7)
|
|
FIRST_ROW, SUBSEQ_ROW = range(2)
|
|
|
|
|
|
def get_charform(col_num):
|
|
"""
|
|
Transfer column number to column charakter,
|
|
limited to letters within a-z;
|
|
26, there is no need for more.
|
|
early test of column count in start_table()
|
|
"""
|
|
if col_num > ord('z') - ord('a'):
|
|
raise ValueError, ''.join((
|
|
'\n number of table columns is ', repr(col_num),
|
|
'\n should be <= ', repr(ord('z') - ord('a'))))
|
|
return chr(ord('a') + col_num)
|
|
|
|
def get_numform(col_char):
|
|
return ord(col_char) - ord('a')
|
|
|
|
|
|
#------------------------------------------
|
|
# row_alph_counter = str_incr(MULTCOL_COUNT_BASE)
|
|
#
|
|
# 'aaa' is sufficient for up to 17576 multicolumns in each table;
|
|
# do you need more?
|
|
# uncomment one of the two lines
|
|
MULTCOL_COUNT_BASE = 'aaa'
|
|
# MULTCOL_COUNT_BASE = 'aaaa'
|
|
#------------------------------------------
|
|
|
|
def str_incr(str_counter):
|
|
""" for counting table rows """
|
|
lili = list(str_counter)
|
|
while 1:
|
|
yield ''.join(lili)
|
|
if ''.join(lili) == len(lili)*'z':
|
|
raise ValueError, ''.join((
|
|
'\n can\'t increment string ', ''.join(lili),
|
|
' of length ', str(len(lili))))
|
|
for i in range(len(lili)-1, -1, -1):
|
|
if lili[i] < 'z':
|
|
lili[i] = chr(ord(lili[i])+1)
|
|
break
|
|
else:
|
|
lili[i] = 'a'
|
|
|
|
#------------------------------------------------------------------------
|
|
#
|
|
# Structure of Table-Memory
|
|
#
|
|
#------------------------------------------------------------------------
|
|
|
|
class Tab_Cell():
|
|
def __init__(self, colchar, span, head, content):
|
|
self.colchar = colchar
|
|
self.span = span
|
|
self.head = head
|
|
self.content = content
|
|
class Tab_Row():
|
|
def __init__(self):
|
|
self.cells =[]
|
|
self.tail = ''
|
|
self.addit = '' # for: \\hline, \\cline{}
|
|
class Tab_Mem():
|
|
def __init__(self, head):
|
|
self.head = head
|
|
self.tail =''
|
|
self.rows =[]
|
|
|
|
#------------------------------------------------------------------------
|
|
#
|
|
# Functions for docbackend
|
|
#
|
|
#------------------------------------------------------------------------
|
|
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
|
|
"""
|
|
text = text.replace('&', '\\&')
|
|
text = text.replace('$', '\\$')
|
|
text = text.replace('%', '\\%')
|
|
text = text.replace('#', '\\#')
|
|
text = text.replace('_', '\\_')
|
|
text = text.replace('{', '\\{')
|
|
text = text.replace('}', '\\}')
|
|
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
|
|
|
|
#------------------------------------------------------------------------
|
|
#
|
|
# Document Backend class for cairo docs
|
|
#
|
|
#------------------------------------------------------------------------
|
|
|
|
class LateXBackend(DocBackend):
|
|
"""
|
|
Implementation of docbackend for latex docs.
|
|
File and File format management for latex docs
|
|
"""
|
|
# overwrite base class attributes, they become static var of LaTeXDoc
|
|
SUPPORTED_MARKUP = [
|
|
DocBackend.BOLD,
|
|
DocBackend.ITALIC,
|
|
DocBackend.UNDERLINE,
|
|
DocBackend.FONTSIZE,
|
|
DocBackend.FONTFACE,
|
|
DocBackend.SUPERSCRIPT ]
|
|
|
|
STYLETAG_MARKUP = {
|
|
DocBackend.BOLD : ("\\textbf{", "}"),
|
|
DocBackend.ITALIC : ("\\textit{", "}"),
|
|
DocBackend.UNDERLINE : ("\\underline{", "}"),
|
|
DocBackend.SUPERSCRIPT : ("\\textsuperscript{", "}"),
|
|
}
|
|
|
|
ESCAPE_FUNC = lambda x: latexescape
|
|
|
|
def setescape(self, preformatted=False):
|
|
"""
|
|
Latex needs two different escape functions depending on the type.
|
|
This function allows to switch the escape function
|
|
"""
|
|
if not preformatted:
|
|
LateXBackend.ESCAPE_FUNC = lambda x: latexescape
|
|
else:
|
|
LateXBackend.ESCAPE_FUNC = lambda x: latexescapeverbatim
|
|
|
|
def _create_xmltag(self, type, value):
|
|
"""
|
|
overwrites the method in DocBackend.
|
|
creates the latex tags needed for non bool style types we support:
|
|
FONTSIZE : use different \large denomination based
|
|
on size
|
|
: very basic, in mono in the font face
|
|
then we use {\ttfamily }
|
|
"""
|
|
if type not in self.SUPPORTED_MARKUP:
|
|
return None
|
|
elif type == DocBackend.FONTSIZE:
|
|
#translate size in point to something LaTeX can work with
|
|
fontsize = map_font_size(value)
|
|
if fontsize:
|
|
return ("{\\" + fontsize + ' ', "}")
|
|
else:
|
|
return ("", "")
|
|
|
|
elif type == DocBackend.FONTFACE:
|
|
if 'MONO' in value.upper():
|
|
return ("{\\ttfamily ", "}")
|
|
elif 'ROMAN' in value.upper():
|
|
return ("{\\rmfamily ", "}")
|
|
return None
|
|
|
|
def _checkfilename(self):
|
|
"""
|
|
Check to make sure filename satisfies the standards for this filetype
|
|
"""
|
|
if not self._filename.endswith(".tex"):
|
|
self._filename = self._filename + ".tex"
|
|
|
|
|
|
#------------------------------------------------------------------------
|
|
#
|
|
# Paragraph Handling
|
|
#
|
|
#------------------------------------------------------------------------
|
|
|
|
class TexFont(object):
|
|
def __init__(self, style=None):
|
|
if style:
|
|
self.font_beg = style.font_beg
|
|
self.font_end = style.font_end
|
|
self.leftIndent = style.left_indent
|
|
self.firstLineIndent = style.firstLineIndent
|
|
else:
|
|
self.font_beg = ""
|
|
self.font_end = ""
|
|
self.leftIndent = ""
|
|
self.firstLineIndent = ""
|
|
|
|
|
|
#------------------------------------------------------------------
|
|
#
|
|
# LaTeXDoc
|
|
#
|
|
#------------------------------------------------------------------
|
|
|
|
class LaTeXDoc(BaseDoc, TextDoc):
|
|
"""LaTeX document interface class. Derived from BaseDoc"""
|
|
|
|
# ---------------------------------------------------------------
|
|
# some additional variables
|
|
# ---------------------------------------------------------------
|
|
in_table = False
|
|
in_multrow_cell = False # for tab-strukt: cols of rows
|
|
pict = ''
|
|
pict_in_table = False
|
|
pict_width = 0
|
|
pict_height = 0
|
|
textmem = []
|
|
in_title = True
|
|
|
|
|
|
# ---------------------------------------------------------------
|
|
# begin of table special treatment
|
|
# ---------------------------------------------------------------
|
|
def emit(self, text, tab_state=CELL_TEXT, span=1):
|
|
"""
|
|
Hand over all text but tables to self._backend.write(), (line 1-2).
|
|
In case of tables pass to specal treatment below.
|
|
"""
|
|
if not self.in_table: # all stuff but table
|
|
self._backend.write(text)
|
|
else:
|
|
self.handle_table(text, tab_state, span)
|
|
|
|
|
|
def handle_table(self, text, tab_state, span):
|
|
"""
|
|
Collect tables elements in an adequate cell/row/table structure and
|
|
call for LaTeX width calculations and writing out
|
|
"""
|
|
if tab_state == CELL_BEG:
|
|
# here text is head
|
|
self.textmem = []
|
|
self.curcol_char = get_charform(self.curcol-1)
|
|
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, '', '')
|
|
self.tabrow.cells.append(phantom)
|
|
self.tabcell = Tab_Cell(self.curcol_char, span, text, '')
|
|
elif tab_state == CELL_TEXT:
|
|
self.textmem.append(text)
|
|
elif tab_state == CELL_END: # text == ''
|
|
self.tabcell.content = ''.join(self.textmem).strip()
|
|
|
|
if self.tabcell.content.find('\\centering') != -1:
|
|
self.tabcell.content = self.tabcell.content.replace(
|
|
'\\centering', '')
|
|
self.tabcell.head = re.sub(
|
|
TBLFMT_PAT, '\\1c\\2', self.tabcell.head)
|
|
self.tabrow.cells.append(self.tabcell)
|
|
self.textmem = []
|
|
elif tab_state == ROW_BEG:
|
|
self.tabrow = Tab_Row()
|
|
elif tab_state == ROW_END:
|
|
self.tabrow.addit = text # text: \\hline, \\cline{}
|
|
self.tabrow.tail = ''.join(self.textmem) # \\\\ row-termination
|
|
if self.in_multrow_cell: # cols of rows: convert to rows of cols
|
|
self.repack_row()
|
|
else:
|
|
self.tabmem.rows.append(self.tabrow)
|
|
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)
|
|
elif tab_state == TAB_END: # text: \\end{longtable}
|
|
self.tabmem.tail = text
|
|
|
|
# table completed, calc widths and write out
|
|
self.calc_latex_widths()
|
|
self.write_table()
|
|
|
|
|
|
def repack_row(self):
|
|
"""
|
|
Transpose contents contained in a row of cols of cells
|
|
to rows of cells with corresponding contents.
|
|
Cols of the mult-row-cell are ended by SEPARATION_PAT
|
|
"""
|
|
# if last col empty: delete
|
|
if self.tabrow.cells[-1].content == '':
|
|
del self.tabrow.cells[-1]
|
|
self.numcols -= 1
|
|
|
|
# extract cell.contents
|
|
bare_contents = [cell.content.strip(SEPARATION_PAT).replace(
|
|
'\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])
|
|
cols_equ_len = []
|
|
for mrc in bare_contents:
|
|
for i in range(num_new_rows - len(mrc)):
|
|
mrc.append('')
|
|
cols_equ_len.append(mrc)
|
|
transp_cont = zip(*cols_equ_len)
|
|
|
|
# picts? extract
|
|
first_cell, last_cell = (0, self.numcols)
|
|
if self.pict_in_table:
|
|
if transp_cont[0][-1].startswith('\\grmkpicture'):
|
|
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.pict_in_table = False
|
|
|
|
# new row-col structure
|
|
for row in range(num_new_rows):
|
|
new_row = Tab_Row()
|
|
for i in range(first_cell, last_cell):
|
|
new_cell = Tab_Cell(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)
|
|
new_row.tail = self.tabrow.tail
|
|
new_row.addit = ''
|
|
self.tabmem.rows.append(new_row)
|
|
|
|
self.tabmem.rows[-1].addit = self.tabrow.addit
|
|
self.in_multrow_cell = False
|
|
return
|
|
|
|
|
|
def calc_latex_widths(self):
|
|
"""
|
|
Control width settings in latex table construction
|
|
Evaluations are set up here and passed to LaTeX
|
|
to calculate required and to fix suitable widths.
|
|
??? Can all this be done exclusively in TeX? Don't know how.
|
|
"""
|
|
tabcol_chars = []
|
|
for col_num in range(self.numcols):
|
|
col_char = get_charform(col_num)
|
|
tabcol_chars.append(col_char)
|
|
for row in self.tabmem.rows:
|
|
cell = row.cells[col_num]
|
|
if cell.span == 0:
|
|
continue
|
|
if cell.content.startswith('\\grmkpicture'):
|
|
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')))
|
|
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),
|
|
'}%\n')))
|
|
|
|
self._backend.write(''.join(('\\grcolsfirstfix',
|
|
' {\\grcolbeg', col_char, '}{\\grtempwidth', col_char,
|
|
'}{\\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',
|
|
' {\\grcolbeg', col_char, '}{\\grtempwidth', col_char,
|
|
'}{\\grfinalwidth', col_char, '}{\\grpictreq', col_char,
|
|
'}%\n')))
|
|
|
|
self._backend.write(''.join(('\\grdividelength%\n')))
|
|
for col_char in tabcol_chars:
|
|
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',
|
|
' {\\grcolbeg', col_char, '}{\\grtempwidth', col_char,
|
|
'}{\\grfinalwidth', col_char, '}%\n')))
|
|
|
|
self.multcol_alph_counter = str_incr(MULTCOL_COUNT_BASE)
|
|
for row in self.tabmem.rows:
|
|
for cell in row.cells:
|
|
if cell.span > 1:
|
|
multcol_alph_id = self.multcol_alph_counter.next()
|
|
self._backend.write(''.join(('\\grgetspanwidth{',
|
|
'\\grspanwidth', multcol_alph_id,
|
|
'}{\\grcolbeg', get_charform(get_numform(cell.colchar)-
|
|
cell.span + 1),
|
|
'}{\\grcolbeg', cell.colchar,
|
|
'}{\\grtempwidth', cell.colchar,
|
|
'}%\n')))
|
|
|
|
def write_table(self):
|
|
# Choosing RaggedRight (with hyphenation) in table and
|
|
# provide manually adjusting of column widths
|
|
self._backend.write(''.join((
|
|
'%\n', self.pict,
|
|
'%\n%\n',
|
|
'% ==> Comment out one of the two lines ',
|
|
'by a leading "%" (first position)\n',
|
|
'{ \\RaggedRight% left align with hyphenation in table \n',
|
|
'%{% no left align in table \n%\n',
|
|
'% ==> 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',
|
|
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',
|
|
'\\setlength{\\grtabprepos}{0ex}%\n')))
|
|
self.pict = ''
|
|
self._backend.write(''.join(self.tabmem.head))
|
|
|
|
# special treatment at begin of longtable for heading and
|
|
# closing at top and bottom of table
|
|
# and parts of it at pagebreak separating
|
|
self.multcol_alph_counter = str_incr(MULTCOL_COUNT_BASE)
|
|
splitting_row = self.mk_splitting_row(self.tabmem.rows[FIRST_ROW])
|
|
self.multcol_alph_counter = str_incr(MULTCOL_COUNT_BASE)
|
|
complete_row = self.mk_complete_row(self.tabmem.rows[FIRST_ROW])
|
|
|
|
self._backend.write(splitting_row)
|
|
self._backend.write('\\endhead%\n')
|
|
self._backend.write(splitting_row.replace('{+2ex}', '{-2ex}'))
|
|
self._backend.write('\\endfoot%\n')
|
|
if self.head_line:
|
|
self._backend.write('\\hline%\n')
|
|
self.head_line= False
|
|
else:
|
|
self._backend.write('%\n')
|
|
self._backend.write(complete_row)
|
|
self._backend.write('\\endfirsthead%\n')
|
|
self._backend.write('\\endlastfoot%\n')
|
|
|
|
# hand over subsequent rows
|
|
for row in self.tabmem.rows[SUBSEQ_ROW:]:
|
|
self._backend.write(self.mk_complete_row(row))
|
|
|
|
# close table by '\\end{longtable}', end '{\\RaggedRight' or '{' by '}'
|
|
self._backend.write(''.join((''.join(self.tabmem.tail), '}%\n\n')))
|
|
|
|
def mk_splitting_row(self, row):
|
|
splitting =[]
|
|
add_vdots = '\\grempty'
|
|
for cell in row.cells:
|
|
if cell.span == 0:
|
|
continue
|
|
if (not splitting and
|
|
get_numform(cell.colchar) == self.numcols - 1):
|
|
add_vdots = '\\graddvdots'
|
|
if cell.span == 1:
|
|
cell_width = ''.join(('\\grtempwidth', cell.colchar))
|
|
else:
|
|
cell_width = ''.join(('\\grspanwidth',
|
|
self.multcol_alph_counter.next()))
|
|
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 =[]
|
|
for cell in row.cells:
|
|
if cell.span == 0:
|
|
continue
|
|
elif cell.span == 1:
|
|
cell_width = ''.join(('\\grtempwidth', cell.colchar))
|
|
else:
|
|
cell_width = ''.join(('\\grspanwidth',
|
|
self.multcol_alph_counter.next()))
|
|
complete.append(''.join(('\\grcolpart{%\n ', cell.head,
|
|
'}{%\n', cell_width, '}{%\n ', cell.content, '%\n}%\n')))
|
|
return ''.join((' & '.join(complete), '%\n', row.tail, row.addit))
|
|
|
|
# ---------------------------------------------------------------------
|
|
# end of special table treatment
|
|
# ---------------------------------------------------------------------
|
|
|
|
def page_break(self):
|
|
"Forces a page break, creating a new page"
|
|
self.emit('\\newpage%\n')
|
|
|
|
def open(self, filename):
|
|
"""Opens the specified file, making sure that it has the
|
|
extension of .tex"""
|
|
self._backend = LateXBackend(filename)
|
|
self._backend.open()
|
|
|
|
# Font size control seems to be limited. For now, ignore
|
|
# any style constraints, and use 12pt has the default
|
|
|
|
options = "12pt"
|
|
|
|
if self.paper.get_orientation() == PAPER_LANDSCAPE:
|
|
options = options + ",landscape"
|
|
|
|
# Paper selections are somewhat limited on a stock installation.
|
|
# If the user picks something not listed here, we'll just accept
|
|
# the default of the user's LaTeX installation (usually letter).
|
|
paper_name = self.paper.get_size().get_name().lower()
|
|
if paper_name in ["a4", "a5", "legal", "letter"]:
|
|
options += ',' + paper_name + 'paper'
|
|
|
|
# Use the article template, T1 font encodings, and specify
|
|
# that we should use Latin1 and unicode character encodings.
|
|
self.emit(_LATEX_TEMPLATE_1 % options)
|
|
self.emit(_LATEX_TEMPLATE)
|
|
|
|
self.in_list = False
|
|
self.in_table = False
|
|
self.head_line = False
|
|
|
|
#Establish some local styles for the report
|
|
self.latexstyle = {}
|
|
self.latex_font = {}
|
|
|
|
style_sheet = self.get_style_sheet()
|
|
for style_name in style_sheet.get_paragraph_style_names():
|
|
style = style_sheet.get_paragraph_style(style_name)
|
|
font = style.get_font()
|
|
size = font.get_size()
|
|
|
|
self.latex_font[style_name] = TexFont()
|
|
thisstyle = self.latex_font[style_name]
|
|
|
|
thisstyle.font_beg = ""
|
|
thisstyle.font_end = ""
|
|
# Is there special alignment? (default is left)
|
|
align = style.get_alignment_text()
|
|
if align == "center":
|
|
thisstyle.font_beg += "{\\centering"
|
|
thisstyle.font_end = ''.join(("\n\n}", thisstyle.font_end))
|
|
elif align == "right":
|
|
thisstyle.font_beg += "\\hfill"
|
|
|
|
# Establish font face and shape
|
|
if font.get_type_face() == FONT_SANS_SERIF:
|
|
thisstyle.font_beg += "\\sffamily"
|
|
thisstyle.font_end = "\\rmfamily" + thisstyle.font_end
|
|
if font.get_bold():
|
|
thisstyle.font_beg += "\\bfseries"
|
|
thisstyle.font_end = "\\mdseries" + thisstyle.font_end
|
|
if font.get_italic() or font.get_underline():
|
|
thisstyle.font_beg += "\\itshape"
|
|
thisstyle.font_end = "\\upshape" + thisstyle.font_end
|
|
|
|
# Now determine font size
|
|
fontsize = map_font_size(size)
|
|
if fontsize:
|
|
thisstyle.font_beg += "\\" + fontsize
|
|
thisstyle.font_end += "\\normalsize"
|
|
|
|
thisstyle.font_beg += " "
|
|
thisstyle.font_end += " "
|
|
|
|
left = style.get_left_margin()
|
|
first = style.get_first_indent() + left
|
|
thisstyle.leftIndent = left
|
|
thisstyle.firstLineIndent = first
|
|
self.latexstyle[style_name] = thisstyle
|
|
|
|
|
|
def close(self):
|
|
"""Clean up and close the document"""
|
|
if self.in_list:
|
|
self.emit('\\end{list}\n')
|
|
self.emit('\\end{document}\n')
|
|
self._backend.close()
|
|
|
|
def end_page(self):
|
|
"""Issue a new page command"""
|
|
self.emit('\\newpage')
|
|
|
|
def start_paragraph(self, style_name, leader=None):
|
|
"""Paragraphs handling - A Gramps paragraph is any
|
|
single body of text from a single word to several sentences.
|
|
We assume a linebreak at the end of each paragraph."""
|
|
style_sheet = self.get_style_sheet()
|
|
|
|
style = style_sheet.get_paragraph_style(style_name)
|
|
ltxstyle = self.latexstyle[style_name]
|
|
self.level = style.get_header_level()
|
|
|
|
self.fbeg = ltxstyle.font_beg
|
|
self.fend = ltxstyle.font_end
|
|
|
|
self.indent = ltxstyle.leftIndent
|
|
self.FLindent = ltxstyle.firstLineIndent
|
|
if self.indent == 0:
|
|
self.indent = self.FLindent
|
|
|
|
# For additional vertical space beneath title line(s)
|
|
# i.e. when the first centering ended:
|
|
if self.in_title and ltxstyle.font_beg.find('centering') == -1:
|
|
self.in_title = False
|
|
self._backend.write('\\vspace{5ex}%\n')
|
|
if self.in_table: # paragraph in table indicates: cols of rows
|
|
self.in_multrow_cell = True
|
|
else:
|
|
if leader:
|
|
self._backend.write(''.join(('\\grprepleader{', leader,
|
|
'}%\n')))
|
|
else:
|
|
self._backend.write('\\grprepnoleader%\n')
|
|
|
|
# -------------------------------------------------------------------
|
|
# Gramps presumes 'cm' as units; here '\\grbaseindent' is used
|
|
# as equivalent, set in '_LATEX_TEMPLATE' above to '3em';
|
|
# 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.fix_indent = True
|
|
|
|
if leader is not None and not self.in_list:
|
|
self.in_list = True
|
|
self._backend.write(''.join(('\\grlisthead{', leader,
|
|
'}%\n')))
|
|
|
|
if leader is None:
|
|
self.emit('\n')
|
|
self.emit('%s ' % self.fbeg)
|
|
|
|
def end_paragraph(self):
|
|
"""End the current paragraph"""
|
|
newline = '%\n\n'
|
|
if self.in_list:
|
|
self.in_list = False
|
|
self.emit('\n\\grlisttail%\n')
|
|
newline = ''
|
|
elif self.in_table:
|
|
newline = SEPARATION_PAT
|
|
|
|
self.emit('%s%s' % (self.fend, newline))
|
|
if self.fix_indent:
|
|
self.emit('\\grminpgtail%\n\n')
|
|
self.fix_indent = False
|
|
|
|
if self.pict_width:
|
|
self.pict_width = 0
|
|
self.pict_height = 0
|
|
|
|
def start_bold(self):
|
|
"""Bold face"""
|
|
self.emit('\\textbf{')
|
|
|
|
def end_bold(self):
|
|
"""End bold face"""
|
|
self.emit('}')
|
|
|
|
def start_superscript(self):
|
|
self.emit('\\textsuperscript{')
|
|
|
|
def end_superscript(self):
|
|
self.emit('}')
|
|
|
|
def start_table(self, name,style_name):
|
|
"""Begin new table"""
|
|
self.in_table = True
|
|
self.currow = 0
|
|
|
|
# We need to know a priori how many columns are in this table
|
|
styles = self.get_style_sheet()
|
|
self.tblstyle = styles.get_table_style(style_name)
|
|
self.numcols = self.tblstyle.get_columns()
|
|
|
|
tblfmt = '*{%d}{l}' % self.numcols
|
|
self.emit('\\begin{longtable}[l]{%s}\n' % (tblfmt), TAB_BEG)
|
|
|
|
def end_table(self):
|
|
"""Close the table environment"""
|
|
self.emit('%\n\\end{longtable}%\n', TAB_END)
|
|
self.in_table = False
|
|
|
|
def start_row(self):
|
|
"""Begin a new row"""
|
|
self.emit('', ROW_BEG)
|
|
# doline/skipfirst are flags for adding hor. rules
|
|
self.doline = False
|
|
self.skipfirst = False
|
|
self.curcol = 0
|
|
self.currow = self.currow + 1
|
|
|
|
def end_row(self):
|
|
"""End the row (new line)"""
|
|
self.emit('\\\\ ')
|
|
if self.doline:
|
|
if self.skipfirst:
|
|
self.emit(''.join((('\\cline{2-%d}' %
|
|
self.numcols), '%\n')), ROW_END)
|
|
else:
|
|
self.emit('\\hline %\n', ROW_END)
|
|
else:
|
|
self.emit('%\n', ROW_END)
|
|
self.emit('%\n')
|
|
|
|
def start_cell(self, style_name, span=1):
|
|
"""Add an entry to the table.
|
|
We always place our data inside braces
|
|
for safety of formatting."""
|
|
self.colspan = span
|
|
self.curcol = self.curcol + self.colspan
|
|
|
|
styles = self.get_style_sheet()
|
|
self.cstyle = styles.get_cell_style(style_name)
|
|
|
|
# ------------------------------------------------------------------
|
|
# begin special modification for boolean values
|
|
# 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.llist not needed any longer.
|
|
# now column widths are arranged in self.calc_latex_widths()
|
|
# serving for fitting of cell contents at any column position.
|
|
# self.llist = 1 == self.cstyle.get_longlist()
|
|
|
|
cellfmt = "l"
|
|
# Account for vertical rules
|
|
if self.lborder:
|
|
cellfmt = '|' + cellfmt
|
|
if self.rborder:
|
|
cellfmt = cellfmt + '|'
|
|
|
|
# and Horizontal rules
|
|
if self.bborder:
|
|
self.doline = True
|
|
elif self.curcol == 1:
|
|
self.skipfirst = True
|
|
if self.tborder:
|
|
self.head_line = True
|
|
# ------------------------------------------------------------------
|
|
# end special modification for boolean values
|
|
# ------------------------------------------------------------------
|
|
|
|
self.emit('\\multicolumn{%d}{%s}' % (span, cellfmt), CELL_BEG, span)
|
|
|
|
|
|
def end_cell(self):
|
|
"""Prepares for next cell"""
|
|
self.emit('', CELL_END)
|
|
|
|
|
|
def add_media_object(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'))
|
|
if infile != outfile:
|
|
try:
|
|
curr_img = Image.open(infile)
|
|
curr_img.save(outfile)
|
|
px, py = curr_img.size
|
|
if py > px:
|
|
y = y*py/px
|
|
except IOError:
|
|
self.emit(''.join(('%\n *** Error: cannot convert ', infile,
|
|
'\n *** to ', outfile,
|
|
'%\n')))
|
|
if self.in_table:
|
|
self.pict_in_table = True
|
|
|
|
self.emit(''.join(('\\grmkpicture{', outfile, '}{', repr(x), '}{',
|
|
repr(y), '}{', pictname, '}%\n')))
|
|
self.pict_width = x
|
|
self.pict_height = y
|
|
|
|
|
|
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:
|
|
text = re.sub(URL_PATTERN, _CLICKABLE, text)
|
|
|
|
#hard coded replace of the underline used for missing names/data
|
|
text = text.replace('\\_'*13, '\\underline{\hspace{3\\grbaseindent}}')
|
|
self.emit(text + ' ')
|
|
|
|
|
|
def write_styled_note(self, styledtext, format, style_name,
|
|
contains_html=False, links=False):
|
|
"""
|
|
Convenience function to write a styledtext to the latex doc.
|
|
styledtext : assumed a StyledText object to write
|
|
format : = 0 : Flowed, = 1 : Preformatted
|
|
style_name : name of the style to use for default presentation
|
|
contains_html: bool, the backend should not check if html is present.
|
|
If contains_html=True, then the textdoc is free to handle that in
|
|
some way. Eg, a textdoc could remove all tags, or could make sure
|
|
a link is clickable. self ignores notes that contain html
|
|
links: bool, make URLs clickable if True
|
|
"""
|
|
if contains_html:
|
|
return
|
|
text = str(styledtext)
|
|
|
|
s_tags = styledtext.get_tags()
|
|
if format:
|
|
#preformatted, use different escape function
|
|
self._backend.setescape(True)
|
|
|
|
markuptext = self._backend.add_markup_from_styled(text, s_tags)
|
|
|
|
if links == True:
|
|
markuptext = re.sub(URL_PATTERN, _CLICKABLE, markuptext)
|
|
markuptext = self._backend.add_markup_from_styled(text, s_tags)
|
|
|
|
#there is a problem if we write out a note in a table.
|
|
# ..................
|
|
# now solved by postprocessing in self.calc_latex_widths()
|
|
# by explicitely setting suitable width for all columns.
|
|
#
|
|
if format:
|
|
self.start_paragraph(style_name)
|
|
self.emit(markuptext)
|
|
self.end_paragraph()
|
|
#preformatted finished, go back to normal escape function
|
|
self._backend.setescape(False)
|
|
else:
|
|
for line in markuptext.split('%\n%\n '):
|
|
self.start_paragraph(style_name)
|
|
for realline in line.split('\n'):
|
|
self.emit(realline)
|
|
self.emit("~\\newline \n")
|
|
self.end_paragraph()
|
|
|