Skip to content

Commit

Permalink
Merge pull request #1146 from Tontyna/bookmark-label
Browse files Browse the repository at this point in the history
Bookmark label
  • Loading branch information
liZe authored Sep 23, 2020
2 parents c9385a4 + 5290cd1 commit b545239
Show file tree
Hide file tree
Showing 3 changed files with 106 additions and 1 deletion.
9 changes: 9 additions & 0 deletions weasyprint/formatting_structure/build.py
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,15 @@ def before_after_to_box(element, pseudo_type, state, style_for,
target_collector, counter_style))

box.children = children

# calculate the bookmark-label
if style['bookmark_label'] == 'none':
box.bookmark_label = ''
else:
_quote_depth, counter_values, _counter_scopes = state
compute_bookmark_label(
element, box, style['bookmark_label'], counter_values,
target_collector, counter_style)
return [box]


Expand Down
28 changes: 27 additions & 1 deletion weasyprint/layout/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,23 +140,49 @@ def layout_document(html, root_box, context, max_loops=8):
if not reloop_content and not reloop_pages:
break

# Calculate string-sets and bookmark-label containing page based counters
# Calculate string-sets and bookmark-labels containing page based counters
# when pagination is finished. No need to do that (maybe multiple times) in
# make_page because they dont create boxes, only appear in MarginBoxes and
# in the final PDF.
# Prevent repetition of bookmarks (see #1145).

watch_elements = []
watch_elements_before = []
watch_elements_after = []
for i, page in enumerate(pages):
# We need the updated page_counter_values
resume_at, next_page, right_page, page_state, remake_state = (
context.page_maker[i + 1])
page_counter_values = page_state[1]

for child in page.descendants():
# Only one bookmark per original box
if child.bookmark_label:
if child.element_tag.endswith('::before'):
checklist = watch_elements_before
elif child.element_tag.endswith('::after'):
checklist = watch_elements_after
else:
checklist = watch_elements
if child.element in checklist:
child.bookmark_label = ''
else:
checklist.append(child.element)

# TODO: remove attribute or set a default value in Box class
if hasattr(child, 'missing_link'):
for (box, css_token), item in (
context.target_collector.counter_lookup_items.items()):
if child.missing_link == box and css_token != 'content':
if (css_token == 'bookmark-label' and
not child.bookmark_label):
# don't refill it!
continue
item.parse_again(page_counter_values)
# string_set is a pointer, but the bookmark_label is
# just a string: copy it
if css_token == 'bookmark-label':
child.bookmark_label = box.bookmark_label
# Collect the string_sets in the LayoutContext
string_sets = child.string_set
if string_sets and string_sets != 'none':
Expand Down
70 changes: 70 additions & 0 deletions weasyprint/tests/test_pdf.py
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,76 @@ def test_bookmarks_8():
assert o2.get_value('Title', '(.*)') == b'(g)'


@assert_no_logs
@requires('cairo', (1, 15, 4))
def test_bookmarks_9():
fileobj = io.BytesIO()
FakeHTML(string='''
<h1 style="bookmark-label: 'h1 on page ' counter(page)">a</h1>
''').write_pdf(target=fileobj)
# h1 on page 1
pdf_file = pdf.PDFFile(fileobj)
outlines = pdf_file.catalog.get_indirect_dict('Outlines', pdf_file)
assert outlines.get_type() == 'Outlines'
assert outlines.get_value('Count', '(.*)') == b'-1'
o1 = outlines.get_indirect_dict('First', pdf_file)
assert o1.get_value('Title', '(.*)') == b'(h1 on page 1)'


@assert_no_logs
@requires('cairo', (1, 15, 4))
def test_bookmarks_10():
fileobj = io.BytesIO()
FakeHTML(string='''
<style>
div:before, div:after {
content: '';
bookmark-level: 1;
bookmark-label: 'x';
}
</style>
<div>a</div>
''').write_pdf(target=fileobj)
# x
# x
pdf_file = pdf.PDFFile(fileobj)
outlines = pdf_file.catalog.get_indirect_dict('Outlines', pdf_file)
assert outlines.get_type() == 'Outlines'
assert outlines.get_value('Count', '(.*)') == b'-2'
o1 = outlines.get_indirect_dict('First', pdf_file)
assert o1.get_value('Title', '(.*)') == b'(x)'
o2 = o1.get_indirect_dict('Next', pdf_file)
assert o2.get_value('Title', '(.*)') == b'(x)'


@assert_no_logs
@requires('cairo', (1, 15, 4))
def test_bookmarks_11():
fileobj = io.BytesIO()
FakeHTML(string='''
<div style="display:inline; white-space:pre;
bookmark-level:1; bookmark-label:'a'">
a
a
a
</div>
<div style="bookmark-level:1; bookmark-label:'b'">
<div>b</div>
<div style="break-before:always">c</div>
</div>
''').write_pdf(target=fileobj)
# a
# b
pdf_file = pdf.PDFFile(fileobj)
outlines = pdf_file.catalog.get_indirect_dict('Outlines', pdf_file)
assert outlines.get_type() == 'Outlines'
assert outlines.get_value('Count', '(.*)') == b'-2'
o1 = outlines.get_indirect_dict('First', pdf_file)
assert o1.get_value('Title', '(.*)') == b'(a)'
o2 = o1.get_indirect_dict('Next', pdf_file)
assert o2.get_value('Title', '(.*)') == b'(b)'


@assert_no_logs
def test_links_none():
fileobj = io.BytesIO()
Expand Down

0 comments on commit b545239

Please sign in to comment.