# PDFDirGenerator2
**Repository Path**: iam002/pdfdir-generator2
## Basic Information
- **Project Name**: PDFDirGenerator2
- **Description**: 基于 wxpython 和 PyPDF2 开发的一个简易的 GUI 程序 , 主要功能是给未添加书签的 PDF 添加目录书签。
- **Primary Language**: Python
- **License**: MIT
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2022-10-05
- **Last Updated**: 2022-10-07
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
- [PDF Dir Generator](#pdf-dir-generator)
- [安装必要的模块](#安装必要的模块)
- [运行前的准备](#运行前的准备)
- [运行命令行](#运行命令行)
- [运行 GUI 界面](#运行-gui-界面)
- [工程目录说明](#工程目录说明)
# PDF Dir Generator
**P**DF **D**ir(ectory) **G**enerator,PDF 目录生成器, 是基于 wxpython 和 PyPDF2 开发的一个简易的 GUI 程序,主要功能是给未添加书签的 PDF 添加目录书签。
---
### 安装必要的模块
首先必须保证 Python 运行版本不低于 3.7, 推荐使用的 Python 版本为 3.9.12.
运行本程序需要使用两个模块: [PyPDF2](https://pypi.org/project/PyPDF2/) 和 [wxpython](https://pypi.org/project/wxPython/), 我们可以通过 `pip` 进行安装.
如果你不需要 GUI 界面,可以只安装 `PyPDF2`.
```bash
pip install PyPDF2
pip install wxPython
```
安装完 PyPDF2 需要对 `_writer.py` 的 `get_outline_roor()` 函数进行修改, 不然运行时可能会如下报错:
```bash
File "C:\Users\q2799\.conda\envs\py36\lib\site-packages\PyPDF2\_writer.py", line 1195, in addBookmark
title, pagenum, parent, color, bold, italic, fit, *args
File "C:\Users\q2799\.conda\envs\py36\lib\site-packages\PyPDF2\_writer.py", line 1174, in add_bookmark
parent = self.get_outline_root()
File "C:\Users\q2799\.conda\envs\py36\lib\site-packages\PyPDF2\_writer.py", line 1008, in get_outline_root
idnum = self._objects.index(outline) + 1
ValueError: {'/Type': '/Outlines', '/First': IndirectObject(1006, 0, 3019818315728), '/Count': 493, '/Last': IndirectObject(1498, 0, 3019818315728)} is not in list
```
根据报错信息,找到 `_writer.py` 的 `get_outline_roor()` 的位置(不同版本的 PyPDF2 可能的位置不一样),修改如下:
```python
def get_outline_root(self) -> TreeObject:
if CO.OUTLINES in self._root_object:
# TABLE 3.25 Entries in the catalog dictionary
outline = cast(TreeObject, self._root_object[CO.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)
outline_ref = self._add_object(outline)
self._add_object(outline_ref.get_object())
self._root_object[NameObject('/Outlines')] = outline_ref
idnum = self._objects.index(outline) + 1
outline_ref = IndirectObject(idnum, 0, self)
assert outline_ref.get_object() == outline
else:
outline = TreeObject()
outline.update({})
outline_ref = self._add_object(outline)
self._root_object[NameObject(CO.OUTLINES)] = outline_ref
return outline
```
---
### 运行前的准备
运行之前,推荐先阅读这篇[博客](https://blog.csdn.net/weixin_44252933/article/details/127174469), 在运行之前我们需要准备好:
1. 待添加书签的 PDF
2. 包含书签目录信息的 txt
3. 目录页的偏移页数 `Offset`(一般是目录页的最后一页的页码)
---
### 运行命令行
1. 格式化目录
```bash
python .\dirFormat.py [<-option> ]
```
| 参数 | 选项 | 说明 |
|-----------------|------|-------------------|
| DIR_INPUT_PATH | 必需参数 | 目录文件的路径 |
| DIR_FORMAT_PATH | -o | 格式化目录的输出文件夹 |
| DIR_LOG_PATH | -l | 格式化目录文件的输出日志存放文件夹 |
| DELIMITER | -d | 单词分隔符 |
| PREFIX | -p | 目录文件每行的前缀 |
| PRE_LEVEL | -pl | 预定义级别 |
| PRE_TITLE | -pt | 预定义标题, 使用 `;` 隔开 |
【**例子**】
```bash
python .\dirFormator.py test/test_dir.txt -pl 2 -pt 参考文献
```
输出结果:
```bash
---------------------------- Format Dir -----------------------------------
运行时间: 2022-10-05 15:45:33
目录源文件路径: test/test_dir.txt
格式化目录路径:
日志路径:
单词分隔符:
级别标志符: .
前缀:
预定义级别: 2
预定义标题: 参考文献
---------------------------------------------------------------------------
[Line 29] 缩略语对照表 395 ==> 无法判断级别信息
[Line 30] 符号表 398 ==> 无法判断级别信息
[Line 31] 参考书目 401 ==> 无法判断级别信息
[Line 32] 索引 415 ==> 无法判断级别信息
---------------------------------------------------------------------------
Dir-log Path : test/test_dir.log
Dir format Path : test/test_dir_format.txt
---------------------------------- Done! ----------------------------------
```
2. 修改目录
格式程序的同时会进行简单的检查,并输出日志信息. 可以根据这些信息对格式化后的目录进一步的修改.
3. 生成目录
修改完后的目录文件,作为这一步的输入目录文件;同时需要删除待添加书签的 PDF 原先的书签。
然后执行指令:
```bash
python .\pdfDirGenerator.py [<-option> ]
```
| 参数 | 选项 | 说明 |
|-----------------|------|-------------|
| DIR_INPUT_PATH | 必需参数 | 目录文件的路径 |
| PDF_INPUT_PATH | 必须参数 | 待添加书签的PDF路径 |
| PDF_OUTPUT_PATH | -o | PDF输出目录 |
| DELIMITER | -d | 单词分隔符 |
| PREFIX | -p | 目录文件每行的前缀 |
| OFFSET | -O | 页码偏移量 |
【**例子**】
```bash
python .\pdfDirGenerator.py test\format\pdg_normal_dir.txt test\pdg_test.pdf -O 5
```
输出结果:
```bash
--------------------------- Adding the bookmark ---------------------------
PDF input path: test\pdg_test.pdf
PDF output path:
Dir path: test\format\pdg_normal_dir.txt
Offset: 5
Prefix:
Delimiter:
---------------------------------------------------------------------------
[ 1/ 23 finished] level: 0, title: 第1章 计算机网络概论, page: 6
[ 2/ 23 finished] level: 1, title: 1.1 计算机网络的形成与发展, page: 6
[ 3/ 23 finished] level: 2, title: 1.1.1 分组交换技术的研究, page: 6
[ 4/ 23 finished] level: 2, title: 1.1.2 互联网的形成, page: 9
[ 5/ 23 finished] level: 2, title: 1.1.3 互联网的高速发展, page: 13
[ 6/ 23 finished] level: 2, title: 1.1.4 移动互联网的发展 , page: 14
[ 7/ 23 finished] level: 1, title: 1.2 计算机网络定义与分类, page: 17
[ 8/ 23 finished] level: 1, title: 1.3 各种类型网络 y 的特点, page: 20
[ 9/ 23 finished] level: 2, title: 1.3.1 广域网, page: 20
[ 10/ 23 finished] level: 2, title: 1.3.2 城域网, page: 23
[ 11/ 23 finished] level: 2, title: 1.3.3 局域网, page: 25
[ 12/ 23 finished] level: 0, title: 小结, page: 26
[ 13/ 23 finished] level: 1, title: 习题, page: 26
[ 14/ 23 finished] level: 0, title: 第2章 物理层, page: 27
[ 18/ 23 finished] level: 1, title: 2.2 数据通信 x 的基本概念 , page: 31
[ 19/ 23 finished] level: 2, title: 2.2.1 测试, page: 32
[ 20/ 23 finished] level: 3, title: 2.2.1.1 测试, page: 33
[ 21/ 23 finished] level: 0, title: 小结, page: 34
[ 22/ 23 finished] level: 1, title: 习题, page: 34
[ 23/ 23 finished] level: 0, title: 附录, page: 35
---------------------------------------------------------------------------
Save: test\pdg_test(书签).pdf
---------------------------------- Done! ----------------------------------
```
---
### 运行 GUI 界面
运行 `main.py` 启动 GUI 界面
```
python .\main.py
```
**演示**
**主界面**
**参数设置**
**参数说明**
- **Offset** 页码偏移量,书签对应的页数 = 目录文件的page + offset, 一般这个值等于目录页最后一页所在的页码;
- **Prefix** 目录文件每行的前缀, 生成书签时用于判断书签的级别
- **DELIMITER** 单词分隔符, 通常是空格, 不建议修改
- **LEVEL_MARKER** 级别标志符, 格式化书签时用于判断书签的级别
- **Pre-level** 预定义级别
- **Pre-title** 预定义标题, 注意使用“;”隔开,不要有多余的空格
- **PDF Output Path** PDF输出目录, 留空会根据输入PDF名自动生成
- **Dir-Log Path** 格式化目录文件的输出日志存放文件夹目录,留空则与原目录在同一文件夹
- **Dir-format Path** 格式化目录的输出文件夹,留空则与原目录在同一文件夹
这里通常需要设置的是 **Offset** 以及 **Pre-title**,设置完成后记得点击 `Finish`。
除了这种设置方式外,还支持直接导入配置文件(文件后缀名为 `.conf`):
**配置文件**
```python
###########################################################################
#
# 这是一个配置文件, 以行为基本单元, 可以分为注释行, 赋值行和空行.
#
# 注释行以字符 '#' 开头, 程序会忽略以'#'开头的行; 行首直接回车,则是空行; 注释行
# 和空行可以提高配置文件的可读性.
#
# 赋值行的格式为: 字段名 = 值:
# 1. "值"可以修改, 但不要修改"字段名";
# 2. "="两边的空格可有可无, 数量没有限制;
#
###########################################################################
# str, 待添加书签的PDF路径
PDF_INPUT_PATH = "C:\Users\q2799\Project\PDFDirGenerator2\test\pdg_test.pdf"
# str, 目录文件的路径
DIR_INPUT_PATH = "C:\Users\q2799\Project\PDFDirGenerator2\test\pdg_normal_dir.txt"
# int, 页码偏移量, 通常是PDF目录页最后一页的页码
OFFSET = 5
# char, 目录文件每行的前缀, 生成书签时用于判断书签的级别
PREFIX = "\t"
# char, 单词分隔符, 通常是空格, 不建议修改
DELIMITER = " "
# char, 级别标志符, 格式化书签时用于判断书签的级别
LEVEL_MARKER = "."
# int, 预定义级别
PRE_LEVEL = 2
# str, 预定义标题, 使用';'隔开
PRE_TITLE = "本章小结;习题"
# str, 格式化目录文件的输出日志存放文件夹,留空则与原目录在同一文件夹
DIR_LOG_PATH = "C:\Users\q2799\Project\PDFDirGenerator2\test\log"
# str, 格式化目录的输出文件夹,留空则与原目录在同一文件夹
DIR_FORMAT_PATH = "C:\Users\q2799\Project\PDFDirGenerator2\test\format"
# PDF输出目录, 留空会根据输入PDF名自动生成
PDF_OUTPUT_PATH = "C:\Users\q2799\Project\PDFDirGenerator2\test\outpdf"
# int, 是否只显示目录日志
IS_PRINT_ACTICE_ONLY = 0
# int, 生成日志前默认先进行格式化
IS_FORMAT_DIR_FIRST = 1
# int, 是否打印生成书签过程中的信息
IS_PRINT_PROCESS = 1
```
### 工程目录说明
- `dirFormator.py` 格式化目录脚本
- `pdfDirGenerator` PDF 目录生成脚本
- `main.py` GUI 运行脚本
- `utils.py` 存放一些公共函数和全局变量
- `wxb_*.py` 通过 [wxFormBuilder](https://github.com/wxFormBuilder/wxFormBuilder) 自动生成的脚本(用于图像界面的设计)
- `mainFrame.py` 主窗口
- `runningDialog.py` 运行弹窗
- `settingsDialog.py` 设置弹窗
- `aboutDialog` 关于弹窗
- `assets\` 存放一些图片等附件
- `design_gui\` 存放 [wxFormBuilder](https://github.com/wxFormBuilder/wxFormBuilder) 工程文件,可用 [wxFormBuilder](https://github.com/wxFormBuilder/wxFormBuilder) 打开
- `test\` 存放一些测试数据
- `conf\` 包含一个配置文件 `pdg_settings.conf`
- `format\` 包含一个格式化目录 `pdg_normal_dir.txt`
- `log\` 包含格式化目录的日志信息 `pdg_normal_dir.log`
- `outpdf\` 存放运行结果 `pdg_test(书签).pdf`
- `pdg_test.pdf` 测试 PDF
- `*_dir.txt` 测试目录文件
```Tree
PDFDirGenerator2
├─ aboutDialog.py
├─ assets
├─ blog.md
├─ design_gui
│ ├─ noname.cpp
│ ├─ noname.h
│ └─ pdg2.fbp
├─ dirFormator.py
├─ main.py
├─ mainFrame.py
├─ pdfDirGenerator.py
├─ README.md
├─ runningDialog.py
├─ settingsDialog.py
├─ test
│ ├─ conf
│ │ └─ pdg_settings.conf
│ ├─ format
│ │ └─ pdg_normal_dir.txt
│ ├─ log
│ │ └─ pdg_normal_dir.log
│ ├─ outpdf
│ │ └─ pdg_test(书签).pdf
│ ├─ pdg_invalidnum_dir.txt
│ ├─ pdg_normal_dir.txt
│ ├─ pdg_test.pdf
│ └─ test_dir.txt
├─ utils.py
├─ wxb_mainFrame.py
├─ wxb_runningDialog.py
└─ wxb_settingsDialog.py