Skip to content

Commit

Permalink
update posts: connection between posts
Browse files Browse the repository at this point in the history
  • Loading branch information
dothinking committed Jun 1, 2021
1 parent b19bd38 commit 3b1d69b
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ tags: [python]

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

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


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

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

## 总结

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

- `Block`代表主体内容,例如文本/图片组成的`TextBlock`和表格结构的`TableBlock`,它们都将作为Word中的段落
- 块元素`Block`代表主体内容,例如直接提取出的文本/图片组成的`TextBlock`,以及后续解析得到的表格结构`TableBlock`

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


[^1]: [TextPage](https://pymupdf.readthedocs.io/en/latest/textpage.html)
Expand Down
2 changes: 1 addition & 1 deletion docs/2020-08-15-pdf2docx开发概要:解析表格.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ PDF中没有语义上的表格的概念,所以需要根据外观上的表格

## 数据结构

表格块`TableBlock`继承了第一类基本数据`Block`的结构(详见[PDF提取](2020-07-14-pdf2docx开发概要:提取文本、图片和形状.md)),同时新增了表征表格结构的`Row``Cell`。其中`Cell`也是布局级别的对象,嵌套了下一级的`Block``Shape`
表格块`TableBlock`继承了块元素`Block`的结构(详见[PDF提取](2020-07-14-pdf2docx开发概要:提取文本、图片和形状.md)),同时新增了表征表格结构的`Row``Cell`。其中`Cell`是布局级别的对象(`Layout`),嵌套了下一级的块元素和形状元素,如此递归,直到块元素中不再包含表格,即只有表征文本和图片的`TextBlock`


```python
Expand Down
22 changes: 15 additions & 7 deletions docs/2020-08-27-pdf2docx开发概要:解析段落.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ tags: [python]
---


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



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

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

竖直间距确定原则:

Expand All @@ -37,20 +37,20 @@ tags: [python]

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

- 固定值:直接设置为计算出的平均行距,优点是定位精确,缺点是不会随着字号的变化而改变,不利于编辑。例如一旦增大字号,则有可能导致显示不全
- 固定值:直接设置为计算出的平均行距,优点是定位精确,缺点是不会随着字号的变化而改变,不利于编辑。例如一旦增大字号,则有可能导致该行文本显示不全

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

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

注意,倍数行距并非与字号的简单比值或者流传的1.2倍的比例关系,而是 **与具体字体相关**。单开一篇介绍倍数行距计算问题:
注意,倍数行距并非行高与字号的简单比值或者流传的1.2倍的比例关系,而是 **与具体字体相关**。单开一篇介绍倍数行距计算问题:

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

## 水平方向定位:对齐方式与缩进

水平方向从内部(块内元素的对齐关系)和外部(页面中的位置)两个方面确定对齐方式:左/居中/右/分散对齐。其中左对齐为默认方式,因为结合段落左缩进和制表符,总能正确定位任何块间元素。
水平方向从内部(块内元素`Line`的对齐关系)和外部(页面中的位置)两个方面确定对齐方式:左/居中/右/分散对齐。其中左对齐为默认方式,因为结合段落左缩进和制表符,总能正确定位任何块间元素。

**内部对齐关系**:行与行之间位置关系

Expand Down Expand Up @@ -81,9 +81,17 @@ tags: [python]
- 如果只有一行,则将这个放宽放到极限,即设置相应边距等于0。


## 文本样式

以上解析结果确定了段落在页面中的位置和呈现样式,接下来深入到段内文本。`PyMuPDF`提取的原始文本块自带了字体、颜色、斜体、粗体等属性,但是`高亮``下划线``删除线`等具体样式需要进一步根据 **文本和形状的位置关系** 来判定。

具体参考下文:

> [pdf2docx开发概要:解析文本样式](2020-07-20-pdf2docx开发概要:解析文本样式.md)
## 数据结构

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

```python
# text block
Expand Down
18 changes: 9 additions & 9 deletions docs/2021-05-30-pdf2docx开发概要:解析页面布局.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ tags: [python]

---

[前文](2020-07-14-pdf2docx开发概要:提取文本、图片和形状.md)介绍了从PDF直接提取出的基本数据文本/图片/表格块`Block`和形状`Shape`,但是它们目前还只有位置数据,接下来要做的是解析语义数据,也就是从`Block`解析出段落和段落属性如段间距、对齐方式,从`Shape`解析出文本样式例如下划线、高亮及其作用的文本、解析表格样式如边框颜色、背景色及其包含的段落。我们先从页面布局开始。
[前文](2020-07-14-pdf2docx开发概要:提取文本、图片和形状.md)介绍了从PDF直接提取出的基本数据:块元素`Block`和形状`Shape`,但是它们目前还只有位置数据,接下来要做的是解析语义数据,例如从块元素解析出表格和普通段落,从形状元素解析出文本样式例如下划线、高亮及其作用的文本、解析表格样式如边框颜色、背景色及其包含的段落。我们先从页面布局开始。


## 数据结构
Expand All @@ -17,9 +17,9 @@ tags: [python]

- `Page`类对应物理上的一个页面,具有尺寸、边距、页眉页脚等属性,主要内容按照流式布局顺序被分为一个或者多个`Section`

- `Section`对应Word中的Section,表示具有类似结构的布局,例如一个或者多个分栏`Column`
- `Section`对应Word的节(Section),表示具有类似结构的布局,例如一个或者多个分栏`Column`

- `Column`对应Word中的Column,表示分栏中的一列。对于普通的排版,主要元素都在一个`Section`的一个`Column`
- `Column`对应Word的分栏(Column),表示分栏中的一列。对于普通的排版,块元素和形状元素都在一个`Section`的一个`Column`

```
┌───────────────────────────────────┐
Expand All @@ -42,14 +42,14 @@ tags: [python]

## 页眉页脚

理论上,无法从单个页面识别出页眉和页脚,所以需要从文档级别、多个Pages一起对比结构相似性,从而从页面开始部分分离出页眉、从页面结束部分分离出页脚。同时,需要处理一些不确定因素,例如页码、奇偶页不同的页眉页脚。
理论上,无法从单个页面识别出页眉和页脚,所以需要从文档级别即多个Pages一起对比结构相似性,从而从页面开始部分分离出页眉、从页面结束部分分离出页脚。同时,需要处理一些不确定因素,例如页码、奇偶页不同的页眉页脚。

截至`v0.5.2`,这部分依然占坑待处理中。


## 页面大小与页边距

`PyMuPDF`提取的数据直接包括了页面宽度和高度,然后根据 **除去页眉页脚后** 的所有块级元素所占据的区域计算页边距,例如最小左上角点确定了左边距和上边距。
`PyMuPDF`提取的数据直接包括了页面宽度和高度,然后根据 **除去页眉页脚后** 的所有块级、形状元素所占据的区域计算页边距,例如最小左上角点确定了左边距和上边距。

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

Expand All @@ -59,7 +59,7 @@ tags: [python]
早期版本通过表格来实现分栏布局,从0.5.2版开始引入`Section``Column`,以保证页面逻辑的合理性,同时减少嵌套表格的泛滥。

!!! note "注意"
兼顾通用性和降低复杂度,目前仅支持单栏或者两栏的布局,更多的分栏将被解析为表格。
为兼顾通用性和降低复杂度,目前仅支持单栏或者两栏的布局,更多的分栏将被解析为表格。

### 分栏逻辑

Expand All @@ -82,10 +82,10 @@ tags: [python]

每一个`Section`在页面竖直方向的位置 **由前一个段落的段后间距** 确定,因此在确定好`Section`后,计算当前`Section`开始位置`y0`与前一个`Section`结束位置`y1`的差值,作为`Section`的一个属性`before_space`

使用`python-docx`重建`Section`时,设置前一个`Section`最后一个段落的段后间距等于当前`Section``before_space`即可。两个`Column`之间设置列分隔符`WD_SECTION.NEW_COLUMN`
使用`python-docx`重建`Section`时,设置前一个`Section`最后一个段落的段后距离等于当前`Section``before_space`即可。两个`Column`之间设置列分隔符`WD_SECTION.NEW_COLUMN`


!!! warning "页面第一个`Section`的处理"
!!! warning "每一页第一个`Section`的处理"
- 计算`before_space`时,前一个参考位置为页面上边距。
- 重建`Section`时,新建一个空段落作为设置段后间距的参考。

Expand All @@ -107,7 +107,7 @@ tags: [python]
└────────────────────────────────┘
```

解析出`TableBlock`后,继续根据位置关系将`Block``Shape`分配到相应的单元格中去。由此可知,容纳`Block``Shape`的单元格也是一个`Layout`对象,可以继续进行单元格内的嵌套表格和段落解析。这样的设计确保可以递归解析出无限嵌套的表格,同时也有助于将PDF的浮动布局转化为流动布局,便于在Word中重建。
PDF直接提取出的块元素只有`TextBlock`,结合形状元素(潜在的表格边框、单元格背景色)解析出`TableBlock`后,继续根据位置关系将表格范围内的`Block``Shape`分配到相应的单元格中去。由此可知,容纳`Block``Shape`的单元格也是一个`Layout`对象,可以继续进行单元格内的嵌套表格和段落解析。这样的设计确保可以递归解析出无限嵌套的表格,同时也有助于将PDF的浮动布局转化为流动布局,便于在Word中重建。

至于表格的具体解析方法,单开一篇进行具体介绍:

Expand Down

0 comments on commit 3b1d69b

Please sign in to comment.