Under Construction This library is currently being developed with heavy assistance from AI and is primarily an experimental project. Use with caution.
A Node.js library to strict-type interact with Visio (.vsdx) files.
Built using specific schema-level abstractions to handle the complex internal structure of Visio documents (ShapeSheets, Pages, Masters).
.vsdx files (zipped XML).VisioPage, VisioShape, and VisioConnect objects.Cells, Rows, and Sections directly.VisioDocument → Page → Shape is the full public surface; XML and OPC internals stay encapsulated.Letter, A4, …) or raw inches; rotate between portrait and landscape.doc.getMetadata() / doc.setMetadata().Top, Right, etc.) and connect to them precisely using fromPort/toPort on any connector API.doc.createStyle() and apply them to shapes at creation time (styleId) or post-creation (shape.applyStyle()).doc.addColor() and look them up by index or hex value with doc.getColors() / doc.getColorIndex().page.getLayers(); delete a layer with layer.delete(), rename with layer.rename(), and read layer.visible / layer.locked state.shape.getChildren(), check shape.isGroup, and read shape.type.page.setDrawingScale()), read it back (page.getDrawingScale()), or reset to 1:1 (page.clearDrawingScale()). Supports all common imperial and metric units.Feature gaps are being tracked in FEATURES.md.
npm install ts-visio
The VisioDocument class is the main entry point.
import { VisioDocument } from 'ts-visio';
// Create a new blank document
const doc = await VisioDocument.create();
// OR Load an existing file
// const doc = await VisioDocument.load('diagram.vsdx');
Access pages through the pages property.
const page = doc.pages[0];
console.log(`Editing Page: ${page.name}`);
Add new shapes or modify existing ones without dealing with XML.
// Add a new rectangle shape
const shape = await page.addShape({
text: "Hello World",
x: 1,
y: 1,
width: 3,
height: 1,
fillColor: "#ff0000", // Hex fill color
fontColor: "#ffffff",
bold: true,
fontSize: 14, // Points
fontFamily: "Segoe UI",
horzAlign: "center", // "left" | "center" | "right" | "justify"
verticalAlign: "middle" // "top" | "middle" | "bottom"
});
// Modify text
await shape.setText("Updated Text");
console.log(`Shape ID: ${shape.id}`);
Create Group shapes and add children to them.
// 1. Create a Group container
const group = await page.addShape({
text: "Group",
x: 5,
y: 5,
width: 4,
height: 4,
type: 'Group'
});
// 2. Add child shape directly to the group
// Coordinates are relative to the Group's bottom-left corner
await page.addShape({
text: "Child",
x: 1, // Relative X
y: 1, // Relative Y
width: 1,
height: 1
}, group.id);
Easily position shapes relative to each other.
const shape1 = await page.addShape({ text: "Step 1", x: 2, y: 5, width: 2, height: 1 });
const shape2 = await page.addShape({ text: "Step 2", x: 0, y: 0, width: 2, height: 1 }); // X,Y ignored if we place it next
// Place Shape 2 to the right of Shape 1 with a 1-inch gap
await shape2.placeRightOf(shape1, { gap: 1 });
// Chain placement
const shape3 = await page.addShape({ text: "Step 3", x: 0, y: 0, width: 2, height: 1 });
await shape3.placeRightOf(shape2);
// Vertical stacking
const shape4 = await page.addShape({ text: "Below", x: 0, y: 0, width: 2, height: 1 });
await shape4.placeBelow(shape1, { gap: 0.5 });
Combine creation, styling, and connection in a clean syntax.
const shape1 = await page.addShape({ text: "Start", x: 2, y: 4, width: 2, height: 1 });
const shape2 = await page.addShape({ text: "End", x: 6, y: 4, width: 2, height: 1 });
// Fluent Connection
await shape1.connectTo(shape2);
// Chaining Styles & Connections
await shape1.setStyle({ fillColor: '#00FF00' })
.connectTo(shape2);
Use specific arrowheads (Crow's Foot, etc.)
import { ArrowHeads } from 'ts-visio';
await page.connectShapes(shape1, shape2, ArrowHeads.One, ArrowHeads.CrowsFoot);
// OR
await shape1.connectTo(shape2, ArrowHeads.One, ArrowHeads.CrowsFoot);
Create a compound stacked shape for database tables.
const tableShape = await page.addTable(
5,
5,
"Users",
["ID: int", "Name: varchar", "Email: varchar"]
);
console.log(tableShape.id); // Access ID
For ER diagrams, use the SchemaDiagram wrapper for simplified semantics.
import { VisioDocument, SchemaDiagram } from 'ts-visio';
const doc = await VisioDocument.create();
const page = doc.pages[0];
const schema = new SchemaDiagram(page);
// Add Tables
const users = await schema.addTable('Users', ['id', 'email'], 0, 0);
const posts = await schema.addTable('Posts', ['id', 'user_id'], 5, 0);
// Add Relations (1:N maps to Crow's Foot arrow)
await schema.addRelation(users, posts, '1:N');
Save the modified document back to disk.
await doc.save('updated_diagram.vsdx');
Create a master from scratch — define a reusable shape with a built-in geometry, then stamp instances onto any page:
const doc = await VisioDocument.create();
const page = doc.pages[0];
// Define masters (geometries: 'rectangle' | 'ellipse' | 'diamond' |
// 'rounded-rectangle' | 'triangle' | 'parallelogram')
const boxMaster = doc.createMaster('Box');
const processMaster = doc.createMaster('Process', 'ellipse');
const decisionMaster = doc.createMaster('Decision', 'diamond');
// Stamp instances — geometry + styling come from the master definition
await page.addShape({ text: 'Start', x: 1, y: 1, width: 2, height: 1, masterId: processMaster.id });
await page.addShape({ text: 'Step 1', x: 4, y: 1, width: 2, height: 1, masterId: boxMaster.id });
await page.addShape({ text: 'Branch?', x: 7, y: 1, width: 2, height: 1, masterId: decisionMaster.id });
Import masters from a .vssx stencil file — bring in an entire stencil and use any of its masters:
const doc = await VisioDocument.create();
// Path to a .vssx file, or pass a Buffer / ArrayBuffer directly
const stencilMasters = await doc.importMastersFromStencil('./Network_Shapes.vssx');
const router = stencilMasters.find(m => m.name === 'Router')!;
await doc.pages[0].addShape({
text: 'Router 1', x: 2, y: 2, width: 1, height: 1,
masterId: router.id,
});
List masters already in the document — useful after loading a .vsdx that was created in Visio:
const doc = await VisioDocument.load('existing_diagram.vsdx');
const masters = doc.getMasters();
// [{ id: '1', name: 'Box', nameU: 'Box', xmlPath: 'visio/masters/master1.xml' }, ...]
// Find and reuse a master by name
const routerMaster = masters.find(m => m.name === 'Router');
if (routerMaster) {
await doc.pages[0].addShape({ text: 'Router 2', x: 5, y: 5, width: 1, height: 1, masterId: routerMaster.id });
}
Create multiple pages and organize your diagram across them.
const doc = await VisioDocument.create();
// Page 1 (Default)
const page1 = doc.pages[0];
await page1.addShape({ text: "Home", x: 1, y: 1 });
// Page 2 (New)
const page2 = await doc.addPage("Architecture Diagram");
await page2.addShape({ text: "Server", x: 4, y: 4 });
await doc.save("multipage.vsdx");
Add metadata to shapes, which is crucial for "Smart" diagrams like visual databases or inventories.
const shape = await page.addShape({ text: "Server DB-01", x: 2, y: 2 });
// Synchronous Fluent API
shape.addData("IP", { value: "192.168.1.10", label: "IP Address" })
.addData("Status", { value: "Active" })
.addData("LastRefreshed", { value: new Date() }) // Auto-serialized as Visio Date
.addData("ConfigID", { value: 1024, hidden: true }); // Invisible to user
Embed PNG or JPEG images directly into the diagram.
import * as fs from 'fs';
const buffer = fs.readFileSync('logo.png');
const page = doc.pages[0];
// Add image at (x=2, y=5) with width=3, height=2
await page.addImage(buffer, 'logo.png', 2, 5, 3, 2);
Create visual grouping containers (Classic Visio style).
// Create a Container
const container = await page.addContainer({
text: "Network Zone",
x: 1, y: 1,
width: 6, height: 4
});
// Add shapes "inside" the container area
// (Visio treats them as members if they are spatially within)
const shape = await page.addShape({ text: "Server A", x: 2, y: 2, width: 1, height: 1 });
// Explicitly link the shape to the container (so they move together)
await container.addMember(shape);
Create ordered lists that automatically stack items either vertically (Tables) or horizontally (Timelines).
// Vertical List (e.g. Database Table)
const list = await page.addList({
text: "Users",
x: 1, y: 10,
width: 3, height: 1
}, 'vertical'); // 'vertical' or 'horizontal'
// Items stack automatically
await list.addListItem(item1);
await list.addListItem(item2);
Add internal links (drill-downs) or external links (web references). These appear in the right-click menu in Visio.
// 1. External URL
await shape.toUrl('https://jira.com/123', 'Open Ticket');
// 2. Internal Page Link
const detailPage = await doc.addPage('Details');
await shape.toPage(detailPage, 'Go to Details');
// 3. Chainable
await shape.toUrl('https://google.com')
.toPage(detailPage);
Organize complex diagrams with layers. Control visibility and locking programmatically, and read layers back from loaded files.
// 1. Define Layers
const wireframe = await page.addLayer('Wireframe');
const annotations = await page.addLayer('Annotations', { visible: false });
// 2. Assign Shapes to Layers
await shape.addToLayer(wireframe);
await noteBox.addToLayer(annotations);
// 3. Toggle Visibility
await annotations.hide(); // Hide for presentation
await annotations.show(); // Show again
// 4. Lock Layer
await wireframe.setLocked(true);
// 5. Read all layers back (works on loaded files too)
const layers = page.getLayers();
// [ { name: 'Wireframe', index: 0, visible: true, locked: true },
// { name: 'Annotations', index: 1, visible: true, locked: false } ]
for (const layer of layers) {
console.log(layer.name, layer.visible, layer.locked);
}
// 6. Rename a layer
await wireframe.rename('Structural');
// 7. Delete a layer (cleans up shape assignments automatically)
await annotations.delete();
Create structured Swimlane diagrams involving Pools and Lanes.
// 1. Create a Pool (Vertical List)
const pool = await page.addSwimlanePool({
text: "User Registration",
x: 5, y: 5, width: 10, height: 6
});
// 2. Create Lanes (Containers)
const lane1 = await page.addSwimlaneLane({ text: "Client", width: 10, height: 2 });
const lane2 = await page.addSwimlaneLane({ text: "Server", width: 10, height: 2 });
// 3. Add Lanes to Pool (Order matters)
await pool.addListItem(lane1);
await pool.addListItem(lane2);
// 4. Group Shapes into Lanes
// This binds their movement so they stay inside the lane
await lane1.addMember(startShape);
await lane2.addMember(serverShape);
Rotate, flip, and resize shapes using a fluent API.
const shape = await page.addShape({ text: "Widget", x: 3, y: 3, width: 2, height: 1 });
// Rotate 45 degrees (clockwise)
await shape.rotate(45);
console.log(shape.angle); // 45
// Mirror horizontally or vertically
await shape.flipX();
await shape.flipY(false); // un-flip
// Resize (keeps the pin point centred)
await shape.resize(4, 2);
console.log(shape.width, shape.height); // 4, 2
// Chainable
await shape.rotate(90).then(s => s.resize(3, 1));
Remove shapes or entire pages. Orphaned connectors and relationships are cleaned up automatically.
// Delete a shape
await shape.delete();
// Delete a page (removes page file, rels, and all back-page references)
await doc.deletePage(page2);
Find shapes and pages without iterating manually.
// Find a shape by its numeric ID (searches nested groups too)
const target = await page.getShapeById("42");
// Find all shapes matching a predicate
const servers = await page.findShapes(s => s.text.startsWith("Server"));
// Look up a page by name (exact, case-sensitive)
const detailPage = doc.getPage("Architecture Diagram");
Retrieve custom properties, hyperlinks, and layer assignments that were previously written.
// Custom properties (shape data)
const props = shape.getProperties();
console.log(props["IP"].value); // "192.168.1.10"
console.log(props["Port"].type); // VisioPropType.Number
// Hyperlinks
const links = shape.getHyperlinks();
// [ { address: "https://example.com", description: "Docs", newWindow: false } ]
// Layer indices
const indices = shape.getLayerIndices(); // e.g. [0, 2]
Use the geometry prop on addShape() to create common flowchart primitives without touching XML.
// Ellipse / circle
await page.addShape({ text: "Start", x: 1, y: 5, width: 2, height: 2, geometry: 'ellipse' });
// Decision diamond
await page.addShape({ text: "Yes?", x: 4, y: 5, width: 2, height: 2, geometry: 'diamond' });
// Rounded rectangle (optional corner radius in inches)
await page.addShape({ text: "Process", x: 7, y: 5, width: 3, height: 2,
geometry: 'rounded-rectangle', cornerRadius: 0.2 });
// Right-pointing triangle
await page.addShape({ text: "Extract", x: 1, y: 2, width: 2, height: 2, geometry: 'triangle' });
// Parallelogram (Data / IO shape)
await page.addShape({ text: "Input", x: 4, y: 2, width: 3, height: 1.5, geometry: 'parallelogram' });
Supported values: 'rectangle' (default), 'ellipse', 'diamond', 'rounded-rectangle', 'triangle', 'parallelogram'.
Control line appearance and routing algorithm on any connector.
import { ArrowHeads } from 'ts-visio';
// Styled connector with crow's foot arrow and custom line
await shape1.connectTo(shape2, ArrowHeads.One, ArrowHeads.CrowsFoot, {
lineColor: '#cc0000', // Hex stroke color
lineWeight: 1.5, // Stroke width in points
linePattern: 2, // 1=solid, 2=dash, 3=dot, 4=dash-dot
routing: 'curved', // 'straight' | 'orthogonal' (default) | 'curved'
});
// Via page.connectShapes()
await page.connectShapes(a, b, undefined, undefined, { routing: 'straight' });
Control the canvas dimensions using named paper sizes or raw inch values.
import { PageSizes } from 'ts-visio';
// Use a named paper size (portrait by default)
page.setNamedSize('A4'); // 8.268" × 11.693"
page.setNamedSize('Letter', 'landscape'); // 11" × 8.5"
// Set arbitrary dimensions in inches
page.setSize(13.33, 7.5); // 13.33" × 7.5" widescreen
// Rotate the existing canvas without changing the paper size
page.setOrientation('landscape'); // swaps width and height if needed
page.setOrientation('portrait');
// Read current dimensions
console.log(page.pageWidth); // e.g. 11
console.log(page.pageHeight); // e.g. 8.5
console.log(page.orientation); // 'landscape' | 'portrait'
// All size methods are chainable
page.setNamedSize('A3').setOrientation('landscape');
Available named sizes in PageSizes: Letter, Legal, Tabloid, A3, A4, A5.
Set and read document-level properties that appear in Visio's Document Properties dialog.
// Write metadata (only supplied fields are changed)
doc.setMetadata({
title: 'Network Topology',
author: 'Alice',
description: 'Data-centre interconnect diagram',
keywords: 'network datacenter cloud',
lastModifiedBy: 'CI pipeline',
company: 'ACME Corp',
manager: 'Bob',
created: new Date('2025-01-01T00:00:00Z'),
modified: new Date(),
});
// Read back all metadata fields
const meta = doc.getMetadata();
console.log(meta.title); // 'Network Topology'
console.log(meta.author); // 'Alice'
console.log(meta.company); // 'ACME Corp'
console.log(meta.created); // Date object
Fields map to OPC parts: title, author, description, keywords, lastModifiedBy,
created, modified → docProps/core.xml; company, manager → docProps/app.xml.
Italic, underline, strikethrough, paragraph spacing, and text block margins are available both at shape-creation time and via shape.setStyle().
// At creation time
const shape = await page.addShape({
text: 'Important',
x: 2, y: 3, width: 3, height: 1,
bold: true,
italic: true,
underline: true,
strikethrough: false,
// Paragraph spacing (in points)
spaceBefore: 6,
spaceAfter: 6,
lineSpacing: 1.5, // 1.5× line height
// Text block margins (in inches)
textMarginTop: 0.1,
textMarginBottom: 0.1,
textMarginLeft: 0.1,
textMarginRight: 0.1,
});
// Post-creation via setStyle()
await shape.setStyle({
italic: true,
lineSpacing: 2.0, // double spacing
textMarginTop: 0.15,
textMarginLeft: 0.05,
});
lineSpacing is a multiplier: 1.0 = single, 1.5 = 1.5×, 2.0 = double.
spaceBefore / spaceAfter are in points. Text margins are in inches.
Define specific ports on shapes and connect to them precisely instead of relying on edge-intersection.
import { StandardConnectionPoints } from 'ts-visio';
// 1. Add connection points at shape-creation time
const nodeA = await page.addShape({
text: 'A', x: 2, y: 3, width: 2, height: 1,
connectionPoints: StandardConnectionPoints.cardinal, // Top, Right, Bottom, Left
});
const nodeB = await page.addShape({
text: 'B', x: 6, y: 3, width: 2, height: 1,
connectionPoints: StandardConnectionPoints.cardinal,
});
// 2. Connect using named ports (Right of A → Left of B)
await page.connectShapes(nodeA, nodeB, undefined, undefined, undefined,
{ name: 'Right' }, // fromPort
{ name: 'Left' }, // toPort
);
// 3. Fluent Shape API
await nodeA.connectTo(nodeB, undefined, undefined, undefined,
{ name: 'Right' }, { name: 'Left' });
// 4. Add a point to an existing shape by index
const ix = nodeA.addConnectionPoint({
name: 'Center',
xFraction: 0.5, yFraction: 0.5,
type: 'both',
});
// 5. Connect by zero-based index instead of name
await page.connectShapes(nodeA, nodeB, undefined, undefined, undefined,
{ index: 1 }, // Right (IX=1 in cardinal preset)
{ index: 3 }, // Left (IX=3 in cardinal preset)
);
// 6. 'center' target (default behaviour) works alongside named ports
await page.connectShapes(nodeA, nodeB, undefined, undefined, undefined,
'center', { name: 'Left' });
StandardConnectionPoints.cardinal — 4 points: Top, Right, Bottom, Left.
StandardConnectionPoints.full — 8 points: cardinal + TopLeft, TopRight, BottomRight, BottomLeft.
Unknown port names fall back gracefully to edge-intersection routing without throwing.
Define reusable named styles at the document level and apply them to shapes so they inherit line, fill, and text properties without repeating the same values on every shape.
// 1. Create a document-level style
const headerStyle = doc.createStyle('Header', {
fillColor: '#4472C4', // Blue fill
lineColor: '#2F5597', // Dark-blue border
lineWeight: 1.5, // 1.5 pt stroke
fontColor: '#FFFFFF', // White text
fontSize: 14, // 14 pt
bold: true,
fontFamily: 'Calibri',
horzAlign: 'center',
verticalAlign: 'middle',
});
const bodyStyle = doc.createStyle('Body', {
fillColor: '#DEEAF1',
lineColor: '#2F5597',
fontSize: 11,
horzAlign: 'left',
});
// 2. Apply at shape-creation time
const title = await page.addShape({
text: 'System Architecture',
x: 1, y: 8, width: 8, height: 1,
styleId: headerStyle.id, // sets LineStyle, FillStyle, TextStyle
});
// Fine-grained: apply only the fill from one style, line from another
const hybrid = await page.addShape({
text: 'Hybrid',
x: 1, y: 6, width: 3, height: 1,
fillStyleId: headerStyle.id,
lineStyleId: bodyStyle.id,
});
// 3. Apply (or change) style post-creation
const box = await page.addShape({ text: 'Server', x: 4, y: 4, width: 2, height: 1 });
box.applyStyle(bodyStyle.id); // all three categories
box.applyStyle(headerStyle.id, 'fill'); // fill only — leaves line & text unchanged
box.applyStyle(headerStyle.id, 'text'); // text only
// 4. List all styles in the document
const styles = doc.getStyles();
// [ { id: 0, name: 'No Style' }, { id: 1, name: 'Normal' }, { id: 2, name: 'Header' }, … ]
StyleProps supports: fillColor, lineColor, lineWeight (pt), linePattern, fontColor, fontSize (pt), bold, italic, underline, strikethrough, fontFamily, horzAlign, verticalAlign, spaceBefore, spaceAfter, lineSpacing, textMarginTop/Bottom/Left/Right (in).
Local shape properties always override inherited stylesheet values.
The alignment and style types are exported for use in typed consumers:
import type { HorzAlign, VertAlign, ShapeStyle } from 'ts-visio';
const style: ShapeStyle = { horzAlign: 'center', verticalAlign: 'middle', bold: true };
const align: HorzAlign = 'justify'; // 'left' | 'center' | 'right' | 'justify'
const valign: VertAlign = 'bottom'; // 'top' | 'middle' | 'bottom'
Enumerate connectors on a page — including those loaded from an existing .vsdx file — and inspect or delete them.
import { VisioDocument } from 'ts-visio';
const doc = await VisioDocument.load(buffer); // or VisioDocument.create()
const page = doc.pages[0];
// Read all connector shapes
const connectors = page.getConnectors();
for (const conn of connectors) {
console.log(`Connector ${conn.id}: ${conn.fromShapeId} → ${conn.toShapeId}`);
console.log(' fromPort:', conn.fromPort); // 'center' | { name } | { index }
console.log(' toPort:', conn.toPort);
console.log(' style:', conn.style); // lineColor, lineWeight, linePattern, routing
console.log(' arrows:', conn.beginArrow, conn.endArrow);
}
// Delete a specific connector
await connectors[0].delete();
// After delete the connector is gone but the shapes remain
console.log(page.getConnectors().length); // 0
console.log(page.getShapes().length); // shapes still exist
Register colors in the document's indexed color table (<Colors> in document.xml). Colors are identified by an integer IX that you can reference anywhere a hex color is accepted.
// Register colors — returns the integer index (IX)
const blueIx = doc.addColor('#4472C4'); // → 2 (first user color)
const redIx = doc.addColor('#FF0000'); // → 3
const greenIx = doc.addColor('#00B050'); // → 4
// De-duplication: adding the same color returns the existing index
doc.addColor('#4472C4'); // → 2 (no duplicate created)
// Shorthand, missing #, and case variations are all normalised
doc.addColor('#FFF'); // same as #FFFFFF → returns 1 (built-in white)
doc.addColor('4472c4'); // same as #4472C4 → returns 2
// Read the full palette (sorted by index)
const palette = doc.getColors();
// [
// { index: 0, rgb: '#000000' }, // built-in black
// { index: 1, rgb: '#FFFFFF' }, // built-in white
// { index: 2, rgb: '#4472C4' },
// { index: 3, rgb: '#FF0000' },
// { index: 4, rgb: '#00B050' },
// ]
// Look up an index by hex value
doc.getColorIndex('#4472C4'); // → 2
doc.getColorIndex('#123456'); // → undefined (not registered)
Built-in colors: IX 0 = #000000 (black), IX 1 = #FFFFFF (white). User colors start at IX 2 and increment sequentially.
shape.getChildren())Access nested child shapes of a group without touching XML. Only direct children are returned — call getChildren() recursively to walk a deeper tree.
// 1. Create a group with children
const group = await page.addShape({
text: 'Container', x: 5, y: 5, width: 6, height: 6, type: 'Group'
});
const childA = await page.addShape({ text: 'A', x: 1, y: 1, width: 2, height: 1 }, group.id);
const childB = await page.addShape({ text: 'B', x: 1, y: 3, width: 2, height: 1 }, group.id);
// 2. Check if a shape is a group
console.log(group.isGroup); // true
console.log(childA.isGroup); // false
console.log(group.type); // 'Group'
// 3. Get direct children
const children = group.getChildren();
// → [Shape('A'), Shape('B')]
for (const child of children) {
console.log(child.text, child.id);
}
// 4. Recursively walk a nested tree
function walk(shape, depth = 0) {
console.log(' '.repeat(depth * 2) + shape.text);
for (const child of shape.getChildren()) {
walk(child, depth + 1);
}
}
walk(group);
// 5. Works on shapes loaded from an existing .vsdx file
const doc2 = await VisioDocument.load('existing.vsdx');
const shapes = doc2.pages[0].getShapes();
const groups = shapes.filter(s => s.isGroup);
for (const g of groups) {
console.log(`Group "${g.text}" has ${g.getChildren().length} children`);
}
getChildren() returns [] for non-group shapes. Children are full Shape instances — all existing methods (setStyle(), getProperties(), delete(), etc.) work on them.
Map page coordinates to real-world units. One pageScale pageUnit on the paper equals drawingScale drawingUnit in reality. Visio uses this to display rulers, grids, and shape dimensions in real-world terms.
import type { LengthUnit, DrawingScaleInfo } from 'ts-visio';
// 1 inch on paper = 10 feet in the real world (architectural)
page.setDrawingScale(1, 'in', 10, 'ft');
// 1:100 metric (1 cm on paper = 100 cm = 1 m in reality)
page.setDrawingScale(1, 'cm', 100, 'cm');
// Engineering: 1 inch = 20 feet
page.setDrawingScale(1, 'in', 20, 'ft');
// Read the current scale back
const scale: DrawingScaleInfo | null = page.getDrawingScale();
if (scale) {
console.log(`${scale.pageScale} ${scale.pageUnit} = ${scale.drawingScale} ${scale.drawingUnit}`);
// → "1 in = 10 ft"
}
// Returns null when no custom scale is set
const freshPage = doc.pages[0];
console.log(freshPage.getDrawingScale()); // null
// Reset to 1:1 (no scale)
page.clearDrawingScale();
console.log(page.getDrawingScale()); // null
// All methods are chainable
page.setDrawingScale(1, 'mm', 1000, 'mm')
.setNamedSize('A1'); // combine with page size changes
Supported LengthUnit values:
'in' (inches), 'ft' (feet), 'yd' (yards), 'mi' (miles)'mm', 'cm', 'm', 'km'The drawing scale does not affect the internal coordinate system — shape positions and sizes are still specified in page-inches. The scale is purely a display/annotation mapping applied by Visio's ruler and grid.
Check out the examples directory for complete scripts.
This project uses Vitest for testing.
npm install
npm test