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

@ -75,6 +75,7 @@ src/gen/plug/docgen/graphdoc.py
src/gen/plug/report/_constants.py
src/gen/plug/report/_paper.py
src/gen/plug/report/endnotes.py
src/gen/plug/report/toc_index.py
src/gen/plug/report/utils.py
# gen proxy API

View File

@ -187,6 +187,7 @@ src/gen/plug/docgen/fontscale.py
src/gen/plug/docgen/fontstyle.py
src/gen/plug/docgen/graphdoc.py
src/gen/plug/docgen/graphicstyle.py
src/gen/plug/docgen/indexoptions.py
src/gen/plug/docgen/paperstyle.py
src/gen/plug/docgen/paragraphstyle.py
src/gen/plug/docgen/stylesheet.py

View File

@ -48,8 +48,9 @@ log = logging.getLogger(".")
#-------------------------------------------------------------------------
import Utils
from gen.plug import BasePluginManager
from gen.plug.docgen import (StyleSheet, StyleSheetList, PaperStyle,
PAPER_PORTRAIT, PAPER_LANDSCAPE, graphdoc)
from gen.plug.docgen import (StyleSheet, StyleSheetList, PaperStyle,
PAPER_PORTRAIT, PAPER_LANDSCAPE, graphdoc,
IndexOptions)
from gen.plug.menu import (FamilyOption, PersonOption, NoteOption,
MediaOption, PersonListOption, NumberOption,
BooleanOption, DestinationOption, StringOption,
@ -59,6 +60,7 @@ from Errors import ReportError
from gen.plug.report import (CATEGORY_TEXT, CATEGORY_DRAW, CATEGORY_BOOK,
CATEGORY_GRAPHVIZ, CATEGORY_CODE)
from gen.plug.report._paper import paper_sizes
from gen.plug.report.toc_index import add_toc_index_styles
import const
import DbState
from cli.grampscli import CLIManager
@ -256,6 +258,8 @@ class CommandLineReport(object):
'papermt' : self.option_class.handler.get_margins()[2],
'papermb' : self.option_class.handler.get_margins()[3],
'css' : self.option_class.handler.get_css_filename(),
'toc' : self.option_class.handler.get_toc(),
'index' : self.option_class.handler.get_index(),
}
self.options_help = {
@ -270,6 +274,10 @@ class CommandLineReport(object):
'papermb' : ["=num", "Bottom paper margin", "Size in cm"],
'css' : ["=css filename", "CSS filename to use, html format"
" only", ""],
'toc' : ["=bool", "Include table of contents.",
["False", "True"]],
'index' : ["=bool", "Include alphabetical index.",
["False", "True"]],
}
if noopt:
@ -311,6 +319,7 @@ class CommandLineReport(object):
if self.category in (CATEGORY_TEXT, CATEGORY_DRAW):
default_style = StyleSheet()
self.option_class.make_default_style(default_style)
add_toc_index_styles(default_style)
# Read all style sheets available for this item
style_file = self.option_class.handler.get_stylesheet_savefile()
@ -498,9 +507,13 @@ class CommandLineReport(object):
self.margint = self.options_dict['papermt']
self.marginb = self.options_dict['papermb']
self.toc = self.options_dict['toc']
self.index = self.options_dict['index']
if self.category in (CATEGORY_TEXT, CATEGORY_DRAW):
default_style = StyleSheet()
self.option_class.make_default_style(default_style)
add_toc_index_styles(default_style)
# Read all style sheets available for this item
style_file = self.option_class.handler.get_stylesheet_savefile()
@ -573,12 +586,14 @@ def cl_report(database, name, category, report_class, options_class,
clr.option_class.handler.doc = clr.format(
clr.selected_style,
PaperStyle(clr.paper,clr.orien,clr.marginl,
clr.marginr,clr.margint,clr.marginb))
clr.marginr,clr.margint,clr.marginb),
IndexOptions(clr.toc, clr.index))
elif category == CATEGORY_GRAPHVIZ:
clr.option_class.handler.doc = clr.format(
clr.option_class,
PaperStyle(clr.paper,clr.orien,clr.marginl,
clr.marginr,clr.margint,clr.marginb))
clr.marginr,clr.margint,clr.marginb),
IndexOptions(clr.toc, clr.index))
if clr.css_filename is not None and \
hasattr(clr.option_class.handler.doc, 'set_css_filename'):
clr.option_class.handler.doc.set_css_filename(clr.css_filename)

View File

@ -30,7 +30,8 @@ class DocGenPlugin(Plugin):
"""
This class represents a plugin for generating documents from Gramps
"""
def __init__(self, name, description, basedoc, paper, style, extension):
def __init__(self, name, description, basedoc, paper, style, index,
extension):
"""
@param name: A friendly name to call this plugin.
Example: "Plain Text"
@ -47,6 +48,10 @@ class DocGenPlugin(Plugin):
@param style: Indicates whether the plugin uses styles or not.
True = use styles; False = do not use styles
@type style: bool
@param index: Indicates whether the plugin supports an Alphabetical
Index and Table of Contents or not.
True = supports indexing; False = does not support indexing
@type index: bool
@param extension: The extension for the output file.
Example: "txt"
@type extension: str
@ -56,6 +61,7 @@ class DocGenPlugin(Plugin):
self.__basedoc = basedoc
self.__paper = paper
self.__style = style
self.__index = index
self.__extension = extension
def get_basedoc(self):
@ -82,6 +88,14 @@ class DocGenPlugin(Plugin):
"""
return self.__style
def get_index_support(self):
"""
Get the index flag for this plugin.
@return: bool - True = index support; False = no index support
"""
return self.__index
def get_extension(self):
"""
Get the file extension for the output file.
@ -106,4 +120,4 @@ class DocGenPlugin(Plugin):
@return: bool: True if DrawDoc is supported; False if DrawDoc is not
supported.
"""
return bool(issubclass(self.__basedoc, DrawDoc))
return bool(issubclass(self.__basedoc, DrawDoc))

View File

@ -533,6 +533,7 @@ class BasePluginManager(object):
basedoc = getattr(mod, pdata.basedocclass),
paper = pdata.paper,
style = pdata.style,
index = pdata.index,
extension = pdata.extension )
self.__docgen_plugins.append(dgp)

View File

@ -268,6 +268,9 @@ class PluginData(object):
bool, Indicates whether the plugin uses paper or not, default=True
.. attribute :: style
bool, Indicates whether the plugin uses styles or not, default=True
.. attribute :: index
bool, Indicates whether the plugin supports an Alphabetical Index and
Table of Contents or not, default=False
Attribute for DOCGEN, EXPORT plugins
.. attribute :: extension
@ -376,6 +379,7 @@ class PluginData(object):
self._basedocclass = None
self._paper = True
self._style = True
self._index = False
self._extension = ''
#QUICKREPORT attr
self._runfunc = None
@ -703,6 +707,16 @@ class PluginData(object):
def _get_style(self):
return self._style
def _set_index(self, index):
if not self._ptype == DOCGEN:
raise ValueError, 'index may only be set for DOCGEN plugins'
if not isinstance(index, bool):
raise ValueError, 'Plugin must have index=True or False'
self._index = index
def _get_index(self):
return self._index
def _set_extension(self, extension):
if not (self._ptype == DOCGEN or self._ptype == EXPORT
or self._ptype == IMPORT):
@ -715,7 +729,8 @@ class PluginData(object):
basedocclass = property(_get_basedocclass, _set_basedocclass)
paper = property(_get_paper, _set_paper)
style = property(_get_style, _set_style)
style = property(_get_style, _set_style)
index = property(_get_index, _set_index)
extension = property(_get_extension, _set_extension)
#QUICKREPORT attributes

View File

@ -14,6 +14,7 @@ pkgdata_PYTHON = \
fontstyle.py \
graphdoc.py \
graphicstyle.py \
indexoptions.py \
paperstyle.py \
paragraphstyle.py \
stylesheet.py \

View File

@ -29,6 +29,7 @@ A docgen plugin should fully implement this api for TextDoc or DrawDoc
from basedoc import BaseDoc
from paperstyle import PaperSize, PaperStyle, PAPER_PORTRAIT, PAPER_LANDSCAPE
from indexoptions import IndexOptions
from fontstyle import FontStyle, FONT_SANS_SERIF, FONT_SERIF, FONT_MONOSPACE
from paragraphstyle import ParagraphStyle, PARA_ALIGN_CENTER, PARA_ALIGN_LEFT,\
PARA_ALIGN_RIGHT, PARA_ALIGN_JUSTIFY

View File

@ -61,7 +61,7 @@ class BaseDoc(object):
such as OpenOffice, AbiWord, and LaTeX are derived from this base
class, providing a common interface to all document generators.
"""
def __init__(self, styles, paper_style):
def __init__(self, styles, paper_style, index_options):
"""
Create a BaseDoc instance, which provides a document generation
interface. This class should never be instantiated directly, but
@ -71,8 +71,13 @@ class BaseDoc(object):
@param paper_style: PaperStyle instance containing information about
the paper. If set to None, then the document is not a page
oriented document (e.g. HTML)
@param index_options: IndexOptions instance containing information
about how an alphabetical index and table of contents should be
generated. If set to None, then the document is does not support
indexing (e.g. HTML)
"""
self.paper = paper_style
self.index_options = index_options
self._style_sheet = styles
self._creator = ""
self.init_called = False

View File

@ -358,8 +358,8 @@ class GVDocBase(BaseDoc, GVDoc):
inherit from this class will only need to implement the close function.
The close function will generate the actual file of the appropriate type.
"""
def __init__(self, options, paper_style):
BaseDoc.__init__(self, None, paper_style)
def __init__(self, options, paper_style, index_opts):
BaseDoc.__init__(self, None, paper_style, index_opts)
self._filename = None
self._dot = StringIO()

View File

@ -0,0 +1,45 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2012 Nick Hall
#
# 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$
#------------------------------------------------------------------------
#
# IndexOptions
#
#------------------------------------------------------------------------
class IndexOptions(object):
"""
Define the options for the alphabetical index and table of contents.
"""
def __init__(self, include_toc, include_index):
self.__include_toc = include_toc
self.__include_index = include_index
def get_include_toc(self):
"""
Return a boolean indicating if a table of contents should be included.
"""
return self.__include_toc
def get_include_index(self):
"""
Return a boolean indicating if an alphabetical index should be included.
"""
return self.__include_index

View File

@ -7,10 +7,11 @@ pkgdata_PYTHON = \
__init__.py\
_bibliography.py\
_constants.py\
_options.py\
_options.py\
_paper.py\
_reportbase.py\
endnotes.py\
toc_index.py\
utils.py
pkgpyexecdir = @pkgpyexecdir@/gen/plug/report

View File

@ -83,6 +83,8 @@ class OptionList(_options.OptionList):
self.margins = [2.54, 2.54, 2.54, 2.54]
self.format_name = None
self.css_filename = None
self.toc = None
self.index = None
def set_style_name(self, style_name):
"""
@ -234,6 +236,38 @@ class OptionList(_options.OptionList):
"""
return self.format_name
def set_toc(self, toc):
"""
Set the template name for the OptionList.
@param template_name: name of the template to set.
@type template_name: str
"""
self.toc = toc
def get_toc(self):
"""
Return the template name of the OptionList.
@returns: template name
@rtype: str
"""
return self.toc
def set_index(self, index):
"""
Set the template name for the OptionList.
@param template_name: name of the template to set.
@type template_name: str
"""
self.index = index
def get_index(self):
"""
Return the template name of the OptionList.
@returns: template name
@rtype: str
"""
return self.index
#-------------------------------------------------------------------------
#
# Collection of option lists
@ -256,6 +290,8 @@ class OptionListCollection(_options.OptionListCollection):
self.default_custom_paper_size = [29.7, 21.0]
self.default_margins = [2.54, 2.54, 2.54, 2.54]
self.default_format_name = 'print'
self.default_toc = False
self.default_index = False
self.last_paper_metric = self.default_paper_metric
self.last_paper_name = self.default_paper_name
@ -264,6 +300,8 @@ class OptionListCollection(_options.OptionListCollection):
self.last_margins = copy.copy(self.default_margins)
self.last_css_filename = self.default_css_filename
self.last_format_name = self.default_format_name
self.last_toc = self.default_toc
self.last_index = self.default_index
self.option_list_map = {}
def set_last_paper_metric(self, paper_metric):
@ -398,6 +436,34 @@ class OptionListCollection(_options.OptionListCollection):
"""
return self.last_format_name
def set_last_toc(self, toc):
"""
Set the last format used for the any report in this collection.
format_name: name of the format to set.
"""
self.last_toc = toc
def get_last_toc(self):
"""
Return the last format used for the any report in this collection.
"""
return self.last_toc
def set_last_index(self, index):
"""
Set the last format used for the any report in this collection.
format_name: name of the format to set.
"""
self.last_index = index
def get_last_index(self):
"""
Return the last format used for the any report in this collection.
"""
return self.last_index
def write_common(self, f):
f.write('<last-common>\n')
if self.get_last_paper_metric() != self.default_paper_metric:
@ -413,6 +479,10 @@ class OptionListCollection(_options.OptionListCollection):
f.write(' <format name="%s"/>\n' % escxml(self.get_last_format_name()) )
if self.get_last_orientation() != self.default_orientation:
f.write(' <orientation value="%d"/>\n' % self.get_last_orientation() )
if self.get_last_toc() != self.default_toc:
f.write(' <toc include="%d"/>\n' % self.get_last_toc())
if self.get_last_index() != self.default_index:
f.write(' <index include="%d"/>\n' % self.get_last_index())
f.write('</last-common>\n')
def write_module_common(self, f, option_list):
@ -441,6 +511,13 @@ class OptionListCollection(_options.OptionListCollection):
for pos in range(len(margins)):
f.write(' <margin number="%s" value="%f"/>\n' %
(pos, margins[pos]))
if option_list.get_format_name() == 'pdf':
if option_list.get_toc():
f.write(' <toc include="%s"/>\n' %
'True' if option_list.get_toc() else False)
if option_list.get_index():
f.write(' <index include="%s"/>\n' %
'True' if option_list.get_index() else False)
if option_list.get_style_name():
f.write(' <style name="%s"/>\n' %
@ -522,13 +599,22 @@ class OptionParser(_options.OptionParser):
self.collection.set_last_custom_paper_size([width, height])
else:
self.option_list.set_custom_paper_size([width, height])
elif tag == "margin":
pos, value = int(attrs['number']), float(attrs['value'])
if self.common:
self.collection.set_last_margin(pos, value)
else:
self.option_list.set_margin(pos, value)
elif tag == "toc":
if self.common:
self.collection.set_last_toc(attrs['include'] == 'True')
else:
self.option_list.set_toc(attrs['include'] == 'True')
elif tag == "index":
if self.common:
self.collection.set_last_index(attrs['include'] == 'True')
else:
self.option_list.set_index(attrs['include'] == 'True')
else:
# Tag is not report-specific, so we let the base class handle it.
_options.OptionParser.startElement(self, tag, attrs)
@ -597,6 +683,8 @@ class OptionHandler(_options.OptionHandler):
self.css_filename = self.option_list_collection.get_last_css_filename()
self.margins = self.option_list_collection.get_last_margins()
self.format_name = self.option_list_collection.get_last_format_name()
self.toc = self.option_list_collection.get_last_toc()
self.index = self.option_list_collection.get_last_index()
def set_common_options(self):
if self.saved_option_list.get_style_name():
@ -615,6 +703,10 @@ class OptionHandler(_options.OptionHandler):
self.paper_name = self.saved_option_list.get_paper_name()
if self.saved_option_list.get_format_name():
self.format_name = self.saved_option_list.get_format_name()
if self.saved_option_list.get_toc():
self.toc = self.saved_option_list.get_toc()
if self.saved_option_list.get_index():
self.index = self.saved_option_list.get_index()
def save_options(self):
"""
@ -643,6 +735,8 @@ class OptionHandler(_options.OptionHandler):
self.saved_option_list.set_paper_name(self.paper_name)
self.saved_option_list.set_css_filename(self.css_filename)
self.saved_option_list.set_format_name(self.format_name)
self.saved_option_list.set_toc(self.toc)
self.saved_option_list.set_index(self.index)
self.option_list_collection.set_option_list(self.module_name,
self.saved_option_list)
@ -654,6 +748,8 @@ class OptionHandler(_options.OptionHandler):
self.option_list_collection.set_last_paper_name(self.paper_name)
self.option_list_collection.set_last_css_filename(self.css_filename)
self.option_list_collection.set_last_format_name(self.format_name)
self.option_list_collection.set_last_toc(self.toc)
self.option_list_collection.set_last_index(self.index)
def get_stylesheet_savefile(self):
"""Where to save user defined styles for this report."""
@ -720,6 +816,18 @@ class OptionHandler(_options.OptionHandler):
def set_margins(self,margins):
self.margins = copy.copy(margins)
def get_toc(self):
return copy.copy(self.toc)
def set_toc(self, toc):
self.toc = copy.copy(toc)
def get_index(self):
return copy.copy(self.index)
def set_index(self, index):
self.index = copy.copy(index)
#------------------------------------------------------------------------
#
# Base Options class

View File

@ -0,0 +1,151 @@
#
# Gramps - a GTK+/GNOME based genealogy program
#
# Copyright (C) 2011 Nick Hall
#
# 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$
"""
Provide utilities for generating a table of contents and alphabetical index in
text reports.
"""
from gen.plug.docgen import (FontStyle, ParagraphStyle, TableStyle,
TableCellStyle, FONT_SANS_SERIF)
from gen.ggettext import gettext as _
def add_toc_index_styles(style_sheet):
"""
Add paragraph styles to a style sheet to be used for displaying a table of
contents and alphabetical index.
"""
font = FontStyle()
font.set(face=FONT_SANS_SERIF, size=14)
para = ParagraphStyle()
para.set_font(font)
para.set_bottom_margin(0.25)
para.set_description(_('The style used for the TOC title.'))
style_sheet.add_paragraph_style("TOC-Title", para)
font = FontStyle()
font.set(face=FONT_SANS_SERIF, size=14)
para = ParagraphStyle()
para.set_font(font)
para.set_bottom_margin(0.25)
para.set_description(_('The style used for the Index title.'))
style_sheet.add_paragraph_style("Index-Title", para)
table = TableStyle()
table.set_width(100)
table.set_columns(2)
table.set_column_width(0, 80)
table.set_column_width(1, 20)
style_sheet.add_table_style("TOC-Table", table)
cell = TableCellStyle()
style_sheet.add_cell_style("TOC-Cell", cell)
font = FontStyle()
font.set(face=FONT_SANS_SERIF, size=10)
para = ParagraphStyle()
para.set_font(font)
para.set_description(_('The style used for the TOC page numbers.'))
style_sheet.add_paragraph_style("TOC-Number", para)
font = FontStyle()
font.set(face=FONT_SANS_SERIF, size=10)
para = ParagraphStyle()
para.set_font(font)
para.set_description(_('The style used for the Index page numbers.'))
style_sheet.add_paragraph_style("Index-Number", para)
para = ParagraphStyle()
para.set_font(font)
para.set_description(_('The style used for the TOC first level heading.'))
style_sheet.add_paragraph_style("TOC-Heading1", para)
para = ParagraphStyle()
para.set_font(font)
para.set_first_indent(0.5)
para.set_description(_('The style used for the TOC second level heading.'))
style_sheet.add_paragraph_style("TOC-Heading2", para)
para = ParagraphStyle()
para.set_font(font)
para.set_first_indent(1)
para.set_description(_('The style used for the TOC third level heading.'))
style_sheet.add_paragraph_style("TOC-Heading3", para)
def write_toc(toc, doc, offset):
"""
Write the table of contents.
"""
if not toc:
return
doc.start_paragraph('TOC-Title')
doc.write_text(_('Contents'))
doc.end_paragraph()
doc.start_table('toc', 'TOC-Table')
for mark, page_nr in toc:
doc.start_row()
doc.start_cell('TOC-Cell')
if mark.level == 1:
style_name = "TOC-Heading1"
elif mark.level == 2:
style_name = "TOC-Heading2"
else:
style_name = "TOC-Heading3"
doc.start_paragraph(style_name)
doc.write_text(mark.key)
doc.end_paragraph()
doc.end_cell()
doc.start_cell('TOC-Cell')
doc.start_paragraph('TOC-Number')
doc.write_text(str(page_nr + offset + 1))
doc.end_paragraph()
doc.end_cell()
doc.end_row()
doc.end_table()
def write_index(index, doc, offset):
"""
Write the alphabetical index.
"""
if not index:
return
doc.start_paragraph('Index-Title')
doc.write_text(_('Index'))
doc.end_paragraph()
doc.start_table('index', 'TOC-Table')
for key in sorted(index.iterkeys()):
doc.start_row()
doc.start_cell('TOC-Cell')
doc.start_paragraph('Index-Number')
doc.write_text(key)
doc.end_paragraph()
doc.end_cell()
doc.start_cell('TOC-Cell')
doc.start_paragraph('Index-Number')
pages = [str(page_nr + offset + 1) for page_nr in index[key]]
doc.write_text(', '.join(pages))
doc.end_paragraph()
doc.end_cell()
doc.end_row()
doc.end_table()

View File

@ -45,6 +45,7 @@ import const
import config
from _reportdialog import ReportDialog
from _papermenu import PaperFrame
from gen.plug.docgen import IndexOptions
from gui.pluginmanager import GuiPluginManager
PLUGMAN = GuiPluginManager.get_instance()
@ -94,7 +95,13 @@ class DocReportDialog(ReportDialog):
"""
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)
if not self.format_menu.get_active_plugin().get_paper_used():
#set css filename
self.doc.set_css_filename(self.css_filename)
@ -116,21 +123,19 @@ class DocReportDialog(ReportDialog):
# Is this to be a printed report or an electronic report
# (i.e. a set of web pages)
if self.firstpage_added:
self.notebook.remove_page(0)
if docgen_plugin.get_paper_used():
self.paper_label = gtk.Label('<b>%s</b>'%_("Paper Options"))
self.paper_label.set_use_markup(True)
self.notebook.insert_page(self.paper_frame, self.paper_label, 0)
self.paper_frame.show_all()
self.html_table.hide_all()
else:
self.html_label = gtk.Label('<b>%s</b>' % _("HTML Options"))
self.html_label.set_use_markup(True)
self.notebook.insert_page(self.html_table, self.html_label, 0)
self.paper_frame.hide_all()
self.html_table.show_all()
self.firstpage_added = True
# Does this report format support indexing?
if docgen_plugin.get_index_support():
self.index_table.show_all()
else:
self.index_table.hide_all()
ext_val = docgen_plugin.get_extension()
if ext_val:
fname = self.target_fileentry.get_full_path(0)
@ -147,12 +152,13 @@ class DocReportDialog(ReportDialog):
if self.style_button:
self.style_button.set_sensitive(docgen_plugin.get_style_support())
self.style_menu.set_sensitive(docgen_plugin.get_style_support())
self.notebook.set_current_page(3) # Report options
def setup_format_frame(self):
"""Set up the format frame of the dialog. This function
relies on the make_doc_menu() function to do all the hard
work."""
self.make_doc_menu(self.options.handler.get_format_name())
self.format_menu.connect('changed', self.doc_type_changed)
label = gtk.Label("%s:" % _("Output Format"))
@ -186,6 +192,19 @@ class DocReportDialog(ReportDialog):
self.options.handler.get_custom_paper_size()
)
self.setup_html_frame()
self.setup_index_frame()
self.paper_label = gtk.Label('<b>%s</b>'%_("Paper Options"))
self.paper_label.set_use_markup(True)
self.notebook.insert_page(self.paper_frame, self.paper_label)
self.html_label = gtk.Label('<b>%s</b>' % _("HTML Options"))
self.html_label.set_use_markup(True)
self.notebook.insert_page(self.html_table, self.html_label)
self.index_label = gtk.Label('<b>%s</b>' % _("Index Options"))
self.index_label.set_use_markup(True)
self.notebook.insert_page(self.index_table, self.index_label)
ReportDialog.setup_report_options_frame(self)
def setup_html_frame(self):
@ -221,6 +240,23 @@ class DocReportDialog(ReportDialog):
self.html_table.attach(self.css_combo,2,3,1,2, yoptions=gtk.SHRINK)
self.css_combo.set_active(active_index)
def setup_index_frame(self):
"""Set up the index frame of the dialog. This sole purpose of
this function is to grab a pointer for later use in the parse
index frame function."""
self.index_table = gtk.Table(3,3)
self.index_table.set_col_spacings(12)
self.index_table.set_row_spacings(6)
self.index_table.set_border_width(6)
self.toc = gtk.CheckButton(_("Include Table of Contents"))
self.toc.set_active(self.options.handler.get_toc())
self.index_table.attach(self.toc, 1, 2, 1, 2, gtk.SHRINK|gtk.FILL,
yoptions=gtk.SHRINK)
self.index = gtk.CheckButton(_("Include Alphbetical Index"))
self.index.set_active(self.options.handler.get_index())
self.index_table.attach(self.index, 1, 2, 2, 3, gtk.SHRINK|gtk.FILL,
yoptions=gtk.SHRINK)
def parse_format_frame(self):
"""Parse the format frame of the dialog. Save the user
selected output format for later use."""
@ -239,6 +275,16 @@ class DocReportDialog(ReportDialog):
self.css_filename = self.CSS[self.css_combo.get_active()]["filename"]
self.options.handler.set_css_filename(self.css_filename)
def parse_index_frame(self):
"""Parse the index frame of the dialog. Save the user selections for
including a table of contents and alphabetical index. Note that this
routine retrieves a value whether or not the file entry box is
displayed on the screen. The subclass will know whether this
entry was enabled. This is for simplicity of programming."""
self.options.handler.set_toc(self.toc.get_active())
self.options.handler.set_index(self.index.get_active())
def on_ok_clicked(self, obj):
"""The user is satisfied with the dialog choices. Validate
the output file name before doing anything else. If there is
@ -252,6 +298,7 @@ class DocReportDialog(ReportDialog):
self.parse_format_frame()
self.parse_style_frame()
self.parse_html_frame()
self.parse_index_frame()
self.options.handler.set_paper_metric(self.paper_frame.get_paper_metric())
self.options.handler.set_paper_name(self.paper_frame.get_paper_name())

View File

@ -58,6 +58,7 @@ from QuestionDialog import ErrorDialog, OptionDialog
from gen.plug.report import (CATEGORY_TEXT, CATEGORY_DRAW, CATEGORY_BOOK,
CATEGORY_CODE, CATEGORY_WEB, CATEGORY_GRAPHVIZ,
standalone_categories)
from gen.plug.report.toc_index import add_toc_index_styles
from gen.plug.docgen import StyleSheet, StyleSheetList
import ManagedWindow
from _stylecombobox import StyleComboBox
@ -304,6 +305,7 @@ class ReportDialog(ManagedWindow.ManagedWindow):
# Build the default style set for this report.
self.default_style = StyleSheet()
self.options.make_default_style(self.default_style)
add_toc_index_styles(self.default_style)
if self.default_style.is_empty():
# Don't display the option of no styles are used

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.