BabelDOC 0.1.25__tar.gz → 0.1.27__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.1.25 → babeldoc-0.1.27}/PKG-INFO +7 -2
- {babeldoc-0.1.25 → babeldoc-0.1.27}/README.md +6 -1
- babeldoc-0.1.27/babeldoc/__init__.py +1 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/const.py +1 -1
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/backend/pdf_creater.py +6 -2
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/midend/il_translator.py +2 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/midend/layout_parser.py +33 -61
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/midend/paragraph_finder.py +2 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/midend/typesetting.py +6 -1
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/translator/translator.py +50 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/docvision/doclayout.py +55 -13
- babeldoc-0.1.27/babeldoc/docvision/rpc_doclayout.py +304 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/high_level.py +107 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/main.py +49 -8
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/progress_monitor.py +33 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/translation_config.py +54 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/pyproject.toml +2 -2
- {babeldoc-0.1.25 → babeldoc-0.1.27}/uv.lock +1 -1
- babeldoc-0.1.25/babeldoc/__init__.py +0 -1
- babeldoc-0.1.25/babeldoc/docvision/rpc_doclayout.py +0 -176
- {babeldoc-0.1.25 → babeldoc-0.1.27}/.cursorignore +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/.github/ISSUE_TEMPLATE/bug_report.yaml +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/.github/ISSUE_TEMPLATE/feature_request.yaml +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/.github/dependabot.yml +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/.github/labels.yml +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/.github/release-drafter.yml +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/.github/workflows/codeql.yml +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/.github/workflows/docs.yml +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/.github/workflows/labeler.yml +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/.github/workflows/publish-to-pypi.yml +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/.github/workflows/test.yml +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/.gitignore +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/.pre-commit-config.yaml +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/LICENSE +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/assets/assets.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/assets/embedding_assets_metadata.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/asynchronize/__init__.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/converter.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/__init__.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/babeldoc_exception/BabelDOCException.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/backend/__init__.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/frontend/__init__.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/frontend/il_creater.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/il_version_1.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/il_version_1.rnc +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/il_version_1.rng +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/il_version_1.xsd +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/midend/__init__.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/midend/add_debug_information.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/midend/detect_scanned_file.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/midend/remove_descent.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/midend/styles_and_formulas.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/translator/__init__.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/translator/cache.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/utils/fontmap.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/utils/layout_helper.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/utils/style_helper.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/document_il/xml_converter.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/docvision/README.md +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/docvision/__init__.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/pdfinterp.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/babeldoc/tools/generate_font_metadata.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/CODE_OF_CONDUCT.md +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/CONTRIBUTING.md +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/CONTRIBUTOR_REWARD.md +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/ImplementationDetails/AsyncTranslate/AsyncTranslate.md +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/ImplementationDetails/ILTranslator/ILTranslator.md +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/ImplementationDetails/PDFCreation/PDFCreation.md +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/ImplementationDetails/PDFParsing/PDFParsing.md +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/ImplementationDetails/ParagraphFinding/ParagraphFinding.md +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/ImplementationDetails/README.md +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/ImplementationDetails/StylesAndFormulas/StylesAndFormulas.md +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/ImplementationDetails/Typesetting/Typesetting.md +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/README.md +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/deploy.sh +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/images/babeldoc-banner.png +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/images/babeldoc-big-logo-darkmode-with-transparent-background.png +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/images/babeldoc-big-logo-darkmode-with-transparent-background.svg +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/images/babeldoc-big-logo-with-transparent-background.png +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/images/babeldoc-big-logo-with-transparent-background.svg +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/images/babeldoc-big-logo.png +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/images/babeldoc-contributor_reward_example.png +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/images/babeldoc-preview.gif +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/images/babeldoc-small-logo-with-transparent-background.png +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/images/babeldoc-small-logo-with-transparent-background.svg +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/images/babeldoc-small-logo.png +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/index.md +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/intro-to-pdf-object.md +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/docs/requirements.txt +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/mkdocs.yml +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.27}/tests/test_translation_config.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: BabelDOC
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.27
|
|
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
|
|
@@ -65,7 +65,8 @@ Description-Content-Type: text/markdown
|
|
|
65
65
|
|
|
66
66
|
PDF scientific paper translation and bilingual comparison library.
|
|
67
67
|
|
|
68
|
-
- Beta version launched [Immersive Translate - BabelDOC](https://app.immersivetranslate.com/babel-doc/) 1000 free pages per month.
|
|
68
|
+
- **Online Service**: Beta version launched [Immersive Translate - BabelDOC](https://app.immersivetranslate.com/babel-doc/) 1000 free pages per month.
|
|
69
|
+
- **Self-deployment**: [PDFMathTranslate](https://github.com/Byaidu/PDFMathTranslate) 1.9.3+ Experimental support for BabelDOC, available for self-deployment + WebUI with more translation services.
|
|
69
70
|
- Provides a simple [command line interface](#getting-started).
|
|
70
71
|
- Provides a [Python API](#python-api).
|
|
71
72
|
- Mainly designed to be embedded into other programs, but can also be used directly for simple translation tasks.
|
|
@@ -157,6 +158,8 @@ uv run babeldoc --bing --files example.pdf --files example2.pdf
|
|
|
157
158
|
- `--disable-rich-text-translate`: Disable rich text translation (may help improve compatibility with some PDFs)
|
|
158
159
|
- `--enhance-compatibility`: Enable all compatibility enhancement options (equivalent to --skip-clean --dual-translate-first --disable-rich-text-translate)
|
|
159
160
|
- `--use-alternating-pages-dual`: Use alternating pages mode for dual PDF. When enabled, original and translated pages are arranged in alternate order. When disabled (default), original and translated pages are shown side by side on the same page.
|
|
161
|
+
- `--watermark-output-mode`: Control watermark output mode: 'watermarked' (default) adds watermark to translated PDF, 'no_watermark' doesn't add watermark, 'both' outputs both versions.
|
|
162
|
+
- `--no-watermark`: [DEPRECATED] Use --watermark-output-mode=no_watermark instead.
|
|
160
163
|
|
|
161
164
|
> [!TIP]
|
|
162
165
|
> - Both `--skip-clean` and `--dual-translate-first` may help improve compatibility with some PDF readers
|
|
@@ -262,6 +265,8 @@ skip-clean = false
|
|
|
262
265
|
dual-translate-first = false
|
|
263
266
|
disable-rich-text-translate = false
|
|
264
267
|
use-alternating-pages-dual = false
|
|
268
|
+
watermark-output-mode = "watermarked" # Choices: "watermarked", "no_watermark", "both"
|
|
269
|
+
# no-watermark = false # DEPRECATED: Use watermark-output-mode instead
|
|
265
270
|
|
|
266
271
|
# Output control
|
|
267
272
|
no-dual = false
|
|
@@ -30,7 +30,8 @@
|
|
|
30
30
|
|
|
31
31
|
PDF scientific paper translation and bilingual comparison library.
|
|
32
32
|
|
|
33
|
-
- Beta version launched [Immersive Translate - BabelDOC](https://app.immersivetranslate.com/babel-doc/) 1000 free pages per month.
|
|
33
|
+
- **Online Service**: Beta version launched [Immersive Translate - BabelDOC](https://app.immersivetranslate.com/babel-doc/) 1000 free pages per month.
|
|
34
|
+
- **Self-deployment**: [PDFMathTranslate](https://github.com/Byaidu/PDFMathTranslate) 1.9.3+ Experimental support for BabelDOC, available for self-deployment + WebUI with more translation services.
|
|
34
35
|
- Provides a simple [command line interface](#getting-started).
|
|
35
36
|
- Provides a [Python API](#python-api).
|
|
36
37
|
- Mainly designed to be embedded into other programs, but can also be used directly for simple translation tasks.
|
|
@@ -122,6 +123,8 @@ uv run babeldoc --bing --files example.pdf --files example2.pdf
|
|
|
122
123
|
- `--disable-rich-text-translate`: Disable rich text translation (may help improve compatibility with some PDFs)
|
|
123
124
|
- `--enhance-compatibility`: Enable all compatibility enhancement options (equivalent to --skip-clean --dual-translate-first --disable-rich-text-translate)
|
|
124
125
|
- `--use-alternating-pages-dual`: Use alternating pages mode for dual PDF. When enabled, original and translated pages are arranged in alternate order. When disabled (default), original and translated pages are shown side by side on the same page.
|
|
126
|
+
- `--watermark-output-mode`: Control watermark output mode: 'watermarked' (default) adds watermark to translated PDF, 'no_watermark' doesn't add watermark, 'both' outputs both versions.
|
|
127
|
+
- `--no-watermark`: [DEPRECATED] Use --watermark-output-mode=no_watermark instead.
|
|
125
128
|
|
|
126
129
|
> [!TIP]
|
|
127
130
|
> - Both `--skip-clean` and `--dual-translate-first` may help improve compatibility with some PDF readers
|
|
@@ -227,6 +230,8 @@ skip-clean = false
|
|
|
227
230
|
dual-translate-first = false
|
|
228
231
|
disable-rich-text-translate = false
|
|
229
232
|
use-alternating-pages-dual = false
|
|
233
|
+
watermark-output-mode = "watermarked" # Choices: "watermarked", "no_watermark", "both"
|
|
234
|
+
# no-watermark = false # DEPRECATED: Use watermark-output-mode instead
|
|
230
235
|
|
|
231
236
|
# Output control
|
|
232
237
|
no-dual = false
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.27"
|
|
@@ -12,6 +12,7 @@ from babeldoc.document_il import il_version_1
|
|
|
12
12
|
from babeldoc.document_il.utils.fontmap import FontMapper
|
|
13
13
|
from babeldoc.translation_config import TranslateResult
|
|
14
14
|
from babeldoc.translation_config import TranslationConfig
|
|
15
|
+
from babeldoc.translation_config import WatermarkOutputMode
|
|
15
16
|
|
|
16
17
|
logger = logging.getLogger(__name__)
|
|
17
18
|
|
|
@@ -416,8 +417,9 @@ class PDFCreater:
|
|
|
416
417
|
pdf = self.subset_fonts_in_subprocess(pdf, translation_config, tag="debug")
|
|
417
418
|
return pdf
|
|
418
419
|
|
|
420
|
+
@staticmethod
|
|
419
421
|
def subset_fonts_in_subprocess(
|
|
420
|
-
|
|
422
|
+
pdf: pymupdf.Document, translation_config: TranslationConfig, tag: str
|
|
421
423
|
) -> pymupdf.Document:
|
|
422
424
|
"""Run font subsetting in a subprocess with timeout.
|
|
423
425
|
|
|
@@ -489,8 +491,8 @@ class PDFCreater:
|
|
|
489
491
|
)
|
|
490
492
|
return original_pdf
|
|
491
493
|
|
|
494
|
+
@staticmethod
|
|
492
495
|
def save_pdf_with_timeout(
|
|
493
|
-
self,
|
|
494
496
|
pdf: pymupdf.Document,
|
|
495
497
|
output_path: str,
|
|
496
498
|
translation_config: TranslationConfig,
|
|
@@ -634,6 +636,8 @@ class PDFCreater:
|
|
|
634
636
|
def write(self, translation_config: TranslationConfig) -> TranslateResult:
|
|
635
637
|
basename = Path(translation_config.input_file).stem
|
|
636
638
|
debug_suffix = ".debug" if translation_config.debug else ""
|
|
639
|
+
if translation_config.watermark_output_mode != WatermarkOutputMode.Watermarked:
|
|
640
|
+
debug_suffix += ".no_watermark"
|
|
637
641
|
mono_out_path = translation_config.get_output_file_path(
|
|
638
642
|
f"{basename}{debug_suffix}.{translation_config.lang_out}.mono.pdf",
|
|
639
643
|
)
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import concurrent.futures
|
|
2
2
|
import json
|
|
3
3
|
import logging
|
|
4
|
+
import re
|
|
4
5
|
from pathlib import Path
|
|
5
6
|
|
|
6
7
|
from tqdm import tqdm
|
|
@@ -555,6 +556,7 @@ class ILTranslator:
|
|
|
555
556
|
return
|
|
556
557
|
|
|
557
558
|
translated_text = self.translate_engine.translate(text)
|
|
559
|
+
translated_text = re.sub(r"[. 。…]{20,}", ".", translated_text)
|
|
558
560
|
|
|
559
561
|
tracker.set_output(translated_text)
|
|
560
562
|
|
|
@@ -110,71 +110,43 @@ class LayoutParser:
|
|
|
110
110
|
def process(self, docs: il_version_1.Document, mupdf_doc: Document):
|
|
111
111
|
"""Generate layouts for all pages that need to be translated."""
|
|
112
112
|
# Get pages that need to be translated
|
|
113
|
-
|
|
114
|
-
page
|
|
115
|
-
for page in docs.page
|
|
116
|
-
if self.translation_config.should_translate_page(page.page_number + 1)
|
|
117
|
-
]
|
|
118
|
-
total = len(pages_to_translate)
|
|
113
|
+
total = len(docs.page)
|
|
119
114
|
with self.translation_config.progress_monitor.stage_start(
|
|
120
115
|
self.stage_name,
|
|
121
116
|
total,
|
|
122
117
|
) as progress:
|
|
123
|
-
# Process
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
pix = mupdf_doc[page.page_number].get_pixmap(
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
# Get predictions for the batch
|
|
141
|
-
layouts_batch = self.model.predict(batch_images, batch_size=batch_size)
|
|
142
|
-
|
|
143
|
-
# Process predictions for each page
|
|
144
|
-
for page, layouts in zip(batch_pages, layouts_batch, strict=False):
|
|
145
|
-
page_layouts = []
|
|
146
|
-
self._save_debug_image(
|
|
147
|
-
batch_images[batch_pages.index(page)],
|
|
148
|
-
layouts,
|
|
149
|
-
page.page_number + 1,
|
|
118
|
+
# Process predictions for each page
|
|
119
|
+
for page, layouts in self.model.handle_document(
|
|
120
|
+
docs.page, mupdf_doc, self.translation_config, self._save_debug_image
|
|
121
|
+
):
|
|
122
|
+
page_layouts = []
|
|
123
|
+
for layout in layouts.boxes:
|
|
124
|
+
# Convert coordinate system from picture to il
|
|
125
|
+
# system to the il coordinate system
|
|
126
|
+
x0, y0, x1, y1 = layout.xyxy
|
|
127
|
+
pix = mupdf_doc[page.page_number].get_pixmap()
|
|
128
|
+
h, w = pix.height, pix.width
|
|
129
|
+
x0, y0, x1, y1 = (
|
|
130
|
+
np.clip(int(x0 - 1), 0, w - 1),
|
|
131
|
+
np.clip(int(h - y1 - 1), 0, h - 1),
|
|
132
|
+
np.clip(int(x1 + 1), 0, w - 1),
|
|
133
|
+
np.clip(int(h - y0 + 1), 0, h - 1),
|
|
150
134
|
)
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
page_layout = il_version_1.PageLayout(
|
|
164
|
-
id=len(page_layouts) + 1,
|
|
165
|
-
box=il_version_1.Box(
|
|
166
|
-
x0.item(),
|
|
167
|
-
y0.item(),
|
|
168
|
-
x1.item(),
|
|
169
|
-
y1.item(),
|
|
170
|
-
),
|
|
171
|
-
conf=layout.conf.item(),
|
|
172
|
-
class_name=layouts.names[layout.cls],
|
|
173
|
-
)
|
|
174
|
-
page_layouts.append(page_layout)
|
|
135
|
+
page_layout = il_version_1.PageLayout(
|
|
136
|
+
id=len(page_layouts) + 1,
|
|
137
|
+
box=il_version_1.Box(
|
|
138
|
+
x0.item(),
|
|
139
|
+
y0.item(),
|
|
140
|
+
x1.item(),
|
|
141
|
+
y1.item(),
|
|
142
|
+
),
|
|
143
|
+
conf=layout.conf.item(),
|
|
144
|
+
class_name=layouts.names[layout.cls],
|
|
145
|
+
)
|
|
146
|
+
page_layouts.append(page_layout)
|
|
175
147
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
148
|
+
page.page_layout = page_layouts
|
|
149
|
+
self._save_debug_box_to_page(page)
|
|
150
|
+
progress.advance(1)
|
|
179
151
|
|
|
180
|
-
|
|
152
|
+
return docs
|
|
@@ -244,6 +244,7 @@ class ParagraphFinder:
|
|
|
244
244
|
def is_text_layout(self, layout: Layout):
|
|
245
245
|
return layout is not None and layout.name in [
|
|
246
246
|
"plain text",
|
|
247
|
+
"tiny text",
|
|
247
248
|
"title",
|
|
248
249
|
"abandon",
|
|
249
250
|
"figure_caption",
|
|
@@ -312,6 +313,7 @@ class ParagraphFinder:
|
|
|
312
313
|
"figure_caption",
|
|
313
314
|
"abandon",
|
|
314
315
|
"plain text",
|
|
316
|
+
"tiny text",
|
|
315
317
|
"title",
|
|
316
318
|
]
|
|
317
319
|
char_box = char.box
|
|
@@ -15,6 +15,7 @@ from babeldoc.document_il import PdfStyle
|
|
|
15
15
|
from babeldoc.document_il import il_version_1
|
|
16
16
|
from babeldoc.document_il.utils.fontmap import FontMapper
|
|
17
17
|
from babeldoc.translation_config import TranslationConfig
|
|
18
|
+
from babeldoc.translation_config import WatermarkOutputMode
|
|
18
19
|
|
|
19
20
|
logger = logging.getLogger(__name__)
|
|
20
21
|
|
|
@@ -450,7 +451,11 @@ class Typesetting:
|
|
|
450
451
|
fonts[xobj.xobj_id] = page_fonts.copy()
|
|
451
452
|
for font in xobj.pdf_font:
|
|
452
453
|
fonts[xobj.xobj_id][font.font_id] = font
|
|
453
|
-
if
|
|
454
|
+
if (
|
|
455
|
+
page.page_number == 0
|
|
456
|
+
and self.translation_config.watermark_output_mode
|
|
457
|
+
== WatermarkOutputMode.Watermarked
|
|
458
|
+
):
|
|
454
459
|
self.add_watermark(page)
|
|
455
460
|
# 开始实际的渲染过程
|
|
456
461
|
for paragraph in page.pdf_paragraph:
|
|
@@ -8,6 +8,7 @@ import unicodedata
|
|
|
8
8
|
from abc import ABC
|
|
9
9
|
from abc import abstractmethod
|
|
10
10
|
|
|
11
|
+
import httpx
|
|
11
12
|
import openai
|
|
12
13
|
import requests
|
|
13
14
|
|
|
@@ -271,3 +272,52 @@ class OpenAITranslator(BaseTranslator):
|
|
|
271
272
|
|
|
272
273
|
def get_rich_text_right_placeholder(self, placeholder_id: int):
|
|
273
274
|
return self.get_formular_placeholder(placeholder_id + 1)
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
class TranslateTranslator(BaseTranslator):
|
|
278
|
+
# https://github.com/openai/openai-python
|
|
279
|
+
name = "openai"
|
|
280
|
+
|
|
281
|
+
def __init__(
|
|
282
|
+
self,
|
|
283
|
+
lang_in,
|
|
284
|
+
lang_out,
|
|
285
|
+
url=None,
|
|
286
|
+
ignore_cache=False,
|
|
287
|
+
):
|
|
288
|
+
super().__init__(lang_in, lang_out, ignore_cache)
|
|
289
|
+
self.client = httpx.Client()
|
|
290
|
+
self.url = url
|
|
291
|
+
|
|
292
|
+
def do_translate(self, text) -> str:
|
|
293
|
+
response = self.client.post(
|
|
294
|
+
self.url,
|
|
295
|
+
json={
|
|
296
|
+
"text": [text],
|
|
297
|
+
"src": "Englih",
|
|
298
|
+
"tgt": "Simplifed Chinese",
|
|
299
|
+
},
|
|
300
|
+
)
|
|
301
|
+
return response.json()["text"][0]
|
|
302
|
+
|
|
303
|
+
def prompt(self, text):
|
|
304
|
+
return [
|
|
305
|
+
{
|
|
306
|
+
"role": "system",
|
|
307
|
+
"content": "You are a professional,authentic machine translation engine.",
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
"role": "user",
|
|
311
|
+
"content": f";; Treat next line as plain text input and translate it into {self.lang_out}, output translation ONLY. If translation is unnecessary (e.g. proper nouns, codes, {'{{1}}, etc. '}), return the original text. NO explanations. NO notes. Input:\n\n{text}",
|
|
312
|
+
},
|
|
313
|
+
]
|
|
314
|
+
|
|
315
|
+
def get_formular_placeholder(self, placeholder_id: int):
|
|
316
|
+
return "{{v" + str(placeholder_id) + "}}"
|
|
317
|
+
return "{{" + str(placeholder_id) + "}}"
|
|
318
|
+
|
|
319
|
+
def get_rich_text_left_placeholder(self, placeholder_id: int):
|
|
320
|
+
return self.get_formular_placeholder(placeholder_id)
|
|
321
|
+
|
|
322
|
+
def get_rich_text_right_placeholder(self, placeholder_id: int):
|
|
323
|
+
return self.get_formular_placeholder(placeholder_id + 1)
|
|
@@ -2,12 +2,15 @@ import abc
|
|
|
2
2
|
import ast
|
|
3
3
|
import logging
|
|
4
4
|
import platform
|
|
5
|
+
from collections.abc import Generator
|
|
5
6
|
|
|
6
7
|
import cv2
|
|
7
8
|
import numpy as np
|
|
8
9
|
import onnx
|
|
9
10
|
import onnxruntime
|
|
11
|
+
import pymupdf
|
|
10
12
|
|
|
13
|
+
import babeldoc.document_il.il_version_1
|
|
11
14
|
from babeldoc.assets.assets import get_doclayout_onnx_model_path
|
|
12
15
|
|
|
13
16
|
# from huggingface_hub import hf_hub_download
|
|
@@ -15,6 +18,19 @@ from babeldoc.assets.assets import get_doclayout_onnx_model_path
|
|
|
15
18
|
logger = logging.getLogger(__name__)
|
|
16
19
|
|
|
17
20
|
|
|
21
|
+
class YoloResult:
|
|
22
|
+
"""Helper class to store detection results from ONNX model."""
|
|
23
|
+
|
|
24
|
+
def __init__(self, names, boxes=None, boxes_data=None):
|
|
25
|
+
if boxes is not None:
|
|
26
|
+
self.boxes = boxes
|
|
27
|
+
else:
|
|
28
|
+
assert boxes_data is not None
|
|
29
|
+
self.boxes = [YoloBox(data=d) for d in boxes_data]
|
|
30
|
+
self.boxes.sort(key=lambda x: x.conf, reverse=True)
|
|
31
|
+
self.names = names
|
|
32
|
+
|
|
33
|
+
|
|
18
34
|
class DocLayoutModel(abc.ABC):
|
|
19
35
|
@staticmethod
|
|
20
36
|
def load_onnx():
|
|
@@ -42,18 +58,19 @@ class DocLayoutModel(abc.ABC):
|
|
|
42
58
|
**kwargs: Additional arguments.
|
|
43
59
|
"""
|
|
44
60
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
61
|
+
@abc.abstractmethod
|
|
62
|
+
def handle_document(
|
|
63
|
+
self,
|
|
64
|
+
pages: list[babeldoc.document_il.il_version_1.Page],
|
|
65
|
+
mupdf_doc: pymupdf.Document,
|
|
66
|
+
translate_config,
|
|
67
|
+
save_debug_image,
|
|
68
|
+
) -> Generator[
|
|
69
|
+
tuple[babeldoc.document_il.il_version_1.Page, YoloResult], None, None
|
|
70
|
+
]:
|
|
71
|
+
"""
|
|
72
|
+
Handle a document.
|
|
73
|
+
"""
|
|
57
74
|
|
|
58
75
|
|
|
59
76
|
class YoloBox:
|
|
@@ -192,7 +209,7 @@ class OnnxModel(DocLayoutModel):
|
|
|
192
209
|
boxes[..., :4] = (boxes[..., :4] - [pad_x, pad_y, pad_x, pad_y]) / gain
|
|
193
210
|
return boxes
|
|
194
211
|
|
|
195
|
-
def predict(self, image, imgsz=
|
|
212
|
+
def predict(self, image, imgsz=800, batch_size=16, **kwargs):
|
|
196
213
|
"""
|
|
197
214
|
Predict the layout of document pages.
|
|
198
215
|
|
|
@@ -254,3 +271,28 @@ class OnnxModel(DocLayoutModel):
|
|
|
254
271
|
results.append(YoloResult(boxes_data=preds, names=self._names))
|
|
255
272
|
|
|
256
273
|
return results
|
|
274
|
+
|
|
275
|
+
def handle_document(
|
|
276
|
+
self,
|
|
277
|
+
pages: list[babeldoc.document_il.il_version_1.Page],
|
|
278
|
+
mupdf_doc: pymupdf.Document,
|
|
279
|
+
translate_config,
|
|
280
|
+
save_debug_image,
|
|
281
|
+
) -> Generator[
|
|
282
|
+
tuple[babeldoc.document_il.il_version_1.Page, YoloResult], None, None
|
|
283
|
+
]:
|
|
284
|
+
for page in pages:
|
|
285
|
+
translate_config.raise_if_cancelled()
|
|
286
|
+
pix = mupdf_doc[page.page_number].get_pixmap(dpi=72)
|
|
287
|
+
image = np.fromstring(pix.samples, np.uint8).reshape(
|
|
288
|
+
pix.height,
|
|
289
|
+
pix.width,
|
|
290
|
+
3,
|
|
291
|
+
)[:, :, ::-1]
|
|
292
|
+
predict_result = self.predict(image)[0]
|
|
293
|
+
save_debug_image(
|
|
294
|
+
image,
|
|
295
|
+
predict_result,
|
|
296
|
+
page.page_number + 1,
|
|
297
|
+
)
|
|
298
|
+
yield page, predict_result
|