mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-06-23 15:30:45 -04:00
Dont use iframes for header/footers
This breaks backwards compat with javascript, but backwards compat is not really preserved anyway. Much better performance without iframes
This commit is contained in:
parent
e64b8b11fb
commit
b8dc1d791c
@ -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::
|
||||
|
||||
<header style="justify-content: flex-end">
|
||||
<div class="even_page">_AUTHOR_</div>
|
||||
<div class="odd_page"><i>_TITLE_</i></div>
|
||||
<div class="even-page">_AUTHOR_</div>
|
||||
<div class="odd-page"><i>_TITLE_</i></div>
|
||||
</header>
|
||||
|
||||
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::
|
||||
|
||||
<header style="justify-content: space-between; font-size: smaller">
|
||||
<div>_TITLE_</div>
|
||||
@ -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::
|
||||
|
||||
<footer><div id="pagenum"></div></footer>
|
||||
<script>document.getElementById("pagenum").innerHTML = "" + (_PAGENUM_ + 3)</script>
|
||||
<footer>
|
||||
<div></div>
|
||||
<script>document.currentScript.parentNode.querySelector("div").innerHTML = "" + (_PAGENUM_ + 3)</script>
|
||||
</footer>
|
||||
|
||||
.. 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`
|
||||
|
@ -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 += '<style>{}</style>'.format(extra_style)
|
||||
repl = skeleton.replace('</body>', template + '</body>', 1)
|
||||
if repl == skeleton:
|
||||
raise ValueError('Failed to insert template into skeleton: ' + skeleton)
|
||||
return repl
|
||||
troot = parse(template, namespace_elements=True)
|
||||
ans = troot[-1][0]
|
||||
style = ans.get('style') or ''
|
||||
style = (
|
||||
'margin: 0; padding: 0; height: {height}pt; border-width: 0;'
|
||||
'display: flex; align-items: center;').format(height=height) + style
|
||||
ans.set('style', style)
|
||||
for child in ans.xpath('descendant-or-self::*[@class]'):
|
||||
cls = frozenset(child.get('class').split())
|
||||
q = 'even-page' if page_num % 2 else 'odd-page'
|
||||
if q in cls or q.replace('-', '_') in cls:
|
||||
style = child.get('style') or ''
|
||||
child.set('style', style + '; display: none')
|
||||
return ans
|
||||
|
||||
for page_num in range(1, pdf_doc.page_count() + 1):
|
||||
div = m('div')
|
||||
body.append(div)
|
||||
margins = page_margins_map[page_num - 1]
|
||||
div = create_container(page_num, margins)
|
||||
body.append(div)
|
||||
if header_template:
|
||||
f = format_template(header_template, page_num)
|
||||
div.append(create_iframe(margins, f))
|
||||
div.append(format_template(header_template, page_num, margins.top))
|
||||
if footer_template:
|
||||
f = format_template(footer_template, page_num)
|
||||
div.append(create_iframe(margins, f, True))
|
||||
div.append(format_template(footer_template, page_num, margins.bottom))
|
||||
|
||||
container.commit()
|
||||
results = manager.convert_html_files([job], wait_for_title='iframe-loading-complete')
|
||||
# print(open(job[0]).read())
|
||||
results = manager.convert_html_files([job], settle_time=1)
|
||||
data = results[name]
|
||||
if not isinstance(data, bytes):
|
||||
raise SystemExit(data)
|
||||
|
Loading…
x
Reference in New Issue
Block a user