ts-visio 1.6.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
@@ -26,6 +26,7 @@ Built using specific schema-level abstractions to handle the complex internal st
26
26
  - **Page Size & Orientation**: Set canvas dimensions with named sizes (`Letter`, `A4`, …) or raw inches; rotate between portrait and landscape.
27
27
  - **Document Metadata**: Read and write document properties (title, author, description, keywords, company, dates) via `doc.getMetadata()` / `doc.setMetadata()`.
28
28
  - **Named Connection Points**: Define specific ports on shapes (`Top`, `Right`, etc.) and connect to them precisely using `fromPort`/`toPort` on any connector API.
29
+ - **StyleSheets**: Create document-level named styles with fill, line, and text properties via `doc.createStyle()` and apply them to shapes at creation time (`styleId`) or post-creation (`shape.applyStyle()`).
29
30
 
30
31
  Feature gaps are being tracked in [FEATURES.md](./FEATURES.md).
31
32
 
@@ -610,6 +611,89 @@ Unknown port names fall back gracefully to edge-intersection routing without thr
610
611
 
611
612
  ---
612
613
 
614
+ #### 29. StyleSheets (Document-Level Styles)
615
+ 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.
616
+
617
+ ```typescript
618
+ // 1. Create a document-level style
619
+ const headerStyle = doc.createStyle('Header', {
620
+ fillColor: '#4472C4', // Blue fill
621
+ lineColor: '#2F5597', // Dark-blue border
622
+ lineWeight: 1.5, // 1.5 pt stroke
623
+ fontColor: '#FFFFFF', // White text
624
+ fontSize: 14, // 14 pt
625
+ bold: true,
626
+ fontFamily: 'Calibri',
627
+ horzAlign: 'center',
628
+ verticalAlign: 'middle',
629
+ });
630
+
631
+ const bodyStyle = doc.createStyle('Body', {
632
+ fillColor: '#DEEAF1',
633
+ lineColor: '#2F5597',
634
+ fontSize: 11,
635
+ horzAlign: 'left',
636
+ });
637
+
638
+ // 2. Apply at shape-creation time
639
+ const title = await page.addShape({
640
+ text: 'System Architecture',
641
+ x: 1, y: 8, width: 8, height: 1,
642
+ styleId: headerStyle.id, // sets LineStyle, FillStyle, TextStyle
643
+ });
644
+
645
+ // Fine-grained: apply only the fill from one style, line from another
646
+ const hybrid = await page.addShape({
647
+ text: 'Hybrid',
648
+ x: 1, y: 6, width: 3, height: 1,
649
+ fillStyleId: headerStyle.id,
650
+ lineStyleId: bodyStyle.id,
651
+ });
652
+
653
+ // 3. Apply (or change) style post-creation
654
+ const box = await page.addShape({ text: 'Server', x: 4, y: 4, width: 2, height: 1 });
655
+ box.applyStyle(bodyStyle.id); // all three categories
656
+ box.applyStyle(headerStyle.id, 'fill'); // fill only — leaves line & text unchanged
657
+ box.applyStyle(headerStyle.id, 'text'); // text only
658
+
659
+ // 4. List all styles in the document
660
+ const styles = doc.getStyles();
661
+ // [ { id: 0, name: 'No Style' }, { id: 1, name: 'Normal' }, { id: 2, name: 'Header' }, … ]
662
+ ```
663
+
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
+ Local shape properties always override inherited stylesheet values.
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
+
695
+ ---
696
+
613
697
  ## Examples
614
698
 
615
699
  Check out the [examples](./examples) directory for complete scripts.
@@ -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
  }
package/dist/Shape.d.ts CHANGED
@@ -36,6 +36,15 @@ export declare class Shape {
36
36
  * Returns the zero-based index (IX) of the newly added point.
37
37
  */
38
38
  addConnectionPoint(point: ConnectionPointDef): number;
39
+ /**
40
+ * Apply a document-level stylesheet to this shape.
41
+ * Create styles via `doc.createStyle()` and pass the returned `id`.
42
+ *
43
+ * @param styleId The stylesheet ID to apply.
44
+ * @param which `'all'` (default) applies to line, fill, and text;
45
+ * `'line'`, `'fill'`, or `'text'` applies to only that category.
46
+ */
47
+ applyStyle(styleId: number, which?: 'all' | 'line' | 'fill' | 'text'): this;
39
48
  setStyle(style: ShapeStyle): Promise<this>;
40
49
  placeRightOf(targetShape: Shape, options?: {
41
50
  gap: number;
package/dist/Shape.js CHANGED
@@ -53,6 +53,18 @@ class Shape {
53
53
  addConnectionPoint(point) {
54
54
  return this.modifier.addConnectionPoint(this.pageId, this.id, point);
55
55
  }
56
+ /**
57
+ * Apply a document-level stylesheet to this shape.
58
+ * Create styles via `doc.createStyle()` and pass the returned `id`.
59
+ *
60
+ * @param styleId The stylesheet ID to apply.
61
+ * @param which `'all'` (default) applies to line, fill, and text;
62
+ * `'line'`, `'fill'`, or `'text'` applies to only that category.
63
+ */
64
+ applyStyle(styleId, which = 'all') {
65
+ this.modifier.applyStyle(this.pageId, this.id, styleId, which);
66
+ return this;
67
+ }
56
68
  async setStyle(style) {
57
69
  await this.modifier.updateShapeStyle(this.pageId, this.id, style);
58
70
  return this;
@@ -37,6 +37,13 @@ export declare class ShapeModifier {
37
37
  * Returns the zero-based IX (row index) of the newly added point.
38
38
  */
39
39
  addConnectionPoint(pageId: string, shapeId: string, point: ConnectionPointDef): number;
40
+ /**
41
+ * Apply a document-level stylesheet to an existing shape by setting its
42
+ * `LineStyle`, `FillStyle`, and/or `TextStyle` attributes.
43
+ *
44
+ * @param which `'all'` (default) sets all three; `'line'`, `'fill'`, or `'text'` sets only one.
45
+ */
46
+ applyStyle(pageId: string, shapeId: string, styleId: number, which?: 'all' | 'line' | 'fill' | 'text'): void;
40
47
  addShape(pageId: string, props: NewShapeProps, parentId?: string): Promise<string>;
41
48
  deleteShape(pageId: string, shapeId: string): Promise<void>;
42
49
  private removeShapeFromTree;
@@ -124,6 +131,12 @@ export declare class ShapeModifier {
124
131
  }
125
132
  export interface ShapeStyle {
126
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;
127
140
  fontColor?: string;
128
141
  bold?: boolean;
129
142
  /** Italic text. */
@@ -244,6 +244,27 @@ class ShapeModifier {
244
244
  this.saveParsed(pageId, parsed);
245
245
  return ix;
246
246
  }
247
+ /**
248
+ * Apply a document-level stylesheet to an existing shape by setting its
249
+ * `LineStyle`, `FillStyle`, and/or `TextStyle` attributes.
250
+ *
251
+ * @param which `'all'` (default) sets all three; `'line'`, `'fill'`, or `'text'` sets only one.
252
+ */
253
+ applyStyle(pageId, shapeId, styleId, which = 'all') {
254
+ const parsed = this.getParsed(pageId);
255
+ const shapeMap = this.getShapeMap(parsed);
256
+ const shape = shapeMap.get(shapeId);
257
+ if (!shape)
258
+ throw new Error(`Shape ${shapeId} not found on page ${pageId}`);
259
+ const sid = styleId.toString();
260
+ if (which === 'all' || which === 'line')
261
+ shape['@_LineStyle'] = sid;
262
+ if (which === 'all' || which === 'fill')
263
+ shape['@_FillStyle'] = sid;
264
+ if (which === 'all' || which === 'text')
265
+ shape['@_TextStyle'] = sid;
266
+ this.saveParsed(pageId, parsed);
267
+ }
247
268
  async addShape(pageId, props, parentId) {
248
269
  const parsed = this.getParsed(pageId);
249
270
  // Ensure Shapes container exists
@@ -379,6 +400,18 @@ class ShapeModifier {
379
400
  shape.Section = shape.Section.filter((s) => s['@_N'] !== 'Fill');
380
401
  shape.Section.push((0, StyleHelpers_1.createFillSection)(style.fillColor));
381
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
+ }
382
415
  // Update/Add Character (Font/Text Style)
383
416
  const hasCharProps = style.fontColor !== undefined
384
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));
@@ -1,11 +1,12 @@
1
1
  import { Page } from './Page';
2
- import { DocumentMetadata } from './types/VisioTypes';
2
+ import { DocumentMetadata, StyleProps, StyleRecord } from './types/VisioTypes';
3
3
  export declare class VisioDocument {
4
4
  private pkg;
5
5
  private pageManager;
6
6
  private _pageCache;
7
7
  private mediaManager;
8
8
  private metadataManager;
9
+ private styleSheetManager;
9
10
  private constructor();
10
11
  static create(): Promise<VisioDocument>;
11
12
  static load(pathOrBuffer: string | Buffer | ArrayBuffer | Uint8Array): Promise<VisioDocument>;
@@ -42,5 +43,18 @@ export declare class VisioDocument {
42
43
  * doc.setMetadata({ title: 'My Diagram', author: 'Alice', company: 'ACME' });
43
44
  */
44
45
  setMetadata(props: Partial<DocumentMetadata>): void;
46
+ /**
47
+ * Create a named document-level stylesheet and return its record.
48
+ * The returned `id` can be passed to `addShape({ styleId })` or `shape.applyStyle()`.
49
+ *
50
+ * @example
51
+ * const s = doc.createStyle('Header', { fillColor: '#4472C4', fontColor: '#ffffff', bold: true });
52
+ * const shape = await page.addShape({ text: 'Title', x: 1, y: 1, width: 3, height: 1, styleId: s.id });
53
+ */
54
+ createStyle(name: string, props?: StyleProps): StyleRecord;
55
+ /**
56
+ * Return all stylesheets defined in the document (including built-in styles).
57
+ */
58
+ getStyles(): StyleRecord[];
45
59
  save(filename?: string): Promise<Buffer>;
46
60
  }
@@ -39,6 +39,7 @@ const PageManager_1 = require("./core/PageManager");
39
39
  const Page_1 = require("./Page");
40
40
  const MediaManager_1 = require("./core/MediaManager");
41
41
  const MetadataManager_1 = require("./core/MetadataManager");
42
+ const StyleSheetManager_1 = require("./core/StyleSheetManager");
42
43
  class VisioDocument {
43
44
  constructor(pkg) {
44
45
  this.pkg = pkg;
@@ -46,6 +47,7 @@ class VisioDocument {
46
47
  this.pageManager = new PageManager_1.PageManager(pkg);
47
48
  this.mediaManager = new MediaManager_1.MediaManager(pkg);
48
49
  this.metadataManager = new MetadataManager_1.MetadataManager(pkg);
50
+ this.styleSheetManager = new StyleSheetManager_1.StyleSheetManager(pkg);
49
51
  }
50
52
  static async create() {
51
53
  const pkg = await VisioPackage_1.VisioPackage.create();
@@ -150,6 +152,23 @@ class VisioDocument {
150
152
  setMetadata(props) {
151
153
  this.metadataManager.setMetadata(props);
152
154
  }
155
+ /**
156
+ * Create a named document-level stylesheet and return its record.
157
+ * The returned `id` can be passed to `addShape({ styleId })` or `shape.applyStyle()`.
158
+ *
159
+ * @example
160
+ * const s = doc.createStyle('Header', { fillColor: '#4472C4', fontColor: '#ffffff', bold: true });
161
+ * const shape = await page.addShape({ text: 'Title', x: 1, y: 1, width: 3, height: 1, styleId: s.id });
162
+ */
163
+ createStyle(name, props = {}) {
164
+ return this.styleSheetManager.createStyle(name, props);
165
+ }
166
+ /**
167
+ * Return all stylesheets defined in the document (including built-in styles).
168
+ */
169
+ getStyles() {
170
+ return this.styleSheetManager.getStyles();
171
+ }
153
172
  async save(filename) {
154
173
  return this.pkg.save(filename);
155
174
  }
@@ -0,0 +1,37 @@
1
+ import { VisioPackage } from '../VisioPackage';
2
+ import { StyleProps, StyleRecord } from '../types/VisioTypes';
3
+ /**
4
+ * Manages document-level stylesheets stored in `visio/document.xml`.
5
+ *
6
+ * Stylesheets define reusable sets of line, fill, and text properties that
7
+ * shapes inherit via the `LineStyle`, `FillStyle`, and `TextStyle` attributes.
8
+ *
9
+ * The minimal document template ships with:
10
+ * - ID 0 "No Style" — the inheritance root (all shapes fall back here)
11
+ * - ID 1 "Normal" — empty style inheriting everything from ID 0
12
+ *
13
+ * User-created styles receive IDs starting at 2.
14
+ */
15
+ export declare class StyleSheetManager {
16
+ private pkg;
17
+ private parser;
18
+ private builder;
19
+ constructor(pkg: VisioPackage);
20
+ /**
21
+ * Create a new named document-level stylesheet and return its record.
22
+ * The returned `id` can be passed to `addShape({ styleId })` or `shape.applyStyle()`.
23
+ */
24
+ createStyle(name: string, props?: StyleProps): StyleRecord;
25
+ /**
26
+ * Return all stylesheets defined in the document (including the built-in ones).
27
+ */
28
+ getStyles(): StyleRecord[];
29
+ private getParsedDoc;
30
+ private saveParsedDoc;
31
+ /** Ensure `<StyleSheets>` contains at least Style 0 and Style 1. */
32
+ private ensureStyleSheets;
33
+ private nextId;
34
+ /** Normalise a fast-xml-parser value that may be undefined, a single object, or an array. */
35
+ private normalizeArray;
36
+ private buildStyleSheetXml;
37
+ }
@@ -0,0 +1,216 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.StyleSheetManager = void 0;
4
+ const StyleHelpers_1 = require("../utils/StyleHelpers");
5
+ const XmlHelper_1 = require("../utils/XmlHelper");
6
+ const HORZ_ALIGN = {
7
+ left: '0', center: '1', right: '2', justify: '3',
8
+ };
9
+ const VERT_ALIGN = {
10
+ top: '0', middle: '1', bottom: '2',
11
+ };
12
+ /**
13
+ * Manages document-level stylesheets stored in `visio/document.xml`.
14
+ *
15
+ * Stylesheets define reusable sets of line, fill, and text properties that
16
+ * shapes inherit via the `LineStyle`, `FillStyle`, and `TextStyle` attributes.
17
+ *
18
+ * The minimal document template ships with:
19
+ * - ID 0 "No Style" — the inheritance root (all shapes fall back here)
20
+ * - ID 1 "Normal" — empty style inheriting everything from ID 0
21
+ *
22
+ * User-created styles receive IDs starting at 2.
23
+ */
24
+ class StyleSheetManager {
25
+ constructor(pkg) {
26
+ this.pkg = pkg;
27
+ this.parser = (0, XmlHelper_1.createXmlParser)();
28
+ this.builder = (0, XmlHelper_1.createXmlBuilder)();
29
+ }
30
+ // ── Public API ────────────────────────────────────────────────────────────
31
+ /**
32
+ * Create a new named document-level stylesheet and return its record.
33
+ * The returned `id` can be passed to `addShape({ styleId })` or `shape.applyStyle()`.
34
+ */
35
+ createStyle(name, props = {}) {
36
+ const parsed = this.getParsedDoc();
37
+ const doc = parsed['VisioDocument'];
38
+ this.ensureStyleSheets(doc);
39
+ const styleSheets = doc['StyleSheets'];
40
+ const existing = this.normalizeArray(styleSheets['StyleSheet']);
41
+ const newId = this.nextId(existing);
42
+ existing.push(this.buildStyleSheetXml(newId, name, props));
43
+ styleSheets['StyleSheet'] = existing;
44
+ this.saveParsedDoc(parsed);
45
+ return { id: newId, name };
46
+ }
47
+ /**
48
+ * Return all stylesheets defined in the document (including the built-in ones).
49
+ */
50
+ getStyles() {
51
+ const parsed = this.getParsedDoc();
52
+ const doc = parsed['VisioDocument'];
53
+ const styleSheets = doc?.['StyleSheets'];
54
+ if (!styleSheets)
55
+ return [];
56
+ return this.normalizeArray(styleSheets['StyleSheet']).map((s) => ({
57
+ id: parseInt(s['@_ID'], 10),
58
+ name: s['@_Name'] ?? s['@_NameU'] ?? `Style ${s['@_ID']}`,
59
+ }));
60
+ }
61
+ // ── Private helpers ───────────────────────────────────────────────────────
62
+ getParsedDoc() {
63
+ const xml = this.pkg.getFileText('visio/document.xml');
64
+ return this.parser.parse(xml);
65
+ }
66
+ saveParsedDoc(parsed) {
67
+ this.pkg.updateFile('visio/document.xml', (0, XmlHelper_1.buildXml)(this.builder, parsed));
68
+ }
69
+ /** Ensure `<StyleSheets>` contains at least Style 0 and Style 1. */
70
+ ensureStyleSheets(doc) {
71
+ if (!doc['StyleSheets']) {
72
+ doc['StyleSheets'] = {};
73
+ }
74
+ const ss = doc['StyleSheets'];
75
+ const existing = this.normalizeArray(ss['StyleSheet']);
76
+ const hasStyle0 = existing.some((s) => s['@_ID'] === '0');
77
+ if (!hasStyle0) {
78
+ existing.unshift({
79
+ '@_ID': '0',
80
+ '@_Name': 'No Style',
81
+ '@_NameU': 'No Style',
82
+ '@_IsCustomName': '0',
83
+ '@_IsCustomNameU': '0',
84
+ Cell: [
85
+ { '@_N': 'EnableLineProps', '@_V': '1' },
86
+ { '@_N': 'EnableFillProps', '@_V': '1' },
87
+ { '@_N': 'EnableTextProps', '@_V': '1' },
88
+ { '@_N': 'HideForApply', '@_V': '0' },
89
+ ],
90
+ });
91
+ }
92
+ const hasStyle1 = existing.some((s) => s['@_ID'] === '1');
93
+ if (!hasStyle1) {
94
+ existing.push({
95
+ '@_ID': '1',
96
+ '@_Name': 'Normal',
97
+ '@_NameU': 'Normal',
98
+ '@_IsCustomName': '0',
99
+ '@_IsCustomNameU': '0',
100
+ '@_LineStyle': '0',
101
+ '@_FillStyle': '0',
102
+ '@_TextStyle': '0',
103
+ });
104
+ }
105
+ ss['StyleSheet'] = existing;
106
+ }
107
+ nextId(existing) {
108
+ if (existing.length === 0)
109
+ return 2;
110
+ const max = existing.reduce((m, s) => {
111
+ const id = parseInt(s['@_ID'], 10);
112
+ return isNaN(id) ? m : Math.max(m, id);
113
+ }, 0);
114
+ return Math.max(max + 1, 2); // user styles always start at ≥ 2
115
+ }
116
+ /** Normalise a fast-xml-parser value that may be undefined, a single object, or an array. */
117
+ normalizeArray(val) {
118
+ if (!val)
119
+ return [];
120
+ return Array.isArray(val) ? val : [val];
121
+ }
122
+ buildStyleSheetXml(id, name, props) {
123
+ const sheet = {
124
+ '@_ID': id.toString(),
125
+ '@_Name': name,
126
+ '@_NameU': name,
127
+ '@_IsCustomName': '1',
128
+ '@_IsCustomNameU': '1',
129
+ '@_LineStyle': (props.parentLineStyleId ?? 0).toString(),
130
+ '@_FillStyle': (props.parentFillStyleId ?? 0).toString(),
131
+ '@_TextStyle': (props.parentTextStyleId ?? 0).toString(),
132
+ };
133
+ const cells = [];
134
+ const sections = [];
135
+ // ── Fill ────────────────────────────────────────────────────────────
136
+ if (props.fillColor !== undefined) {
137
+ cells.push({ '@_N': 'FillForegnd', '@_V': props.fillColor, '@_F': (0, StyleHelpers_1.hexToRgb)(props.fillColor) });
138
+ cells.push({ '@_N': 'FillPattern', '@_V': '1' });
139
+ }
140
+ // ── Line ────────────────────────────────────────────────────────────
141
+ if (props.lineColor !== undefined) {
142
+ cells.push({ '@_N': 'LineColor', '@_V': props.lineColor, '@_F': (0, StyleHelpers_1.hexToRgb)(props.lineColor) });
143
+ }
144
+ if (props.lineWeight !== undefined) {
145
+ cells.push({ '@_N': 'LineWeight', '@_V': (props.lineWeight / 72).toString(), '@_U': 'PT' });
146
+ }
147
+ if (props.linePattern !== undefined) {
148
+ cells.push({ '@_N': 'LinePattern', '@_V': props.linePattern.toString() });
149
+ }
150
+ // ── TextBlock ───────────────────────────────────────────────────────
151
+ if (props.verticalAlign !== undefined) {
152
+ cells.push({ '@_N': 'VerticalAlign', '@_V': VERT_ALIGN[props.verticalAlign] });
153
+ }
154
+ if (props.textMarginTop !== undefined)
155
+ cells.push({ '@_N': 'TopMargin', '@_V': props.textMarginTop.toString(), '@_U': 'IN' });
156
+ if (props.textMarginBottom !== undefined)
157
+ cells.push({ '@_N': 'BottomMargin', '@_V': props.textMarginBottom.toString(), '@_U': 'IN' });
158
+ if (props.textMarginLeft !== undefined)
159
+ cells.push({ '@_N': 'LeftMargin', '@_V': props.textMarginLeft.toString(), '@_U': 'IN' });
160
+ if (props.textMarginRight !== undefined)
161
+ cells.push({ '@_N': 'RightMargin', '@_V': props.textMarginRight.toString(), '@_U': 'IN' });
162
+ // ── Character section (no @_T on rows — stylesheet convention) ─────
163
+ const charCells = [];
164
+ const colorVal = props.fontColor ?? '#000000';
165
+ if (props.fontColor !== undefined) {
166
+ charCells.push({ '@_N': 'Color', '@_V': colorVal, '@_F': (0, StyleHelpers_1.hexToRgb)(colorVal) });
167
+ }
168
+ let styleVal = 0;
169
+ if (props.bold)
170
+ styleVal |= 1;
171
+ if (props.italic)
172
+ styleVal |= 2;
173
+ if (props.underline)
174
+ styleVal |= 4;
175
+ if (props.strikethrough)
176
+ styleVal |= 8;
177
+ if (styleVal > 0 || props.bold !== undefined || props.italic !== undefined
178
+ || props.underline !== undefined || props.strikethrough !== undefined) {
179
+ charCells.push({ '@_N': 'Style', '@_V': styleVal.toString() });
180
+ }
181
+ if (props.fontSize !== undefined) {
182
+ charCells.push({ '@_N': 'Size', '@_V': (props.fontSize / 72).toString(), '@_U': 'PT' });
183
+ }
184
+ if (props.fontFamily !== undefined) {
185
+ charCells.push({ '@_N': 'Font', '@_V': '0', '@_F': `FONT("${props.fontFamily}")` });
186
+ }
187
+ if (charCells.length > 0) {
188
+ sections.push({
189
+ '@_N': 'Character',
190
+ Row: { '@_IX': '0', Cell: charCells },
191
+ });
192
+ }
193
+ // ── Paragraph section ───────────────────────────────────────────────
194
+ const paraCells = [];
195
+ if (props.horzAlign !== undefined)
196
+ paraCells.push({ '@_N': 'HorzAlign', '@_V': HORZ_ALIGN[props.horzAlign] });
197
+ if (props.spaceBefore !== undefined)
198
+ paraCells.push({ '@_N': 'SpBefore', '@_V': (props.spaceBefore / 72).toString(), '@_U': 'PT' });
199
+ if (props.spaceAfter !== undefined)
200
+ paraCells.push({ '@_N': 'SpAfter', '@_V': (props.spaceAfter / 72).toString(), '@_U': 'PT' });
201
+ if (props.lineSpacing !== undefined)
202
+ paraCells.push({ '@_N': 'SpLine', '@_V': (-props.lineSpacing).toString() });
203
+ if (paraCells.length > 0) {
204
+ sections.push({
205
+ '@_N': 'Paragraph',
206
+ Row: { '@_IX': '0', Cell: paraCells },
207
+ });
208
+ }
209
+ if (cells.length > 0)
210
+ sheet.Cell = cells;
211
+ if (sections.length > 0)
212
+ sheet.Section = sections;
213
+ return sheet;
214
+ }
215
+ }
216
+ exports.StyleSheetManager = StyleSheetManager;
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");
@@ -29,6 +29,20 @@ class ShapeBuilder {
29
29
  if (props.masterId) {
30
30
  shape['@_Master'] = props.masterId;
31
31
  }
32
+ // Apply document-level stylesheet references
33
+ if (props.styleId !== undefined) {
34
+ shape['@_LineStyle'] = props.styleId.toString();
35
+ shape['@_FillStyle'] = props.styleId.toString();
36
+ shape['@_TextStyle'] = props.styleId.toString();
37
+ }
38
+ else {
39
+ if (props.lineStyleId !== undefined)
40
+ shape['@_LineStyle'] = props.lineStyleId.toString();
41
+ if (props.fillStyleId !== undefined)
42
+ shape['@_FillStyle'] = props.fillStyleId.toString();
43
+ if (props.textStyleId !== undefined)
44
+ shape['@_TextStyle'] = props.textStyleId.toString();
45
+ }
32
46
  // Add Styles
33
47
  if (props.fillColor) {
34
48
  shape.Section.push((0, StyleHelpers_1.createFillSection)(props.fillColor));
@@ -27,7 +27,15 @@ exports.DOCUMENT_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
27
27
  <ColorEntry IX="1" RGB="#FFFFFF"/>
28
28
  </Colors>
29
29
  <FaceNames/>
30
- <StyleSheets/>
30
+ <StyleSheets>
31
+ <StyleSheet ID="0" Name="No Style" NameU="No Style" IsCustomName="0" IsCustomNameU="0">
32
+ <Cell N="EnableLineProps" V="1"/>
33
+ <Cell N="EnableFillProps" V="1"/>
34
+ <Cell N="EnableTextProps" V="1"/>
35
+ <Cell N="HideForApply" V="0"/>
36
+ </StyleSheet>
37
+ <StyleSheet ID="1" Name="Normal" NameU="Normal" IsCustomName="0" IsCustomNameU="0" LineStyle="0" FillStyle="0" TextStyle="0"/>
38
+ </StyleSheets>
31
39
  </VisioDocument>`;
32
40
  exports.DOCUMENT_RELS_XML = `<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
33
41
  <Relationships xmlns="${VisioConstants_1.XML_NAMESPACES.RELATIONSHIPS}">
@@ -146,6 +146,65 @@ export interface ConnectorStyle {
146
146
  }
147
147
  /** Non-rectangular geometry variants supported by ShapeBuilder. */
148
148
  export type ShapeGeometry = 'rectangle' | 'ellipse' | 'diamond' | 'rounded-rectangle' | 'triangle' | 'parallelogram';
149
+ /** A reference to a document-level stylesheet, returned by `doc.createStyle()`. */
150
+ export interface StyleRecord {
151
+ /** Zero-based integer ID used as `LineStyle` / `FillStyle` / `TextStyle` on shapes. */
152
+ id: number;
153
+ /** Human-readable name given to the style. */
154
+ name: string;
155
+ }
156
+ /**
157
+ * Visual properties for a document-level stylesheet created via `doc.createStyle()`.
158
+ * All fields are optional — omitted properties are inherited from the parent style (default: Style 0 "No Style").
159
+ */
160
+ export interface StyleProps {
161
+ /** Parent style for line property inheritance. Defaults to 0 ("No Style"). */
162
+ parentLineStyleId?: number;
163
+ /** Parent style for fill property inheritance. Defaults to 0 ("No Style"). */
164
+ parentFillStyleId?: number;
165
+ /** Parent style for text property inheritance. Defaults to 0 ("No Style"). */
166
+ parentTextStyleId?: number;
167
+ /** Stroke colour as a CSS hex string (e.g. `'#cc0000'`). */
168
+ lineColor?: string;
169
+ /** Stroke weight in **points**. Stored internally as inches (pt / 72). */
170
+ lineWeight?: number;
171
+ /** Line pattern. 0 = none, 1 = solid, 2 = dash, 3 = dot, 4 = dash-dot. */
172
+ linePattern?: number;
173
+ /** Background fill colour as a CSS hex string. */
174
+ fillColor?: string;
175
+ /** Text colour as a CSS hex string. */
176
+ fontColor?: string;
177
+ /** Font size in **points**. */
178
+ fontSize?: number;
179
+ /** Bold text. */
180
+ bold?: boolean;
181
+ /** Italic text. */
182
+ italic?: boolean;
183
+ /** Underline text. */
184
+ underline?: boolean;
185
+ /** Strikethrough text. */
186
+ strikethrough?: boolean;
187
+ /** Font family name (e.g. `'Calibri'`). */
188
+ fontFamily?: string;
189
+ /** Horizontal text alignment within the paragraph. */
190
+ horzAlign?: 'left' | 'center' | 'right' | 'justify';
191
+ /** Space before each paragraph in **points**. */
192
+ spaceBefore?: number;
193
+ /** Space after each paragraph in **points**. */
194
+ spaceAfter?: number;
195
+ /** Line-height multiplier (1.0 = single, 1.5 = 1.5×, 2.0 = double). */
196
+ lineSpacing?: number;
197
+ /** Vertical text alignment within the shape. */
198
+ verticalAlign?: 'top' | 'middle' | 'bottom';
199
+ /** Top text margin in inches. */
200
+ textMarginTop?: number;
201
+ /** Bottom text margin in inches. */
202
+ textMarginBottom?: number;
203
+ /** Left text margin in inches. */
204
+ textMarginLeft?: number;
205
+ /** Right text margin in inches. */
206
+ textMarginRight?: number;
207
+ }
149
208
  /** Whether a connection point accepts incoming glue, outgoing glue, or both. */
150
209
  export type ConnectionPointType = 'inward' | 'outward' | 'both';
151
210
  /**
@@ -240,4 +299,16 @@ export interface NewShapeProps {
240
299
  * or `StandardConnectionPoints.full` for eight points.
241
300
  */
242
301
  connectionPoints?: ConnectionPointDef[];
302
+ /**
303
+ * Apply a document-level stylesheet to this shape (sets `LineStyle`, `FillStyle`,
304
+ * and `TextStyle` all to the same ID). Create styles via `doc.createStyle()`.
305
+ * Takes precedence over `lineStyleId`, `fillStyleId`, and `textStyleId`.
306
+ */
307
+ styleId?: number;
308
+ /** Apply a stylesheet only for line properties (`LineStyle` attribute). */
309
+ lineStyleId?: number;
310
+ /** Apply a stylesheet only for fill properties (`FillStyle` attribute). */
311
+ fillStyleId?: number;
312
+ /** Apply a stylesheet only for text properties (`TextStyle` attribute). */
313
+ textStyleId?: number;
243
314
  }
@@ -4,6 +4,7 @@ export interface VisioSection {
4
4
  Row?: any[];
5
5
  Cell?: any[];
6
6
  }
7
+ export declare const hexToRgb: (hex: string) => string;
7
8
  export declare function createFillSection(hexColor: string): VisioSection;
8
9
  export declare const ArrowHeads: {
9
10
  None: string;
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ArrowHeads = void 0;
3
+ exports.ArrowHeads = exports.hexToRgb = void 0;
4
4
  exports.createFillSection = createFillSection;
5
5
  exports.horzAlignValue = horzAlignValue;
6
6
  exports.vertAlignValue = vertAlignValue;
@@ -15,10 +15,11 @@ const hexToRgb = (hex) => {
15
15
  const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
16
16
  return result ? `RGB(${parseInt(result[1], 16)},${parseInt(result[2], 16)},${parseInt(result[3], 16)})` : 'RGB(0,0,0)';
17
17
  };
18
+ exports.hexToRgb = hexToRgb;
18
19
  function createFillSection(hexColor) {
19
20
  // Visio uses FillForegnd for the main background color.
20
21
  // Ideally we should sanitize hexColor to be #RRGGBB.
21
- const rgbFormula = hexToRgb(hexColor);
22
+ const rgbFormula = (0, exports.hexToRgb)(hexColor);
22
23
  return {
23
24
  '@_N': 'Fill',
24
25
  Cell: [
@@ -68,7 +69,7 @@ function createCharacterSection(props) {
68
69
  styleVal |= 8;
69
70
  const colorVal = props.color || '#000000';
70
71
  const cells = [
71
- { '@_N': 'Color', '@_V': colorVal, '@_F': hexToRgb(colorVal) },
72
+ { '@_N': 'Color', '@_V': colorVal, '@_F': (0, exports.hexToRgb)(colorVal) },
72
73
  { '@_N': 'Style', '@_V': styleVal.toString() },
73
74
  ];
74
75
  if (props.fontSize !== undefined) {
@@ -144,7 +145,7 @@ function createLineSection(props) {
144
145
  ];
145
146
  // Add RGB Formula for custom colors
146
147
  if (props.color) {
147
- cells[0]['@_F'] = hexToRgb(props.color);
148
+ cells[0]['@_F'] = (0, exports.hexToRgb)(props.color);
148
149
  }
149
150
  return {
150
151
  '@_N': 'Line',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-visio",
3
- "version": "1.6.0",
3
+ "version": "1.9.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "scripts": {