From d32a5ecfc78ef838e07f9f024857b7ab65b745a5 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Sat, 13 Oct 2018 10:16:02 +0530 Subject: [PATCH] PDF Output: Add a new "page number map" setting to easily modify page numbers as needed in headers/fotters and the generated inline table of contents. Fixes #1796902 [Feature: shift for page numbers in pdf](https://bugs.launchpad.net/calibre/+bug/1796902) --- src/calibre/ebooks/conversion/config.py | 2 +- .../ebooks/conversion/plugins/pdf_output.py | 4 ++++ src/calibre/ebooks/pdf/render/from_html.py | 8 +++++-- src/calibre/ebooks/pdf/render/toc.py | 21 ++++++++++++------- src/calibre/gui2/convert/pdf_output.ui | 19 ++++++++++++++--- src/pyj/book_list/conversion_widgets.pyj | 1 + 6 files changed, 42 insertions(+), 13 deletions(-) diff --git a/src/calibre/ebooks/conversion/config.py b/src/calibre/ebooks/conversion/config.py index 9fa74d3b45..cde5aecf59 100644 --- a/src/calibre/ebooks/conversion/config.py +++ b/src/calibre/ebooks/conversion/config.py @@ -303,7 +303,7 @@ OPTIONS = { 'pdf_footer_template', 'pdf_header_template', 'pdf_add_toc', 'toc_title', 'pdf_page_margin_left', 'pdf_page_margin_top', 'pdf_page_margin_right', 'pdf_page_margin_bottom', - 'pdf_use_document_margins',), + 'pdf_use_document_margins', 'pdf_page_number_map',), 'pml': ('inline_toc', 'full_image_depth', 'pml_output_encoding'), diff --git a/src/calibre/ebooks/conversion/plugins/pdf_output.py b/src/calibre/ebooks/conversion/plugins/pdf_output.py index d797c5ffc7..dc344e6422 100644 --- a/src/calibre/ebooks/conversion/plugins/pdf_output.py +++ b/src/calibre/ebooks/conversion/plugins/pdf_output.py @@ -148,6 +148,10 @@ class PDFOutput(OutputFormatPlugin): ' This will cause the margins specified in the conversion settings to be ignored.' ' If the document does not specify page margins, the conversion settings will be used as a fallback.') ), + OptionRecommendation(name='pdf_page_number_map', recommended_value=None, + help=_('Adjust page numbers, as needed. Syntax is a JavaScript expression for the page number.' + ' For example, "if (n < 3) 0; else n - 3;", where n is current page number.') + ), } def specialize_options(self, log, opts, input_fmt): diff --git a/src/calibre/ebooks/pdf/render/from_html.py b/src/calibre/ebooks/pdf/render/from_html.py index 093cca34a5..1178cc89cd 100644 --- a/src/calibre/ebooks/pdf/render/from_html.py +++ b/src/calibre/ebooks/pdf/render/from_html.py @@ -254,9 +254,10 @@ class PDFWriter(QObject): raise Exception('PDF Output failed, see log for details') def render_inline_toc(self): + evaljs = self.view.page().mainFrame().evaluateJavaScript self.rendered_inline_toc = True from calibre.ebooks.pdf.render.toc import toc_as_html - raw = toc_as_html(self.toc, self.doc, self.opts) + raw = toc_as_html(self.toc, self.doc, self.opts, evaljs) pt = PersistentTemporaryFile('_pdf_itoc.htm') pt.write(raw) pt.close() @@ -460,12 +461,15 @@ class PDFWriter(QObject): if idx is not None: setattr(self, attr, sections[idx][0]) + from calibre.ebooks.pdf.render.toc import calculate_page_number + while True: set_section(col, sections, 'current_section') set_section(col, tl_sections, 'current_tl_section') self.doc.init_page(page_margins) + num = calculate_page_number(self.current_page_num, self.opts.pdf_page_number_map, evaljs) if self.header or self.footer: - if evaljs('paged_display.update_header_footer(%d)'%self.current_page_num) is True: + if evaljs('paged_display.update_header_footer(%d)'%num) is True: self.load_header_footer_images() self.painter.save() diff --git a/src/calibre/ebooks/pdf/render/toc.py b/src/calibre/ebooks/pdf/render/toc.py index ea84f2b0a3..51faf445db 100644 --- a/src/calibre/ebooks/pdf/render/toc.py +++ b/src/calibre/ebooks/pdf/render/toc.py @@ -12,7 +12,14 @@ from lxml.html import tostring from lxml.html.builder import (HTML, HEAD, BODY, TABLE, TR, TD, H2, STYLE) -def convert_node(toc, table, level, pdf): +def calculate_page_number(num, map_expression, evaljs): + if map_expression: + num = int(evaljs('(function(){{var n={}; return {};}})()'.format( + num, map_expression))) + return num + + +def convert_node(toc, table, level, pdf, pdf_page_number_map, evaljs): tr = TR( TD(toc.text or _('Unknown')), TD(), ) @@ -28,18 +35,18 @@ def convert_node(toc, table, level, pdf): return None a = anchors[path] dest = a.get(frag, a[None]) - num = pdf.page_tree.obj.get_num(dest[0]) + num = calculate_page_number(pdf.page_tree.obj.get_num(dest[0]), pdf_page_number_map, evaljs) tr[1].text = type('')(num) table.append(tr) -def process_children(toc, table, level, pdf): +def process_children(toc, table, level, pdf, pdf_page_number_map, evaljs): for child in toc: - convert_node(child, table, level, pdf) - process_children(child, table, level+1, pdf) + convert_node(child, table, level, pdf, pdf_page_number_map, evaljs) + process_children(child, table, level+1, pdf, pdf_page_number_map, evaljs) -def toc_as_html(toc, pdf, opts): +def toc_as_html(toc, pdf, opts, evaljs): pdf = pdf.engine.pdf indents = [] for i in xrange(1, 7): @@ -73,6 +80,6 @@ def toc_as_html(toc, pdf, opts): body = html[1] body.set('class', 'calibre-pdf-toc') - process_children(toc, body[1], 0, pdf) + process_children(toc, body[1], 0, pdf, opts.pdf_page_number_map, evaljs) return tostring(html, pretty_print=True, include_meta_content_type=True, encoding='utf-8') diff --git a/src/calibre/gui2/convert/pdf_output.ui b/src/calibre/gui2/convert/pdf_output.ui index 45a35235a5..95a92a2344 100644 --- a/src/calibre/gui2/convert/pdf_output.ui +++ b/src/calibre/gui2/convert/pdf_output.ui @@ -7,7 +7,7 @@ 0 0 638 - 588 + 634 @@ -197,14 +197,27 @@ - + + + + Page &number map: + + + opt_pdf_page_number_map + + + + + + + Page margins - + Page headers and footers diff --git a/src/pyj/book_list/conversion_widgets.pyj b/src/pyj/book_list/conversion_widgets.pyj index 9d32786ab1..ef6068ef21 100644 --- a/src/pyj/book_list/conversion_widgets.pyj +++ b/src/pyj/book_list/conversion_widgets.pyj @@ -658,6 +658,7 @@ def pdf_output(container): g.appendChild(choices('pdf_standard_font', _('S&tandard font:'), ui_data.font_types)) g.appendChild(int_spin('pdf_default_font_size', _('Default font si&ze:'), unit='px')) g.appendChild(int_spin('pdf_mono_font_size', _('Default font si&ze:'), unit='px')) + g.appendChild(lineedit('pdf_page_number_map', _('Page number &map:'))) g.appendChild(checkbox('pdf_use_document_margins', _('Use page margins from the &document being converted'))) g.appendChild(float_spin('pdf_page_margin_left', indent + _('Left page margin'), unit='pt', min=-100, max=500)) g.appendChild(float_spin('pdf_page_margin_top', indent + _('Top page margin'), unit='pt', min=-100, max=500))