docling-core 2.97.2__tar.gz → 2.98.1__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.97.2 → docling_core-2.98.1}/PKG-INFO +1 -1
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/deserializer/doclang.py +74 -20
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/serializer/doclang.py +247 -65
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/serializer/markdown.py +82 -3
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/doc/__init__.py +1 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/doc/base.py +7 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/doc/common/origin.py +2 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/doc/document.py +11 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/doc/webvtt.py +38 -6
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core.egg-info/PKG-INFO +1 -1
- {docling_core-2.97.2 → docling_core-2.98.1}/pyproject.toml +1 -1
- {docling_core-2.97.2 → docling_core-2.98.1}/tests/test_serialization.py +123 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/tests/test_serialization_doclang.py +116 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/tests/test_webvtt.py +30 -1
- {docling_core-2.97.2 → docling_core-2.98.1}/LICENSE +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/README.md +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/__init__.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/cli/__init__.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/cli/serialize.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/cli/view.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/compat.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/experimental/__init__.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/experimental/serializer/__init__.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/experimental/serializer/outline.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/py.typed +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/search/__init__.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/search/package.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/__init__.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/chunker/__init__.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/chunker/base.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/chunker/chunk_expander.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/chunker/code_chunking/__init__.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/chunker/code_chunking/_language_code_chunkers.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/chunker/code_chunking/_utils.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/chunker/code_chunking/base_code_chunking_strategy.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/chunker/code_chunking/code_chunk.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/chunker/code_chunking/standard_code_chunking_strategy.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/chunker/doc_chunk.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/chunker/hierarchical_chunker.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/chunker/hybrid_chunker.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/chunker/line_chunker.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/chunker/page_chunker.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/chunker/tokenizer/__init__.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/chunker/tokenizer/base.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/chunker/tokenizer/huggingface.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/chunker/tokenizer/openai.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/deserializer/__init__.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/deserializer/base.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/deserializer/doclang_source_mapping.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/profiler/__init__.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/profiler/doc_profiler.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/serializer/__init__.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/serializer/_doclang_utils.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/serializer/azure.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/serializer/base.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/serializer/common.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/serializer/doctags.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/serializer/html.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/serializer/html_styles.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/serializer/latex.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/serializer/markdown_excel.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/serializer/plain_text.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/serializer/webvtt.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/visualizer/__init__.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/visualizer/base.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/visualizer/key_value_visualizer.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/visualizer/layout_visualizer.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/visualizer/reading_order_visualizer.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/transforms/visualizer/table_visualizer.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/__init__.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/base.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/doc/common/__init__.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/doc/common/annotations.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/doc/common/constants.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/doc/common/content_layer.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/doc/common/formatting.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/doc/common/meta.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/doc/common/reference.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/doc/common/scalars.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/doc/common/source.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/doc/doctags.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/doc/items/__init__.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/doc/items/code.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/doc/items/content.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/doc/items/form.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/doc/items/group.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/doc/items/key_value.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/doc/items/node.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/doc/items/picture/__init__.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/doc/items/picture/charts.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/doc/items/picture/classification.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/doc/items/picture/meta.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/doc/items/picture/molecule.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/doc/items/picture/picture.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/doc/items/table/__init__.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/doc/items/table/table.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/doc/items/table/table_data.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/doc/items/text.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/doc/labels.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/doc/page.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/doc/tokens.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/doc/utils.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/io/__init__.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/legacy_doc/__init__.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/legacy_doc/base.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/types/legacy_doc/document.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/utils/__init__.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/utils/alias.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/utils/file.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/utils/generate_docs.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/utils/generate_jsonschema.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/utils/legacy.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/utils/settings.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core/utils/validators.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core.egg-info/SOURCES.txt +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core.egg-info/dependency_links.txt +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core.egg-info/entry_points.txt +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core.egg-info/requires.txt +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/docling_core.egg-info/top_level.txt +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/setup.cfg +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/tests/test_azure_serializer.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/tests/test_base.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/tests/test_chunk_expander.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/tests/test_cli_serialize.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/tests/test_code_chunker.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/tests/test_code_chunking_strategy.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/tests/test_compat.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/tests/test_data_gen_flag.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/tests/test_deserializer_doclang.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/tests/test_deserializer_doclang_source_mapping.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/tests/test_doc_base.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/tests/test_doclang_archive.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/tests/test_docling_doc.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/tests/test_doctags_load.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/tests/test_hierarchical_chunker.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/tests/test_hierarchy.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/tests/test_hybrid_chunker.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/tests/test_latex_serialization.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/tests/test_line_chunker.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/tests/test_metadata.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/tests/test_otsl_table_export.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/tests/test_page.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/tests/test_page_chunker.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/tests/test_plain_text_serialization.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/tests/test_profiler.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/tests/test_regions_to_table.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/tests/test_serialization_doctag.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/tests/test_serialization_outline.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/tests/test_tokens.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/tests/test_upath_support.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/tests/test_utils.py +0 -0
- {docling_core-2.97.2 → docling_core-2.98.1}/tests/test_visualization.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: docling-core
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.98.1
|
|
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>
|
|
@@ -353,11 +353,14 @@ class DocLangDocDeserializer(BaseDocDeserializer, BaseModel):
|
|
|
353
353
|
# holding a picture or a table is not a float.
|
|
354
354
|
if group_name := el.getAttribute(DocLangAttributeKey.NAME.value):
|
|
355
355
|
self._parse_named_group(doc=doc, el=el, parent=parent, name=group_name)
|
|
356
|
-
# Float +
|
|
357
|
-
elif
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
356
|
+
# Float + satellites (extra captions, footnotes): parse as one unit (not a Docling GroupItem).
|
|
357
|
+
elif (host_el := self._float_group_host(el)) is not None:
|
|
358
|
+
if host_el.tagName == DocLangToken.PICTURE.value:
|
|
359
|
+
self._parse_picture(doc=doc, el=el, parent=parent)
|
|
360
|
+
elif host_el.tagName == DocLangToken.CODE.value:
|
|
361
|
+
self._parse_code_group(doc=doc, el=el, code_el=host_el, parent=parent)
|
|
362
|
+
else:
|
|
363
|
+
self._parse_table(doc=doc, el=el, parent=parent)
|
|
361
364
|
else:
|
|
362
365
|
self._walk_children(doc=doc, el=el, parent=parent)
|
|
363
366
|
elif name in {DocLangToken.TABLE.value, DocLangToken.INDEX.value}:
|
|
@@ -482,6 +485,8 @@ class DocLangDocDeserializer(BaseDocDeserializer, BaseModel):
|
|
|
482
485
|
content_layer=content_layer,
|
|
483
486
|
)
|
|
484
487
|
self._apply_initial_text_provenance(item, text=code_text, prov_list=prov_list)
|
|
488
|
+
if (caption := self._extract_caption(doc=doc, el=el)) is not None:
|
|
489
|
+
item.captions.append(caption.get_ref())
|
|
485
490
|
if thread_id:
|
|
486
491
|
self._register_thread(thread_id=thread_id, host=nm, item=item)
|
|
487
492
|
self._apply_custom_meta_from_element(item=item, el=el)
|
|
@@ -606,6 +611,7 @@ class DocLangDocDeserializer(BaseDocDeserializer, BaseModel):
|
|
|
606
611
|
DocLangToken.LOCATION.value,
|
|
607
612
|
DocLangToken.LAYER.value,
|
|
608
613
|
DocLangToken.LABEL.value,
|
|
614
|
+
DocLangToken.CAPTION.value,
|
|
609
615
|
}:
|
|
610
616
|
continue
|
|
611
617
|
elif nm_child == DocLangToken.BR.value:
|
|
@@ -1463,23 +1469,23 @@ class DocLangDocDeserializer(BaseDocDeserializer, BaseModel):
|
|
|
1463
1469
|
return DocItemLabel.TABLE
|
|
1464
1470
|
|
|
1465
1471
|
def _parse_table(self, *, doc: DoclingDocument, el: Element, parent: NodeItem | None) -> None:
|
|
1466
|
-
"""Parse ``<table>``, ``<index>``, or a ``<group>`` wrapping them (with
|
|
1472
|
+
"""Parse ``<table>``, ``<index>``, or a ``<group>`` wrapping them (with satellites)."""
|
|
1467
1473
|
otsl_el: Element | None
|
|
1474
|
+
extra_captions: list[TextItem] = []
|
|
1468
1475
|
footnotes: list[TextItem] = []
|
|
1469
1476
|
if el.tagName in {DocLangToken.TABLE.value, DocLangToken.INDEX.value}:
|
|
1470
1477
|
caption = self._extract_caption(doc=doc, el=el)
|
|
1471
1478
|
otsl_el = el
|
|
1472
1479
|
table_label = self._table_label_from_otsl_element(el)
|
|
1473
1480
|
else:
|
|
1474
|
-
footnotes = self.
|
|
1481
|
+
extra_captions, footnotes = self._extract_satellites(doc=doc, el=el)
|
|
1475
1482
|
otsl_el = self._first_child(el, DocLangToken.TABLE.value) or self._first_child(el, DocLangToken.INDEX.value)
|
|
1476
1483
|
caption = self._extract_caption(doc=doc, el=el)
|
|
1477
1484
|
if caption is None and otsl_el is not None:
|
|
1478
1485
|
caption = self._extract_caption(doc=doc, el=otsl_el)
|
|
1479
1486
|
if otsl_el is None:
|
|
1480
1487
|
tbl = doc.add_table(data=TableData(), caption=caption, parent=parent)
|
|
1481
|
-
|
|
1482
|
-
tbl.footnotes.append(ftn.get_ref())
|
|
1488
|
+
self._attach_satellites(item=tbl, captions=extra_captions, footnotes=footnotes)
|
|
1483
1489
|
return
|
|
1484
1490
|
table_label = self._table_label_from_otsl_element(otsl_el)
|
|
1485
1491
|
|
|
@@ -1543,18 +1549,18 @@ class DocLangDocDeserializer(BaseDocDeserializer, BaseModel):
|
|
|
1543
1549
|
tbl.prov.append(p)
|
|
1544
1550
|
if thread_id:
|
|
1545
1551
|
self._register_thread(thread_id=thread_id, host=table_host, item=tbl)
|
|
1546
|
-
|
|
1547
|
-
tbl.footnotes.append(ftn.get_ref())
|
|
1552
|
+
self._attach_satellites(item=tbl, captions=extra_captions, footnotes=footnotes)
|
|
1548
1553
|
|
|
1549
1554
|
def _parse_picture(self, *, doc: DoclingDocument, el: Element, parent: NodeItem | None) -> None:
|
|
1550
|
-
"""Parse ``<picture>`` or a ``<group>`` wrapping it (with
|
|
1555
|
+
"""Parse ``<picture>`` or a ``<group>`` wrapping it (with satellites)."""
|
|
1551
1556
|
picture_el: Element | None
|
|
1557
|
+
extra_captions: list[TextItem] = []
|
|
1552
1558
|
footnotes: list[TextItem] = []
|
|
1553
1559
|
if el.tagName == DocLangToken.PICTURE.value:
|
|
1554
1560
|
caption = self._extract_caption(doc=doc, el=el)
|
|
1555
1561
|
picture_el = el
|
|
1556
1562
|
else:
|
|
1557
|
-
footnotes = self.
|
|
1563
|
+
extra_captions, footnotes = self._extract_satellites(doc=doc, el=el)
|
|
1558
1564
|
picture_el = self._first_child(el, DocLangToken.PICTURE.value)
|
|
1559
1565
|
caption = self._extract_caption(doc=doc, el=el)
|
|
1560
1566
|
if caption is None and picture_el is not None:
|
|
@@ -1576,8 +1582,7 @@ class DocLangDocDeserializer(BaseDocDeserializer, BaseModel):
|
|
|
1576
1582
|
self._source_recorder.bind_item(picture_el, pic)
|
|
1577
1583
|
for p in prov_list[1:]:
|
|
1578
1584
|
pic.prov.append(p)
|
|
1579
|
-
|
|
1580
|
-
pic.footnotes.append(ftn.get_ref())
|
|
1585
|
+
self._attach_satellites(item=pic, captions=extra_captions, footnotes=footnotes)
|
|
1581
1586
|
|
|
1582
1587
|
if picture_el is not None:
|
|
1583
1588
|
if label_val := self._extract_label_value(el=picture_el):
|
|
@@ -1763,23 +1768,72 @@ class DocLangDocDeserializer(BaseDocDeserializer, BaseModel):
|
|
|
1763
1768
|
self._source_recorder.bind_item(cap_el, item)
|
|
1764
1769
|
return item
|
|
1765
1770
|
|
|
1766
|
-
def
|
|
1771
|
+
def _float_group_host(self, el: Element) -> Element | None:
|
|
1772
|
+
"""Return the host of a float group, or ``None`` if ``el`` is not one.
|
|
1773
|
+
|
|
1774
|
+
DocLang has no dedicated construct for a float's extra captions and footnotes
|
|
1775
|
+
yet, so by convention they are emitted as ``<text>`` and ``<footnote>`` siblings
|
|
1776
|
+
of the host inside an unnamed ``<group>``. Such a group wraps exactly one
|
|
1777
|
+
``<picture>``, ``<table>``, ``<index>`` or ``<code>`` and nothing but satellites
|
|
1778
|
+
besides it.
|
|
1779
|
+
"""
|
|
1780
|
+
host_tags = {
|
|
1781
|
+
DocLangToken.PICTURE.value,
|
|
1782
|
+
DocLangToken.TABLE.value,
|
|
1783
|
+
DocLangToken.INDEX.value,
|
|
1784
|
+
DocLangToken.CODE.value,
|
|
1785
|
+
}
|
|
1786
|
+
satellite_tags = {DocLangToken.TEXT.value, DocLangToken.FOOTNOTE.value, DocLangToken.CAPTION.value}
|
|
1787
|
+
hosts: list[Element] = []
|
|
1788
|
+
for node in el.childNodes:
|
|
1789
|
+
if not isinstance(node, Element):
|
|
1790
|
+
continue
|
|
1791
|
+
if node.tagName in host_tags:
|
|
1792
|
+
hosts.append(node)
|
|
1793
|
+
elif node.tagName not in satellite_tags:
|
|
1794
|
+
return None
|
|
1795
|
+
return hosts[0] if len(hosts) == 1 else None
|
|
1796
|
+
|
|
1797
|
+
def _extract_satellites(self, *, doc: DoclingDocument, el: Element) -> tuple[list[TextItem], list[TextItem]]:
|
|
1798
|
+
"""Extract the satellites of a float group: ``<text>`` as captions, ``<footnote>`` as footnotes."""
|
|
1799
|
+
labels = {
|
|
1800
|
+
DocLangToken.TEXT.value: DocItemLabel.CAPTION,
|
|
1801
|
+
DocLangToken.FOOTNOTE.value: DocItemLabel.FOOTNOTE,
|
|
1802
|
+
}
|
|
1803
|
+
captions: list[TextItem] = []
|
|
1767
1804
|
footnotes: list[TextItem] = []
|
|
1768
1805
|
for node in el.childNodes:
|
|
1769
|
-
if isinstance(node, Element) and node.tagName
|
|
1806
|
+
if isinstance(node, Element) and (label := labels.get(node.tagName)) is not None:
|
|
1770
1807
|
text = self._get_text(node).strip()
|
|
1771
1808
|
if text:
|
|
1772
1809
|
prov_list = self._extract_provenance(doc=doc, el=node)
|
|
1773
1810
|
item = doc.add_text(
|
|
1774
|
-
label=
|
|
1811
|
+
label=label,
|
|
1775
1812
|
text=text,
|
|
1776
1813
|
prov=(prov_list[0] if prov_list else None),
|
|
1777
1814
|
)
|
|
1778
1815
|
for p in prov_list[1:]:
|
|
1779
1816
|
item.prov.append(p)
|
|
1780
1817
|
self._source_recorder.bind_item(node, item)
|
|
1781
|
-
footnotes.append(item)
|
|
1782
|
-
return footnotes
|
|
1818
|
+
(captions if label == DocItemLabel.CAPTION else footnotes).append(item)
|
|
1819
|
+
return captions, footnotes
|
|
1820
|
+
|
|
1821
|
+
def _parse_code_group(
|
|
1822
|
+
self, *, doc: DoclingDocument, el: Element, code_el: Element, parent: NodeItem | None
|
|
1823
|
+
) -> None:
|
|
1824
|
+
"""Parse a ``<group>`` wrapping a ``<code>`` with its satellites."""
|
|
1825
|
+
extra_captions, footnotes = self._extract_satellites(doc=doc, el=el)
|
|
1826
|
+
num_texts = len(doc.texts)
|
|
1827
|
+
self._parse_text_like(doc=doc, el=code_el, parent=parent)
|
|
1828
|
+
if (code := next((t for t in doc.texts[num_texts:] if isinstance(t, CodeItem)), None)) is not None:
|
|
1829
|
+
self._attach_satellites(item=code, captions=extra_captions, footnotes=footnotes)
|
|
1830
|
+
|
|
1831
|
+
@staticmethod
|
|
1832
|
+
def _attach_satellites(*, item: FloatingItem, captions: list[TextItem], footnotes: list[TextItem]) -> None:
|
|
1833
|
+
for cap in captions:
|
|
1834
|
+
item.captions.append(cap.get_ref())
|
|
1835
|
+
for ftn in footnotes:
|
|
1836
|
+
item.footnotes.append(ftn.get_ref())
|
|
1783
1837
|
|
|
1784
1838
|
def _first_child(self, el: Element, tag_name: str) -> Element | None:
|
|
1785
1839
|
for node in el.childNodes:
|
|
@@ -89,6 +89,7 @@ from docling_core.types.doc import (
|
|
|
89
89
|
PictureItem,
|
|
90
90
|
PictureMeta,
|
|
91
91
|
ProvenanceItem,
|
|
92
|
+
RefItem,
|
|
92
93
|
Script,
|
|
93
94
|
SectionHeaderItem,
|
|
94
95
|
Size,
|
|
@@ -397,13 +398,158 @@ def _serialize_floating_caption_head(
|
|
|
397
398
|
params: DocLangParams,
|
|
398
399
|
**kwargs: Any,
|
|
399
400
|
) -> str:
|
|
400
|
-
"""Serialize referenced caption
|
|
401
|
-
|
|
401
|
+
"""Serialize the first referenced caption for inclusion in the host element head.
|
|
402
|
+
|
|
403
|
+
The element head holds at most one ``<caption>``; any further captions are emitted
|
|
404
|
+
next to the host by ``_serialize_floating_satellites``.
|
|
405
|
+
"""
|
|
406
|
+
if not params.add_referenced_caption:
|
|
407
|
+
return ""
|
|
408
|
+
captions = _resolve_satellites(refs=item.captions, doc_serializer=doc_serializer, doc=doc, **kwargs)
|
|
409
|
+
if not captions:
|
|
402
410
|
return ""
|
|
403
|
-
|
|
411
|
+
head_item = item.model_copy(update={"captions": [captions[0].get_ref()]})
|
|
412
|
+
cap_res = doc_serializer.serialize_captions(item=head_item, **kwargs)
|
|
404
413
|
return cap_res.text or ""
|
|
405
414
|
|
|
406
415
|
|
|
416
|
+
def _resolve_satellites(
|
|
417
|
+
*,
|
|
418
|
+
refs: list[RefItem],
|
|
419
|
+
doc_serializer: BaseDocSerializer,
|
|
420
|
+
doc: DoclingDocument,
|
|
421
|
+
**kwargs: Any,
|
|
422
|
+
) -> list[TextItem]:
|
|
423
|
+
"""Resolve caption or footnote references, skipping excluded ones."""
|
|
424
|
+
excluded = doc_serializer.get_excluded_refs(**kwargs)
|
|
425
|
+
return [sat for ref in refs if ref.cref not in excluded and isinstance(sat := ref.resolve(doc), TextItem)]
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
def _serialize_satellite(
|
|
429
|
+
*,
|
|
430
|
+
item: TextItem,
|
|
431
|
+
tag: DocLangToken,
|
|
432
|
+
content_type: ContentType,
|
|
433
|
+
doc: DoclingDocument,
|
|
434
|
+
params: DocLangParams,
|
|
435
|
+
) -> str:
|
|
436
|
+
"""Serialize a caption or footnote of a floating item as a standalone element."""
|
|
437
|
+
head = _element_head_prefix(item=item, doc=doc, params=params)
|
|
438
|
+
content = _escape_text(item.text, params) if item.text and content_type in params.content_types else ""
|
|
439
|
+
text = f"{head}{content}"
|
|
440
|
+
return _wrap(text=text, wrap_tag=tag.value) if text else ""
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
def _top_left_bbox(prov: ProvenanceItem, doc: DoclingDocument) -> BoundingBox | None:
|
|
444
|
+
"""Return the provenance bbox in top-left origin, or ``None`` if the page height is unknown."""
|
|
445
|
+
if prov.bbox.coord_origin == CoordOrigin.TOPLEFT:
|
|
446
|
+
return prov.bbox
|
|
447
|
+
page = doc.pages.get(prov.page_no)
|
|
448
|
+
if page is None:
|
|
449
|
+
return None
|
|
450
|
+
return prov.bbox.to_top_left_origin(page_height=page.size.height)
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
def _satellite_placement(
|
|
454
|
+
*,
|
|
455
|
+
sat: DocItem,
|
|
456
|
+
host: FloatingItem,
|
|
457
|
+
doc: DoclingDocument,
|
|
458
|
+
) -> tuple[bool, tuple[int, float, float]] | None:
|
|
459
|
+
"""Place a satellite (extra caption or footnote) relative to its host by layout.
|
|
460
|
+
|
|
461
|
+
The satellite goes after the host if it sits on a later page, entirely below the
|
|
462
|
+
host, or (when overlapping it vertically) entirely to its right; if it overlaps
|
|
463
|
+
the host in both directions, if its bbox center is lower. Returns this decision
|
|
464
|
+
together with the satellite's reading-order sort key, or ``None`` if the position
|
|
465
|
+
cannot be determined.
|
|
466
|
+
"""
|
|
467
|
+
if not sat.prov or not host.prov:
|
|
468
|
+
return None
|
|
469
|
+
sat_prov = sat.prov[0]
|
|
470
|
+
if (sat_bbox := _top_left_bbox(sat_prov, doc)) is None:
|
|
471
|
+
return None
|
|
472
|
+
key = (sat_prov.page_no, sat_bbox.t, sat_bbox.l)
|
|
473
|
+
host_pages = [p.page_no for p in host.prov]
|
|
474
|
+
if sat_prov.page_no < min(host_pages):
|
|
475
|
+
return False, key
|
|
476
|
+
if sat_prov.page_no > max(host_pages):
|
|
477
|
+
return True, key
|
|
478
|
+
host_prov = next((p for p in host.prov if p.page_no == sat_prov.page_no), None)
|
|
479
|
+
if host_prov is None or (host_bbox := _top_left_bbox(host_prov, doc)) is None:
|
|
480
|
+
return None
|
|
481
|
+
# clearly above or below the host
|
|
482
|
+
if sat_bbox.b <= host_bbox.t:
|
|
483
|
+
return False, key
|
|
484
|
+
if sat_bbox.t >= host_bbox.b:
|
|
485
|
+
return True, key
|
|
486
|
+
# beside the host
|
|
487
|
+
if sat_bbox.r <= host_bbox.l:
|
|
488
|
+
return False, key
|
|
489
|
+
if sat_bbox.l >= host_bbox.r:
|
|
490
|
+
return True, key
|
|
491
|
+
# overlapping the host
|
|
492
|
+
sat_y = (sat_bbox.t + sat_bbox.b) / 2
|
|
493
|
+
host_y = (host_bbox.t + host_bbox.b) / 2
|
|
494
|
+
return sat_y > host_y, key
|
|
495
|
+
|
|
496
|
+
|
|
497
|
+
def _serialize_floating_satellites(
|
|
498
|
+
*,
|
|
499
|
+
item: FloatingItem,
|
|
500
|
+
doc_serializer: BaseDocSerializer,
|
|
501
|
+
doc: DoclingDocument,
|
|
502
|
+
params: DocLangParams,
|
|
503
|
+
**kwargs: Any,
|
|
504
|
+
) -> tuple[list[SerializationResult], list[SerializationResult]]:
|
|
505
|
+
"""Serialize the extra captions and the footnotes of a floating item.
|
|
506
|
+
|
|
507
|
+
Captions beyond the first one (which goes in the host element head) become
|
|
508
|
+
``<text>`` elements, footnotes become ``<footnote>`` elements. Each is placed
|
|
509
|
+
before or after the host by layout (see ``_satellite_placement``), and sorted in
|
|
510
|
+
reading order (page, top, left) on either side. Satellites without a determinable
|
|
511
|
+
position fall back to extra captions before and footnotes after the host.
|
|
512
|
+
|
|
513
|
+
Returns the ``(before, after)`` serialization results.
|
|
514
|
+
"""
|
|
515
|
+
sats: list[tuple[TextItem, str, bool]] = []
|
|
516
|
+
if params.add_referenced_caption:
|
|
517
|
+
captions = _resolve_satellites(refs=item.captions, doc_serializer=doc_serializer, doc=doc, **kwargs)
|
|
518
|
+
for cap in captions[1:]:
|
|
519
|
+
text = _serialize_satellite(
|
|
520
|
+
item=cap, tag=DocLangToken.TEXT, content_type=ContentType.REF_CAPTION, doc=doc, params=params
|
|
521
|
+
)
|
|
522
|
+
sats.append((cap, text, False))
|
|
523
|
+
if params.add_referenced_footnote:
|
|
524
|
+
footnotes = _resolve_satellites(refs=item.footnotes, doc_serializer=doc_serializer, doc=doc, **kwargs)
|
|
525
|
+
for ftn in footnotes:
|
|
526
|
+
text = _serialize_satellite(
|
|
527
|
+
item=ftn, tag=DocLangToken.FOOTNOTE, content_type=ContentType.REF_FOOTNOTE, doc=doc, params=params
|
|
528
|
+
)
|
|
529
|
+
sats.append((ftn, text, True))
|
|
530
|
+
|
|
531
|
+
before_unplaced: list[SerializationResult] = []
|
|
532
|
+
after_unplaced: list[SerializationResult] = []
|
|
533
|
+
before: list[tuple[tuple[int, float, float], SerializationResult]] = []
|
|
534
|
+
after: list[tuple[tuple[int, float, float], SerializationResult]] = []
|
|
535
|
+
for sat, text, default_after in sats:
|
|
536
|
+
if not text:
|
|
537
|
+
continue
|
|
538
|
+
res = create_ser_result(text=text, span_source=sat)
|
|
539
|
+
if (placement := _satellite_placement(sat=sat, host=item, doc=doc)) is None:
|
|
540
|
+
(after_unplaced if default_after else before_unplaced).append(res)
|
|
541
|
+
else:
|
|
542
|
+
goes_after, key = placement
|
|
543
|
+
(after if goes_after else before).append((key, res))
|
|
544
|
+
|
|
545
|
+
before.sort(key=lambda x: x[0])
|
|
546
|
+
after.sort(key=lambda x: x[0])
|
|
547
|
+
return (
|
|
548
|
+
before_unplaced + [res for _, res in before],
|
|
549
|
+
[res for _, res in after] + after_unplaced,
|
|
550
|
+
)
|
|
551
|
+
|
|
552
|
+
|
|
407
553
|
def _element_label_for_serialization(
|
|
408
554
|
*,
|
|
409
555
|
raw_label: str | None,
|
|
@@ -723,6 +869,7 @@ class DocLangTextSerializer(BaseModel, BaseTextSerializer):
|
|
|
723
869
|
visited=visited,
|
|
724
870
|
is_inline_scope=is_inline_scope,
|
|
725
871
|
thread_id=thread_id,
|
|
872
|
+
include_caption_head=idp == 0,
|
|
726
873
|
**kwargs,
|
|
727
874
|
)
|
|
728
875
|
res.append(tres)
|
|
@@ -814,14 +961,17 @@ class DocLangTextSerializer(BaseModel, BaseTextSerializer):
|
|
|
814
961
|
is_inline_scope: bool = False,
|
|
815
962
|
visited: set[str] | None = None,
|
|
816
963
|
thread_id: str | None = None,
|
|
964
|
+
include_caption_head: bool = True,
|
|
817
965
|
**kwargs: Any,
|
|
818
966
|
) -> SerializationResult:
|
|
819
967
|
"""Serialize a ``TextItem`` into DocLang markup.
|
|
820
968
|
|
|
821
969
|
Depending on parameters, emits meta blocks, location tokens, and the
|
|
822
970
|
item's textual content (prefixing code language for ``CodeItem``). For
|
|
823
|
-
floating items,
|
|
824
|
-
|
|
971
|
+
floating items, the first caption goes in the element head and further
|
|
972
|
+
captions and footnotes are emitted around it (see
|
|
973
|
+
``_serialize_floating_satellites``). The result can be wrapped in a tag
|
|
974
|
+
derived from the item's label when applicable.
|
|
825
975
|
|
|
826
976
|
Args:
|
|
827
977
|
item: The text-like item to serialize.
|
|
@@ -935,6 +1085,12 @@ class DocLangTextSerializer(BaseModel, BaseTextSerializer):
|
|
|
935
1085
|
params=params,
|
|
936
1086
|
)
|
|
937
1087
|
|
|
1088
|
+
caption_head = ""
|
|
1089
|
+
if include_caption_head and isinstance(item, FloatingItem):
|
|
1090
|
+
caption_head = _serialize_floating_caption_head(
|
|
1091
|
+
item=item, doc_serializer=doc_serializer, doc=doc, params=params, **kwargs
|
|
1092
|
+
)
|
|
1093
|
+
|
|
938
1094
|
include_href = not is_inline_scope
|
|
939
1095
|
if not skip_location:
|
|
940
1096
|
parts.append(
|
|
@@ -943,6 +1099,7 @@ class DocLangTextSerializer(BaseModel, BaseTextSerializer):
|
|
|
943
1099
|
doc=doc,
|
|
944
1100
|
params=params,
|
|
945
1101
|
label_value=code_label,
|
|
1102
|
+
caption_text=caption_head or None,
|
|
946
1103
|
custom_text=custom_head or None,
|
|
947
1104
|
include_href=include_href,
|
|
948
1105
|
thread_id=thread_id,
|
|
@@ -966,24 +1123,46 @@ class DocLangTextSerializer(BaseModel, BaseTextSerializer):
|
|
|
966
1123
|
or (isinstance(item, FormulaItem) and ContentType.TEXT_FORMULA in params.content_types)
|
|
967
1124
|
or (not isinstance(item, CodeItem | FormulaItem) and ContentType.TEXT_OTHER in params.content_types)
|
|
968
1125
|
):
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
1126
|
+
first_child_item = item.children[0].resolve(doc) if item.children else None
|
|
1127
|
+
if isinstance(first_child_item, InlineGroup):
|
|
1128
|
+
# Only serialize the first child (InlineGroup) as the text content
|
|
1129
|
+
# Other children are hierarchical subordinates and will be serialized separately.
|
|
1130
|
+
# The item's own text, when present, precedes the runs within the same element:
|
|
1131
|
+
# emitting the runs as siblings instead yields invalid DocLang and loses content.
|
|
1132
|
+
inline_part = doc_serializer.serialize(item=first_child_item, visited=my_visited, **kwargs).text
|
|
1133
|
+
if item.text:
|
|
1134
|
+
own_part = _escape_text(item.text, params)
|
|
1135
|
+
own_part = doc_serializer.post_process(
|
|
1136
|
+
text=own_part,
|
|
1137
|
+
formatting=item.formatting,
|
|
1138
|
+
hyperlink=None,
|
|
1139
|
+
)
|
|
1140
|
+
if item.label == DocItemLabel.HANDWRITTEN_TEXT:
|
|
1141
|
+
own_part = _wrap(text=own_part, wrap_tag=DocLangToken.HANDWRITING.value)
|
|
1142
|
+
elif item.label in [
|
|
1143
|
+
DocItemLabel.CHECKBOX_SELECTED,
|
|
1144
|
+
DocItemLabel.CHECKBOX_UNSELECTED,
|
|
1145
|
+
]:
|
|
1146
|
+
# Add checkbox token before the text
|
|
1147
|
+
checkbox_token = DocLangVocabulary._create_checkbox_token(
|
|
1148
|
+
selected=(item.label == DocItemLabel.CHECKBOX_SELECTED)
|
|
1149
|
+
)
|
|
1150
|
+
own_part = checkbox_token + own_part
|
|
1151
|
+
# Join with a plain space rather than the pretty-printing delimiter:
|
|
1152
|
+
# own_part and inline_part are two runs of the same inline flow, so
|
|
1153
|
+
# they need a real word boundary even in minimized (non-pretty) output.
|
|
1154
|
+
text_part = " ".join([p for p in (own_part, inline_part) if p])
|
|
978
1155
|
else:
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
1156
|
+
text_part = inline_part
|
|
1157
|
+
elif item.children and not item.text:
|
|
1158
|
+
# Serialize all children as text content
|
|
1159
|
+
sub_parts: list[str] = []
|
|
1160
|
+
for child_ref in item.children:
|
|
1161
|
+
child_item = child_ref.resolve(doc)
|
|
1162
|
+
if isinstance(item, ListItem) and _list_item_segment_sibling(child_item):
|
|
1163
|
+
continue
|
|
1164
|
+
sub_parts.append(doc_serializer.serialize(item=child_item, visited=my_visited, **kwargs).text)
|
|
1165
|
+
text_part = _get_delim(params=params).join(sub_parts)
|
|
987
1166
|
else:
|
|
988
1167
|
text_part = _escape_text(item.text, params)
|
|
989
1168
|
text_part = doc_serializer.post_process(
|
|
@@ -1006,18 +1185,6 @@ class DocLangTextSerializer(BaseModel, BaseTextSerializer):
|
|
|
1006
1185
|
if text_part:
|
|
1007
1186
|
parts.append(text_part)
|
|
1008
1187
|
|
|
1009
|
-
if params.add_referenced_caption and isinstance(item, FloatingItem):
|
|
1010
|
-
cap_text = doc_serializer.serialize_captions(item=item, **kwargs).text
|
|
1011
|
-
if cap_text:
|
|
1012
|
-
cap_text = _escape_text(cap_text, params)
|
|
1013
|
-
parts.append(cap_text)
|
|
1014
|
-
|
|
1015
|
-
if params.add_referenced_footnote and isinstance(item, FloatingItem):
|
|
1016
|
-
ftn_text = doc_serializer.serialize_footnotes(item=item, **kwargs).text
|
|
1017
|
-
if ftn_text:
|
|
1018
|
-
ftn_text = _escape_text(ftn_text, params)
|
|
1019
|
-
parts.append(ftn_text)
|
|
1020
|
-
|
|
1021
1188
|
text_res = "".join(parts)
|
|
1022
1189
|
|
|
1023
1190
|
# Special handling for ListItems with suppress_empty_elements
|
|
@@ -1043,6 +1210,17 @@ class DocLangTextSerializer(BaseModel, BaseTextSerializer):
|
|
|
1043
1210
|
elif item.label in (DocItemLabel.FIELD_KEY, DocItemLabel.FIELD_VALUE):
|
|
1044
1211
|
text_res = _wrap_field_kv_markup_if_needed(text=text_res, item=item, doc=doc)
|
|
1045
1212
|
|
|
1213
|
+
if include_caption_head and isinstance(item, FloatingItem) and not is_inline_scope:
|
|
1214
|
+
before, after = _serialize_floating_satellites(
|
|
1215
|
+
item=item, doc_serializer=doc_serializer, doc=doc, params=params, **kwargs
|
|
1216
|
+
)
|
|
1217
|
+
if before or after:
|
|
1218
|
+
# Wrap the host with its satellites so that they can be re-attached on deserialization.
|
|
1219
|
+
text_res = _wrap(
|
|
1220
|
+
text="".join(r.text for r in before) + text_res + "".join(r.text for r in after),
|
|
1221
|
+
wrap_tag=DocLangToken.GROUP.value,
|
|
1222
|
+
)
|
|
1223
|
+
|
|
1046
1224
|
# Prepend ldiv element for ListItems (it's a delimiter, not a wrapper)
|
|
1047
1225
|
if ldiv_element:
|
|
1048
1226
|
text_res = ldiv_element + text_res
|
|
@@ -1254,19 +1432,21 @@ class DocLangPictureSerializer(BasePictureSerializer):
|
|
|
1254
1432
|
picture_open += ">"
|
|
1255
1433
|
picture_text = f"{picture_open}{inner}</{DocLangToken.PICTURE.value}>"
|
|
1256
1434
|
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
footnote_text = ftn_res.text
|
|
1262
|
-
res_parts.append(ftn_res)
|
|
1435
|
+
before, after = _serialize_floating_satellites(
|
|
1436
|
+
item=item, doc_serializer=doc_serializer, doc=doc, params=params, **kwargs
|
|
1437
|
+
)
|
|
1438
|
+
res_parts.extend(before + after)
|
|
1263
1439
|
|
|
1264
|
-
if not inner and not
|
|
1440
|
+
if not inner and not (before or after):
|
|
1265
1441
|
if params.suppress_empty_elements:
|
|
1266
1442
|
return create_ser_result()
|
|
1267
1443
|
text_res = f"<{DocLangToken.PICTURE.value}></{DocLangToken.PICTURE.value}>"
|
|
1268
|
-
elif
|
|
1269
|
-
|
|
1444
|
+
elif before or after:
|
|
1445
|
+
# Wrap the host with its satellites so that they can be re-attached on deserialization.
|
|
1446
|
+
text_res = _wrap(
|
|
1447
|
+
text="".join(r.text for r in before) + picture_text + "".join(r.text for r in after),
|
|
1448
|
+
wrap_tag=DocLangToken.GROUP.value,
|
|
1449
|
+
)
|
|
1270
1450
|
else:
|
|
1271
1451
|
text_res = picture_text
|
|
1272
1452
|
|
|
@@ -1488,19 +1668,24 @@ class DocLangTableSerializer(BaseTableSerializer):
|
|
|
1488
1668
|
table_text = _wrap(text=head + "".join(inner_parts), wrap_tag=host_token.value)
|
|
1489
1669
|
res_parts.append(create_ser_result(text=head + "".join(inner_parts), span_source=item))
|
|
1490
1670
|
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1671
|
+
before: list[SerializationResult] = []
|
|
1672
|
+
after: list[SerializationResult] = []
|
|
1673
|
+
if include_caption_head:
|
|
1674
|
+
before, after = _serialize_floating_satellites(
|
|
1675
|
+
item=item, doc_serializer=doc_serializer, doc=doc, params=params, **kwargs
|
|
1676
|
+
)
|
|
1677
|
+
res_parts.extend(before + after)
|
|
1497
1678
|
|
|
1498
|
-
if not (head or inner_parts) and not
|
|
1679
|
+
if not (head or inner_parts) and not (before or after):
|
|
1499
1680
|
if params.suppress_empty_elements:
|
|
1500
1681
|
return create_ser_result()
|
|
1501
1682
|
text_res = f"<{host_token.value}></{host_token.value}>"
|
|
1502
|
-
elif
|
|
1503
|
-
|
|
1683
|
+
elif before or after:
|
|
1684
|
+
# Wrap the host with its satellites so that they can be re-attached on deserialization.
|
|
1685
|
+
text_res = _wrap(
|
|
1686
|
+
text="".join(r.text for r in before) + table_text + "".join(r.text for r in after),
|
|
1687
|
+
wrap_tag=DocLangToken.GROUP.value,
|
|
1688
|
+
)
|
|
1504
1689
|
else:
|
|
1505
1690
|
text_res = table_text
|
|
1506
1691
|
|
|
@@ -1863,19 +2048,16 @@ class DocLangDocSerializer(DocSerializer):
|
|
|
1863
2048
|
"""Serialize the item's footnotes with DocLang location tokens."""
|
|
1864
2049
|
params = DocLangParams(**kwargs)
|
|
1865
2050
|
results: list[SerializationResult] = []
|
|
1866
|
-
for
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
if text_res:
|
|
1877
|
-
text_res = _wrap(text_res, wrap_tag=DocLangToken.FOOTNOTE.value)
|
|
1878
|
-
results.append(create_ser_result(text=text_res))
|
|
2051
|
+
for ftn in _resolve_satellites(refs=item.footnotes, doc_serializer=self, doc=self.doc, **kwargs):
|
|
2052
|
+
text_res = _serialize_satellite(
|
|
2053
|
+
item=ftn,
|
|
2054
|
+
tag=DocLangToken.FOOTNOTE,
|
|
2055
|
+
content_type=ContentType.REF_FOOTNOTE,
|
|
2056
|
+
doc=self.doc,
|
|
2057
|
+
params=params,
|
|
2058
|
+
)
|
|
2059
|
+
if text_res:
|
|
2060
|
+
results.append(create_ser_result(text=text_res))
|
|
1879
2061
|
|
|
1880
2062
|
text_res = "".join([r.text for r in results])
|
|
1881
2063
|
|