如何修改PDF中Inline Text/FreeText注释的边框与字体颜色
问题描述
我正在编写Python脚本,想要修改Okular插入PDF文档中的FreeText(内联文本)注释的文字与边框颜色,但当前脚本并未仅修改文字和边框,反而将整个注释变为红色填充矩形。请问该需求是否支持?我深知PDF格式复杂,但期望这类简单修改无需特殊操作。
Okular生成的PDF内联文本注释示例
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␍␊
qpdf转换后的PDF片段
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␍␊
相关Python脚本
#!/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)
解决方案
这个需求是完全可以实现的,问题出在原脚本对PDF注释属性的理解偏差:FreeText注释的/C属性默认同时控制填充色和边框色,删除/AP后阅读器会用这个属性生成带填充的注释外观。要只修改文字和边框颜色,需要区分控制各部分的属性:
修改后的脚本
#!/usr/bin/env python3 import sys import pikepdf from pikepdf import Name, Array, Dictionary import re def change_annotation_font_and_border_to_red(input_path): output_path = input_path.replace(".pdf", "-output.pdf") with pikepdf.open(input_path) as pdf: for page in pdf.pages: if "/Annots" not in page: continue for annot in page["/Annots"]: # 只处理FreeText类型的注释 if annot.get(Name("/Subtype")) != Name("/FreeText"): continue # 1. 设置文字颜色:修改/DA,指定非描边色(文字用此属性) if "/DA" in annot: da_str = str(annot[Name("/DA")]) # 移除现有颜色设置 da_str = re.sub(r"(\d+\.?\d*\s){1,3}[rgRG]", "", da_str).strip() # 添加红色文字设置(rg表示非描边颜色) da_str += " 1 0 0 rg" annot[Name("/DA")] = da_str # 2. 设置边框颜色:用/BS字典单独控制,不影响填充色 if "/BS" not in annot: annot[Name("/BS")] = Dictionary() # 边框颜色设为红色(RGB) annot[Name("/BS")][Name("/C")] = Array([1, 0, 0]) # 保持原边框宽度,或手动设置为2 annot[Name("/BS")][Name("/W")] = annot.get(Name("/Border"), [0,0,2])[2] # 3. 设置填充色为透明:用RGBA格式,最后一位0表示完全透明 annot[Name("/C")] = Array([1, 0, 0, 0]) # 可选:删除/AP让阅读器重新生成外观,或保留并直接修改外观流 if "/AP" in annot: del annot[Name("/AP")] pdf.save(output_path) print(f"修改完成,保存为: {output_path}") if __name__ == "__main__": if len(sys.argv) != 2: print("用法: python script.py <输入PDF路径>") else: change_annotation_font_and_border_to_red(sys.argv[1])
关键说明
/BS字典:FreeText注释的专属边框样式字典,其中/C仅控制边框颜色,不会影响填充。/C属性:默认控制填充色,设置为RGBA格式(最后一位为alpha通道),设为0即可让填充完全透明。/DA中的rg指令:用于设置文字的非描边颜色,确保文字显示为红色。
内容
相关产品推荐
相关产品推荐

