如何用Ghostscript检测PDF中的/OpenAction并修改默认打开页
检测PDF中的/OpenAction(基于Ghostscript PostScript脚本)
我的PDF的Pages字典及相关Catalog内容如下:
<< /Type /Pages /Kids [ 5 0 R 142 0 R 225 0 R 296 0 R 302 0 R 308 0 R 314 0 R 4 0 R 336 0 R ] /Count 9 >> endobj 341 0 obj << /Type /Outlines /Count 1 /First 342 0 R /Last 342 0 R >> endobj 1 0 obj << /Type /Catalog /Pages 3 0 R /Outlines 341 0 R /OpenAction [4 0 R /XYZ null null null] /PageLabels<< /Nums [0 << /S /D >>] >>
因为这个/OpenAction设置,PDF总是打开在第8页而非首页。我已经通过在PostScript文件中添加[/Page 1 /DOCVIEW pdfmark]重置了默认打开页,但现在需要用Ghostscript检测PDF是否存在/OpenAction。
我基于Ghostscript自带的PS示例写了一段脚本,可以获取PDF的各类信息,但无法检测/OpenAction,脚本如下:
/PDFContext << >> .PDFInit def (D:\Test\test.pdf) (r) file PDFContext .PDFStream PDFContext .PDFInfo dup /Title knownoget { (Title: ) print write-doc-string () = flush } if dup /Author knownoget { (Author: ) print write-doc-string () = flush } if dup /Subject knownoget { (Subject: ) print write-doc-string () = flush } if dup /Keywords knownoget { (Keywords: ) print write-doc-string () = flush } if dup /Creator knownoget { (Creator: ) print write-doc-string () = flush } if dup /Producer knownoget { (Producer: ) print write-doc-string () = flush } if dup /CreationDate knownoget { (CreationDate: ) print write-doc-string () = flush } if dup /ModDate knownoget { (ModDate: ) print write-doc-string () = flush } if (Pages: ) print dup /NumPages get = flush /NumPages get 0 1 3 -1 roll 1 sub { dup PDFContext exch .PDFPageInfoExt exch ( - page: ) print 1 add =print ( - ) print dup /MediaBox knownoget { /MediaBox exch def (left ) print MediaBox 0 get =print (, top ) print MediaBox 1 get =print (, width ) print MediaBox 2 get =print (, height ) print MediaBox 3 get =print } { (left 0, top 0, width 0, height 0) print } ifelse dup /Rotate knownoget { (, rotate ) print =print } { (, rotate 0) print } ifelse (, annotations ) print dup /Annots knownoget { (yes) print } { (no) print } ifelse (\n) print } for flush PDFContext .PDFClose
解决方案:添加/OpenAction检测逻辑
要检测/OpenAction,需从PDF的Catalog字典中读取该字段。Ghostscript的PDF API提供了.PDFCatalog方法可直接获取Catalog字典,之后判断是否存在/OpenAction键即可。
修改后的完整脚本(保留原有功能,新增检测模块):
/PDFContext << >> .PDFInit def (D:\Test\test.pdf) (r) file PDFContext .PDFStream % 获取文档基础信息(原有逻辑保留) PDFContext .PDFInfo dup /Title knownoget { (Title: ) print write-doc-string () = flush } if dup /Author knownoget { (Author: ) print write-doc-string () = flush } if dup /Subject knownoget { (Subject: ) print write-doc-string () = flush } if dup /Keywords knownoget { (Keywords: ) print write-doc-string () = flush } if dup /Creator knownoget { (Creator: ) print write-doc-string () = flush } if dup /Producer knownoget { (Producer: ) print write-doc-string () = flush } if dup /CreationDate knownoget { (CreationDate: ) print write-doc-string () = flush } if dup /ModDate knownoget { (ModDate: ) print write-doc-string () = flush } if (Pages: ) print dup /NumPages get = flush % 核心:检测/OpenAction (OpenAction: ) print PDFContext .PDFCatalog % 获取根目录Catalog字典 dup /OpenAction knownoget { % 存在/OpenAction时输出细节 (Present, value: ) print % 处理两种常见格式:数组或字典 dup type /arraytype eq { % 数组形式(如示例中的[4 0 R /XYZ null null null]) { =print ( ) print } forall } { % 字典形式(如<< /Type /Action /S /GoTo /D [...] >>) dup == } ifelse () = flush } { % 不存在时输出提示 (Not present) = flush } ifelse % 原有页面信息逻辑保留 /NumPages get 0 1 3 -1 roll 1 sub { dup PDFContext exch .PDFPageInfoExt exch ( - page: ) print 1 add =print ( - ) print dup /MediaBox knownoget { /MediaBox exch def (left ) print MediaBox 0 get =print (, top ) print MediaBox 1 get =print (, width ) print MediaBox 2 get =print (, height ) print MediaBox 3 get =print } { (left 0, top 0, width 0, height 0) print } ifelse dup /Rotate knownoget { (, rotate ) print =print } { (, rotate 0) print } ifelse (, annotations ) print dup /Annots knownoget { (yes) print } { (no) print } ifelse (\n) print } for flush PDFContext .PDFClose
关键说明:
- 使用
PDFContext .PDFCatalog直接获取PDF的根目录(Catalog),/OpenAction是Catalog的顶层属性。 - 兼容
/OpenAction的两种常见格式:- 数组形式:直接遍历数组元素输出内容;
- 字典形式:用
==命令完整输出字典结构,适合包含更多属性的动作定义。
- 脚本完全保留原有功能,仅新增
/OpenAction检测模块,不影响其他信息获取。
内容的提问来源于stack exchange,提问作者Kees van Spelde
相关产品推荐
相关产品推荐

