BabelDOC 0.2.23__tar.gz → 0.2.25__tar.gz
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-0.2.23 → babeldoc-0.2.25}/PKG-INFO +1 -1
- babeldoc-0.2.25/babeldoc/__init__.py +1 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/const.py +1 -1
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/converter.py +6 -1
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/document_il/backend/pdf_creater.py +238 -213
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/document_il/frontend/il_creater.py +15 -5
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/document_il/utils/fontmap.py +5 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/main.py +1 -1
- {babeldoc-0.2.23 → babeldoc-0.2.25}/pyproject.toml +2 -2
- {babeldoc-0.2.23 → babeldoc-0.2.25}/uv.lock +149 -126
- babeldoc-0.2.23/babeldoc/__init__.py +0 -1
- {babeldoc-0.2.23 → babeldoc-0.2.25}/.cursorignore +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/.github/ISSUE_TEMPLATE/bug_report.yaml +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/.github/ISSUE_TEMPLATE/feature_request.yaml +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/.github/dependabot.yml +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/.github/labels.yml +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/.github/release-drafter.yml +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/.github/workflows/codeql.yml +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/.github/workflows/docs.yml +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/.github/workflows/labeler.yml +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/.github/workflows/publish-to-pypi.yml +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/.github/workflows/test.yml +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/.gitignore +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/.pre-commit-config.yaml +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/LICENSE +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/README.md +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/assets/assets.py +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/assets/embedding_assets_metadata.py +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/asynchronize/__init__.py +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/document_il/__init__.py +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/document_il/babeldoc_exception/BabelDOCException.py +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/document_il/backend/__init__.py +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/document_il/frontend/__init__.py +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/document_il/il_version_1.py +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/document_il/il_version_1.rnc +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/document_il/il_version_1.rng +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/document_il/il_version_1.xsd +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/document_il/midend/__init__.py +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/document_il/midend/add_debug_information.py +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/document_il/midend/detect_scanned_file.py +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/document_il/midend/il_translator.py +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/document_il/midend/il_translator_llm_only.py +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/document_il/midend/layout_parser.py +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/document_il/midend/paragraph_finder.py +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/document_il/midend/remove_descent.py +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/document_il/midend/styles_and_formulas.py +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/document_il/midend/table_parser.py +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/document_il/midend/typesetting.py +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/document_il/translator/__init__.py +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/document_il/translator/cache.py +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/document_il/translator/translator.py +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/document_il/utils/atomic_integer.py +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/document_il/utils/layout_helper.py +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/document_il/utils/priority_thread_pool_executor.py +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/document_il/utils/style_helper.py +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/document_il/xml_converter.py +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/docvision/README.md +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/docvision/__init__.py +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/docvision/doclayout.py +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/docvision/rpc_doclayout.py +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/docvision/table_detection/rapidocr.py +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/high_level.py +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/pdfinterp.py +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/progress_monitor.py +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/result_merger.py +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/split_manager.py +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/tools/generate_font_metadata.py +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/babeldoc/translation_config.py +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/docs/CODE_OF_CONDUCT.md +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/docs/CONTRIBUTING.md +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/docs/CONTRIBUTOR_REWARD.md +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/docs/ImplementationDetails/AsyncTranslate/AsyncTranslate.md +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/docs/ImplementationDetails/ILTranslator/ILTranslator.md +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/docs/ImplementationDetails/PDFCreation/PDFCreation.md +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/docs/ImplementationDetails/PDFParsing/PDFParsing.md +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/docs/ImplementationDetails/ParagraphFinding/ParagraphFinding.md +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/docs/ImplementationDetails/README.md +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/docs/ImplementationDetails/StylesAndFormulas/StylesAndFormulas.md +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/docs/ImplementationDetails/Typesetting/Typesetting.md +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/docs/README.md +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/docs/deploy.sh +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/docs/images/babeldoc-banner.png +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/docs/images/babeldoc-big-logo-darkmode-with-transparent-background.png +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/docs/images/babeldoc-big-logo-darkmode-with-transparent-background.svg +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/docs/images/babeldoc-big-logo-with-transparent-background.png +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/docs/images/babeldoc-big-logo-with-transparent-background.svg +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/docs/images/babeldoc-big-logo.png +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/docs/images/babeldoc-contributor_reward_example.png +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/docs/images/babeldoc-preview.gif +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/docs/images/babeldoc-small-logo-with-transparent-background.png +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/docs/images/babeldoc-small-logo-with-transparent-background.svg +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/docs/images/babeldoc-small-logo.png +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/docs/index.md +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/docs/intro-to-pdf-object.md +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/docs/requirements.txt +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/mkdocs.yml +0 -0
- {babeldoc-0.2.23 → babeldoc-0.2.25}/tests/test_translation_config.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.2.25"
|
|
@@ -282,7 +282,12 @@ class TranslateConverter(PDFConverterEx):
|
|
|
282
282
|
# A. 原文档解析
|
|
283
283
|
for child in ltpage:
|
|
284
284
|
if isinstance(child, LTChar):
|
|
285
|
-
|
|
285
|
+
try:
|
|
286
|
+
self.il_creater.on_lt_char(child)
|
|
287
|
+
except Exception:
|
|
288
|
+
log.exception(
|
|
289
|
+
'Error processing LTChar',
|
|
290
|
+
)
|
|
286
291
|
continue
|
|
287
292
|
cur_v = False
|
|
288
293
|
layout = self.layout[ltpage.pageid]
|
|
@@ -158,27 +158,30 @@ class PDFCreater:
|
|
|
158
158
|
return self.get_xobj_available_fonts(page_xref_id, pdf)
|
|
159
159
|
|
|
160
160
|
def get_xobj_available_fonts(self, page_xref_id, pdf):
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
xref_id
|
|
170
|
-
|
|
161
|
+
try:
|
|
162
|
+
resources_type, r_id = pdf.xref_get_key(page_xref_id, "Resources")
|
|
163
|
+
if resources_type == "xref":
|
|
164
|
+
resource_xref_id = re.search("(\\d+) 0 R", r_id).group(1)
|
|
165
|
+
r_id = pdf.xref_object(int(resource_xref_id))
|
|
166
|
+
resources_type = "dict"
|
|
167
|
+
if resources_type == "dict":
|
|
168
|
+
xref_id = re.search("/Font (\\d+) 0 R", r_id)
|
|
169
|
+
if xref_id is not None:
|
|
170
|
+
xref_id = xref_id.group(1)
|
|
171
|
+
font_dict = pdf.xref_object(int(xref_id))
|
|
172
|
+
else:
|
|
173
|
+
search = re.search("/Font *<<(.+?)>>", r_id.replace("\n", " "))
|
|
174
|
+
if search is None:
|
|
175
|
+
# Have resources but no fonts
|
|
176
|
+
return set()
|
|
177
|
+
font_dict = search.group(1)
|
|
171
178
|
else:
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
r_id = int(r_id.split(" ")[0])
|
|
179
|
-
_, font_dict = pdf.xref_get_key(r_id, "Font")
|
|
180
|
-
fonts = re.findall("/([^ ]+?) ", font_dict)
|
|
181
|
-
return set(fonts)
|
|
179
|
+
r_id = int(r_id.split(" ")[0])
|
|
180
|
+
_, font_dict = pdf.xref_get_key(r_id, "Font")
|
|
181
|
+
fonts = re.findall("/([^ ]+?) ", font_dict)
|
|
182
|
+
return set(fonts)
|
|
183
|
+
except Exception:
|
|
184
|
+
return set()
|
|
182
185
|
|
|
183
186
|
def _debug_render_rectangle(
|
|
184
187
|
self,
|
|
@@ -633,213 +636,235 @@ class PDFCreater:
|
|
|
633
636
|
|
|
634
637
|
return False
|
|
635
638
|
|
|
636
|
-
def write(
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
xobj_available_fonts
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
639
|
+
def write(
|
|
640
|
+
self, translation_config: TranslationConfig, check_font_exists: bool = False
|
|
641
|
+
) -> TranslateResult:
|
|
642
|
+
try:
|
|
643
|
+
basename = Path(translation_config.input_file).stem
|
|
644
|
+
debug_suffix = ".debug" if translation_config.debug else ""
|
|
645
|
+
if (
|
|
646
|
+
translation_config.watermark_output_mode
|
|
647
|
+
!= WatermarkOutputMode.Watermarked
|
|
648
|
+
):
|
|
649
|
+
debug_suffix += ".no_watermark"
|
|
650
|
+
mono_out_path = translation_config.get_output_file_path(
|
|
651
|
+
f"{basename}{debug_suffix}.{translation_config.lang_out}.mono.pdf",
|
|
652
|
+
)
|
|
653
|
+
pdf = pymupdf.open(self.original_pdf_path)
|
|
654
|
+
self.font_mapper.add_font(pdf, self.docs)
|
|
655
|
+
with self.translation_config.progress_monitor.stage_start(
|
|
656
|
+
self.stage_name,
|
|
657
|
+
len(self.docs.page),
|
|
658
|
+
) as pbar:
|
|
659
|
+
for page in self.docs.page:
|
|
660
|
+
translation_config.raise_if_cancelled()
|
|
661
|
+
xobj_available_fonts = {}
|
|
662
|
+
xobj_draw_ops = {}
|
|
663
|
+
xobj_encoding_length_map = {}
|
|
664
|
+
available_font_list = self.get_available_font_list(pdf, page)
|
|
665
|
+
|
|
666
|
+
for xobj in page.pdf_xobject:
|
|
667
|
+
xobj_available_fonts[xobj.xobj_id] = available_font_list.copy()
|
|
668
|
+
try:
|
|
669
|
+
xobj_available_fonts[xobj.xobj_id].update(
|
|
670
|
+
self.get_xobj_available_fonts(xobj.xref_id, pdf),
|
|
671
|
+
)
|
|
672
|
+
except Exception:
|
|
673
|
+
pass
|
|
674
|
+
xobj_encoding_length_map[xobj.xobj_id] = {
|
|
675
|
+
f.font_id: f.encoding_length for f in xobj.pdf_font
|
|
676
|
+
}
|
|
677
|
+
xobj_op = BitStream()
|
|
678
|
+
xobj_op.append(xobj.base_operations.value.encode())
|
|
679
|
+
xobj_draw_ops[xobj.xobj_id] = xobj_op
|
|
680
|
+
page_encoding_length_map = {
|
|
681
|
+
f.font_id: f.encoding_length for f in page.pdf_font
|
|
667
682
|
}
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
chars
|
|
688
|
-
|
|
689
|
-
for paragraph in page.pdf_paragraph:
|
|
690
|
-
chars.extend(self.render_paragraph_to_char(paragraph))
|
|
691
|
-
|
|
692
|
-
# 渲染所有字符
|
|
693
|
-
for char in chars:
|
|
694
|
-
if char.char_unicode == "\n":
|
|
695
|
-
continue
|
|
696
|
-
if char.pdf_character_id is None:
|
|
697
|
-
# dummy char
|
|
698
|
-
continue
|
|
699
|
-
char_size = char.pdf_style.font_size
|
|
700
|
-
font_id = char.pdf_style.font_id
|
|
701
|
-
if char.xobj_id in xobj_available_fonts:
|
|
702
|
-
if font_id not in xobj_available_fonts[char.xobj_id]:
|
|
683
|
+
page_op = BitStream()
|
|
684
|
+
# q {ops_base}Q 1 0 0 1 {x0} {y0} cm {ops_new}
|
|
685
|
+
# page_op.append(b"q ")
|
|
686
|
+
page_op.append(page.base_operations.value.encode())
|
|
687
|
+
page_op.append(b" \n")
|
|
688
|
+
# page_op.append(b" Q ")
|
|
689
|
+
# page_op.append(
|
|
690
|
+
# f"q Q 1 0 0 1 {page.cropbox.box.x} {page.cropbox.box.y} cm \n".encode(),
|
|
691
|
+
# )
|
|
692
|
+
# 收集所有字符
|
|
693
|
+
chars = []
|
|
694
|
+
# 首先添加页面级别的字符
|
|
695
|
+
if page.pdf_character:
|
|
696
|
+
chars.extend(page.pdf_character)
|
|
697
|
+
# 然后添加段落中的字符
|
|
698
|
+
for paragraph in page.pdf_paragraph:
|
|
699
|
+
chars.extend(self.render_paragraph_to_char(paragraph))
|
|
700
|
+
|
|
701
|
+
# 渲染所有字符
|
|
702
|
+
for char in chars:
|
|
703
|
+
if char.char_unicode == "\n":
|
|
703
704
|
continue
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
else:
|
|
707
|
-
if font_id not in available_font_list:
|
|
705
|
+
if char.pdf_character_id is None:
|
|
706
|
+
# dummy char
|
|
708
707
|
continue
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
708
|
+
char_size = char.pdf_style.font_size
|
|
709
|
+
font_id = char.pdf_style.font_id
|
|
710
|
+
if char.xobj_id in xobj_available_fonts:
|
|
711
|
+
if (
|
|
712
|
+
check_font_exists
|
|
713
|
+
and font_id not in xobj_available_fonts[char.xobj_id]
|
|
714
|
+
):
|
|
715
|
+
continue
|
|
716
|
+
draw_op = xobj_draw_ops[char.xobj_id]
|
|
717
|
+
encoding_length_map = xobj_encoding_length_map[char.xobj_id]
|
|
718
|
+
else:
|
|
719
|
+
if check_font_exists and font_id not in available_font_list:
|
|
720
|
+
continue
|
|
721
|
+
draw_op = page_op
|
|
722
|
+
encoding_length_map = page_encoding_length_map
|
|
723
|
+
|
|
724
|
+
draw_op.append(b"q ")
|
|
725
|
+
self.render_graphic_state(draw_op, char.pdf_style.graphic_state)
|
|
726
|
+
if char.vertical:
|
|
727
|
+
draw_op.append(
|
|
728
|
+
f"BT /{font_id} {char_size:f} Tf 0 1 -1 0 {char.box.x2:f} {char.box.y:f} Tm ".encode(),
|
|
729
|
+
)
|
|
730
|
+
else:
|
|
731
|
+
draw_op.append(
|
|
732
|
+
f"BT /{font_id} {char_size:f} Tf 1 0 0 1 {char.box.x:f} {char.box.y:f} Tm ".encode(),
|
|
733
|
+
)
|
|
734
|
+
|
|
735
|
+
encoding_length = encoding_length_map.get(font_id, None)
|
|
736
|
+
if encoding_length is None:
|
|
737
|
+
logger.debug(
|
|
738
|
+
f"Font {font_id} not found in encoding length map for page {page.page_number}"
|
|
739
|
+
)
|
|
740
|
+
continue
|
|
741
|
+
# pdf32000-2008 page14:
|
|
742
|
+
# As hexadecimal data enclosed in angle brackets < >
|
|
743
|
+
# see 7.3.4.3, "Hexadecimal Strings."
|
|
719
744
|
draw_op.append(
|
|
720
|
-
f"
|
|
745
|
+
f"<{char.pdf_character_id:0{encoding_length * 2}x}>".upper().encode(),
|
|
721
746
|
)
|
|
722
747
|
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
748
|
+
draw_op.append(b" Tj ET Q \n")
|
|
749
|
+
for xobj in page.pdf_xobject:
|
|
750
|
+
draw_op = xobj_draw_ops[xobj.xobj_id]
|
|
751
|
+
try:
|
|
752
|
+
pdf.update_stream(xobj.xref_id, draw_op.tobytes())
|
|
753
|
+
except Exception:
|
|
754
|
+
logger.warning(
|
|
755
|
+
f"update xref {xobj.xref_id} stream fail, continue"
|
|
756
|
+
)
|
|
757
|
+
# pdf.update_stream(xobj.xref_id, b'')
|
|
758
|
+
for rect in page.pdf_rectangle:
|
|
759
|
+
self._debug_render_rectangle(page_op, rect)
|
|
760
|
+
draw_op = page_op
|
|
761
|
+
op_container = pdf.get_new_xref()
|
|
762
|
+
# Since this is a draw instruction container,
|
|
763
|
+
# no additional information is needed
|
|
764
|
+
pdf.update_object(op_container, "<<>>")
|
|
765
|
+
pdf.update_stream(op_container, draw_op.tobytes())
|
|
766
|
+
pdf[page.page_number].set_contents(op_container)
|
|
767
|
+
pbar.advance()
|
|
768
|
+
translation_config.raise_if_cancelled()
|
|
769
|
+
with self.translation_config.progress_monitor.stage_start(
|
|
770
|
+
SUBSET_FONT_STAGE_NAME,
|
|
771
|
+
1,
|
|
772
|
+
) as pbar:
|
|
773
|
+
if not translation_config.skip_clean:
|
|
774
|
+
pdf = self.subset_fonts_in_subprocess(
|
|
775
|
+
pdf, translation_config, tag="mono"
|
|
729
776
|
)
|
|
730
777
|
|
|
731
|
-
draw_op.append(b" Tj ET Q \n")
|
|
732
|
-
for xobj in page.pdf_xobject:
|
|
733
|
-
draw_op = xobj_draw_ops[xobj.xobj_id]
|
|
734
|
-
try:
|
|
735
|
-
pdf.update_stream(xobj.xref_id, draw_op.tobytes())
|
|
736
|
-
except Exception:
|
|
737
|
-
logger.warning(
|
|
738
|
-
f"update xref {xobj.xref_id} stream fail, continue"
|
|
739
|
-
)
|
|
740
|
-
# pdf.update_stream(xobj.xref_id, b'')
|
|
741
|
-
for rect in page.pdf_rectangle:
|
|
742
|
-
self._debug_render_rectangle(page_op, rect)
|
|
743
|
-
draw_op = page_op
|
|
744
|
-
op_container = pdf.get_new_xref()
|
|
745
|
-
# Since this is a draw instruction container,
|
|
746
|
-
# no additional information is needed
|
|
747
|
-
pdf.update_object(op_container, "<<>>")
|
|
748
|
-
pdf.update_stream(op_container, draw_op.tobytes())
|
|
749
|
-
pdf[page.page_number].set_contents(op_container)
|
|
750
778
|
pbar.advance()
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
SAVE_PDF_STAGE_NAME,
|
|
764
|
-
2,
|
|
765
|
-
) as pbar:
|
|
766
|
-
if not translation_config.no_mono:
|
|
767
|
-
if translation_config.debug:
|
|
779
|
+
with self.translation_config.progress_monitor.stage_start(
|
|
780
|
+
SAVE_PDF_STAGE_NAME,
|
|
781
|
+
2,
|
|
782
|
+
) as pbar:
|
|
783
|
+
if not translation_config.no_mono:
|
|
784
|
+
if translation_config.debug:
|
|
785
|
+
translation_config.raise_if_cancelled()
|
|
786
|
+
pdf.save(
|
|
787
|
+
f"{mono_out_path}.decompressed.pdf",
|
|
788
|
+
expand=True,
|
|
789
|
+
pretty=True,
|
|
790
|
+
)
|
|
768
791
|
translation_config.raise_if_cancelled()
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
pretty=True,
|
|
773
|
-
)
|
|
774
|
-
translation_config.raise_if_cancelled()
|
|
775
|
-
self.save_pdf_with_timeout(
|
|
776
|
-
pdf,
|
|
777
|
-
mono_out_path,
|
|
778
|
-
translation_config,
|
|
779
|
-
garbage=1,
|
|
780
|
-
deflate=True,
|
|
781
|
-
clean=not translation_config.skip_clean,
|
|
782
|
-
deflate_fonts=True,
|
|
783
|
-
linear=False,
|
|
784
|
-
tag="mono",
|
|
785
|
-
)
|
|
786
|
-
pbar.advance()
|
|
787
|
-
dual_out_path = None
|
|
788
|
-
if not translation_config.no_dual:
|
|
789
|
-
dual_out_path = translation_config.get_output_file_path(
|
|
790
|
-
f"{basename}{debug_suffix}.{translation_config.lang_out}.dual.pdf",
|
|
791
|
-
)
|
|
792
|
-
translation_config.raise_if_cancelled()
|
|
793
|
-
original_pdf = pymupdf.open(self.original_pdf_path)
|
|
794
|
-
translated_pdf = pdf
|
|
795
|
-
|
|
796
|
-
# Choose between alternating pages and side-by-side format
|
|
797
|
-
# Default to side-by-side if not specified
|
|
798
|
-
use_alternating_pages = translation_config.use_alternating_pages_dual
|
|
799
|
-
|
|
800
|
-
if use_alternating_pages:
|
|
801
|
-
# Create a dual PDF with alternating pages (original and translation)
|
|
802
|
-
dual = self.create_alternating_pages_dual_pdf(
|
|
803
|
-
self.original_pdf_path,
|
|
804
|
-
translated_pdf,
|
|
792
|
+
self.save_pdf_with_timeout(
|
|
793
|
+
pdf,
|
|
794
|
+
mono_out_path,
|
|
805
795
|
translation_config,
|
|
796
|
+
garbage=1,
|
|
797
|
+
deflate=True,
|
|
798
|
+
clean=not translation_config.skip_clean,
|
|
799
|
+
deflate_fonts=True,
|
|
800
|
+
linear=False,
|
|
801
|
+
tag="mono",
|
|
806
802
|
)
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
dual_out_path,
|
|
813
|
-
translation_config,
|
|
803
|
+
pbar.advance()
|
|
804
|
+
dual_out_path = None
|
|
805
|
+
if not translation_config.no_dual:
|
|
806
|
+
dual_out_path = translation_config.get_output_file_path(
|
|
807
|
+
f"{basename}{debug_suffix}.{translation_config.lang_out}.dual.pdf",
|
|
814
808
|
)
|
|
815
|
-
|
|
816
|
-
if translation_config.debug:
|
|
817
809
|
translation_config.raise_if_cancelled()
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
810
|
+
original_pdf = pymupdf.open(self.original_pdf_path)
|
|
811
|
+
translated_pdf = pdf
|
|
812
|
+
|
|
813
|
+
# Choose between alternating pages and side-by-side format
|
|
814
|
+
# Default to side-by-side if not specified
|
|
815
|
+
use_alternating_pages = (
|
|
816
|
+
translation_config.use_alternating_pages_dual
|
|
817
|
+
)
|
|
818
|
+
|
|
819
|
+
if use_alternating_pages:
|
|
820
|
+
# Create a dual PDF with alternating pages (original and translation)
|
|
821
|
+
dual = self.create_alternating_pages_dual_pdf(
|
|
822
|
+
self.original_pdf_path,
|
|
823
|
+
translated_pdf,
|
|
824
|
+
translation_config,
|
|
825
|
+
)
|
|
826
|
+
else:
|
|
827
|
+
# Create a dual PDF with side-by-side pages (original and translation)
|
|
828
|
+
dual = self.create_side_by_side_dual_pdf(
|
|
829
|
+
original_pdf,
|
|
830
|
+
translated_pdf,
|
|
831
|
+
dual_out_path,
|
|
832
|
+
translation_config,
|
|
824
833
|
)
|
|
825
834
|
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
835
|
+
if translation_config.debug:
|
|
836
|
+
translation_config.raise_if_cancelled()
|
|
837
|
+
try:
|
|
838
|
+
dual = self.write_debug_info(dual, translation_config)
|
|
839
|
+
except Exception:
|
|
840
|
+
logger.warning(
|
|
841
|
+
"Failed to write debug info to dual PDF",
|
|
842
|
+
exc_info=True,
|
|
843
|
+
)
|
|
844
|
+
|
|
845
|
+
self.save_pdf_with_timeout(
|
|
846
|
+
dual,
|
|
847
|
+
dual_out_path,
|
|
848
|
+
translation_config,
|
|
849
|
+
garbage=1,
|
|
850
|
+
deflate=True,
|
|
851
|
+
clean=not translation_config.skip_clean,
|
|
852
|
+
deflate_fonts=True,
|
|
853
|
+
linear=False,
|
|
854
|
+
tag="dual",
|
|
843
855
|
)
|
|
844
|
-
|
|
845
|
-
|
|
856
|
+
if translation_config.debug:
|
|
857
|
+
translation_config.raise_if_cancelled()
|
|
858
|
+
dual.save(
|
|
859
|
+
f"{dual_out_path}.decompressed.pdf",
|
|
860
|
+
expand=True,
|
|
861
|
+
pretty=True,
|
|
862
|
+
)
|
|
863
|
+
pbar.advance()
|
|
864
|
+
return TranslateResult(mono_out_path, dual_out_path)
|
|
865
|
+
except Exception:
|
|
866
|
+
logger.exception(
|
|
867
|
+
"Failed to create PDF: %s",
|
|
868
|
+
translation_config.input_file,
|
|
869
|
+
)
|
|
870
|
+
return self.write(translation_config, True)
|
|
@@ -497,11 +497,21 @@ class ILCreater:
|
|
|
497
497
|
|
|
498
498
|
char_id = char.cid
|
|
499
499
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
500
|
+
try:
|
|
501
|
+
if (
|
|
502
|
+
font_bounding_box_map
|
|
503
|
+
:= self.current_page_font_char_bounding_box_map.get(
|
|
504
|
+
self.xobj_id, self.current_page_font_char_bounding_box_map
|
|
505
|
+
).get(font.font_id)
|
|
506
|
+
):
|
|
507
|
+
char_bounding_box = font_bounding_box_map.get(char_id, None)
|
|
508
|
+
else:
|
|
509
|
+
char_bounding_box = None
|
|
510
|
+
except Exception:
|
|
511
|
+
logger.debug(
|
|
512
|
+
"Failed to get font bounding box for char %s",
|
|
513
|
+
char.get_text(),
|
|
514
|
+
)
|
|
505
515
|
char_bounding_box = None
|
|
506
516
|
|
|
507
517
|
char_unicode = char.get_text()
|
|
@@ -187,6 +187,11 @@ class FontMapper:
|
|
|
187
187
|
pbar.advance(1)
|
|
188
188
|
for xref in range(1, xreflen):
|
|
189
189
|
pbar.advance(1)
|
|
190
|
+
# xref_type = doc_zh.xref_get_key(xref, "Type")
|
|
191
|
+
# if xref_type[1] == "/Page":
|
|
192
|
+
# resources_xref = doc_zh.xref_get_key(xref, "Resources")
|
|
193
|
+
# if resources_xref[0] == 'null':
|
|
194
|
+
# doc_zh.xref_set_key(xref, "Resources", f"<</Font<<>>>>")
|
|
190
195
|
for label in ["Resources/", ""]: # 可能是基于 xobj 的 res
|
|
191
196
|
try: # xref 读写可能出错
|
|
192
197
|
font_res = doc_zh.xref_get_key(xref, f"{label}Font")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "BabelDOC"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.25"
|
|
4
4
|
description = "Yet Another Document Translator"
|
|
5
5
|
license = "AGPL-3.0"
|
|
6
6
|
readme = "README.md"
|
|
@@ -148,7 +148,7 @@ pythonpath = [".", "src"]
|
|
|
148
148
|
testpaths = ["tests"]
|
|
149
149
|
|
|
150
150
|
[bumpver]
|
|
151
|
-
current_version = "0.2.
|
|
151
|
+
current_version = "0.2.25"
|
|
152
152
|
version_pattern = "MAJOR.MINOR.PATCH[.PYTAGNUM]"
|
|
153
153
|
|
|
154
154
|
[bumpver.file_patterns]
|