pdf - 使用 Ghostscript,如何在合并的 PDF 文件上添加页码
问题描述
在相当锁定的裸机 Linux 机器上仅使用 Ghostscript,我需要将三个现有 PDF 组合成一个新 PDF,在新 PDF 的每一页上放置一个静态页眉,在新 PDF 的每一页上放置一个静态页脚,并且为每一页编号(例如“第 1257 页”)。
我有前三个问题的解决方案。开箱即用的 Ghostscript 可以轻松地将多个 PDF 合并为一个新的 PDF。在 -c 命令行选项中修改 PostScript,我可以将静态页眉和页脚添加到新 PDF。我还不能做的是让它把页码放在新的 PDF 上。
这是我现在拥有的复杂命令行:
gs -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=final.pdf -c "<</EndPage {2 ne {200 0 0 setrgbcolor /NimbusSans-Bold 24 selectfont 240 2 moveto (Static Footer) show 240 773 moveto (Static Header) show 0 0 0 setrgbcolor /NimbusSans-Regular 12 selectfont 2 24 moveto (Page ) show true}{pop false} ifelse} >> setpagedevice" -f title.pdf parta.pdf partb.pdf
删除静态页眉和页脚部分会得到一个稍微简单的命令行:
gs -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=final.pdf -c "<</EndPage {2 ne {0 0 0 setrgbcolor /NimbusSans-Regular 12 selectfont 2 24 moveto (Page ) show true}{pop false} ifelse} >> setpagedevice" -f title.pdf parta.pdf partb.pdf
我尝试了很多方法来显示页码,但它们要么使 Ghostscript 崩溃,要么只是在每页上显示相同的页码。唯一的其他复杂情况是新的 PDF 将在 1,000 到 2,000 页之间。
我需要的是一个很好的代码示例,说明如何使 PostScript 在 PDF 的每一页上显示递增的页码。
解决方案
在 PostScript 中计算页数并不难。我希望您已经知道以下大部分内容,但是为了以后遇到此问题的其他人,我将采取一些小步骤。
我们首先创建一个我们自己的字典,我们可以在其中存储东西。我们确保这是在 'userdict' 中定义的,所以我们总能找到它(userdict,像 systemdict,总是可用的)。选择的名称应该是独一无二的,以防止任何其他 PostScript 或 PDF 程序覆盖它!
userdict begin %% Make userdict the current dictionary
/My_working_Dict %% Create a name for our dict (choose something unique)
10 dict %% create a 10-element dictionary
def %% def takes a key and value and puts them in the current dictionary
My_working_Dict begin %% make our working dictionary the current one
/PageCount %% Make a name for our counter
0 %% 0, this will be the initial value
def %% define PageCount as 0 in the current dictionary
end %% close the current dictionary (My_working_Dict)
end %% close the current dictionary (userdict)
有更有效的方法可以做到这一点,但这是一种易于描述和遵循的方法。从这一点开始,直到我们关闭 PostScript 解释器(或将其恢复到较早的状态),userdict 将包含一个名为 My_working_Dict 的字典,该字典将具有一个名为 PageCount 的键。与 PageCount 关联的值最初将为 0,但我们可以更改它。
您已经定义了一个 EndPage 过程,如下所示:
<<
/EndPage {
2 ne {
200 0 0 setrgbcolor
/NimbusSans-Bold 24 selectfont
240 2 moveto
(Static Footer) show
240 773 moveto
(Static Header) show
0 0 0 setrgbcolor
/NimbusSans-Regular 12 selectfont
2 24 moveto
(Page ) show
true
}
{
pop
false
} ifelse
}
>> setpagedevice
现在,当调用 EndPage 过程时,堆栈上有两个数字,最上面的数字是“原因代码”,下一个数字是先前显示页面执行的计数。现在您会(合理地)认为您可以使用该计数作为您的页数,但不幸的是,它在每次“setpagedevice”调用时重置为 0,并且 PDF 解释器在每一页上调用 setpagedevice,因为它可能适用于 PDF 中的每一页文件的大小不同,而 setpagedevice 是我们更改页面大小的方式。
当我们从 EndPage 过程返回时,我们必须将一个布尔值压入堆栈,它要么是“真”(将页面发送到输出),要么是“假”(把它扔掉,什么也不做)。
因此,您的程序测试原因码以查看调用 EndPage 的原因。如果它不是“2”(设备被停用),那么它的复制页或展示页,所以你在页面上绘制你想要的添加。如果是 2,那么我们只需弹出页面数并返回“false”,这样我们就不会尝试发出额外的页面。
如果它是 2,那么您将颜色设置为 RGB 黑色(您可以这样做0 setgray
)找到字体 NimbusSans-Bold,将其缩放到 24 磅并将其设置为当前字体。然后移动到位置 x=240, y = 2(0,0 位于左下角,单位是点,1/72 英寸)并绘制文本“静态页脚”(NB 括号是 PostScript 中的字符串分隔符)
然后移动到位置 x=240, y=773 并绘制文本“Static Header”。
然后你再次冗余设置颜色,你不需要这样做,它保持不变,直到你改变它,再次找到字体 NimbusSans-Bold,这次将它缩放到 12 点并选择它作为当前字体。最后,您移动到位置 x=2, y=24 并绘制文本“Page”。
因此,您需要做的就是扩展 EndPage 过程,以便它从我们的字典中获取页数,将其转换为字符串,并绘制结果字符串。
就像是 :
userdict begin %% Make userdict the current dictionary
/My_working_Dict %% Create a name for our dict (choose something unique)
10 dict %% create a 10-element dictionary
def %% def takes a key and value and puts them in the current dictionary
My_working_Dict begin %% make our working dictionary the current one
/PageCount %% Make a name for our counter
0 %% 0, this will be the initial value
def %% define PageCount as 0 in the current dictionary
end %% close the current dictionary (My_working_Dict)
end %% close the current dictionary (userdict)
<<
/EndPage {
2 ne {
0 0 0 setrgbcolor
/NimbusSans-Bold 24 selectfont
240 2 moveto
(Static Footer) show
240 773 moveto
(Static Header) show
0 0 0 setrgbcolor
/NimbusSans-Regular 12 selectfont
2 24 moveto
(Page ) show
userdict /My_working_Dict get %% get My_working_dict from userdict (leaves My_working_dict on the operand stack
dup %% duplicate the dictionary reference
/PageCount get %% get PageCount from the dictionary on the stack
1 add %% add one to the count
/PageCount %% put the key on the stack
%% stack now holds << >> int /PageCount
%% where << >> is a reference to My_working_Dict, int is our new value for PageCount, and /PageCount is the key name we are using
exch %% swap the topmost two stack items
%% stack is now << >> /PageCount int
put %% puts the top two items on the stack into the dictionary which is third on the stack.
256 string %% Temporary string to hold the count
userdict /My_working_Dict get %% get My_working_dict from userdict (leaves My_working_dict on the operand stack
/PageCount get %% get PageCount from the dictionary on the stack
exch
cvs %% Convert the top object on the stack into a string, storing the result in the second object down, whic must be a string
show %% draw the string on the page using the current colour and font.
true
}
{
pop
false
} ifelse
}
>> setpagedevice
然后,您将使用以下命令执行 Ghostscript:
gs -dNOPAUSE -dBATCH -sDEVICE=pdfwrite -sOutputFile=final.pdf modifyPDF.ps title.pdf parta.pdf partb.pdf
现在我还没有真正尝试过这段代码,所以完全有可能出现错误。
[更新 2]
该程序基本相同,但将变量存储在全局 VM 中的字典中,存储在 globaldict 中,以防止保存/恢复。
globaldict begin %% Make globaldict the current dictionary
currentglobal true setglobal %% We need to make the VM allocation mode for the dictionary global
/My_working_Dict %% Create a name for our dict (choose something unique)
10 dict %% create a 10-element dictionary
def %% def takes a key and value and puts them in the current dictionary
setglobal %% put the VM allocation mode back
globaldict /My_working_Dict %% Get the dictionary from globaldict
get begin %% make our working dictionary the current one
/PageCount %% Make a name for our counter
0 %% 0, this will be the initial value
def %% define PageCount as 0 in the current dictionary
end %% close the current dictionary (My_working_Dict)
end %% close the current dictionary (globaldict)
<<
/EndPage {
2 ne {
0 0 0 setrgbcolor
/NimbusSans-Bold 24 selectfont
240 2 moveto
(Static Footer) show
240 773 moveto
(Static Header) show
0 0 0 setrgbcolor
/NimbusSans-Regular 12 selectfont
2 24 moveto
(Page ) show
globaldict /My_working_Dict get %% get My_working_dict from globaldict (leaves My_working_dict on the operand stack
dup %% duplicate the dictionary reference
/PageCount get %% get PageCount from the dictionary on the stack
1 add %% add one to the count
/PageCount %% put the key on the stack
%% stack now holds << >> int /PageCount
%% where << >> is a reference to My_working_Dict, int is our new value for PageCount, and /PageCount is the key name we are using
exch %% swap the topmost two stack items
%% stack is now << >> /PageCount int
put %% puts the top two items on the stack into the dictionary which is third on the stack.
globaldict /My_working_Dict get %% get My_working_dict from globaldict (leaves My_working_dict on the operand stack
/PageCount get %% get PageCount from the dictionary on the stack
256 string %% Temporary string to hold the count
globaldict /My_working_Dict get %% get My_working_dict from globaldict (leaves My_working_dict on the operand stack
/PageCount get %% get PageCount from the dictionary on the stack
exch
cvs %% Convert the top object on the stack into a string, storing the result in the second object down, whic must be a string
show %% draw the string on the page using the current colour and font.
true
}
{
pop
false
} ifelse
}
>> setpagedevice
我已经用补充的例子试过了,它对我有用。
推荐阅读
- tableau-api - Tableau:根据最大销售额返回站点的前 N 个部门
- sharepoint-online - GetFileByServerRelativeUrl 不适用于特殊字符
- flutter - 我如何设置 x 轴相对于秒,例如 12:01:00。使用 chart_flutter 折线图
- javascript - 如何调整 max = "100" 的
- r - 只有在 tidyverse 中删除了 id 变量时,pivot_longer() 才有效
- javascript - 有没有办法在 NodeJS 中监视端口(已被另一个应用程序使用)?
- sql - 如何在 SQL 中查找最后更改行 - Big Query
- angular - 有时某些微前端在单个 spa 框架中未正确加载
- teradata - 使用数据库中的重复视图 (Teradata) 时更改了 Tableau 数据源列名称
- linux - Shell 解释器转义特殊字符 $