ENH: Support transparency channel (RGBA) in annotations interiour_color
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",
)
quad components in PDF means most of the time CMYK components. Transparency should be considered as an independent attribute
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,
)
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)
amazing, I will try it out next time I need it!
@sky-code you should push it into Discussion to ease people to find the information.
Omg I needed this too thank you so much @vors is it possible to add this feature to main?
Looks awesome! I definitely need that feature
:shipit: 🙏 Yes please!
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_annotationfor 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.
Ohh yeah, I need this feature as well 🙏