[EMNLP 2025 Demo] PDF scientific paper translation with preserved formats - 基于 AI 完整保留排版的 PDF 文档全文双语翻译,支持 Google/DeepL/Ollama/OpenAI 等服务,提供 CLI/GUI/MCP/Docker/Zotero
This report presents the forensic synthetic code analysis of PDFMathTranslate/PDFMathTranslate, a Python project with 35,558 GitHub stars. SynthScan v2.0 examined 11,455 lines of code across 56 source files, recording 128 pattern matches distributed across 11 syntactic categories. The overall adjusted score of 15.0 places this repository in the Moderate AI signal band.
The scanner applied 160+ deterministic lexical heuristics, multi-line block detectors, abstract syntax tree depth profilers, and a cross-file Jaccard similarity matrix to construct a statistically normalised synthetic code estimate. All matches are individually weighted by severity coefficient and contextual multiplier before summation, and the resulting headline score is temporally discounted to account for the repository's development history relative to the commercial emergence of large language model coding tooling (November 2022 onward).
This chart maps the temporal evolution of the adjusted synthetic code score across successive scan runs. An upward trajectory indicates ongoing incorporation of AI-generated code or expanding LLM-assisted scaffolding; a stable or declining trajectory may reflect active human refactoring, code removal, or the adoption of stricter authorship policies. The dashed secondary line (right axis) independently tracks total raw pattern hit count, which can diverge from the normalised score when codebase size changes significantly between scans.
Classifies detected patterns by their diagnostic confidence and structural impact. CRITICAL patterns (coefficient 10) represent definitive synthetic signatures — hallucinated imports, explicit LLM attribution metadata — virtually never produced by human authors. HIGH (5) indicates strong structural tells such as cross-file repetition or cross-linguistic idioms. MEDIUM (2) covers recognisable conversational padding and AI-specific vocabulary. LOW (1) captures subtle indicators like tautological comments and generic boilerplate that require density to carry independent signal.
This horizontal bar chart decomposes the repository's raw synthetic code score by top-level directory, allowing you to pinpoint precisely which modules or components carry the highest AI authorship density. Directories with disproportionately high scores relative to their size warrant targeted manual review: concentrated AI signatures often trace back to mass-generated configuration layers, auto-ported test suites, LLM-scaffolded boilerplate classes, or entire subsystems authored under heavy copilot assistance. Use this view to prioritise your human code-review effort.
The scanner identified 128 distinct pattern matches across 11 syntactic categories. Each entry below represents a discrete location in the source code where the engine recorded a statistically significant AI authorship indicator. Expand any category row to inspect the individual file paths, line numbers, code snippets, and the lexical context (CODE, COMMENT, or STRING) in which each match was detected.
Reading the findings table: The Severity column indicates the diagnostic confidence level (CRITICAL / HIGH / MEDIUM / LOW). The Context column identifies whether the match occurred inside executable code, an inline comment, or a string literal — comment-context matches receive a ×1.5 weight because LLMs systematically over-annotate. The ⚡ bolt icon marks clustered matches: three or more patterns within a 10-line window, each receiving an additional ×1.5 density multiplier as dense clusters constitute far stronger evidence of synthetic authorship than isolated hits.
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | test/test_translator.py | 52 | def test_add_cache_impact_parameters(self): | CODE |
| LOW | test/test_translator.py | 80 | def test_base_translator_throw(self): | CODE |
| LOW | test/test_translator.py | 94 | def test_missing_base_url_raises_error(self): | CODE |
| LOW | test/test_translator.py | 103 | def test_missing_model_raises_error(self): | CODE |
| LOW | test/test_translator.py | 116 | def test_initialization_with_valid_envs(self): | CODE |
| LOW | test/test_translator.py | 135 | def test_default_api_key_fallback(self): | CODE |
| LOW⚡ | test/test_kernel.py | 319 | def test_legacy_version_matches_package(self): | CODE |
| LOW⚡ | test/test_kernel.py | 326 | def test_experimental_version_is_string(self): | CODE |
| LOW⚡ | test/test_kernel.py | 334 | def test_experimental_version_semver_when_available(self): | CODE |
| LOW | test/test_kernel.py | 17 | def test_translate_request_defaults(self): | CODE |
| LOW | test/test_kernel.py | 29 | def test_translate_result_defaults(self): | CODE |
| LOW | test/test_kernel.py | 37 | def test_translate_result_with_paths(self): | CODE |
| LOW | test/test_kernel.py | 73 | def test_get_nonexistent_raises(self): | CODE |
| LOW | test/test_kernel.py | 79 | def test_switch_and_active_name(self): | CODE |
| LOW | test/test_kernel.py | 90 | def test_switch_unavailable_raises(self): | CODE |
| LOW | test/test_kernel.py | 101 | def test_available_filters_correctly(self): | CODE |
| LOW | test/test_kernel.py | 149 | def test_is_available_without_venv(self): | CODE |
| LOW | test/test_kernel.py | 198 | def test_cli_args_vfont_vchar(self): | CODE |
| LOW | test/test_kernel.py | 226 | def test_cli_args_ignore_cache(self): | CODE |
| LOW | test/test_kernel.py | 245 | def test_cli_args_service_model_split(self): | CODE |
| LOW | test/test_kernel.py | 253 | def test_service_name_mapping(self): | CODE |
| LOW | test/test_kernel.py | 266 | def test_request_to_env_maps_envs(self): | CODE |
| LOW | test/test_kernel.py | 282 | def test_request_to_env_service_model(self): | CODE |
| LOW | test/test_kernel.py | 351 | def test_translate_single_file(self, mock_model_inst, mock_onnx, mock_translate): | CODE |
| LOW | test/test_kernel.py | 370 | def test_translate_passes_all_params( | CODE |
| LOW | test/test_kernel.py | 405 | def test_translate_with_prompt_wraps_template( | CODE |
| LOW | test/test_kernel.py | 434 | def test_translate_single_file(self, mock_venv, mock_avail, mock_popen): | CODE |
| LOW | test/test_kernel.py | 464 | def test_translate_subprocess_failure_raises(self, mock_venv, mock_popen): | CODE |
| LOW | test/test_kernel.py | 488 | def test_translate_progress_callback(self, mock_venv, mock_avail, mock_popen): | CODE |
| LOW | test/test_kernel.py | 531 | def test_all_services_produce_cli_flag(self): | CODE |
| LOW | test/test_kernel.py | 601 | def test_output_defaults_to_input_parent(self): | CODE |
| LOW | test/test_kernel.py | 611 | def test_output_resolved_to_absolute_path(self): | CODE |
| LOW | test/test_kernel.py | 623 | def test_explicit_output_resolved_to_absolute(self): | CODE |
| LOW | test/test_kernel.py | 643 | def test_cli_fast_mode_routes_through_kernel( | CODE |
| LOW | test/test_kernel.py | 664 | def test_cli_precise_mode_routes_through_kernel( | CODE |
| LOW | test/test_kernel.py | 685 | def test_cli_service_model_syntax_passed_through( | CODE |
| LOW | test/test_doclayout.py | 31 | def test_resize_and_pad_image(self): | CODE |
| LOW | test/test_converter.py | 56 | def test_translator_initialization(self): | CODE |
| LOW | test/test_converter.py | 83 | def test_receive_layout_with_complex_formula(self): | CODE |
| LOW | test/test_converter.py | 98 | def test_invalid_translation_service(self): | CODE |
| LOW | test/test_cli.py | 16 | def test_importing_package_does_not_eagerly_load_translation_pipeline(self): | CODE |
| LOW | test/test_cli.py | 22 | def test_version_flag_exits_before_loading_heavy_modules(self): | CODE |
| LOW | test/test_cache.py | 90 | def test_consistent_param_serialization(self): | CODE |
| LOW | test/test_cache.py | 122 | def test_cache_with_sorted_params(self): | CODE |
| LOW | pdf2zh/config.py | 62 | def _remove_circular_references(self, obj, seen=None): | CODE |
| LOW | pdf2zh/config.py | 155 | def get_env_by_translatername(cls, translater_name, name, default=None): | CODE |
| LOW⚡ | pdf2zh/pdf2zh.py | 231 | def find_all_files_in_directory(directory_path): | CODE |
| LOW | pdf2zh/translator.py | 35 | def remove_control_characters(s): | CODE |
| LOW | pdf2zh/translator.py | 81 | def add_cache_impact_parameters(self, k: str, v): | CODE |
| LOW | pdf2zh/translator.py | 154 | def get_rich_text_left_placeholder(self, id: int): | CODE |
| LOW | pdf2zh/translator.py | 157 | def get_rich_text_right_placeholder(self, id: int): | CODE |
| LOW | pdf2zh/translator.py | 496 | def get_rich_text_left_placeholder(self, id: int): | CODE |
| LOW | pdf2zh/translator.py | 499 | def get_rich_text_right_placeholder(self, id: int): | CODE |
| LOW | pdf2zh/kernel/v2_worker.py | 22 | def _redirect_stdout_to_stderr(): | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | docs/PROXY_CONFIGURATION.md | 22 | "GROK_API_KEY": "your-api-key", | CODE |
| HIGH | docs/PROXY_CONFIGURATION.md | 56 | "OPENAILIKED_API_KEY": "your-api-key", | CODE |
| HIGH | docs/PROXY_CONFIGURATION.md | 70 | "OPENAI_API_KEY": "your-api-key", | CODE |
| HIGH | docs/PROXY_CONFIGURATION.md | 86 | -H "Authorization: Bearer your-api-key" | CODE |
| HIGH | docs/PROXY_CONFIGURATION.md | 168 | export GROK_API_KEY="your-api-key" | CODE |
| HIGH | docs/PROXY_CONFIGURATION.md | 189 | "GROK_API_KEY": "your-api-key", | CODE |
| HIGH | docs/PROXY_CONFIGURATION.md | 198 | "OPENAILIKED_API_KEY": "your-api-key", | CODE |
| HIGH | docs/ADVANCED.md | 251 | "GROK_API_KEY": "your-api-key", | CODE |
| HIGH | docs/ADVANCED.md | 310 | "GROK_API_KEY": "your-api-key", | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | pdf2zh/pdfinterp.py | 244 | except Exception: | CODE |
| LOW | pdf2zh/high_level.py | 228 | except Exception: | CODE |
| LOW | pdf2zh/high_level.py | 353 | except Exception as e: | CODE |
| LOW | pdf2zh/high_level.py | 390 | except Exception: | CODE |
| LOW | pdf2zh/pdf2zh.py | 317 | except Exception: | CODE |
| LOW | pdf2zh/pdf2zh.py | 413 | except Exception: | CODE |
| LOW | pdf2zh/cache.py | 89 | except Exception as e: | STRING |
| LOW | pdf2zh/converter.py | 419 | except Exception: | CODE |
| LOW | pdf2zh/converter.py | 429 | except Exception: | CODE |
| LOW | pdf2zh/translator.py | 130 | except Exception: | CODE |
| LOW | pdf2zh/translator.py | 395 | except Exception as e: | CODE |
| LOW | pdf2zh/translator.py | 917 | except Exception: | CODE |
| MEDIUM | pdf2zh/translator.py | 617 | def do_translate(self, text) -> str: | CODE |
| LOW | pdf2zh/gui.py | 192 | except Exception: # filename from url | CODE |
| MEDIUM | pdf2zh/gui.py | 838 | print(f"Error: File '{file_path[1]}' not found.") | STRING |
| MEDIUM | pdf2zh/gui.py | 845 | print(f"Error: File '{file_path[0]}' not found.") | STRING |
| LOW | pdf2zh/gui.py | 902 | except Exception: | CODE |
| LOW | pdf2zh/kernel/v2_worker.py | 92 | except Exception as e: | CODE |
| LOW | pdf2zh/kernel/precise.py | 56 | except Exception: | CODE |
| LOW | pdf2zh/kernel/precise.py | 112 | except Exception: | CODE |
| LOW | pdf2zh/kernel/precise.py | 172 | except Exception: | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | pdf2zh/doclayout.py | 21 | logger = logging.getLogger(__name__) | CODE |
| LOW | pdf2zh/doclayout.py | 32 | def set_backend(name: str) -> None: | CODE |
| LOW | pdf2zh/high_level.py | 36 | logger = logging.getLogger(__name__) | CODE |
| LOW | pdf2zh/pdf2zh.py | 18 | logger = logging.getLogger(__name__) | CODE |
| LOW | pdf2zh/cache.py | 9 | logger = logging.getLogger(__name__) | CODE |
| LOW | pdf2zh/__init__.py | 7 | __all__ = ["translate", "translate_stream"] | CODE |
| LOW | pdf2zh/converter_docx.py | 9 | logger = logging.getLogger(__name__) | CODE |
| LOW | pdf2zh/translator.py | 32 | logger = logging.getLogger(__name__) | CODE |
| LOW | pdf2zh/gui.py | 52 | logger = logging.getLogger(__name__) | CODE |
| LOW | pdf2zh/kernel/legacy.py | 12 | logger = logging.getLogger(__name__) | CODE |
| LOW | pdf2zh/kernel/__init__.py | 12 | __all__ = ["KernelRegistry"] | CODE |
| LOW | pdf2zh/kernel/precise.py | 17 | logger = logging.getLogger(__name__) | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | pdf2zh/pdfinterp.py | 67 | CODE | |
| LOW | pdf2zh/pdfinterp.py | 301 | CODE | |
| LOW | pdf2zh/config.py | 155 | CODE | |
| LOW | pdf2zh/high_level.py | 72 | CODE | |
| LOW | pdf2zh/high_level.py | 171 | CODE | |
| LOW | pdf2zh/high_level.py | 304 | CODE | |
| LOW | pdf2zh/converter.py | 170 | CODE | |
| LOW | pdf2zh/gui.py | 172 | CODE | |
| LOW | pdf2zh/kernel/v2_worker.py | 36 | CODE | |
| LOW | pdf2zh/kernel/precise.py | 122 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | test/test_kernel.py | 336 | CODE | |
| LOW | pdf2zh/pdf2zh.py | 6 | CODE | |
| LOW | pdf2zh/translator.py | 926 | CODE | |
| LOW | pdf2zh/kernel/v2_bridge.py | 8 | CODE | |
| LOW | pdf2zh/kernel/legacy.py | 3 | CODE | |
| LOW | pdf2zh/kernel/registry.py | 3 | CODE | |
| LOW | pdf2zh/kernel/protocol.py | 3 | CODE | |
| LOW | pdf2zh/kernel/v2_worker.py | 13 | CODE | |
| LOW | pdf2zh/kernel/precise.py | 3 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| MEDIUM | test/test_doclayout.py | 32 | # Create a dummy image (100x200) | COMMENT |
| MEDIUM | test/test_doclayout.py | 62 | # Create a dummy image | COMMENT |
| MEDIUM | pdf2zh/gui.py | 567 | # The following code creates the GUI | STRING |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW⚡ | pdf2zh/pdf2zh.py | 238 | # Check if the provided path is a directory | COMMENT |
| LOW⚡ | pdf2zh/pdf2zh.py | 247 | # Check if the file is a PDF | COMMENT |
| LOW | pdf2zh/gui.py | 123 | # Check if this is a public demo, which has resource limits | COMMENT |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| HIGH | pdf2zh/pdfinterp.py | 364 | self.push(obj) | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | pdf2zh/high_level.py | 72 | CODE | |
| LOW | pdf2zh/high_level.py | 171 | CODE | |
| LOW | pdf2zh/high_level.py | 304 | CODE | |
| LOW | pdf2zh/converter.py | 133 | CODE |
| Severity | File | Line | Snippet | Context |
|---|---|---|---|---|
| LOW | test/test_cache.py | 161 | # cache_instance = cache.TranslationCache("test_engine") | COMMENT |
| LOW | test/test_cache.py | 181 | # # Read operation - verify our own write | COMMENT |