将结构化目录中的 PDF 合并为一个带书签的 PDF

将结构化目录中的 PDF 合并为一个带书签的 PDF

我有很多旧报告的扫描页面存储在以下目录结构中:

Report 1/
 contents.pdf
 execsummary.pdf
 chapter 1/
   page 1.pdf
   page 2.pdf
   page 3.pdf
 chapter 2/
   page 4.pdf
   page 5.pdf
   page 6.pdf

我想Report 1.pdf从这些书签中生成与目录结构匹配的书签。我该怎么做?

我在 Windows 10 上,没有 Adob​​e Acrobat,但我有 Foxit Phantompdf。

答案1

这可能不是您正在寻找的解决方案:

您可以使用 LaTeX 来实现这一点。您需要以另一种方式生成 TeX 文件,例如您最喜欢的编程语言。这需要您知道如何编程、使用 LaTeX,当然还要安装所需的工具。

如果您有兴趣,我可以详细说明这一点并添加示例脚本。

编辑:

我编写了一个小型 FreeBASIC 程序(有点脏,但能完成工作)来生成 .tex 文件。然后可以使用例如 Miktex 和 TexnicCenter 来生成最终的 pdf 文件。

  • 从以下网址下载并解压 FreeBASIC 编译器http://www.freebasic.net/(我用了FreeBASIC-1.05.0-win64.zip)。
  • 将下面的代码保存为code.bas,然后用进行编译fbc.exe code.bas
  • 将“Report 1”、“Report 2”等文件夹拖放到新的可执行文件中code.exe。这将在各自的文件夹中生成文件“Report 1.tex”、“Report 2.tex”。
  • 从以下位置下载并安装 Miktexhttp://www.miktex.org/(在安装过程中启用软件包的即时安装)和 TexnicCenter 来自http://www.texniccenter.org/download/并在 TexnicCenter 中打开报告文件。我不确定您是否需要对默认设置进行任何更改,但互联网上有很多这方面的资源。编译时LaTeX -> PDF应该安装缺少的软件包。

源代码:明确处理提到的文件夹结构和文件名,仅此而已。

    ' Drag and drop folders onto the executable in order to generate a .tex-file 
    ' which can be used to merge the pdfs in each passed folder using LaTeX.
    '

    #include "vbcompat.bi"

    sub expandEnviron__isFileOrFolder ( byref strPath as string )
        dim iLetter as integer
        if left(strPath,1)="%" then
            for iLetter=2 to len(strPath)
                if mid(strPath,iLetter,1)="%" then              
                    strPath=environ(mid(strPath,2,iLetter-2))+right(strPath,len(strPath)-iLetter)
                    exit for
                end if
            next iLetter
        end if
    end sub

    function isFileOrFolder ( byref strPath as string, byval expPath as string ptr = 0 ) as integer
        ' return value:
        '    0: path doesn't exist
        '    1: file
        '    2: folder
        '

        dim strDir as string = curdir

        dim as string strPathCopy
        dim as string ptr pPath
        if expPath then
            *expPath = strPath
            expandEnviron__isFileOrFolder(*expPath)
            pPath = expPath
        else
            strPathCopy = strPath
            expandEnviron__isFileOrFolder(strPathCopy)
            pPath = @strPathCopy
        end if

        if fileExists(*pPath) then
            return 1
        elseif ( chdir(*pPath) = 0 ) then
            chdir(strDir)
            return 2
        else
            return 0
        end if
    end function


    color(1,15)
    cls

    if command(1) = "" then
        print "Drag and drop folders onto the executable."
        sleep
        end
    end if

    dim as string basedir
    dim as string strPath = ""
    dim as integer i = 1
    ' Process all command line arguments i.e process all folders.
    while command(i) <> ""
        basedir = command(i)
        dim as string basedirName

        ' Make sure the argument is indeed a folder.
        if isFileOrFolder(basedir,@strPath) = 2 then
            if right(strPath,1) = "\" then basedir = left(strPath,len(strPath)-1)
            basedirName = right(basedir,len(basedir)-instrrev(basedir,"\"))
            print ""
            print baseDirName
            '
            ' Print some LaTeX commands.
            open basedir+"\"+baseDirName+".tex" for output as #1
            print #1, $"\documentclass{scrreprt}"
            print #1, $"\usepackage{grffile}"
            print #1, $"\usepackage{pdfpages}"
            print #1, $"\usepackage{bookmark}"
            print #1, $"\hypersetup{pageanchor=false}"
            print #1, $"\begin{document}"
            print #1, $"\pagestyle{empty}"
            print #1, $"\pagenumbering{gobble}"
            print #1, "%"
            '
            ' Process contents.pdf.
            dim as string tmp = basedir+"\contents.pdf"
            if isFileOrFolder(tmp) = 1 then
                print #1, $"\includepdf[pages=-]{contents.pdf}"
            else
                color(12,15):print chr(9);"missing contents.pdf":color(1,15)
            end if
            '
            ' Process execsummary.pdf.
            tmp = basedir+$"\execsummary.pdf"
            if isFileOrFolder(tmp) = 1 then
                print #1, $"\includepdf[pages=-]{execsummary.pdf}"
            else
                color(12,15):print chr(9);"missing execsummary.pdf":color(1,15)
            end if
            '
            ' Process all subfolders named "chapter 1", "chapter 2" etc.
            ' If "chapter 4" exists but "chapter 3" does not, then "chapter 4" and 
            ' all after that will be ignored.
            dim as integer chapter_link_cnt = 0
            dim as integer j = 1
            dim as string nextChapterDir = basedir+$"\chapter "+str(j)
            while isFileOrFolder(nextChapterDir) = 2
                print #1, "%"
                dim as integer k = 1
                '
                ' Process all files named "page 1", "page 2" etc.
                dim as string nextPage = nextChapterDir + $"\page "+str(k)+".pdf"
                while isFileOrFolder(nextPage) = 1
                    if k = 1 then
                        chapter_link_cnt += 1
                        print #1, $"\includepdf[link,linkname=l";str(chapter_link_cnt); _
                            ",pages=-]{chapter ";str(j);"/page ";str(k);".pdf}"
                        print #1, $"\bookmark[dest=l";str(chapter_link_cnt); _
                            ".1]{chapter ";str(j);"}"
                    else
                        print #1, $"\includepdf[pages=-]{chapter ";str(j);"/page ";str(k);".pdf}"
                    end if
                    k += 1
                    nextPage = nextChapterDir + $"\page "+str(k)+".pdf"
                wend
                j += 1
                nextChapterDir = basedir+$"\chapter "+str(j)
            wend
            '
            print #1, $"\end{document}"
            close #1
        else
            print ""
            color(12,15):print "Error (not a folder): ";command(i):color(1,15)
        end if
        i += 1
    wend

    print ""
    print ""
    print "Done."
    sleep

如果您想使用不同的语言(也许可以使用 powershell 脚本来完成),这里有一个示例 tex 文件:

\documentclass{scrreprt}
\usepackage{grffile}
\usepackage{pdfpages}
\usepackage{bookmark}
\hypersetup{pageanchor=false}
\begin{document}
\pagestyle{empty}
\pagenumbering{gobble}
%
\includepdf[pages=-]{contents.pdf}
\includepdf[pages=-]{execsummary.pdf}
%
\includepdf[link,linkname=l1,pages=-]{chapter 1/page 1.pdf}
\bookmark[dest=l1.1]{chapter 1}
\includepdf[pages=-]{chapter 1/page 2.pdf}
%
\includepdf[link,linkname=l2,pages=-]{chapter 2/page 1.pdf}
\bookmark[dest=l2.1]{chapter 2}
\includepdf[pages=-]{chapter 2/page 2.pdf}
%
\includepdf[link,linkname=l3,pages=-]{chapter 3/page 1.pdf}
\bookmark[dest=l3.1]{chapter 3}
\includepdf[pages=-]{chapter 3/page 2.pdf}
\includepdf[pages=-]{chapter 3/page 3.pdf}
\includepdf[pages=-]{chapter 3/page 4.pdf}
\end{document}

答案2

PDFsam 基础版免费做你想做的事

http://www.pdfsam.org/

https://sourceforge.net/projects/pdfsam/

答案3

我遇到了同样的问题,最后只能使用 Excel VBA 编写自己的代码。我已将模块上传到 Github: https://github.com/markbrauning/BlueBeam-eX-Excel-Submittal-from-Folder-Structure

请注意,此模块要求您安装 Bluebeam eXtreme,因为它使用 ScriptEngine 命令行进行实际的 PDF 合并。

Dim i As Long, k As Long
Dim scriptRevu As String
Dim SectionPDFpath As String
Dim PDFFilePath As String
Dim SectionFolder As String
Dim QuoteCheck As Boolean
For i = 0 To UBound(xFoldersList_temp, 2)
    SectionFolder = xFoldersList_temp(0, i)
    '---SectionPDFpath = ParentFolderPath + SubFolderName + .pdf
    SectionPDFpath = xFoldersList_temp(3, i) & "\" & xFoldersList_temp(1, i) & ".pdf"
    scriptRevu = "Open('" & SectionPDFpath & "', '')"
    QuoteCheck = Not GetFilesList(SectionFolder, False, "pdf", 0)
    If QuoteCheck Then GoTo Sub_Exit
    For k = 0 To UBound(xFilesList, 1)
        PDFFilePath = xFilesList(k)
        scriptRevu = scriptRevu & " " & "InsertPages(9999, '" & PDFFilePath & "', true, false, false, false, false)"
    Next k
    scriptRevu = scriptRevu & " Save('" & SectionPDFpath & "', 1)"
    scriptRevu = scriptRevu & " Close(true, 1)"
    RunRevuScript scriptRevu
    
    'if next script is for the next folder depth up, then wait for a few seconds
    If Not (i = UBound(xFoldersList_temp, 2)) Then
        If xFoldersList_temp(2, i) <> xFoldersList_temp(2, i + 1) Then
            waitdepth Int(xFoldersList_temp(2, i + 1)), 7
        End If
    End If
    
Next i

RunRevuScript 是一个通过 shell 发送书面命令的函数

Sub RunRevuScript(RevuScript As String)
    Dim RevuPath As String
    Dim command As String    
    RevuPath = "C:\Program Files\Bluebeam Software\Bluebeam Revu\20\Revu\ScriptEngine.exe"
    command = RevuPath & " " & RevuScript
    Shell command, vbNormalFocus
    End sub

希望这对某人有帮助!

相关内容