API reference

The distribution installs as vsdxkit but keeps the vsdx import namespace.

VisioFile

class vsdx.vsdxfile.VisioFile(filename: str, debug: bool = False, limits: PackageLimits | None = None, limits_path: str | None = None)

Represents a vsdx file

Parameters:
  • filename (str) – filename the VisioFile was created from

  • pages (list of Page) – a list of pages in the VisioFile

  • master_pages (list of Page) – a list of master pages in the VisioFile

__enter__() VisioFile
__exit__(exc_type: type[BaseException] | None, exc_val: BaseException | None, exc_tb: TracebackType | None) None
__init__(filename: str, debug: bool = False, limits: PackageLimits | None = None, limits_path: str | None = None) None

VisioFile constructor

Parameters:
  • filename (str) – the vsdx file to load and create the VisioFile object from

  • debug (bool, default to False) – enable/disable debugging

  • limits (PackageLimits, optional) – package expansion caps; the defaults suit untrusted documents

  • limits_path (str, optional) – JSON file with the same keys as the PackageLimits fields

add_page(name: str | None = None) Page

Add a new page at the end of the VisioFile

Parameters:

name (str, optional) – The name of the new page

Returns:

Page object representing the new page

add_page_at(index: int, name: str | None = None) Page

Add a new page at the specified index of the VisioFile

Parameters:
  • index (int) – zero-based index where the new page will be placed

  • name (str, optional) – The name of the new page

Returns:

Page object representing the new page

copy_page(page: Page, *, index: int | PagePosition = PagePosition.AFTER, name: str | None = None) Page

Copy an existing page and insert in VisioFile

Parameters:
  • page (Page) – the page to copy

  • index (int | PagePosition) – the specific int or relation PagePosition location for new page

  • name (str) – name of new page (note this may be altered if name already exists)

Returns:

the newly created page

create_shape(page: Page, palette_name: str, x: float, y: float, w: float | None = None, h: float | None = None, text: str | None = None) Shape

Create a new shape on a page from the extended shape palette.

Palette shapes are deliberately masterless, so creation needs no master-import and works on any document. Reuses copy_shape and the existing position/size setters (no second copy or id-rewrite path).

Parameters:
  • page – destination page

  • palette_name – sentinel name, e.g. ‘PALETTE_PROCESS’, ‘PALETTE_DECISION’, ‘PALETTE_START_END’, ‘PALETTE_PARALLELOGRAM’, ‘PALETTE_DATABASE’

  • y (x,) – centre position of the new shape

  • h (w,) – optional width/height overrides

  • text – label text; the palette sentinel name is cleared when None

Returns:

the new Shape

get_page_by_name(name: str) Page | None

Get page from VisioFile with matching name

Parameters:

name (str) – The name of the required page

Returns:

Page object representing the page (or None if not found)

jinja_render_vsdx(context: dict[str, object]) None

Transform a template VisioFile object using the Jinja language The method updates the VisioFile object loaded from the template file, so does not return any value Note: vsdx specific extensions are available such as {% for item in list %} statements with no {% endfor %}

Parameters:

context (dict) – A dictionary containing values that can be accessed by the Jinja processor

Returns:

None

remove_page_by_index(index: int) None

Remove zero-based nth page from VisioFile object

Parameters:

index (int) – Zero-based index of the page

Returns:

None

remove_page_by_name(page_name: str) None

Remove first page from VisioFile object that matches the page_name

Parameters:

page_name (str) – page of page to delete

Returns:

None

save_vsdx(new_filename: str | None = None) None

save the VisioFile object as new vsdx file

Parameters:

new_filename (str) – path to save vsdx file. A .vsdx or .vsdm extension must match the package’s own kind; any other name gets the matching extension appended. Omit it to save over the source file, which is checked the same way but never renamed.

Raises:

ValueError – if the extension contradicts the package kind

Page and PagePosition

class vsdx.pages.Page(xml: ET.ElementTree[ET.Element], filename: str, page_name: str, page_id: str, rel_id: str, vis: VisioFile)

Represents a page or a master page in a vsdx file

Parameters:
  • vis (VisioFile) – the VisioFile object the page belongs to

  • name (str) – the name of the page

  • connects (List of Connect) – a list of Connect objects in the page

add_shape_to_lane(shape: Shape, lane: Shape) None

Move a shape so its centre lies within a CFF swimlane’s geometric band.

add_swimlane(label: str | None = None) Shape

Add a swimlane to this page’s CFF Container by cloning its top lane.

Returns:

the new lane Shape

property all_shapes: list[Shape]
property child_shapes: list[Shape]

Return list of Shape objects at top level of VisioFile.Page

Returns:

list of Shape objects

Return type:

List[Shape]

connect_shapes(from_shape: Shape, to_shape: Shape, route: str = 'dynamic', from_cp: int = 0, to_cp: int = 0) Shape

Create a Visio-faithful dynamic connector between two shapes on this page.

route: ‘dynamic’ (shape glue, default), ‘point’ (connection-point glue using from_cp/to_cp 0-based connection point indexes), optionally with routing behaviour ‘straight’, ‘rightangle’ or ‘curved’ - e.g. route=’straight’ or route=’point|curved’.

Returns:

the new connector Shape

Return type:

Shape

property connects: list[Connect]
delete_shape(shape: Shape) None

Delete a shape from this page, removing any incident connectors.

A group takes its children with it, so connectors glued to a child and records naming one are removed alongside the group’s own. Connectors are deleted first (including their Connect records), then the shape.

Raises:

ValueError – if the shape is not on this page

find_shape_by_id(shape_id: str) Shape | None
find_shape_by_text(text: str) Shape | None
find_shapes_by_text(text: str) list[Shape]
get_connectors_between(shape_a_id: str = '', shape_a_text: str = '', shape_b_id: str = '', shape_b_text: str = '') set[Shape]
get_container() Container | None

Return the page’s CFF Container (swimlane diagram root), or None.

reanchor_connector(connector_shape: Shape, from_shape: Shape | None = None, to_shape: Shape | None = None, route: str = 'dynamic', from_cp: int = 0, to_cp: int = 0) Shape

Retarget an existing connector to new endpoints (either end may be kept by passing None).

Returns:

the connector Shape

class vsdx.pages.PagePosition(*values)
AFTER = -2
BEFORE = -3
END = -1
FIRST = 0
LAST = -1

Shape, Cell and DataProperty

class vsdx.shapes.Shape(xml: Element, parent: Page | Shape, page: Page)

Represents a single shape, or a group shape containing other shapes

property all_shapes: list[Shape]
property bounds: tuple[float, float, float, float]
cell_value(name: str) str | None
cells: dict[str, Cell]
property center_x_y: tuple[float | None, float | None]
property child_shapes: list[Shape]

Get child/sub shapes contained by a Shape

Returns:

list of Shape objects

Return type:

List[Shape]

property connected_shapes: list[Shape]

Shapes at the far end of this shape’s connector records.

Connect records can reference a shape that is not on this page (for example a connector record left behind by a deleted shape), so lookups that resolve to nothing are skipped rather than held as None.

property connects: list[Connect]

Connect items linking this shape to others.

The annotation quotes Connect so typing.get_type_hints stays runtime-resolvable while avoiding an import cycle with vsdx.connectors (resolved lazily by the type checker).

copy(page: Page | None = None) Shape

Copy this Shape to the specified destination Page, and return the copy.

If the destination page is not specified, the Shape is copied to its containing Page.

Parameters:

page (Page (Optional)) – The page where the new Shape will be placed. If not specified, the copy will be placed in the original shape’s page.

Returns:

Shape the new copy of shape

property data_properties: dict[str, DataProperty]

Get data properties of the shape - which labels, names, and values returns a dictionary of DataProperty objects indexed by property label

The result is cached against this shape’s own Property rows, so adding, removing or replacing one is picked up on the next read. A row edited in place is not: the cache is keyed on row identity, so renaming a property’s Label leaves the dictionary keyed under the old label until some row is added or removed. One limitation remains, over inherited properties:

  • A property inherited from a master is resolved when this shape is first read. Editing the master afterwards is not reflected here: the cache key covers this shape’s own rows, not the master’s, and this shape holds the master it resolved for as long as it lives.

Setting DataProperty.value on an inherited property is safe: the property is marked inherited, so writing to it creates an override row on this shape and leaves the master alone. DataProperty.set_attribute() and DataProperty.remove_attribute() do not yet do this, and still write an inherited property’s cells in the master.

Returns:

Dict[str, DataProperty]

property fill_color: str | None
find_replace(old: str, new: str) None
find_shape_by_text(text: str) Shape | None
find_shapes_by_text(text: str) list[Shape]
property geometry: Geometry | None

This shape’s Geometry section, merged with the master’s, or None.

Built on first read and then held for as long as this Shape object lives, so shape.geometry twice gives the same object and a row written through one read is seen by the next.

Building it resolves the shape’s master, which is why it is deferred: walking a page for ids or text mints a Shape per element and touches no geometry at all.

Only the building is deferred. Which section it reads is decided when the Shape is built, as cells decides which of that section’s rows it lists, so the two stay in step: a section added to or removed from the XML afterwards is seen by neither until the shape is read again.

get_or_create_cell(name: str, v: str | None = None, f: str | None = None) Cell

Set or create a named cell on this shape.

Existing cells have their V/F attributes updated in place. New cells are inserted after the last direct Cell child so the shape keeps the schema ordering (cells ahead of Text/Sections).

Parameters:
  • name – cell name (N attribute), e.g. ‘PinX’

  • v – value to set on the V attribute (optional)

  • f – formula to set on the F attribute (optional)

Returns:

the Cell object

property height: float | None
property line_color: str | None
property line_weight: float | None
move(x_delta: float, y_delta: float) None
remove() None

Remove this shape from its page or group.

Deprecated in favour of Page.delete_shape(), which this now calls: it is the single path that also deletes the connectors glued to the shape and their Connect records. Detaching the element alone left orphan connectors and dangling records behind, and Visio repairs such a package on open. It also raised ValueError for a shape inside a group, whose XML is held by the group’s Shapes container rather than by the group element the parent Shape wraps.

shape_name: str | None
property text: str
property text_color: str | None

Get text color of shape - returns only first color attribute if there are many

property width: float | None
property x: float | None
property y: float | None
class vsdx.shapes.Cell(xml: Element, shape: Shape)

Represents a Cell element in a vsdx xml file

property formula: str | None
property func: str | None
property name: str | None
property value: str | None
class vsdx.shapes.DataProperty(*, xml: Element, shape: Shape)

Represents a single Data Property item associated with a Shape object

A property a shape inherits from its master is handed out marked inherited. Setting value on one materialises an override row on the instance rather than writing to the master page’s XML.

get_attribute(name: str, attrib: str) str | None

Get the attribute value of the cell element

inherited_by(shape: Shape) DataProperty

This property as an instance of the master sees it, marked inherited.

The copy reads the master’s Row element, so label, type and prompt are already resolved; the first write to value calls make_local(), which gives shape a row of its own.

label: str | None
name: str | None
prompt: str | None
remove_attribute(name: str, attrib: str) bool

Remove the attribute from the cell element

set_attribute(name: str, attrib: str, value: str) bool

Set the attribute value of the cell element

shape: Shape
sort_key: str | None
property value: str | None

Get the value of the data property, or None when it has none.

Reading is free of side effects: it neither creates the Value cell nor tidies a No Formula formula, so inspecting a document does not change the bytes it saves.

value_type: str | None
xml: Element

Connect and Container

class vsdx.connectors.Connect(xml: Element | None = None, page: Page | None = None)

Connect class to represent a connection between two Shape objects

property connector_shape: Shape | None
property connector_shape_id: str | None
property shape: Shape | None
property shape_id: str | None

Connection records also expose the raw from_id, to_id, from_rel and to_rel values from the Visio Connect element. from_id is the connector shape ID. to_id is the connected shape ID. The relationship fields identify the source and target cells, such as BeginX, EndX, PinX or Connections.X1.

vsdx.connectors.Connect.from_id: str | None
vsdx.connectors.Connect.to_id: str | None
vsdx.connectors.Connect.from_rel: str | None
vsdx.connectors.Connect.to_rel: str | None
class vsdx.containers.Container(page: Page)

Read/write view over a CFF (swimlane) diagram structure.

Membership is geometric: lane_of() maps a shape to the lane whose vertical band contains its centre, mirroring Visio’s own containment behaviour. There are no membership cells to write.

add_shape_to_lane(shape: Shape, lane: Shape) None

Assign a shape to a lane by geometry: set the shape’s PinY to the lane’s centre, keeping its PinX. Mirrors Visio’s own behaviour when a shape is dragged into a lane; membership stays geometric.

add_swimlane(label: str | None = None) Shape

Add a lane above the current top lane by cloning it and shifting one lane pitch. The Swimlane List and CFF Container grow to match.

Returns:

the new lane Shape

property container_shape: Shape | None
static find(page: Page) Container | None

Return a Container for the page, or None if this is not a CFF page.

static lane_band(lane: Shape) tuple[float, float]

(bottom, top) Y band of a lane, from its centre and height.

static lane_heading(lane: Shape) Shape | None

The lane’s heading sub-shape (child carrying MasterShape).

lane_of(shape: Shape) Shape | None

The lane whose band contains the shape’s centre, or None.

property lanes: list[Shape]

Lane shapes in visual order, top lane first.

members(lane: Shape) list[Shape]

Flowchart shapes whose centre lies in the lane’s band.

set_lane_label(lane: Shape, label: str) None

Set a lane’s heading label (visHeadingText row + heading text).

property swimlane_list: Shape | None

Package limits

class vsdx.vsdxfile.PackageLimits(max_members: int = 512, max_member_size: int = 67108864, max_total_uncompressed: int = 268435456, max_ratio: float = 100.0)

Conservative caps applied while loading a package from disk.

The defaults suit documents from unknown sources: a hostile or accidental archive is rejected well before it can exhaust process memory. Even at the caps the loader materialises at most max_total_uncompressed bytes (256 MiB by default); callers loading larger trusted documents should raise the caps explicitly via VisioFile(filename, limits=PackageLimits(...)) or a JSON file passed as limits_path with the same keys.

classmethod from_json_file(path: str) PackageLimits

Load limits from a JSON object; unusable files are PackageLimitError, not surprises.

max_member_size: int = 67108864
max_members: int = 512
max_ratio: float = 100.0
max_total_uncompressed: int = 268435456
class vsdx.vsdxfile.PackageLimitError(reason: str, message: str)

A package violated a load limit: size, member count, ratio, names or duplicates.

reason is a stable slug (member_size, total_size, member_count, compression_ratio, duplicate_member, member_name, limits_file) so callers can branch by failure mode.