Skip to content

Add hide_icon option to file attachment annotations - #1924

Merged
andersonhc merged 4 commits into
py-pdf:masterfrom
ChrisJr404:hide-file-attachment-icon
Sep 2, 2026
Merged

Add hide_icon option to file attachment annotations#1924
andersonhc merged 4 commits into
py-pdf:masterfrom
ChrisJr404:hide-file-attachment-icon

Conversation

@ChrisJr404

Copy link
Copy Markdown

This adds a hide_icon parameter to FPDF.file_attachment_annotation() so the default icon a viewer draws for an attachment (the paperclip, pushpin, etc.) can be suppressed while the file itself stays embedded and reachable, which is what was asked for in #561.

The mechanism is the portable one from the PDF spec: when hide_icon=True, the annotation gets an empty normal appearance stream (/AP << /N ... >>) pointing at a blank Form XObject. Conforming readers render the appearance stream instead of their built-in icon, and since it is empty, nothing is drawn. The /FS file specification is untouched, so the attachment can still be opened or extracted from the annotation.

The default is False, so existing output is unchanged. I wired the appearance stream up at output time (once the XObject has an object id) next to where embedded files are added, and kept the mechanism generic on the annotation itself in case other annotation types want appearance streams later.

Checklist:

  • A unit test is covering the code added / modified by this PR
  • In case of a new feature, docstrings have been added, with also some documentation in the docs/ folder
  • A mention of the change is present in CHANGELOG.md
  • This PR is ready to be merged

By submitting this pull request, I confirm that my contribution is made under the terms of the GNU LGPL 3.0 license.

PDF viewers render a default icon (a paperclip, a pushpin, ...) for a
file attachment annotation. Add a hide_icon parameter to
FPDF.file_attachment_annotation() that gives the annotation an empty
normal appearance stream, so conforming viewers draw no icon while the
file stays embedded and reachable through the annotation. Cf. issue py-pdf#561.
@andersonhc

andersonhc commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

The Vera PDF check is failing:

Non-whitelisted issues found: 6.5.3-6

This happens because the current implementation of hide_icon creates a normal appearance that is not valid for PDF/A:

ISO 19005-1:2005 - 6.5.3 Annotation dictionaries - Normal appearance - If an annotation dictionary's Subtype key has value other than Widget, or if FT key associated with Widget annotation has value other than Btn, the value of the N key shall be an appearance stream

For a FileAttachment annotation, the /N entry of the appearance dictionary must be an appearance stream. The representation currently used by hide_icon therefore isn't PDF/A compliant.

To proceed with this PR, we need to:

  1. Change scripts/vera-ignore.json to add 6.5.3-6 as ignored code
  2. Change your implementation to raise PDFAComplianceError if hide_icon and _compliance are both True.

Comment thread fpdf/fpdf.py Outdated
h: float = 1,
name: Optional[FileAttachmentAnnotationName | str] = None,
flags: tuple[AnnotationFlag | str, ...] = DEFAULT_ANNOT_FLAGS,
hide_icon: bool = False,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe appearance or something similar would be better here than hide_icon, since we can do another enhancement later to add custom appearances for the icon.

I believe we should add to enums.py:

class FileAttachmentAppearance(CoerciveEnum):
    DEFAULT = "DEFAULT"
    HIDDEN = "HIDDEN"  # I'm not sure if HIDDEN or BLANK is the best term here

then the parameter becomes:

Suggested change
hide_icon: bool = False,
appearance: FileAttachmentAppearance | str = FileAttachmentAppearance.DEFAULT,

Later we could expand appearance to accept a DrawingContext or an image (SVG, PNG, etc)

@ChrisJr404

Copy link
Copy Markdown
Author

Thanks for digging into the VeraPDF failure. I pushed both changes: added 6.5.3-6 to scripts/verapdf-ignore.json, and hide_icon now raises PDFAComplianceError when the document enforces PDF/A, so the empty appearance stream only ever lands in regular PDFs. Added a test for the new guard too.

@andersonhc

Copy link
Copy Markdown
Collaborator

Thanks for digging into the VeraPDF failure. I pushed both changes: added 6.5.3-6 to scripts/verapdf-ignore.json, and hide_icon now raises PDFAComplianceError when the document enforces PDF/A, so the empty appearance stream only ever lands in regular PDFs. Added a test for the new guard too.

Nice. The PDF validation is passing now - the only job failing is the lint job because black doesn't pass on test_pdf_a_embed.py

Can you also check the other review comments I made?

Thanks.

@ChrisJr404

Copy link
Copy Markdown
Author

Thanks. Ran black 26.3.1 on test_pdf_a_embed.py and pushed the fix, so the lint job should pass now. On the appearance enum suggestion: agreed, a FileAttachmentAppearance (DEFAULT/HIDDEN) reads better than a boolean and leaves room to grow into custom icon appearances later. I'll rework the parameter along those lines in a follow-up commit.

@ChrisJr404

Copy link
Copy Markdown
Author

Reworked per your review:

  • Added FileAttachmentAppearance(CoerciveEnum) to enums.py with DEFAULT and HIDDEN.
  • The parameter is now appearance: FileAttachmentAppearance | str = FileAttachmentAppearance.DEFAULT instead of hide_icon. It is coerced, and HIDDEN produces the empty appearance stream (still guarded against PDF/A). This leaves room to later accept a DrawingContext or an image for a custom icon, as you suggested.
  • Updated the two tests, the CHANGELOG entry, and docs/FileAttachments.md to the new API.

black now passes on test_pdf_a_embed.py and the rest, and the embed-file plus PDF/A test modules pass locally (26 passed).

@andersonhc

Copy link
Copy Markdown
Collaborator

@allcontributors please add @ChrisJr404 for code

@allcontributors

Copy link
Copy Markdown

@andersonhc

I've put up a pull request to add @ChrisJr404! 🎉

@andersonhc
andersonhc merged commit c397016 into py-pdf:master Sep 2, 2026
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants