Extract hierarchical document graphs and baseline chunks from PDF
Project description
document2graph
Extract hierarchical document graphs and baseline chunks from PDF files. The package parses PDFs using Docling and produces two complementary representations:
- Document graph — text, image, and table nodes connected by weighted structural edges, capturing the layout hierarchy of the document. Every graph is guaranteed to be connected: nodes without a resolvable parent are attached to a synthetic document root node.
- Baseline chunks — flat, semantically merged chunks suitable for retrieval pipelines, with optional context enrichment.
Installation
pip install document2graph
Or from source:
pip install -e .
For development dependencies (pytest):
pip install -e ".[dev]"
Requires Python 3.11+.
Quick start
Configuration
Both extractors share the same ExtractorConfig:
from document2graph.models import ExtractorConfig
from docling.datamodel.pipeline_options import PdfPipelineOptions
config = ExtractorConfig(
pdf_path="data/pdfs/", # directory containing input PDFs
data_path="data/output/", # root directory for all output files
save_json=True, # write results to disk as JSON (default: True)
document_type="report", # arbitrary label stored in document metadata
pdfPipelineOptions=PdfPipelineOptions(), # optional Docling pipeline config
)
Metadata extraction
The metadata_config field controls how document metadata (title, authors, version, etc.) is
located inside each PDF. Each field is described by a search string and an inclusive page range
to search on. Fields set to None are skipped.
from document2graph.models import ExtractorConfig, MetadataExtractionConfig, MetadataFieldConfig
config = ExtractorConfig(
pdf_path="data/pdfs/",
data_path="data/output/",
metadata_config=MetadataExtractionConfig(
title_page=1,
version=MetadataFieldConfig(label="edition", pages=(1, 1)),
authors=MetadataFieldConfig(label="authors", pages=(1, 2)),
institutions=MetadataFieldConfig(label="affiliations", pages=(1, 2)),
bibliography=None, # not present in this document type
correspondence=MetadataFieldConfig(label="contact", pages=(2, 3)),
),
)
The default MetadataExtractionConfig() targets German-language Praxisempfehlungen PDFs
(version on page 1, authors/institutions/bibliography/correspondence on page 2).
Edge weights
Every edge in the document graph carries a weight attribute reflecting the strength of the
structural connection. The defaults can be overridden per category via edge_weights:
from document2graph.models import ExtractorConfig, EdgeWeightConfig
config = ExtractorConfig(
pdf_path="data/pdfs/",
data_path="data/output/",
edge_weights=EdgeWeightConfig(
section=1.0, # heading -> subheading
text=0.8, # heading/body -> body text, subtext, footnotes
list_item=0.6, # anchor text -> grouped list item (bullets)
media=0.9, # referencing text -> image/table (matched via caption)
unreferenced_media=0.3, # fallback heading -> image/table without a caption match
root=0.1, # document root -> otherwise disconnected node
),
)
Connectivity guarantee
Each graph contains a synthetic root node (#/document-root, labelled with the document
title). Any node whose parent cannot be resolved — as well as any component that would
otherwise be disconnected — is attached to this root with the root edge weight, so every
document graph is a single (weakly) connected component.
Extracted metadata is available on the Document object under the metadata sub-object:
result = extractor.run("my_document.pdf")
doc = result["document_metadata"]
print(doc.title)
print(doc.metadata.authors)
print(doc.metadata.version)
Document graph extraction
Processes every PDF in pdf_path and builds a NetworkX graph per document.
from document2graph.document2graph_extractor import DocumentGraphExtractor
extractor = DocumentGraphExtractor(config)
# Process all PDFs and save graphs + snippets to disk
extractor.generate_snippets()
# Or collect Snippet objects in memory as well
snippets = extractor.generate_snippets(return_snippets=True)
To process a single file and get the raw graph components:
result = extractor.run("my_document.pdf")
text_nodes = result["text_nodes"]
image_nodes = result["image_nodes"]
table_nodes = result["table_nodes"]
edges = result["edges"] # list of (parent_id, child_id, weight) tuples
metadata = result["document_metadata"]
Graphs are saved as .gexf files under <data_path>/nx_graphs/ and can be loaded with NetworkX:
import networkx as nx
G = nx.read_gexf("data/output/nx_graphs/my_document.gexf")
Baseline chunk extraction
Produces flat, retrieval-ready chunks using Docling's hybrid chunker.
from document2graph.baseline_extractor import BaselineExtractor
extractor = BaselineExtractor(config)
# Process all PDFs; saves JSON to disk
extractor.generate_baseline_chunks()
# Or return chunks in memory
all_chunks = extractor.generate_baseline_chunks(return_chunks=True)
# all_chunks["my_document.pdf"]["baseline"] -> List[Chunk]
# all_chunks["my_document.pdf"]["enriched"] -> List[Chunk] (context-enriched)
To process a single file:
chunks = extractor.extract_baseline_chunks(
filename="my_document.pdf",
baseline_description="Q1 report chunks",
enrich=True, # also produce context-enriched variants
)
Output structure
<data_path>/
├── raw_texts/ # intermediate Docling extraction output
├── nx_graphs/ # .gexf graph files (one per document)
├── snippets/ # <filename>_snippets.json (document graph output)
└── baseline_chunks/ # <filename>_baseline_chunks.json
Data models
| Model | Key fields |
|---|---|
Snippet |
snippet_id, type (text/image/table), document_id, sequence_no, label, level, page_no, bbox, text |
Chunk |
chunk_id, document_id, text, meta, baseline_description, embedding |
Document |
document_id, document_type, filename, title, metadata: DocumentMetadata |
DocumentMetadata |
version, authors, institutions, bibliography, correspondence |
MetadataExtractionConfig |
title_page, version, authors, institutions, bibliography, correspondence (each a MetadataFieldConfig) |
MetadataFieldConfig |
label (search string), pages (inclusive 1-based page range, e.g. (1, 3)) |
EdgeWeightConfig |
section, text, list_item, media, unreferenced_media, root (edge weights by category) |
Running tests
pytest
Skip slow tests that download models:
pytest -m "not slow"
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file document2graph-0.1.0.tar.gz.
File metadata
- Download URL: document2graph-0.1.0.tar.gz
- Upload date:
- Size: 21.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fe37d2364baee83a7ba0d853e7c5f78ad226b43273a0a4cdec8bd1e96a4db2c1
|
|
| MD5 |
a5ada733d9bc306a0154443b145cb850
|
|
| BLAKE2b-256 |
75d908c7f9dfa23a775494bcfb89411dd2829e670e582de2a6791ad78a33d9fc
|
Provenance
The following attestation bundles were made for document2graph-0.1.0.tar.gz:
Publisher:
python-publish.yml on mwalther10/document2graph
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
document2graph-0.1.0.tar.gz -
Subject digest:
fe37d2364baee83a7ba0d853e7c5f78ad226b43273a0a4cdec8bd1e96a4db2c1 - Sigstore transparency entry: 2125380894
- Sigstore integration time:
-
Permalink:
mwalther10/document2graph@d056a9664864288e5fe5c9a4aa8d80e3fb80982d -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/mwalther10
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@d056a9664864288e5fe5c9a4aa8d80e3fb80982d -
Trigger Event:
release
-
Statement type:
File details
Details for the file document2graph-0.1.0-py3-none-any.whl.
File metadata
- Download URL: document2graph-0.1.0-py3-none-any.whl
- Upload date:
- Size: 23.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
b24a51dff30c6162e22eeac7a34e34eadf442fba78b945d761097546997b5be8
|
|
| MD5 |
ae3e242902b1332f176a7e08694028a2
|
|
| BLAKE2b-256 |
ab05c495f7e8499b089bc0b3f19eca77df80d3718b89e0a82cbef990ce2aab79
|
Provenance
The following attestation bundles were made for document2graph-0.1.0-py3-none-any.whl:
Publisher:
python-publish.yml on mwalther10/document2graph
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
document2graph-0.1.0-py3-none-any.whl -
Subject digest:
b24a51dff30c6162e22eeac7a34e34eadf442fba78b945d761097546997b5be8 - Sigstore transparency entry: 2125380907
- Sigstore integration time:
-
Permalink:
mwalther10/document2graph@d056a9664864288e5fe5c9a4aa8d80e3fb80982d -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/mwalther10
-
Access:
private
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
python-publish.yml@d056a9664864288e5fe5c9a4aa8d80e3fb80982d -
Trigger Event:
release
-
Statement type: