Skip to content

bug: IndexError for Page.get_links after Page.clip_to_rect #4942

Closed

Description

@mahlzahn

Description of the bug

On some sample PDF files, I get following IndexError, trying to get their links:

/usr/lib/python3.14/site-packages/pymupdf/__init__.py in ?(page)
  11835         links = []
  11836         while ln:
  11837             nl = utils.getLinkDict(ln, page.parent)
  11838             links.append(nl)
> 11839             ln = ln.next
  11840         if links != [] and page.parent.is_pdf:
  11841             linkxrefs = [x for x in
  11842                     #page.annot_xrefs()

/usr/lib/python3.14/site-packages/pymupdf/__init__.py in ?(self)
   8588             if self.xref > 0:  # prev link has an xref
   8589                 link_xrefs = [x[0] for x in self.parent.annot_xrefs() if x[1] == mupdf.PDF_ANNOT_LINK]
   8590                 link_ids = [x[2] for x in self.parent.annot_xrefs() if x[1] == mupdf.PDF_ANNOT_LINK]
   8591                 idx = link_xrefs.index(self.xref)
-> 8592                 val.xref = link_xrefs[idx + 1]
   8593                 val.id = link_ids[idx + 1]
   8594             else:
   8595                 val.xref = 0

IndexError: list index out of range

How to reproduce the bug

  1. Get sample file from here: https://github.com/ArtifexSoftware/tests/blob/master/pdf/Transparency-DesignGuide.pdf
  2. Run following python code:
document = pymupdf.Document("Transparency-DesignGuide.pdf")
page = document[0]
page.clip_to_rect(page.cropbox)
page.get_links()

PyMuPDF version

1.27.1

Operating system

Linux

Python version

3.14

Activity

  1. JorjMcKie commented on Mar 18, 2026

    @JorjMcKie
    Collaborator

    When do page.clip_to_rect(...) the page content can be mutilated in a subtle way - such that not all internally caches kept by MuPDF have already been resynchronized.
    This happens in your case.
    If you execute get_links() before clip_to_rect() things will work, as well as when you force resynchronization with this statement:

    import pymupdf
    
    document = pymupdf.Document("Transparency-DesignGuide.pdf")
    page = document[0]
    page.clip_to_rect(page.cropbox)
    page = document.reload_page(page)  # <===
    page.get_links()
  2. mahlzahn commented on Mar 18, 2026

    @mahlzahn
    Author

    Thank you! This solves the issue for me, maybe it would be good to document it in https://pymupdf.readthedocs.io/en/latest/page.html#Page.clip_to_rect

    The method roughly has the same effect as if four redactions had been applied that cover the rectangle’s outside.

    mislead me a bit ;)

  3. JorjMcKie commented on Mar 18, 2026

    @JorjMcKie
    Collaborator

    The true resolution would be to do this resynchronization internally at the right place - e.g. before returning from clip_to_rect().

    BTW:
    Be extremely careful using page.cropbox: You seem to assume that this must equal the page rectangle coordinates ... this is wrong!

    • page.rect and page.cropbox are systematically different if page width and height are different und the page is rotated by 90 or 270.
    • They are also always different if the cropboxes top left coordinates are not (0, 0).
  4. JorjMcKie commented on Mar 18, 2026

    @JorjMcKie
    Collaborator

    Thank you! This solves the issue for me, maybe it would be good to document it in https://pymupdf.readthedocs.io/en/latest/page.html#Page.clip_to_rect

    The method roughly has the same effect as if four redactions had been applied that cover the rectangle’s outside.

    mislead me a bit ;)

    The word "roughly" was meant seriously: redactions ...

    • remove all characters whose boundary box is not disjoint from all redaction annots
    • mutilate images that lap into annot rects

    page.clip_to_rect() does not remove characters that lap into the rectangle and it leaves images complete untouched.

  5. mahlzahn commented on Mar 18, 2026

    @mahlzahn
    Author

    Be extremely careful using page.cropbox

    Thanks for your warning. Indeed, I was using it here in the reproducer examples but should have used page.bound() or page.rect instead.

    The word "roughly" was meant seriously […]

    Yeah, instead of using page.clip_to_rect() I implemented it with four almost infinite redaction annotations which let’s me control also how the removal is performed.

  6. JorjMcKie commented on Mar 22, 2026

    @JorjMcKie
    Collaborator

    The fix immunizes annotations / links against being invalidated by the method.

  7. julian-smith-artifex-com commented on Apr 24, 2026

    @julian-smith-artifex-com
    Collaborator

    Fixed in pymupdf-1.27.2.3.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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