diff --git a/manual/conversion.rst b/manual/conversion.rst
index 72592b95c1..27b74a0f61 100644
--- a/manual/conversion.rst
+++ b/manual/conversion.rst
@@ -841,14 +841,17 @@ 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::
- _AUTHOR_
- _TITLE_
+ _AUTHOR_
+ _TITLE_
calibre will automatically replace :code:`_TITLE_` and :code:`_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::
+the title and author of the document being converted. Setting
+:code:`justify-content` to :code:`flex-end` will cause the text to be right
+aligned.
+
+You can also display text at the left and right edges and change the font size,
+as demonstrated with this header template::
_TITLE_
@@ -873,8 +876,10 @@ You can even use JavaScript inside the header and footer templates, for
example, the following template will cause page numbers to start at 4 instead
of 1::
-
-
+
.. note:: When adding headers and footers make sure you set the page top and
bottom margins to large enough values, under the :guilabel:`PDF Output`
diff --git a/src/calibre/ebooks/pdf/html_writer.py b/src/calibre/ebooks/pdf/html_writer.py
index 0a5f987104..0f2ac82142 100644
--- a/src/calibre/ebooks/pdf/html_writer.py
+++ b/src/calibre/ebooks/pdf/html_writer.py
@@ -21,11 +21,12 @@ from PyQt5.Qt import (
)
from PyQt5.QtWebEngineCore import QWebEngineUrlRequestInterceptor
from PyQt5.QtWebEngineWidgets import QWebEnginePage, QWebEngineProfile
+from html5_parser import parse
from calibre import detect_ncpus, prepare_string_for_xml
from calibre.constants import __version__, iswindows
from calibre.ebooks.metadata.xmp import metadata_to_xmp_packet
-from calibre.ebooks.oeb.base import XHTML, xml2text
+from calibre.ebooks.oeb.base import XHTML
from calibre.ebooks.oeb.polish.container import Container as ContainerBase
from calibre.ebooks.oeb.polish.toc import get_toc
from calibre.ebooks.pdf.image_writer import (
@@ -859,39 +860,20 @@ def add_header_footer(manager, opts, pdf_doc, container, page_number_display_map
body = root[-1]
body.attrib.pop('id', None)
body.set('style', 'margin: 0; padding: 0; border-width: 0')
- skeleton = xml2text(root, method='html')
job = job_for_name(container, name, Margins(0, 0, 0, 0), page_layout)
- def m(tag_name, text=None, **attrs):
+ def m(tag_name, text=None, style=None, **attrs):
ans = root.makeelement(XHTML(tag_name), **attrs)
if text is not None:
ans.text = text
+ if style is not None:
+ style = '; '.join('{}: {}'.format(k, v) for k, v in iteritems(style))
+ ans.set('style', style)
return ans
justify = 'flex-end'
if header_template:
justify = 'space-between' if footer_template else 'flex-start'
- del root[0][:]
- root[0].append(m('style', '''
- * {{ margin: 0; padding: 0; border-width: 0; box-sizing: border-box; }}
- div {{
- page-break-inside: avoid;
- page-break-after:always;
- display: flex;
- flex-direction: column;
- height: 100%;
- margin-bottom: 0pt;
- justify-content: {justify}
- }}
- '''.format(justify=justify)))
- root[0].append(m('script', '''
- function iframe_loaded(iframe) {
- for (let i of document.getElementsByTagName('iframe')) {
- if (i.contentWindow.document.readyState !== 'complete') return;
- }
- document.title = 'iframe-loading-complete'
- }
- '''))
def create_toc_stack(iterator):
ans = []
@@ -930,45 +912,58 @@ def add_header_footer(manager, opts, pdf_doc, container, page_number_display_map
yield 0, x
toplevel_toc_map = stack_to_map(create_toc_stack(tc()))
- def create_iframe(margins, f, is_footer=False):
+ def create_container(page_num, margins):
style = {
+ 'page-break-inside': 'avoid',
+ 'page-break-after': 'always',
+ 'display': 'flex',
+ 'flex-direction': 'column',
+ 'height': '100%',
+ 'justify-content': justify,
'margin-left': '{}pt'.format(margins.left),
'margin-right': '{}pt'.format(margins.right),
- 'height': '{}pt'.format(margins.bottom if is_footer else margins.top)}
- style = '; '.join('{}: {}'.format(k, v) for k, v in iteritems(style))
- return m('iframe', seamless='seamless', style=style, onload='iframe_loaded(this)', srcdoc=f)
+ 'margin-top': '0',
+ 'margin-bottom': '0',
+ 'padding': '0',
+ 'border-width': '0',
+ }
- def format_template(template, page_num):
- extra_style = 'header, footer { margin: 0; padding: 0; border-width: 0; height: 100vh; display: flex; align-items: center }'
- if page_num % 2:
- extra_style += '.even_page { display: none }'
- else:
- extra_style += '.odd_page { display: none }'
+ ans = m('div', style=style, id='p{}'.format(page_num))
+ return ans
+ def format_template(template, page_num, height):
template = template.replace('_PAGENUM_', unicode_type(page_number_display_map[page_num]))
template = template.replace('_TITLE_', prepare_string_for_xml(pdf_metadata.title, True))
template = template.replace('_AUTHOR_', prepare_string_for_xml(pdf_metadata.author, True))
template = template.replace('_TOP_LEVEL_SECTION_', prepare_string_for_xml(toplevel_toc_map[page_num - 1]))
template = template.replace('_SECTION_', prepare_string_for_xml(page_toc_map[page_num - 1]))
- template += ''.format(extra_style)
- repl = skeleton.replace('