diff --git a/docling_core/types/doc/__init__.py b/docling_core/types/doc/__init__.py index 49061213..09d2fe1b 100644 --- a/docling_core/types/doc/__init__.py +++ b/docling_core/types/doc/__init__.py @@ -46,6 +46,7 @@ OrderedList, Orientation, PageItem, + PageMeta, PictureBarChartData, PictureChartData, PictureClassificationClass, diff --git a/docling_core/types/doc/document.py b/docling_core/types/doc/document.py index 6bc82850..f29108f0 100644 --- a/docling_core/types/doc/document.py +++ b/docling_core/types/doc/document.py @@ -1679,6 +1679,10 @@ class FloatingMeta(BaseMeta): description: Optional[DescriptionMetaField] = None +class PageMeta(BaseMeta): + """Metadata model for pages.""" + + class CodeMetaField(BasePrediction): """Code representation for the respective item.""" @@ -2870,6 +2874,7 @@ class PageItem(BaseModel): # only referencing items on the page size: Size image: Optional[ImageRef] = None + meta: Optional[PageMeta] = None page_no: int @@ -7539,14 +7544,22 @@ def get_text(text: str, max_text_len: int): return "\n".join(result) - def add_page(self, page_no: int, size: Size, image: Optional[ImageRef] = None) -> PageItem: + def add_page( + self, + page_no: int, + size: Size, + image: Optional[ImageRef] = None, + meta: Optional[PageMeta] = None, + ) -> PageItem: """add_page. :param page_no: int: :param size: Size: + :param image: Optional[ImageRef]: + :param meta: Optional[PageMeta]: """ - pitem = PageItem(page_no=page_no, size=size, image=image) + pitem = PageItem(page_no=page_no, size=size, image=image, meta=meta) self.pages[page_no] = pitem return pitem diff --git a/test/test_serialization.py b/test/test_serialization.py index b6bd4b1d..f2aee647 100644 --- a/test/test_serialization.py +++ b/test/test_serialization.py @@ -24,8 +24,8 @@ ) from docling_core.transforms.serializer.webvtt import WebVTTDocSerializer, WebVTTParams from docling_core.transforms.visualizer.layout_visualizer import LayoutVisualizer -from docling_core.types.doc import DoclingDocument -from docling_core.types.doc.base import ImageRefMode +from docling_core.types.doc import DoclingDocument, PageMeta +from docling_core.types.doc.base import ImageRefMode, Size from docling_core.types.doc.document import ( BaseMeta, CharSpan, @@ -74,6 +74,48 @@ def _normalize_quotes(s: str) -> str: # =============================== +def test_page_item_meta_round_trips_in_exported_document(): + doc = DoclingDocument(name="page meta") + page_meta = PageMeta( + summary=SummaryMetaField( + text="Summary of page 1", + created_by="page-summary-model", + ), + ) + page_meta.set_custom_field( + namespace="research", + name="source_scope", + value={"source_type": "10-K", "valid_as_of": "2026-06-18"}, + ) + + doc.add_page( + page_no=1, + size=Size(width=612, height=792), + meta=page_meta, + ) + + exported = doc.export_to_dict() + assert exported["pages"]["1"]["meta"]["summary"] == { + "text": "Summary of page 1", + "created_by": "page-summary-model", + } + assert exported["pages"]["1"]["meta"]["research__source_scope"] == { + "source_type": "10-K", + "valid_as_of": "2026-06-18", + } + + loaded = DoclingDocument.model_validate(exported) + assert loaded.pages[1].meta is not None + assert loaded.pages[1].meta.summary is not None + assert loaded.pages[1].meta.summary.text == "Summary of page 1" + assert loaded.pages[1].meta.get_custom_part() == { + "research__source_scope": { + "source_type": "10-K", + "valid_as_of": "2026-06-18", + }, + } + + def test_md_cross_page_list_page_break(): src = Path("./test/data/doc/activities.json") doc = DoclingDocument.load_from_json(src)