Skip to content

set_toc alters link coordinates for some rotated pages on pymupdf 1.24.2 #3400

@MaelkoM

Description

@MaelkoM

Description of the bug

Hi there!

First of all, I really appreciate this library and its performance and features.
Also, this is my first bug report on github, so I hope it's all right.

Now to the suspected bug:

Coordinates of GOTO links in newly created toc entries get altered when confronted with some rotated pages.
If a page is rotated by 270° (plus multiples of 360°), the given coordinates in links specified in new toc entries change after setting the new toc. This results in the links pointing to wrong positions.

Thanks!

How to reproduce the bug

  • Open or create a pdf document with a page that is rotated by 270°
  • Add toc entry with GOTO link to some coordinates on the rotated page
  • Set the new toc
  • Save document

Minimal Script

import fitz

width = 75
height = 111
circle_middle_point = fitz.Point(height / 4, width / 4)

with fitz.open() as doc:

    page = doc.new_page(width=width, height=height)
    page.set_rotation(270)
    # draw a circle at the middle point to facilitate debugging
    page.draw_circle(circle_middle_point, color=(0, 0, 1), radius=5, width=2)
    # rotate the middle point by the page rotation for the new toc entry
    toc_link_coords = circle_middle_point * page.rotation_matrix
    toc = [
        (
            1,
            "broken link to circle",
            1,
            {
                "kind": fitz.LINK_GOTO,
                "page": 0,
                "to": toc_link_coords,
            },
        )
    ]
    doc.set_toc(toc)  # set the toc
    doc.save(f"270_rotation_test_with_toc.pdf")

Additional info

  • Tested with python 3.10 and pymupdf 1.24.2 but I first noticed it a few months ago.
  • Tested on Windows 10/11 and Ubuntu 22 on different Intel and AMD CPUs
  • A fudge factor of (page height - page width) applied to the link coordinates can correct whatever miscalculation happens while setting the toc.

Example Script

The following example script implements this fudge factor and creates a document with multiple rotated pages:

import fitz

paper_size = fitz.paper_size("A4")
width = paper_size[0]
height = paper_size[1]

f_factor = height - width  # fudge factor

box_coords = (0, 0, paper_size[0] / 2, paper_size[1] / 2)
box_rect = fitz.Rect(box_coords)
middle_point = fitz.Point(
    (box_coords[0] + box_coords[2]) / 2, (box_coords[1] + box_coords[3]) / 2
)

toc = []
true_toc_link_coords = []

with fitz.open() as doc:

    for i in range(8):
        page = doc.new_page(width=width, height=height)
        page.insert_textbox(
            fitz.Rect(box_coords),
            buffer=f"page {i+1}\n{i * 90}° rotation",
            align=fitz.TEXT_ALIGN_CENTER,
            fontsize=50,
        )
        page.draw_rect(
            box_rect, color=(0, 1, 0), width=1
        )  # draw a rectangle to facilitate debugging

        # draw a circle at the middle point to facilitate debugging
        page.draw_circle(middle_point, color=(0, 0, 1), radius=10, width=5)

        page.set_rotation(90 * i)  # rotate the page by i * 90 degrees

        # rotate the middle point by the page rotation for the new toc entry
        toc_middle_point = middle_point * page.rotation_matrix
        true_toc_link_coords.append(toc_middle_point)

        if (page.rotation + 90) % 360 == 0:
            # fix thecoordinates for pages by subtracting/adding the fudge factor
            toc_middle_point = fitz.Point(
                (toc_middle_point[0] - f_factor, toc_middle_point[1] + f_factor)
            )

        toc.append(
            (
                1,
                f"page {i}",
                i + 1,
                {
                    "kind": fitz.LINK_GOTO,
                    "page": i,
                    "to": toc_middle_point,
                },
            )
        )

    doc.set_toc(toc)  # set the toc
    toc2 = doc.get_toc(simple=False)  # export the toc for debugging

    # show differences in GOTO links before and after setting the toc
    for i in range(len(toc)):
        toc_link_original = toc[i][3]["to"]
        toc_link_new = toc2[i][3]["to"]
        if toc_link_original != toc_link_new:
            print(f"ToC link for page {i + 1} had to be fixed.")
            print(f"desired coords: {true_toc_link_coords[i]}")
            print(f"coords with fudge factor before set_toc: {toc_link_original}")
            print(f"resulting coords after set_toc: {toc_link_new}\n")

    doc.save(f"rotation_test_with_toc.pdf")

PyMuPDF version

1.24.2

Operating system

Windows

Python version

3.10

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions