BabelDOC 0.2.22__tar.gz → 0.2.24__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.
Files changed (97) hide show
  1. {babeldoc-0.2.22 → babeldoc-0.2.24}/PKG-INFO +1 -1
  2. babeldoc-0.2.24/babeldoc/__init__.py +1 -0
  3. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/const.py +1 -1
  4. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/document_il/backend/pdf_creater.py +233 -213
  5. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/document_il/midend/paragraph_finder.py +1 -1
  6. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/document_il/utils/fontmap.py +5 -0
  7. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/high_level.py +11 -4
  8. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/main.py +1 -1
  9. {babeldoc-0.2.22 → babeldoc-0.2.24}/pyproject.toml +2 -2
  10. {babeldoc-0.2.22 → babeldoc-0.2.24}/uv.lock +1 -1
  11. babeldoc-0.2.22/babeldoc/__init__.py +0 -1
  12. {babeldoc-0.2.22 → babeldoc-0.2.24}/.cursorignore +0 -0
  13. {babeldoc-0.2.22 → babeldoc-0.2.24}/.github/ISSUE_TEMPLATE/bug_report.yaml +0 -0
  14. {babeldoc-0.2.22 → babeldoc-0.2.24}/.github/ISSUE_TEMPLATE/feature_request.yaml +0 -0
  15. {babeldoc-0.2.22 → babeldoc-0.2.24}/.github/dependabot.yml +0 -0
  16. {babeldoc-0.2.22 → babeldoc-0.2.24}/.github/labels.yml +0 -0
  17. {babeldoc-0.2.22 → babeldoc-0.2.24}/.github/release-drafter.yml +0 -0
  18. {babeldoc-0.2.22 → babeldoc-0.2.24}/.github/workflows/codeql.yml +0 -0
  19. {babeldoc-0.2.22 → babeldoc-0.2.24}/.github/workflows/docs.yml +0 -0
  20. {babeldoc-0.2.22 → babeldoc-0.2.24}/.github/workflows/labeler.yml +0 -0
  21. {babeldoc-0.2.22 → babeldoc-0.2.24}/.github/workflows/publish-to-pypi.yml +0 -0
  22. {babeldoc-0.2.22 → babeldoc-0.2.24}/.github/workflows/test.yml +0 -0
  23. {babeldoc-0.2.22 → babeldoc-0.2.24}/.gitignore +0 -0
  24. {babeldoc-0.2.22 → babeldoc-0.2.24}/.pre-commit-config.yaml +0 -0
  25. {babeldoc-0.2.22 → babeldoc-0.2.24}/LICENSE +0 -0
  26. {babeldoc-0.2.22 → babeldoc-0.2.24}/README.md +0 -0
  27. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/assets/assets.py +0 -0
  28. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/assets/embedding_assets_metadata.py +0 -0
  29. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/asynchronize/__init__.py +0 -0
  30. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/converter.py +0 -0
  31. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/document_il/__init__.py +0 -0
  32. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/document_il/babeldoc_exception/BabelDOCException.py +0 -0
  33. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/document_il/backend/__init__.py +0 -0
  34. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/document_il/frontend/__init__.py +0 -0
  35. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/document_il/frontend/il_creater.py +0 -0
  36. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/document_il/il_version_1.py +0 -0
  37. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/document_il/il_version_1.rnc +0 -0
  38. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/document_il/il_version_1.rng +0 -0
  39. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/document_il/il_version_1.xsd +0 -0
  40. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/document_il/midend/__init__.py +0 -0
  41. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/document_il/midend/add_debug_information.py +0 -0
  42. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/document_il/midend/detect_scanned_file.py +0 -0
  43. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/document_il/midend/il_translator.py +0 -0
  44. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/document_il/midend/il_translator_llm_only.py +0 -0
  45. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/document_il/midend/layout_parser.py +0 -0
  46. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/document_il/midend/remove_descent.py +0 -0
  47. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/document_il/midend/styles_and_formulas.py +0 -0
  48. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/document_il/midend/table_parser.py +0 -0
  49. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/document_il/midend/typesetting.py +0 -0
  50. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/document_il/translator/__init__.py +0 -0
  51. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/document_il/translator/cache.py +0 -0
  52. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/document_il/translator/translator.py +0 -0
  53. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/document_il/utils/atomic_integer.py +0 -0
  54. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/document_il/utils/layout_helper.py +0 -0
  55. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/document_il/utils/priority_thread_pool_executor.py +0 -0
  56. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/document_il/utils/style_helper.py +0 -0
  57. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/document_il/xml_converter.py +0 -0
  58. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/docvision/README.md +0 -0
  59. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/docvision/__init__.py +0 -0
  60. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/docvision/doclayout.py +0 -0
  61. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/docvision/rpc_doclayout.py +0 -0
  62. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/docvision/table_detection/rapidocr.py +0 -0
  63. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/pdfinterp.py +0 -0
  64. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/progress_monitor.py +0 -0
  65. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/result_merger.py +0 -0
  66. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/split_manager.py +0 -0
  67. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/tools/generate_font_metadata.py +0 -0
  68. {babeldoc-0.2.22 → babeldoc-0.2.24}/babeldoc/translation_config.py +0 -0
  69. {babeldoc-0.2.22 → babeldoc-0.2.24}/docs/CODE_OF_CONDUCT.md +0 -0
  70. {babeldoc-0.2.22 → babeldoc-0.2.24}/docs/CONTRIBUTING.md +0 -0
  71. {babeldoc-0.2.22 → babeldoc-0.2.24}/docs/CONTRIBUTOR_REWARD.md +0 -0
  72. {babeldoc-0.2.22 → babeldoc-0.2.24}/docs/ImplementationDetails/AsyncTranslate/AsyncTranslate.md +0 -0
  73. {babeldoc-0.2.22 → babeldoc-0.2.24}/docs/ImplementationDetails/ILTranslator/ILTranslator.md +0 -0
  74. {babeldoc-0.2.22 → babeldoc-0.2.24}/docs/ImplementationDetails/PDFCreation/PDFCreation.md +0 -0
  75. {babeldoc-0.2.22 → babeldoc-0.2.24}/docs/ImplementationDetails/PDFParsing/PDFParsing.md +0 -0
  76. {babeldoc-0.2.22 → babeldoc-0.2.24}/docs/ImplementationDetails/ParagraphFinding/ParagraphFinding.md +0 -0
  77. {babeldoc-0.2.22 → babeldoc-0.2.24}/docs/ImplementationDetails/README.md +0 -0
  78. {babeldoc-0.2.22 → babeldoc-0.2.24}/docs/ImplementationDetails/StylesAndFormulas/StylesAndFormulas.md +0 -0
  79. {babeldoc-0.2.22 → babeldoc-0.2.24}/docs/ImplementationDetails/Typesetting/Typesetting.md +0 -0
  80. {babeldoc-0.2.22 → babeldoc-0.2.24}/docs/README.md +0 -0
  81. {babeldoc-0.2.22 → babeldoc-0.2.24}/docs/deploy.sh +0 -0
  82. {babeldoc-0.2.22 → babeldoc-0.2.24}/docs/images/babeldoc-banner.png +0 -0
  83. {babeldoc-0.2.22 → babeldoc-0.2.24}/docs/images/babeldoc-big-logo-darkmode-with-transparent-background.png +0 -0
  84. {babeldoc-0.2.22 → babeldoc-0.2.24}/docs/images/babeldoc-big-logo-darkmode-with-transparent-background.svg +0 -0
  85. {babeldoc-0.2.22 → babeldoc-0.2.24}/docs/images/babeldoc-big-logo-with-transparent-background.png +0 -0
  86. {babeldoc-0.2.22 → babeldoc-0.2.24}/docs/images/babeldoc-big-logo-with-transparent-background.svg +0 -0
  87. {babeldoc-0.2.22 → babeldoc-0.2.24}/docs/images/babeldoc-big-logo.png +0 -0
  88. {babeldoc-0.2.22 → babeldoc-0.2.24}/docs/images/babeldoc-contributor_reward_example.png +0 -0
  89. {babeldoc-0.2.22 → babeldoc-0.2.24}/docs/images/babeldoc-preview.gif +0 -0
  90. {babeldoc-0.2.22 → babeldoc-0.2.24}/docs/images/babeldoc-small-logo-with-transparent-background.png +0 -0
  91. {babeldoc-0.2.22 → babeldoc-0.2.24}/docs/images/babeldoc-small-logo-with-transparent-background.svg +0 -0
  92. {babeldoc-0.2.22 → babeldoc-0.2.24}/docs/images/babeldoc-small-logo.png +0 -0
  93. {babeldoc-0.2.22 → babeldoc-0.2.24}/docs/index.md +0 -0
  94. {babeldoc-0.2.22 → babeldoc-0.2.24}/docs/intro-to-pdf-object.md +0 -0
  95. {babeldoc-0.2.22 → babeldoc-0.2.24}/docs/requirements.txt +0 -0
  96. {babeldoc-0.2.22 → babeldoc-0.2.24}/mkdocs.yml +0 -0
  97. {babeldoc-0.2.22 → babeldoc-0.2.24}/tests/test_translation_config.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: BabelDOC
3
- Version: 0.2.22
3
+ Version: 0.2.24
4
4
  Summary: Yet Another Document Translator
5
5
  Project-URL: Homepage, https://github.com/funstory-ai/BabelDOC
6
6
  Project-URL: Issues, https://github.com/funstory-ai/BabelDOC/issues
@@ -0,0 +1 @@
1
+ __version__ = "0.2.24"
@@ -3,7 +3,7 @@ import shutil
3
3
  import subprocess
4
4
  from pathlib import Path
5
5
 
6
- __version__ = "0.2.22"
6
+ __version__ = "0.2.24"
7
7
 
8
8
  CACHE_FOLDER = Path.home() / ".cache" / "babeldoc"
9
9
 
@@ -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
- resources_type, r_id = pdf.xref_get_key(page_xref_id, "Resources")
162
- if resources_type == "xref":
163
- resource_xref_id = re.search("(\\d+) 0 R", r_id).group(1)
164
- r_id = pdf.xref_object(int(resource_xref_id))
165
- resources_type = "dict"
166
- if resources_type == "dict":
167
- xref_id = re.search("/Font (\\d+) 0 R", r_id)
168
- if xref_id is not None:
169
- xref_id = xref_id.group(1)
170
- font_dict = pdf.xref_object(int(xref_id))
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
- search = re.search("/Font *<<(.+?)>>", r_id.replace("\n", " "))
173
- if search is None:
174
- # Have resources but no fonts
175
- return set()
176
- font_dict = search.group(1)
177
- else:
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,230 @@ class PDFCreater:
633
636
 
634
637
  return False
635
638
 
636
- def write(self, translation_config: TranslationConfig) -> TranslateResult:
637
- basename = Path(translation_config.input_file).stem
638
- debug_suffix = ".debug" if translation_config.debug else ""
639
- if translation_config.watermark_output_mode != WatermarkOutputMode.Watermarked:
640
- debug_suffix += ".no_watermark"
641
- mono_out_path = translation_config.get_output_file_path(
642
- f"{basename}{debug_suffix}.{translation_config.lang_out}.mono.pdf",
643
- )
644
- pdf = pymupdf.open(self.original_pdf_path)
645
- self.font_mapper.add_font(pdf, self.docs)
646
- with self.translation_config.progress_monitor.stage_start(
647
- self.stage_name,
648
- len(self.docs.page),
649
- ) as pbar:
650
- for page in self.docs.page:
651
- translation_config.raise_if_cancelled()
652
- xobj_available_fonts = {}
653
- xobj_draw_ops = {}
654
- xobj_encoding_length_map = {}
655
- available_font_list = self.get_available_font_list(pdf, page)
656
-
657
- for xobj in page.pdf_xobject:
658
- xobj_available_fonts[xobj.xobj_id] = available_font_list.copy()
659
- try:
660
- xobj_available_fonts[xobj.xobj_id].update(
661
- self.get_xobj_available_fonts(xobj.xref_id, pdf),
662
- )
663
- except Exception:
664
- pass
665
- xobj_encoding_length_map[xobj.xobj_id] = {
666
- f.font_id: f.encoding_length for f in xobj.pdf_font
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
- xobj_op = BitStream()
669
- xobj_op.append(xobj.base_operations.value.encode())
670
- xobj_draw_ops[xobj.xobj_id] = xobj_op
671
- page_encoding_length_map = {
672
- f.font_id: f.encoding_length for f in page.pdf_font
673
- }
674
- page_op = BitStream()
675
- # q {ops_base}Q 1 0 0 1 {x0} {y0} cm {ops_new}
676
- # page_op.append(b"q ")
677
- page_op.append(page.base_operations.value.encode())
678
- page_op.append(b" \n")
679
- # page_op.append(b" Q ")
680
- # page_op.append(
681
- # f"q Q 1 0 0 1 {page.cropbox.box.x} {page.cropbox.box.y} cm \n".encode(),
682
- # )
683
- # 收集所有字符
684
- chars = []
685
- # 首先添加页面级别的字符
686
- if page.pdf_character:
687
- chars.extend(page.pdf_character)
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
- draw_op = xobj_draw_ops[char.xobj_id]
705
- encoding_length_map = xobj_encoding_length_map[char.xobj_id]
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
- draw_op = page_op
710
- encoding_length_map = page_encoding_length_map
711
-
712
- draw_op.append(b"q ")
713
- self.render_graphic_state(draw_op, char.pdf_style.graphic_state)
714
- if char.vertical:
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[font_id]
736
+ # pdf32000-2008 page14:
737
+ # As hexadecimal data enclosed in angle brackets < >
738
+ # see 7.3.4.3, "Hexadecimal Strings."
715
739
  draw_op.append(
716
- f"BT /{font_id} {char_size:f} Tf 0 1 -1 0 {char.box.x2:f} {char.box.y:f} Tm ".encode(),
717
- )
718
- else:
719
- draw_op.append(
720
- f"BT /{font_id} {char_size:f} Tf 1 0 0 1 {char.box.x:f} {char.box.y:f} Tm ".encode(),
740
+ f"<{char.pdf_character_id:0{encoding_length * 2}x}>".upper().encode(),
721
741
  )
722
742
 
723
- encoding_length = encoding_length_map[font_id]
724
- # pdf32000-2008 page14:
725
- # As hexadecimal data enclosed in angle brackets < >
726
- # see 7.3.4.3, "Hexadecimal Strings."
727
- draw_op.append(
728
- f"<{char.pdf_character_id:0{encoding_length * 2}x}>".upper().encode(),
743
+ draw_op.append(b" Tj ET Q \n")
744
+ for xobj in page.pdf_xobject:
745
+ draw_op = xobj_draw_ops[xobj.xobj_id]
746
+ try:
747
+ pdf.update_stream(xobj.xref_id, draw_op.tobytes())
748
+ except Exception:
749
+ logger.warning(
750
+ f"update xref {xobj.xref_id} stream fail, continue"
751
+ )
752
+ # pdf.update_stream(xobj.xref_id, b'')
753
+ for rect in page.pdf_rectangle:
754
+ self._debug_render_rectangle(page_op, rect)
755
+ draw_op = page_op
756
+ op_container = pdf.get_new_xref()
757
+ # Since this is a draw instruction container,
758
+ # no additional information is needed
759
+ pdf.update_object(op_container, "<<>>")
760
+ pdf.update_stream(op_container, draw_op.tobytes())
761
+ pdf[page.page_number].set_contents(op_container)
762
+ pbar.advance()
763
+ translation_config.raise_if_cancelled()
764
+ with self.translation_config.progress_monitor.stage_start(
765
+ SUBSET_FONT_STAGE_NAME,
766
+ 1,
767
+ ) as pbar:
768
+ if not translation_config.skip_clean:
769
+ pdf = self.subset_fonts_in_subprocess(
770
+ pdf, translation_config, tag="mono"
729
771
  )
730
772
 
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
773
  pbar.advance()
751
- translation_config.raise_if_cancelled()
752
- with self.translation_config.progress_monitor.stage_start(
753
- SUBSET_FONT_STAGE_NAME,
754
- 1,
755
- ) as pbar:
756
- if not translation_config.skip_clean:
757
- pdf = self.subset_fonts_in_subprocess(
758
- pdf, translation_config, tag="mono"
759
- )
760
-
761
- pbar.advance()
762
- with self.translation_config.progress_monitor.stage_start(
763
- SAVE_PDF_STAGE_NAME,
764
- 2,
765
- ) as pbar:
766
- if not translation_config.no_mono:
767
- if translation_config.debug:
774
+ with self.translation_config.progress_monitor.stage_start(
775
+ SAVE_PDF_STAGE_NAME,
776
+ 2,
777
+ ) as pbar:
778
+ if not translation_config.no_mono:
779
+ if translation_config.debug:
780
+ translation_config.raise_if_cancelled()
781
+ pdf.save(
782
+ f"{mono_out_path}.decompressed.pdf",
783
+ expand=True,
784
+ pretty=True,
785
+ )
768
786
  translation_config.raise_if_cancelled()
769
- pdf.save(
770
- f"{mono_out_path}.decompressed.pdf",
771
- expand=True,
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,
787
+ self.save_pdf_with_timeout(
788
+ pdf,
789
+ mono_out_path,
805
790
  translation_config,
791
+ garbage=1,
792
+ deflate=True,
793
+ clean=not translation_config.skip_clean,
794
+ deflate_fonts=True,
795
+ linear=False,
796
+ tag="mono",
806
797
  )
807
- else:
808
- # Create a dual PDF with side-by-side pages (original and translation)
809
- dual = self.create_side_by_side_dual_pdf(
810
- original_pdf,
811
- translated_pdf,
812
- dual_out_path,
813
- translation_config,
798
+ pbar.advance()
799
+ dual_out_path = None
800
+ if not translation_config.no_dual:
801
+ dual_out_path = translation_config.get_output_file_path(
802
+ f"{basename}{debug_suffix}.{translation_config.lang_out}.dual.pdf",
814
803
  )
815
-
816
- if translation_config.debug:
817
804
  translation_config.raise_if_cancelled()
818
- try:
819
- dual = self.write_debug_info(dual, translation_config)
820
- except Exception:
821
- logger.warning(
822
- "Failed to write debug info to dual PDF",
823
- exc_info=True,
805
+ original_pdf = pymupdf.open(self.original_pdf_path)
806
+ translated_pdf = pdf
807
+
808
+ # Choose between alternating pages and side-by-side format
809
+ # Default to side-by-side if not specified
810
+ use_alternating_pages = (
811
+ translation_config.use_alternating_pages_dual
812
+ )
813
+
814
+ if use_alternating_pages:
815
+ # Create a dual PDF with alternating pages (original and translation)
816
+ dual = self.create_alternating_pages_dual_pdf(
817
+ self.original_pdf_path,
818
+ translated_pdf,
819
+ translation_config,
820
+ )
821
+ else:
822
+ # Create a dual PDF with side-by-side pages (original and translation)
823
+ dual = self.create_side_by_side_dual_pdf(
824
+ original_pdf,
825
+ translated_pdf,
826
+ dual_out_path,
827
+ translation_config,
824
828
  )
825
829
 
826
- self.save_pdf_with_timeout(
827
- dual,
828
- dual_out_path,
829
- translation_config,
830
- garbage=1,
831
- deflate=True,
832
- clean=not translation_config.skip_clean,
833
- deflate_fonts=True,
834
- linear=False,
835
- tag="dual",
836
- )
837
- if translation_config.debug:
838
- translation_config.raise_if_cancelled()
839
- dual.save(
840
- f"{dual_out_path}.decompressed.pdf",
841
- expand=True,
842
- pretty=True,
830
+ if translation_config.debug:
831
+ translation_config.raise_if_cancelled()
832
+ try:
833
+ dual = self.write_debug_info(dual, translation_config)
834
+ except Exception:
835
+ logger.warning(
836
+ "Failed to write debug info to dual PDF",
837
+ exc_info=True,
838
+ )
839
+
840
+ self.save_pdf_with_timeout(
841
+ dual,
842
+ dual_out_path,
843
+ translation_config,
844
+ garbage=1,
845
+ deflate=True,
846
+ clean=not translation_config.skip_clean,
847
+ deflate_fonts=True,
848
+ linear=False,
849
+ tag="dual",
843
850
  )
844
- pbar.advance()
845
- return TranslateResult(mono_out_path, dual_out_path)
851
+ if translation_config.debug:
852
+ translation_config.raise_if_cancelled()
853
+ dual.save(
854
+ f"{dual_out_path}.decompressed.pdf",
855
+ expand=True,
856
+ pretty=True,
857
+ )
858
+ pbar.advance()
859
+ return TranslateResult(mono_out_path, dual_out_path)
860
+ except Exception:
861
+ logger.exception(
862
+ "Failed to create PDF: %s",
863
+ translation_config.input_file,
864
+ )
865
+ return self.write(translation_config, True)
@@ -29,7 +29,7 @@ class ParagraphFinder:
29
29
 
30
30
  # 定义项目符号的正则表达式模式
31
31
  BULLET_POINT_PATTERN = re.compile(
32
- r"[■•⚫⬤◆◇○●◦‣⁃▪▫∗†‡¹²³⁴⁵⁶⁷⁸⁹⁰₁₂₃₄₅₆₇₈₉₀ᵃᵇᶜᵈᵉᶠᵍʰⁱʲᵏˡᵐⁿᵒᵖᵍʳˢᵗᵘᵛʷˣʸᶻ†‡§¶※⁑⁂⁕⁎⁜⁑❧☙⁋‖‽]"
32
+ r"[■•⚫⬤◆◇○●◦‣⁃▪▫∗†‡¹²³⁴⁵⁶⁷⁸⁹⁰₁₂₃₄₅₆₇₈₉₀ᵃᵇᶜᵈᵉᶠᵍʰⁱʲᵏˡᵐⁿᵒᵖᵍʳˢᵗᵘᵛʷˣʸᶻ†‡§¶※⁑⁂⁕⁎⁜⁑❧☙⁋‖‽]"
33
33
  )
34
34
 
35
35
  def __init__(self, translation_config: TranslationConfig):
@@ -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")
@@ -646,11 +646,18 @@ def _do_translate_single(
646
646
  )
647
647
  mono_watermark_first_page_doc_bytes = None
648
648
  dual_watermark_first_page_doc_bytes = None
649
-
650
- if translation_config.watermark_output_mode == WatermarkOutputMode.Both:
651
- mono_watermark_first_page_doc_bytes, dual_watermark_first_page_doc_bytes = (
652
- generate_first_page_with_watermark(doc_pdf2zh, translation_config, docs)
649
+ try:
650
+ if translation_config.watermark_output_mode == WatermarkOutputMode.Both:
651
+ mono_watermark_first_page_doc_bytes, dual_watermark_first_page_doc_bytes = (
652
+ generate_first_page_with_watermark(doc_pdf2zh, translation_config, docs)
653
+ )
654
+ except Exception:
655
+ logger.warning(
656
+ "Failed to generate watermark for first page, using no watermark"
653
657
  )
658
+ translation_config.watermark_output_mode = WatermarkOutputMode.NoWatermark
659
+ mono_watermark_first_page_doc_bytes = None
660
+ dual_watermark_first_page_doc_bytes = None
654
661
 
655
662
  Typesetting(translation_config).typsetting_document(docs)
656
663
  logger.debug(f"finish typsetting from {temp_pdf_path}")
@@ -23,7 +23,7 @@ from babeldoc.translation_config import TranslationConfig
23
23
  from babeldoc.translation_config import WatermarkOutputMode
24
24
 
25
25
  logger = logging.getLogger(__name__)
26
- __version__ = "0.2.22"
26
+ __version__ = "0.2.24"
27
27
 
28
28
 
29
29
  def create_parser():
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "BabelDOC"
3
- version = "0.2.22"
3
+ version = "0.2.24"
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.22"
151
+ current_version = "0.2.24"
152
152
  version_pattern = "MAJOR.MINOR.PATCH[.PYTAGNUM]"
153
153
 
154
154
  [bumpver.file_patterns]
@@ -48,7 +48,7 @@ wheels = [
48
48
 
49
49
  [[package]]
50
50
  name = "babeldoc"
51
- version = "0.2.22"
51
+ version = "0.2.24"
52
52
  source = { editable = "." }
53
53
  dependencies = [
54
54
  { name = "bitstring" },
@@ -1 +0,0 @@
1
- __version__ = "0.2.22"
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes