Skip to main content

vsdxkit

CI Python 3.10–3.14 BSD-3-Clause Docs

Create, edit and analyse Microsoft Visio .vsdx files with Python. Visio is not required at runtime.

0.x API notice. 0.7 is the last release of the inherited API. 1.0 renames VisioFile to Document and Container to SwimlaneDiagram, splits Connect into an internal ConnectionRecord and a public Connector, and drops the context manager: opening closes the archive before it returns, and save() is the only write. The 1.0 design lists every change, and a migration guide lands at docs/migration-1.0.rst with the first breaking release. Once there is a release on PyPI again, pin vsdxkit<1 to stay on the 0.x names; see Installation for what the index holds today.

The distribution is named vsdxkit. The import remains vsdx, so existing code keeps working.

vsdxkit adds shape creation, Visio-faithful connectors, connector re-anchoring, cross-functional flowchart swimlanes, stricter package handling, current Python tooling and typed public APIs. It began as a fork of dave-howard/vsdx and is now developed as its own project; see Provenance and licence.

What it does

  • Opens, queries and edits existing .vsdx files without Microsoft Visio.
  • Finds shapes by ID, text, regular expression or Shape Data.
  • Creates common flowchart shapes from a bundled palette.
  • Creates dynamic or connection-point glue with straight, right-angle or curved routing.
  • Re-anchors either end of an existing connector.
  • Reads and extends Visio cross-functional flowchart swimlanes.
  • Copies shapes and pages while rewriting package-local IDs and importing masters.
  • Renders data into Visio templates with Jinja.
  • Saves to a new file or safely replaces the source file in place.

The implementation edits the XML parts inside the Open Packaging Convention archive. It does not drive the Visio user interface. Generated files are checked against Microsoft Visio through COM before a change that Visio could silently repair is considered done, and what Visio reported is recorded so CI can replay it without Visio. The COM check itself is manual; no workflow runs Visio. See When a change needs Visio.

Installation

As of 2026-09-13 there is no release on PyPI. The first release was withdrawn after a security defect was found in it and cannot be republished, so pip install vsdxkit finds no versions until the next one lands. The PyPI project page shows the current state.

Install from GitHub in the meantime:

python -m pip install "vsdxkit @ git+https://github.com/firmfooting/vsdxkit.git"

That tracks main, so pin a commit if you need a reproducible install. Once there is a release on the index, install it with pip install vsdxkit.

For development:

git clone https://github.com/firmfooting/vsdxkit.git
cd vsdxkit
uv sync --locked

Python 3.10–3.14 is supported on Linux, Windows and macOS. Add --group docs to that sync if you also want to build the documentation; Sphinx needs Python 3.12 or later.

Open, edit and save

Use the context manager to close the package cleanly. Saving is explicit.

from vsdx import VisioFile

with VisioFile("diagram.vsdx") as vis:
    page = vis.pages[0]
    shape = page.find_shape_by_text("Shape to remove")

    if shape is not None:
        shape.text = "Renamed shape"

    vis.save_vsdx("edited.vsdx")

Call save_vsdx() without a filename to replace the source file in place:

with VisioFile("diagram.vsdx") as vis:
    vis.pages[0].name = "Current state"
    vis.save_vsdx()

Create shapes and connectors

Shape coordinates are in Visio page units, normally inches. x and y identify the shape centre.

from vsdx import VisioFile

with VisioFile("diagram.vsdx") as vis:
    page = vis.pages[0]

    start = vis.create_shape(
        page, "PALETTE_START_END", 2.0, 6.0, text="Start"
    )
    work = vis.create_shape(
        page, "PALETTE_PROCESS", 6.0, 6.0, text="Do the thing"
    )
    decision = vis.create_shape(
        page, "PALETTE_DECISION", 10.0, 6.0, text="OK?"
    )

    page.connect_shapes(start, work)
    page.connect_shapes(work, decision, route="rightangle")
    vis.save_vsdx("flow.vsdx")

Bundled palette names are:

  • PALETTE_PROCESS
  • PALETTE_DECISION
  • PALETTE_START_END
  • PALETTE_PARALLELOGRAM
  • PALETTE_DATABASE

Connector route combines glue and routing behaviour:

Value Meaning
dynamic Dynamic shape glue. This is the default.
point Glue to zero-based connection points selected with from_cp and to_cp.
straight Dynamic glue with straight routing.
rightangle Dynamic glue with right-angle routing.
curved Dynamic glue with curved routing.
`point curved`

Re-anchor a connector

Pass only the end that should move. A None endpoint keeps the current shape.

with VisioFile("flow.vsdx") as vis:
    page = vis.pages[0]
    store = vis.create_shape(
        page, "PALETTE_DATABASE", 10.0, 2.0, text="Store"
    )

    # a connector is the shape a Connect record points from
    connector = page.find_shape_by_id(page.connects[0].from_id)
    page.reanchor_connector(connector, to_shape=store)

    vis.save_vsdx("reanchored.vsdx")

Deleting a shape through page.delete_shape(shape) also removes incident connectors and their Connect records.

Work with swimlanes

Swimlane operations require an existing Visio cross-functional flowchart (CFF) page. add_swimlane() clones the current top lane and updates the CFF container geometry.

with VisioFile("cross-functional-flow.vsdx") as vis:
    page = vis.pages[0]
    container = page.get_container()

    if container is None:
        raise ValueError("The page is not a Visio CFF diagram")

    review_lane = page.add_swimlane("Review")
    check = vis.create_shape(
        page, "PALETTE_PROCESS", 6.0, 2.0, text="Check"
    )
    page.add_shape_to_lane(check, review_lane)

    assert container.lane_of(check) is not None
    vis.save_vsdx("with-review-lane.vsdx")

Visio CFF membership is geometric. Shapes are associated with the lane whose vertical band contains their centre; there is no separate membership field to write.

Render a Jinja template

Jinja expressions can be stored in shape text and rendered into a new file:

with VisioFile("template.vsdx") as vis:
    vis.jinja_render_vsdx(
        context={"project": "Ward refurbishment", "owner": "Facilities"}
    )
    vis.save_vsdx("rendered.vsdx")

The package also supports its existing group-shape loop and showif conventions. See docs/templating.rst and the tests/test_jinja*.py cases for the exact template structure.

Limits

  • The library starts from an existing .vsdx; it does not create a complete Visio document package from nothing.
  • .vsdm files can be read and saved, but only back to a .vsdm destination. The package kind is decided by the content type of visio/document.xml, not by the filename, so save_vsdx() refuses a .vsdx destination for a macro-enabled package and a .vsdm destination for one that is not — either would produce a file whose extension and [Content_Types].xml disagree, which Visio reports as corrupt. Stripping macros to convert a .vsdm into a .vsdx is not supported. A destination with no extension, or with an unrelated one, gets the matching Visio extension appended.
  • Swimlane creation works on existing Visio CFF diagrams. It does not convert an ordinary page into a CFF diagram.
  • Visio may recalculate layout when a generated file opens. The library writes the glue and route cells but does not reproduce Visio's entire layout engine.
  • Loading enforces package expansion limits before any archive member is read: at most 512 members, 64 MiB per member, 256 MiB total uncompressed, and a 100:1 compression ratio, plus rejection of duplicate and path-unsafe member names. A hostile or accidental archive is refused with vsdx.PackageLimitError instead of exhausting process memory. The defaults suit documents from unknown sources; trusted callers can relax the caps with VisioFile(filename, limits=PackageLimits(...)) or limits_path="vsdx.limits.json" (same keys, JSON object).

Development and verification

uv run --no-sync python -m pytest tests -q
uv run --no-sync ruff check vsdx tests tools
uv run --no-sync ruff format --check vsdx tests tools
uv run --no-sync pyrefly check vsdx --min-severity warn --output-format min-text
uv run --no-sync sphinx-build -W --keep-going -b html docs docs/_build/html
uv run --no-sync python -m build

The package is held at pyrefly's strict preset. CI tests Python 3.10–3.14 on Linux and Windows, and 3.10 and 3.14 on macOS. Changes that Visio could silently repair also run through tools/visio_verify.py, which opens the file in an invisible Microsoft Visio instance and diffs what Visio reports against what the package declares. That step is manual, on a maintainer's Windows machine; GitHub's runners have no Visio. What Visio said is recorded under tests/fixtures/visio_observations/, and CI replays those recordings against the fixtures on every run.

Documentation

The Sphinx source is in docs/. Build it locally with:

uv sync --locked --group docs
uv run --no-sync python -m sphinx -W --keep-going -b html docs docs/_build/html

Sphinx is pinned in the docs dependency group, which requires Python 3.12 or later. The library itself still supports 3.10, so run the docs build on a 3.12+ interpreter.

Provenance and licence

vsdxkit descends from dave-howard/vsdx, originally written by Dave Howard and released under the BSD 3-Clause licence. That work is the foundation this library is built on, and its copyright notice is retained in LICENSE alongside our own.

vsdxkit is now developed independently: it is not a downstream of that project and does not track it. The vsdx import namespace is kept so existing code continues to work, and the licence remains BSD 3-Clause.

Metadata

Release files for vsdxkit 0.8.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for vsdxkit 0.8.0
File Size Uploaded Provenance Actions
vsdxkit-0.8.0.tar.gz 765.7 kB With provenance Details for vsdxkit-0.8.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for vsdxkit 0.8.0
File Interpreter ABI Platform Actions
vsdxkit-0.8.0-py3-none-any.whl Python 3 none any Details for vsdxkit-0.8.0-py3-none-any.whl

Total release size: 883.1 kB

Release files / vsdxkit-0.8.0.tar.gz

Download URL vsdxkit-0.8.0.tar.gz
Size 765.7 kB
Tags Source
SHA-256 checksum
How to use checksums
a72a6fcc238c0dd54f69de485e6e1f9d98396a8a520d8429c8a533ce147c4a27
BLAKE2b-256 checksum
How to use checksums
def8b51b07daa6838916ad6a5f3c32b04c492d518a8a88eb5378f98e75ad1cab
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 13, 2026.

Transparency log

Release files / vsdxkit-0.8.0-py3-none-any.whl

Download URL vsdxkit-0.8.0-py3-none-any.whl
Size 117.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f1ad4ac42cd5d4e02c9f009aa1af6f89d3b65e8800b9e319ab51732681dfb416
BLAKE2b-256 checksum
How to use checksums
50900b608b3541ac099f1eb70e706e6e80bb05e6a979f72fd830dfc297166007
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 13, 2026.

Transparency log

Security for vsdxkit 0.8.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for vsdxkit-0.8.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 13, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

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