PDF Output: Add an option to add page numbers to the bottom of every page in the generated PDF file

This commit is contained in:
Kovid Goyal 2013-01-29 13:01:59 +05:30
parent 015f3d48a7
commit f9a5cab366
6 changed files with 85 additions and 15 deletions

Binary file not shown.

View File

@ -99,6 +99,18 @@ class PDFOutput(OutputFormatPlugin):
recommended_value=False, help=_( recommended_value=False, help=_(
'Generate an uncompressed PDF, useful for debugging, ' 'Generate an uncompressed PDF, useful for debugging, '
'only works with the new PDF engine.')), 'only works with the new PDF engine.')),
OptionRecommendation(name='pdf_page_numbers', recommended_value=False,
help=_('Add page numbers to the bottom of every page in the generated PDF file. If you '
'specify a footer template, it will take precedence '
'over this option.')),
OptionRecommendation(name='pdf_footer_template', recommended_value=None,
help=_('An HTML template used to generate footers on every page.'
' The string _PAGENUM_ will be replaced by the current page'
' number.')),
OptionRecommendation(name='pdf_header_template', recommended_value=None,
help=_('An HTML template used to generate headers on every page.'
' The string _PAGENUM_ will be replaced by the current page'
' number.')),
]) ])
def convert(self, oeb_book, output_path, input_plugin, opts, log): def convert(self, oeb_book, output_path, input_plugin, opts, log):

View File

@ -29,6 +29,10 @@ class PagedDisplay
this.current_page_height = null this.current_page_height = null
this.document_margins = null this.document_margins = null
this.use_document_margins = false this.use_document_margins = false
this.footer_template = null
this.header_template = null
this.header = null
this.footer = null
read_document_margins: () -> read_document_margins: () ->
# Read page margins from the document. First checks for an @page rule. # Read page margins from the document. First checks for an @page rule.
@ -102,6 +106,7 @@ class PagedDisplay
# than max_col_width # than max_col_width
sm += Math.ceil( (col_width - this.max_col_width) / 2*n ) sm += Math.ceil( (col_width - this.max_col_width) / 2*n )
col_width = Math.max(100, ((ww - adjust)/n) - 2*sm) col_width = Math.max(100, ((ww - adjust)/n) - 2*sm)
this.col_width = col_width
this.page_width = col_width + 2*sm this.page_width = col_width + 2*sm
this.screen_width = this.page_width * this.cols_per_screen this.screen_width = this.page_width * this.cols_per_screen
this.current_page_height = window.innerHeight - this.margin_top - this.margin_bottom this.current_page_height = window.innerHeight - this.margin_top - this.margin_bottom
@ -171,6 +176,30 @@ class PagedDisplay
# log('Time to layout:', new Date().getTime() - start_time) # log('Time to layout:', new Date().getTime() - start_time)
return sm return sm
create_header_footer: () ->
if this.header_template != null
this.header = document.createElement('div')
this.header.setAttribute('style', "overflow:hidden; display:block; position:absolute; left:#{ this.side_margin }px; top: 0px; height: #{ this.margin_top }px; width: #{ this.col_width }px; margin: 0; padding: 0")
document.body.appendChild(this.header)
if this.footer_template != null
this.footer = document.createElement('div')
this.footer.setAttribute('style', "overflow:hidden; display:block; position:absolute; left:#{ this.side_margin }px; top: #{ window.innerHeight - this.margin_bottom }px; height: #{ this.margin_bottom }px; width: #{ this.col_width }px; margin: 0; padding: 0")
document.body.appendChild(this.footer)
this.update_header_footer(1)
position_header_footer: () ->
[left, top] = calibre_utils.viewport_to_document(0, 0, document.body.ownerDocument)
if this.header != null
this.header.style.setProperty('left', left+'px')
if this.footer != null
this.footer.style.setProperty('left', left+'px')
update_header_footer: (pagenum) ->
if this.header != null
this.header.innerHTML = this.header_template.replace(/_PAGENUM_/g, pagenum+"")
if this.footer != null
this.footer.innerHTML = this.footer_template.replace(/_PAGENUM_/g, pagenum+"")
fit_images: () -> fit_images: () ->
# Ensure no images are wider than the available width in a column. Note # Ensure no images are wider than the available width in a column. Note
# that this method use getBoundingClientRect() which means it will # that this method use getBoundingClientRect() which means it will

View File

@ -161,6 +161,17 @@ class PDFWriter(QObject):
debug=self.log.debug, compress=not debug=self.log.debug, compress=not
opts.uncompressed_pdf, opts.uncompressed_pdf,
mark_links=opts.pdf_mark_links) mark_links=opts.pdf_mark_links)
self.footer = opts.pdf_footer_template
if self.footer is None and opts.pdf_page_numbers:
self.footer = '<p style="text-align:center; text-indent: 0">_PAGENUM_</p>'
self.header = opts.pdf_header_template
min_margin = 36
if self.footer and opts.margin_bottom < min_margin:
self.log.warn('Bottom margin is too small for footer, increasing it.')
opts.margin_bottom = min_margin
if self.header and opts.margin_top < min_margin:
self.log.warn('Top margin is too small for header, increasing it.')
opts.margin_top = min_margin
self.page.setViewportSize(QSize(self.doc.width(), self.doc.height())) self.page.setViewportSize(QSize(self.doc.width(), self.doc.height()))
self.render_queue = items self.render_queue = items
@ -264,6 +275,15 @@ class PDFWriter(QObject):
py_bridge.value = book_indexing.all_links_and_anchors(); py_bridge.value = book_indexing.all_links_and_anchors();
'''%(self.margin_top, 0, self.margin_bottom)) '''%(self.margin_top, 0, self.margin_bottom))
if self.header:
self.bridge_value = self.header
evaljs('paged_display.header_template = py_bridge.value')
if self.footer:
self.bridge_value = self.footer
evaljs('paged_display.footer_template = py_bridge.value')
if self.header or self.footer:
evaljs('paged_display.create_header_footer();')
amap = self.bridge_value amap = self.bridge_value
if not isinstance(amap, dict): if not isinstance(amap, dict):
amap = {'links':[], 'anchors':{}} # Some javascript error occurred amap = {'links':[], 'anchors':{}} # Some javascript error occurred
@ -272,6 +292,8 @@ class PDFWriter(QObject):
mf = self.view.page().mainFrame() mf = self.view.page().mainFrame()
while True: while True:
self.doc.init_page() self.doc.init_page()
if self.header or self.footer:
evaljs('paged_display.update_header_footer(%d)'%self.current_page_num)
self.painter.save() self.painter.save()
mf.render(self.painter) mf.render(self.painter)
self.painter.restore() self.painter.restore()
@ -279,7 +301,7 @@ class PDFWriter(QObject):
self.doc.end_page() self.doc.end_page()
if not nsl[1] or nsl[0] <= 0: if not nsl[1] or nsl[0] <= 0:
break break
evaljs('window.scrollTo(%d, 0)'%nsl[0]) evaljs('window.scrollTo(%d, 0); paged_display.position_header_footer();'%nsl[0])
if self.doc.errors_occurred: if self.doc.errors_occurred:
break break

View File

@ -22,7 +22,7 @@ class PluginWidget(Widget, Ui_Form):
'override_profile_size', 'paper_size', 'custom_size', 'override_profile_size', 'paper_size', 'custom_size',
'preserve_cover_aspect_ratio', 'pdf_serif_family', 'unit', 'preserve_cover_aspect_ratio', 'pdf_serif_family', 'unit',
'pdf_sans_family', 'pdf_mono_family', 'pdf_standard_font', 'pdf_sans_family', 'pdf_mono_family', 'pdf_standard_font',
'pdf_default_font_size', 'pdf_mono_font_size']) 'pdf_default_font_size', 'pdf_mono_font_size', 'pdf_page_numbers'])
self.db, self.book_id = db, book_id self.db, self.book_id = db, book_id
for x in get_option('paper_size').option.choices: for x in get_option('paper_size').option.choices:

View File

@ -84,7 +84,7 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="0"> <item row="6" column="0">
<widget class="QLabel" name="label_4"> <widget class="QLabel" name="label_4">
<property name="text"> <property name="text">
<string>Se&amp;rif family:</string> <string>Se&amp;rif family:</string>
@ -94,10 +94,10 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="5" column="1"> <item row="6" column="1">
<widget class="QFontComboBox" name="opt_pdf_serif_family"/> <widget class="QFontComboBox" name="opt_pdf_serif_family"/>
</item> </item>
<item row="6" column="0"> <item row="7" column="0">
<widget class="QLabel" name="label_5"> <widget class="QLabel" name="label_5">
<property name="text"> <property name="text">
<string>&amp;Sans family:</string> <string>&amp;Sans family:</string>
@ -107,10 +107,10 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="6" column="1"> <item row="7" column="1">
<widget class="QFontComboBox" name="opt_pdf_sans_family"/> <widget class="QFontComboBox" name="opt_pdf_sans_family"/>
</item> </item>
<item row="7" column="0"> <item row="8" column="0">
<widget class="QLabel" name="label_6"> <widget class="QLabel" name="label_6">
<property name="text"> <property name="text">
<string>&amp;Monospace family:</string> <string>&amp;Monospace family:</string>
@ -120,10 +120,10 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="7" column="1"> <item row="8" column="1">
<widget class="QFontComboBox" name="opt_pdf_mono_family"/> <widget class="QFontComboBox" name="opt_pdf_mono_family"/>
</item> </item>
<item row="8" column="0"> <item row="9" column="0">
<widget class="QLabel" name="label_7"> <widget class="QLabel" name="label_7">
<property name="text"> <property name="text">
<string>S&amp;tandard font:</string> <string>S&amp;tandard font:</string>
@ -133,10 +133,10 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="8" column="1"> <item row="9" column="1">
<widget class="QComboBox" name="opt_pdf_standard_font"/> <widget class="QComboBox" name="opt_pdf_standard_font"/>
</item> </item>
<item row="9" column="0"> <item row="10" column="0">
<widget class="QLabel" name="label_8"> <widget class="QLabel" name="label_8">
<property name="text"> <property name="text">
<string>Default font si&amp;ze:</string> <string>Default font si&amp;ze:</string>
@ -146,14 +146,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="9" column="1"> <item row="10" column="1">
<widget class="QSpinBox" name="opt_pdf_default_font_size"> <widget class="QSpinBox" name="opt_pdf_default_font_size">
<property name="suffix"> <property name="suffix">
<string> px</string> <string> px</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="10" column="0"> <item row="11" column="0">
<widget class="QLabel" name="label_9"> <widget class="QLabel" name="label_9">
<property name="text"> <property name="text">
<string>Monospace &amp;font size:</string> <string>Monospace &amp;font size:</string>
@ -163,14 +163,14 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="10" column="1"> <item row="11" column="1">
<widget class="QSpinBox" name="opt_pdf_mono_font_size"> <widget class="QSpinBox" name="opt_pdf_mono_font_size">
<property name="suffix"> <property name="suffix">
<string> px</string> <string> px</string>
</property> </property>
</widget> </widget>
</item> </item>
<item row="11" column="0"> <item row="12" column="0">
<spacer name="verticalSpacer"> <spacer name="verticalSpacer">
<property name="orientation"> <property name="orientation">
<enum>Qt::Vertical</enum> <enum>Qt::Vertical</enum>
@ -183,6 +183,13 @@
</property> </property>
</spacer> </spacer>
</item> </item>
<item row="5" column="0" colspan="2">
<widget class="QCheckBox" name="opt_pdf_page_numbers">
<property name="text">
<string>Add page &amp;numbers to the bottom of every page</string>
</property>
</widget>
</item>
</layout> </layout>
</widget> </widget>
<resources/> <resources/>