Skip to main content

docxchart

Edit the data behind Word charts without disturbing the formatting.

Extract every chart's values from a .docx to JSON, edit the JSON, apply it back. Fills, label positions, axis settings and theme bindings are never touched.

Status: early. Handles same-length value edits on standard category charts, and setting the input cells of embedded Excel worksheets (including dropdowns). Anything it can't handle raises rather than guessing — see Limitations.

Install

pip install docxchart

Requires Python 3.11+.

CLI

docxchart list    report.docx
docxchart extract report.docx -o data.json
docxchart apply   report.docx --data data.json -o report-updated.docx
Command
list FILE chart keys, titles, and shape
extract FILE [-o OUT] write chart data to JSON (default data.json)
apply FILE --data JSON [-o OUT] write edited data back to a new docx
--sidecar on extract, also emit the internal manifest

apply never writes in place. Omit -o and it errors.

Data format

{
  "_docxchart": {
    "source": "report.docx",
    "basedOn": "sha256:1f3a…",
    "extractedAt": "2026-07-29T09:14:22Z",
    "formatVersion": "1.0"
  },
  "charts": [
    {
      "key": "revenue_by_quarter",
      "_meta": {
        "title": "Revenue by Quarter",
        "type": "barChart",
        "keyStable": true,
        "readOnly": true
      },
      "series": [
        { "name": "2024", "formatCode": "#,##0" },
        { "name": "2025", "formatCode": "#,##0" }
      ],
      "rows": [
        { "category": "Q1", "2024": 120,  "2025": 130 },
        { "category": "Q2", "2024": null, "2025": 110 },
        { "category": "Q3", "2024": 95,   "2025": 99  }
      ]
    }
  ]
}
Field
key chart identifier (see Naming)
_meta read-only. apply errors if changed.
series display order of the series. Order matters for legend and stacking.
rows one row per category. Series values keyed by series name.
basedOn hash of the source docx. apply refuses if the file has changed since extract.

null is a genuine blank in the chart, rendered as a gap. It is not zero. Omitting a key means the same thing.

Dates are stored as Excel serial numbers with a formatCode, and are passed through unconverted.

Python API

from docxchart import ChartDoc

doc = ChartDoc.open("report.docx")

list(doc.charts)
# ['revenue_by_quarter', 'headcount']

chart = doc.charts["revenue_by_quarter"]
chart.table
# ChartTable(
#     categories=['Q1', 'Q2', 'Q3'],
#     series=[Series(name='2024', values=[120, None, 95])],
# )

chart.table.series[0].values = [130, 110, 99]   # values (same count)
chart.table.series[0].name   = "FY2024"          # series name / legend text
chart.table.categories[1]    = "Q2 (revised)"    # category label (same count)
chart.table.hide_legend_entry(3)                 # suppress an empty series' legend entry

chart.table.insert_category(1, "Q1.5")           # add a row (every series gets a blank)
chart.table.series[0].values[1] = 88             # then fill it
chart.table.delete_category(4)                    # remove a row

doc.validate()               # -> list[Finding]
doc.save("report-updated.docx")

ChartTable and Series are plain dataclasses. No XML is exposed.

Category labels are editable (the count is fixed — adding/removing rows is not supported). A label write fans out to every series' c:cat and is mirrored into the embedded workbook; None is a genuine blank, and labels round-trip byte-exactly (trailing spaces and footnote markers are preserved).

hide_legend_entry(index) / show_legend_entry(index) (or series.legend_hidden = True) suppress or restore a series' legend entry via c:legendEntry/c:delete — useful when a template carries a spare, empty series that would otherwise show a blank rater in the legend.

Category rows can be added and removed with insert_category(index, label) and delete_category(index) (programmatic path only). This resizes every series' cache, rewrites the c:f ranges, inserts/deletes the embedded-workbook row, and shifts per-point formatting (c:dPt/c:dLbl). A chart carrying overlay shapes (c:userShapes) is refused — a resize moves the plot area out from under absolutely-positioned annotations — unless you pass force=True.

Embedded worksheets

Some documents embed a live Excel worksheet as an OLE object (an Excel.Sheet object) rather than a chart — often a small "raw scores in, converted results out" table. docxchart can set the input cells of these programmatically, alongside charts.

docxchart list    report.docx      # embedded worksheets show up too
docxchart extract report.docx -o data.json
docxchart apply   report.docx --data data.json -o report-updated.docx

extract adds a worksheets section:

{
  "worksheets": [
    {
      "key": "worksheet_1",
      "_meta": { "progId": "Excel.Sheet.12", "sheet": "Sheet1", "readOnly": true },
      "cells":    { "A1": 40, "A2": 20, "B1": "NEUROTICISM" },
      "_derived": { "C1": "=NORM.DIST(A1, 50, 10, TRUE) * 100" }
    }
  ]
}
  • cells are the constant (non-formula) cells — the editable inputs.
  • _derived lists the formula cells for context. It is read-only: those values are computed by Excel, not by you. apply errors if you change one, or if you put a formula cell in cells.
  • _dropdowns (when present) lists each list-validated cell and its allowed values. You still set the value in cells, but it must be one of the options — apply refuses anything else (e.g. a CliftonStrengths cell that only accepts the 34 theme names).
doc = ChartDoc.open("report.docx")
ws = doc.worksheets["worksheet_1"]
ws.set_cell("A1", 63)                 # one input cell
ws.set_cells({"A2": 11, "A3": 50})    # several
doc.save("report-updated.docx")

Only cell values change; conditional formatting, formulas, number formats and every other byte are preserved (the inner .xlsx is edited surgically, not reserialized).

One thing to know about the display. What Word draws for an embedded object is a cached picture, and the formula cells (C/D/E…) are recomputed by Excel — neither refreshes just from editing the data. After apply, activate the object in Word once (double-click it, or update fields / print) and Excel recomputes the formulas and regenerates the picture. docxchart sets fullCalcOnLoad so that recompute is guaranteed when it happens; it cannot regenerate the picture itself (that needs an Office engine).

Editing input cells that don't exist yet (extending the input range) is not supported yet — it raises rather than guessing a new cell's formatting.

To inspect one of these documents — inputs, formulas, dropdown options, and the cached preview picture — and to see for yourself that editing the data leaves the picture untouched until Word re-renders:

python scripts/probe_worksheet.py report.docx
python scripts/probe_worksheet.py report.docx --set C2=Strategic -o out.docx

Naming

Charts are identified by their Alt Text, which survives Word re-saves, insertions and deletions — unlike file paths and relationship IDs.

  1. Right-click the chart → Edit Alt Text
  2. Enter a key, e.g. revenue_by_quarter

Without Alt Text, the key falls back to the chart's title, slugified. This works, but the key changes if anyone edits the title. list marks these as unstable. Duplicate titles are suffixed by document order: revenue, revenue-2.

Limitations

Unsupported cases raise UnsupportedChart or UnsupportedEdit with a reason. Nothing is written on a partial or best-guess basis.

Not supported:

  • Adding or removing categories via the JSON apply path — use the programmatic insert_category / delete_category instead (row resize is refused on charts with c:userShapes unless forced)
  • Adding or removing series
  • Scatter and bubble charts
  • chartEx types: waterfall, treemap, sunburst, funnel, histogram, box & whisker, map
  • Multi-level category axes
  • Charts in headers, footers or footnotes
  • Charts bound to an external data source
  • Any change to appearance — colours, fonts, chart type, axis config and layout are read-only (suppressing a legend entry is supported; other legend styling is not)

Charts must be created in Word. docxchart only rebinds data on charts that already exist.

Notes

Word renders from the chart's cached values, not from the embedded workbook, but the workbook is what you see when you click Edit Data. docxchart always writes both, so the two never disagree.

Parts of the package that weren't changed come out byte-identical.

License

MIT — see LICENSE.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

docxchart-0.4.1.tar.gz (58.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

docxchart-0.4.1-py3-none-any.whl (54.7 kB view details)

Uploaded Python 3

File details

Details for the file docxchart-0.4.1.tar.gz.

File metadata

  • Download URL: docxchart-0.4.1.tar.gz
  • Upload date:
  • Size: 58.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.14.6

File hashes

Hashes for docxchart-0.4.1.tar.gz
Algorithm Hash digest
SHA256 9258ab6e98f71609bfa691a8f8938b85e0df0ada6207e19fb78e37f94128c925
MD5 9a87d760b7adb625ebff92876db335e1
BLAKE2b-256 8175b646144e94fc572e83d161cf3090b6672f04665ef061d502635a30ee9211

See more details on using hashes here.

File details

Details for the file docxchart-0.4.1-py3-none-any.whl.

File metadata

  • Download URL: docxchart-0.4.1-py3-none-any.whl
  • Upload date:
  • Size: 54.7 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.14.6

File hashes

Hashes for docxchart-0.4.1-py3-none-any.whl
Algorithm Hash digest
SHA256 09424752d6b9597dc1c8f5d40d09acc4cda7ae5d6e234b5cafa2f89aae7ab47b
MD5 09c695e548758c87707c3c4635b7dd43
BLAKE2b-256 4de5ebd65fcb687be5d4b7fa56a323f56c40fd9d2df7edfe259c4e365feb1dba

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.4.1 This release

2 files

0.4.0

2 files

0.1.1

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page