文章目录

  • 1. 使用 python 给 PDF 添加书签
    • 1.1. 安装 `PyPDF2`
    • 1.2. 提取 PDF 的目录信息并保存在 txt
    • 1.3. 编程实现
    • 1.4. 可能遇到的错误
      • 1.4.1. 问题一:ValueError: {’/Type’: ‘/Outlines’, ‘/Count’: 0} is not in list
      • 1.4.2. 问题二:RuntimeError: generator raised StopIteration
    • 1.5. 代码下载
    • 1.6. 参考

1. 使用 python 给 PDF 添加书签

有时下载到扫描版的 PDF 是不带书签目录的,这样阅读起来很不方便。下面通过 python 实现一个半自动化添加书签目录的脚本。

1.1. 安装 PyPDF2

pip install pypdf2

未避免后续运行程序报错,python 版本必须是 3.7 之前的(3.6)。

1.2. 提取 PDF 的目录信息并保存在 txt

这一步是比较麻烦的,需要手动实现。一般可以使用一些 OCR 文字识别工具,或者将目录页转化为 word 来操作。然后整理为如下的 txt 格式:

  • 每一行包含三项:级别 level、 标题 title、 页数 page,用空格隔开
  • 使用“.”来判断书签的级别,例如:
    • “第1章” 包含 0 个 “.” 是一级标题
    • “1.1” 包含 1 个 “.” 是二级标题
    • “1.1.1” 包含 2 个 “.” 是三级标题
    • ……(以此类推)
  • 请不要有多余的空行,title里也不要有空格

这里是我整理后的 txt:

第1章 绪论 1
1.1 本书的目的 1
1.2 信息融合的主要挑战 5
1.3 为什么需要随机集或FISST 5
1.3.1 多目标滤波的复杂性 6
1.3.2 超越启发式 7
1.3.3 单目标与多目标统计学的区别 7
1.3.4 常规数据与模糊数据的区别 7
1.3.5 形式化贝叶斯建模 8
1.3.6 模糊信息建模 8
1.3.7 多源多目标形式化建模 9

1.3. 编程实现

import PyPDF2
import sysclass PdfDirGenerator:def __init__(self, pdf_path:str, txt_path:str, offset:int, out_path:str=None, levelmark:str='.'):self.pdf_path = pdf_path    # pdf路径self.txt_path = txt_path    # 包含pdf目录信息的txtself.offset = offset        # 目录页数偏移量self.out_path = out_path    # 输出路径self.levelmark = levelmark  # 用于判断书签级别的标志符self.dir_parent = [None]    def getLevelId(self, level):"""计算书签的级数(级数的标志符号为“.”)一级目录: 0 个“.”,例如: 第1章、附录A等二级目录: 1个“.”,例如: 1.1、A.1三级目录: 2个“.”,例如: 2.1.3"""mark_num = 0for c in level:if c == self.levelmark:mark_num += 1return mark_num + 1def run(self):print("--------------------------- Adding the bookmark ---------------------------")print(" * PDF Source: %s" % self.pdf_path)print(" * TXT Source: %s" % self.txt_path)print(" * Offset: %d" % self.offset)print("---------------------------------------------------------------------------")with open(self.txt_path, 'r', encoding='utf-8') as txt:pdf_reader = PyPDF2.PdfFileReader(self.pdf_path)pdf_writer = PyPDF2.PdfFileWriter()pdf_writer.cloneDocumentFromReader(pdf_reader)# BUG: ValueError: {’/Type’: ‘/Outlines’, ‘/Count’: 0} is not in list# 修改代码 ${PYTHON_PATH}/site-packages/PyPDF2/pdf.py): getOutlineRoot 函数# 参考:https://www.codetd.com/en/article/11823498lines = txt.readlines()num_all_lines = len(lines)for i, line in enumerate(lines):# pline = line.split(' ') # 要求level title page之间只能有一个空格且不能是\t, 比较麻烦换成下面这个pline = line.split(None, maxsplit=-1) # python 3.6.13, 支持删除\t\n 空格, 且不限次数level = pline[0]; title = pline[1]; page = int(pline[2]) + self.offset# 1. 计算当前的 level 的级数 id# 2. 当前书签的父结点存放在 dir_parent[id-1] 上# 3. 更新/插入 dir_parent[id] id = self.getLevelId(level)if id >= len(self.dir_parent):self.dir_parent.append(None)self.dir_parent[id] = pdf_writer.addBookmark(level+' '+title, page-1, self.dir_parent[id-1])print(" * [%d/%d finished] level: %s(%d), title: %s, page: %d" % (i+1, num_all_lines, level, id, title, page))if self.out_path is None:self.out_path = self.pdf_path[:-4] + '(书签).pdf'with open(self.out_path, 'wb') as out_pdf:pdf_writer.write(out_pdf)print("---------------------------------------------------------------------------")print(" * Save: %s" % self.out_path)print("---------------------------------- Done! ----------------------------------")if __name__ == '__main__':input_num = len(sys.argv)assert(input_num > 3)opath = Noneif input_num > 4:opath = sys.argv[4]mark='.'if input_num > 5:mark = sys.argv[5]pdg = PdfDirGenerator(pdf_path=sys.argv[1],txt_path=sys.argv[2],offset=int(sys.argv[3]), # 一般是目录结束页的页数out_path=opath,levelmark=mark)pdg.run()

上述代码保存在 PdfDirGenerator.py中,其中有3个参数和2个可选参数:

  • 第1个参数:待插入书签的 PDF 的路径
  • 第2个参数:包含目录信息的 txt 的路径
  • 第3个参数:正文内容的偏移页数(一般填目录结束页的页数)
  • 第4个参数(可选):输出路径
  • 第5个参数(可选):级数标志,默认为“.”

例如,在命令行中输入:

python .\PdfDirGenerator.py .\多源多目标统计信息融合Mahler.pdf .\dir.txt 27

运行效果:

1.4. 可能遇到的错误

这里主要参考 https://www.codetd.com/en/article/11823498

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

如果 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 addBookmarknew_bookmark = writer.addBookmark(title, page + page_offset, parent=parent)File "C:\Anaconda3\lib\site-packages\PyPDF2\pdf.py", line 732, in addBookmarkoutlineRef = self.getOutlineRoot()File "C:\Anaconda3\lib\site-packages\PyPDF2\pdf.py", line 607, in getOutlineRootidnum = self._objects.index(outline) + 1
ValueError: {'/Type': '/Outlines', '/Count': 0} is not in list

解决方法:修改 pdf.pygetOutlineRoot() 函数(pdf.py 的路径为 ${PYTHON_PATH}/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) + 1except ValueError:if not isinstance(outline, TreeObject):def _walk(node):node.__class__ = TreeObjectfor child in node.children():_walk(child)_walk(outline)outlineRef = self._addObject(outline)self._addObject(outlineRef.getObject())self._root_object[NameObject('/Outlines')] = outlineRefidnum = self._objects.index(outline) + 1outlineRef = IndirectObject(idnum, 0, self)assert outlineRef.getObject() == outlineelse:outline = TreeObject()outline.update({ })outlineRef = self._addObject(outline)self._root_object[NameObject('/Outlines')] = outlineRefreturn outline

1.4.2. 问题二:RuntimeError: generator raised StopIteration

如果在你做了上面的修改后,在运行脚本时报错:untimeError: generator raised StopIteration,请检查使用 Python 版本是不是 3.7或者更高版本(从版本v3.7之后,Python终止迭代过程发生了变化,细节可以参考PEP 479)。为避免报错,请使用低于3.7版本的 python,例如 3.6 版本。

1.5. 代码下载

  • https://gitee.com/iam002/add_pdf_bookmarker

  • 这里用的 PDF 是 多源多目标统计信息融合 by Mahler (z-lib.org).pdf ,有需要的同学可点击 阿里云盘 下载。

1.6. 参考

  • https://www.codetd.com/en/article/11823498
  • https://www.cnblogs.com/1blog/p/15186521.html
  • https://www.jianshu.com/p/1aac3ae4d620?tdsourcetag=s_pcqq_aiomsg

使用 python 给 PDF 添加目录书签相关推荐

  1. python 给PDF添加目录

    目录 一.相关库安装 1.PyPDF系列: 2.pdfplumber 3.pdfminer3k 二.主要代码 1.pdfplumber提取相关信息 2.addBookmark 一.相关库安装 pip ...

  2. python读取pdf文档书签 bookmark_Python利用PyPDF2库获取PDF文件总页码实例

    Python中可以利用PyPDF2库来获取该pdf文件的总页码,可以根据下面的方法一步步进行下去: 1.首先,要安装PyPDF2库,利用以下命令即可: pip install PyPDF2 2.接着, ...

  3. 批量给pdf添加目录(最完整详细方法)

    前言 很多时候 , 大家在网上下的电子书籍 , 因为是扫描版本 , 所以根本没有目录 , 尽管pdf阅读器它提供了添加目录的方法 , 但是 , 需要自己一个一个去手动添加 , 这样效率太低了.没有目录 ...

  4. 批量给pdf添加目录-亲测可用

    批量给pdf添加目录 下载工具FreePic2Pdf 我们获取目录文件和添加目录都是靠着这个软件. 下载地址(百度网盘 请输入提取码)密码:at9e 找到您要添加的目录 方法很多 , 大家可以百度直接 ...

  5. 如何为PDF添加目录

    这里介绍一个在线为PDF添加目录的网站 https://magic-pdf.tk 使用步骤 1. 进入在线编辑PDF目录的网站 https://magic-pdf.tk 2. 点击"选择文件 ...

  6. 给知网没有书签的pdf文件添加书签(利用python解析txt文件内容并为pdf添加目录)

    今天利用tampermonkey的知网下载助手脚本下载pdf格式论文时,发现论文缺少书签,而脚本可以下载一个txt格式的书签(目录),因此打算利用python将txt格式的目录添加到pdf中. txt ...

  7. python读取pdf文档书签 bookmark_用Python为PDF文件批量添加书签

    平时看一些大部头的技术书籍,大多数都是PDF版的,而且有一些书籍是影印扫描版的,几百上千页的书,没有任何书签,想要找到一个章节的位置非常费劲.那么就想,能不能搞一个工具,来自动地为这些大部头的PDF书 ...

  8. Adobe Acrobat 给pdf添加多级书签(制作目录)

    1,用Adobe acrobat 软件打开待添加书签的pdf文档. 2,打开之后点击软件左边栏的书签(有时被隐藏了,点击一下界面左边靠近中间位置的箭头). 3,打开书签栏之后,把pdf定位到要加书签的 ...

  9. 使用python合并pdf文件带书签

    1.需求: 将几本纸质书进行了扫描,可是扫描的每页生成一个pdf文件.需要怎么才能把这些pdf文件合成一个呢?adoba acrobat工具支持,可是收费.我们平时用的都是adoba reader,只 ...

  10. 扫描版PDF添加目录

    扫描版PDF如何添加目录- 准备工作 使用的软件:FreePic2Pdf-v11.0.3.1.exe(下载链接:百度网盘 提取码:28xb)单独一个exe的文件 PDF文件 插入目录步骤 双击打开Fr ...

最新文章

  1. CSDN粉丝解答:六月份第一期精选——互联网笔试编程解决、简单bug处理、编程系统设计等
  2. redis与lua整合
  3. Git 通过源码编译安装 2.12.2 遇到的问题及解决
  4. netsuite 数据集成_Java中带有NetSuite数据实体的对象关系映射(ORM)
  5. 会话跟踪技术之Cookie
  6. 使用深度学习框架 Tensorflow 实现线性回归 (Linear Model)
  7. struts中ModelDriven()接口
  8. 对于公司来说,企业内训是否真的有必要?
  9. Jenkins 自动化部署上线
  10. linux shell 基本规范
  11. 中国糖和甜味剂市场趋势报告、技术动态创新及市场预测
  12. 科技正在淘汰传统行业,这次的“倒霉鬼”是券商
  13. 即时通讯工具的基本功能和如何做防御
  14. 幅相曲线渐近线_第十讲 频域分析法(Nyquist曲线)
  15. 基于MFC和c++的销售管理系统,课程设计,实训
  16. Pytorch学习——池化层
  17. 2019 NeurIPS | Graph Transformer Networks
  18. 第三方对接-支付宝支付
  19. Android12之OpenSL ES基础数据结构总结(十)
  20. [Vue][面试]你怎么理解vue中的diff算法?

热门文章

  1. 华中农业大学计算机学硕,华中农业大学工学院2017年研究生招生预录取名单(含专业和导师)...
  2. Johnnie Walker惊艳的广告
  3. 位置不可用无法访问介质受写入保护 chkdsk无法修复. 不知道怎样找到呢?
  4. php编程怎样装数据库,php数据库管理工具phpmyadmin下载、安装、配置
  5. less中使用global的两种用法
  6. ECSHOP问题总结
  7. 假设检验与常见的统计检验方法
  8. [BZOJ4556][Tjoi2016Heoi2016]字符串(二分+后缀数组+主席树)
  9. office 复合文档数据结构解析“初探”
  10. Shaderlab 玻璃效果