WeasyPrint icon indicating copy to clipboard operation
WeasyPrint copied to clipboard

Consider using an appearance stream to render links in PDF

Open cleitner opened this issue 11 years ago • 8 comments

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.

cleitner avatar Apr 22 '14 20:04 cleitner

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?

SimonSapin avatar Apr 23 '14 13:04 SimonSapin

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.

cleitner avatar Apr 23 '14 13:04 cleitner

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).

cleitner avatar Apr 24 '14 14:04 cleitner

It's should be possible with cairo's implementation of links.

liZe avatar Mar 01 '19 16:03 liZe

It's should be possible with cairo's implementation of links.

It also should be possible with pydyf.

liZe avatar Jan 16 '21 14:01 liZe

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.

cleitner avatar Jun 25 '24 04:06 cleitner

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:

That’s interesting, thanks for sharing!

liZe avatar Jun 25 '24 04:06 liZe