docling-core 2.85.0__tar.gz → 2.86.0__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.
- {docling_core-2.85.0 → docling_core-2.86.0}/PKG-INFO +1 -1
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/cli/view.py +1 -2
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/types/doc/document.py +111 -95
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/types/doc/page.py +6 -12
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/types/doc/utils.py +39 -7
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/utils/generate_docs.py +7 -6
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core.egg-info/PKG-INFO +1 -1
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core.egg-info/SOURCES.txt +1 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/pyproject.toml +2 -1
- docling_core-2.86.0/test/test_upath_support.py +182 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/LICENSE +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/README.md +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/__init__.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/cli/__init__.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/cli/serialize.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/experimental/__init__.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/experimental/serializer/__init__.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/experimental/serializer/outline.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/py.typed +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/search/__init__.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/search/package.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/__init__.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/chunker/__init__.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/chunker/base.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/chunker/chunk_expander.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/chunker/code_chunking/__init__.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/chunker/code_chunking/_language_code_chunkers.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/chunker/code_chunking/_utils.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/chunker/code_chunking/base_code_chunking_strategy.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/chunker/code_chunking/code_chunk.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/chunker/code_chunking/standard_code_chunking_strategy.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/chunker/doc_chunk.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/chunker/hierarchical_chunker.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/chunker/hybrid_chunker.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/chunker/line_chunker.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/chunker/page_chunker.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/chunker/tokenizer/__init__.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/chunker/tokenizer/base.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/chunker/tokenizer/huggingface.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/chunker/tokenizer/openai.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/deserializer/__init__.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/deserializer/base.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/deserializer/doclang.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/profiler/__init__.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/profiler/doc_profiler.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/serializer/__init__.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/serializer/_doclang_utils.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/serializer/azure.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/serializer/base.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/serializer/common.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/serializer/doclang.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/serializer/doctags.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/serializer/html.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/serializer/html_styles.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/serializer/latex.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/serializer/markdown.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/serializer/markdown_excel.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/serializer/plain_text.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/serializer/webvtt.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/visualizer/__init__.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/visualizer/base.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/visualizer/key_value_visualizer.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/visualizer/layout_visualizer.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/visualizer/reading_order_visualizer.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/visualizer/table_visualizer.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/types/__init__.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/types/base.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/types/doc/__init__.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/types/doc/base.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/types/doc/labels.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/types/doc/tokens.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/types/doc/webvtt.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/types/io/__init__.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/types/legacy_doc/__init__.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/types/legacy_doc/base.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/types/legacy_doc/document.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/utils/__init__.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/utils/alias.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/utils/file.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/utils/generate_jsonschema.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/utils/legacy.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/utils/settings.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core/utils/validators.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core.egg-info/dependency_links.txt +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core.egg-info/entry_points.txt +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core.egg-info/requires.txt +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/docling_core.egg-info/top_level.txt +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/setup.cfg +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/test/test_azure_serializer.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/test/test_base.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/test/test_chunk_expander.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/test/test_code_chunker.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/test/test_code_chunking_strategy.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/test/test_data_gen_flag.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/test/test_deserializer_doclang.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/test/test_doc_base.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/test/test_doclang_archive.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/test/test_docling_doc.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/test/test_doctags_load.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/test/test_hierarchical_chunker.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/test/test_hierarchy.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/test/test_hybrid_chunker.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/test/test_latex_serialization.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/test/test_line_chunker.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/test/test_metadata.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/test/test_otsl_table_export.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/test/test_page.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/test/test_page_chunker.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/test/test_plain_text_serialization.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/test/test_profiler.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/test/test_regions_to_table.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/test/test_serialization.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/test/test_serialization_doclang.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/test/test_serialization_doctag.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/test/test_serialization_outline.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/test/test_utils.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/test/test_visualization.py +0 -0
- {docling_core-2.85.0 → docling_core-2.86.0}/test/test_webvtt.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: docling-core
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.86.0
|
|
4
4
|
Summary: A python library to define and validate data types in Docling.
|
|
5
5
|
Author-email: Cesar Berrospi Ramis <ceb@zurich.ibm.com>, Panos Vagenas <pva@zurich.ibm.com>, Michele Dolfi <dol@zurich.ibm.com>, Christoph Auer <cau@zurich.ibm.com>, Peter Staar <taa@zurich.ibm.com>
|
|
6
6
|
Maintainer-email: Panos Vagenas <pva@zurich.ibm.com>, Michele Dolfi <dol@zurich.ibm.com>, Christoph Auer <cau@zurich.ibm.com>, Peter Staar <taa@zurich.ibm.com>, Cesar Berrospi Ramis <ceb@zurich.ibm.com>
|
|
@@ -69,8 +69,7 @@ def view(
|
|
|
69
69
|
image_mode=ImageRefMode.EMBEDDED,
|
|
70
70
|
split_page_view=split_view,
|
|
71
71
|
)
|
|
72
|
-
|
|
73
|
-
f.write(html_output)
|
|
72
|
+
target_path.write_text(html_output, encoding="utf-8")
|
|
74
73
|
webbrowser.open(url=f"file://{target_path.absolute().resolve()}")
|
|
75
74
|
|
|
76
75
|
|
|
@@ -6,7 +6,6 @@ import hashlib
|
|
|
6
6
|
import json
|
|
7
7
|
import logging
|
|
8
8
|
import mimetypes
|
|
9
|
-
import os
|
|
10
9
|
import re
|
|
11
10
|
import sys
|
|
12
11
|
import tempfile
|
|
@@ -15,7 +14,7 @@ import warnings
|
|
|
15
14
|
from collections.abc import Iterable, Sequence
|
|
16
15
|
from dataclasses import dataclass
|
|
17
16
|
from enum import Enum
|
|
18
|
-
from io import BytesIO
|
|
17
|
+
from io import BytesIO, StringIO
|
|
19
18
|
from pathlib import Path
|
|
20
19
|
from typing import (
|
|
21
20
|
Annotated,
|
|
@@ -69,6 +68,7 @@ from docling_core.types.doc.labels import (
|
|
|
69
68
|
)
|
|
70
69
|
from docling_core.types.doc.tokens import DocumentToken, TableToken
|
|
71
70
|
from docling_core.types.doc.utils import (
|
|
71
|
+
is_remote_path,
|
|
72
72
|
parse_otsl_table_content,
|
|
73
73
|
relative_path,
|
|
74
74
|
resolve_archive_path,
|
|
@@ -1074,22 +1074,23 @@ class DocumentOrigin(BaseModel):
|
|
|
1074
1074
|
)
|
|
1075
1075
|
|
|
1076
1076
|
_extra_mimetypes: typing.ClassVar[list[str]] = [
|
|
1077
|
-
"application/vnd.
|
|
1078
|
-
"application/vnd.
|
|
1079
|
-
"application/vnd.
|
|
1080
|
-
"application/vnd.
|
|
1077
|
+
"application/vnd.box.boxnote",
|
|
1078
|
+
"application/vnd.oasis.opendocument.presentation",
|
|
1079
|
+
"application/vnd.oasis.opendocument.spreadsheet",
|
|
1080
|
+
"application/vnd.oasis.opendocument.text",
|
|
1081
1081
|
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
|
1082
|
+
"application/vnd.openxmlformats-officedocument.presentationml.slideshow",
|
|
1083
|
+
"application/vnd.openxmlformats-officedocument.presentationml.template",
|
|
1082
1084
|
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
|
1083
|
-
"application/vnd.
|
|
1084
|
-
"application/vnd.
|
|
1085
|
-
"
|
|
1085
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
|
1086
|
+
"application/vnd.openxmlformats-officedocument.wordprocessingml.template",
|
|
1087
|
+
"audio/mp3",
|
|
1088
|
+
"audio/wav",
|
|
1089
|
+
"audio/x-wav",
|
|
1086
1090
|
"text/asciidoc",
|
|
1087
|
-
"text/markdown",
|
|
1088
1091
|
"text/csv",
|
|
1092
|
+
"text/markdown",
|
|
1089
1093
|
"text/vtt",
|
|
1090
|
-
"audio/x-wav",
|
|
1091
|
-
"audio/wav",
|
|
1092
|
-
"audio/mp3",
|
|
1093
1094
|
]
|
|
1094
1095
|
|
|
1095
1096
|
@field_validator("binary_hash", mode="before")
|
|
@@ -6070,6 +6071,39 @@ class DoclingDocument(BaseModel):
|
|
|
6070
6071
|
|
|
6071
6072
|
return result
|
|
6072
6073
|
|
|
6074
|
+
@staticmethod
|
|
6075
|
+
def _save_image_and_resolve_uri(
|
|
6076
|
+
img: PILImage.Image,
|
|
6077
|
+
loc_path: Path,
|
|
6078
|
+
reference_path: Optional[Path],
|
|
6079
|
+
) -> Union[AnyUrl, Path]:
|
|
6080
|
+
"""Save *img* to *loc_path* and return the URI to store on the ImageRef.
|
|
6081
|
+
|
|
6082
|
+
Uses a BytesIO intermediate buffer so that the write is compatible with
|
|
6083
|
+
UPath remote backends (PIL cannot write directly to non-local paths).
|
|
6084
|
+
For remote paths the URI is returned as an absolute AnyUrl string; for
|
|
6085
|
+
local paths it is returned relative to *reference_path* when available,
|
|
6086
|
+
or as the absolute *loc_path* when *reference_path* is None.
|
|
6087
|
+
|
|
6088
|
+
Args:
|
|
6089
|
+
img: The PIL image to save.
|
|
6090
|
+
loc_path: Destination path (local or remote UPath).
|
|
6091
|
+
reference_path: Base path used to compute a relative URI for local
|
|
6092
|
+
storage. Pass ``None`` to store the absolute path instead.
|
|
6093
|
+
|
|
6094
|
+
Returns:
|
|
6095
|
+
An AnyUrl for remote paths, or a Path (relative or absolute) for local paths.
|
|
6096
|
+
"""
|
|
6097
|
+
buf = BytesIO()
|
|
6098
|
+
img.save(buf, format="PNG")
|
|
6099
|
+
loc_path.write_bytes(buf.getvalue())
|
|
6100
|
+
|
|
6101
|
+
if is_remote_path(loc_path) or is_remote_path(reference_path):
|
|
6102
|
+
return AnyUrl(str(loc_path))
|
|
6103
|
+
if reference_path is not None:
|
|
6104
|
+
return relative_path(reference_path.resolve(), loc_path.resolve())
|
|
6105
|
+
return loc_path
|
|
6106
|
+
|
|
6073
6107
|
def _with_pictures_refs(
|
|
6074
6108
|
self,
|
|
6075
6109
|
image_dir: Path,
|
|
@@ -6088,65 +6122,37 @@ class DoclingDocument(BaseModel):
|
|
|
6088
6122
|
"""
|
|
6089
6123
|
result: DoclingDocument = copy.deepcopy(self)
|
|
6090
6124
|
|
|
6091
|
-
img_count = 0
|
|
6092
6125
|
image_dir.mkdir(parents=True, exist_ok=True)
|
|
6093
6126
|
|
|
6094
|
-
|
|
6095
|
-
|
|
6096
|
-
|
|
6097
|
-
|
|
6098
|
-
|
|
6099
|
-
hexhash = PictureItem._image_to_hexhash(img)
|
|
6100
|
-
|
|
6101
|
-
# loc_path = image_dir / f"image_{img_count:06}.png"
|
|
6102
|
-
if hexhash is not None:
|
|
6103
|
-
loc_path = image_dir / f"image_{img_count:06}_{hexhash}.png"
|
|
6104
|
-
|
|
6105
|
-
img.save(loc_path)
|
|
6106
|
-
if reference_path is not None:
|
|
6107
|
-
obj_path = relative_path(
|
|
6108
|
-
reference_path.resolve(),
|
|
6109
|
-
loc_path.resolve(),
|
|
6110
|
-
)
|
|
6111
|
-
else:
|
|
6112
|
-
obj_path = loc_path
|
|
6113
|
-
|
|
6114
|
-
if item.image is None:
|
|
6115
|
-
scale = img.size[0] / item.prov[0].bbox.width
|
|
6116
|
-
item.image = ImageRef.from_pil(image=img, dpi=round(72 * scale))
|
|
6117
|
-
item.image.uri = Path(obj_path)
|
|
6118
|
-
elif item.image is not None:
|
|
6119
|
-
item.image.uri = Path(obj_path)
|
|
6120
|
-
|
|
6121
|
-
# if item.image._pil is not None:
|
|
6122
|
-
# item.image._pil.close()
|
|
6123
|
-
|
|
6124
|
-
img_count += 1
|
|
6125
|
-
|
|
6126
|
-
if include_page_images:
|
|
6127
|
-
for p_no, page in result.pages.items():
|
|
6128
|
-
if page_no is not None and p_no != page_no:
|
|
6129
|
-
continue
|
|
6130
|
-
if page.image is None:
|
|
6131
|
-
continue
|
|
6132
|
-
img = page.image.pil_image
|
|
6133
|
-
if img is None:
|
|
6134
|
-
continue
|
|
6127
|
+
img_count = 0
|
|
6128
|
+
for item, _ in result.iterate_items(page_no=page_no, with_groups=False):
|
|
6129
|
+
if isinstance(item, PictureItem):
|
|
6130
|
+
img = item.get_image(doc=self)
|
|
6131
|
+
if img is not None:
|
|
6135
6132
|
hexhash = PictureItem._image_to_hexhash(img)
|
|
6136
|
-
if hexhash is None:
|
|
6137
|
-
|
|
6138
|
-
|
|
6139
|
-
|
|
6140
|
-
|
|
6141
|
-
|
|
6142
|
-
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
|
-
|
|
6133
|
+
if hexhash is not None:
|
|
6134
|
+
loc_path = image_dir / f"image_{img_count:06}_{hexhash}.png"
|
|
6135
|
+
obj_path = self._save_image_and_resolve_uri(img, loc_path, reference_path)
|
|
6136
|
+
if item.image is None:
|
|
6137
|
+
scale = img.size[0] / item.prov[0].bbox.width
|
|
6138
|
+
item.image = ImageRef.from_pil(image=img, dpi=round(72 * scale))
|
|
6139
|
+
item.image.uri = obj_path # type: ignore[assignment]
|
|
6140
|
+
img_count += 1
|
|
6141
|
+
|
|
6142
|
+
if include_page_images:
|
|
6143
|
+
for p_no, page in result.pages.items():
|
|
6144
|
+
if page_no is not None and p_no != page_no:
|
|
6145
|
+
continue
|
|
6146
|
+
if page.image is None:
|
|
6147
|
+
continue
|
|
6148
|
+
img = page.image.pil_image
|
|
6149
|
+
if img is None:
|
|
6150
|
+
continue
|
|
6151
|
+
hexhash = PictureItem._image_to_hexhash(img)
|
|
6152
|
+
if hexhash is None:
|
|
6153
|
+
continue
|
|
6154
|
+
loc_path = image_dir / f"page_{p_no:06}_{hexhash}.png"
|
|
6155
|
+
page.image.uri = self._save_image_and_resolve_uri(img, loc_path, reference_path) # type: ignore[assignment]
|
|
6150
6156
|
|
|
6151
6157
|
return result
|
|
6152
6158
|
|
|
@@ -6207,7 +6213,7 @@ class DoclingDocument(BaseModel):
|
|
|
6207
6213
|
artifacts_dir, reference_path = self._get_output_paths(filename, artifacts_dir)
|
|
6208
6214
|
|
|
6209
6215
|
if image_mode == ImageRefMode.REFERENCED:
|
|
6210
|
-
|
|
6216
|
+
artifacts_dir.mkdir(parents=True, exist_ok=True)
|
|
6211
6217
|
|
|
6212
6218
|
new_doc = self._make_copy_with_refmode(
|
|
6213
6219
|
artifacts_dir,
|
|
@@ -6218,8 +6224,7 @@ class DoclingDocument(BaseModel):
|
|
|
6218
6224
|
)
|
|
6219
6225
|
|
|
6220
6226
|
out = new_doc.export_to_dict(coord_precision=coord_precision, confid_precision=confid_precision)
|
|
6221
|
-
|
|
6222
|
-
json.dump(out, fw, indent=indent)
|
|
6227
|
+
filename.write_text(json.dumps(out, indent=indent), encoding="utf-8")
|
|
6223
6228
|
|
|
6224
6229
|
@classmethod
|
|
6225
6230
|
def load_from_json(cls, filename: Union[str, Path]) -> "DoclingDocument":
|
|
@@ -6234,8 +6239,7 @@ class DoclingDocument(BaseModel):
|
|
|
6234
6239
|
"""
|
|
6235
6240
|
if isinstance(filename, str):
|
|
6236
6241
|
filename = Path(filename)
|
|
6237
|
-
|
|
6238
|
-
return cls.model_validate_json(f.read())
|
|
6242
|
+
return cls.model_validate_json(filename.read_text(encoding="utf-8"))
|
|
6239
6243
|
|
|
6240
6244
|
def save_as_yaml(
|
|
6241
6245
|
self,
|
|
@@ -6252,7 +6256,7 @@ class DoclingDocument(BaseModel):
|
|
|
6252
6256
|
artifacts_dir, reference_path = self._get_output_paths(filename, artifacts_dir)
|
|
6253
6257
|
|
|
6254
6258
|
if image_mode == ImageRefMode.REFERENCED:
|
|
6255
|
-
|
|
6259
|
+
artifacts_dir.mkdir(parents=True, exist_ok=True)
|
|
6256
6260
|
|
|
6257
6261
|
new_doc = self._make_copy_with_refmode(
|
|
6258
6262
|
artifacts_dir,
|
|
@@ -6263,8 +6267,9 @@ class DoclingDocument(BaseModel):
|
|
|
6263
6267
|
)
|
|
6264
6268
|
|
|
6265
6269
|
out = new_doc.export_to_dict(coord_precision=coord_precision, confid_precision=confid_precision)
|
|
6266
|
-
|
|
6267
|
-
|
|
6270
|
+
stream = StringIO()
|
|
6271
|
+
yaml.dump(out, stream, default_flow_style=default_flow_style)
|
|
6272
|
+
filename.write_text(stream.getvalue(), encoding="utf-8")
|
|
6268
6273
|
|
|
6269
6274
|
@classmethod
|
|
6270
6275
|
def load_from_yaml(cls, filename: Union[str, Path]) -> "DoclingDocument":
|
|
@@ -6278,8 +6283,7 @@ class DoclingDocument(BaseModel):
|
|
|
6278
6283
|
"""
|
|
6279
6284
|
if isinstance(filename, str):
|
|
6280
6285
|
filename = Path(filename)
|
|
6281
|
-
|
|
6282
|
-
data = yaml.load(f, Loader=yaml.SafeLoader)
|
|
6286
|
+
data = yaml.load(filename.read_text(encoding="utf-8"), Loader=yaml.SafeLoader)
|
|
6283
6287
|
return DoclingDocument.model_validate(data)
|
|
6284
6288
|
|
|
6285
6289
|
def export_to_dict(
|
|
@@ -6330,7 +6334,7 @@ class DoclingDocument(BaseModel):
|
|
|
6330
6334
|
artifacts_dir, reference_path = self._get_output_paths(filename, artifacts_dir)
|
|
6331
6335
|
|
|
6332
6336
|
if image_mode == ImageRefMode.REFERENCED:
|
|
6333
|
-
|
|
6337
|
+
artifacts_dir.mkdir(parents=True, exist_ok=True)
|
|
6334
6338
|
|
|
6335
6339
|
new_doc = self._make_copy_with_refmode(artifacts_dir, image_mode, page_no, reference_path=reference_path)
|
|
6336
6340
|
|
|
@@ -6355,8 +6359,7 @@ class DoclingDocument(BaseModel):
|
|
|
6355
6359
|
mark_meta=mark_meta,
|
|
6356
6360
|
)
|
|
6357
6361
|
|
|
6358
|
-
|
|
6359
|
-
fw.write(md_out)
|
|
6362
|
+
filename.write_text(md_out, encoding="utf-8")
|
|
6360
6363
|
|
|
6361
6364
|
def export_to_markdown(
|
|
6362
6365
|
self,
|
|
@@ -6592,7 +6595,7 @@ class DoclingDocument(BaseModel):
|
|
|
6592
6595
|
artifacts_dir, reference_path = self._get_output_paths(filename, artifacts_dir)
|
|
6593
6596
|
|
|
6594
6597
|
if image_mode == ImageRefMode.REFERENCED:
|
|
6595
|
-
|
|
6598
|
+
artifacts_dir.mkdir(parents=True, exist_ok=True)
|
|
6596
6599
|
|
|
6597
6600
|
new_doc = self._make_copy_with_refmode(artifacts_dir, image_mode, page_no, reference_path=reference_path)
|
|
6598
6601
|
|
|
@@ -6610,16 +6613,30 @@ class DoclingDocument(BaseModel):
|
|
|
6610
6613
|
include_annotations=include_annotations,
|
|
6611
6614
|
)
|
|
6612
6615
|
|
|
6613
|
-
|
|
6614
|
-
fw.write(html_out)
|
|
6616
|
+
filename.write_text(html_out, encoding="utf-8")
|
|
6615
6617
|
|
|
6616
6618
|
def _get_output_paths(
|
|
6617
|
-
self,
|
|
6619
|
+
self,
|
|
6620
|
+
filename: Path,
|
|
6621
|
+
artifacts_dir: Optional[Path] = None,
|
|
6618
6622
|
) -> tuple[Path, Optional[Path]]:
|
|
6619
|
-
|
|
6620
|
-
|
|
6623
|
+
"""Resolve output and artifacts directory paths from the given filename.
|
|
6624
|
+
|
|
6625
|
+
Both ``Path`` and ``UPath`` objects are accepted since ``UPath`` is ``Path``-compatible for local paths, and remote ``UPath`` objects implement the same interface used here (``with_suffix``, ``with_name``,
|
|
6626
|
+
``is_absolute``, ``parent``, ``/``).
|
|
6627
|
+
|
|
6628
|
+
Args:
|
|
6629
|
+
filename: Destination file path.
|
|
6630
|
+
artifacts_dir: Optional explicit artifacts directory. When ``None``, a sibling
|
|
6631
|
+
directory named ``<stem>_artifacts`` is derived from ``filename``.
|
|
6632
|
+
|
|
6633
|
+
Returns:
|
|
6634
|
+
A tuple of ``(artifacts_dir, reference_path)`` where ``reference_path`` is
|
|
6635
|
+
the parent of ``filename`` when ``artifacts_dir`` is relative, or ``None``
|
|
6636
|
+
when it is absolute.
|
|
6637
|
+
"""
|
|
6621
6638
|
if artifacts_dir is None:
|
|
6622
|
-
# Remove the extension and add '
|
|
6639
|
+
# Remove the extension and add '_artifacts'
|
|
6623
6640
|
artifacts_dir = filename.with_suffix("")
|
|
6624
6641
|
artifacts_dir = artifacts_dir.with_name(artifacts_dir.name + "_artifacts")
|
|
6625
6642
|
if artifacts_dir.is_absolute():
|
|
@@ -6767,8 +6784,7 @@ class DoclingDocument(BaseModel):
|
|
|
6767
6784
|
omit_voice_end=omit_voice_end,
|
|
6768
6785
|
)
|
|
6769
6786
|
|
|
6770
|
-
|
|
6771
|
-
fw.write(vtt_out)
|
|
6787
|
+
filename.write_text(vtt_out, encoding="utf-8")
|
|
6772
6788
|
|
|
6773
6789
|
@staticmethod
|
|
6774
6790
|
def load_from_doctags( # noqa: C901
|
|
@@ -7379,8 +7395,7 @@ class DoclingDocument(BaseModel):
|
|
|
7379
7395
|
minified=minified,
|
|
7380
7396
|
)
|
|
7381
7397
|
|
|
7382
|
-
|
|
7383
|
-
fw.write(out)
|
|
7398
|
+
filename.write_text(out, encoding="utf-8")
|
|
7384
7399
|
|
|
7385
7400
|
@deprecated("Use export_to_doctags() instead.")
|
|
7386
7401
|
def export_to_document_tokens(self, *args, **kwargs):
|
|
@@ -7470,9 +7485,10 @@ class DoclingDocument(BaseModel):
|
|
|
7470
7485
|
filename: Union[str, Path],
|
|
7471
7486
|
) -> None:
|
|
7472
7487
|
"""Save the document as DocLang."""
|
|
7488
|
+
if isinstance(filename, str):
|
|
7489
|
+
filename = Path(filename)
|
|
7473
7490
|
out = self.export_to_doclang()
|
|
7474
|
-
|
|
7475
|
-
fw.write(f"{out}\n")
|
|
7491
|
+
filename.write_text(f"{out}\n", encoding="utf-8")
|
|
7476
7492
|
|
|
7477
7493
|
def save_as_doclang_archive(
|
|
7478
7494
|
self,
|
|
@@ -688,8 +688,7 @@ class SegmentedPdfPage(SegmentedPage):
|
|
|
688
688
|
if isinstance(filename, str):
|
|
689
689
|
filename = Path(filename)
|
|
690
690
|
out = self.export_to_dict()
|
|
691
|
-
|
|
692
|
-
json.dump(out, fw, indent=indent)
|
|
691
|
+
filename.write_text(json.dumps(out, indent=indent), encoding="utf-8")
|
|
693
692
|
|
|
694
693
|
@classmethod
|
|
695
694
|
def load_from_json(cls, filename: Union[str, Path]) -> "SegmentedPdfPage":
|
|
@@ -703,8 +702,7 @@ class SegmentedPdfPage(SegmentedPage):
|
|
|
703
702
|
"""
|
|
704
703
|
if isinstance(filename, str):
|
|
705
704
|
filename = Path(filename)
|
|
706
|
-
|
|
707
|
-
return cls.model_validate_json(f.read())
|
|
705
|
+
return cls.model_validate_json(filename.read_text(encoding="utf-8"))
|
|
708
706
|
|
|
709
707
|
def crop_text(self, cell_unit: TextCellUnit, bbox: BoundingBox, eps: float = 1.0) -> str:
|
|
710
708
|
"""Extract text from cells within the specified bounding box.
|
|
@@ -1390,8 +1388,7 @@ class PdfTableOfContents(BaseModel):
|
|
|
1390
1388
|
if isinstance(filename, str):
|
|
1391
1389
|
filename = Path(filename)
|
|
1392
1390
|
out = self.export_to_dict()
|
|
1393
|
-
|
|
1394
|
-
json.dump(out, fw, indent=indent)
|
|
1391
|
+
filename.write_text(json.dumps(out, indent=indent), encoding="utf-8")
|
|
1395
1392
|
|
|
1396
1393
|
@classmethod
|
|
1397
1394
|
def load_from_json(cls, filename: Union[str, Path]) -> "PdfTableOfContents":
|
|
@@ -1405,8 +1402,7 @@ class PdfTableOfContents(BaseModel):
|
|
|
1405
1402
|
"""
|
|
1406
1403
|
if isinstance(filename, str):
|
|
1407
1404
|
filename = Path(filename)
|
|
1408
|
-
|
|
1409
|
-
return cls.model_validate_json(f.read())
|
|
1405
|
+
return cls.model_validate_json(filename.read_text(encoding="utf-8"))
|
|
1410
1406
|
|
|
1411
1407
|
|
|
1412
1408
|
class ParsedPdfDocument(BaseModel):
|
|
@@ -1451,8 +1447,7 @@ class ParsedPdfDocument(BaseModel):
|
|
|
1451
1447
|
if isinstance(filename, str):
|
|
1452
1448
|
filename = Path(filename)
|
|
1453
1449
|
out = self.export_to_dict()
|
|
1454
|
-
|
|
1455
|
-
json.dump(out, fw, indent=indent)
|
|
1450
|
+
filename.write_text(json.dumps(out, indent=indent), encoding="utf-8")
|
|
1456
1451
|
|
|
1457
1452
|
@classmethod
|
|
1458
1453
|
def load_from_json(cls, filename: Union[str, Path]) -> "ParsedPdfDocument":
|
|
@@ -1466,5 +1461,4 @@ class ParsedPdfDocument(BaseModel):
|
|
|
1466
1461
|
"""
|
|
1467
1462
|
if isinstance(filename, str):
|
|
1468
1463
|
filename = Path(filename)
|
|
1469
|
-
|
|
1470
|
-
return cls.model_validate_json(f.read())
|
|
1464
|
+
return cls.model_validate_json(filename.read_text(encoding="utf-8"))
|
|
@@ -7,7 +7,7 @@ import shutil
|
|
|
7
7
|
import unicodedata
|
|
8
8
|
import zipfile
|
|
9
9
|
from pathlib import Path
|
|
10
|
-
from typing import TYPE_CHECKING,
|
|
10
|
+
from typing import TYPE_CHECKING, Any
|
|
11
11
|
|
|
12
12
|
from docling_core.types.doc.tokens import _LOC_PREFIX, DocumentToken, TableToken
|
|
13
13
|
|
|
@@ -15,21 +15,53 @@ if TYPE_CHECKING:
|
|
|
15
15
|
from docling_core.types.doc.document import TableCell, TableData
|
|
16
16
|
|
|
17
17
|
|
|
18
|
-
def
|
|
18
|
+
def is_remote_path(p: Any) -> bool:
|
|
19
|
+
"""Check if a path is a remote/cloud path (e.g., S3, GCS, Azure).
|
|
20
|
+
|
|
21
|
+
Works with UPath objects from universal-pathlib. Local paths return False.
|
|
22
|
+
|
|
23
|
+
Args:
|
|
24
|
+
p: A path object (Path, UPath, or similar)
|
|
25
|
+
|
|
26
|
+
Returns:
|
|
27
|
+
bool: True if the path is a remote/cloud path, False otherwise.
|
|
28
|
+
"""
|
|
29
|
+
# UPath objects have a 'protocol' attribute
|
|
30
|
+
protocol = getattr(p, "protocol", None)
|
|
31
|
+
return protocol is not None and protocol not in ("file", "")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def relative_path(src: str | Path, target: str | Path) -> Path:
|
|
19
35
|
"""Compute the relative path from `src` to `target`.
|
|
20
36
|
|
|
21
37
|
Args:
|
|
22
|
-
src
|
|
23
|
-
target
|
|
38
|
+
src: The source directory or file path (must be absolute).
|
|
39
|
+
target: The target directory or file path (must be absolute).
|
|
24
40
|
|
|
25
41
|
Returns:
|
|
26
42
|
Path: The relative path from `src` to `target`.
|
|
27
43
|
|
|
28
44
|
Raises:
|
|
29
45
|
ValueError: If either `src` or `target` is not an absolute path.
|
|
46
|
+
|
|
47
|
+
Note:
|
|
48
|
+
For remote paths (UPath with non-file protocols), this function cannot
|
|
49
|
+
compute relative paths. Use is_remote_path() to check before calling.
|
|
30
50
|
"""
|
|
31
|
-
|
|
32
|
-
|
|
51
|
+
# Convert to Path only if string, otherwise keep original type
|
|
52
|
+
if isinstance(src, str):
|
|
53
|
+
src = Path(src)
|
|
54
|
+
if isinstance(target, str):
|
|
55
|
+
target = Path(target)
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
src = src.resolve()
|
|
59
|
+
target = target.resolve()
|
|
60
|
+
except (AttributeError, NotImplementedError, OSError) as e:
|
|
61
|
+
raise ValueError(
|
|
62
|
+
f"Cannot resolve paths. This function only supports local filesystem paths. "
|
|
63
|
+
f"Remote paths should use absolute URIs. Error: {e}"
|
|
64
|
+
) from e
|
|
33
65
|
|
|
34
66
|
# Ensure both paths are absolute
|
|
35
67
|
if not src.is_absolute():
|
|
@@ -93,7 +125,7 @@ def safe_extract_zip_archive(archive: Path, destination: Path) -> None:
|
|
|
93
125
|
shutil.copyfileobj(src, dst)
|
|
94
126
|
|
|
95
127
|
|
|
96
|
-
def get_html_tag_with_text_direction(html_tag: str, text: str, attrs:
|
|
128
|
+
def get_html_tag_with_text_direction(html_tag: str, text: str, attrs: dict | None = None) -> str:
|
|
97
129
|
"""Form the HTML element with tag, text, and optional dir attribute."""
|
|
98
130
|
my_attrs = attrs or {}
|
|
99
131
|
if (dir := my_attrs.get("dir")) is not None and dir != "ltr":
|
|
@@ -6,7 +6,6 @@ Example:
|
|
|
6
6
|
|
|
7
7
|
import argparse
|
|
8
8
|
import json
|
|
9
|
-
import os
|
|
10
9
|
from argparse import BooleanOptionalAction
|
|
11
10
|
from pathlib import Path
|
|
12
11
|
from shutil import rmtree
|
|
@@ -24,15 +23,16 @@ def _prepare_directory(folder: str, clean: bool = False) -> None:
|
|
|
24
23
|
folder: The name of the directory.
|
|
25
24
|
clean: Whether any existing content in the directory should be removed.
|
|
26
25
|
"""
|
|
27
|
-
|
|
26
|
+
folder_path = Path(folder)
|
|
27
|
+
if folder_path.is_dir():
|
|
28
28
|
if clean:
|
|
29
|
-
for path in
|
|
29
|
+
for path in folder_path.glob("**/*"):
|
|
30
30
|
if path.is_file():
|
|
31
31
|
path.unlink()
|
|
32
32
|
elif path.is_dir():
|
|
33
33
|
rmtree(path)
|
|
34
34
|
else:
|
|
35
|
-
|
|
35
|
+
folder_path.mkdir(parents=True, exist_ok=True)
|
|
36
36
|
|
|
37
37
|
|
|
38
38
|
def generate_collection_jsonschema(folder: str):
|
|
@@ -41,10 +41,11 @@ def generate_collection_jsonschema(folder: str):
|
|
|
41
41
|
Args:
|
|
42
42
|
folder: The name of the directory.
|
|
43
43
|
"""
|
|
44
|
+
folder_path = Path(folder)
|
|
44
45
|
for item in MODELS:
|
|
45
46
|
json_schema = generate_json_schema(item)
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
output_file = folder_path / f"{item}.json"
|
|
48
|
+
output_file.write_text(json.dumps(json_schema, ensure_ascii=False, indent=2), encoding="utf-8")
|
|
48
49
|
|
|
49
50
|
|
|
50
51
|
def main() -> None:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: docling-core
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.86.0
|
|
4
4
|
Summary: A python library to define and validate data types in Docling.
|
|
5
5
|
Author-email: Cesar Berrospi Ramis <ceb@zurich.ibm.com>, Panos Vagenas <pva@zurich.ibm.com>, Michele Dolfi <dol@zurich.ibm.com>, Christoph Auer <cau@zurich.ibm.com>, Peter Staar <taa@zurich.ibm.com>
|
|
6
6
|
Maintainer-email: Panos Vagenas <pva@zurich.ibm.com>, Michele Dolfi <dol@zurich.ibm.com>, Christoph Auer <cau@zurich.ibm.com>, Peter Staar <taa@zurich.ibm.com>, Cesar Berrospi Ramis <ceb@zurich.ibm.com>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "docling-core"
|
|
3
|
-
version = "2.
|
|
3
|
+
version = "2.86.0" # DO NOT EDIT, updated automatically
|
|
4
4
|
description = "A python library to define and validate data types in Docling."
|
|
5
5
|
license = "MIT"
|
|
6
6
|
license-files = ["LICENSE"]
|
|
@@ -116,6 +116,7 @@ dev = [
|
|
|
116
116
|
"ruff>=0.14.11",
|
|
117
117
|
"types-defusedxml (>=0.7.0.20250822, <0.8.0)",
|
|
118
118
|
"opencv-python-headless>=4.6.0.66,<5.0.0.0",
|
|
119
|
+
"universal-pathlib>=0.3.10",
|
|
119
120
|
]
|
|
120
121
|
constraints = [
|
|
121
122
|
'pandas (>=2.1.4,<3.0.0); python_version < "3.11"',
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
"""Tests for UPath/fsspec support for cloud storage compatibility."""
|
|
2
|
+
|
|
3
|
+
import uuid
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import pytest
|
|
7
|
+
from PIL import Image as PILImage
|
|
8
|
+
from pydantic import AnyUrl
|
|
9
|
+
from upath import UPath
|
|
10
|
+
|
|
11
|
+
from docling_core.types.doc import DoclingDocument, ImageRefMode
|
|
12
|
+
from docling_core.types.doc.base import Size
|
|
13
|
+
from docling_core.types.doc.document import ImageRef
|
|
14
|
+
from docling_core.types.doc.utils import is_remote_path, relative_path
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@pytest.fixture
|
|
18
|
+
def memory_base():
|
|
19
|
+
"""Provide a unique memory:// base path for each test."""
|
|
20
|
+
return UPath(f"memory://test-{uuid.uuid4()}")
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
# =============================================================================
|
|
24
|
+
# Tests for is_remote_path()
|
|
25
|
+
# =============================================================================
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def test_is_remote_path():
|
|
29
|
+
"""Test is_remote_path() function with various path types."""
|
|
30
|
+
# Local paths should return False
|
|
31
|
+
assert is_remote_path(Path("/local/path")) is False
|
|
32
|
+
assert is_remote_path(Path(".")) is False
|
|
33
|
+
assert is_remote_path(None) is False
|
|
34
|
+
assert is_remote_path("/some/path") is False
|
|
35
|
+
assert is_remote_path(object()) is False
|
|
36
|
+
|
|
37
|
+
# File protocol should return False
|
|
38
|
+
class MockFilePath:
|
|
39
|
+
protocol = "file"
|
|
40
|
+
|
|
41
|
+
class MockEmptyProtocol:
|
|
42
|
+
protocol = ""
|
|
43
|
+
|
|
44
|
+
assert is_remote_path(MockFilePath()) is False
|
|
45
|
+
assert is_remote_path(MockEmptyProtocol()) is False
|
|
46
|
+
|
|
47
|
+
# Cloud protocols should return True
|
|
48
|
+
for protocol in ["s3", "gcs", "az", "http", "https"]:
|
|
49
|
+
|
|
50
|
+
class MockCloudPath:
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
MockCloudPath.protocol = protocol
|
|
54
|
+
assert is_remote_path(MockCloudPath()) is True
|
|
55
|
+
|
|
56
|
+
# Real UPath objects
|
|
57
|
+
assert is_remote_path(UPath("/tmp/test")) is False
|
|
58
|
+
assert is_remote_path(UPath("memory://test/path")) is True
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
# =============================================================================
|
|
62
|
+
# Tests for relative_path()
|
|
63
|
+
# =============================================================================
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def test_relative_path():
|
|
67
|
+
"""Test relative_path() function with various scenarios."""
|
|
68
|
+
# Basic relative path computation
|
|
69
|
+
assert relative_path(Path("/a/b"), Path("/a/b/c/d.txt")) == Path("c/d.txt")
|
|
70
|
+
assert relative_path("/a/b", "/a/b/c.txt") == Path("c.txt")
|
|
71
|
+
|
|
72
|
+
# Navigation with parent directories
|
|
73
|
+
# Sibling directory
|
|
74
|
+
assert relative_path(Path("/a/b/c"), Path("/a/b/d/e.txt")) == Path("../d/e.txt")
|
|
75
|
+
# Parent directory
|
|
76
|
+
assert relative_path(Path("/a/b/c/d"), Path("/a/b/e.txt")) == Path("../../e.txt")
|
|
77
|
+
|
|
78
|
+
# UPath with local file protocol
|
|
79
|
+
src = UPath("/home/user/docs")
|
|
80
|
+
target = UPath("/home/user/docs/images/img.png")
|
|
81
|
+
assert relative_path(src, target) == Path("images/img.png")
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# =============================================================================
|
|
85
|
+
# UPath integration tests (memory filesystem)
|
|
86
|
+
# =============================================================================
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def test_upath_integration(sample_doc, memory_base):
|
|
90
|
+
"""Test UPath integration with various document operations."""
|
|
91
|
+
# JSON roundtrip
|
|
92
|
+
json_path = memory_base / "doc.json"
|
|
93
|
+
sample_doc.save_as_json(json_path)
|
|
94
|
+
assert json_path.exists()
|
|
95
|
+
loaded = DoclingDocument.load_from_json(json_path)
|
|
96
|
+
assert sample_doc.export_to_dict() == loaded.export_to_dict()
|
|
97
|
+
|
|
98
|
+
# YAML roundtrip
|
|
99
|
+
yaml_path = memory_base / "doc.yaml"
|
|
100
|
+
sample_doc.save_as_yaml(yaml_path)
|
|
101
|
+
assert yaml_path.exists()
|
|
102
|
+
loaded = DoclingDocument.load_from_yaml(yaml_path)
|
|
103
|
+
assert sample_doc.export_to_dict() == loaded.export_to_dict()
|
|
104
|
+
|
|
105
|
+
# Markdown export
|
|
106
|
+
md_path = memory_base / "doc.md"
|
|
107
|
+
sample_doc.save_as_markdown(md_path)
|
|
108
|
+
assert md_path.exists()
|
|
109
|
+
assert len(md_path.read_text()) > 0
|
|
110
|
+
|
|
111
|
+
# HTML export
|
|
112
|
+
html_path = memory_base / "doc.html"
|
|
113
|
+
sample_doc.save_as_html(html_path)
|
|
114
|
+
assert html_path.exists()
|
|
115
|
+
assert "<html" in html_path.read_text().lower()
|
|
116
|
+
|
|
117
|
+
# Doctags export
|
|
118
|
+
dt_path = memory_base / "doc.dt"
|
|
119
|
+
sample_doc.save_as_doctags(dt_path)
|
|
120
|
+
assert dt_path.exists()
|
|
121
|
+
|
|
122
|
+
# Referenced mode
|
|
123
|
+
json_ref_path = memory_base / "doc_ref.json"
|
|
124
|
+
artifacts_dir = memory_base / "artifacts"
|
|
125
|
+
sample_doc.save_as_json(json_ref_path, artifacts_dir=artifacts_dir, image_mode=ImageRefMode.REFERENCED)
|
|
126
|
+
assert json_ref_path.exists()
|
|
127
|
+
|
|
128
|
+
# Referenced images use string URI
|
|
129
|
+
image_dir = memory_base / "images"
|
|
130
|
+
doc_with_refs = sample_doc._with_pictures_refs(image_dir=image_dir, page_no=None)
|
|
131
|
+
for pic in doc_with_refs.pictures:
|
|
132
|
+
if pic.image is not None and pic.image.uri is not None:
|
|
133
|
+
# For remote storage, URI should not be a UPath (can't serialize)
|
|
134
|
+
assert not is_remote_path(pic.image.uri)
|
|
135
|
+
|
|
136
|
+
# Nested path support
|
|
137
|
+
nested_path = memory_base / "a" / "b" / "c" / "doc.json"
|
|
138
|
+
sample_doc.save_as_json(nested_path)
|
|
139
|
+
assert nested_path.exists()
|
|
140
|
+
|
|
141
|
+
# Empty document
|
|
142
|
+
empty_doc = DoclingDocument(name="Empty")
|
|
143
|
+
empty_path = memory_base / "empty.json"
|
|
144
|
+
empty_doc.save_as_json(empty_path)
|
|
145
|
+
loaded_empty = DoclingDocument.load_from_json(empty_path)
|
|
146
|
+
assert loaded_empty.name == "Empty"
|
|
147
|
+
assert len(loaded_empty.texts) == 0
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def test_upath_include_page_images(memory_base):
|
|
151
|
+
"""Test _with_pictures_refs() with include_page_images=True on a remote path.
|
|
152
|
+
|
|
153
|
+
Ensures that page images are saved via BytesIO (UPath-compatible) and that
|
|
154
|
+
page.image.uri is stored as an AnyUrl, not a Path, for remote storage.
|
|
155
|
+
"""
|
|
156
|
+
page_img = PILImage.new("RGB", (64, 64), "blue")
|
|
157
|
+
image_ref = ImageRef.from_pil(image=page_img, dpi=72)
|
|
158
|
+
|
|
159
|
+
doc = DoclingDocument(name="PageImageDoc")
|
|
160
|
+
doc.add_page(page_no=1, size=Size(width=64, height=64), image=image_ref)
|
|
161
|
+
|
|
162
|
+
image_dir = memory_base / "page_images"
|
|
163
|
+
|
|
164
|
+
result = doc._with_pictures_refs(
|
|
165
|
+
image_dir=image_dir,
|
|
166
|
+
page_no=None,
|
|
167
|
+
include_page_images=True,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
# The image directory must have been created and contain the page image file
|
|
171
|
+
assert image_dir.exists()
|
|
172
|
+
saved_files = list(image_dir.iterdir())
|
|
173
|
+
assert len(saved_files) == 1, "Expected exactly one page image to be saved"
|
|
174
|
+
assert saved_files[0].name.endswith(".png")
|
|
175
|
+
|
|
176
|
+
# For a remote path the URI must be an AnyUrl, not a plain Path or UPath,
|
|
177
|
+
# so that the document remains JSON-serialisable
|
|
178
|
+
page = result.pages[1]
|
|
179
|
+
assert page.image is not None
|
|
180
|
+
assert page.image.uri is not None
|
|
181
|
+
assert isinstance(page.image.uri, AnyUrl), f"Expected AnyUrl for remote page image URI, got {type(page.image.uri)}"
|
|
182
|
+
assert not is_remote_path(page.image.uri)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{docling_core-2.85.0 → docling_core-2.86.0}/docling_core/experimental/serializer/__init__.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
|
{docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/chunker/chunk_expander.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/chunker/code_chunking/_utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/chunker/hierarchical_chunker.py
RENAMED
|
File without changes
|
{docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/chunker/hybrid_chunker.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/chunker/tokenizer/__init__.py
RENAMED
|
File without changes
|
{docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/chunker/tokenizer/base.py
RENAMED
|
File without changes
|
{docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/chunker/tokenizer/huggingface.py
RENAMED
|
File without changes
|
{docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/chunker/tokenizer/openai.py
RENAMED
|
File without changes
|
{docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/deserializer/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/profiler/doc_profiler.py
RENAMED
|
File without changes
|
|
File without changes
|
{docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/serializer/_doclang_utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/serializer/html_styles.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/serializer/markdown_excel.py
RENAMED
|
File without changes
|
{docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/serializer/plain_text.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/visualizer/layout_visualizer.py
RENAMED
|
File without changes
|
|
File without changes
|
{docling_core-2.85.0 → docling_core-2.86.0}/docling_core/transforms/visualizer/table_visualizer.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
|
|
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
|