ts-visio 1.4.0 → 1.5.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
@@ -18,7 +18,7 @@ Built using specific schema-level abstractions to handle the complex internal st
18
18
  - **Modify Content**: Update text content of shapes.
19
19
  - **Create Shapes**: Rectangles, ellipses, diamonds, rounded rectangles, triangles, parallelograms.
20
20
  - **Connect Shapes**: Dynamic connectors with arrow styles, line styling, and routing (straight / orthogonal / curved).
21
- - **Text Styling**: Font size, font family, bold, color, horizontal/vertical alignment.
21
+ - **Text Styling**: Font size, font family, bold, italic, underline, strikethrough, color, alignment, paragraph spacing, and text margins.
22
22
  - **Shape Transformations**: Rotate, flip (X/Y), and resize shapes via a fluent API.
23
23
  - **Deletion**: Remove shapes and pages cleanly (including orphaned connectors and relationships).
24
24
  - **Lookup API**: Find shapes by ID, predicate, or look up pages by name.
@@ -521,6 +521,43 @@ Fields map to OPC parts: `title`, `author`, `description`, `keywords`, `lastModi
521
521
 
522
522
  ---
523
523
 
524
+ #### 27. Rich Text Formatting
525
+ Italic, underline, strikethrough, paragraph spacing, and text block margins are available both at shape-creation time and via `shape.setStyle()`.
526
+
527
+ ```typescript
528
+ // At creation time
529
+ const shape = await page.addShape({
530
+ text: 'Important',
531
+ x: 2, y: 3, width: 3, height: 1,
532
+ bold: true,
533
+ italic: true,
534
+ underline: true,
535
+ strikethrough: false,
536
+ // Paragraph spacing (in points)
537
+ spaceBefore: 6,
538
+ spaceAfter: 6,
539
+ lineSpacing: 1.5, // 1.5× line height
540
+ // Text block margins (in inches)
541
+ textMarginTop: 0.1,
542
+ textMarginBottom: 0.1,
543
+ textMarginLeft: 0.1,
544
+ textMarginRight: 0.1,
545
+ });
546
+
547
+ // Post-creation via setStyle()
548
+ await shape.setStyle({
549
+ italic: true,
550
+ lineSpacing: 2.0, // double spacing
551
+ textMarginTop: 0.15,
552
+ textMarginLeft: 0.05,
553
+ });
554
+ ```
555
+
556
+ `lineSpacing` is a multiplier: `1.0` = single, `1.5` = 1.5×, `2.0` = double.
557
+ `spaceBefore` / `spaceAfter` are in **points**. Text margins are in **inches**.
558
+
559
+ ---
560
+
524
561
  ## Examples
525
562
 
526
563
  Check out the [examples](./examples) directory for complete scripts.
@@ -121,6 +121,12 @@ export interface ShapeStyle {
121
121
  fillColor?: string;
122
122
  fontColor?: string;
123
123
  bold?: boolean;
124
+ /** Italic text. */
125
+ italic?: boolean;
126
+ /** Underline text. */
127
+ underline?: boolean;
128
+ /** Strikethrough text. */
129
+ strikethrough?: boolean;
124
130
  /** Font size in points (e.g. 14 for 14pt). */
125
131
  fontSize?: number;
126
132
  /** Font family name (e.g. "Arial"). */
@@ -129,4 +135,18 @@ export interface ShapeStyle {
129
135
  horzAlign?: HorzAlign;
130
136
  /** Vertical text alignment. */
131
137
  verticalAlign?: VertAlign;
138
+ /** Space before each paragraph in **points**. */
139
+ spaceBefore?: number;
140
+ /** Space after each paragraph in **points**. */
141
+ spaceAfter?: number;
142
+ /** Line-height multiplier (1.0 = single, 1.5 = 1.5×, 2.0 = double). */
143
+ lineSpacing?: number;
144
+ /** Top text margin in inches. */
145
+ textMarginTop?: number;
146
+ /** Bottom text margin in inches. */
147
+ textMarginBottom?: number;
148
+ /** Left text margin in inches. */
149
+ textMarginLeft?: number;
150
+ /** Right text margin in inches. */
151
+ textMarginRight?: number;
132
152
  }
@@ -349,19 +349,52 @@ class ShapeModifier {
349
349
  shape.Section.push((0, StyleHelpers_1.createFillSection)(style.fillColor));
350
350
  }
351
351
  // Update/Add Character (Font/Text Style)
352
- if (style.fontColor || style.bold !== undefined || style.fontSize !== undefined || style.fontFamily !== undefined) {
352
+ const hasCharProps = style.fontColor !== undefined
353
+ || style.bold !== undefined
354
+ || style.italic !== undefined
355
+ || style.underline !== undefined
356
+ || style.strikethrough !== undefined
357
+ || style.fontSize !== undefined
358
+ || style.fontFamily !== undefined;
359
+ if (hasCharProps) {
353
360
  shape.Section = shape.Section.filter((s) => s['@_N'] !== 'Character');
354
361
  shape.Section.push((0, StyleHelpers_1.createCharacterSection)({
355
362
  bold: style.bold,
363
+ italic: style.italic,
364
+ underline: style.underline,
365
+ strikethrough: style.strikethrough,
356
366
  color: style.fontColor,
357
367
  fontSize: style.fontSize,
358
368
  fontFamily: style.fontFamily,
359
369
  }));
360
370
  }
361
- // Update/Add Paragraph (Horizontal Alignment)
362
- if (style.horzAlign !== undefined) {
371
+ // Update/Add Paragraph
372
+ const hasParagraphProps = style.horzAlign !== undefined
373
+ || style.spaceBefore !== undefined
374
+ || style.spaceAfter !== undefined
375
+ || style.lineSpacing !== undefined;
376
+ if (hasParagraphProps) {
363
377
  shape.Section = shape.Section.filter((s) => s['@_N'] !== 'Paragraph');
364
- shape.Section.push((0, StyleHelpers_1.createParagraphSection)(style.horzAlign));
378
+ shape.Section.push((0, StyleHelpers_1.createParagraphSection)({
379
+ horzAlign: style.horzAlign,
380
+ spaceBefore: style.spaceBefore,
381
+ spaceAfter: style.spaceAfter,
382
+ lineSpacing: style.lineSpacing,
383
+ }));
384
+ }
385
+ // Update/Add TextBlock (text margins)
386
+ const hasTextBlockProps = style.textMarginTop !== undefined
387
+ || style.textMarginBottom !== undefined
388
+ || style.textMarginLeft !== undefined
389
+ || style.textMarginRight !== undefined;
390
+ if (hasTextBlockProps) {
391
+ shape.Section = shape.Section.filter((s) => s['@_N'] !== 'TextBlock');
392
+ shape.Section.push((0, StyleHelpers_1.createTextBlockSection)({
393
+ topMargin: style.textMarginTop,
394
+ bottomMargin: style.textMarginBottom,
395
+ leftMargin: style.textMarginLeft,
396
+ rightMargin: style.textMarginRight,
397
+ }));
365
398
  }
366
399
  // Update/Add VerticalAlign (top-level shape Cell)
367
400
  if (style.verticalAlign !== undefined) {
@@ -37,16 +37,47 @@ class ShapeBuilder {
37
37
  weight: '0.01'
38
38
  }));
39
39
  }
40
- if (props.fontColor || props.bold || props.fontSize !== undefined || props.fontFamily !== undefined) {
40
+ const hasCharProps = props.fontColor !== undefined
41
+ || props.bold !== undefined
42
+ || props.italic !== undefined
43
+ || props.underline !== undefined
44
+ || props.strikethrough !== undefined
45
+ || props.fontSize !== undefined
46
+ || props.fontFamily !== undefined;
47
+ if (hasCharProps) {
41
48
  shape.Section.push((0, StyleHelpers_1.createCharacterSection)({
42
49
  bold: props.bold,
50
+ italic: props.italic,
51
+ underline: props.underline,
52
+ strikethrough: props.strikethrough,
43
53
  color: props.fontColor,
44
54
  fontSize: props.fontSize,
45
55
  fontFamily: props.fontFamily,
46
56
  }));
47
57
  }
48
- if (props.horzAlign !== undefined) {
49
- shape.Section.push((0, StyleHelpers_1.createParagraphSection)(props.horzAlign));
58
+ const hasParagraphProps = props.horzAlign !== undefined
59
+ || props.spaceBefore !== undefined
60
+ || props.spaceAfter !== undefined
61
+ || props.lineSpacing !== undefined;
62
+ if (hasParagraphProps) {
63
+ shape.Section.push((0, StyleHelpers_1.createParagraphSection)({
64
+ horzAlign: props.horzAlign,
65
+ spaceBefore: props.spaceBefore,
66
+ spaceAfter: props.spaceAfter,
67
+ lineSpacing: props.lineSpacing,
68
+ }));
69
+ }
70
+ const hasTextBlockProps = props.textMarginTop !== undefined
71
+ || props.textMarginBottom !== undefined
72
+ || props.textMarginLeft !== undefined
73
+ || props.textMarginRight !== undefined;
74
+ if (hasTextBlockProps) {
75
+ shape.Section.push((0, StyleHelpers_1.createTextBlockSection)({
76
+ topMargin: props.textMarginTop,
77
+ bottomMargin: props.textMarginBottom,
78
+ leftMargin: props.textMarginLeft,
79
+ rightMargin: props.textMarginRight,
80
+ }));
50
81
  }
51
82
  if (props.verticalAlign !== undefined) {
52
83
  shape.Cell.push({ '@_N': 'VerticalAlign', '@_V': (0, StyleHelpers_1.vertAlignValue)(props.verticalAlign) });
@@ -172,4 +172,24 @@ export interface NewShapeProps {
172
172
  masterId?: string;
173
173
  imgRelId?: string;
174
174
  lineColor?: string;
175
+ /** Italic text. */
176
+ italic?: boolean;
177
+ /** Underline text. */
178
+ underline?: boolean;
179
+ /** Strikethrough text. */
180
+ strikethrough?: boolean;
181
+ /** Space before each paragraph in **points**. */
182
+ spaceBefore?: number;
183
+ /** Space after each paragraph in **points**. */
184
+ spaceAfter?: number;
185
+ /** Line-height multiplier (1.0 = single, 1.5 = 1.5×, 2.0 = double). */
186
+ lineSpacing?: number;
187
+ /** Top text margin in inches. */
188
+ textMarginTop?: number;
189
+ /** Bottom text margin in inches. */
190
+ textMarginBottom?: number;
191
+ /** Left text margin in inches. */
192
+ textMarginLeft?: number;
193
+ /** Right text margin in inches. */
194
+ textMarginRight?: number;
175
195
  }
@@ -32,13 +32,50 @@ export declare function horzAlignValue(align: HorzAlign): string;
32
32
  export declare function vertAlignValue(align: VertAlign): string;
33
33
  export declare function createCharacterSection(props: {
34
34
  bold?: boolean;
35
+ /** Italic text (Style bit 2). */
36
+ italic?: boolean;
37
+ /** Underline text (Style bit 4). */
38
+ underline?: boolean;
39
+ /** Strikethrough text (Style bit 8). */
40
+ strikethrough?: boolean;
35
41
  color?: string;
36
42
  /** Font size in points (e.g. 12 for 12pt). Stored internally as inches (pt / 72). */
37
43
  fontSize?: number;
38
44
  /** Font family name (e.g. "Arial"). Uses FONT() formula for portability. */
39
45
  fontFamily?: string;
40
46
  }): VisioSection;
41
- export declare function createParagraphSection(horzAlign: HorzAlign): VisioSection;
47
+ export interface ParagraphProps {
48
+ /** Horizontal text alignment within the paragraph. */
49
+ horzAlign?: HorzAlign;
50
+ /**
51
+ * Space before each paragraph in **points**.
52
+ * Converted to inches internally (pt / 72).
53
+ */
54
+ spaceBefore?: number;
55
+ /**
56
+ * Space after each paragraph in **points**.
57
+ * Converted to inches internally (pt / 72).
58
+ */
59
+ spaceAfter?: number;
60
+ /**
61
+ * Line-height multiplier (1.0 = single, 1.5 = 1.5×, 2.0 = double).
62
+ * Stored as a negative value in Visio's `SpLine` cell (negative means
63
+ * proportional; positive means absolute in inches).
64
+ */
65
+ lineSpacing?: number;
66
+ }
67
+ export declare function createParagraphSection(props: ParagraphProps): VisioSection;
68
+ export interface TextBlockProps {
69
+ /** Top text margin in inches. */
70
+ topMargin?: number;
71
+ /** Bottom text margin in inches. */
72
+ bottomMargin?: number;
73
+ /** Left text margin in inches. */
74
+ leftMargin?: number;
75
+ /** Right text margin in inches. */
76
+ rightMargin?: number;
77
+ }
78
+ export declare function createTextBlockSection(props: TextBlockProps): VisioSection;
42
79
  export declare function createLineSection(props: {
43
80
  color?: string;
44
81
  pattern?: string;
@@ -6,6 +6,7 @@ exports.horzAlignValue = horzAlignValue;
6
6
  exports.vertAlignValue = vertAlignValue;
7
7
  exports.createCharacterSection = createCharacterSection;
8
8
  exports.createParagraphSection = createParagraphSection;
9
+ exports.createTextBlockSection = createTextBlockSection;
9
10
  exports.createLineSection = createLineSection;
10
11
  const hexToRgb = (hex) => {
11
12
  // Expand shorthand form (e.g. "03F") to full form (e.g. "0033FF")
@@ -57,9 +58,14 @@ function vertAlignValue(align) {
57
58
  }
58
59
  function createCharacterSection(props) {
59
60
  let styleVal = 0;
60
- if (props.bold) {
61
- styleVal += 1; // Bold bit
62
- }
61
+ if (props.bold)
62
+ styleVal |= 1;
63
+ if (props.italic)
64
+ styleVal |= 2;
65
+ if (props.underline)
66
+ styleVal |= 4;
67
+ if (props.strikethrough)
68
+ styleVal |= 8;
63
69
  const colorVal = props.color || '#000000';
64
70
  const cells = [
65
71
  { '@_N': 'Color', '@_V': colorVal, '@_F': hexToRgb(colorVal) },
@@ -89,20 +95,47 @@ function createCharacterSection(props) {
89
95
  ]
90
96
  };
91
97
  }
92
- function createParagraphSection(horzAlign) {
98
+ function createParagraphSection(props) {
99
+ const cells = [];
100
+ if (props.horzAlign !== undefined) {
101
+ cells.push({ '@_N': 'HorzAlign', '@_V': HORZ_ALIGN_VALUES[props.horzAlign] });
102
+ }
103
+ if (props.spaceBefore !== undefined) {
104
+ cells.push({ '@_N': 'SpBefore', '@_V': (props.spaceBefore / 72).toString(), '@_U': 'PT' });
105
+ }
106
+ if (props.spaceAfter !== undefined) {
107
+ cells.push({ '@_N': 'SpAfter', '@_V': (props.spaceAfter / 72).toString(), '@_U': 'PT' });
108
+ }
109
+ if (props.lineSpacing !== undefined) {
110
+ // Negative value = proportional multiplier; positive = absolute (inches)
111
+ cells.push({ '@_N': 'SpLine', '@_V': (-props.lineSpacing).toString() });
112
+ }
93
113
  return {
94
114
  '@_N': 'Paragraph',
95
115
  Row: [
96
116
  {
97
117
  '@_T': 'Paragraph',
98
118
  '@_IX': '0',
99
- Cell: [
100
- { '@_N': 'HorzAlign', '@_V': HORZ_ALIGN_VALUES[horzAlign] },
101
- ]
119
+ Cell: cells,
102
120
  }
103
121
  ]
104
122
  };
105
123
  }
124
+ function createTextBlockSection(props) {
125
+ const cells = [];
126
+ if (props.topMargin !== undefined)
127
+ cells.push({ '@_N': 'TopMargin', '@_V': props.topMargin.toString(), '@_U': 'IN' });
128
+ if (props.bottomMargin !== undefined)
129
+ cells.push({ '@_N': 'BottomMargin', '@_V': props.bottomMargin.toString(), '@_U': 'IN' });
130
+ if (props.leftMargin !== undefined)
131
+ cells.push({ '@_N': 'LeftMargin', '@_V': props.leftMargin.toString(), '@_U': 'IN' });
132
+ if (props.rightMargin !== undefined)
133
+ cells.push({ '@_N': 'RightMargin', '@_V': props.rightMargin.toString(), '@_U': 'IN' });
134
+ return {
135
+ '@_N': 'TextBlock',
136
+ Cell: cells,
137
+ };
138
+ }
106
139
  function createLineSection(props) {
107
140
  const cells = [
108
141
  { '@_N': 'LineColor', '@_V': props.color || '#000000' },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-visio",
3
- "version": "1.4.0",
3
+ "version": "1.5.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "scripts": {