最新消息:Welcome to the puzzle paradise for programmers! Here, a well-designed puzzle awaits you. From code logic puzzles to algorithmic challenges, each level is closely centered on the programmer's expertise and skills. Whether you're a novice programmer or an experienced tech guru, you'll find your own challenges on this site. In the process of solving puzzles, you can not only exercise your thinking skills, but also deepen your understanding and application of programming knowledge. Come to start this puzzle journey full of wisdom and challenges, with many programmers to compete with each other and show your programming wisdom! Translated with DeepL.com (free version)

python - How to change color of border and font in an inline textfreetext annotation in a pdf document? - Stack Overflow

matteradmin9PV0评论

I'm writing a python script to change color of text and borders of inline annotations inserted with okular into a pdf document.

This script instead of changing only text and border seems change the entire annotation red (full filled red rectangle shows up instead).

Is this supported at all? I realize how complex pdf format is, but I hoped that this simple change won't require anything unusual.

sample inline text annotation in pdf as produced by okular:

11533   │ 229·13·obj␍␊
11534   │ <</Length·293·/Subtype·/Form·/BBox·[0·0·232.7894972067·28.173484252·]·/Resources·<</Font·<</popplerfont·227·0·R·>>·>>·>>·stream␍␊
11535   │ q␊
11536   │ []·0·d␊
11537   │ 2.00·w␊
11538   │ 0.00000·0.00000·0.00000·RG␊
11539   │ 1.00·1.00·230.79·26.17·re␊
11540   │ 0.21569·0.18824·1.00000·rg␊
11541   │ B␊
11542   │ 4.00·4.00·224.79·20.17·re·W·n␊
11543   │ 0.00000·0.00000·0.00000·rg␊
11544   │ BT·1·0·0·1·4.00·24.17·Tm␊
11545   │ /popplerfont·10.00·Tf␊
11546   │ 0.00·-10.00·Td␊
11547   │ (\000e\000r\000g\000r\000g\000h\000r\000e\000h\000r\000h\000e\000h)·Tj␊
11548   │ ET·Q␊
11549   │ ␍␊
11550   │ endstream␍␊
11551   │ ␍␊
11552   │ endobj␍␊
11553   │ 230·0·obj␍␊
11554   │ <</Type·/Annot·/Rect·[505.4857653631·525.0882627953·507.5172614261·534.4675688976·]·/Subtype·/FreeText·/DA·(/popplerfont·10.00·Tf\n)·/M·(D:20241117102025Z␀································)·/T·<feff00750073006500720031>·/Contents·<feff>·/NM·(okular-{a598c121-968e-4134-8fa3-b6d17caa86f2})·/CreationDate·(D:20241117102025Z␀································)·/F·20·/C·[0.2156862766·0.1882352978·1·]·/CA·1·/Border·[0·0·2·]·/Q·0·/IT·/FreeText·/P·2·0·R·/AP·<</N·231·0·R·>>·/AS·/N·>>·␍␊
11555   │ endobj␍␊
11556   │ 231·0·obj␍␊
11557   │ <</Length·222·/Subtype·/Form·/BBox·[0·0·2.031496063·9.3793061024·]·/Resources·<</Font·<</popplerfont·227·0·R·>>·>>·>>·stream␍␊
11558   │ q␊
11559   │ []·0·d␊
11560   │ 2.00·w␊
11561   │ 0.00000·0.00000·0.00000·RG␊
11562   │ 1.00·1.00·0.03·7.38·re␊
11563   │ 0.21569·0.18824·1.00000·rg␊
11564   │ B␊
11565   │ 4.00·4.00·-5.97·1.38·re·W·n␊
11566   │ 0.00000·0.00000·0.00000·rg␊
11567   │ BT·1·0·0·1·4.00·5.38·Tm␊
11568   │ /popplerfont·10.00·Tf␊
11569   │ 0.00·-10.00·Td␊
11570   │ ()·Tj␊
11571   │ ET·Q␊
11572   │ ␍␊
11573   │ endstream␍␊
11574   │ ␍␊
11575   │ endobj␍␊
11576   │ xref␍␊

and here converted to qpdf format:

40687   │ 0183013890·00000·n·␊
40688   │ 0185638157·00000·n·␊
40689   │ trailer·<<␊
40690   │ ··/Info·2·0·R␊
40691   │ ··/Root·1·0·R␊
40692   │ ··/Size·372␊
40693   │ ··/ID·[<fa440df8ed780aedfcf5266a1bfed4ad><fa440df8ed780aedfcf5266a1bfed4ad>]␊
40694   │ >>␊
40695   │ startxref␊
40696   │ 185638183␊
40697   │ %%EOF␊
40698   │ 1·0·obj␍␊
40699   │ <</Pages·3·0·R·/Type·/Catalog·/AcroForm·373·0·R·>>·␍␊
40700   │ endobj␍␊
40701   │ 4·0·obj␍␊
40702   │ <</Contents·68·0·R·/MediaBox·[0·0·595.276·841.89·]·/Parent·3·0·R·/Resources·<</Font·<</F4·70·0·R·/F5·71·0·R·/F6·72·0·R·>>·/ProcSet·73·0·R·/XObject·<</Im3·74·0·R·/Im7·76·0·R·>>·>>·/Type·/Page·/Annots·378·0·R·>>·␍␊
40703   │ endobj␍␊
40704   │ 372·0·obj␍␊
40705   │ <</Type·/Annot·/Rect·[142.1173581129·531.1166868381·347.6343759828·552.3443721634·]·/Subtype·/FreeText·/DA·(/popplerfont·10.00·Tf\n)·/M·(D:20241117100201Z␀································)·/T·<feff00750073006500720031>·/Contents·<feff00750069006500620072006700730075006b0067006200730073006b00650075>·/NM·(okular-{c67d430b-37dd-4ba6-85c6-45a45db4e512})·/CreationDate·(D:20241117100143Z␀································)·/F·20·/C·[0.2156862766·0.1882352978·1·]·/CA·1·/Border·[0·0·2·]·/Q·0·/IT·/FreeText·/P·4·0·R·/AP·<</N·379·16·R·>>·/AS·/N·>>·␍␊
40706   │ endobj␍␊
40707   │ 373·0·obj␍␊
40708   │ <</Fields·[]·/DR·<</Font·<</popplerfont·377·0·R·>>·>>·>>·␍␊
40709   │ endobj␍␊
40710   │ 374·0·obj␍␊
40711   │ <</Length·210957·/Filter·/FlateDecode·>>·stream␍␊
#!/usr/bin/env python3

import sys
import pikepdf
from pikepdf import Name
import re

def change_annotation_font_and_border_to_red(input_path):
    output_path = input_path.replace(".pdf", "-output.pdf")

    # Open the PDF file
    with pikepdf.open(input_path) as pdf:
        for page_number, page in enumerate(pdf.pages, start=1):
            # Check if the page has annotations
            if "/Annots" in page:
                print(f"Page {page_number} contains annotations:")
                # Iterate over each annotation in the /Annots array
                for annot in page["/Annots"]:
                    print(f"  - Annotation detected: {annot}")

                    # Set the border color to red (RGB)
                    annot[Name("/C")] = pikepdf.Array([1, 0, 0])  # RGB for red border

                    # Update the default appearance string (/DA) to set text color to red
                    if "/DA" in annot:
                        da_str = str(annot[Name("/DA")])
                        print(f"    Original /DA: {da_str}")
                        # Remove any existing color settings in the DA string
                        da_str = re.sub(r"(\d+\.?\d*\s){1,3}[rg]", "", da_str)
                        # Append red color setting for text color
                        da_str = da_str.strip() + " 1 0 0 rg"
                        annot[Name("/DA")] = da_str
                        print(f"    Updated /DA: {annot[Name('/DA')]}")

                    # Remove the appearance stream to apply changes if present
                    if "/AP" in annot:
                        del annot[Name("/AP")]
                        print("    Removed /AP to force appearance regeneration")

        # Save the modified PDF with '-output.pdf' suffix
        pdf.save(output_path)
        print(f"Font and border color changed to red. Saved as: {output_path}")

if __name__ == "__main__":
    if len(sys.argv) != 2:
        print("Usage: python change_annotation_font_and_border_to_red.py <input-pdf-path>")
    else:
        input_path = sys.argv[1]
        change_annotation_font_and_border_to_red(input_path)

I'm writing a python script to change color of text and borders of inline annotations inserted with okular into a pdf document.

This script instead of changing only text and border seems change the entire annotation red (full filled red rectangle shows up instead).

Is this supported at all? I realize how complex pdf format is, but I hoped that this simple change won't require anything unusual.

sample inline text annotation in pdf as produced by okular:

11533   │ 229·13·obj␍␊
11534   │ <</Length·293·/Subtype·/Form·/BBox·[0·0·232.7894972067·28.173484252·]·/Resources·<</Font·<</popplerfont·227·0·R·>>·>>·>>·stream␍␊
11535   │ q␊
11536   │ []·0·d␊
11537   │ 2.00·w␊
11538   │ 0.00000·0.00000·0.00000·RG␊
11539   │ 1.00·1.00·230.79·26.17·re␊
11540   │ 0.21569·0.18824·1.00000·rg␊
11541   │ B␊
11542   │ 4.00·4.00·224.79·20.17·re·W·n␊
11543   │ 0.00000·0.00000·0.00000·rg␊
11544   │ BT·1·0·0·1·4.00·24.17·Tm␊
11545   │ /popplerfont·10.00·Tf␊
11546   │ 0.00·-10.00·Td␊
11547   │ (\000e\000r\000g\000r\000g\000h\000r\000e\000h\000r\000h\000e\000h)·Tj␊
11548   │ ET·Q␊
11549   │ ␍␊
11550   │ endstream␍␊
11551   │ ␍␊
11552   │ endobj␍␊
11553   │ 230·0·obj␍␊
11554   │ <</Type·/Annot·/Rect·[505.4857653631·525.0882627953·507.5172614261·534.4675688976·]·/Subtype·/FreeText·/DA·(/popplerfont·10.00·Tf\n)·/M·(D:20241117102025Z␀································)·/T·<feff00750073006500720031>·/Contents·<feff>·/NM·(okular-{a598c121-968e-4134-8fa3-b6d17caa86f2})·/CreationDate·(D:20241117102025Z␀································)·/F·20·/C·[0.2156862766·0.1882352978·1·]·/CA·1·/Border·[0·0·2·]·/Q·0·/IT·/FreeText·/P·2·0·R·/AP·<</N·231·0·R·>>·/AS·/N·>>·␍␊
11555   │ endobj␍␊
11556   │ 231·0·obj␍␊
11557   │ <</Length·222·/Subtype·/Form·/BBox·[0·0·2.031496063·9.3793061024·]·/Resources·<</Font·<</popplerfont·227·0·R·>>·>>·>>·stream␍␊
11558   │ q␊
11559   │ []·0·d␊
11560   │ 2.00·w␊
11561   │ 0.00000·0.00000·0.00000·RG␊
11562   │ 1.00·1.00·0.03·7.38·re␊
11563   │ 0.21569·0.18824·1.00000·rg␊
11564   │ B␊
11565   │ 4.00·4.00·-5.97·1.38·re·W·n␊
11566   │ 0.00000·0.00000·0.00000·rg␊
11567   │ BT·1·0·0·1·4.00·5.38·Tm␊
11568   │ /popplerfont·10.00·Tf␊
11569   │ 0.00·-10.00·Td␊
11570   │ ()·Tj␊
11571   │ ET·Q␊
11572   │ ␍␊
11573   │ endstream␍␊
11574   │ ␍␊
11575   │ endobj␍␊
11576   │ xref␍␊

and here converted to qpdf format:

40687   │ 0183013890·00000·n·␊
40688   │ 0185638157·00000·n·␊
40689   │ trailer·<<␊
40690   │ ··/Info·2·0·R␊
40691   │ ··/Root·1·0·R␊
40692   │ ··/Size·372␊
40693   │ ··/ID·[<fa440df8ed780aedfcf5266a1bfed4ad><fa440df8ed780aedfcf5266a1bfed4ad>]␊
40694   │ >>␊
40695   │ startxref␊
40696   │ 185638183␊
40697   │ %%EOF␊
40698   │ 1·0·obj␍␊
40699   │ <</Pages·3·0·R·/Type·/Catalog·/AcroForm·373·0·R·>>·␍␊
40700   │ endobj␍␊
40701   │ 4·0·obj␍␊
40702   │ <</Contents·68·0·R·/MediaBox·[0·0·595.276·841.89·]·/Parent·3·0·R·/Resources·<</Font·<</F4·70·0·R·/F5·71·0·R·/F6·72·0·R·>>·/ProcSet·73·0·R·/XObject·<</Im3·74·0·R·/Im7·76·0·R·>>·>>·/Type·/Page·/Annots·378·0·R·>>·␍␊
40703   │ endobj␍␊
40704   │ 372·0·obj␍␊
40705   │ <</Type·/Annot·/Rect·[142.1173581129·531.1166868381·347.6343759828·552.3443721634·]·/Subtype·/FreeText·/DA·(/popplerfont·10.00·Tf\n)·/M·(D:20241117100201Z␀································)·/T·<feff00750073006500720031>·/Contents·<feff00750069006500620072006700730075006b0067006200730073006b00650075>·/NM·(okular-{c67d430b-37dd-4ba6-85c6-45a45db4e512})·/CreationDate·(D:20241117100143Z␀································)·/F·20·/C·[0.2156862766·0.1882352978·1·]·/CA·1·/Border·[0·0·2·]·/Q·0·/IT·/FreeText·/P·4·0·R·/AP·<</N·379·16·R·>>·/AS·/N·>>·␍␊
40706   │ endobj␍␊
40707   │ 373·0·obj␍␊
40708   │ <</Fields·[]·/DR·<</Font·<</popplerfont·377·0·R·>>·>>·>>·␍␊
40709   │ endobj␍␊
40710   │ 374·0·obj␍␊
40711   │ <</Length·210957·/Filter·/FlateDecode·>>·stream␍␊
#!/usr/bin/env python3

import sys
import pikepdf
from pikepdf import Name
import re

def change_annotation_font_and_border_to_red(input_path):
    output_path = input_path.replace(".pdf", "-output.pdf")

    # Open the PDF file
    with pikepdf.open(input_path) as pdf:
        for page_number, page in enumerate(pdf.pages, start=1):
            # Check if the page has annotations
            if "/Annots" in page:
                print(f"Page {page_number} contains annotations:")
                # Iterate over each annotation in the /Annots array
                for annot in page["/Annots"]:
                    print(f"  - Annotation detected: {annot}")

                    # Set the border color to red (RGB)
                    annot[Name("/C")] = pikepdf.Array([1, 0, 0])  # RGB for red border

                    # Update the default appearance string (/DA) to set text color to red
                    if "/DA" in annot:
                        da_str = str(annot[Name("/DA")])
                        print(f"    Original /DA: {da_str}")
                        # Remove any existing color settings in the DA string
                        da_str = re.sub(r"(\d+\.?\d*\s){1,3}[rg]", "", da_str)
                        # Append red color setting for text color
                        da_str = da_str.strip() + " 1 0 0 rg"
                        annot[Name("/DA")] = da_str
                        print(f"    Updated /DA: {annot[Name('/DA')]}")

                    # Remove the appearance stream to apply changes if present
                    if "/AP" in annot:
                        del annot[Name("/AP")]
                        print("    Removed /AP to force appearance regeneration")

        # Save the modified PDF with '-output.pdf' suffix
        pdf.save(output_path)
        print(f"Font and border color changed to red. Saved as: {output_path}")

if __name__ == "__main__":
    if len(sys.argv) != 2:
        print("Usage: python change_annotation_font_and_border_to_red.py <input-pdf-path>")
    else:
        input_path = sys.argv[1]
        change_annotation_font_and_border_to_red(input_path)
Share Improve this question edited Nov 17, 2024 at 10:27 vqqkomb0 asked Nov 16, 2024 at 19:50 vqqkomb0vqqkomb0 11 bronze badge 1
  • thanks for looking into this, I've updated the question with examples of inline text annotations as produced by okular – vqqkomb0 Commented Nov 17, 2024 at 10:26
Add a comment  | 

1 Answer 1

Reset to default 0

You can round-trip the annotations with Cpdf. First:

cpdf -list-annotations-json in.pdf > out.json

This creates a JSON file with the annotations (and any associated objects). Now, edit the JSON as you wish. Then, we can re-apply them:

cpdf -remove-annotations in.pdf AND -set-annotations-json out.json -o out.pdf

More details in Chapter 10 of the manual.

Articles related to this article

Post a comment

comment list (0)

  1. No comments so far