Skip to content

Commit

Permalink
update posts: section/column
Browse files Browse the repository at this point in the history
  • Loading branch information
yunjian.wu committed Jun 1, 2021
1 parent 7072fcb commit b19bd38
Show file tree
Hide file tree
Showing 6 changed files with 249 additions and 36 deletions.
31 changes: 24 additions & 7 deletions docs/2020-07-13-pdf2docx开发概要.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,14 @@ tags: [python]

**PDF转Word** 是一个古老的话题,其难点在于建立PDF基于元素位置的格式与Word基于内容的格式之间的映射关系。[`Solid Documents`](https://solidframework.net/)是这方面的佼佼者,其技术的应用案例:在线PDF转换网站[Smallpdf](https://smallpdf.com/pdf-to-word)

在某个项目的调研过程中,作者尝试了这个话题,编写了一个用于转换PDF到Word的Python库`pdf2docx`——依赖`PyMuPDF`解析PDF文件,并用`python-docx`创建Word文件。本文记录相关开发思路(一些特性仅针对如下列出版本而言)。
在某个项目的调研过程中,作者尝试了这个话题,编写了一个用于转换PDF到Word的Python库`pdf2docx`——借助`PyMuPDF`从PDF文件提取内容,基于位置规则解析内容,最后用`python-docx`创建Word文件。

> https://github.com/dothinking/pdf2docx
本文记录主要开发思路,具体细节随着版本升级可能略有差异。

![sample](https://camo.githubusercontent.com/a581ee7caccdcf093648d54445fcc689a32ef205a81594c94b2e410cbde07757/68747470733a2f2f73312e617831782e636f6d2f323032302f30382f30342f6144727978312e706e67)

- 项目地址:https://github.com/dothinking/pdf2docx
- 当前版本:v0.4.3
- 依赖库版本:`PyMuPDF v1.17.3``python-docx v0.8.10`


## 思路
Expand All @@ -27,19 +30,33 @@ tags: [python]

以上技术路线也决定了`pdf2docx`的局限:

- 只能处理标准格式的PDF,不支持扫描版、图片格式PDF。

- 只能处理水平、竖直方向文本,忽略旋转角度的文本。

- 根据有限的、确定的规则建立PDF导出元素位置与docx要求的内容之间的映射并非完全可靠,也就是说仅能处理常见的规范的格式,而非百分百还原。

- Word格式还受到`python-docx`处理能力的限制,例如截至版本`0.8.10``python-docx`尚不支持浮动图片。


## 目录

以下分篇介绍提取PDF页面数据、解析和重建docx过程中的具体细节:

- [提取文本、图片和形状](2020-07-14-pdf2docx开发概要:提取文本、图片和形状.md)
- [解析文本样式](2020-07-20-pdf2docx开发概要:解析文本样式.md)

- [获取图片及其位置](2020-10-15-pdf2docx开发概要:获取图片及其位置.md)
- [创建浮动图片](2020-10-25-pdf2docx开发概要:创建浮动图片.md)
- [矢量图处理](2020-10-01-pdf2docx开发概要:矢量图处理.md)

- [解析页面布局](2021-05-30-pdf2docx开发概要:解析页面布局.md)

- [解析表格](2020-08-15-pdf2docx开发概要:解析表格.md)
- [解析页面布局](2020-08-27-pdf2docx开发概要:解析页面布局.md)

- [对齐隐式表格线](2020-09-27-pdf2docx开发概要:对齐隐式表格线.md)

- [解析段落](2020-08-27-pdf2docx开发概要:解析段落.md)

- [解析文本样式](2020-07-20-pdf2docx开发概要:解析文本样式.md)


[^1]: [PDF Reference 1.7](https://www.adobe.com/content/dam/acom/en/devnet/pdf/pdf_reference_archive/pdf_reference_1-7.pdf)
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,13 @@ tags: [python]

![文本与图片块结构](https://pymupdf.readthedocs.io/en/latest/_images/img-textpage.png)

`pdf2docx`继续沿用以上数据结构,并稍作改动:
`pdf2docx`继续沿用以上数据结构,并稍作改动,作为第一类基本数据`Block`

- 将图片块整合到文本块中,作为`line`>`span`级别的元素
- 将图片块整合到文本块`TextBlock`,作为`line`>`span`级别的元素

- `block`元素字典增加了页面布局参数,例如`alignment``left_space``before_space`用以保存后续页面布局解析结果
- `block`增加了布局参数,例如`alignment``left_space``before_space`用以保存后续段落布局解析结果

- 新增了表格块,用以保存表格解析结果
- 新增了表格块`TableBlock`,用以保存表格解析结果

!!! warning "注意"
后续版本中发现`extractDICT()`获取图片存在问题(如只能获取完全显示在页面中的图片、丢失alpha通道),还需配合`page.getImageList()`处理;另外,v0.5.0版本中对浮动图片也进行了支持,详见以下两篇图片相关的记录。
Expand All @@ -35,7 +35,9 @@ tags: [python]

## 形状

文本与图片构成了主体内容,它们的样式则由 **形状** 来描述,例如代表文本高亮的矩形块,表明表格边线的直线(很细的矩形)。这里的所谓的形状具体指两类来源:
文本与图片构成了主体内容,它们的样式则由 **形状** 来描述,例如代表文本高亮的矩形块,表明表格边线的直线(很细的矩形)。形状构成了`pdf2docx`的第二类基本数据`Shape`

这里的所谓的形状具体指两类来源:

- PDF原始文件中的路径`Path`

Expand Down Expand Up @@ -150,6 +152,13 @@ EMC
`Hyperlink`类似于`Stroke`,本质区别在于`Hyperlink`的语义类型是已知的。更一般地理解,`Stroke`是一种几何类型,事先并不知道它的语义类型(边框、删除线还是下划线?);而`Hyperlink`是一种确定了语义类型的`Stroke`


## 总结

借助`PyMuPDF`从PDF提取文本、图片和形状数据,它们构成了`pdf2docx`的基础数据:

- `Block`代表主体内容,例如文本/图片组成的`TextBlock`和表格结构的`TableBlock`,它们都将作为Word中的段落。

- `Shape`代表格式内容,例如描边`Stroke`将对应下划线、表格边框等,填充`Fill`将对应文本高亮、单元格背景色等。


[^1]: [TextPage](https://pymupdf.readthedocs.io/en/latest/textpage.html)
Expand Down
4 changes: 2 additions & 2 deletions docs/2020-07-20-pdf2docx开发概要:解析文本样式.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,9 @@ tags: [python]
在原始文本块数据结构的基础上,为`span`块新增表征解析结果的`style`属性:

```python
# dict of span
# span
{
"bbox": (float, float, float, float)
"bbox": [float, float, float, float],
"size": float,
"flags": int,
"font": str,
Expand Down
14 changes: 9 additions & 5 deletions docs/2020-08-15-pdf2docx开发概要:解析表格.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,13 +84,14 @@ PDF中没有语义上的表格的概念,所以需要根据外观上的表格

## 数据结构

参考文本和图片,定义表格类型的数据结构:
表格块`TableBlock`继承了第一类基本数据`Block`的结构(详见[PDF提取](2020-07-14-pdf2docx开发概要:提取文本、图片和形状.md)),同时新增了表征表格结构的`Row``Cell`。其中`Cell`也是布局级别的对象,嵌套了下一级的`Block``Shape`


```python
{
'type': int, # 3-explicit table; 4-implicit table
'bbox': (float, float, float, float),
..., # some spacing properties
..., # some spacing properties same with Block
"rows": [
{
'bbox': (float, float, float, float),
Expand All @@ -103,9 +104,12 @@ PDF中没有语义上的表格的概念,所以需要根据外观上的表格
'border-width': (float, float, float, float),
'merged-cells': (int, int),
'blocks': [
{
# text/image blocks contained in current cell
}
{ # text/image blocks contained in current cell },
{ ... }
],
'shapes': [
{ # shapes contained in current cell }
{ ... }
]
},
... # more cells
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,19 @@ categories: [process automation]
tags: [python]
---

# pdf2docx开发概要:解析页面布局
# pdf2docx开发概要:解析段落


---


经过[表格解析](2020-08-15-pdf2docx开发概要:解析表格.md)[文本样式识别](2020-07-20-pdf2docx开发概要:解析文本样式.md)后,我们得到了整合后的文本/图片/表格块。其中文本/图片块将被重建为段落,表格块将被重建为表格,单元格内文本/图片块按照相同的逻辑处理。在此基础上,计算相邻元素之间的间距,例如竖直方向的前后段间距、水平方向的段落缩进。
经过[表格解析](2020-08-15-pdf2docx开发概要:解析表格.md)后,我们得到了整合的文本/图片/表格`Block`。其中文本/图片块将被重建为段落,表格块将被重建为表格,单元格内文本/图片块按照相同的逻辑处理。在此基础上,计算相邻元素之间的间距,例如竖直方向的前后段间距、水平方向的段落缩进。

## 页面大小与页边距

`PyMuPDF`解析结果直接包括了页面宽度和高度,同时根据所有块级元素占据区域的极限值得到页边距,例如最小左上角点确定了左边距和上边距。

`python-docx`中页面`section`对象恰好提供了这六个属性,例如`page_width``left_margin`。于是页面基本形式得以确定。


## 竖直方向定位:段间距

块级元素之间通过段前/段后间距确定相对位置关系。由于Word中段落具有段前/段后间距属性,文本块将被作为定位的参考元素。
`Block`块级元素之间通过间距确定相对位置关系。由于Word中 **段落具有段前/段后间距属性**,文本块将被作为定位的参考元素。

竖直间距确定原则:

Expand All @@ -42,12 +37,15 @@ tags: [python]

已知块的高度和内部行数,容易计算得到平均行距。Word中有两种设置行间距的方式:

- 固定值:即为计算得到的平均行距,优点是定位精确,缺点是不会随着字体大小修改而改变,不利于编辑。
- 倍数行距:与单倍行距的比值,优缺点与固定值行距正好相反
- 固定值:直接设置为计算出的平均行距,优点是定位精确,缺点是不会随着字号的变化而改变,不利于编辑。例如一旦增大字号,则有可能导致显示不全。

- 倍数行距:与单倍行距的比值,优缺点与固定值行距正好相反。

综合来看,倍数行距有更好的适应性,v0.5.2版开始启用倍数行距。

综合来看,倍数行距有更好的适应性,但是 **这个值的计算和字体相关**,单开一篇进行具体介绍
注意,倍数行距并非与字号的简单比值或者流传的1.2倍的比例关系,而是 **与具体字体相关**。单开一篇介绍倍数行距计算问题

> [TODO](to_do)
> [此坑待填...](to_do)

## 水平方向定位:对齐方式与缩进
Expand Down Expand Up @@ -85,15 +83,13 @@ tags: [python]

## 数据结构

综上,文本/表格块在标准数据结构的基础上,加入了如下定位相关属性:
综上,文本块`TextBlock`在标准`Block`数据结构(`Line`->`Span`->`Char`)的基础上,加入了如下定位相关属性:

```python
# text/table block
# text block
{
"type": 0,
"bbox": [float, float, float, float],

..., # text/table properties

# ----- vertical spacing -----
"before_space": float,
Expand All @@ -104,7 +100,32 @@ tags: [python]
"alignment": int,
"left_space": float,
"right_space": float,
"tab_stops": [float, float, ...]
"first_line_space": float,
"tab_stops": [float, float, ...],

"lines": [
{
"bbox": [float, float, float, float],
"wmode": int,
"dir": [float, float],
"line_break": int, # new property
"tab_stop": int, # new property
"spans": [
{
"bbox": [float, float, float, float],
"color": int,
"font": str,
"size": float,
"flags": int,
"text": str,
"chars": [{
"bbox": [float, float, float, float],
"c": str
}]
}
]
}
]
}
```

Loading

0 comments on commit b19bd38

Please sign in to comment.