Merge pull request #687 from Awei-996/fix/docling-image-dislocation

fix(parser): 修复 Docling 解析文档时图片错位问题
This commit is contained in:
Wenjie Zhang 2026-05-12 10:25:07 +08:00 committed by GitHub
commit 6315f9ccae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 49 additions and 22 deletions

View File

@ -127,31 +127,25 @@ def _convert_with_docling(file_path: Path, params: dict | None = None) -> str:
doc = result.document
if hasattr(doc, "pictures") and doc.pictures:
image_refs: list[tuple[str, bytes]] = []
replacements: list[str] = []
for pic in doc.pictures:
if hasattr(pic, "image") and hasattr(pic.image, "uri"):
uri = str(pic.image.uri)
if uri.startswith("data:"):
uri = str(pic.image.uri) if hasattr(pic, "image") and hasattr(pic.image, "uri") else ""
if uri.startswith("data:"):
filename = "image"
try:
image_data, mime_type = _parse_data_uri(uri)
timestamp = int(time.time() * 1000000)
filename = f"image_{timestamp}.{mime_type.split('/')[-1]}"
image_refs.append((filename, image_data))
image_urls: list[str] = []
for filename, image_data in image_refs:
try:
url = _upload_image_to_minio(image_data, filename, image_bucket, image_prefix)
image_urls.append(f"![{filename}]({url})")
except Exception as e: # noqa: BLE001
logger.error(f"上传图片失败 {filename}: {e}")
image_urls.append(f"[图片: {filename}]")
filename = f"image_{int(time.time() * 1000000)}.{mime_type.split('/')[-1]}"
url = _upload_image_to_minio(image_data, filename, image_bucket, image_prefix)
replacements.append(f"![{filename}]({url})")
except Exception as e: # noqa: BLE001
logger.error(f"上传图片失败 {filename}: {e}")
replacements.append(f"[图片: {filename}]")
else:
replacements.append("")
markdown = doc.export_to_markdown()
for url in image_urls:
markdown = re.sub(r"<!--\s*image\s*-->", url, markdown, count=1)
for replacement in replacements:
markdown = re.sub(r"<!--\s*image\s*-->", replacement, markdown, count=1)
return markdown
return doc.export_to_markdown()

View File

@ -76,9 +76,10 @@ def test_convert_with_docling_reinserts_image_links_in_document_order(
fake_doc = SimpleNamespace(
pictures=[
SimpleNamespace(image=SimpleNamespace(uri=f"data:image/png;base64,{first_image}")),
SimpleNamespace(image=SimpleNamespace(uri="https://example.test/remote.png")),
SimpleNamespace(image=SimpleNamespace(uri=f"data:image/png;base64,{second_image}")),
],
export_to_markdown=lambda: "before\n<!-- image -->\nbetween\n<!-- image -->\nafter",
export_to_markdown=lambda: "before\n<!-- image -->\nremote\n<!-- image -->\nbetween\n<!-- image -->\nafter",
)
fake_result = SimpleNamespace(status=SimpleNamespace(name="SUCCESS"), document=fake_doc)
uploaded_images: list[bytes] = []
@ -103,12 +104,44 @@ def test_convert_with_docling_reinserts_image_links_in_document_order(
assert markdown == (
"before\n"
"![image_1000000.png](https://example.test/1.png)\n"
"remote\n"
"\n"
"between\n"
"![image_2000000.png](https://example.test/2.png)\n"
"after"
)
def test_convert_with_docling_keeps_image_placeholder_when_upload_fails(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
):
file_path = tmp_path / "parser_test.docx"
file_path.write_bytes(b"fake docx")
image = base64.b64encode(b"image data").decode()
fake_doc = SimpleNamespace(
pictures=[SimpleNamespace(image=SimpleNamespace(uri=f"data:image/png;base64,{image}"))],
export_to_markdown=lambda: "before\n<!-- image -->\nafter",
)
fake_result = SimpleNamespace(status=SimpleNamespace(name="SUCCESS"), document=fake_doc)
class FakeConverter:
def convert(self, path: Path):
assert path == file_path
return fake_result
def _raise_upload_error(*args, **kwargs):
raise RuntimeError("upload failed")
monkeypatch.setattr(parser_unified, "_get_docling_converter", lambda: FakeConverter())
monkeypatch.setattr(parser_unified, "_upload_image_to_minio", _raise_upload_error)
monkeypatch.setattr(parser_unified.time, "time", lambda: 1.0)
markdown = parser_unified._convert_with_docling(file_path)
assert markdown == "before\n[图片: image_1000000.png]\nafter"
def test_parser_parse_png_file_returns_markdown_text_with_mocked_ocr(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,