Kozea / WeasyPrint

The awesome document factory
https://weasyprint.org
BSD 3-Clause "New" or "Revised" License
7.21k stars 684 forks source link

Consider using an appearance stream to render links in PDF #185

Open cleitner opened 10 years ago

cleitner commented 10 years ago

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.

SimonSapin commented 10 years ago

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?

cleitner commented 10 years ago

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 commented 10 years ago

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

liZe commented 5 years ago

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

liZe commented 3 years ago

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

It also should be possible with pydyf.

cleitner commented 4 months ago

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.

liZe commented 4 months ago

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!