If I have already a link annotation on the current page and I run a dry-run of FPDF.multi_cell(...) with markdown active and a link in the text, the 2nd link remains permanently on the page although the dry-run should stop this behavior.
Error details
No error, just a clickable area as artifact where no clickable area should be!
Minimal code
import fpdf
pdf = fpdf.FPDF()
pdf.set_font("helvetica")
# Empty page, no annotations
pdf.add_page()
assert len(pdf.pages[1].annots) == 0, len(pdf.pages[1].annots)
# Dry run without former link
pdf.multi_cell(
w=0,
text="This is a [md link](www.example.com)",
dry_run=True,
markdown=True,
new_x=fpdf.XPos.LEFT,
new_y=fpdf.YPos.NEXT,
)
assert len(pdf.pages[1].annots) == 0, len(pdf.pages[1].annots)
# Create link
pdf.multi_cell(
w=0,
text="This is a [md link](www.example.com)",
markdown=True,
new_x=fpdf.XPos.LEFT,
new_y=fpdf.YPos.NEXT,
)
assert len(pdf.pages[1].annots) == 1, len(pdf.pages[1].annots)
# Dry run with former link
pdf.multi_cell(
w=0,
text="This is a [md link](www.example.com)",
dry_run=True,
markdown=True,
new_x=fpdf.XPos.LEFT,
new_y=fpdf.YPos.NEXT,
)
assert len(pdf.pages[1].annots) == 1, len(pdf.pages[1].annots) # ISSUE
pdf.output("bug.pdf")
Environment
Please provide the following information:
- Operating System: Windows
- Python version: 3.12
fpdf2 version used: Master
Solution
The error originates from the function FPDF. _disable_writing(...) L4689:
The code assigns the former PDFArray of annotations of the current page to the local var annots.
After executing the wrapped function FPDF.multi_cell(...), the local var annots is assigned "back" to the page.
Since the PDFArray is basically a list, the changes of annotations in FPDF.multi_cell(...) (in self.pages[self.page].annots) will end up also in the local variable annots since both refer to the same underlying PDFArray (list).
So, annots must create a copy of the original self.pages[self.page].annots-instance: Either by wrapping it in a new PDFArray-instance (easy solution) or by using proper type collections.UserList so that the PDFArray is a "real" subclass of a list and so that PDFArray.copy() returns a copy of a PDFArray and no list (more complex).
If I have already a link annotation on the current page and I run a dry-run of
FPDF.multi_cell(...)with markdown active and a link in the text, the 2nd link remains permanently on the page although the dry-run should stop this behavior.Error details
No error, just a clickable area as artifact where no clickable area should be!
Minimal code
Environment
Please provide the following information:
fpdf2version used: MasterSolution
The error originates from the function
FPDF. _disable_writing(...)L4689:The code assigns the former PDFArray of annotations of the current page to the local var
annots.After executing the wrapped function
FPDF.multi_cell(...), the local varannotsis assigned "back" to the page.Since the PDFArray is basically a list, the changes of annotations in
FPDF.multi_cell(...)(inself.pages[self.page].annots) will end up also in the local variableannotssince both refer to the same underlying PDFArray (list).So,
annotsmust create a copy of the originalself.pages[self.page].annots-instance: Either by wrapping it in a new PDFArray-instance (easy solution) or by using proper typecollections.UserListso that the PDFArray is a "real" subclass of a list and so thatPDFArray.copy()returns a copy of a PDFArray and no list (more complex).