Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH: Support transparency channel (RGBA) in annotations interiour_color #2201

Open
vors opened this issue Sep 18, 2023 · 10 comments
Open

ENH: Support transparency channel (RGBA) in annotations interiour_color #2201

vors opened this issue Sep 18, 2023 · 10 comments
Labels
is-feature A feature request

Comments

@vors
Copy link

vors commented Sep 18, 2023

Explanation

I'd like to do highlighting of a certain parts of the pdf, but I want it to be transept so the original pdf is readable.
The proposal is to support RGBA in annotation to allow for it

Code Example

How would your feature be used? (Remove this if it is not applicable.)

  annotation = annotation_builder.rectangle(
      rect=(x[0], y[0], x[0] + 10, y[0] + 30),
      # transparency part of the RGBA is currently ignored, can we support it?
      interiour_color="f1e740aa",
  )
@MartinThoma MartinThoma removed their assignment Sep 18, 2023
@MartinThoma MartinThoma added the is-feature A feature request label Sep 18, 2023
@MartinThoma MartinThoma changed the title Support transparency channel (RGBA) in annotations interiour_color ENH: Support transparency channel (RGBA) in annotations interiour_color Sep 18, 2023
@pubpub-zz
Copy link
Collaborator

quad components in PDF means most of the time CMYK components. Transparency should be considered as an independent attribute

@vors
Copy link
Author

vors commented Sep 18, 2023

sounds good to me, so something like?

annotation = annotation_builder.rectangle(
      rect=(x[0], y[0], x[0] + 10, y[0] + 30),
      interiour_color="f1e740",
      transparency=0.5,
  )

@sky-code
Copy link

sky-code commented Nov 4, 2023

Here is a function I created to set opacity for annotation

def annotation_set_opacity(annotation: AnnotationDictionary, opacity: float):
    pdf_writer: PdfWriter = annotation.indirect_reference.pdf
    rect = cast(RectangleObject, annotation["/Rect"])
    x = rect[0]
    y = rect[1]
    width = rect.width
    height = rect.height
    line_width = 1
    color = cast(ArrayObject, annotation["/IC"])

    # https://pikepdf.readthedocs.io/en/latest/topics/content_streams.html
    # https://ghostscript.com/~robin/pdf_reference17.pdf
    ap_stream = (
        "q\n"  # Save the current graphics state on the graphics state stack (see “Graphics State Stack” on page 214).
        "/H gs\n"  # Set the specified parameters in the graphics state. dictName is the name of a
        # graphics state parameter dictionary in the ExtGState subdictionary of the current resource dictionary
        f"{line_width} w\n"  # Set the line width in the graphics state (see “Line Width” on page 215).
        f"{color[0]} {color[1]} {color[2]} rg\n"  # Set nonstroking colour
        f"{x} {y} {width} {height} re\n"  # Construct rectangular path
        # x y width height re
        # Append a rectangle to the current path as a complete subpath, with
        # lower-left corner (x, y) and dimensions width and height in user
        # space.
        # page 227
        "f\n"  # Fill path
        "\nQ"  # Pop graphics stack.
    ).encode()

    try:
        x_object = cast(DictionaryObject, annotation["/AP"])["/N"]
        x_object = cast(DecodedStreamObject, x_object)
        x_object.set_data(ap_stream)
    except KeyError:
        x_object = DecodedStreamObject.initialize_from_dictionary(
            {
                NameObject("/Type"): NameObject("/XObject"),
                NameObject("/Subtype"): NameObject("/Form"),
                NameObject("/BBox"): rect,
                NameObject("/Matrix"): ArrayObject(
                    [
                        NumberObject(1),
                        NumberObject(0),
                        NumberObject(0),
                        NumberObject(1),
                        NumberObject(0),
                        NumberObject(0),
                    ]
                ),
                NameObject("/Resources"): DictionaryObject(),
                "__streamdata__": ByteStringObject(ap_stream),
                "/Length": 0,
            }
        )
    resources = cast(DictionaryObject, x_object.setdefault(NameObject("/Resources"), DictionaryObject()))

    extg = cast(DictionaryObject, resources.setdefault(NameObject("/ExtGState"), DictionaryObject()))
    ext_g_state_h = cast(DictionaryObject, extg.setdefault(NameObject("/H"), DictionaryObject()))
    ext_g_state_h[NameObject("/ca")] = FloatObject(opacity)
    ext_g_state_h[NameObject("/CA")] = FloatObject(opacity)

    # noinspection PyProtectedMember
    pdf_writer._add_object(x_object)
    annotation_ap = cast(DictionaryObject, annotation.setdefault(NameObject("/AP"), DictionaryObject()))
    annotation_ap[NameObject("/N")] = x_object.indirect_reference
    annotation[NameObject("/CA")] = FloatObject(opacity)

must be called after writer.add_annotation for example:

writer.add_annotation(page_number=0, annotation=annotation)
annotation_set_opacity(annotation, 0.3)

@vors
Copy link
Author

vors commented Nov 4, 2023

amazing, I will try it out next time I need it!

@pubpub-zz
Copy link
Collaborator

@sky-code you should push it into Discussion to ease people to find the information.

@avishaan
Copy link

avishaan commented Nov 5, 2023

Omg I needed this too thank you so much @vors is it possible to add this feature to main?

@ofilimonov
Copy link

Looks awesome! I definitely need that feature

@msisto
Copy link

msisto commented Nov 6, 2023

:shipit: 🙏 Yes please!

@andy71
Copy link

andy71 commented Nov 7, 2023

Here is a function I created to set opacity for annotation

def annotation_set_opacity(annotation: AnnotationDictionary, opacity: float):
    ...

must be called after writer.add_annotation for example:

writer.add_annotation(page_number=0, annotation=annotation)
annotation_set_opacity(annotation, 0.3)

Great work, but could it be that this only works for annotations on the first page? Or does I missed something?
If I add the annotation to the second page and set the opacity with your function, the result shows a full transparent rectangle on the second page and the colored background with opacity on the first page.

@aglov
Copy link

aglov commented Nov 10, 2023

Ohh yeah, I need this feature as well 🙏

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
is-feature A feature request
Projects
None yet
Development

No branches or pull requests

9 participants