通过命令行将书签创建到 PDF 文件中

Create bookmarks into a PDF file via command line

我正在寻找一个命令行工具来为 PDF 文件添加书签。

我有一个page number和一个label。很想创建名为 label 的书签,链接到页面 page number

有没有人知道用于此操作的命令行工具(最好是 OSX)?

我有大约 4000 页的 PDF 文件和大约 150 个书签,很想将其自动化。

我的计划是在 r 脚本中使用系统调用。

编辑

我创建了大约 4000 个带有图表的单个 PDF 文件,我正在使用 OSX 系统命令 /System/Library/Automator/Combine PDF Pages.action/Contents/Resources/join.py 将 PDF 连接在一起。以前我使用 pdfjam 包中的 pdfjoin ,但这太慢了。最后,这就是我现在使用 Adob​​e Acrobat Professional 手动添加书签的方式获取 PDF 的方式。

好的,这是同时完成三项工作的快捷方式:

  1. 合并您的 400 个单页 PDF。
  2. 创建文档顶级目录(Table 的目录)。
  3. 为每一页创建一个 PDF 书签。

它涉及使用 LaTeX 安装。

您从一个空的 LaTeX 模板开始,如下所示:

\documentclass[]{article}
\usepackage{pdfpages}
\usepackage{hyperref}
    \hypersetup{breaklinks=true,
                bookmarks=true,
                pdfauthor={},
                pdftitle={},
                colorlinks=true,
                citecolor=blue,
                urlcolor=blue,
                linkcolor=magenta,
                pdfborder={0 0 0}}
\begin{document}

{
    \hypersetup{linkcolor=black}
    \setcounter{tocdepth}{3}
    % Comment next line in or out if you want a ToC or not:
    \tableofcontents
}

%% Here goes your additional code:
%% 1 line per included PDF!

\end{document}

现在就在该模板的最后一行之前,为每个要包含的外部 PDF 文件插入一行。

  1. 如果你想生成一个目录,它必须像这样格式化:

    \includepdf[pages={<pagenumber>},addtotoc{<pagenumber>,<section>,<level>,\
                       <heading>,<label>}]{pdffilename.pdf}
    
  2. 如果您确定每个包含的 PDF 都是一页文档,它可以简化为:

    \includepdf[addtotoc{<pagenumber>,<section>,<level>,\
                         <heading>,<label>}]]{pdffilename.pdf}
    

这里 all 以下五个参数中的 addtotoc 是必需的,按照文件出现的顺序在书签和目录中。请参阅下面的具体示例:

  • <pagenumber> : 要link编辑到的插入文档的页码。 (在你的情况下总是“1”,因为你只插入 1 页的文档;你可以插入 5 页的文档和 link 到插入的 PDF 的第 3 页)。
  • <section> :LaTeX 分节名称。可能是 sectionsubsectionsubsubsection... 在您的情况下 "section".
  • <level> : LaTeX 部分的级别。在你的情况下 "1".
  • <heading> :这是一个字符串。用于书签的文字
  • <label> :每个书签都必须是唯一的。在 PDF 内部用于在单击书签时跳转到正确的页面。

为了快速测试,我使用 Ghostscript 生成了 20 个单页 PDF 文档:

for i in {1..20}; do
   gs -o p${i}.pdf -sDEVICE=pdfwrite               \
      -c "/Helvetica findfont 30 scalefont setfont \
          100 600 moveto                           \
          (Page ${i}) show                         \
          showpage"; 
done

使用这些测试文件,我可以使要插入到模板中的行看起来像这样:

\includepdf[addtotoc={1,section,1,Page 1 (First),p1}]{p1.pdf}
\includepdf[addtotoc={1,section,1,Page 2,p2}]{p2.pdf}
\includepdf[addtotoc={1,section,1,Page 3,p3}]{p3.pdf}
[...]
\includepdf[addtotoc={1,section,1,Page 11 (In the Middle),p11}]{p11.pdf}
[...]
\includepdf[addtotoc={1,section,1,Page 20 (Last),p20}]{p20.pdf}

用插入的行保存模板,然后运行下面的命令两次:

 pdflatex template.tex
 pdflatex template.tex

生成的文件将带有书签,在 Preview.app 中看起来像这样:


注意: LaTeX 可通过两种方法用于 OSX:


如果我有更多时间,我也会在稍后或接下来的几天添加一两个其他方法来在命令行上插入书签。

现在必须做这个,因为我从来没有在 AFAICR 上展示它。

但我想因为你给了背景"I'm merging 1-page PDFs, and it is slow; now I want to add bookmarks too...",我可以用一种方法来展示如何做。

提示其他方法之一是使用pdftk IS 可用于 Mac OS X!

这是另一个答案。这个使用 Ghostscript 处理 PDF 到 PDF,并使用 pdfmark PostScript 运算符插入书签。

有关 pdfmark 主题的一些介绍,另请参阅:

此方法包括两个步骤:

  1. 创建一个文本文件(实际上是一个 PostScript 文件),其中包含一组有限的 pdfmark 命令,每行一个命令和您要添加的书签。
  2. 运行 处理当前 PDF 文件和文本文件的 Ghostscript 命令。

1.

文本文件中的内容应如下所示:

[/Page 1   /View [/XYZ null null null] /Title (This is page 1)         /OUT pdfmark
[/Page 2   /View [/XYZ null null null] /Title (Dunno which page this is....) /OUT pdfmark
[/Page 3   /View [/XYZ null null null] /Title (Some other name)        /OUT pdfmark
[/Page 4   /View [/XYZ null null null] /Title (File 4)                 /OUT pdfmark
[/Page 5   /View [/XYZ null null null] /Title (File 5)                 /OUT pdfmark
[/Page 6   /View [/XYZ null null null] /Title (File 6)                 /OUT pdfmark
[/Page 7   /View [/XYZ null null null] /Title (File 7)                 /OUT pdfmark
% more lines for more pages to bookmark...
[/Page 13  /View [/XYZ null null null] /Title (File 13)                /OUT pdfmark
[/Page 14  /View [/XYZ null null null] /Title (Bookmark for page 14)   /OUT pdfmark
% more lines for more pages to bookmark...

例如命名此文件:addmybookmarks.txt

2.

现在运行这个命令:

gs -o bookmarked.pdf   \
   -sDEVICE=pdfwrite   \
    addmybookmarks.txt \
   -f original.pdf

生成的 PDF bookmarked.pdf 现在包含书签。请参阅此屏幕截图:

您也可以使用pdftk。也可用 for OS X.

我现在不在这里详细介绍所有细节,因为其他地方已经用很长的篇幅完成了。简单说一下:

  1. 从您的原始文件(不带书签)创建示例 PDF。
  2. 使用 Adob​​e Acrobat(您似乎可以访问)添加一些 书签。
  3. 运行 这些命令之一:

    pdftk my.pdf dump_data output -
    pdftk my.pdf dump_data output bookmarks+otherdata.txt
    
  4. 研究输出格式。

  5. 通过添加所需的所有条目来修改输出 .txt 文件。
  6. 运行 PDFTK 再次:

    pdftk my.pdf update_info bookmarks.txt output bookmarked.pdf
    

附加信息

这是我在上面第 4 步检查后注意到的书签格式。

BookmarkBegin
BookmarkTitle: -- Your Title 1 --
BookmarkLevel: 1
BookmarkPageNumber: 1
BookmarkBegin
BookmarkTitle: -- Your Title 2 --
BookmarkLevel: 1
BookmarkPageNumber: 2
BookmarkBegin
BookmarkTitle: -- Your Title 3 --
...
...
and so on...

并在适当的地方替换上面的..

这里是 python 将书签添加到目录 Table 的方法。无需任何其他安装即可在 MacOS 上运行。

#!/usr/bin/python    
from Foundation import  NSURL, NSString
import Quartz as Quartz
import sys

# You will need to change these filepaths to a local test pdf and an output file.
infile = "/path/to/file.pdf"
outfile = "/path/to/output.pdf"

def getOutline(page, label):
    # Create Destination
    myPage = myPDF.pageAtIndex_(page)
    pageSize = myPage.boundsForBox_(Quartz.kCGPDFMediaBox)
    x = 0
    y = Quartz.CGRectGetMaxY(pageSize)
    pagePoint = Quartz.CGPointMake(x,y)
    myDestination = Quartz.PDFDestination.alloc().initWithPage_atPoint_(myPage, pagePoint)
    myLabel = NSString.stringWithString_(label)
    myOutline = Quartz.PDFOutline.alloc().init()
    myOutline.setLabel_(myLabel)
    myOutline.setDestination_(myDestination)
    return myOutline

pdfURL = NSURL.fileURLWithPath_(infile)
myPDF = Quartz.PDFDocument.alloc().initWithURL_(pdfURL)
if myPDF:
    # Here's where you list your page index (starts at 0) and label.
    outline1 = getOutline(0, 'Page 1')
    outline2 = getOutline(1, 'Page 2')
    outline3 = getOutline(2, 'Page 3')

    # Create a root Outline and add each outline. (Needs a loop.)
    rootOutline = Quartz.PDFOutline.alloc().init()
    rootOutline.insertChild_atIndex_(outline1, 0)
    rootOutline.insertChild_atIndex_(outline2, 1)
    rootOutline.insertChild_atIndex_(outline3, 2)
    myPDF.setOutlineRoot_(rootOutline)
    myPDF.writeToFile_(outfile)