refigure 0.3.2__tar.gz → 0.3.4__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.
Files changed (74) hide show
  1. {refigure-0.3.2 → refigure-0.3.4}/.gitignore +1 -0
  2. {refigure-0.3.2 → refigure-0.3.4}/PKG-INFO +153 -87
  3. {refigure-0.3.2 → refigure-0.3.4}/README.md +152 -86
  4. {refigure-0.3.2 → refigure-0.3.4}/pyproject.toml +1 -1
  5. {refigure-0.3.2 → refigure-0.3.4}/ATTRIBUTION.md +0 -0
  6. {refigure-0.3.2 → refigure-0.3.4}/LICENSE +0 -0
  7. {refigure-0.3.2 → refigure-0.3.4}/NOTICE +0 -0
  8. {refigure-0.3.2 → refigure-0.3.4}/refigure/__init__.py +0 -0
  9. {refigure-0.3.2 → refigure-0.3.4}/refigure/__main__.py +0 -0
  10. {refigure-0.3.2 → refigure-0.3.4}/refigure/_io.py +0 -0
  11. {refigure-0.3.2 → refigure-0.3.4}/refigure/api.py +0 -0
  12. {refigure-0.3.2 → refigure-0.3.4}/refigure/cli.py +0 -0
  13. {refigure-0.3.2 → refigure-0.3.4}/refigure/core/__init__.py +0 -0
  14. {refigure-0.3.2 → refigure-0.3.4}/refigure/core/chart_data.py +0 -0
  15. {refigure-0.3.2 → refigure-0.3.4}/refigure/core/chart_render.py +0 -0
  16. {refigure-0.3.2 → refigure-0.3.4}/refigure/core/zipsafe.py +0 -0
  17. {refigure-0.3.2 → refigure-0.3.4}/refigure/docx/__init__.py +0 -0
  18. {refigure-0.3.2 → refigure-0.3.4}/refigure/docx_groups.py +0 -0
  19. {refigure-0.3.2 → refigure-0.3.4}/refigure/mcp/__init__.py +0 -0
  20. {refigure-0.3.2 → refigure-0.3.4}/refigure/mcp/_lru.py +0 -0
  21. {refigure-0.3.2 → refigure-0.3.4}/refigure/mcp/auth.py +0 -0
  22. {refigure-0.3.2 → refigure-0.3.4}/refigure/mcp/cli.py +0 -0
  23. {refigure-0.3.2 → refigure-0.3.4}/refigure/mcp/exceptions.py +0 -0
  24. {refigure-0.3.2 → refigure-0.3.4}/refigure/mcp/server.py +0 -0
  25. {refigure-0.3.2 → refigure-0.3.4}/refigure/mcp/state.py +0 -0
  26. {refigure-0.3.2 → refigure-0.3.4}/refigure/mcp/vlm_cache.py +0 -0
  27. {refigure-0.3.2 → refigure-0.3.4}/refigure/py.typed +0 -0
  28. {refigure-0.3.2 → refigure-0.3.4}/refigure/vlm/__init__.py +0 -0
  29. {refigure-0.3.2 → refigure-0.3.4}/refigure/vlm/cache.py +0 -0
  30. {refigure-0.3.2 → refigure-0.3.4}/refigure/vlm/client.py +0 -0
  31. {refigure-0.3.2 → refigure-0.3.4}/refigure/xlsx/__init__.py +0 -0
  32. {refigure-0.3.2 → refigure-0.3.4}/refigure/xlsx/charts.py +0 -0
  33. {refigure-0.3.2 → refigure-0.3.4}/tests/__init__.py +0 -0
  34. {refigure-0.3.2 → refigure-0.3.4}/tests/support.py +0 -0
  35. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/__init__.py +0 -0
  36. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/core/__init__.py +0 -0
  37. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/core/test_chart_data.py +0 -0
  38. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/core/test_chart_render.py +0 -0
  39. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/core/test_chart_render_missing_mermaidx.py +0 -0
  40. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/core/test_chart_render_visual.py +0 -0
  41. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/docx/__init__.py +0 -0
  42. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/docx/test_docx.py +0 -0
  43. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/mcp/__init__.py +0 -0
  44. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/mcp/conftest.py +0 -0
  45. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/mcp/test_auth.py +0 -0
  46. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/mcp/test_batch.py +0 -0
  47. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/mcp/test_bridge.py +0 -0
  48. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/mcp/test_cli.py +0 -0
  49. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/mcp/test_http.py +0 -0
  50. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/mcp/test_lru.py +0 -0
  51. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/mcp/test_prompts.py +0 -0
  52. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/mcp/test_resources.py +0 -0
  53. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/mcp/test_schema_pin.py +0 -0
  54. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/mcp/test_state.py +0 -0
  55. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/mcp/test_tools.py +0 -0
  56. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/mcp/test_vlm_cache.py +0 -0
  57. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/test_cli.py +0 -0
  58. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/test_concurrency.py +0 -0
  59. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/test_docx_chart_group_coexistence.py +0 -0
  60. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/test_docx_groups.py +0 -0
  61. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/test_io.py +0 -0
  62. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/test_optional_dependency_guards.py +0 -0
  63. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/test_property_based.py +0 -0
  64. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/test_robustness.py +0 -0
  65. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/test_vlm_max_markers.py +0 -0
  66. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/test_xml_security.py +0 -0
  67. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/vlm/__init__.py +0 -0
  68. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/vlm/test_soffice_profile_isolation.py +0 -0
  69. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/vlm/test_vlm.py +0 -0
  70. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/vlm/test_vlm_cache.py +0 -0
  71. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/vlm/test_vlm_client.py +0 -0
  72. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/xlsx/__init__.py +0 -0
  73. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/xlsx/test_xlsx.py +0 -0
  74. {refigure-0.3.2 → refigure-0.3.4}/tests/unit/xlsx/test_xlsx_charts.py +0 -0
@@ -11,6 +11,7 @@ env/
11
11
  dist/
12
12
  build/
13
13
  *.egg-info/
14
+ *.mcpb
14
15
 
15
16
  # uv auto-generates this on `uv run`/`uv pip` if absent — project's
16
17
  # dependency source of truth is requirements.txt/requirements-dev.txt
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: refigure
3
- Version: 0.3.2
3
+ Version: 0.3.4
4
4
  Summary: DOCX/XLSX -> Markdown conversion with native OOXML chart-data extraction (no rasterize/OCR/VLM needed) + optional VLM interpretation for figures with no native chart data
5
5
  Project-URL: Homepage, https://github.com/HelgDemidov/refigure
6
6
  Project-URL: Repository, https://github.com/HelgDemidov/refigure
@@ -46,15 +46,66 @@ Description-Content-Type: text/markdown
46
46
  [![Coverage](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/HelgDemidov/refigure/main/docs/assets/coverage-badge.json)](https://github.com/HelgDemidov/refigure/actions/workflows/ci.yml)
47
47
  [![License: Apache 2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
48
48
  [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](pyproject.toml)
49
+ [![PyPI](https://img.shields.io/pypi/v/refigure)](https://pypi.org/project/refigure/)
50
+ [![Docker](https://img.shields.io/badge/ghcr.io-refigure-2496ED?logo=docker&logoColor=white)](https://github.com/HelgDemidov/refigure/pkgs/container/refigure)
51
+ [![MCP Registry](https://img.shields.io/badge/MCP_Registry-listed-6f42c1)](https://registry.modelcontextprotocol.io/?q=refigure)
52
+ [![Claude Desktop](https://img.shields.io/badge/Claude_Desktop-.mcpb-D97757)](https://github.com/HelgDemidov/refigure/releases/latest/download/refigure.mcpb)
49
53
 
50
54
  <!-- mcp-name: io.github.HelgDemidov/refigure -->
51
55
 
52
- DOCX / XLSX → Markdown converters that treat embedded charts, composite
53
- diagrams and infographics as single semantic objects instead of silently
54
- dropping or fragmenting them: native OOXML chart-data extraction (no
55
- rasterize/OCR/VLM) plus positioned machine-readable markers as the zero-loss
56
- floor, optional VLM interpretation (prose + mermaid) on top, cached and
57
- reproducible offline.
56
+ DOCX/XLSX → Markdown that keeps charts and infographics machine-readable
57
+ instead of losing them to OCR or a vision model: native OOXML chart data
58
+ (`numCache`/`strCache`) recovers exact numbers with zero GPU calls, zero
59
+ VLM calls, zero lost precision — by default, not as a fallback.
60
+
61
+ That default path is also why the base install (`pip install
62
+ "refigure[docx,xlsx]"`) is **~500x lighter than PyTorch-based
63
+ alternatives** (5.6MB vs. multi-GB) — the core conversion needs no ML
64
+ model at all. That number is about the core architecture, not every
65
+ distribution format: the Docker image trades it back deliberately,
66
+ bundling VLM providers + LibreOffice for a turnkey composite-figure path
67
+ (see Docker below).
68
+
69
+ VLM interpretation itself is there for the rare figure with no native
70
+ data at all (a dashboard screenshot) — never required just to get real
71
+ numbers out of a chart, on any distribution format.
72
+
73
+ Ships as a library, CLI, MCP server, and a one-click Claude Desktop
74
+ bundle — every surface returns the same native-fidelity output, not a
75
+ degraded summary for agents.
76
+
77
+ ## Features
78
+
79
+ - **Native chart-data extraction** — reads OOXML `numCache`/`strCache`
80
+ directly; no rasterize/OCR/VLM step for charts, real numbers every time.
81
+ - **Positioned zero-loss markers for composite figures** (DOCX) — grouped
82
+ shapes/infographics that mammoth would otherwise silently fragment into
83
+ disconnected pieces get a clean marker instead, with position and any
84
+ caption text preserved. Absent even in well-funded incumbents — see
85
+ [Docling issue #1287](https://github.com/docling-project/docling/issues/1287).
86
+ - **Optional VLM interpretation** (DOCX composite figures, `[vlm]` extra,
87
+ `--vlm`/`Config(use_vlm=True)`) — cloud description + a real rendered
88
+ mermaid diagram (26 supported diagram types — flowcharts, pie/xy charts,
89
+ sequence/state/ER diagrams, Gantt/timeline/sankey/treemap and more, see
90
+ Status below) on top of the zero-loss floor, for figures with no native
91
+ chart data at all (e.g. a dashboard screenshot). Provider-agnostic —
92
+ OpenRouter by default, or direct OpenAI/Ollama/vLLM/LM Studio/Anthropic
93
+ via `--vlm-provider` (`[vlm-direct]` extra). `--strict` upgrades one
94
+ specific failure (the system `soffice`/LibreOffice binary missing) from
95
+ a graceful skip to a hard error; every other VLM failure still degrades.
96
+ - **Rich, typed result** — `ConversionResult` (markdown + warnings +
97
+ chart/group counts + `vlm_used`), not a bare string.
98
+ - **CLI included** — `refigure` console command, stdin/stdout-first, native
99
+ batch mode, typed exit codes (see below).
100
+ - **MCP server included** — `refigure-mcp` console command (`[mcp]` extra),
101
+ stdio or Streamable HTTP, tools/resources/prompts, batch conversion with
102
+ per-file isolation (see below).
103
+ - **Docker image** — `ghcr.io/helgdemidov/refigure`, both console commands
104
+ on `PATH`, `soffice`/LibreOffice baked in — the VLM composite-figure
105
+ path works turnkey, no manual LibreOffice install. Multi-arch —
106
+ `linux/amd64` + `linux/arm64`, native Apple Silicon (see below).
107
+ - **`.mcpb` bundle for Claude Desktop** — one-click install, no terminal
108
+ (`docx`+`xlsx` only, see below).
58
109
 
59
110
  ## Demo
60
111
 
@@ -64,7 +115,10 @@ construct either (a dense radial sunburst — nothing in the 4 original
64
115
  mermaid types could represent it), `--vlm` both recovers the real content
65
116
  and produces a genuinely renderable diagram, not just recovered text:
66
117
 
67
- <img src="docs/assets/demo-vlm-dark.svg" alt="A real docx image (a dense wireless-technology sunburst chart with no native chart data) converted by refigure.docx.convert(use_vlm=True) into a rich VLM-generated description and a real rendered mermaid mindmap diagram, laid out radially instead of the unreadable flat strip a generic flowchart construct would have produced">
118
+ <picture>
119
+ <source media="(prefers-color-scheme: dark)" srcset="docs/assets/demo-vlm-dark.svg">
120
+ <img src="docs/assets/demo-vlm-light.svg" alt="A real docx image (a dense wireless-technology sunburst chart with no native chart data) converted by refigure.docx.convert(use_vlm=True) into a rich VLM-generated description and a real rendered mermaid mindmap diagram, laid out radially instead of the unreadable flat strip a generic flowchart construct would have produced">
121
+ </picture>
68
122
 
69
123
  **Native chart-data extraction** — real OOXML `numCache`, not a screenshot,
70
124
  not OCR:
@@ -117,7 +171,7 @@ uvx --from "refigure[docx,xlsx]" refigure report.docx
117
171
  ```
118
172
 
119
173
  Optional VLM interpretation, for a composite figure the chart engine can't
120
- reconstruct on its own (see Features below):
174
+ reconstruct on its own (see Features above):
121
175
 
122
176
  ```bash
123
177
  pip install "refigure[docx,vlm]"
@@ -125,37 +179,13 @@ export OPENROUTER_API_KEY=... # or --vlm-api-key-file/--vlm-prov
125
179
  refigure report.docx --vlm # needs the system soffice/LibreOffice binary too
126
180
  ```
127
181
 
128
- ## Features
182
+ ## Installation & usage
129
183
 
130
- - **Native chart-data extraction** — reads OOXML `numCache`/`strCache`
131
- directly; no rasterize/OCR/VLM step for charts, real numbers every time.
132
- - **Positioned zero-loss markers for composite figures** (DOCX) — grouped
133
- shapes/infographics that mammoth would otherwise silently fragment into
134
- disconnected pieces get a clean marker instead, with position and any
135
- caption text preserved. Absent even in well-funded incumbents — see
136
- [Docling issue #1287](https://github.com/docling-project/docling/issues/1287).
137
- - **Optional VLM interpretation** (DOCX composite figures, `[vlm]` extra,
138
- `--vlm`/`Config(use_vlm=True)`) — cloud description + a real rendered
139
- mermaid diagram (26 supported diagram types — flowcharts, pie/xy charts,
140
- sequence/state/ER diagrams, Gantt/timeline/sankey/treemap and more, see
141
- Status below) on top of the zero-loss floor, for figures with no native
142
- chart data at all (e.g. a dashboard screenshot). Provider-agnostic —
143
- OpenRouter by default, or direct OpenAI/Ollama/vLLM/LM Studio/Anthropic
144
- via `--vlm-provider` (`[vlm-direct]` extra). `--strict` upgrades one
145
- specific failure (the system `soffice`/LibreOffice binary missing) from
146
- a graceful skip to a hard error; every other VLM failure still degrades.
147
- - **Rich, typed result** — `ConversionResult` (markdown + warnings +
148
- chart/group counts + `vlm_used`), not a bare string.
149
- - **CLI included** — `refigure` console command, stdin/stdout-first, native
150
- batch mode, typed exit codes (see below).
151
- - **MCP server included** — `refigure-mcp` console command (`[mcp]` extra),
152
- stdio or Streamable HTTP, tools/resources/prompts, batch conversion with
153
- per-file isolation (see below).
154
- - **Docker image** — `ghcr.io/helgdemidov/refigure`, both console commands
155
- on `PATH`, `soffice`/LibreOffice baked in — the VLM composite-figure
156
- path works turnkey, no manual LibreOffice install (see below).
184
+ One converter, four ways to run it — pick whichever fits your pipeline.
185
+ Click a heading to expand it.
157
186
 
158
- ## CLI
187
+ <details>
188
+ <summary><b>CLI</b> — a console command, stdin/stdout-first, native batch mode</summary>
159
189
 
160
190
  `refigure` installs a console command — a thin wrapper over the same
161
191
  `convert()` used programmatically, no separate logic:
@@ -187,12 +217,17 @@ Exit codes:
187
217
  | 5 | the format's extra (`[docx]`/`[xlsx]`) isn't installed |
188
218
  | 6 | unexpected internal error |
189
219
 
190
- ## MCP server
220
+ </details>
221
+
222
+ <details>
223
+ <summary><b>MCP server</b> — for agents/IDEs that speak the protocol directly</summary>
191
224
 
192
225
  `refigure-mcp` — the same converters as an
193
226
  [MCP](https://modelcontextprotocol.io) server, for agents/IDEs that speak
194
227
  the protocol directly instead of shelling out to a CLI or importing the
195
- library:
228
+ library. Listed on the official
229
+ [MCP Registry](https://registry.modelcontextprotocol.io/?q=refigure) as
230
+ `io.github.HelgDemidov/refigure`:
196
231
 
197
232
  ```bash
198
233
  pip install "refigure[mcp,docx,xlsx]"
@@ -246,22 +281,31 @@ fairness soft-cap once 2+ callers are configured; `refigure-mcp --help`
246
281
  covers every tuning flag (concurrency, timeouts, resource-store limits,
247
282
  batch size, VLM ceiling).
248
283
 
249
- ## Docker
284
+ </details>
285
+
286
+ <details>
287
+ <summary><b>Docker</b> — one image, CLI and MCP server both on PATH, soffice baked in</summary>
250
288
 
251
289
  One image, both surfaces — `refigure` and `refigure-mcp` are already on
252
290
  `PATH`, no separate CLI/MCP builds to choose between. The one thing this
253
291
  format buys over `pip`/`uvx` that neither can: the system `soffice`/
254
292
  LibreOffice binary the VLM composite-figure path needs is baked in, not a
255
- manual install.
293
+ manual install. Multi-arch manifest (`linux/amd64` + `linux/arm64`) —
294
+ `docker pull` resolves the right layer automatically, including on
295
+ Apple Silicon.
256
296
 
257
297
  ```bash
258
- docker pull ghcr.io/helgdemidov/refigure:0.3.1
298
+ docker pull ghcr.io/helgdemidov/refigure:latest
259
299
  ```
260
300
 
301
+ Pin an exact version instead of `:latest` for reproducibility — e.g.
302
+ `:0.3.3` — see the [package page](https://github.com/HelgDemidov/refigure/pkgs/container/refigure)
303
+ for available tags.
304
+
261
305
  CLI, via a bind mount (the image's working directory is already `/data`):
262
306
 
263
307
  ```bash
264
- docker run --rm -v "$PWD:/data:ro" ghcr.io/helgdemidov/refigure:0.3.1 \
308
+ docker run --rm -v "$PWD:/data:ro" ghcr.io/helgdemidov/refigure:latest \
265
309
  refigure /data/report.docx
266
310
  ```
267
311
 
@@ -272,7 +316,7 @@ MCP, stdio — the client launches the container itself:
272
316
  "mcpServers": {
273
317
  "refigure": {
274
318
  "command": "docker",
275
- "args": ["run", "-i", "--rm", "ghcr.io/helgdemidov/refigure:0.3.1", "refigure-mcp"]
319
+ "args": ["run", "-i", "--rm", "ghcr.io/helgdemidov/refigure:latest", "refigure-mcp"]
276
320
  }
277
321
  }
278
322
  }
@@ -287,24 +331,43 @@ flag would silently never respond:
287
331
  ```bash
288
332
  echo "sk-... = alice" > tokens.txt
289
333
  docker run --rm -p 8000:8000 -v "$PWD/tokens.txt:/data/tokens.txt:ro" \
290
- ghcr.io/helgdemidov/refigure:0.3.1 \
334
+ ghcr.io/helgdemidov/refigure:latest \
291
335
  refigure-mcp --transport http --mcp-http-host 0.0.0.0 \
292
336
  --mcp-auth-token-file /data/tokens.txt
293
337
  ```
294
338
 
339
+ </details>
340
+
341
+ <details>
342
+ <summary><b>Claude Desktop (<code>.mcpb</code>)</b> — download, double-click, done</summary>
343
+
344
+ The simplest install for a non-technical user: download, double-click,
345
+ done — no terminal, no `pip`/`uvx`/`docker`. Covers `docx`+`xlsx`
346
+ conversion only (no VLM — that needs the `[vlm]` extra, deliberately
347
+ not carried by this bundle); dependencies resolve fresh from PyPI via
348
+ `uv` on first launch, the same mechanism `uvx` uses under the hood,
349
+ just one click instead of a config snippet.
350
+
351
+ [**Download refigure.mcpb**](https://github.com/HelgDemidov/refigure/releases/latest/download/refigure.mcpb)
352
+ — open it with Claude Desktop to install.
353
+
354
+ </details>
355
+
295
356
  ## Real examples
296
357
 
297
- Full `convert()` output on real, openly-licensed documents — not
298
- cherry-picked snippets. Each file's own header states its source, license
299
- and attribution.
358
+ Concentrated excerpts (≤200 lines each) of real `convert()` output on
359
+ real, openly-licensed documents — the actual markdown a pipeline would
360
+ ingest, not a screenshot or a cherry-picked one-liner. Each file's own
361
+ header states its source, license and attribution; trimmed sections are
362
+ marked inline, never fabricated to fill space.
300
363
 
301
364
  | Source | Demonstrates | Output |
302
365
  | --- | --- | --- |
303
- | `hackair-d7.7-pilot-evaluation.docx` | native chart extraction — 8 charts, 6 render as mermaid diagrams | [examples/hackair-native-charts.md](examples/hackair-native-charts.md) |
304
- | `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](examples/swd2018-combo.md) |
305
- | `govtech-2025-charts.xlsx` | XLSX at scale — 55 charts, 33 render as mermaid diagrams | [examples/govtech-xlsx-charts.md](examples/govtech-xlsx-charts.md) |
306
- | `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](examples/swd2021-pie-chart.md) |
307
- | `efsa-trichinella-dashboard-guide.docx` | `--vlm` interpretation — 27 figures with no native chart data, real numbers recovered from screenshots | [examples/efsa-trichinella-vlm.md](examples/efsa-trichinella-vlm.md) |
366
+ | `hackair-d7.7-pilot-evaluation.docx` | native chart extraction — real survey tables + `xychart-beta` bar charts | [examples/hackair-native-charts.md](examples/hackair-native-charts.md) |
367
+ | `swd2018-254-marine-litter-ia-annex.docx` | honest fallback — a chart that fails render-verification degrades to a clean table, plus 2 composite-figure zero-loss markers | [examples/swd2018-combo.md](examples/swd2018-combo.md) |
368
+ | `govtech-2025-charts.xlsx` | XLSX native charts — 3 distinct types (`xychart-beta`/`radar-beta`/`pie`) from one workbook | [examples/govtech-xlsx-charts.md](examples/govtech-xlsx-charts.md) |
369
+ | `swd2021-396-platform-work-ia.docx` | native pie + a 23-year time series, real EU-survey labels | [examples/swd2021-pie-chart.md](examples/swd2021-pie-chart.md) |
370
+ | `efsa-trichinella-dashboard-guide.docx` | `--vlm` interpretation — 2 screenshot figures recovered as a bar chart and a UI flowchart, real numbers | [examples/efsa-trichinella-vlm.md](examples/efsa-trichinella-vlm.md) |
308
371
 
309
372
  Open any of these on GitHub and both views are right there: the raw
310
373
  ```` ```mermaid ```` fence an LLM/RAG pipeline would read, and its native
@@ -312,37 +375,42 @@ GitHub rendering — no extra step, that's GitHub's own Markdown support.
312
375
 
313
376
  ## Status
314
377
 
315
- Published on PyPI as `refigure`. Tested against 27 real documents (15 DOCX +
316
- 12 XLSX) — 407 native charts found (400 rendered), 35 composite figures
317
- recovered as positioned zero-loss markers — see
318
- [`tests/integration/fixtures/manifest.yaml`](tests/integration/fixtures/manifest.yaml)
319
- for provenance, licenses and attribution. CI gates on a combined
320
- unit+integration test-coverage floor of 95%.
321
-
322
- The converters were extracted from a working document-analysis pipeline
323
- (government AI-policy corpus) into a single package with per-format extras
324
- (`[docx]` / `[xlsx]`). VLM interpretation of composite figures the chart
325
- engine can't reconstruct (`[vlm]` extra, `Config(use_vlm=True)`,
326
- provider-agnostic — direct OpenAI/Anthropic via `[vlm-direct]`, also needs
327
- the system `soffice`/LibreOffice binary, not installable via pip) is fully
328
- implemented, tested, and exposed through the `refigure` CLI (`--vlm` and
329
- friends — see CLI above and Quickstart). Mermaid-diagram recognition on
330
- top of that varies by diagram type and by what's actually on the source
331
- figure — common types (flowcharts, pie/xy charts) are picked reliably;
332
- more specialized ones depend on the figure carrying an unambiguous visual
333
- cue, and not every figure produces a diagram at all — a plain text
334
- description is a valid, honest fallback when it doesn't.
378
+ - **Validated** against 27 real documents (15 DOCX + 12 XLSX) — 407 native
379
+ charts found (400 rendered), 35 composite figures recovered as
380
+ positioned zero-loss markers. Full provenance:
381
+ [`tests/integration/fixtures/manifest.yaml`](tests/integration/fixtures/manifest.yaml).
382
+ - **Tested**: CI gates on a combined unit+integration coverage floor of 95%.
383
+ - **Published** as `v0.3.3` — [PyPI](https://pypi.org/project/refigure/)
384
+ (trusted publishing, no stored tokens),
385
+ [GHCR](https://github.com/HelgDemidov/refigure/pkgs/container/refigure),
386
+ and the official
387
+ [MCP Registry](https://registry.modelcontextprotocol.io/?q=refigure) as
388
+ `io.github.HelgDemidov/refigure`. `refigure-md` is a reserved alternate
389
+ name, not an active release.
390
+
391
+ Extracted from a working document-analysis pipeline (a government
392
+ AI-policy research corpus), not built from scratch for this release.
393
+
394
+ VLM interpretation of composite figures the chart engine can't reconstruct
395
+ is fully implemented and tested, not a stub — `[vlm]` extra,
396
+ provider-agnostic (direct OpenAI/Anthropic via `[vlm-direct]`), also needs
397
+ the system `soffice`/LibreOffice binary.
398
+
399
+ Mermaid-diagram recognition depends on diagram type and on what the
400
+ source figure actually contains:
401
+
402
+ - Common types (flowcharts, pie/xy charts) are picked reliably.
403
+ - More specialized ones need an unambiguous visual cue on the source figure.
404
+ - Not every figure produces a diagram — a plain-text description is an
405
+ honest fallback, not a failure.
335
406
 
336
407
  **PDF is out of scope, on purpose — a boundary, not a gap.** PDF has no
337
408
  equivalent of OOXML's cached chart data (`numCache`/`strCache`) for any
338
- mainstream chart generator, so the native, rasterize-free extraction
339
- this project is built on doesn't transfer to it — confirmed by research
340
- into PDF's own structure and how leading PDF converters handle charts
341
- today, not assumed. For mixed-format corpora, route by extension instead
342
- of expecting one tool to cover everything —
343
- [Docling](https://github.com/docling-project/docling) or
344
- [MarkItDown](https://github.com/microsoft/markitdown) for PDF, refigure
345
- for DOCX/XLSX where the chart data actually survives in the file:
409
+ mainstream chart generator, so the native, rasterize-free extraction this
410
+ project is built on doesn't transfer to it — confirmed by research into
411
+ PDF's own structure and how leading PDF converters handle charts today,
412
+ not assumed. For mixed-format corpora, route by extension instead of
413
+ expecting one tool to cover everything:
346
414
 
347
415
  ```python
348
416
  import refigure.docx
@@ -356,11 +424,9 @@ else:
356
424
  markdown = refigure.xlsx.convert(path).markdown
357
425
  ```
358
426
 
359
- `v0.3.1` published via trusted publishing (GitHub↔PyPI, no stored tokens),
360
- also on GHCR as `ghcr.io/helgdemidov/refigure` and on the official
361
- [MCP Registry](https://registry.modelcontextprotocol.io) as
362
- `io.github.HelgDemidov/refigure`. `refigure-md` is a reserved alternate
363
- name, not an active release.
427
+ Use [Docling](https://github.com/docling-project/docling) or
428
+ [MarkItDown](https://github.com/microsoft/markitdown) for PDF, refigure
429
+ for DOCX/XLSX where the chart data actually survives in the file.
364
430
 
365
431
  ## License
366
432
 
@@ -6,15 +6,66 @@
6
6
  [![Coverage](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/HelgDemidov/refigure/main/docs/assets/coverage-badge.json)](https://github.com/HelgDemidov/refigure/actions/workflows/ci.yml)
7
7
  [![License: Apache 2.0](https://img.shields.io/badge/license-Apache--2.0-blue.svg)](LICENSE)
8
8
  [![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-blue.svg)](pyproject.toml)
9
+ [![PyPI](https://img.shields.io/pypi/v/refigure)](https://pypi.org/project/refigure/)
10
+ [![Docker](https://img.shields.io/badge/ghcr.io-refigure-2496ED?logo=docker&logoColor=white)](https://github.com/HelgDemidov/refigure/pkgs/container/refigure)
11
+ [![MCP Registry](https://img.shields.io/badge/MCP_Registry-listed-6f42c1)](https://registry.modelcontextprotocol.io/?q=refigure)
12
+ [![Claude Desktop](https://img.shields.io/badge/Claude_Desktop-.mcpb-D97757)](https://github.com/HelgDemidov/refigure/releases/latest/download/refigure.mcpb)
9
13
 
10
14
  <!-- mcp-name: io.github.HelgDemidov/refigure -->
11
15
 
12
- DOCX / XLSX → Markdown converters that treat embedded charts, composite
13
- diagrams and infographics as single semantic objects instead of silently
14
- dropping or fragmenting them: native OOXML chart-data extraction (no
15
- rasterize/OCR/VLM) plus positioned machine-readable markers as the zero-loss
16
- floor, optional VLM interpretation (prose + mermaid) on top, cached and
17
- reproducible offline.
16
+ DOCX/XLSX → Markdown that keeps charts and infographics machine-readable
17
+ instead of losing them to OCR or a vision model: native OOXML chart data
18
+ (`numCache`/`strCache`) recovers exact numbers with zero GPU calls, zero
19
+ VLM calls, zero lost precision — by default, not as a fallback.
20
+
21
+ That default path is also why the base install (`pip install
22
+ "refigure[docx,xlsx]"`) is **~500x lighter than PyTorch-based
23
+ alternatives** (5.6MB vs. multi-GB) — the core conversion needs no ML
24
+ model at all. That number is about the core architecture, not every
25
+ distribution format: the Docker image trades it back deliberately,
26
+ bundling VLM providers + LibreOffice for a turnkey composite-figure path
27
+ (see Docker below).
28
+
29
+ VLM interpretation itself is there for the rare figure with no native
30
+ data at all (a dashboard screenshot) — never required just to get real
31
+ numbers out of a chart, on any distribution format.
32
+
33
+ Ships as a library, CLI, MCP server, and a one-click Claude Desktop
34
+ bundle — every surface returns the same native-fidelity output, not a
35
+ degraded summary for agents.
36
+
37
+ ## Features
38
+
39
+ - **Native chart-data extraction** — reads OOXML `numCache`/`strCache`
40
+ directly; no rasterize/OCR/VLM step for charts, real numbers every time.
41
+ - **Positioned zero-loss markers for composite figures** (DOCX) — grouped
42
+ shapes/infographics that mammoth would otherwise silently fragment into
43
+ disconnected pieces get a clean marker instead, with position and any
44
+ caption text preserved. Absent even in well-funded incumbents — see
45
+ [Docling issue #1287](https://github.com/docling-project/docling/issues/1287).
46
+ - **Optional VLM interpretation** (DOCX composite figures, `[vlm]` extra,
47
+ `--vlm`/`Config(use_vlm=True)`) — cloud description + a real rendered
48
+ mermaid diagram (26 supported diagram types — flowcharts, pie/xy charts,
49
+ sequence/state/ER diagrams, Gantt/timeline/sankey/treemap and more, see
50
+ Status below) on top of the zero-loss floor, for figures with no native
51
+ chart data at all (e.g. a dashboard screenshot). Provider-agnostic —
52
+ OpenRouter by default, or direct OpenAI/Ollama/vLLM/LM Studio/Anthropic
53
+ via `--vlm-provider` (`[vlm-direct]` extra). `--strict` upgrades one
54
+ specific failure (the system `soffice`/LibreOffice binary missing) from
55
+ a graceful skip to a hard error; every other VLM failure still degrades.
56
+ - **Rich, typed result** — `ConversionResult` (markdown + warnings +
57
+ chart/group counts + `vlm_used`), not a bare string.
58
+ - **CLI included** — `refigure` console command, stdin/stdout-first, native
59
+ batch mode, typed exit codes (see below).
60
+ - **MCP server included** — `refigure-mcp` console command (`[mcp]` extra),
61
+ stdio or Streamable HTTP, tools/resources/prompts, batch conversion with
62
+ per-file isolation (see below).
63
+ - **Docker image** — `ghcr.io/helgdemidov/refigure`, both console commands
64
+ on `PATH`, `soffice`/LibreOffice baked in — the VLM composite-figure
65
+ path works turnkey, no manual LibreOffice install. Multi-arch —
66
+ `linux/amd64` + `linux/arm64`, native Apple Silicon (see below).
67
+ - **`.mcpb` bundle for Claude Desktop** — one-click install, no terminal
68
+ (`docx`+`xlsx` only, see below).
18
69
 
19
70
  ## Demo
20
71
 
@@ -24,7 +75,10 @@ construct either (a dense radial sunburst — nothing in the 4 original
24
75
  mermaid types could represent it), `--vlm` both recovers the real content
25
76
  and produces a genuinely renderable diagram, not just recovered text:
26
77
 
27
- <img src="docs/assets/demo-vlm-dark.svg" alt="A real docx image (a dense wireless-technology sunburst chart with no native chart data) converted by refigure.docx.convert(use_vlm=True) into a rich VLM-generated description and a real rendered mermaid mindmap diagram, laid out radially instead of the unreadable flat strip a generic flowchart construct would have produced">
78
+ <picture>
79
+ <source media="(prefers-color-scheme: dark)" srcset="docs/assets/demo-vlm-dark.svg">
80
+ <img src="docs/assets/demo-vlm-light.svg" alt="A real docx image (a dense wireless-technology sunburst chart with no native chart data) converted by refigure.docx.convert(use_vlm=True) into a rich VLM-generated description and a real rendered mermaid mindmap diagram, laid out radially instead of the unreadable flat strip a generic flowchart construct would have produced">
81
+ </picture>
28
82
 
29
83
  **Native chart-data extraction** — real OOXML `numCache`, not a screenshot,
30
84
  not OCR:
@@ -77,7 +131,7 @@ uvx --from "refigure[docx,xlsx]" refigure report.docx
77
131
  ```
78
132
 
79
133
  Optional VLM interpretation, for a composite figure the chart engine can't
80
- reconstruct on its own (see Features below):
134
+ reconstruct on its own (see Features above):
81
135
 
82
136
  ```bash
83
137
  pip install "refigure[docx,vlm]"
@@ -85,37 +139,13 @@ export OPENROUTER_API_KEY=... # or --vlm-api-key-file/--vlm-prov
85
139
  refigure report.docx --vlm # needs the system soffice/LibreOffice binary too
86
140
  ```
87
141
 
88
- ## Features
142
+ ## Installation & usage
89
143
 
90
- - **Native chart-data extraction** — reads OOXML `numCache`/`strCache`
91
- directly; no rasterize/OCR/VLM step for charts, real numbers every time.
92
- - **Positioned zero-loss markers for composite figures** (DOCX) — grouped
93
- shapes/infographics that mammoth would otherwise silently fragment into
94
- disconnected pieces get a clean marker instead, with position and any
95
- caption text preserved. Absent even in well-funded incumbents — see
96
- [Docling issue #1287](https://github.com/docling-project/docling/issues/1287).
97
- - **Optional VLM interpretation** (DOCX composite figures, `[vlm]` extra,
98
- `--vlm`/`Config(use_vlm=True)`) — cloud description + a real rendered
99
- mermaid diagram (26 supported diagram types — flowcharts, pie/xy charts,
100
- sequence/state/ER diagrams, Gantt/timeline/sankey/treemap and more, see
101
- Status below) on top of the zero-loss floor, for figures with no native
102
- chart data at all (e.g. a dashboard screenshot). Provider-agnostic —
103
- OpenRouter by default, or direct OpenAI/Ollama/vLLM/LM Studio/Anthropic
104
- via `--vlm-provider` (`[vlm-direct]` extra). `--strict` upgrades one
105
- specific failure (the system `soffice`/LibreOffice binary missing) from
106
- a graceful skip to a hard error; every other VLM failure still degrades.
107
- - **Rich, typed result** — `ConversionResult` (markdown + warnings +
108
- chart/group counts + `vlm_used`), not a bare string.
109
- - **CLI included** — `refigure` console command, stdin/stdout-first, native
110
- batch mode, typed exit codes (see below).
111
- - **MCP server included** — `refigure-mcp` console command (`[mcp]` extra),
112
- stdio or Streamable HTTP, tools/resources/prompts, batch conversion with
113
- per-file isolation (see below).
114
- - **Docker image** — `ghcr.io/helgdemidov/refigure`, both console commands
115
- on `PATH`, `soffice`/LibreOffice baked in — the VLM composite-figure
116
- path works turnkey, no manual LibreOffice install (see below).
144
+ One converter, four ways to run it — pick whichever fits your pipeline.
145
+ Click a heading to expand it.
117
146
 
118
- ## CLI
147
+ <details>
148
+ <summary><b>CLI</b> — a console command, stdin/stdout-first, native batch mode</summary>
119
149
 
120
150
  `refigure` installs a console command — a thin wrapper over the same
121
151
  `convert()` used programmatically, no separate logic:
@@ -147,12 +177,17 @@ Exit codes:
147
177
  | 5 | the format's extra (`[docx]`/`[xlsx]`) isn't installed |
148
178
  | 6 | unexpected internal error |
149
179
 
150
- ## MCP server
180
+ </details>
181
+
182
+ <details>
183
+ <summary><b>MCP server</b> — for agents/IDEs that speak the protocol directly</summary>
151
184
 
152
185
  `refigure-mcp` — the same converters as an
153
186
  [MCP](https://modelcontextprotocol.io) server, for agents/IDEs that speak
154
187
  the protocol directly instead of shelling out to a CLI or importing the
155
- library:
188
+ library. Listed on the official
189
+ [MCP Registry](https://registry.modelcontextprotocol.io/?q=refigure) as
190
+ `io.github.HelgDemidov/refigure`:
156
191
 
157
192
  ```bash
158
193
  pip install "refigure[mcp,docx,xlsx]"
@@ -206,22 +241,31 @@ fairness soft-cap once 2+ callers are configured; `refigure-mcp --help`
206
241
  covers every tuning flag (concurrency, timeouts, resource-store limits,
207
242
  batch size, VLM ceiling).
208
243
 
209
- ## Docker
244
+ </details>
245
+
246
+ <details>
247
+ <summary><b>Docker</b> — one image, CLI and MCP server both on PATH, soffice baked in</summary>
210
248
 
211
249
  One image, both surfaces — `refigure` and `refigure-mcp` are already on
212
250
  `PATH`, no separate CLI/MCP builds to choose between. The one thing this
213
251
  format buys over `pip`/`uvx` that neither can: the system `soffice`/
214
252
  LibreOffice binary the VLM composite-figure path needs is baked in, not a
215
- manual install.
253
+ manual install. Multi-arch manifest (`linux/amd64` + `linux/arm64`) —
254
+ `docker pull` resolves the right layer automatically, including on
255
+ Apple Silicon.
216
256
 
217
257
  ```bash
218
- docker pull ghcr.io/helgdemidov/refigure:0.3.1
258
+ docker pull ghcr.io/helgdemidov/refigure:latest
219
259
  ```
220
260
 
261
+ Pin an exact version instead of `:latest` for reproducibility — e.g.
262
+ `:0.3.3` — see the [package page](https://github.com/HelgDemidov/refigure/pkgs/container/refigure)
263
+ for available tags.
264
+
221
265
  CLI, via a bind mount (the image's working directory is already `/data`):
222
266
 
223
267
  ```bash
224
- docker run --rm -v "$PWD:/data:ro" ghcr.io/helgdemidov/refigure:0.3.1 \
268
+ docker run --rm -v "$PWD:/data:ro" ghcr.io/helgdemidov/refigure:latest \
225
269
  refigure /data/report.docx
226
270
  ```
227
271
 
@@ -232,7 +276,7 @@ MCP, stdio — the client launches the container itself:
232
276
  "mcpServers": {
233
277
  "refigure": {
234
278
  "command": "docker",
235
- "args": ["run", "-i", "--rm", "ghcr.io/helgdemidov/refigure:0.3.1", "refigure-mcp"]
279
+ "args": ["run", "-i", "--rm", "ghcr.io/helgdemidov/refigure:latest", "refigure-mcp"]
236
280
  }
237
281
  }
238
282
  }
@@ -247,24 +291,43 @@ flag would silently never respond:
247
291
  ```bash
248
292
  echo "sk-... = alice" > tokens.txt
249
293
  docker run --rm -p 8000:8000 -v "$PWD/tokens.txt:/data/tokens.txt:ro" \
250
- ghcr.io/helgdemidov/refigure:0.3.1 \
294
+ ghcr.io/helgdemidov/refigure:latest \
251
295
  refigure-mcp --transport http --mcp-http-host 0.0.0.0 \
252
296
  --mcp-auth-token-file /data/tokens.txt
253
297
  ```
254
298
 
299
+ </details>
300
+
301
+ <details>
302
+ <summary><b>Claude Desktop (<code>.mcpb</code>)</b> — download, double-click, done</summary>
303
+
304
+ The simplest install for a non-technical user: download, double-click,
305
+ done — no terminal, no `pip`/`uvx`/`docker`. Covers `docx`+`xlsx`
306
+ conversion only (no VLM — that needs the `[vlm]` extra, deliberately
307
+ not carried by this bundle); dependencies resolve fresh from PyPI via
308
+ `uv` on first launch, the same mechanism `uvx` uses under the hood,
309
+ just one click instead of a config snippet.
310
+
311
+ [**Download refigure.mcpb**](https://github.com/HelgDemidov/refigure/releases/latest/download/refigure.mcpb)
312
+ — open it with Claude Desktop to install.
313
+
314
+ </details>
315
+
255
316
  ## Real examples
256
317
 
257
- Full `convert()` output on real, openly-licensed documents — not
258
- cherry-picked snippets. Each file's own header states its source, license
259
- and attribution.
318
+ Concentrated excerpts (≤200 lines each) of real `convert()` output on
319
+ real, openly-licensed documents — the actual markdown a pipeline would
320
+ ingest, not a screenshot or a cherry-picked one-liner. Each file's own
321
+ header states its source, license and attribution; trimmed sections are
322
+ marked inline, never fabricated to fill space.
260
323
 
261
324
  | Source | Demonstrates | Output |
262
325
  | --- | --- | --- |
263
- | `hackair-d7.7-pilot-evaluation.docx` | native chart extraction — 8 charts, 6 render as mermaid diagrams | [examples/hackair-native-charts.md](examples/hackair-native-charts.md) |
264
- | `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](examples/swd2018-combo.md) |
265
- | `govtech-2025-charts.xlsx` | XLSX at scale — 55 charts, 33 render as mermaid diagrams | [examples/govtech-xlsx-charts.md](examples/govtech-xlsx-charts.md) |
266
- | `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](examples/swd2021-pie-chart.md) |
267
- | `efsa-trichinella-dashboard-guide.docx` | `--vlm` interpretation — 27 figures with no native chart data, real numbers recovered from screenshots | [examples/efsa-trichinella-vlm.md](examples/efsa-trichinella-vlm.md) |
326
+ | `hackair-d7.7-pilot-evaluation.docx` | native chart extraction — real survey tables + `xychart-beta` bar charts | [examples/hackair-native-charts.md](examples/hackair-native-charts.md) |
327
+ | `swd2018-254-marine-litter-ia-annex.docx` | honest fallback — a chart that fails render-verification degrades to a clean table, plus 2 composite-figure zero-loss markers | [examples/swd2018-combo.md](examples/swd2018-combo.md) |
328
+ | `govtech-2025-charts.xlsx` | XLSX native charts — 3 distinct types (`xychart-beta`/`radar-beta`/`pie`) from one workbook | [examples/govtech-xlsx-charts.md](examples/govtech-xlsx-charts.md) |
329
+ | `swd2021-396-platform-work-ia.docx` | native pie + a 23-year time series, real EU-survey labels | [examples/swd2021-pie-chart.md](examples/swd2021-pie-chart.md) |
330
+ | `efsa-trichinella-dashboard-guide.docx` | `--vlm` interpretation — 2 screenshot figures recovered as a bar chart and a UI flowchart, real numbers | [examples/efsa-trichinella-vlm.md](examples/efsa-trichinella-vlm.md) |
268
331
 
269
332
  Open any of these on GitHub and both views are right there: the raw
270
333
  ```` ```mermaid ```` fence an LLM/RAG pipeline would read, and its native
@@ -272,37 +335,42 @@ GitHub rendering — no extra step, that's GitHub's own Markdown support.
272
335
 
273
336
  ## Status
274
337
 
275
- Published on PyPI as `refigure`. Tested against 27 real documents (15 DOCX +
276
- 12 XLSX) — 407 native charts found (400 rendered), 35 composite figures
277
- recovered as positioned zero-loss markers — see
278
- [`tests/integration/fixtures/manifest.yaml`](tests/integration/fixtures/manifest.yaml)
279
- for provenance, licenses and attribution. CI gates on a combined
280
- unit+integration test-coverage floor of 95%.
281
-
282
- The converters were extracted from a working document-analysis pipeline
283
- (government AI-policy corpus) into a single package with per-format extras
284
- (`[docx]` / `[xlsx]`). VLM interpretation of composite figures the chart
285
- engine can't reconstruct (`[vlm]` extra, `Config(use_vlm=True)`,
286
- provider-agnostic — direct OpenAI/Anthropic via `[vlm-direct]`, also needs
287
- the system `soffice`/LibreOffice binary, not installable via pip) is fully
288
- implemented, tested, and exposed through the `refigure` CLI (`--vlm` and
289
- friends — see CLI above and Quickstart). Mermaid-diagram recognition on
290
- top of that varies by diagram type and by what's actually on the source
291
- figure — common types (flowcharts, pie/xy charts) are picked reliably;
292
- more specialized ones depend on the figure carrying an unambiguous visual
293
- cue, and not every figure produces a diagram at all — a plain text
294
- description is a valid, honest fallback when it doesn't.
338
+ - **Validated** against 27 real documents (15 DOCX + 12 XLSX) — 407 native
339
+ charts found (400 rendered), 35 composite figures recovered as
340
+ positioned zero-loss markers. Full provenance:
341
+ [`tests/integration/fixtures/manifest.yaml`](tests/integration/fixtures/manifest.yaml).
342
+ - **Tested**: CI gates on a combined unit+integration coverage floor of 95%.
343
+ - **Published** as `v0.3.3` — [PyPI](https://pypi.org/project/refigure/)
344
+ (trusted publishing, no stored tokens),
345
+ [GHCR](https://github.com/HelgDemidov/refigure/pkgs/container/refigure),
346
+ and the official
347
+ [MCP Registry](https://registry.modelcontextprotocol.io/?q=refigure) as
348
+ `io.github.HelgDemidov/refigure`. `refigure-md` is a reserved alternate
349
+ name, not an active release.
350
+
351
+ Extracted from a working document-analysis pipeline (a government
352
+ AI-policy research corpus), not built from scratch for this release.
353
+
354
+ VLM interpretation of composite figures the chart engine can't reconstruct
355
+ is fully implemented and tested, not a stub — `[vlm]` extra,
356
+ provider-agnostic (direct OpenAI/Anthropic via `[vlm-direct]`), also needs
357
+ the system `soffice`/LibreOffice binary.
358
+
359
+ Mermaid-diagram recognition depends on diagram type and on what the
360
+ source figure actually contains:
361
+
362
+ - Common types (flowcharts, pie/xy charts) are picked reliably.
363
+ - More specialized ones need an unambiguous visual cue on the source figure.
364
+ - Not every figure produces a diagram — a plain-text description is an
365
+ honest fallback, not a failure.
295
366
 
296
367
  **PDF is out of scope, on purpose — a boundary, not a gap.** PDF has no
297
368
  equivalent of OOXML's cached chart data (`numCache`/`strCache`) for any
298
- mainstream chart generator, so the native, rasterize-free extraction
299
- this project is built on doesn't transfer to it — confirmed by research
300
- into PDF's own structure and how leading PDF converters handle charts
301
- today, not assumed. For mixed-format corpora, route by extension instead
302
- of expecting one tool to cover everything —
303
- [Docling](https://github.com/docling-project/docling) or
304
- [MarkItDown](https://github.com/microsoft/markitdown) for PDF, refigure
305
- for DOCX/XLSX where the chart data actually survives in the file:
369
+ mainstream chart generator, so the native, rasterize-free extraction this
370
+ project is built on doesn't transfer to it — confirmed by research into
371
+ PDF's own structure and how leading PDF converters handle charts today,
372
+ not assumed. For mixed-format corpora, route by extension instead of
373
+ expecting one tool to cover everything:
306
374
 
307
375
  ```python
308
376
  import refigure.docx
@@ -316,11 +384,9 @@ else:
316
384
  markdown = refigure.xlsx.convert(path).markdown
317
385
  ```
318
386
 
319
- `v0.3.1` published via trusted publishing (GitHub↔PyPI, no stored tokens),
320
- also on GHCR as `ghcr.io/helgdemidov/refigure` and on the official
321
- [MCP Registry](https://registry.modelcontextprotocol.io) as
322
- `io.github.HelgDemidov/refigure`. `refigure-md` is a reserved alternate
323
- name, not an active release.
387
+ Use [Docling](https://github.com/docling-project/docling) or
388
+ [MarkItDown](https://github.com/microsoft/markitdown) for PDF, refigure
389
+ for DOCX/XLSX where the chart data actually survives in the file.
324
390
 
325
391
  ## License
326
392
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "refigure"
7
- version = "0.3.2"
7
+ version = "0.3.4"
8
8
  description = "DOCX/XLSX -> Markdown conversion with native OOXML chart-data extraction (no rasterize/OCR/VLM needed) + optional VLM interpretation for figures with no native chart data"
9
9
  readme = "README.md"
10
10
  license = "Apache-2.0"
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