BabelDOC 0.1.25__tar.gz → 0.1.26__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.26}/PKG-INFO +5 -1
- {babeldoc-0.1.25 → babeldoc-0.1.26}/README.md +4 -0
- babeldoc-0.1.26/babeldoc/__init__.py +1 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/const.py +1 -1
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/document_il/backend/pdf_creater.py +6 -2
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/document_il/midend/typesetting.py +6 -1
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/docvision/doclayout.py +1 -1
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/high_level.py +107 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/main.py +20 -6
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/progress_monitor.py +33 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/translation_config.py +54 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/pyproject.toml +2 -2
- {babeldoc-0.1.25 → babeldoc-0.1.26}/uv.lock +1 -1
- babeldoc-0.1.25/babeldoc/__init__.py +0 -1
- {babeldoc-0.1.25 → babeldoc-0.1.26}/.cursorignore +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/.github/ISSUE_TEMPLATE/bug_report.yaml +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/.github/ISSUE_TEMPLATE/feature_request.yaml +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/.github/dependabot.yml +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/.github/labels.yml +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/.github/release-drafter.yml +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/.github/workflows/codeql.yml +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/.github/workflows/docs.yml +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/.github/workflows/labeler.yml +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/.github/workflows/publish-to-pypi.yml +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/.github/workflows/test.yml +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/.gitignore +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/.pre-commit-config.yaml +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/LICENSE +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/assets/assets.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/assets/embedding_assets_metadata.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/asynchronize/__init__.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/converter.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/document_il/__init__.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/document_il/babeldoc_exception/BabelDOCException.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/document_il/backend/__init__.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/document_il/frontend/__init__.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/document_il/frontend/il_creater.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/document_il/il_version_1.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/document_il/il_version_1.rnc +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/document_il/il_version_1.rng +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/document_il/il_version_1.xsd +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/document_il/midend/__init__.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/document_il/midend/add_debug_information.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/document_il/midend/detect_scanned_file.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/document_il/midend/il_translator.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/document_il/midend/layout_parser.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/document_il/midend/paragraph_finder.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/document_il/midend/remove_descent.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/document_il/midend/styles_and_formulas.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/document_il/translator/__init__.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/document_il/translator/cache.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/document_il/translator/translator.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/document_il/utils/fontmap.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/document_il/utils/layout_helper.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/document_il/utils/style_helper.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/document_il/xml_converter.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/docvision/README.md +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/docvision/__init__.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/docvision/rpc_doclayout.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/pdfinterp.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/tools/generate_font_metadata.py +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/docs/CODE_OF_CONDUCT.md +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/docs/CONTRIBUTING.md +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/docs/CONTRIBUTOR_REWARD.md +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/docs/ImplementationDetails/AsyncTranslate/AsyncTranslate.md +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/docs/ImplementationDetails/ILTranslator/ILTranslator.md +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/docs/ImplementationDetails/PDFCreation/PDFCreation.md +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/docs/ImplementationDetails/PDFParsing/PDFParsing.md +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/docs/ImplementationDetails/ParagraphFinding/ParagraphFinding.md +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/docs/ImplementationDetails/README.md +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/docs/ImplementationDetails/StylesAndFormulas/StylesAndFormulas.md +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/docs/ImplementationDetails/Typesetting/Typesetting.md +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/docs/README.md +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/docs/deploy.sh +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/docs/images/babeldoc-banner.png +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/docs/images/babeldoc-big-logo-darkmode-with-transparent-background.png +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/docs/images/babeldoc-big-logo-darkmode-with-transparent-background.svg +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/docs/images/babeldoc-big-logo-with-transparent-background.png +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/docs/images/babeldoc-big-logo-with-transparent-background.svg +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/docs/images/babeldoc-big-logo.png +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/docs/images/babeldoc-contributor_reward_example.png +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/docs/images/babeldoc-preview.gif +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/docs/images/babeldoc-small-logo-with-transparent-background.png +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/docs/images/babeldoc-small-logo-with-transparent-background.svg +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/docs/images/babeldoc-small-logo.png +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/docs/index.md +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/docs/intro-to-pdf-object.md +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/docs/requirements.txt +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/mkdocs.yml +0 -0
- {babeldoc-0.1.25 → babeldoc-0.1.26}/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.26
|
|
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
|
|
@@ -157,6 +157,8 @@ uv run babeldoc --bing --files example.pdf --files example2.pdf
|
|
|
157
157
|
- `--disable-rich-text-translate`: Disable rich text translation (may help improve compatibility with some PDFs)
|
|
158
158
|
- `--enhance-compatibility`: Enable all compatibility enhancement options (equivalent to --skip-clean --dual-translate-first --disable-rich-text-translate)
|
|
159
159
|
- `--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.
|
|
160
|
+
- `--watermark-output-mode`: Control watermark output mode: 'watermarked' (default) adds watermark to translated PDF, 'no_watermark' doesn't add watermark, 'both' outputs both versions.
|
|
161
|
+
- `--no-watermark`: [DEPRECATED] Use --watermark-output-mode=no_watermark instead.
|
|
160
162
|
|
|
161
163
|
> [!TIP]
|
|
162
164
|
> - Both `--skip-clean` and `--dual-translate-first` may help improve compatibility with some PDF readers
|
|
@@ -262,6 +264,8 @@ skip-clean = false
|
|
|
262
264
|
dual-translate-first = false
|
|
263
265
|
disable-rich-text-translate = false
|
|
264
266
|
use-alternating-pages-dual = false
|
|
267
|
+
watermark-output-mode = "watermarked" # Choices: "watermarked", "no_watermark", "both"
|
|
268
|
+
# no-watermark = false # DEPRECATED: Use watermark-output-mode instead
|
|
265
269
|
|
|
266
270
|
# Output control
|
|
267
271
|
no-dual = false
|
|
@@ -122,6 +122,8 @@ uv run babeldoc --bing --files example.pdf --files example2.pdf
|
|
|
122
122
|
- `--disable-rich-text-translate`: Disable rich text translation (may help improve compatibility with some PDFs)
|
|
123
123
|
- `--enhance-compatibility`: Enable all compatibility enhancement options (equivalent to --skip-clean --dual-translate-first --disable-rich-text-translate)
|
|
124
124
|
- `--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.
|
|
125
|
+
- `--watermark-output-mode`: Control watermark output mode: 'watermarked' (default) adds watermark to translated PDF, 'no_watermark' doesn't add watermark, 'both' outputs both versions.
|
|
126
|
+
- `--no-watermark`: [DEPRECATED] Use --watermark-output-mode=no_watermark instead.
|
|
125
127
|
|
|
126
128
|
> [!TIP]
|
|
127
129
|
> - Both `--skip-clean` and `--dual-translate-first` may help improve compatibility with some PDF readers
|
|
@@ -227,6 +229,8 @@ skip-clean = false
|
|
|
227
229
|
dual-translate-first = false
|
|
228
230
|
disable-rich-text-translate = false
|
|
229
231
|
use-alternating-pages-dual = false
|
|
232
|
+
watermark-output-mode = "watermarked" # Choices: "watermarked", "no_watermark", "both"
|
|
233
|
+
# no-watermark = false # DEPRECATED: Use watermark-output-mode instead
|
|
230
234
|
|
|
231
235
|
# Output control
|
|
232
236
|
no-dual = false
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.26"
|
|
@@ -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
|
)
|
|
@@ -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:
|
|
@@ -192,7 +192,7 @@ class OnnxModel(DocLayoutModel):
|
|
|
192
192
|
boxes[..., :4] = (boxes[..., :4] - [pad_x, pad_y, pad_x, pad_y]) / gain
|
|
193
193
|
return boxes
|
|
194
194
|
|
|
195
|
-
def predict(self, image, imgsz=
|
|
195
|
+
def predict(self, image, imgsz=800, batch_size=16, **kwargs):
|
|
196
196
|
"""
|
|
197
197
|
Predict the layout of document pages.
|
|
198
198
|
|
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
import copy
|
|
2
3
|
import hashlib
|
|
4
|
+
import io
|
|
3
5
|
import logging
|
|
6
|
+
import pathlib
|
|
4
7
|
import threading
|
|
5
8
|
import time
|
|
6
9
|
from asyncio import CancelledError
|
|
@@ -19,6 +22,7 @@ from babeldoc import asynchronize
|
|
|
19
22
|
from babeldoc.assets.assets import warmup
|
|
20
23
|
from babeldoc.const import CACHE_FOLDER
|
|
21
24
|
from babeldoc.converter import TranslateConverter
|
|
25
|
+
from babeldoc.document_il import il_version_1
|
|
22
26
|
from babeldoc.document_il.backend.pdf_creater import SAVE_PDF_STAGE_NAME
|
|
23
27
|
from babeldoc.document_il.backend.pdf_creater import SUBSET_FONT_STAGE_NAME
|
|
24
28
|
from babeldoc.document_il.backend.pdf_creater import PDFCreater
|
|
@@ -37,6 +41,7 @@ from babeldoc.pdfinterp import PDFPageInterpreterEx
|
|
|
37
41
|
from babeldoc.progress_monitor import ProgressMonitor
|
|
38
42
|
from babeldoc.translation_config import TranslateResult
|
|
39
43
|
from babeldoc.translation_config import TranslationConfig
|
|
44
|
+
from babeldoc.translation_config import WatermarkOutputMode
|
|
40
45
|
|
|
41
46
|
logger = logging.getLogger(__name__)
|
|
42
47
|
|
|
@@ -365,6 +370,13 @@ def do_translate(pm, translation_config):
|
|
|
365
370
|
docs,
|
|
366
371
|
translation_config.get_working_file_path("add_debug_information.json"),
|
|
367
372
|
)
|
|
373
|
+
mono_watermark_first_page_doc_bytes = None
|
|
374
|
+
dual_watermark_first_page_doc_bytes = None
|
|
375
|
+
|
|
376
|
+
if translation_config.watermark_output_mode == WatermarkOutputMode.Both:
|
|
377
|
+
mono_watermark_first_page_doc_bytes, dual_watermark_first_page_doc_bytes = (
|
|
378
|
+
generate_first_page_with_watermark(doc_input, translation_config, docs)
|
|
379
|
+
)
|
|
368
380
|
|
|
369
381
|
Typesetting(translation_config).typsetting_document(docs)
|
|
370
382
|
logger.debug(f"finish typsetting from {temp_pdf_path}")
|
|
@@ -377,6 +389,22 @@ def do_translate(pm, translation_config):
|
|
|
377
389
|
# docs2 = xml_converter.deepcopy(docs)
|
|
378
390
|
pdf_creater = PDFCreater(temp_pdf_path, docs, translation_config)
|
|
379
391
|
result = pdf_creater.write(translation_config)
|
|
392
|
+
|
|
393
|
+
if mono_watermark_first_page_doc_bytes:
|
|
394
|
+
mono_watermark_pdf = merge_watermark_doc(
|
|
395
|
+
result.mono_pdf_path,
|
|
396
|
+
mono_watermark_first_page_doc_bytes,
|
|
397
|
+
translation_config,
|
|
398
|
+
)
|
|
399
|
+
result.mono_pdf_path = mono_watermark_pdf
|
|
400
|
+
if dual_watermark_first_page_doc_bytes:
|
|
401
|
+
dual_watermark_pdf = merge_watermark_doc(
|
|
402
|
+
result.dual_pdf_path,
|
|
403
|
+
dual_watermark_first_page_doc_bytes,
|
|
404
|
+
translation_config,
|
|
405
|
+
)
|
|
406
|
+
result.dual_pdf_path = dual_watermark_pdf
|
|
407
|
+
|
|
380
408
|
finish_time = time.time()
|
|
381
409
|
result.original_pdf_path = original_pdf_path
|
|
382
410
|
result.total_seconds = finish_time - start_time
|
|
@@ -395,6 +423,85 @@ def do_translate(pm, translation_config):
|
|
|
395
423
|
translation_config.cleanup_temp_files()
|
|
396
424
|
|
|
397
425
|
|
|
426
|
+
def generate_first_page_with_watermark(
|
|
427
|
+
mupdf: Document,
|
|
428
|
+
translation_config: TranslationConfig,
|
|
429
|
+
doc_il: il_version_1.Document,
|
|
430
|
+
) -> (io.BytesIO, io.BytesIO):
|
|
431
|
+
first_page_doc = Document()
|
|
432
|
+
first_page_doc.insert_pdf(mupdf, from_page=0, to_page=0)
|
|
433
|
+
|
|
434
|
+
il_only_first_page_doc = il_version_1.Document()
|
|
435
|
+
il_only_first_page_doc.total_pages = 1
|
|
436
|
+
il_only_first_page_doc.page = [copy.deepcopy(doc_il.page[0])]
|
|
437
|
+
|
|
438
|
+
watermarked_config = copy.copy(translation_config)
|
|
439
|
+
watermarked_config.watermark_output_mode = WatermarkOutputMode.Watermarked
|
|
440
|
+
watermarked_config.progress_monitor.disable = True
|
|
441
|
+
watermarked_temp_pdf_path = watermarked_config.get_working_file_path(
|
|
442
|
+
"watermarked_temp_input.pdf"
|
|
443
|
+
)
|
|
444
|
+
first_page_doc.save(watermarked_temp_pdf_path)
|
|
445
|
+
|
|
446
|
+
Typesetting(watermarked_config).typsetting_document(il_only_first_page_doc)
|
|
447
|
+
pdf_creater = PDFCreater(
|
|
448
|
+
watermarked_temp_pdf_path.as_posix(), il_only_first_page_doc, watermarked_config
|
|
449
|
+
)
|
|
450
|
+
result = pdf_creater.write(watermarked_config)
|
|
451
|
+
watermarked_config.progress_monitor.disable = False
|
|
452
|
+
mono_pdf_bytes = None
|
|
453
|
+
dual_pdf_bytes = None
|
|
454
|
+
if result.mono_pdf_path:
|
|
455
|
+
mono_pdf_bytes = io.BytesIO()
|
|
456
|
+
with Path(result.mono_pdf_path).open("rb") as f:
|
|
457
|
+
mono_pdf_bytes.write(f.read())
|
|
458
|
+
result.mono_pdf_path.unlink()
|
|
459
|
+
mono_pdf_bytes.seek(0)
|
|
460
|
+
|
|
461
|
+
if result.dual_pdf_path:
|
|
462
|
+
dual_pdf_bytes = io.BytesIO()
|
|
463
|
+
with Path(result.dual_pdf_path).open("rb") as f:
|
|
464
|
+
dual_pdf_bytes.write(f.read())
|
|
465
|
+
result.dual_pdf_path.unlink()
|
|
466
|
+
dual_pdf_bytes.seek(0)
|
|
467
|
+
|
|
468
|
+
return mono_pdf_bytes, dual_pdf_bytes
|
|
469
|
+
|
|
470
|
+
|
|
471
|
+
def merge_watermark_doc(
|
|
472
|
+
no_watermark_pdf_path: pathlib.PosixPath,
|
|
473
|
+
watermark_first_page_pdf_bytes: io.BytesIO,
|
|
474
|
+
translation_config: TranslationConfig,
|
|
475
|
+
) -> pathlib.PosixPath:
|
|
476
|
+
if not no_watermark_pdf_path.exists():
|
|
477
|
+
raise FileNotFoundError(
|
|
478
|
+
f"no_watermark_pdf_path not found: {no_watermark_pdf_path}"
|
|
479
|
+
)
|
|
480
|
+
if not watermark_first_page_pdf_bytes:
|
|
481
|
+
raise FileNotFoundError(
|
|
482
|
+
f"watermark_first_page_pdf_bytes not found: {watermark_first_page_pdf_bytes}"
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
no_watermark_pdf = Document(no_watermark_pdf_path.as_posix())
|
|
486
|
+
no_watermark_pdf.delete_page(0)
|
|
487
|
+
|
|
488
|
+
watermark_first_page_pdf = Document("pdf", watermark_first_page_pdf_bytes)
|
|
489
|
+
no_watermark_pdf.insert_pdf(
|
|
490
|
+
watermark_first_page_pdf, from_page=0, to_page=0, start_at=0
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
new_save_path = no_watermark_pdf_path.with_name(
|
|
494
|
+
no_watermark_pdf_path.name.replace(".no_watermark", "")
|
|
495
|
+
)
|
|
496
|
+
|
|
497
|
+
PDFCreater.save_pdf_with_timeout(
|
|
498
|
+
no_watermark_pdf,
|
|
499
|
+
new_save_path.as_posix(),
|
|
500
|
+
translation_config=translation_config,
|
|
501
|
+
)
|
|
502
|
+
return new_save_path
|
|
503
|
+
|
|
504
|
+
|
|
398
505
|
def download_font_assets():
|
|
399
506
|
warmup()
|
|
400
507
|
|
|
@@ -21,9 +21,10 @@ from babeldoc.document_il.translator.translator import set_translate_rate_limite
|
|
|
21
21
|
from babeldoc.docvision.doclayout import DocLayoutModel
|
|
22
22
|
from babeldoc.docvision.rpc_doclayout import RpcDocLayoutModel
|
|
23
23
|
from babeldoc.translation_config import TranslationConfig
|
|
24
|
+
from babeldoc.translation_config import WatermarkOutputMode
|
|
24
25
|
|
|
25
26
|
logger = logging.getLogger(__name__)
|
|
26
|
-
__version__ = "0.1.
|
|
27
|
+
__version__ = "0.1.26"
|
|
27
28
|
|
|
28
29
|
|
|
29
30
|
def create_parser():
|
|
@@ -169,6 +170,18 @@ def create_parser():
|
|
|
169
170
|
action="store_true",
|
|
170
171
|
help="Use alternating pages mode for dual PDF. When enabled, original and translated pages are arranged in alternate order.",
|
|
171
172
|
)
|
|
173
|
+
translation_group.add_argument(
|
|
174
|
+
"--watermark-output-mode",
|
|
175
|
+
type=str,
|
|
176
|
+
choices=["watermarked", "no_watermark", "both"],
|
|
177
|
+
default="watermarked",
|
|
178
|
+
help="Control watermark output mode: 'watermarked' (default) adds watermark to translated PDF, 'no_watermark' doesn't add watermark, 'both' outputs both versions.",
|
|
179
|
+
)
|
|
180
|
+
translation_group.add_argument(
|
|
181
|
+
"--no-watermark",
|
|
182
|
+
action="store_true",
|
|
183
|
+
help="[DEPRECATED] Use --watermark-output-mode=no_watermark instead. Do not add watermark to the translated PDF.",
|
|
184
|
+
)
|
|
172
185
|
translation_group.add_argument(
|
|
173
186
|
"--report-interval",
|
|
174
187
|
type=float,
|
|
@@ -336,6 +349,11 @@ async def main():
|
|
|
336
349
|
use_alternating_pages_dual=args.use_alternating_pages_dual,
|
|
337
350
|
report_interval=args.report_interval,
|
|
338
351
|
min_text_length=args.min_text_length,
|
|
352
|
+
watermark_output_mode=(
|
|
353
|
+
WatermarkOutputMode.NoWatermark
|
|
354
|
+
if args.no_watermark
|
|
355
|
+
else getattr(WatermarkOutputMode, args.watermark_output_mode.title())
|
|
356
|
+
),
|
|
339
357
|
)
|
|
340
358
|
|
|
341
359
|
# Create progress handler
|
|
@@ -349,11 +367,7 @@ async def main():
|
|
|
349
367
|
logger.debug(event)
|
|
350
368
|
if event["type"] == "finish":
|
|
351
369
|
result = event["translate_result"]
|
|
352
|
-
logger.info(
|
|
353
|
-
logger.info(f" Original PDF: {result.original_pdf_path}")
|
|
354
|
-
logger.info(f" Time Cost: {result.total_seconds:.2f}s")
|
|
355
|
-
logger.info(f" Mono PDF: {result.mono_pdf_path or 'None'}")
|
|
356
|
-
logger.info(f" Dual PDF: {result.dual_pdf_path or 'None'}")
|
|
370
|
+
logger.info(str(result))
|
|
357
371
|
break
|
|
358
372
|
|
|
359
373
|
|
|
@@ -35,6 +35,7 @@ class ProgressMonitor:
|
|
|
35
35
|
self.finish_event = finish_event
|
|
36
36
|
self.cancel_event = cancel_event
|
|
37
37
|
self.loop = loop
|
|
38
|
+
self.disable = False
|
|
38
39
|
if finish_event and not loop:
|
|
39
40
|
raise ValueError("finish_event requires a loop")
|
|
40
41
|
if self.progress_change_callback:
|
|
@@ -51,6 +52,8 @@ class ProgressMonitor:
|
|
|
51
52
|
self.lock = threading.Lock()
|
|
52
53
|
|
|
53
54
|
def stage_start(self, stage_name: str, total: int):
|
|
55
|
+
if self.disable:
|
|
56
|
+
return DummyTranslationStage(stage_name, total, self, 0)
|
|
54
57
|
stage = self.stage[stage_name]
|
|
55
58
|
stage.run_time += 1
|
|
56
59
|
stage.name = stage_name
|
|
@@ -78,6 +81,8 @@ class ProgressMonitor:
|
|
|
78
81
|
logger.debug("ProgressMonitor __exit__")
|
|
79
82
|
|
|
80
83
|
def on_finish(self):
|
|
84
|
+
if self.disable:
|
|
85
|
+
return
|
|
81
86
|
if self.cancel_event:
|
|
82
87
|
self.cancel_event.set()
|
|
83
88
|
if self.finish_event and self.loop:
|
|
@@ -86,6 +91,8 @@ class ProgressMonitor:
|
|
|
86
91
|
self.finish_callback(type="error", error=CancelledError)
|
|
87
92
|
|
|
88
93
|
def stage_done(self, stage):
|
|
94
|
+
if self.disable:
|
|
95
|
+
return
|
|
89
96
|
self.last_report_time = 0.0
|
|
90
97
|
self.finish_stage_count += 1
|
|
91
98
|
if (
|
|
@@ -128,6 +135,8 @@ class ProgressMonitor:
|
|
|
128
135
|
return progress
|
|
129
136
|
|
|
130
137
|
def stage_update(self, stage, n: int):
|
|
138
|
+
if self.disable:
|
|
139
|
+
return
|
|
131
140
|
with self.lock:
|
|
132
141
|
report_time_delta = time.time() - self.last_report_time
|
|
133
142
|
if report_time_delta < self.report_interval and stage.total > 3:
|
|
@@ -144,10 +153,14 @@ class ProgressMonitor:
|
|
|
144
153
|
self.last_report_time = time.time()
|
|
145
154
|
|
|
146
155
|
def translate_done(self, translate_result):
|
|
156
|
+
if self.disable:
|
|
157
|
+
return
|
|
147
158
|
if self.finish_callback:
|
|
148
159
|
self.finish_callback(type="finish", translate_result=translate_result)
|
|
149
160
|
|
|
150
161
|
def translate_error(self, error):
|
|
162
|
+
if self.disable:
|
|
163
|
+
return
|
|
151
164
|
if self.finish_callback:
|
|
152
165
|
self.finish_callback(type="error", error=error)
|
|
153
166
|
|
|
@@ -156,6 +169,8 @@ class ProgressMonitor:
|
|
|
156
169
|
raise asyncio.CancelledError
|
|
157
170
|
|
|
158
171
|
def cancel(self):
|
|
172
|
+
if self.disable:
|
|
173
|
+
return
|
|
159
174
|
if self.cancel_event:
|
|
160
175
|
logger.info("Translation canceled")
|
|
161
176
|
self.cancel_event.set()
|
|
@@ -180,3 +195,21 @@ class TranslationStage:
|
|
|
180
195
|
def advance(self, n: int = 1):
|
|
181
196
|
self.current += n
|
|
182
197
|
self.pm.stage_update(self, n)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
class DummyTranslationStage:
|
|
201
|
+
def __init__(self, name: str, total: int, pm: ProgressMonitor, weight: float):
|
|
202
|
+
self.name = name
|
|
203
|
+
self.display_name = name
|
|
204
|
+
self.current = 0
|
|
205
|
+
self.total = total
|
|
206
|
+
self.pm = pm
|
|
207
|
+
|
|
208
|
+
def __enter__(self):
|
|
209
|
+
return self
|
|
210
|
+
|
|
211
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
212
|
+
pass
|
|
213
|
+
|
|
214
|
+
def advance(self, n: int = 1):
|
|
215
|
+
pass
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import enum
|
|
1
2
|
import logging
|
|
2
3
|
import shutil
|
|
3
4
|
import tempfile
|
|
@@ -12,6 +13,12 @@ from babeldoc.progress_monitor import ProgressMonitor
|
|
|
12
13
|
logger = logging.getLogger(__name__)
|
|
13
14
|
|
|
14
15
|
|
|
16
|
+
class WatermarkOutputMode(enum.Enum):
|
|
17
|
+
Watermarked = "watermarked"
|
|
18
|
+
NoWatermark = "no_watermark"
|
|
19
|
+
Both = "both"
|
|
20
|
+
|
|
21
|
+
|
|
15
22
|
class TranslationConfig:
|
|
16
23
|
def __init__(
|
|
17
24
|
self,
|
|
@@ -43,6 +50,7 @@ class TranslationConfig:
|
|
|
43
50
|
min_text_length: int = 5,
|
|
44
51
|
use_side_by_side_dual: bool = True, # Deprecated: 是否使用拼版式双语 PDF(并排显示原文和译文) 向下兼容选项,已停用。
|
|
45
52
|
use_alternating_pages_dual: bool = False,
|
|
53
|
+
watermark_output_mode: WatermarkOutputMode = WatermarkOutputMode.Watermarked,
|
|
46
54
|
):
|
|
47
55
|
self.translator = translator
|
|
48
56
|
|
|
@@ -55,6 +63,7 @@ class TranslationConfig:
|
|
|
55
63
|
self.pages = pages
|
|
56
64
|
self.page_ranges = self._parse_pages(pages) if pages else None
|
|
57
65
|
self.debug = debug
|
|
66
|
+
self.watermark_output_mode = watermark_output_mode
|
|
58
67
|
|
|
59
68
|
self.output_dir = output_dir
|
|
60
69
|
self.working_dir = working_dir
|
|
@@ -175,7 +184,52 @@ class TranslateResult:
|
|
|
175
184
|
total_seconds: float
|
|
176
185
|
mono_pdf_path: str | None
|
|
177
186
|
dual_pdf_path: str | None
|
|
187
|
+
no_watermark_mono_pdf_path: str | None
|
|
188
|
+
no_watermark_dual_pdf_path: str | None
|
|
178
189
|
|
|
179
190
|
def __init__(self, mono_pdf_path: str | None, dual_pdf_path: str | None):
|
|
180
191
|
self.mono_pdf_path = mono_pdf_path
|
|
181
192
|
self.dual_pdf_path = dual_pdf_path
|
|
193
|
+
|
|
194
|
+
# For compatibility considerations, if only a non-watermarked PDF is generated,
|
|
195
|
+
# the values of mono_pdf_path and no_watermark_mono_pdf_path are the same.
|
|
196
|
+
self.no_watermark_mono_pdf_path = mono_pdf_path
|
|
197
|
+
self.no_watermark_dual_pdf_path = dual_pdf_path
|
|
198
|
+
|
|
199
|
+
def __str__(self):
|
|
200
|
+
"""Return a human-readable string representation of the translation result."""
|
|
201
|
+
result = []
|
|
202
|
+
if hasattr(self, "original_pdf_path") and self.original_pdf_path:
|
|
203
|
+
result.append(f"\tOriginal PDF: {self.original_pdf_path}")
|
|
204
|
+
|
|
205
|
+
if hasattr(self, "total_seconds") and self.total_seconds:
|
|
206
|
+
result.append(f"\tTotal time: {self.total_seconds:.2f} seconds")
|
|
207
|
+
|
|
208
|
+
if self.mono_pdf_path:
|
|
209
|
+
result.append(f"\tMonolingual PDF: {self.mono_pdf_path}")
|
|
210
|
+
|
|
211
|
+
if self.dual_pdf_path:
|
|
212
|
+
result.append(f"\tDual-language PDF: {self.dual_pdf_path}")
|
|
213
|
+
|
|
214
|
+
if (
|
|
215
|
+
hasattr(self, "no_watermark_mono_pdf_path")
|
|
216
|
+
and self.no_watermark_mono_pdf_path
|
|
217
|
+
and self.no_watermark_mono_pdf_path != self.mono_pdf_path
|
|
218
|
+
):
|
|
219
|
+
result.append(
|
|
220
|
+
f"\tNo-watermark Monolingual PDF: {self.no_watermark_mono_pdf_path}"
|
|
221
|
+
)
|
|
222
|
+
|
|
223
|
+
if (
|
|
224
|
+
hasattr(self, "no_watermark_dual_pdf_path")
|
|
225
|
+
and self.no_watermark_dual_pdf_path
|
|
226
|
+
and self.no_watermark_dual_pdf_path != self.dual_pdf_path
|
|
227
|
+
):
|
|
228
|
+
result.append(
|
|
229
|
+
f"\tNo-watermark Dual-language PDF: {self.no_watermark_dual_pdf_path}"
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
if result:
|
|
233
|
+
result.insert(0, "Translation results:")
|
|
234
|
+
|
|
235
|
+
return "\n".join(result) if result else "No translation results available"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "BabelDOC"
|
|
3
|
-
version = "0.1.
|
|
3
|
+
version = "0.1.26"
|
|
4
4
|
description = "Yet Another Document Translator"
|
|
5
5
|
license = "AGPL-3.0"
|
|
6
6
|
readme = "README.md"
|
|
@@ -133,7 +133,7 @@ pythonpath = [".", "src"]
|
|
|
133
133
|
testpaths = ["tests"]
|
|
134
134
|
|
|
135
135
|
[bumpver]
|
|
136
|
-
current_version = "0.1.
|
|
136
|
+
current_version = "0.1.26"
|
|
137
137
|
version_pattern = "MAJOR.MINOR.PATCH[.PYTAGNUM]"
|
|
138
138
|
|
|
139
139
|
[bumpver.file_patterns]
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.1.25"
|
|
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
|
|
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
|
|
File without changes
|
{babeldoc-0.1.25 → babeldoc-0.1.26}/babeldoc/document_il/babeldoc_exception/BabelDOCException.py
RENAMED
|
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
|
|
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
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{babeldoc-0.1.25 → babeldoc-0.1.26}/docs/ImplementationDetails/AsyncTranslate/AsyncTranslate.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{babeldoc-0.1.25 → babeldoc-0.1.26}/docs/ImplementationDetails/ParagraphFinding/ParagraphFinding.md
RENAMED
|
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
|
{babeldoc-0.1.25 → babeldoc-0.1.26}/docs/images/babeldoc-big-logo-with-transparent-background.png
RENAMED
|
File without changes
|
{babeldoc-0.1.25 → babeldoc-0.1.26}/docs/images/babeldoc-big-logo-with-transparent-background.svg
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{babeldoc-0.1.25 → babeldoc-0.1.26}/docs/images/babeldoc-small-logo-with-transparent-background.png
RENAMED
|
File without changes
|
{babeldoc-0.1.25 → babeldoc-0.1.26}/docs/images/babeldoc-small-logo-with-transparent-background.svg
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|