refigure
Converters where figures survive.
DOCX / XLSX → Markdown converters that treat embedded charts, composite diagrams and infographics as single semantic objects instead of silently dropping or fragmenting them: native OOXML chart-data extraction (no rasterize/OCR/VLM) plus positioned machine-readable markers as the zero-loss floor, optional VLM interpretation (prose + mermaid) on top, cached and reproducible offline.
Demo
Native chart-data extraction — real OOXML numCache, not a screenshot,
not OCR:
Same extraction, from DOCX — Word embeds native charts too, not just Excel; refigure reads the same cached OOXML data either way:
Composite figures — positioned, zero-loss, even when the figure itself can't be rendered (no incumbent does this — see Docling issue #1287, open >1 year):
Quickstart
pip install "refigure[docx,xlsx]"
refigure report.docx # markdown to stdout
from refigure.docx import convert
result = convert("report.docx")
print(result.markdown)
print(f"{result.charts_found} charts, {result.groups_found} composite figures")
Features
- Native chart-data extraction — reads OOXML
numCache/strCachedirectly; no rasterize/OCR/VLM step for charts, real numbers every time. - Positioned zero-loss markers for composite figures (DOCX) — grouped shapes/infographics that mammoth would otherwise silently fragment into disconnected pieces get a clean marker instead, with position and any caption text preserved. Absent even in well-funded incumbents — see Docling issue #1287.
- Optional VLM interpretation (DOCX composite figures,
[vlm]extra) — cloud description + mermaid diagram on top of the zero-loss floor. Implemented and tested, but not active or announced as a v1 feature yet (see Status). - Rich, typed result —
ConversionResult(markdown + warnings + chart/group counts +vlm_used), not a bare string. - CLI included —
refigureconsole command, stdin/stdout-first, native batch mode, typed exit codes (see below).
CLI
refigure installs a console command — a thin wrapper over the same
convert() used programmatically, no separate logic:
refigure report.docx # markdown to stdout
refigure report.docx -o report.md # markdown to a file
cat report.docx | refigure --format docx # stdin, format hint required
refigure reports/ -o out/ # batch: directory, walked recursively
refigure a.docx b.xlsx -o out/ # batch: 2+ explicit sources
Batch mode (2+ sources, or a single directory) requires -o DIR, keeps
going past a failed source by default (--fail-fast aborts on the first
one instead), and always prints a summary (N/M converted, K failed) to
stderr. --json emits the full result — markdown plus chart/group counts
and warnings — instead of plain markdown. -v/-q control verbosity;
--strict is forwarded to the same Config.strict the Python API uses.
Exit codes:
| Code | Meaning |
|---|---|
| 0 | success |
| 1 | batch mode: 1+ sources failed (keep-going default) |
| 2 | usage error (bad arguments/flags) |
| 3 | input isn't a valid document of its format |
| 4 | input isn't a valid/safe archive |
| 5 | the format's extra ([docx]/[xlsx]) isn't installed |
| 6 | unexpected internal error |
Real examples
Full convert() output on real, openly-licensed documents — not
cherry-picked snippets. Each file's own header states its source, license
and attribution.
| Source | Demonstrates | Output |
|---|---|---|
hackair-d7.7-pilot-evaluation.docx |
native chart extraction — 8 charts, 6 render as mermaid diagrams | examples/hackair-native-charts.md |
swd2018-254-marine-litter-ia-annex.docx |
combo: 1 chart (table-only — real verify+fallback in action, not every chart maps to mermaid) + 2 composite-figure zero-loss markers | examples/swd2018-combo.md |
govtech-2025-charts.xlsx |
XLSX at scale — 55 charts, 33 render as mermaid diagrams | examples/govtech-xlsx-charts.md |
swd2021-396-platform-work-ia.docx |
native pie chart — real EU-survey labels, all 8 charts render (3 as mermaid) | examples/swd2021-pie-chart.md |
Open any of these on GitHub and both views are right there: the raw
```mermaid fence an LLM/RAG pipeline would read, and its native
GitHub rendering — no extra step, that's GitHub's own Markdown support.
Status
Pre-release. Tested against 27 real documents (15 DOCX + 12 XLSX) — 407
native charts found (400 rendered), 35 composite figures recovered as
positioned zero-loss markers — see
tests/integration/fixtures/manifest.yaml
for provenance, licenses and attribution. CI gates on a combined
unit+integration test-coverage floor of 95%.
The converters are being extracted from a working document-analysis
pipeline (government AI-policy corpus); first public release planned for
August 2026 as a single package with per-format extras ([docx] /
[xlsx]). A [vlm] extra also exists (DOCX-only cloud interpretation of
composite figures the chart engine can't reconstruct,
Config(use_vlm=True), provider-agnostic — also needs the system
soffice/LibreOffice binary, not installable via pip) — implemented and
tested, but not active or announced as a v1 feature yet; no CLI flag
exposes it.
PDF is out of scope, on purpose — a boundary, not a gap. PDF has no
equivalent of OOXML's cached chart data (numCache/strCache) for any
mainstream chart generator, so the native, rasterize-free extraction
this project is built on doesn't transfer to it — confirmed by research
into PDF's own structure and how leading PDF converters handle charts
today, not assumed. For mixed-format corpora, route by extension instead
of expecting one tool to cover everything —
Docling or
MarkItDown for PDF, refigure
for DOCX/XLSX where the chart data actually survives in the file:
import refigure.docx
import refigure.xlsx
if path.suffix == ".pdf":
markdown = docling_convert(path) # or any PDF-capable converter
elif path.suffix == ".docx":
markdown = refigure.docx.convert(path).markdown
else:
markdown = refigure.xlsx.convert(path).markdown
v0.1.0 is packaged, CI-verified and ready — trusted publishing
(GitHub↔PyPI, no stored tokens) is configured on both ends; publication
itself is a separate, deliberate step not yet taken. Until then, PyPI
name refigure still shows its 0.0.0 placeholder. refigure-md is a
reserved alternate name, not an active release.
License
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 refigure-0.1.0.tar.gz.
File metadata
- Download URL: refigure-0.1.0.tar.gz
- Upload date:
- Size: 137.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
dbfe767771d8e86a95baa2f7c1edb59d3123577e95a0733cf1a5409f876c7bc9
|
|
| MD5 |
22071d282ed7091fac3aa6ab57ecd0b0
|
|
| BLAKE2b-256 |
44b31d74797eeb3a0237e5111b7f9de40e3ee8a898fbdd6edb14dc4ed632b347
|
Provenance
The following attestation bundles were made for refigure-0.1.0.tar.gz:
Publisher:
publish.yml on HelgDemidov/refigure
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
refigure-0.1.0.tar.gz -
Subject digest:
dbfe767771d8e86a95baa2f7c1edb59d3123577e95a0733cf1a5409f876c7bc9 - Sigstore transparency entry: 2518128959
- Sigstore integration time:
-
Permalink:
HelgDemidov/refigure@968c17824a1f9eab290d27160f48b3eeb68cb97b -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/HelgDemidov
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@968c17824a1f9eab290d27160f48b3eeb68cb97b -
Trigger Event:
push
-
Statement type:
File details
Details for the file refigure-0.1.0-py3-none-any.whl.
File metadata
- Download URL: refigure-0.1.0-py3-none-any.whl
- Upload date:
- Size: 80.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via:
twine/7.0.0 CPython/3.13.14
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a4fcfe15b0b2b000aff7e0607f7a1071dbb709fae5cc78873fc4caf8403cc06c
|
|
| MD5 |
20755412269500af82f3b081bb89a812
|
|
| BLAKE2b-256 |
903696c0f09957eed27ef8efc05ac0a266c6ba5f321879da9387da1dc509f2ff
|
Provenance
The following attestation bundles were made for refigure-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on HelgDemidov/refigure
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
refigure-0.1.0-py3-none-any.whl -
Subject digest:
a4fcfe15b0b2b000aff7e0607f7a1071dbb709fae5cc78873fc4caf8403cc06c - Sigstore transparency entry: 2518129343
- Sigstore integration time:
-
Permalink:
HelgDemidov/refigure@968c17824a1f9eab290d27160f48b3eeb68cb97b -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/HelgDemidov
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@968c17824a1f9eab290d27160f48b3eeb68cb97b -
Trigger Event:
push
-
Statement type: