PDF Output: Allow using templates to create arbitrary headers and footers. Look under PDF Output in the conversion dialog for this feature.

This commit is contained in:
Kovid Goyal 2013-04-01 13:34:32 +05:30
parent c6c9e5acf7
commit 6201e2a19b
7 changed files with 138 additions and 31 deletions

View File

@ -750,8 +750,51 @@ If this property is detected by |app|, the following custom properties are recog
opf.series opf.series
opf.seriesindex opf.seriesindex
In addition to this, you can specify the picture to use as the cover by naming it ``opf.cover`` (right click, Picture->Options->Name) in the ODT. If no picture with this name is found, the 'smart' method is used. In addition to this, you can specify the picture to use as the cover by naming
As the cover detection might result in double covers in certain output formats, the process will remove the paragraph (only if the only content is the cover!) from the document. But this works only with the named picture! it ``opf.cover`` (right click, Picture->Options->Name) in the ODT. If no
picture with this name is found, the 'smart' method is used. As the cover
detection might result in double covers in certain output formats, the process
will remove the paragraph (only if the only content is the cover!) from the
document. But this works only with the named picture!
To disable cover detection you can set the custom property ``opf.nocover`` ('Yes or No' type) to Yes in advanced mode. To disable cover detection you can set the custom property ``opf.nocover`` ('Yes or No' type) to Yes in advanced mode.
Converting to PDF
~~~~~~~~~~~~~~~~~~~
The first, most important, setting to decide on when converting to PDF is the page
size. By default, |app| uses a page size defined by the current
:guilabel:`Output profile`. So if your output profile is set to Kindle, |app|
will create a PDF with page size suitable for viewing on the small kindle
screen. However, if you view this PDF file on a computer screen, then it will
appear to have too large fonts. To create "normal" sized PDFs, use the override
page size option under :guilabel:`PDF Output` in the conversion dialog.
You can insert arbitrary headers and footers on each page of the PDF by
specifying header and footer templates. Templates are just snippets of HTML
code that get rendered in the header and footer locations. For example, to
display page numbers centered at the bottom of every page, in green, use the following
footer template::
<p style="text-align:center; color:green">Page _PAGENUM_</p>
|app| will automatically replace _PAGENUM_ with the current page number. You
can even put different content on even and odd pages, for example the following
header template will show the title on odd pages and the author on even pages::
<p style="text-align:right"><span class="even_page">_AUTHOR_</span><span class="odd_page"><i>_TITLE_</i></span></p>
|app| will automatically replace _TITLE_ and _AUTHOR_ with the title and author
of the document being converted. You can also display text at the left and
right edges and change the font size, as demonstrated with this header
template::
<div style="font-size:x-small"><p style="float:left">_TITLE_</p><p style="float:right;"><i>_AUTHOR_</i></p></div>
This will display the title at the left and the author at the right, in a font
size smaller than the main text.
.. note:: When adding headers and footers make sure you set the page top and
bottom margins to large enough values, under the Page Setup section of the
conversion dialog.

Binary file not shown.

View File

@ -105,12 +105,10 @@ class PDFOutput(OutputFormatPlugin):
'over this option.')), 'over this option.')),
OptionRecommendation(name='pdf_footer_template', recommended_value=None, OptionRecommendation(name='pdf_footer_template', recommended_value=None,
help=_('An HTML template used to generate footers on every page.' help=_('An HTML template used to generate footers on every page.'
' The string _PAGENUM_ will be replaced by the current page' ' The strings _PAGENUM_, _TITLE_ and _AUTHOR_ will be replaced by their current values.')),
' number.')),
OptionRecommendation(name='pdf_header_template', recommended_value=None, OptionRecommendation(name='pdf_header_template', recommended_value=None,
help=_('An HTML template used to generate headers on every page.' help=_('An HTML template used to generate headers on every page.'
' The string _PAGENUM_ will be replaced by the current page' ' The strings _PAGENUM_, _TITLE_ and _AUTHOR_ will be replaced by their current values.')),
' 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

@ -33,6 +33,7 @@ class PagedDisplay
this.header_template = null this.header_template = null
this.header = null this.header = null
this.footer = null this.footer = null
this.hf_style = 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.
@ -184,15 +185,22 @@ 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: () -> create_header_footer: (uuid) ->
if this.header_template != null if this.header_template != null
this.header = document.createElement('div') 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") 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")
this.header.setAttribute('id', 'pdf_page_header_'+uuid)
document.body.appendChild(this.header) document.body.appendChild(this.header)
if this.footer_template != null if this.footer_template != null
this.footer = document.createElement('div') 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") 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")
this.footer.setAttribute('id', 'pdf_page_footer_'+uuid)
document.body.appendChild(this.footer) document.body.appendChild(this.footer)
if this.header != null or this.footer != null
this.hf_uuid = uuid
this.hf_style = document.createElement('style')
this.hf_style.setAttribute('type', 'text/css')
document.head.appendChild(this.hf_style)
this.update_header_footer(1) this.update_header_footer(1)
position_header_footer: () -> position_header_footer: () ->
@ -203,10 +211,15 @@ class PagedDisplay
this.footer.style.setProperty('left', left+'px') this.footer.style.setProperty('left', left+'px')
update_header_footer: (pagenum) -> update_header_footer: (pagenum) ->
if this.hf_style != null
if pagenum%2 == 1 then cls = "even_page" else cls = "odd_page"
this.hf_style.innerHTML = "#pdf_page_header_#{ this.hf_uuid } .#{ cls }, #pdf_page_footer_#{ this.hf_uuid } .#{ cls } { display: none }"
title = py_bridge.title()
author = py_bridge.author()
if this.header != null if this.header != null
this.header.innerHTML = this.header_template.replace(/_PAGENUM_/g, pagenum+"") this.header.innerHTML = this.header_template.replace(/_PAGENUM_/g, pagenum+"").replace(/_TITLE_/g, title+"").replace(/_AUTHOR_/g, author+"")
if this.footer != null if this.footer != null
this.footer.innerHTML = this.footer_template.replace(/_PAGENUM_/g, pagenum+"") this.footer.innerHTML = this.footer_template.replace(/_PAGENUM_/g, pagenum+"").replace(/_TITLE_/g, title+"").replace(/_AUTHOR_/g, author+"")
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

View File

@ -130,6 +130,14 @@ class PDFWriter(QObject):
_pass_json_value = pyqtProperty(QString, fget=_pass_json_value_getter, _pass_json_value = pyqtProperty(QString, fget=_pass_json_value_getter,
fset=_pass_json_value_setter) fset=_pass_json_value_setter)
@pyqtSlot(result=unicode)
def title(self):
return self.doc_title
@pyqtSlot(result=unicode)
def author(self):
return self.doc_author
def __init__(self, opts, log, cover_data=None, toc=None): def __init__(self, opts, log, cover_data=None, toc=None):
from calibre.gui2 import is_ok_to_use_qt from calibre.gui2 import is_ok_to_use_qt
if not is_ok_to_use_qt(): if not is_ok_to_use_qt():
@ -170,9 +178,13 @@ class PDFWriter(QObject):
opts.uncompressed_pdf, opts.uncompressed_pdf,
mark_links=opts.pdf_mark_links) mark_links=opts.pdf_mark_links)
self.footer = opts.pdf_footer_template self.footer = opts.pdf_footer_template
if self.footer is None and opts.pdf_page_numbers: if self.footer:
self.footer = self.footer.strip()
if not self.footer and opts.pdf_page_numbers:
self.footer = '<p style="text-align:center; text-indent: 0">_PAGENUM_</p>' self.footer = '<p style="text-align:center; text-indent: 0">_PAGENUM_</p>'
self.header = opts.pdf_header_template self.header = opts.pdf_header_template
if self.header:
self.header = self.header.strip()
min_margin = 36 min_margin = 36
if self.footer and opts.margin_bottom < min_margin: if self.footer and opts.margin_bottom < min_margin:
self.log.warn('Bottom margin is too small for footer, increasing it.') self.log.warn('Bottom margin is too small for footer, increasing it.')
@ -192,6 +204,8 @@ class PDFWriter(QObject):
self.doc.set_metadata(title=pdf_metadata.title, self.doc.set_metadata(title=pdf_metadata.title,
author=pdf_metadata.author, author=pdf_metadata.author,
tags=pdf_metadata.tags) tags=pdf_metadata.tags)
self.doc_title = pdf_metadata.title
self.doc_author = pdf_metadata.author
self.painter.save() self.painter.save()
try: try:
if self.cover_data is not None: if self.cover_data is not None:
@ -275,11 +289,13 @@ class PDFWriter(QObject):
def do_paged_render(self): def do_paged_render(self):
if self.paged_js is None: if self.paged_js is None:
import uuid
from calibre.utils.resources import compiled_coffeescript as cc from calibre.utils.resources import compiled_coffeescript as cc
self.paged_js = cc('ebooks.oeb.display.utils') self.paged_js = cc('ebooks.oeb.display.utils')
self.paged_js += cc('ebooks.oeb.display.indexing') self.paged_js += cc('ebooks.oeb.display.indexing')
self.paged_js += cc('ebooks.oeb.display.paged') self.paged_js += cc('ebooks.oeb.display.paged')
self.paged_js += cc('ebooks.oeb.display.mathjax') self.paged_js += cc('ebooks.oeb.display.mathjax')
self.hf_uuid = str(uuid.uuid4()).replace('-', '')
self.view.page().mainFrame().addToJavaScriptWindowObject("py_bridge", self) self.view.page().mainFrame().addToJavaScriptWindowObject("py_bridge", self)
self.view.page().longjs_counter = 0 self.view.page().longjs_counter = 0
@ -313,7 +329,7 @@ class PDFWriter(QObject):
self.bridge_value = self.footer self.bridge_value = self.footer
evaljs('paged_display.footer_template = py_bridge.value') evaljs('paged_display.footer_template = py_bridge.value')
if self.header or self.footer: if self.header or self.footer:
evaljs('paged_display.create_header_footer();') evaljs('paged_display.create_header_footer("%s");'%self.hf_uuid)
start_page = self.current_page_num start_page = self.current_page_num

View File

@ -22,7 +22,9 @@ 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_page_numbers']) 'pdf_default_font_size', 'pdf_mono_font_size', 'pdf_page_numbers',
'pdf_footer_template', 'pdf_header_template',
])
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

@ -6,8 +6,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>590</width> <width>638</width>
<height>395</height> <height>498</height>
</rect> </rect>
</property> </property>
<property name="windowTitle"> <property name="windowTitle">
@ -84,6 +84,13 @@
</property> </property>
</widget> </widget>
</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>
<item row="6" 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">
@ -170,24 +177,52 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="12" column="0"> <item row="12" column="0" colspan="2">
<spacer name="verticalSpacer"> <widget class="QGroupBox" name="groupBox">
<property name="orientation"> <property name="title">
<enum>Qt::Vertical</enum> <string>Page headers and footers</string>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>213</height>
</size>
</property>
</spacer>
</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> </property>
<layout class="QFormLayout" name="formLayout_2">
<item row="0" column="0" colspan="2">
<widget class="QLabel" name="label_2">
<property name="text">
<string>You can insert headers and footers into every page of the produced PDF file by using header and footer templates. For examples, see the &lt;a href=&quot;http://manual.calibre-ebook.com/conversion.html#converting-to-pdf&quot;&gt;documentation&lt;/a&gt;.</string>
</property>
<property name="wordWrap">
<bool>true</bool>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_12">
<property name="text">
<string>&amp;Header template:</string>
</property>
<property name="buddy">
<cstring>opt_pdf_header_template</cstring>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QLineEdit" name="opt_pdf_header_template"/>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_13">
<property name="text">
<string>&amp;Footer template:</string>
</property>
<property name="buddy">
<cstring>opt_pdf_footer_template</cstring>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QLineEdit" name="opt_pdf_footer_template"/>
</item>
</layout>
</widget> </widget>
</item> </item>
</layout> </layout>