BabelDOC 0.1.6__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.
- babeldoc/__init__.py +1 -0
- babeldoc/asynchronize/__init__.py +51 -0
- babeldoc/const.py +7 -0
- babeldoc/converter.py +510 -0
- babeldoc/document_il/__init__.py +43 -0
- babeldoc/document_il/backend/__init__.py +0 -0
- babeldoc/document_il/backend/pdf_creater.py +402 -0
- babeldoc/document_il/frontend/__init__.py +0 -0
- babeldoc/document_il/frontend/il_creater.py +348 -0
- babeldoc/document_il/il_version_1.py +799 -0
- babeldoc/document_il/il_version_1.rnc +141 -0
- babeldoc/document_il/il_version_1.rng +390 -0
- babeldoc/document_il/il_version_1.xsd +235 -0
- babeldoc/document_il/midend/__init__.py +0 -0
- babeldoc/document_il/midend/add_debug_information.py +138 -0
- babeldoc/document_il/midend/il_translator.py +553 -0
- babeldoc/document_il/midend/layout_parser.py +180 -0
- babeldoc/document_il/midend/paragraph_finder.py +452 -0
- babeldoc/document_il/midend/remove_descent.py +168 -0
- babeldoc/document_il/midend/styles_and_formulas.py +701 -0
- babeldoc/document_il/midend/typesetting.py +761 -0
- babeldoc/document_il/translator/__init__.py +0 -0
- babeldoc/document_il/translator/cache.py +145 -0
- babeldoc/document_il/translator/translator.py +267 -0
- babeldoc/document_il/utils/fontmap.py +227 -0
- babeldoc/document_il/utils/layout_helper.py +467 -0
- babeldoc/document_il/utils/style_helper.py +86 -0
- babeldoc/document_il/xml_converter.py +51 -0
- babeldoc/docvision/README.md +0 -0
- babeldoc/docvision/__init__.py +0 -0
- babeldoc/docvision/doclayout.py +278 -0
- babeldoc/docvision/rpc_doclayout.py +162 -0
- babeldoc/high_level.py +468 -0
- babeldoc/main.py +466 -0
- babeldoc/pdfinterp.py +464 -0
- babeldoc/progress_monitor.py +146 -0
- babeldoc/translation_config.py +156 -0
- babeldoc-0.1.6.dist-info/METADATA +283 -0
- babeldoc-0.1.6.dist-info/RECORD +42 -0
- babeldoc-0.1.6.dist-info/WHEEL +4 -0
- babeldoc-0.1.6.dist-info/entry_points.txt +2 -0
- babeldoc-0.1.6.dist-info/licenses/LICENSE +661 -0
|
@@ -0,0 +1,553 @@
|
|
|
1
|
+
import concurrent.futures
|
|
2
|
+
import json
|
|
3
|
+
import logging
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from tqdm import tqdm
|
|
7
|
+
|
|
8
|
+
from babeldoc.document_il import Document
|
|
9
|
+
from babeldoc.document_il import Page
|
|
10
|
+
from babeldoc.document_il import PdfFont
|
|
11
|
+
from babeldoc.document_il import PdfFormula
|
|
12
|
+
from babeldoc.document_il import PdfParagraph
|
|
13
|
+
from babeldoc.document_il import PdfParagraphComposition
|
|
14
|
+
from babeldoc.document_il import PdfSameStyleCharacters
|
|
15
|
+
from babeldoc.document_il import PdfSameStyleUnicodeCharacters
|
|
16
|
+
from babeldoc.document_il import PdfStyle
|
|
17
|
+
from babeldoc.document_il.translator.translator import BaseTranslator
|
|
18
|
+
from babeldoc.document_il.utils.fontmap import FontMapper
|
|
19
|
+
from babeldoc.document_il.utils.layout_helper import get_char_unicode_string
|
|
20
|
+
from babeldoc.document_il.utils.layout_helper import is_same_style
|
|
21
|
+
from babeldoc.document_il.utils.layout_helper import is_same_style_except_font
|
|
22
|
+
from babeldoc.document_il.utils.layout_helper import is_same_style_except_size
|
|
23
|
+
from babeldoc.translation_config import TranslationConfig
|
|
24
|
+
|
|
25
|
+
logger = logging.getLogger(__name__)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class RichTextPlaceholder:
|
|
29
|
+
def __init__(
|
|
30
|
+
self,
|
|
31
|
+
placeholder_id: int,
|
|
32
|
+
composition: PdfSameStyleCharacters,
|
|
33
|
+
left_placeholder: str,
|
|
34
|
+
right_placeholder: str,
|
|
35
|
+
):
|
|
36
|
+
self.id = placeholder_id
|
|
37
|
+
self.composition = composition
|
|
38
|
+
self.left_placeholder = left_placeholder
|
|
39
|
+
self.right_placeholder = right_placeholder
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class FormulaPlaceholder:
|
|
43
|
+
def __init__(self, placeholder_id: int, formula: PdfFormula, placeholder: str):
|
|
44
|
+
self.id = placeholder_id
|
|
45
|
+
self.formula = formula
|
|
46
|
+
self.placeholder = placeholder
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class PbarContext:
|
|
50
|
+
def __init__(self, pbar):
|
|
51
|
+
self.pbar = pbar
|
|
52
|
+
|
|
53
|
+
def __enter__(self):
|
|
54
|
+
return self.pbar
|
|
55
|
+
|
|
56
|
+
def __exit__(self, exc_type, exc_value, traceback):
|
|
57
|
+
self.pbar.advance()
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class DocumentTranslateTracker:
|
|
61
|
+
def __init__(self):
|
|
62
|
+
self.page = []
|
|
63
|
+
|
|
64
|
+
def new_page(self):
|
|
65
|
+
page = PageTranslateTracker()
|
|
66
|
+
self.page.append(page)
|
|
67
|
+
return page
|
|
68
|
+
|
|
69
|
+
def to_json(self):
|
|
70
|
+
pages = []
|
|
71
|
+
for page in self.page:
|
|
72
|
+
paragraphs = []
|
|
73
|
+
for para in page.paragraph:
|
|
74
|
+
i_str = getattr(para, "input", None)
|
|
75
|
+
o_str = getattr(para, "output", None)
|
|
76
|
+
pdf_unicode = getattr(para, "pdf_unicode", None)
|
|
77
|
+
if pdf_unicode is None or i_str is None:
|
|
78
|
+
continue
|
|
79
|
+
paragraphs.append(
|
|
80
|
+
{
|
|
81
|
+
"input": i_str,
|
|
82
|
+
"output": o_str,
|
|
83
|
+
"pdf_unicode": pdf_unicode,
|
|
84
|
+
},
|
|
85
|
+
)
|
|
86
|
+
pages.append({"paragraph": paragraphs})
|
|
87
|
+
return json.dumps({"page": pages}, ensure_ascii=False, indent=2)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class PageTranslateTracker:
|
|
91
|
+
def __init__(self):
|
|
92
|
+
self.paragraph = []
|
|
93
|
+
|
|
94
|
+
def new_paragraph(self):
|
|
95
|
+
paragraph = ParagraphTranslateTracker()
|
|
96
|
+
self.paragraph.append(paragraph)
|
|
97
|
+
return paragraph
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class ParagraphTranslateTracker:
|
|
101
|
+
def __init__(self):
|
|
102
|
+
pass
|
|
103
|
+
|
|
104
|
+
def set_pdf_unicode(self, unicode: str):
|
|
105
|
+
self.pdf_unicode = unicode
|
|
106
|
+
|
|
107
|
+
def set_input(self, input_text: str):
|
|
108
|
+
self.input = input_text
|
|
109
|
+
|
|
110
|
+
def set_output(self, output: str):
|
|
111
|
+
self.output = output
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
class ILTranslator:
|
|
115
|
+
stage_name = "翻译段落"
|
|
116
|
+
|
|
117
|
+
def __init__(
|
|
118
|
+
self,
|
|
119
|
+
translate_engine: BaseTranslator,
|
|
120
|
+
translation_config: TranslationConfig,
|
|
121
|
+
):
|
|
122
|
+
self.translate_engine = translate_engine
|
|
123
|
+
self.translation_config = translation_config
|
|
124
|
+
self.font_mapper = FontMapper(translation_config)
|
|
125
|
+
|
|
126
|
+
def translate(self, docs: Document):
|
|
127
|
+
tracker = DocumentTranslateTracker()
|
|
128
|
+
# count total paragraph
|
|
129
|
+
total = sum(len(page.pdf_paragraph) for page in docs.page)
|
|
130
|
+
with self.translation_config.progress_monitor.stage_start(
|
|
131
|
+
self.stage_name,
|
|
132
|
+
total,
|
|
133
|
+
) as pbar:
|
|
134
|
+
with concurrent.futures.ThreadPoolExecutor(
|
|
135
|
+
max_workers=min(
|
|
136
|
+
self.translation_config.qps * 2,
|
|
137
|
+
self.translation_config.qps + 5,
|
|
138
|
+
),
|
|
139
|
+
) as executor:
|
|
140
|
+
for page in docs.page:
|
|
141
|
+
self.process_page(page, executor, pbar, tracker.new_page())
|
|
142
|
+
|
|
143
|
+
path = self.translation_config.get_working_file_path("translate_tracking.json")
|
|
144
|
+
|
|
145
|
+
if self.translation_config.debug:
|
|
146
|
+
logger.debug(f"save translate tracking to {path}")
|
|
147
|
+
with Path(path).open("w", encoding="utf-8") as f:
|
|
148
|
+
f.write(tracker.to_json())
|
|
149
|
+
|
|
150
|
+
def process_page(
|
|
151
|
+
self,
|
|
152
|
+
page: Page,
|
|
153
|
+
executor: concurrent.futures.ThreadPoolExecutor,
|
|
154
|
+
pbar: tqdm | None = None,
|
|
155
|
+
tracker: PageTranslateTracker = None,
|
|
156
|
+
):
|
|
157
|
+
self.translation_config.raise_if_cancelled()
|
|
158
|
+
for paragraph in page.pdf_paragraph:
|
|
159
|
+
page_font_map = {}
|
|
160
|
+
for font in page.pdf_font:
|
|
161
|
+
page_font_map[font.font_id] = font
|
|
162
|
+
page_xobj_font_map = {}
|
|
163
|
+
for xobj in page.pdf_xobject:
|
|
164
|
+
page_xobj_font_map[xobj.xobj_id] = page_font_map.copy()
|
|
165
|
+
for font in xobj.pdf_font:
|
|
166
|
+
page_xobj_font_map[xobj.xobj_id][font.font_id] = font
|
|
167
|
+
# self.translate_paragraph(paragraph, pbar,tracker.new_paragraph(), page_font_map, page_xobj_font_map)
|
|
168
|
+
executor.submit(
|
|
169
|
+
self.translate_paragraph,
|
|
170
|
+
paragraph,
|
|
171
|
+
pbar,
|
|
172
|
+
tracker.new_paragraph(),
|
|
173
|
+
page_font_map,
|
|
174
|
+
page_xobj_font_map,
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
class TranslateInput:
|
|
178
|
+
def __init__(
|
|
179
|
+
self,
|
|
180
|
+
unicode: str,
|
|
181
|
+
placeholders: [RichTextPlaceholder | FormulaPlaceholder],
|
|
182
|
+
base_style: PdfStyle = None,
|
|
183
|
+
):
|
|
184
|
+
self.unicode = unicode
|
|
185
|
+
self.placeholders = placeholders
|
|
186
|
+
self.base_style = base_style
|
|
187
|
+
|
|
188
|
+
def create_formula_placeholder(
|
|
189
|
+
self,
|
|
190
|
+
formula: PdfFormula,
|
|
191
|
+
formula_id: int,
|
|
192
|
+
paragraph: PdfParagraph,
|
|
193
|
+
):
|
|
194
|
+
placeholder = self.translate_engine.get_formular_placeholder(formula_id)
|
|
195
|
+
if placeholder in paragraph.unicode:
|
|
196
|
+
return self.create_formula_placeholder(formula, formula_id + 1, paragraph)
|
|
197
|
+
|
|
198
|
+
return FormulaPlaceholder(formula_id, formula, placeholder)
|
|
199
|
+
|
|
200
|
+
def create_rich_text_placeholder(
|
|
201
|
+
self,
|
|
202
|
+
composition: PdfSameStyleCharacters,
|
|
203
|
+
composition_id: int,
|
|
204
|
+
paragraph: PdfParagraph,
|
|
205
|
+
):
|
|
206
|
+
left_placeholder = self.translate_engine.get_rich_text_left_placeholder(
|
|
207
|
+
composition_id,
|
|
208
|
+
)
|
|
209
|
+
right_placeholder = self.translate_engine.get_rich_text_right_placeholder(
|
|
210
|
+
composition_id,
|
|
211
|
+
)
|
|
212
|
+
if (
|
|
213
|
+
left_placeholder in paragraph.unicode
|
|
214
|
+
or right_placeholder in paragraph.unicode
|
|
215
|
+
):
|
|
216
|
+
return self.create_rich_text_placeholder(
|
|
217
|
+
composition,
|
|
218
|
+
composition_id + 1,
|
|
219
|
+
paragraph,
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
return RichTextPlaceholder(
|
|
223
|
+
composition_id,
|
|
224
|
+
composition,
|
|
225
|
+
left_placeholder,
|
|
226
|
+
right_placeholder,
|
|
227
|
+
)
|
|
228
|
+
|
|
229
|
+
def get_translate_input(
|
|
230
|
+
self,
|
|
231
|
+
paragraph: PdfParagraph,
|
|
232
|
+
page_font_map: dict[str, PdfFont] = None,
|
|
233
|
+
):
|
|
234
|
+
if not paragraph.pdf_paragraph_composition:
|
|
235
|
+
return
|
|
236
|
+
if len(paragraph.pdf_paragraph_composition) == 1:
|
|
237
|
+
# 如果整个段落只有一个组成部分,那么直接返回,不需要套占位符等
|
|
238
|
+
composition = paragraph.pdf_paragraph_composition[0]
|
|
239
|
+
if (
|
|
240
|
+
composition.pdf_line
|
|
241
|
+
or composition.pdf_same_style_characters
|
|
242
|
+
or composition.pdf_character
|
|
243
|
+
):
|
|
244
|
+
return self.TranslateInput(paragraph.unicode, [], paragraph.pdf_style)
|
|
245
|
+
elif composition.pdf_formula:
|
|
246
|
+
# 不需要翻译纯公式
|
|
247
|
+
return None
|
|
248
|
+
elif composition.pdf_same_style_unicode_characters:
|
|
249
|
+
# DEBUG INSERT CHAR, NOT TRANSLATE
|
|
250
|
+
return None
|
|
251
|
+
else:
|
|
252
|
+
logger.error(
|
|
253
|
+
f"Unknown composition type. "
|
|
254
|
+
f"Composition: {composition}. "
|
|
255
|
+
f"Paragraph: {paragraph}. ",
|
|
256
|
+
)
|
|
257
|
+
return None
|
|
258
|
+
|
|
259
|
+
placeholder_id = 1
|
|
260
|
+
placeholders = []
|
|
261
|
+
chars = []
|
|
262
|
+
for composition in paragraph.pdf_paragraph_composition:
|
|
263
|
+
if composition.pdf_line:
|
|
264
|
+
chars.extend(composition.pdf_line.pdf_character)
|
|
265
|
+
elif composition.pdf_formula:
|
|
266
|
+
formula_placeholder = self.create_formula_placeholder(
|
|
267
|
+
composition.pdf_formula,
|
|
268
|
+
placeholder_id,
|
|
269
|
+
paragraph,
|
|
270
|
+
)
|
|
271
|
+
placeholders.append(formula_placeholder)
|
|
272
|
+
# 公式只需要一个占位符,所以 id+1
|
|
273
|
+
placeholder_id = formula_placeholder.id + 1
|
|
274
|
+
chars.extend(formula_placeholder.placeholder)
|
|
275
|
+
elif composition.pdf_character:
|
|
276
|
+
chars.append(composition.pdf_character)
|
|
277
|
+
elif composition.pdf_same_style_characters:
|
|
278
|
+
fonta = self.font_mapper.map(
|
|
279
|
+
page_font_map[
|
|
280
|
+
composition.pdf_same_style_characters.pdf_style.font_id
|
|
281
|
+
],
|
|
282
|
+
"1",
|
|
283
|
+
)
|
|
284
|
+
fontb = self.font_mapper.map(
|
|
285
|
+
page_font_map[paragraph.pdf_style.font_id],
|
|
286
|
+
"1",
|
|
287
|
+
)
|
|
288
|
+
if (
|
|
289
|
+
# 样式和段落基准样式一致,无需占位符
|
|
290
|
+
is_same_style(
|
|
291
|
+
composition.pdf_same_style_characters.pdf_style,
|
|
292
|
+
paragraph.pdf_style,
|
|
293
|
+
)
|
|
294
|
+
# 字号差异在0.7-1.3之间,可能是首字母变大效果,无需占位符
|
|
295
|
+
or is_same_style_except_size(
|
|
296
|
+
composition.pdf_same_style_characters.pdf_style,
|
|
297
|
+
paragraph.pdf_style,
|
|
298
|
+
)
|
|
299
|
+
or (
|
|
300
|
+
# 除了字体以外样式都和基准一样,并且字体都映射到同一个字体。无需占位符
|
|
301
|
+
is_same_style_except_font(
|
|
302
|
+
composition.pdf_same_style_characters.pdf_style,
|
|
303
|
+
paragraph.pdf_style,
|
|
304
|
+
)
|
|
305
|
+
and fonta
|
|
306
|
+
and fontb
|
|
307
|
+
and fonta.font_id == fontb.font_id
|
|
308
|
+
)
|
|
309
|
+
# or len(composition.pdf_same_style_characters.pdf_character) == 1
|
|
310
|
+
):
|
|
311
|
+
chars.extend(composition.pdf_same_style_characters.pdf_character)
|
|
312
|
+
continue
|
|
313
|
+
placeholder = self.create_rich_text_placeholder(
|
|
314
|
+
composition.pdf_same_style_characters,
|
|
315
|
+
placeholder_id,
|
|
316
|
+
paragraph,
|
|
317
|
+
)
|
|
318
|
+
placeholders.append(placeholder)
|
|
319
|
+
# 样式需要一左一右两个占位符,所以 id+2
|
|
320
|
+
placeholder_id = placeholder.id + 2
|
|
321
|
+
chars.append(placeholder.left_placeholder)
|
|
322
|
+
chars.extend(composition.pdf_same_style_characters.pdf_character)
|
|
323
|
+
chars.append(placeholder.right_placeholder)
|
|
324
|
+
else:
|
|
325
|
+
logger.error(
|
|
326
|
+
"Unexpected PdfParagraphComposition type "
|
|
327
|
+
"in PdfParagraph during translation. "
|
|
328
|
+
f"Composition: {composition}. "
|
|
329
|
+
f"Paragraph: {paragraph}. ",
|
|
330
|
+
)
|
|
331
|
+
return None
|
|
332
|
+
|
|
333
|
+
text = get_char_unicode_string(chars)
|
|
334
|
+
return self.TranslateInput(text, placeholders, paragraph.pdf_style)
|
|
335
|
+
|
|
336
|
+
def process_formula(
|
|
337
|
+
self,
|
|
338
|
+
formula: PdfFormula,
|
|
339
|
+
formula_id: int,
|
|
340
|
+
paragraph: PdfParagraph,
|
|
341
|
+
):
|
|
342
|
+
placeholder = self.create_formula_placeholder(formula, formula_id, paragraph)
|
|
343
|
+
if placeholder.placeholder in paragraph.unicode:
|
|
344
|
+
return self.process_formula(formula, formula_id + 1, paragraph)
|
|
345
|
+
|
|
346
|
+
return placeholder
|
|
347
|
+
|
|
348
|
+
def process_composition(
|
|
349
|
+
self,
|
|
350
|
+
composition: PdfSameStyleCharacters,
|
|
351
|
+
composition_id: int,
|
|
352
|
+
paragraph: PdfParagraph,
|
|
353
|
+
):
|
|
354
|
+
placeholder = self.create_rich_text_placeholder(
|
|
355
|
+
composition,
|
|
356
|
+
composition_id,
|
|
357
|
+
paragraph,
|
|
358
|
+
)
|
|
359
|
+
if (
|
|
360
|
+
placeholder.left_placeholder in paragraph.unicode
|
|
361
|
+
or placeholder.right_placeholder in paragraph.unicode
|
|
362
|
+
):
|
|
363
|
+
return self.process_composition(
|
|
364
|
+
composition,
|
|
365
|
+
composition_id + 1,
|
|
366
|
+
paragraph,
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
return placeholder
|
|
370
|
+
|
|
371
|
+
def parse_translate_output(
|
|
372
|
+
self,
|
|
373
|
+
input_text: TranslateInput,
|
|
374
|
+
output: str,
|
|
375
|
+
) -> [PdfParagraphComposition]:
|
|
376
|
+
import re
|
|
377
|
+
|
|
378
|
+
result = []
|
|
379
|
+
|
|
380
|
+
# 如果没有占位符,直接返回整个文本
|
|
381
|
+
if not input_text.placeholders:
|
|
382
|
+
comp = PdfParagraphComposition()
|
|
383
|
+
comp.pdf_same_style_unicode_characters = PdfSameStyleUnicodeCharacters()
|
|
384
|
+
comp.pdf_same_style_unicode_characters.unicode = output
|
|
385
|
+
comp.pdf_same_style_unicode_characters.pdf_style = input_text.base_style
|
|
386
|
+
return [comp]
|
|
387
|
+
|
|
388
|
+
# 构建正则表达式模式
|
|
389
|
+
patterns = []
|
|
390
|
+
placeholder_patterns = []
|
|
391
|
+
placeholder_map = {}
|
|
392
|
+
|
|
393
|
+
for placeholder in input_text.placeholders:
|
|
394
|
+
if isinstance(placeholder, FormulaPlaceholder):
|
|
395
|
+
# 转义特殊字符
|
|
396
|
+
pattern = re.escape(placeholder.placeholder)
|
|
397
|
+
patterns.append(f"({pattern})")
|
|
398
|
+
placeholder_patterns.append(f"({pattern})")
|
|
399
|
+
placeholder_map[placeholder.placeholder] = placeholder
|
|
400
|
+
else:
|
|
401
|
+
left = re.escape(placeholder.left_placeholder)
|
|
402
|
+
right = re.escape(placeholder.right_placeholder)
|
|
403
|
+
patterns.append(f"({left}.*?{right})")
|
|
404
|
+
placeholder_patterns.append(f"({left})")
|
|
405
|
+
placeholder_patterns.append(f"({right})")
|
|
406
|
+
placeholder_map[placeholder.left_placeholder] = placeholder
|
|
407
|
+
|
|
408
|
+
# 合并所有模式
|
|
409
|
+
combined_pattern = "|".join(patterns)
|
|
410
|
+
combined_placeholder_pattern = "|".join(placeholder_patterns)
|
|
411
|
+
|
|
412
|
+
def remove_placeholder(text: str):
|
|
413
|
+
return re.sub(combined_placeholder_pattern, "", text)
|
|
414
|
+
|
|
415
|
+
# 找到所有匹配
|
|
416
|
+
last_end = 0
|
|
417
|
+
for match in re.finditer(combined_pattern, output):
|
|
418
|
+
# 处理匹配之前的普通文本
|
|
419
|
+
if match.start() > last_end:
|
|
420
|
+
text = output[last_end : match.start()]
|
|
421
|
+
if text:
|
|
422
|
+
comp = PdfParagraphComposition()
|
|
423
|
+
comp.pdf_same_style_unicode_characters = (
|
|
424
|
+
PdfSameStyleUnicodeCharacters()
|
|
425
|
+
)
|
|
426
|
+
comp.pdf_same_style_unicode_characters.unicode = remove_placeholder(
|
|
427
|
+
text,
|
|
428
|
+
)
|
|
429
|
+
comp.pdf_same_style_unicode_characters.pdf_style = (
|
|
430
|
+
input_text.base_style
|
|
431
|
+
)
|
|
432
|
+
result.append(comp)
|
|
433
|
+
|
|
434
|
+
matched_text = match.group(0)
|
|
435
|
+
|
|
436
|
+
# 处理占位符
|
|
437
|
+
if any(
|
|
438
|
+
isinstance(p, FormulaPlaceholder) and matched_text == p.placeholder
|
|
439
|
+
for p in input_text.placeholders
|
|
440
|
+
):
|
|
441
|
+
# 处理公式占位符
|
|
442
|
+
placeholder = next(
|
|
443
|
+
p
|
|
444
|
+
for p in input_text.placeholders
|
|
445
|
+
if isinstance(p, FormulaPlaceholder)
|
|
446
|
+
and matched_text == p.placeholder
|
|
447
|
+
)
|
|
448
|
+
comp = PdfParagraphComposition()
|
|
449
|
+
comp.pdf_formula = placeholder.formula
|
|
450
|
+
result.append(comp)
|
|
451
|
+
else:
|
|
452
|
+
# 处理富文本占位符
|
|
453
|
+
placeholder = next(
|
|
454
|
+
p
|
|
455
|
+
for p in input_text.placeholders
|
|
456
|
+
if not isinstance(p, FormulaPlaceholder)
|
|
457
|
+
and matched_text.startswith(p.left_placeholder)
|
|
458
|
+
)
|
|
459
|
+
text = matched_text[
|
|
460
|
+
len(placeholder.left_placeholder) : -len(
|
|
461
|
+
placeholder.right_placeholder,
|
|
462
|
+
)
|
|
463
|
+
]
|
|
464
|
+
|
|
465
|
+
if isinstance(
|
|
466
|
+
placeholder.composition,
|
|
467
|
+
PdfSameStyleCharacters,
|
|
468
|
+
) and text.replace(" ", "") == "".join(
|
|
469
|
+
x.char_unicode for x in placeholder.composition.pdf_character
|
|
470
|
+
).replace(" ", ""):
|
|
471
|
+
comp = PdfParagraphComposition(
|
|
472
|
+
pdf_same_style_characters=placeholder.composition,
|
|
473
|
+
)
|
|
474
|
+
else:
|
|
475
|
+
comp = PdfParagraphComposition()
|
|
476
|
+
comp.pdf_same_style_unicode_characters = (
|
|
477
|
+
PdfSameStyleUnicodeCharacters()
|
|
478
|
+
)
|
|
479
|
+
comp.pdf_same_style_unicode_characters.pdf_style = (
|
|
480
|
+
placeholder.composition.pdf_style
|
|
481
|
+
)
|
|
482
|
+
comp.pdf_same_style_unicode_characters.unicode = remove_placeholder(
|
|
483
|
+
text,
|
|
484
|
+
)
|
|
485
|
+
result.append(comp)
|
|
486
|
+
|
|
487
|
+
last_end = match.end()
|
|
488
|
+
|
|
489
|
+
# 处理最后的普通文本
|
|
490
|
+
if last_end < len(output):
|
|
491
|
+
text = output[last_end:]
|
|
492
|
+
if text:
|
|
493
|
+
comp = PdfParagraphComposition()
|
|
494
|
+
comp.pdf_same_style_unicode_characters = PdfSameStyleUnicodeCharacters()
|
|
495
|
+
comp.pdf_same_style_unicode_characters.unicode = remove_placeholder(
|
|
496
|
+
text,
|
|
497
|
+
)
|
|
498
|
+
comp.pdf_same_style_unicode_characters.pdf_style = input_text.base_style
|
|
499
|
+
result.append(comp)
|
|
500
|
+
|
|
501
|
+
return result
|
|
502
|
+
|
|
503
|
+
def translate_paragraph(
|
|
504
|
+
self,
|
|
505
|
+
paragraph: PdfParagraph,
|
|
506
|
+
pbar: tqdm | None = None,
|
|
507
|
+
tracker: ParagraphTranslateTracker = None,
|
|
508
|
+
page_font_map: dict[str, PdfFont] = None,
|
|
509
|
+
xobj_font_map: dict[int, dict[str, PdfFont]] = None,
|
|
510
|
+
):
|
|
511
|
+
self.translation_config.raise_if_cancelled()
|
|
512
|
+
with PbarContext(pbar):
|
|
513
|
+
try:
|
|
514
|
+
if paragraph.vertical:
|
|
515
|
+
return
|
|
516
|
+
|
|
517
|
+
tracker.set_pdf_unicode(paragraph.unicode)
|
|
518
|
+
if paragraph.xobj_id in xobj_font_map:
|
|
519
|
+
page_font_map = xobj_font_map[paragraph.xobj_id]
|
|
520
|
+
translate_input = self.get_translate_input(paragraph, page_font_map)
|
|
521
|
+
if not translate_input:
|
|
522
|
+
return
|
|
523
|
+
|
|
524
|
+
tracker.set_input(translate_input.unicode)
|
|
525
|
+
|
|
526
|
+
text = translate_input.unicode
|
|
527
|
+
translated_text = self.translate_engine.translate(text)
|
|
528
|
+
|
|
529
|
+
tracker.set_output(translated_text)
|
|
530
|
+
|
|
531
|
+
if translated_text == text:
|
|
532
|
+
return
|
|
533
|
+
|
|
534
|
+
paragraph.unicode = translated_text
|
|
535
|
+
paragraph.pdf_paragraph_composition = self.parse_translate_output(
|
|
536
|
+
translate_input,
|
|
537
|
+
translated_text,
|
|
538
|
+
)
|
|
539
|
+
for composition in paragraph.pdf_paragraph_composition:
|
|
540
|
+
if (
|
|
541
|
+
composition.pdf_same_style_unicode_characters
|
|
542
|
+
and composition.pdf_same_style_unicode_characters.pdf_style
|
|
543
|
+
is None
|
|
544
|
+
):
|
|
545
|
+
composition.pdf_same_style_unicode_characters.pdf_style = (
|
|
546
|
+
paragraph.pdf_style
|
|
547
|
+
)
|
|
548
|
+
except Exception as e:
|
|
549
|
+
logger.exception(
|
|
550
|
+
f"Error translating paragraph. Paragraph: {paragraph}. Error: {e}. ",
|
|
551
|
+
)
|
|
552
|
+
# ignore error and continue
|
|
553
|
+
return
|