ts-visio 1.7.0 → 1.9.0

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.
package/README.md CHANGED
@@ -664,6 +664,34 @@ const styles = doc.getStyles();
664
664
  `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).
665
665
  Local shape properties always override inherited stylesheet values.
666
666
 
667
+ #### 30. Reading Connectors Back
668
+ Enumerate connectors on a page — including those loaded from an existing `.vsdx` file — and inspect or delete them.
669
+
670
+ ```typescript
671
+ import { VisioDocument } from 'ts-visio';
672
+
673
+ const doc = await VisioDocument.load(buffer); // or VisioDocument.create()
674
+ const page = doc.pages[0];
675
+
676
+ // Read all connector shapes
677
+ const connectors = page.getConnectors();
678
+
679
+ for (const conn of connectors) {
680
+ console.log(`Connector ${conn.id}: ${conn.fromShapeId} → ${conn.toShapeId}`);
681
+ console.log(' fromPort:', conn.fromPort); // 'center' | { name } | { index }
682
+ console.log(' toPort:', conn.toPort);
683
+ console.log(' style:', conn.style); // lineColor, lineWeight, linePattern, routing
684
+ console.log(' arrows:', conn.beginArrow, conn.endArrow);
685
+ }
686
+
687
+ // Delete a specific connector
688
+ await connectors[0].delete();
689
+
690
+ // After delete the connector is gone but the shapes remain
691
+ console.log(page.getConnectors().length); // 0
692
+ console.log(page.getShapes().length); // shapes still exist
693
+ ```
694
+
667
695
  ---
668
696
 
669
697
  ## Examples
@@ -0,0 +1,53 @@
1
+ import { ConnectorStyle, ConnectionTarget } from './types/VisioTypes';
2
+ import { ShapeModifier } from './ShapeModifier';
3
+ /**
4
+ * Raw data extracted from the page XML for a single connector shape.
5
+ * Returned by `ShapeReader.readConnectors()` and wrapped by the `Connector` class.
6
+ */
7
+ export interface ConnectorData {
8
+ /** Visio shape ID of the connector (1D shape). */
9
+ id: string;
10
+ /** ID of the shape at the connector's begin-point (BeginX). */
11
+ fromShapeId: string;
12
+ /** ID of the shape at the connector's end-point (EndX). */
13
+ toShapeId: string;
14
+ /** Connection point used on the from-shape. */
15
+ fromPort: ConnectionTarget;
16
+ /** Connection point used on the to-shape. */
17
+ toPort: ConnectionTarget;
18
+ /** Line style extracted from the connector's Line section. */
19
+ style: ConnectorStyle;
20
+ /** Begin-arrow head value (Visio ArrowHeads integer string). */
21
+ beginArrow: string;
22
+ /** End-arrow head value (Visio ArrowHeads integer string). */
23
+ endArrow: string;
24
+ }
25
+ /**
26
+ * A read/delete wrapper around a connector shape found on a page.
27
+ * Obtain instances via `page.getConnectors()`.
28
+ */
29
+ export declare class Connector {
30
+ private readonly pageId;
31
+ private readonly modifier;
32
+ /** Visio shape ID of this connector. */
33
+ readonly id: string;
34
+ /** ID of the shape this connector starts from. */
35
+ readonly fromShapeId: string;
36
+ /** ID of the shape this connector ends at. */
37
+ readonly toShapeId: string;
38
+ /** Connection point used on the from-shape ('center', `{ name }`, or `{ index }`). */
39
+ readonly fromPort: ConnectionTarget;
40
+ /** Connection point used on the to-shape ('center', `{ name }`, or `{ index }`). */
41
+ readonly toPort: ConnectionTarget;
42
+ /** Line style (color, weight, pattern, routing) for this connector. */
43
+ readonly style: ConnectorStyle;
44
+ /** Begin-arrow head value string (e.g. `'0'` = none, `'1'` = standard). */
45
+ readonly beginArrow: string;
46
+ /** End-arrow head value string (e.g. `'0'` = none, `'1'` = standard). */
47
+ readonly endArrow: string;
48
+ constructor(data: ConnectorData, pageId: string, modifier: ShapeModifier);
49
+ /**
50
+ * Delete this connector from the page (removes the shape and its Connect entries).
51
+ */
52
+ delete(): Promise<void>;
53
+ }
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Connector = void 0;
4
+ /**
5
+ * A read/delete wrapper around a connector shape found on a page.
6
+ * Obtain instances via `page.getConnectors()`.
7
+ */
8
+ class Connector {
9
+ constructor(data, pageId, modifier) {
10
+ this.pageId = pageId;
11
+ this.modifier = modifier;
12
+ this.id = data.id;
13
+ this.fromShapeId = data.fromShapeId;
14
+ this.toShapeId = data.toShapeId;
15
+ this.fromPort = data.fromPort;
16
+ this.toPort = data.toPort;
17
+ this.style = data.style;
18
+ this.beginArrow = data.beginArrow;
19
+ this.endArrow = data.endArrow;
20
+ }
21
+ /**
22
+ * Delete this connector from the page (removes the shape and its Connect entries).
23
+ */
24
+ async delete() {
25
+ await this.modifier.deleteShape(this.pageId, this.id);
26
+ }
27
+ }
28
+ exports.Connector = Connector;
package/dist/Page.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { VisioPage, ConnectorStyle, PageOrientation, PageSizeName, ConnectionTarget } from './types/VisioTypes';
2
+ import { Connector } from './Connector';
2
3
  import { VisioPackage } from './VisioPackage';
3
4
  import { ShapeModifier } from './ShapeModifier';
4
5
  import { NewShapeProps } from './types/VisioTypes';
@@ -50,6 +51,12 @@ export declare class Page {
50
51
  */
51
52
  findShapes(predicate: (shape: Shape) => boolean): Shape[];
52
53
  addShape(props: NewShapeProps, parentId?: string): Promise<Shape>;
54
+ /**
55
+ * Return all connector shapes on the page.
56
+ * Each `Connector` exposes the from/to shape IDs, port targets, line style, arrows,
57
+ * and a `delete()` method to remove the connector.
58
+ */
59
+ getConnectors(): Connector[];
53
60
  connectShapes(fromShape: Shape, toShape: Shape, beginArrow?: string, endArrow?: string, style?: ConnectorStyle, fromPort?: ConnectionTarget, toPort?: ConnectionTarget): Promise<void>;
54
61
  addImage(data: Buffer, name: string, x: number, y: number, width: number, height: number): Promise<Shape>;
55
62
  addContainer(props: NewShapeProps): Promise<Shape>;
package/dist/Page.js CHANGED
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Page = void 0;
4
4
  const VisioTypes_1 = require("./types/VisioTypes");
5
+ const Connector_1 = require("./Connector");
5
6
  const ShapeReader_1 = require("./ShapeReader");
6
7
  const ShapeModifier_1 = require("./ShapeModifier");
7
8
  const Shape_1 = require("./Shape");
@@ -124,6 +125,22 @@ class Page {
124
125
  });
125
126
  return new Shape_1.Shape(internalStub, this.id, this.pkg, this.modifier);
126
127
  }
128
+ /**
129
+ * Return all connector shapes on the page.
130
+ * Each `Connector` exposes the from/to shape IDs, port targets, line style, arrows,
131
+ * and a `delete()` method to remove the connector.
132
+ */
133
+ getConnectors() {
134
+ const reader = new ShapeReader_1.ShapeReader(this.pkg);
135
+ try {
136
+ const data = reader.readConnectors(this.pagePath);
137
+ return data.map(d => new Connector_1.Connector(d, this.id, this.modifier));
138
+ }
139
+ catch (e) {
140
+ console.warn(`Could not read connectors for page ${this.id}:`, e);
141
+ return [];
142
+ }
143
+ }
127
144
  async connectShapes(fromShape, toShape, beginArrow, endArrow, style, fromPort, toPort) {
128
145
  await this.modifier.addConnector(this.id, fromShape.id, toShape.id, beginArrow, endArrow, style, fromPort, toPort);
129
146
  }
@@ -131,6 +131,12 @@ export declare class ShapeModifier {
131
131
  }
132
132
  export interface ShapeStyle {
133
133
  fillColor?: string;
134
+ /** Border/stroke colour as a CSS hex string (e.g. `'#cc0000'`). */
135
+ lineColor?: string;
136
+ /** Stroke weight in **points**. Stored internally as inches (pt / 72). */
137
+ lineWeight?: number;
138
+ /** Line pattern. 0 = none, 1 = solid (default), 2 = dash, 3 = dot, 4 = dash-dot. */
139
+ linePattern?: number;
134
140
  fontColor?: string;
135
141
  bold?: boolean;
136
142
  /** Italic text. */
@@ -400,6 +400,18 @@ class ShapeModifier {
400
400
  shape.Section = shape.Section.filter((s) => s['@_N'] !== 'Fill');
401
401
  shape.Section.push((0, StyleHelpers_1.createFillSection)(style.fillColor));
402
402
  }
403
+ // Update/Add Line
404
+ const hasLineProps = style.lineColor !== undefined
405
+ || style.lineWeight !== undefined
406
+ || style.linePattern !== undefined;
407
+ if (hasLineProps) {
408
+ shape.Section = shape.Section.filter((s) => s['@_N'] !== 'Line');
409
+ shape.Section.push((0, StyleHelpers_1.createLineSection)({
410
+ color: style.lineColor,
411
+ weight: style.lineWeight !== undefined ? (style.lineWeight / 72).toString() : undefined,
412
+ pattern: style.linePattern !== undefined ? style.linePattern.toString() : undefined,
413
+ }));
414
+ }
403
415
  // Update/Add Character (Font/Text Style)
404
416
  const hasCharProps = style.fontColor !== undefined
405
417
  || style.bold !== undefined
@@ -1,5 +1,6 @@
1
1
  import { VisioPackage } from './VisioPackage';
2
2
  import { VisioShape } from './types/VisioTypes';
3
+ import { ConnectorData } from './Connector';
3
4
  export declare class ShapeReader {
4
5
  private pkg;
5
6
  private parser;
@@ -15,6 +16,14 @@ export declare class ShapeReader {
15
16
  * Returns undefined if not found.
16
17
  */
17
18
  readShapeById(path: string, shapeId: string): VisioShape | undefined;
19
+ /**
20
+ * Read all connector shapes from a page XML file.
21
+ * A shape is considered a connector if it has `ObjType=2` or a `BeginX` cell,
22
+ * and is referenced in the page's `<Connects>` section.
23
+ */
24
+ readConnectors(path: string): ConnectorData[];
25
+ /** Decode a Visio ToPart integer string to a ConnectionTarget. */
26
+ private decodeToPart;
18
27
  private gatherShapes;
19
28
  private findShapeById;
20
29
  private parseShape;
@@ -63,6 +63,125 @@ class ShapeReader {
63
63
  return undefined;
64
64
  return this.findShapeById((0, VisioParsers_1.asArray)(shapesData), shapeId);
65
65
  }
66
+ /**
67
+ * Read all connector shapes from a page XML file.
68
+ * A shape is considered a connector if it has `ObjType=2` or a `BeginX` cell,
69
+ * and is referenced in the page's `<Connects>` section.
70
+ */
71
+ readConnectors(path) {
72
+ let content;
73
+ try {
74
+ content = this.pkg.getFileText(path);
75
+ }
76
+ catch {
77
+ return [];
78
+ }
79
+ const parsed = this.parser.parse(content);
80
+ const shapesData = parsed.PageContents?.Shapes?.Shape;
81
+ if (!shapesData)
82
+ return [];
83
+ // Build a flat map of all shapes by ID (including nested)
84
+ const shapeMap = new Map();
85
+ const collectShapes = (list) => {
86
+ for (const s of list) {
87
+ shapeMap.set(s['@_ID'], s);
88
+ if (s.Shapes?.Shape)
89
+ collectShapes((0, VisioParsers_1.asArray)(s.Shapes.Shape));
90
+ }
91
+ };
92
+ collectShapes((0, VisioParsers_1.asArray)(shapesData));
93
+ // Identify connector shapes by ObjType=2 or presence of BeginX cell
94
+ const connectorShapes = [];
95
+ for (const [, shape] of shapeMap) {
96
+ const cells = (0, VisioParsers_1.parseCells)(shape);
97
+ if (cells['ObjType']?.V === '2' || cells['BeginX'] !== undefined) {
98
+ connectorShapes.push(shape);
99
+ }
100
+ }
101
+ if (connectorShapes.length === 0)
102
+ return [];
103
+ // Group <Connect> elements by connector ID (FromSheet)
104
+ const connectsRaw = parsed.PageContents?.Connects?.Connect;
105
+ const connects = (0, VisioParsers_1.asArray)(connectsRaw);
106
+ const connectsByConnector = new Map();
107
+ for (const c of connects) {
108
+ const fromSheet = c['@_FromSheet'];
109
+ const fromCell = c['@_FromCell'];
110
+ if (!connectsByConnector.has(fromSheet)) {
111
+ connectsByConnector.set(fromSheet, {});
112
+ }
113
+ const entry = connectsByConnector.get(fromSheet);
114
+ if (fromCell === 'BeginX')
115
+ entry.beginConnect = c;
116
+ else if (fromCell === 'EndX')
117
+ entry.endConnect = c;
118
+ }
119
+ const ROUTING_BY_VALUE = {
120
+ '1': 'orthogonal',
121
+ '2': 'straight',
122
+ '16': 'curved',
123
+ };
124
+ const result = [];
125
+ for (const connShape of connectorShapes) {
126
+ const connId = connShape['@_ID'];
127
+ const entry = connectsByConnector.get(connId);
128
+ if (!entry?.beginConnect || !entry?.endConnect)
129
+ continue;
130
+ const { beginConnect, endConnect } = entry;
131
+ const fromShapeId = beginConnect['@_ToSheet'];
132
+ const toShapeId = endConnect['@_ToSheet'];
133
+ const fromPort = this.decodeToPart(beginConnect['@_ToPart']);
134
+ const toPort = this.decodeToPart(endConnect['@_ToPart']);
135
+ // Extract line style from the connector's Line section
136
+ const sections = (0, VisioParsers_1.asArray)(connShape.Section);
137
+ let lineColor;
138
+ let lineWeight;
139
+ let linePattern;
140
+ for (const sec of sections) {
141
+ if (sec['@_N'] === 'Line') {
142
+ const lineCells = (0, VisioParsers_1.parseCells)(sec);
143
+ if (lineCells['LineColor']?.V)
144
+ lineColor = lineCells['LineColor'].V;
145
+ if (lineCells['LineWeight']?.V)
146
+ lineWeight = parseFloat(lineCells['LineWeight'].V) * 72; // in→pt
147
+ if (lineCells['LinePattern']?.V)
148
+ linePattern = parseInt(lineCells['LinePattern'].V, 10);
149
+ }
150
+ }
151
+ const cells = (0, VisioParsers_1.parseCells)(connShape);
152
+ const routeStyleVal = cells['ShapeRouteStyle']?.V;
153
+ const routing = routeStyleVal ? ROUTING_BY_VALUE[routeStyleVal] : undefined;
154
+ const style = {};
155
+ if (lineColor !== undefined)
156
+ style.lineColor = lineColor;
157
+ if (lineWeight !== undefined)
158
+ style.lineWeight = lineWeight;
159
+ if (linePattern !== undefined)
160
+ style.linePattern = linePattern;
161
+ if (routing !== undefined)
162
+ style.routing = routing;
163
+ result.push({
164
+ id: connId,
165
+ fromShapeId,
166
+ toShapeId,
167
+ fromPort,
168
+ toPort,
169
+ style,
170
+ beginArrow: cells['BeginArrow']?.V ?? '0',
171
+ endArrow: cells['EndArrow']?.V ?? '0',
172
+ });
173
+ }
174
+ return result;
175
+ }
176
+ /** Decode a Visio ToPart integer string to a ConnectionTarget. */
177
+ decodeToPart(toPart) {
178
+ const part = toPart !== undefined ? parseInt(toPart, 10) : 3;
179
+ if (isNaN(part) || part === 3)
180
+ return 'center';
181
+ if (part >= 100)
182
+ return { index: part - 100 };
183
+ return 'center';
184
+ }
66
185
  gatherShapes(rawShapes, result) {
67
186
  for (const s of rawShapes) {
68
187
  result.push(this.parseShape(s));
package/dist/index.d.ts CHANGED
@@ -6,6 +6,8 @@ export { ShapeModifier } from './ShapeModifier';
6
6
  export { VisioDocument } from './VisioDocument';
7
7
  export { Page } from './Page';
8
8
  export { Shape } from './Shape';
9
+ export { Connector } from './Connector';
10
+ export type { ConnectorData } from './Connector';
9
11
  export type { ShapeData, ShapeHyperlink } from './Shape';
10
12
  export { Layer } from './Layer';
11
13
  export { SchemaDiagram } from './SchemaDiagram';
package/dist/index.js CHANGED
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.VisioValidator = exports.SchemaDiagram = exports.Layer = exports.Shape = exports.Page = exports.VisioDocument = exports.ShapeModifier = exports.ShapeReader = exports.PageManager = exports.VisioPackage = void 0;
17
+ exports.VisioValidator = exports.SchemaDiagram = exports.Layer = exports.Connector = exports.Shape = exports.Page = exports.VisioDocument = exports.ShapeModifier = exports.ShapeReader = exports.PageManager = exports.VisioPackage = void 0;
18
18
  var VisioPackage_1 = require("./VisioPackage");
19
19
  Object.defineProperty(exports, "VisioPackage", { enumerable: true, get: function () { return VisioPackage_1.VisioPackage; } });
20
20
  var PageManager_1 = require("./core/PageManager");
@@ -29,6 +29,8 @@ var Page_1 = require("./Page");
29
29
  Object.defineProperty(exports, "Page", { enumerable: true, get: function () { return Page_1.Page; } });
30
30
  var Shape_1 = require("./Shape");
31
31
  Object.defineProperty(exports, "Shape", { enumerable: true, get: function () { return Shape_1.Shape; } });
32
+ var Connector_1 = require("./Connector");
33
+ Object.defineProperty(exports, "Connector", { enumerable: true, get: function () { return Connector_1.Connector; } });
32
34
  var Layer_1 = require("./Layer");
33
35
  Object.defineProperty(exports, "Layer", { enumerable: true, get: function () { return Layer_1.Layer; } });
34
36
  var SchemaDiagram_1 = require("./SchemaDiagram");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-visio",
3
- "version": "1.7.0",
3
+ "version": "1.9.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "scripts": {