Consider using an appearance stream to render links in PDF
The current implementation of PDF hyperlinks uses the annotation rectangle /Rect and splits a link into multiple annotations to support links spaning multiple lines.
It might be possible to use an appearance stream /AP to render such links as a complex shape of multiple rectangles in a single annotation. This would allow a reader to highlight the complete link if hovered or clicked and not only the active segment. An additional benefit would be the ability to support <area> hyperlinks with complex shapes.
There might be usability problems though, if readers chose to ignore /AP and use the simpler /Rect rectangle instead, which acts as a bounding box.
What version of the PDF spec was this introduced in? How is the support in various PDF readers? Is an annotation using /AP expected to also have /Rect as a fallback?
IIRC its available since PDF 1.2. /Rect is still required and acts as a fallback/bounding box.
As for the reader support I don't know. Readers have to support appearance streams for any non-rect annotations.
Readers have to support appearance streams for any non-rect annotations.
That's of course not true. PDF defines a set of presentational annotations as well, but limited to basic shapes.
For reference, the appearance streams are described in chapter 12.5.5 (PDF32000_2008).
It's should be possible with cairo's implementation of links.
It's should be possible with cairo's implementation of links.
It also should be possible with pydyf.
I couldn't get appearance streams to work, but QuadPoints seem to be the way to go for the normal usecase of multiple rects for a single link:
document = pydyf.PDF()
annot = pydyf.Dictionary({
"Type": "/Annot",
"Subtype": "/Link",
"Rect": pydyf.Array([ 1, 2, 9, 7 ]),
"QuadPoints": pydyf.Array([
# bl br tl tr (spec is wrong according to
# https://github.com/highkite/pdfAnnotate?tab=readme-ov-file#quadpoints)
1, 2, 6, 2, 1, 5, 6, 5,
4, 4, 7, 4, 4, 7, 7, 7,
]),
"Border": pydyf.Array([ 0, 0, 1 ]), # usually [0, 0, 0], but highlights the regions for this example
"C": pydyf.Array([ 1, 0, 1 ]),
"A": pydyf.Dictionary({
"S": "/URI",
"URI": pydyf.String("https://weasyprint.org/"),
}),
})
document.add_object(annot)
document.add_page(pydyf.Dictionary({
"Type": "/Page",
"Parent": document.pages.reference,
"Contents": pydyf.Array([]),
"Annots": pydyf.Array([ annot.reference, ]),
"MediaBox": pydyf.Array([0, 0, 10, 10]),
}))
Output: multi_rect_link.pdf
Tested with pdf.js and Preview.app.
I couldn't get appearance streams to work, but
QuadPointsseem to be the way to go for the normal usecase of multiple rects for a single link:
That’s interesting, thanks for sharing!