pyxllib 0.3.197__py3-none-any.whl → 3.201.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pyxllib/__init__.py +14 -21
- pyxllib/algo/__init__.py +8 -8
- pyxllib/algo/disjoint.py +54 -54
- pyxllib/algo/geo.py +537 -541
- pyxllib/algo/intervals.py +964 -964
- pyxllib/algo/matcher.py +389 -389
- pyxllib/algo/newbie.py +166 -166
- pyxllib/algo/pupil.py +629 -629
- pyxllib/algo/shapelylib.py +67 -67
- pyxllib/algo/specialist.py +241 -241
- pyxllib/algo/stat.py +494 -494
- pyxllib/algo/treelib.py +145 -149
- pyxllib/algo/unitlib.py +62 -66
- pyxllib/autogui/__init__.py +5 -5
- pyxllib/autogui/activewin.py +246 -246
- pyxllib/autogui/all.py +9 -9
- pyxllib/autogui/autogui.py +846 -852
- pyxllib/autogui/uiautolib.py +362 -362
- pyxllib/autogui/virtualkey.py +102 -102
- pyxllib/autogui/wechat.py +827 -827
- pyxllib/autogui/wechat_msg.py +421 -421
- pyxllib/autogui/wxautolib.py +84 -84
- pyxllib/cv/__init__.py +5 -5
- pyxllib/cv/expert.py +267 -267
- pyxllib/cv/imfile.py +159 -159
- pyxllib/cv/imhash.py +39 -39
- pyxllib/cv/pupil.py +9 -9
- pyxllib/cv/rgbfmt.py +1525 -1525
- pyxllib/cv/slidercaptcha.py +137 -137
- pyxllib/cv/trackbartools.py +251 -251
- pyxllib/cv/xlcvlib.py +1040 -1040
- pyxllib/cv/xlpillib.py +423 -423
- pyxllib/data/echarts.py +236 -240
- pyxllib/data/jsonlib.py +85 -89
- pyxllib/data/oss.py +72 -72
- pyxllib/data/pglib.py +1111 -1127
- pyxllib/data/sqlite.py +568 -568
- pyxllib/data/sqllib.py +297 -297
- pyxllib/ext/JLineViewer.py +505 -505
- pyxllib/ext/__init__.py +6 -6
- pyxllib/ext/demolib.py +251 -246
- pyxllib/ext/drissionlib.py +277 -277
- pyxllib/ext/kq5034lib.py +12 -12
- pyxllib/ext/qt.py +449 -449
- pyxllib/ext/robustprocfile.py +493 -497
- pyxllib/ext/seleniumlib.py +76 -76
- pyxllib/ext/tk.py +173 -173
- pyxllib/ext/unixlib.py +821 -827
- pyxllib/ext/utools.py +345 -351
- pyxllib/ext/webhook.py +124 -119
- pyxllib/ext/win32lib.py +40 -40
- pyxllib/ext/wjxlib.py +91 -88
- pyxllib/ext/wpsapi.py +124 -124
- pyxllib/ext/xlwork.py +9 -9
- pyxllib/ext/yuquelib.py +1110 -1105
- pyxllib/file/__init__.py +17 -17
- pyxllib/file/docxlib.py +757 -761
- pyxllib/file/gitlib.py +309 -309
- pyxllib/file/libreoffice.py +165 -165
- pyxllib/file/movielib.py +144 -148
- pyxllib/file/newbie.py +10 -10
- pyxllib/file/onenotelib.py +1469 -1469
- pyxllib/file/packlib/__init__.py +330 -330
- pyxllib/file/packlib/zipfile.py +2441 -2441
- pyxllib/file/pdflib.py +422 -426
- pyxllib/file/pupil.py +185 -185
- pyxllib/file/specialist/__init__.py +681 -685
- pyxllib/file/specialist/dirlib.py +799 -799
- pyxllib/file/specialist/download.py +193 -193
- pyxllib/file/specialist/filelib.py +2825 -2829
- pyxllib/file/xlsxlib.py +3122 -3131
- pyxllib/file/xlsyncfile.py +341 -341
- pyxllib/prog/__init__.py +5 -5
- pyxllib/prog/cachetools.py +58 -64
- pyxllib/prog/deprecatedlib.py +233 -233
- pyxllib/prog/filelock.py +42 -42
- pyxllib/prog/ipyexec.py +253 -253
- pyxllib/prog/multiprogs.py +940 -940
- pyxllib/prog/newbie.py +451 -451
- pyxllib/prog/pupil.py +1208 -1197
- pyxllib/prog/sitepackages.py +33 -33
- pyxllib/prog/specialist/__init__.py +348 -391
- pyxllib/prog/specialist/bc.py +203 -203
- pyxllib/prog/specialist/browser.py +497 -497
- pyxllib/prog/specialist/common.py +347 -347
- pyxllib/prog/specialist/datetime.py +198 -198
- pyxllib/prog/specialist/tictoc.py +240 -240
- pyxllib/prog/specialist/xllog.py +180 -180
- pyxllib/prog/xlosenv.py +110 -108
- pyxllib/stdlib/__init__.py +17 -17
- pyxllib/stdlib/tablepyxl/__init__.py +10 -10
- pyxllib/stdlib/tablepyxl/style.py +303 -303
- pyxllib/stdlib/tablepyxl/tablepyxl.py +130 -130
- pyxllib/text/__init__.py +8 -8
- pyxllib/text/ahocorasick.py +36 -39
- pyxllib/text/airscript.js +754 -744
- pyxllib/text/charclasslib.py +121 -121
- pyxllib/text/jiebalib.py +267 -267
- pyxllib/text/jinjalib.py +27 -32
- pyxllib/text/jsa_ai_prompt.md +271 -271
- pyxllib/text/jscode.py +922 -922
- pyxllib/text/latex/__init__.py +158 -158
- pyxllib/text/levenshtein.py +303 -303
- pyxllib/text/nestenv.py +1215 -1215
- pyxllib/text/newbie.py +300 -300
- pyxllib/text/pupil/__init__.py +8 -8
- pyxllib/text/pupil/common.py +1121 -1121
- pyxllib/text/pupil/xlalign.py +326 -326
- pyxllib/text/pycode.py +47 -47
- pyxllib/text/specialist/__init__.py +8 -8
- pyxllib/text/specialist/common.py +112 -112
- pyxllib/text/specialist/ptag.py +186 -186
- pyxllib/text/spellchecker.py +172 -172
- pyxllib/text/templates/echart_base.html +10 -10
- pyxllib/text/templates/highlight_code.html +16 -16
- pyxllib/text/templates/latex_editor.html +102 -102
- pyxllib/text/vbacode.py +17 -17
- pyxllib/text/xmllib.py +741 -747
- pyxllib/xl.py +42 -39
- pyxllib/xlcv.py +17 -17
- pyxllib-3.201.1.dist-info/METADATA +296 -0
- pyxllib-3.201.1.dist-info/RECORD +125 -0
- {pyxllib-0.3.197.dist-info → pyxllib-3.201.1.dist-info}/licenses/LICENSE +190 -190
- pyxllib/ext/old.py +0 -663
- pyxllib-0.3.197.dist-info/METADATA +0 -48
- pyxllib-0.3.197.dist-info/RECORD +0 -126
- {pyxllib-0.3.197.dist-info → pyxllib-3.201.1.dist-info}/WHEEL +0 -0
pyxllib/file/pdflib.py
CHANGED
@@ -1,426 +1,422 @@
|
|
1
|
-
#!/usr/bin/env python3
|
2
|
-
# -*- coding: utf-8 -*-
|
3
|
-
# @Author : 陈坤泽
|
4
|
-
# @Email : 877362867@qq.com
|
5
|
-
# @Date : 2020/06/02 16:06
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
import
|
12
|
-
|
13
|
-
import
|
14
|
-
|
15
|
-
|
16
|
-
import
|
17
|
-
|
18
|
-
from pyxllib.
|
19
|
-
from pyxllib.prog.pupil import
|
20
|
-
from pyxllib.
|
21
|
-
from pyxllib.
|
22
|
-
from
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
:param
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
:param
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
:
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
#
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
return
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
return
|
148
|
-
|
149
|
-
def
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
:param
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
msgdict['
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
if
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
if
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
331
|
-
|
332
|
-
|
333
|
-
self.doc.
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
pngdata
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
#
|
360
|
-
|
361
|
-
|
362
|
-
|
363
|
-
|
364
|
-
|
365
|
-
|
366
|
-
|
367
|
-
|
368
|
-
def
|
369
|
-
"""
|
370
|
-
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
n
|
376
|
-
|
377
|
-
|
378
|
-
|
379
|
-
|
380
|
-
|
381
|
-
|
382
|
-
|
383
|
-
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
419
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
# 打开浏览器查看重建的html效果
|
424
|
-
f = pdf_file.with_suffix('.html')
|
425
|
-
f.write(output_string.getvalue())
|
426
|
-
browser(f)
|
1
|
+
#!/usr/bin/env python3
|
2
|
+
# -*- coding: utf-8 -*-
|
3
|
+
# @Author : 陈坤泽
|
4
|
+
# @Email : 877362867@qq.com
|
5
|
+
# @Date : 2020/06/02 16:06
|
6
|
+
|
7
|
+
|
8
|
+
import json
|
9
|
+
import os
|
10
|
+
import pprint
|
11
|
+
import re
|
12
|
+
|
13
|
+
import fitz
|
14
|
+
|
15
|
+
from pyxllib.prog.newbie import round_int, decode_bitflags
|
16
|
+
from pyxllib.prog.pupil import DictTool, inject_members, dprint
|
17
|
+
from pyxllib.prog.specialist import browser
|
18
|
+
from pyxllib.algo.newbie import round_unit
|
19
|
+
from pyxllib.prog.pupil import get_number_width
|
20
|
+
from pyxllib.file.specialist import XlPath, writefile, get_etag
|
21
|
+
from pyxllib.cv.expert import xlcv, xlpil
|
22
|
+
from pyxlpr.data.labelme import LabelmeDict
|
23
|
+
|
24
|
+
|
25
|
+
def __fitz():
|
26
|
+
print(fitz.__doc__)
|
27
|
+
|
28
|
+
|
29
|
+
class FitzDoc:
|
30
|
+
""" 原名叫FitzPdf,但不一定是处理pdf,也可能是其他文档,所以改名 FitzDoc
|
31
|
+
"""
|
32
|
+
|
33
|
+
def __init__(self, file):
|
34
|
+
self.src_file = XlPath(file)
|
35
|
+
self.doc = fitz.open(str(file))
|
36
|
+
|
37
|
+
def to_images(self, dst_dir=None, file_fmt='{filestem}_{number}.jpg', num_width=None, *,
|
38
|
+
scale=1, start=1, fmt_onepage=False):
|
39
|
+
""" 将pdf转为若干页图片
|
40
|
+
|
41
|
+
:param dst_dir: 目标目录
|
42
|
+
默认情况下,只有一页pdf则存储到对应的pdf目录,多页则存储到同名子目录下
|
43
|
+
如果不想这样被智能控制,只要指定明确的dst即可
|
44
|
+
:param file_fmt: 后缀格式,包括修改导出的图片类型,注意要用 {} 占位符表示页码编号
|
45
|
+
:param num_width: 生成的每一页文件编号,使用的数字前导0域宽
|
46
|
+
默认根据pdf总页数来设置对应所用域宽
|
47
|
+
0表示不设域宽
|
48
|
+
:param scale: 对每页图片进行缩放,一般推荐都要设成2,导出的图片才清晰
|
49
|
+
:param start: 起始页码,一般建议从1开始比较符合常识直觉
|
50
|
+
:param fmt_onepage: 当pdf就只有一页的时候,是否还对导出的图片编号
|
51
|
+
默认只有一页的时候,进行优化,不增设后缀格式
|
52
|
+
:return: 返回转换完的图片名称清单
|
53
|
+
|
54
|
+
注:如果要导出单张图,可以用 FitzPdfPage.get_cv_image
|
55
|
+
"""
|
56
|
+
# 1 基本参数计算
|
57
|
+
srcfile, doc = self.src_file, self.doc
|
58
|
+
filestem, n_page = srcfile.stem, doc.page_count
|
59
|
+
|
60
|
+
# 自动推导目标目录
|
61
|
+
if dst_dir is None:
|
62
|
+
dst_dir = XlPath.init(srcfile.stem, srcfile.parent) if n_page > 1 else XlPath(srcfile.parent)
|
63
|
+
os.makedirs(dst_dir, exist_ok=True)
|
64
|
+
|
65
|
+
# 域宽
|
66
|
+
num_width = num_width or get_number_width(n_page) # 根据总页数计算需要的对齐域宽
|
67
|
+
|
68
|
+
# 2 导出图片
|
69
|
+
if fmt_onepage or n_page != 1: # 多页的处理规则
|
70
|
+
res = []
|
71
|
+
for i in range(n_page):
|
72
|
+
im = self.load_page(i).get_cv_image(scale)
|
73
|
+
number = ('{:0' + str(num_width) + 'd}').format(i + start) # 前面的括号不要删,这样才是完整的一个字符串来使用format
|
74
|
+
f = xlcv.write(im, XlPath.init(file_fmt.format(filestem=filestem, number=number), dst_dir))
|
75
|
+
res.append(f)
|
76
|
+
return res
|
77
|
+
else:
|
78
|
+
im = self.load_page(0).get_cv_image(scale)
|
79
|
+
return [xlcv.write(im, XlPath.init(srcfile.stem + os.path.splitext(file_fmt)[1], dst_dir))]
|
80
|
+
|
81
|
+
def to_labelmes(self, imfiles, opt='dict', *, views=(0, 0, 1, 0), scale=1, indent=None):
|
82
|
+
""" 生成图片对应的标注,常跟to_images配合使用 """
|
83
|
+
for i, imfile in enumerate(imfiles):
|
84
|
+
page = self.load_page(i)
|
85
|
+
lmdict = LabelmeDict.gen_data(imfile)
|
86
|
+
lmdict['shapes'] = page.get_labelme_shapes(opt, views=views, scale=scale)
|
87
|
+
imfile.with_suffix('.json').write(lmdict, indent=indent)
|
88
|
+
|
89
|
+
def to_docx(self, docx_file=None):
|
90
|
+
""" pdf转docx """
|
91
|
+
from pdf2docx import parse
|
92
|
+
|
93
|
+
pdf_file = self.src_file
|
94
|
+
|
95
|
+
if docx_file is None:
|
96
|
+
docx_file = pdf_file.with_suffix('.docx')
|
97
|
+
|
98
|
+
# 注意这里是日志显示进度,不是printf输出.
|
99
|
+
parse(str(pdf_file), str(docx_file))
|
100
|
+
|
101
|
+
def browser(self, opt='pdf'):
|
102
|
+
if opt == 'pdf':
|
103
|
+
f = self.src_file
|
104
|
+
browser(self.src_file)
|
105
|
+
elif opt == 'html':
|
106
|
+
ls = []
|
107
|
+
for i in range(self.page_count):
|
108
|
+
page = self.load_page(i)
|
109
|
+
ls.append(page.get_text('html'))
|
110
|
+
data = '\n'.join(ls)
|
111
|
+
etag = get_etag(data)
|
112
|
+
f = XlPath.init(etag, XlPath.tempdir(), suffix='.html')
|
113
|
+
f.write(data)
|
114
|
+
browser(f)
|
115
|
+
else:
|
116
|
+
raise ValueError(f'{opt}')
|
117
|
+
return f
|
118
|
+
|
119
|
+
def __getattr__(self, item):
|
120
|
+
return getattr(self.doc, item)
|
121
|
+
|
122
|
+
|
123
|
+
class XlFitzPage(fitz.fitz.Page):
|
124
|
+
""" 对fitz.fitz.Page的扩展成员方法 """
|
125
|
+
|
126
|
+
def get_svg_image2(self, scale=1):
|
127
|
+
# svg 是一段表述性文本
|
128
|
+
if scale != 1:
|
129
|
+
txt = self.get_svg_image(matrix=fitz.Matrix(scale, scale))
|
130
|
+
else:
|
131
|
+
txt = self.get_svg_image()
|
132
|
+
return txt
|
133
|
+
|
134
|
+
def _get_png_data(self, scale=1):
|
135
|
+
# TODO 增加透明通道?
|
136
|
+
if scale != 1:
|
137
|
+
pix = self.get_pixmap(matrix=fitz.Matrix(scale, scale)) # 长宽放大到scale倍
|
138
|
+
else:
|
139
|
+
pix = self.get_pixmap()
|
140
|
+
return pix.tobytes()
|
141
|
+
|
142
|
+
def get_cv_image(self, scale=1):
|
143
|
+
return xlcv.read_from_buffer(self._get_png_data(scale), flags=1)
|
144
|
+
|
145
|
+
def get_pil_image(self, scale=1):
|
146
|
+
# TODO 可以优化,直接从内存数据转pil,不用这样先转cv再转pil
|
147
|
+
return xlpil.read_from_buffer(self._get_png_data(scale), flags=1)
|
148
|
+
|
149
|
+
def to_image(self, outfile, *, scale=1, if_exists=None):
|
150
|
+
""" 转成为文件 """
|
151
|
+
f = XlPath(outfile)
|
152
|
+
suffix = f.suffix.lower()
|
153
|
+
|
154
|
+
if suffix == '.svg':
|
155
|
+
content = self.get_svg_image()
|
156
|
+
f.write(content, if_exists=if_exists)
|
157
|
+
else:
|
158
|
+
im = self.get_cv_image(scale)
|
159
|
+
xlcv.write(im, if_exists=if_exists)
|
160
|
+
|
161
|
+
def get_labelme_shapes(self, opt='dict', *, views=1, scale=1):
|
162
|
+
""" 得到labelme版本的shapes标注信息
|
163
|
+
|
164
|
+
:param opt: get_text的参数,默认使用无字符集标注的精简的dict
|
165
|
+
也可以使用rawdict,带有字符集标注的数据
|
166
|
+
:param views: 若非list或者长度不足4,会补足
|
167
|
+
各位标记依次代表是否显示对应细粒度的标注:blocks、lines、spans、chars
|
168
|
+
默认只显示blocks
|
169
|
+
例如 (0, 0, 1, 0),表示只显示spans的标注
|
170
|
+
:param scale: 是否需要对坐标按比例放大 (pdf经常放大两倍提取图片,则这里标注也要对应放大两倍)
|
171
|
+
|
172
|
+
【字典属性解释】
|
173
|
+
blocks:
|
174
|
+
number: int, 区块编号
|
175
|
+
type: 0表示文本行,1表示图片
|
176
|
+
lines:
|
177
|
+
wmode: 好像都是0,不知道啥东西
|
178
|
+
dir: [1, 0],可能是文本方向吧
|
179
|
+
spans:
|
180
|
+
size: 字号
|
181
|
+
flags: 格式标记
|
182
|
+
1,superscript,上标
|
183
|
+
2,italic,斜体
|
184
|
+
4,serifed,有衬线。如果没开,对立面就是"sans",无衬线。
|
185
|
+
8,monospaced,等距。对立面proportional,均衡。
|
186
|
+
16,bold,加粗
|
187
|
+
font:字体名称(直接用字符串赋值)
|
188
|
+
color:颜色
|
189
|
+
ascender:?
|
190
|
+
descender:?
|
191
|
+
origin:所在方格右上角坐标
|
192
|
+
text/chars: dict模式有text内容,rawdict有chars详细信息。我扩展的版本,rawdict也会有text属性。
|
193
|
+
char:
|
194
|
+
origin: 差不多是其所在方格的右上角坐标,同一行文本,其top位置是会对齐的
|
195
|
+
c: 字符内容
|
196
|
+
"""
|
197
|
+
from pyxlpr.data.labelme import LabelmeDict
|
198
|
+
|
199
|
+
# 1 参数配置
|
200
|
+
if isinstance(views, int):
|
201
|
+
views = [views]
|
202
|
+
if len(views) < 4:
|
203
|
+
views += [0] * (4 - len(views))
|
204
|
+
|
205
|
+
shapes = []
|
206
|
+
page_dict = self.get_text(opt)
|
207
|
+
|
208
|
+
# 2 辅助函数
|
209
|
+
def add_shape(name, refdict, add_keys, drop_keys=('bbox',)):
|
210
|
+
""" 生成一个标注框 """
|
211
|
+
msgdict = {'category_name': name}
|
212
|
+
msgdict.update(add_keys)
|
213
|
+
DictTool.ior(msgdict, refdict)
|
214
|
+
DictTool.isub(msgdict, drop_keys)
|
215
|
+
bbox = [round_int(v * scale) for v in refdict['bbox']]
|
216
|
+
|
217
|
+
if 'size' in msgdict:
|
218
|
+
x = round_unit(msgdict['size'], 0.5)
|
219
|
+
msgdict['size'] = round_int(x) if (x * 10) % 10 < 1 else x # 没有小数的时候,优先展示为11,而不是11.0
|
220
|
+
if 'color' in msgdict:
|
221
|
+
# 把color映射为直观的(r, g, b)
|
222
|
+
# 这个pdf解析器获取的color,不一定精确等于原值,可能会有偏差,小一个像素
|
223
|
+
v = msgdict['color']
|
224
|
+
msgdict['color'] = (v // 256 // 256, (v // 256) % 256, v % 256)
|
225
|
+
if 'origin' in msgdict:
|
226
|
+
msgdict['origin'] = [round_int(v) for v in msgdict['origin']]
|
227
|
+
|
228
|
+
sp = LabelmeDict.gen_shape(json.dumps(msgdict), bbox)
|
229
|
+
shapes.append(sp)
|
230
|
+
|
231
|
+
# 3 遍历获取标注数据
|
232
|
+
for block in page_dict['blocks']:
|
233
|
+
if block['type'] == 0: # 普通的文本行
|
234
|
+
if views[0]:
|
235
|
+
add_shape('text_block', block, {'n_lines': len(block['lines'])}, ['bbox', 'lines'])
|
236
|
+
for line in block['lines']:
|
237
|
+
if views[1]:
|
238
|
+
add_shape('line', line, {'n_spans': len(line['spans'])}, ['bbox', 'spans'])
|
239
|
+
for span in line['spans']:
|
240
|
+
if 'text' not in span and 'chars' in span:
|
241
|
+
span['text'] = ''.join([x['c'] for x in span['chars']])
|
242
|
+
if views[2]:
|
243
|
+
add_shape('span', span, {'n_chars': len(span.get('text', ''))}, ['bbox', 'chars'])
|
244
|
+
if views[3] and 'chars' in span: # 最后层算法不太一样,这样写可以加速
|
245
|
+
for char in span['chars']:
|
246
|
+
add_shape('char', char, {}, ['bbox'])
|
247
|
+
elif block['type'] == 1: # 应该是图片
|
248
|
+
add_shape('image', block, {'image_filesize': len(block['image'])}, ['bbox', 'image'])
|
249
|
+
else:
|
250
|
+
raise ValueError
|
251
|
+
|
252
|
+
return shapes
|
253
|
+
|
254
|
+
@classmethod
|
255
|
+
def parse_flags(cls, n):
|
256
|
+
""" 解析spans的flags参数明文含义 """
|
257
|
+
flags = decode_bitflags(n, ('superscript', 'italic', 'serifed', 'monospaced', 'bold'))
|
258
|
+
flags['sans'] = not flags['serifed']
|
259
|
+
flags['proportional'] = not flags['monospaced']
|
260
|
+
return flags
|
261
|
+
|
262
|
+
def browser(self, opt='html'):
|
263
|
+
if opt == 'html':
|
264
|
+
data = self.get_text('html') # html、xhtml 可以转网页,虽然排版相对来说还是会乱一点
|
265
|
+
data = ''.join(data)
|
266
|
+
etag = get_etag(data)
|
267
|
+
f = XlPath.init(etag, XlPath.tempdir(), suffix='.html')
|
268
|
+
f.write(data)
|
269
|
+
browser(f)
|
270
|
+
else:
|
271
|
+
raise ValueError
|
272
|
+
|
273
|
+
|
274
|
+
inject_members(XlFitzPage, fitz.fitz.Page)
|
275
|
+
|
276
|
+
|
277
|
+
class DemoFitz:
|
278
|
+
"""
|
279
|
+
安装: pip install PyMuPdf
|
280
|
+
使用: import fitz
|
281
|
+
官方文档: https://pymupdf.readthedocs.io/en/latest/intro/
|
282
|
+
demo: https://github.com/rk700/PyMuPDF/tree/master/demo
|
283
|
+
examples: https://github.com/rk700/PyMuPDF/tree/master/examples
|
284
|
+
"""
|
285
|
+
|
286
|
+
def __init__(self, file):
|
287
|
+
self.doc = fitz.open(file)
|
288
|
+
|
289
|
+
def message(self):
|
290
|
+
"""查看pdf文档一些基础信息"""
|
291
|
+
dprint(fitz.version) # fitz模块的版本
|
292
|
+
dprint(self.doc.pageCount) # pdf页数
|
293
|
+
dprint(self.doc._getXrefLength()) # 文档的对象总数
|
294
|
+
|
295
|
+
def getToC(self):
|
296
|
+
"""获得书签目录"""
|
297
|
+
toc = self.doc.getToC()
|
298
|
+
browser(toc)
|
299
|
+
|
300
|
+
def setToC(self):
|
301
|
+
"""设置书签目录
|
302
|
+
可以调层级、改名称、修改指向页码
|
303
|
+
"""
|
304
|
+
toc = self.doc.getToC()
|
305
|
+
toc[1][1] = '改标题名称'
|
306
|
+
self.doc.setToC(toc)
|
307
|
+
file = XlPath('a.pdf', XlPath.tempdir()).to_str()
|
308
|
+
self.doc.save(file, garbage=4)
|
309
|
+
browser(file)
|
310
|
+
|
311
|
+
def setToC2(self):
|
312
|
+
"""修改人教版教材的标签名"""
|
313
|
+
toc = self.doc.getToC()
|
314
|
+
newtoc = []
|
315
|
+
for i in range(len(toc)):
|
316
|
+
name = toc[i][1]
|
317
|
+
if '.' in name: continue
|
318
|
+
# m = re.search(r'\d+', name)
|
319
|
+
# if m: name = name.replace(m.group(), digits2chinese(int(m.group())))
|
320
|
+
m = re.search(r'([一二三四五六]年级).*?([上下])', name)
|
321
|
+
if i < len(toc) - 1:
|
322
|
+
pages = toc[i + 1][2] - toc[i][2] + 1
|
323
|
+
else:
|
324
|
+
pages = self.doc.pageCount - toc[i][2] + 1
|
325
|
+
toc[i][1] = m.group(1) + m.group(2) + ',' + str(pages)
|
326
|
+
newtoc.append(toc[i])
|
327
|
+
self.doc.setToC(newtoc)
|
328
|
+
file = writefile(b'', 'a.pdf', if_exists='replace')
|
329
|
+
self.doc.save(file, garbage=4)
|
330
|
+
|
331
|
+
def rearrange_pages(self):
|
332
|
+
"""重新布局页面"""
|
333
|
+
self.doc.select([0, 0, 1]) # 第1页展示两次后,再跟第2页
|
334
|
+
file = writefile(b'', 'a.pdf', root=XlPath.tempdir(), if_exists='replace')
|
335
|
+
self.doc.save(file, garbage=4) # 注意要设置garbage,否则文档并没有实际删除内容压缩文件大小
|
336
|
+
browser(file)
|
337
|
+
|
338
|
+
def page2png(self, page=0):
|
339
|
+
""" 查看单页渲染图片 """
|
340
|
+
page = self.doc.loadPage(page) # 索引第i页,下标规律同py,支持-1索引最后页
|
341
|
+
# dprint(page.bound()) # 页面边界,x,y轴同图像处理中的常识定义,返回Rect(x0, y0, x1, y1)
|
342
|
+
|
343
|
+
pix = page.getPixmap(fitz.Matrix(2, 2)) # 获得页面的RGBA图像,Pixmap类型;还可以用page.getSVGimage()获得矢量图
|
344
|
+
# pix.writePNG('page-0.png') # 将Pixmal
|
345
|
+
pngdata = pix.tobytes() # 获png文件的bytes字节码
|
346
|
+
# print(len(pngdata))
|
347
|
+
# browser(pngdata, 'a.png') # 用我的工具函数打开图片
|
348
|
+
|
349
|
+
return pngdata
|
350
|
+
|
351
|
+
def pagetext(self):
|
352
|
+
"""单页上的文本"""
|
353
|
+
page = self.doc[0]
|
354
|
+
|
355
|
+
# 获得页面上的所有文本,还支持参数: html,dict,xml,xhtml,json
|
356
|
+
text = page.getText('text')
|
357
|
+
dprint(text)
|
358
|
+
|
359
|
+
# 获得页面上的所有文本(返回字典对象)
|
360
|
+
textdict = page.getText('dict')
|
361
|
+
textdict['blocks'] = textdict['blocks'][:-1]
|
362
|
+
browser(pprint.pformat(textdict))
|
363
|
+
|
364
|
+
def text(self):
|
365
|
+
"""获得整份pdf的所有文本"""
|
366
|
+
return '\n'.join([page.getText('text') for page in self.doc])
|
367
|
+
|
368
|
+
def xrefstr(self):
|
369
|
+
"""查看pdf文档的所有对象"""
|
370
|
+
xrefstr = []
|
371
|
+
n = self.doc._getXrefLength()
|
372
|
+
for i in range(1, n): # 注意下标实际要从1卡开始
|
373
|
+
# 可以边遍历边删除,不影响下标位置,因为其本质只是去除关联引用而已
|
374
|
+
xrefstr.append(self.doc._getXrefString(i))
|
375
|
+
browser('\n'.join(xrefstr))
|
376
|
+
|
377
|
+
def page_add_ele(self):
|
378
|
+
"""往页面添加元素
|
379
|
+
添加元素前后xrefstr的区别: https://paste.ubuntu.com/p/Dxhnzp4XJ2/
|
380
|
+
"""
|
381
|
+
self.doc.select([0])
|
382
|
+
page = self.doc.loadPage(0)
|
383
|
+
# page.insertText(fitz.Point(100, 200), 'test\ntest')
|
384
|
+
file = str(XlPath.tempdir() / 'a.pdf')
|
385
|
+
dprint(file)
|
386
|
+
self.doc.save(file, garbage=4)
|
387
|
+
browser(file)
|
388
|
+
|
389
|
+
|
390
|
+
def __pdfminer():
|
391
|
+
""" pdfminer的实验代码也先放这里
|
392
|
+
|
393
|
+
!pip install pdfminer.six
|
394
|
+
"""
|
395
|
+
|
396
|
+
import pdfminer
|
397
|
+
print(pdfminer.__version__)
|
398
|
+
# 20201018
|
399
|
+
|
400
|
+
|
401
|
+
class PdfMiner:
|
402
|
+
@classmethod
|
403
|
+
def to_html(cls, pdf_file):
|
404
|
+
""" 相比fitz,pdfminer能正常提取出下划线
|
405
|
+
|
406
|
+
文本重叠比fitz更严重,整体来说其实更不好用~~
|
407
|
+
"""
|
408
|
+
|
409
|
+
from io import StringIO
|
410
|
+
|
411
|
+
from pdfminer.high_level import extract_text_to_fp
|
412
|
+
from pdfminer.layout import LAParams
|
413
|
+
|
414
|
+
output_string = StringIO()
|
415
|
+
with open(str(pdf_file)) as fin:
|
416
|
+
extract_text_to_fp(fin, output_string, laparams=LAParams(),
|
417
|
+
output_type='html', codec=None)
|
418
|
+
|
419
|
+
# 打开浏览器查看重建的html效果
|
420
|
+
f = pdf_file.with_suffix('.html')
|
421
|
+
f.write(output_string.getvalue())
|
422
|
+
browser(f)
|