Advertisement

用python和FreePic2Pdf工具手把手教你生成扫描版PDF文档的目录

阅读量:

在互联网上获取一本电子书进行查看时发现一个问题: hundreds of pages 的电子书中竟然没有 目录 ,想要找到感兴趣的部分就必须手动翻阅每一页。看到这种情况心情就会有些崩溃。我也遇到过类似的情况: many 下载下来的 pdf 文件都是 scan 版本, 通常是没有 目录 的。这种情况下确实会让人感到麻烦, 因此必须寻找一些便捷的方法来获取电子书 目录 。考虑到资源有限的情况下无法获取 原版 书籍, 所以只能依靠一些巧妙的小窍门来完成这项任务。

目录

补充

补充

  • Python 导出 PDF 中目录/书签
  • FreePic2Pdf工具生成PDF文档目录
    • 1. 软件下载
    • 2. 将书籍的目录挂载到pdf步骤
      • 2.1 打开软件FreePic2PDF,点击右下角“更改pdf”
      • 2.2 选择“从PDF取书签”→需要操作的pdf文件→点击开始
      • 2.3 复制PDF目录页面所有目录说明到文本编辑
      • 2.4 将书签挂到pdf中

利用python生成PDF文档目录

以下将介绍一种专为程序员设计的工具。它仅需具备 Python 编程环境即可使用,并无需额外安装第三方 PDF 软件。

事前准备:安装 python 第三方库 PyPDF2

首先安装 python 第三方库 PyPDF2:pip install PyPDF2

注意:原版 PyPDF2 在读取某些 PDF 时存在 bug,
按照该链接https://github.com/mstamy2/PyPDF2/issues/368中描述的方法对源代码进行优化,

即将 ${PYTHON_PATH}/site-packages/PyPDF2/pdf.py 中的dest = Destination(NameObject("/"+title + " bookmark"), pageRef, NameObject(fit), *zoomArgs) 进行替换。

具体来说,

将上述代码行替换为dest = Destination(NullObject(), pageRef, NameObject(fit), *zoomArgs)

然后创建包含以下代码的脚本(假设命名为 PDFbookmark.py):

复制代码
    import re
    import sys
    
    from distutils.version import LooseVersion
    from os.path import exists, splitext
    from PyPDF2 import PdfFileReader, PdfFileWriter
    
    is_python2 = LooseVersion(sys.version) < '3'
    
    def _get_parent_bookmark(current_indent, history_indent, bookmarks):
       
    assert len(history_indent) == len(bookmarks)
    if current_indent == 0:
        return None
    for i in range(len(history_indent) - 1, -1, -1):
        # len(history_indent) - 1   ===>   0
        if history_indent[i] < current_indent:
            return bookmarks[i]
    return None
    
    def addBookmark(pdf_path, bookmark_txt_path, page_offset):
    if not exists(pdf_path):
        return "Error: No such file: {}".format(pdf_path)
    if not exists(bookmark_txt_path):
        return "Error: No such file: {}".format(bookmark_txt_path)
    
    with open(bookmark_txt_path, 'r', encoding='utf-8') as f:
        bookmark_lines = f.readlines()
    reader = PdfFileReader(pdf_path)
    writer = PdfFileWriter()
    writer.cloneDocumentFromReader(reader)
    
    maxPages = reader.getNumPages()
    bookmarks, history_indent = [], []
    # decide the level of each bookmark according to the relative indent size in each line
    #   no indent:          level 1
    #     small indent:     level 2
    #       larger indent:  level 3
    #   ...
    for line in bookmark_lines:
        line2 = re.split(r'\s+', unicode(line.strip(), 'utf-8')) if is_python2 else re.split(r'\s+', line.strip())
        if len(line2) == 1:
            continue
    
        indent_size = len(line) - len(line.lstrip())
        parent = _get_parent_bookmark(indent_size, history_indent, bookmarks)
        history_indent.append(indent_size)
    
        title, page = ' '.join(line2[:-1]), int(line2[-1]) - 1
        if page + page_offset >= maxPages:
            return "Error: page index out of range: %d >= %d" % (page + page_offset, maxPages)
        new_bookmark = writer.addBookmark(title, page + page_offset, parent=parent)
        bookmarks.append(new_bookmark)
    
    out_path = splitext(pdf_path)[0] + '-new.pdf'
    with open(out_path,'wb') as f:
        writer.write(f)
    
    return "The bookmarks have been added to %s" % out_path
    
    if __name__ == "__main__":
    import sys
    args = sys.argv
    if len(args) != 4:
        print("Usage: %s [pdf] [bookmark_txt] [page_offset]" % args[0])
    else:
        print(addBookmark(args[1], args[2], int(args[3])))
    
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    AI助手

在大多数情况下,默认将 page_offset 设置为 0 是合理的。除非遇到特定场景,在 PDF 文件中的目录页码与实际页面存在固定偏移时(通常是一个固定的偏移),此时只需将 page_offset 调整为该偏差值即可。

添加书签/目录

创建一个新的 txt 文件,并将其命名为 toc.txt;然后在其中手动输入待添加的目录项;如果需要,则可直接从文本型 PDF 中复制出对应的目录列表;按照以下统一格式进行组织:

复制代码
    Introduction                            	   14
    I. Interview Questions                         99
    Data Structures                            100
    Chapter 1 | Arrays and Strings             100
        Hash Tables                            100
        StringBuilder                          101
    Chapter 2 | Linked Lists                   104
        Creating a Linked List                 104
        The "Runner" Technique                 105
    Additional Review Problems                 193
    
    
      
      
      
      
      
      
      
      
      
      
    
    AI助手

唯一的要求是让每条记录的最后一位成为对应的页码位置,并且同一层级的书签应具有相同的缩进(使用空格或Tab)。

注意

注意

注意

在第 ① 步生成的PDFbookmark.py文件所在目录中启动终端/命令提示符,并通过以下代码即可完成任务。

复制代码
    其中最后一个参数 9 表示将 toc.txt 文件中的页码全部偏移 +9(即全部加上 9)
    
    
      
    
    AI助手
复制代码
     python ./PDFbookmark.py '/Users/Emrys/Desktop/demo 2015.pdf' '/Users/Emrys/Desktop/toc.txt' 9
    
    
      
    
    AI助手

运行完成后,在目标PDF文件及其子文件夹中会自动生成一个新文件。命名为[原始文件名]-new.pdf。举例如下:

【补充】:可能存在的问题

问题一:ValueError: {’/Type’: ‘/Outlines’, ‘/Count’: 0} is not in list

当某个PDF文件在经过其他PDF编辑器软件进行目录/书签修改后进行操作时,可能会导致类似以下情况出现:

复制代码
    Traceback (most recent call last):
      File ".\PDFbookmark.py", line 70, in <module>
    print(addBookmark(args[1], args[2], int(args[3])))
      File ".\PDFbookmark.py", line 55, in addBookmark
    new_bookmark = writer.addBookmark(title, page + page_offset, parent=parent)
      File "C:\Anaconda3\lib\site-packages\PyPDF2\pdf.py", line 732, in addBookmark
    outlineRef = self.getOutlineRoot()
      File "C:\Anaconda3\lib\site-packages\PyPDF2\pdf.py", line 607, in getOutlineRoot
    idnum = self._objects.index(outline) + 1
    ValueError: {'/Type': '/Outlines', '/Count': 0} is not in list
    
    
      
      
      
      
      
      
      
      
      
      
    
    AI助手

在这种情况下,请您指导如何对 PyPDF2 源码中 pdf.py 文件中的 getOutlineRoot() 函数进行优化。其中源码的位置位于 $PythonPath/-site-packages/PyPDF2/pdf.py$

复制代码
      def getOutlineRoot(self):
        if '/Outlines' in self._root_object:
            outline = self._root_object['/Outlines']
            try:
                idnum = self._objects.index(outline) + 1
            except ValueError:
                if not isinstance(outline, TreeObject):
                    def _walk(node):
                        node.__class__ = TreeObject
                        for child in node.children():
                            _walk(child)
                    _walk(outline)
                outlineRef = self._addObject(outline)
                self._addObject(outlineRef.getObject())
                self._root_object[NameObject('/Outlines')] = outlineRef
                idnum = self._objects.index(outline) + 1
            outlineRef = IndirectObject(idnum, 0, self)
            assert outlineRef.getObject() == outline
        else:
            outline = TreeObject()
            outline.update({ })
            outlineRef = self._addObject(outline)
            self._root_object[NameObject('/Outlines')] = outlineRef
    
        return outline
    
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    AI助手

问题二:RuntimeError: generator raised StopIteration

如果在进行了上述调整后,在执行脚本时触发了RuntimeError: generator raised StopIteration错误,请核实当前使用的Python版本是否为3.7及以上(因为自Python 3.7版本起对处理生成器终止迭代的行为进行了重大修改,请参见PEP 479),如果是,则建议尝试使用Python 3.6或更低版本来重新运行该代码。

Python 导出 PDF 中目录/书签

复制代码
    import sys
    
    from distutils.version import LooseVersion
    from os.path import exists
    from PyPDF2 import PdfFileReader
    
    is_python2 = LooseVersion(sys.version) < '3'
    
    
    def _parse_outline_tree(outline_tree, level=0):
    """Return List[Tuple[level(int), page(int), title(str)]]"""
    ret = []
    for heading in outline_tree:
        if isinstance(heading, list):
            # contains sub-headings
            ret.extend(_parse_outline_tree(heading, level=level+1))
        else:
            ret.append((level, heading.page.idnum, heading.title))
    return ret
    
    def extractBookmark(pdf_path, bookmark_txt_path):
    if not exists(pdf_path):
        return "Error: No such file: {}".format(pdf_path)
    if exists(bookmark_txt_path):
        print("Warning: Overwritting {}".format(bookmark_txt_path))
    
    reader = PdfFileReader(pdf_path)
    # List of ('Destination' objects) or ('Destination' object lists)
    #  [{'/Type': '/Fit', '/Title': u'heading', '/Page': IndirectObject(6, 0)}, ...]
    outlines = reader.outlines
    # List[Tuple[level(int), page(int), title(str)]]
    outlines = _parse_outline_tree(outlines)
    max_length = max(len(item[-1]) + 2 * item[0] for item in outlines) + 1
    # print(outlines)
    with open(bookmark_txt_path, 'w') as f:
        for level, page, title in outlines:
            level_space = '  ' * level
            title_page_space = ' ' * (max_length - level * 2 - len(title))
            if is_python2:
                title = title.encode('utf-8')
            f.write("{}{}{}{}\n".format(level_space, title, title_page_space, page))
    return "The bookmarks have been exported to %s" % bookmark_txt_path
    
    
    if __name__ == "__main__":
    import sys
    args = sys.argv
    if len(args) != 3:
        print("Usage: %s [pdf] [bookmark_txt]" % args[0])
    else:
        print(extractBookmark(args[1], args[2]))
    
    
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
    
    AI助手

以上代码来源【侵删】:
链接:https://www.zhihu.com/question/344805337/answer/1116258929
来源:知乎

FreePic2Pdf工具生成PDF文档目录

1. 软件下载

下载地址(https://pan.baidu.com/s/1kVHzVmf)密码:at9e

【注:该链接为VIP专属博文批量给pdf添加目录(最完整详细方法)提供,侵删】

软件面貌:

加粗样式

2. 将书籍的目录挂载到pdf步骤

2.1 打开软件FreePic2PDF,点击右下角“更改pdf”

在这里插入图片描述

2.2 选择“从PDF取书签”→需要操作的pdf文件→点击开始

在这里插入图片描述

该工具将在当前PDF文件及其目录中自动生成一个名为"Bookmarks"的配置文件夹,在此夹中包含了两个关键的ITF格式文档:"FreePic2Pdf.itf"以及对应的 bookmarks.txt 文件。
这些*.txt 文件主要用于存储 PDF 文件中的链接设置信息,在没有相关链接配置的情况下,默认状态下这些 files 会保持为空状态或者仅包含一些基础的 bookmarks.

在这里插入图片描述

2.3 复制PDF目录页面所有目录说明到文本编辑

接下来将PDF文档中的目录页面上的所有 directories 信息复制到文本编辑区域中,并使用的是Notepad++这一款文本编辑工具。

格式一定要对,特别是页码和节标题之间是tab键,各级目录之间要正确!!!(这是最麻烦的一步,但是肯定比自己一个个搞书签快)如下:

在这里插入图片描述

请将按规范整理好的文本导入到FreePic2Pdf_bkmk.txt文件中提取并生成书签文件。

2.4 将书签挂到pdf中

通过直接点击PDF文档并将其书签设置为跨页面链接的方式完成操作(此时需要确保该PDF文档已关闭)。请先打开文件并检查目录条目是否已成功添加。如果未能成功或存在一些不正确的地方,请稍作调整以解决问题。通常情况下这种情况可能源于TXT文件中的数据格式存在差异,请稍作调整。

如下添加成功了,哈哈哈~~~~~~

在这里插入图片描述
在这里插入图片描述

全部评论 (0)

还没有任何评论哟~