5326: Add Alphabetical Index and Table of Contents generation for pdf reports

svn: r18842
This commit is contained in:
Nick Hall
2012-02-10 14:53:58 +00:00
parent 249c5dc46b
commit 3767c1d1e4
25 changed files with 598 additions and 41 deletions

View File

@@ -72,7 +72,7 @@ import Utils
import ListModel
import Errors
from gui.pluginmanager import GuiPluginManager
from gen.plug.docgen import StyleSheet, StyleSheetList, PaperStyle
from gen.plug.docgen import StyleSheet, StyleSheetList, PaperStyle, IndexOptions
from QuestionDialog import WarningDialog, ErrorDialog
from gen.plug.menu import PersonOption, FilterOption, FamilyOption
import ManagedWindow
@@ -85,6 +85,7 @@ from gen.plug.report import CATEGORY_BOOK, book_categories
from gui.plug.report._reportdialog import ReportDialog
from gui.plug.report._docreportdialog import DocReportDialog
from gen.plug.report._options import ReportOptions
from gen.plug.report.toc_index import add_toc_index_styles
from cli.plug import CommandLineReport
import cli.user
@@ -1203,6 +1204,7 @@ class BookReportDialog(DocReportDialog):
default_style = StyleSheet()
make_default_style = item.option_class.make_default_style
make_default_style(default_style)
add_toc_index_styles(default_style)
# Read all style sheets available for this item
style_file = item.option_class.handler.get_stylesheet_savefile()
@@ -1255,7 +1257,14 @@ class BookReportDialog(DocReportDialog):
def make_document(self):
"""Create a document of the type requested by the user."""
pstyle = self.paper_frame.get_paper_style()
self.doc = self.format(self.selected_style, pstyle)
if self.format_menu.get_active_plugin().get_index_support():
index_opts = IndexOptions(self.toc.get_active(),
self.index.get_active())
else:
index_opts = None
self.doc = self.format(self.selected_style, pstyle, index_opts)
user = gui.user.User()
self.rptlist = []
for item in self.book.get_item_list():
@@ -1319,6 +1328,7 @@ def cl_report(database, name, category, options_str_dict):
default_style = StyleSheet()
make_default_style = item.option_class.make_default_style
make_default_style(default_style)
add_toc_index_styles(default_style)
# Read all style sheets available for this item
style_file = item.option_class.handler.get_stylesheet_savefile()

View File

@@ -130,8 +130,8 @@ def reformat_para(para='',left=0,right=72,just=LEFT,right_pad=0,first=0):
#------------------------------------------------------------------------
class AsciiDoc(BaseDoc,TextDoc):
def __init__(self, styles, type):
BaseDoc.__init__(self, styles, type)
def __init__(self, styles, type, index_opts):
BaseDoc.__init__(self, styles, type, index_opts)
self.__note_format = False
#--------------------------------------------------------------------

View File

@@ -93,8 +93,8 @@ class HtmlDoc(BaseDoc, TextDoc):
Fontface is removed. Size, italic, bold, margins, borders are retained
"""
def __init__(self, styles, paper_style):
BaseDoc.__init__(self, styles, None)
def __init__(self, styles, paper_style, index_opts):
BaseDoc.__init__(self, styles, None, index_opts)
self.style_declaration = ''
self.htmllist = []
self._backend = None

View File

@@ -433,11 +433,11 @@ class ODFDoc(BaseDoc, TextDoc, DrawDoc):
The ODF document class
"""
def __init__(self, styles, ftype):
def __init__(self, styles, ftype, index_opts):
"""
Class init
"""
BaseDoc.__init__(self, styles, ftype)
BaseDoc.__init__(self, styles, ftype, index_opts)
self.media_list = []
self.init_called = False
self.cntnt = None

View File

@@ -56,8 +56,8 @@ def coords(grp):
#-------------------------------------------------------------------------
class PSDrawDoc(BaseDoc, DrawDoc):
def __init__(self, styles, type):
BaseDoc.__init__(self, styles, type)
def __init__(self, styles, type, index_opts):
BaseDoc.__init__(self, styles, type, index_opts)
self.file = None
self.filename = None
self.level = 0

View File

@@ -38,6 +38,8 @@ import sys
#
#------------------------------------------------------------------------
import libcairodoc
from gen.plug.docgen import INDEX_TYPE_ALP, INDEX_TYPE_TOC
from gen.plug.report.toc_index import write_toc, write_index
import Errors
#------------------------------------------------------------------------
@@ -85,6 +87,10 @@ class PdfDoc(libcairodoc.CairoDoc):
left_margin = self.paper.get_left_margin() * DPI / 2.54
top_margin = self.paper.get_top_margin() * DPI / 2.54
# get index options
include_toc = self.index_options.get_include_toc()
include_index = self.index_options.get_include_index()
# create cairo context and pango layout
filename = self._backend.filename.encode(sys.getfilesystemencoding())
try:
@@ -109,11 +115,39 @@ class PdfDoc(libcairodoc.CairoDoc):
cr.update_context(pango_context)
# paginate the document
finished = self.paginate(layout, page_width, page_height, DPI, DPI)
while not finished:
finished = self.paginate(layout, page_width, page_height, DPI, DPI)
self.paginate_document(layout, page_width, page_height, DPI, DPI)
body_pages = self._pages
# build the table of contents and alphabetical index
toc = []
index = {}
for page_nr, page in enumerate(body_pages):
for mark in page.get_marks():
if mark.type == INDEX_TYPE_ALP:
if mark.key in index:
if page_nr not in index[mark.key]:
index[mark.key].append(page_nr)
else:
index[mark.key] = [page_nr]
elif mark.type == INDEX_TYPE_TOC:
toc.append([mark, page_nr])
# paginate the table of contents
if include_toc:
toc_pages = self.__generate_toc(layout, page_width, page_height,
toc)
else:
toc_pages = []
# paginate the index
if include_index:
index_pages = self.__generate_index(layout, page_width, page_height,
index, len(toc_pages))
else:
index_pages = []
# render the pages
self._pages = toc_pages + body_pages + index_pages
for page_nr in range(len(self._pages)):
cr.save()
cr.translate(left_margin, top_margin)
@@ -130,3 +164,37 @@ class PdfDoc(libcairodoc.CairoDoc):
# if we don't restore the resolution.
fontmap.set_resolution(saved_resolution)
def __generate_toc(self, layout, page_width, page_height, toc):
'''
Generate the table of contents
'''
self._doc = libcairodoc.GtkDocDocument()
self._active_element = self._doc
self._pages = []
write_toc(toc, self, 1) # assume single page
self.paginate_document(layout, page_width, page_height, DPI, DPI)
toc_pages = self._pages
# re-generate if table spans more than one page
offset = len(toc_pages)
if offset != 1:
self._doc = libcairodoc.GtkDocDocument()
self._active_element = self._doc
self._pages = []
write_toc(toc, self, offset)
self.paginate_document(layout, page_width, page_height, DPI, DPI)
toc_pages = self._pages
return self._pages
def __generate_index(self, layout, page_width, page_height, index, offset):
'''
Generate the index
'''
self._doc = libcairodoc.GtkDocDocument()
self._active_element = self._doc
self._pages = []
write_index(index, self, offset)
self.paginate_document(layout, page_width, page_height, DPI, DPI)
return self._pages

View File

@@ -49,8 +49,8 @@ import Errors
#-------------------------------------------------------------------------
class SvgDrawDoc(BaseDoc, DrawDoc):
def __init__(self, styles, type):
BaseDoc.__init__(self, styles, type)
def __init__(self, styles, type, index_opts):
BaseDoc.__init__(self, styles, type, index_opts)
self.f = None
self.filename = None
self.level = 0

View File

@@ -38,6 +38,7 @@ plg.ptype = DOCGEN
plg.basedocclass = 'AsciiDoc'
plg.paper = True
plg.style = True
plg.index = False
plg.extension = "txt"
#------------------------------------------------------------------------
@@ -58,6 +59,7 @@ plg.ptype = DOCGEN
plg.basedocclass = 'GtkPrint'
plg.paper = True
plg.style = True
plg.index = False
plg.extension = ""
#------------------------------------------------------------------------
@@ -78,6 +80,7 @@ plg.ptype = DOCGEN
plg.basedocclass = 'HtmlDoc'
plg.paper = False
plg.style = True
plg.index = False
plg.extension = "html"
#------------------------------------------------------------------------
@@ -98,6 +101,7 @@ plg.ptype = DOCGEN
plg.basedocclass = 'LaTeXDoc'
plg.paper = True
plg.style = False
plg.index = False
plg.extension = "tex"
#------------------------------------------------------------------------
@@ -119,6 +123,7 @@ plg.ptype = DOCGEN
plg.basedocclass = 'ODFDoc'
plg.paper = True
plg.style = True
plg.index = False
plg.extension = "odt"
#------------------------------------------------------------------------
@@ -139,6 +144,7 @@ plg.ptype = DOCGEN
plg.basedocclass = 'PdfDoc'
plg.paper = True
plg.style = True
plg.index = True
plg.extension = "pdf"
#------------------------------------------------------------------------
@@ -159,6 +165,7 @@ plg.ptype = DOCGEN
plg.basedocclass = 'PSDrawDoc'
plg.paper = True
plg.style = True
plg.index = False
plg.extension = "ps"
#------------------------------------------------------------------------
@@ -179,6 +186,7 @@ plg.ptype = DOCGEN
plg.basedocclass = 'RTFDoc'
plg.paper = True
plg.style = True
plg.index = False
plg.extension = "rtf"
#------------------------------------------------------------------------
@@ -200,4 +208,5 @@ plg.ptype = DOCGEN
plg.basedocclass = 'SvgDrawDoc'
plg.paper = True
plg.style = True
plg.index = False
plg.extension = "svg"

View File

@@ -35,6 +35,7 @@
#------------------------------------------------------------------------
from gen.ggettext import gettext as _
from math import radians
import re
#------------------------------------------------------------------------
#
@@ -174,6 +175,18 @@ def tabstops_to_tabarray(tab_stops, dpi):
return tab_array
def raw_length(s):
"""
Return the length of the raw string after all pango markup has been removed.
"""
s = re.sub('<.*?>', '', s)
s = s.replace('&amp;', '&')
s = s.replace('&lt;', '<')
s = s.replace('&gt;', '>')
s = s.replace('&quot;', '"')
s = s.replace('&apos;', "'")
return len(s)
###------------------------------------------------------------------------
###
### Table row style
@@ -321,6 +334,14 @@ class GtkDocBaseElement(object):
"""
return self._children
def get_marks(self):
"""Get the list of index marks for this element.
"""
marks = []
for child in self._children:
marks.extend(child.get_marks())
return marks
def divide(self, layout, width, height, dpi_x, dpi_y):
"""Divide the element into two depending on available space.
@@ -410,12 +431,32 @@ class GtkDocParagraph(GtkDocBaseElement):
self._plaintext = None
self._attrlist = None
self._marklist = []
def add_text(self, text):
if self._plaintext is not None:
raise PluginError('CairoDoc: text is already parsed.'
' You cannot add text anymore')
self._text = self._text + text
def add_mark(self, mark):
"""
Add an index mark to this paragraph
"""
self._marklist.append((mark, raw_length(self._text)))
def get_marks(self):
"""
Return a list of index marks for this paragraph
"""
return [elem[0] for elem in self._marklist]
def __set_marklist(self, marklist):
"""
Internal method to allow for splitting of paragraphs
"""
self._marklist = marklist
def __set_plaintext(self, plaintext):
"""
Internal method to allow for splitting of paragraphs
@@ -562,7 +603,18 @@ class GtkDocParagraph(GtkDocBaseElement):
# then update the first one
self.__set_plaintext(self._plaintext.encode('utf-8')[:index])
self._style.set_bottom_margin(0)
# split the list of index marks
para1 = []
para2 = []
for mark, position in self._marklist:
if position < index:
para1.append((mark, position))
else:
para2.append((mark, position - index))
self.__set_marklist(para1)
new_paragraph.__set_marklist(para2)
paragraph_height = endheight - startheight + spacing + t_margin + 2 * v_padding
return (self, new_paragraph), paragraph_height
@@ -1374,6 +1426,10 @@ links (like ODF) and write PDF from that format.
# The markup in the note editor is not in the text so is not
# considered. It must be added by pango too
text = self._backend.ESCAPE_FUNC()(text)
if mark:
self._active_element.add_mark(mark)
self._active_element.add_text(text)
def write_text(self, text, mark=None, links=False):
@@ -1515,6 +1571,12 @@ links (like ODF) and write PDF from that format.
"""
raise NotImplementedError
def paginate_document(self, layout, page_width, page_height, dpi_x, dpi_y):
"""Paginate the entire document.
"""
while not self.paginate(layout, page_width, page_height, dpi_x, dpi_y):
pass
def paginate(self, layout, page_width, page_height, dpi_x, dpi_y):
"""Paginate the meta document in chunks.