Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding annotations to the PDF to link back its content to its source. #2192

Merged
merged 5 commits into from
Sep 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,10 @@ def _round_meta(pages):
"""Eliminate errors of floating point arithmetic for metadata."""
for page in pages:
anchors = page.anchors
for anchor_name, (pos_x, pos_y) in anchors.items():
anchors[anchor_name] = round(pos_x, 6), round(pos_y, 6)
for anchor_name, (x1, y1, x2, y2) in anchors.items():
anchors[anchor_name] = (
round(x1, 6), round(y1, 6),
round(x2, 6), round(y2, 6))
links = page.links
for i, link in enumerate(links):
link_type, target, rectangle, box = link
Expand Down Expand Up @@ -884,8 +886,8 @@ def test_links_1():
],
[('internal', 'hello', (0, 0, 200, 30))],
], [
{'hello': (0, 200)},
{'lipsum': (0, 0)}
{'hello': (0, 200, 200, 290)},
{'lipsum': (0, 0, 200, 90)}
], [
(
[
Expand Down Expand Up @@ -966,7 +968,7 @@ def test_links_6():
''', [[
('internal', 'lipsum', (5, 10, 195, 10)),
('external', 'https://weasyprint.org/', (0, 10, 200, 10))]],
[{'lipsum': (5, 10)}],
[{'lipsum': (5, 10, 195, 10)}],
[([('internal', 'lipsum', (5, 10, 195, 10)),
('external', 'https://weasyprint.org/', (0, 10, 200, 10))],
[('lipsum', 5, 10)])],
Expand All @@ -982,7 +984,7 @@ def test_links_7():
margin: 10px 5px" id="lipsum">
''',
[[('internal', 'lipsum', (5, 10, 195, 10))]],
[{'lipsum': (5, 10)}],
[{'lipsum': (5, 10, 195, 10)}],
[([('internal', 'lipsum', (5, 10, 195, 10))], [('lipsum', 5, 10)])],
base_url=None)

Expand All @@ -998,7 +1000,7 @@ def test_links_8():
''',
[[('internal', 'lipsum', (0, 0, 200, 15)),
('internal', 'missing', (0, 15, 200, 30))]],
[{'lipsum': (0, 15)}],
[{'lipsum': (0, 15, 200, 30)}],
[([('internal', 'lipsum', (0, 0, 200, 15))], [('lipsum', 0, 15)])],
base_url=None,
warnings=[
Expand All @@ -1014,7 +1016,7 @@ def test_links_9():
transform: rotate(90deg) scale(2)">
''',
[[('internal', 'lipsum', (30, 10, 70, 210))]],
[{'lipsum': (70, 10)}],
[{'lipsum': (70, 10, 30, 210)}],
[([('internal', 'lipsum', (30, 10, 70, 210))], [('lipsum', 70, 10)])],
round=True)

Expand Down
10 changes: 7 additions & 3 deletions weasyprint/anchors.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,13 +113,17 @@ def gather_anchors(box, anchors, links, bookmarks, forms, parent_matrix=None,
links.append((link_type, target, rectangle, box))
if is_input:
forms[parent_form].append((box.element, box.style, rectangle))
if matrix and (has_bookmark or has_anchor):
pos_x, pos_y = matrix.transform_point(pos_x, pos_y)
if has_bookmark:
if matrix:
pos_x, pos_y = matrix.transform_point(pos_x, pos_y)
bookmark = (bookmark_level, bookmark_label, (pos_x, pos_y), state)
bookmarks.append(bookmark)
if has_anchor:
anchors[anchor_name] = pos_x, pos_y
pos_x1, pos_y1, pos_x2, pos_y2 = pos_x, pos_y, pos_x + width, pos_y + height
if matrix:
pos_x1, pos_y1 = matrix.transform_point(pos_x1, pos_y1)
pos_x2, pos_y2 = matrix.transform_point(pos_x2, pos_y2)
anchors[anchor_name] = (pos_x1, pos_y1, pos_x2, pos_y2)

for child in box.all_children():
gather_anchors(child, anchors, links, bookmarks, forms, matrix, parent_form)
Expand Down
4 changes: 2 additions & 2 deletions weasyprint/pdf/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from ..html import W3C_DATE_RE
from ..logger import LOGGER, PROGRESS_LOGGER
from ..matrix import Matrix
from . import pdfa, pdfua
from . import debug, pdfa, pdfua
from .fonts import build_fonts_dictionary
from .stream import Stream

Expand All @@ -17,7 +17,7 @@
write_pdf_attachment)

VARIANTS = {
name: data for variants in (pdfa.VARIANTS, pdfua.VARIANTS)
name: data for variants in (pdfa.VARIANTS, pdfua.VARIANTS, debug.VARIANTS)
for (name, data) in variants.items()}


Expand Down
2 changes: 1 addition & 1 deletion weasyprint/pdf/anchors.py
Original file line number Diff line number Diff line change
Expand Up @@ -406,7 +406,7 @@ def resolve_links(pages):
paged_anchors = []
for i, page in enumerate(pages):
paged_anchors.append([])
for anchor_name, (point_x, point_y) in page.anchors.items():
for anchor_name, (point_x, point_y, _, _) in page.anchors.items():
if anchor_name not in anchors:
paged_anchors[-1].append((anchor_name, point_x, point_y))
anchors.add(anchor_name)
Expand Down
43 changes: 43 additions & 0 deletions weasyprint/pdf/debug.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"""PDF generation with debug information."""

import pydyf

from ..matrix import Matrix


def debug(pdf, metadata, document, page_streams, attachments, compress):
"""Set debug PDF metadata."""

# Add links on ids.
pages = zip(pdf.pages['Kids'][::3], document.pages, page_streams)
for pdf_page_number, document_page, stream in pages:
if not document_page.anchors:
continue

page = pdf.objects[pdf_page_number]
if 'Annots' not in page:
page['Annots'] = pydyf.Array()

for id, (x1, y1, x2, y2) in document_page.anchors.items():
# TODO: handle zoom correctly.
matrix = Matrix(0.75, 0, 0, 0.75) @ stream.ctm
x1, y1 = matrix.transform_point(x1, y1)
x2, y2 = matrix.transform_point(x2, y2)
annotation = pydyf.Dictionary({
'Type': '/Annot',
'Subtype': '/Link',
'Rect': pydyf.Array([x1, y1, x2, y2]),
'BS': pydyf.Dictionary({'W': 0}),
'P': page.reference,
'T': pydyf.String(id), # id added as metadata
})

# The next line makes all of this relevent to use
# with PDFjs
annotation['Dest'] = pydyf.String(id)

pdf.add_object(annotation)
page['Annots'].append(annotation.reference)


VARIANTS = {'debug': (debug, {})}
Loading