@cj-tech-master/excelts 9.6.1 → 10.0.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.
Files changed (207) hide show
  1. package/README.md +18 -3
  2. package/README_zh.md +18 -3
  3. package/dist/browser/modules/excel/cell.d.ts +4 -0
  4. package/dist/browser/modules/excel/note.js +5 -1
  5. package/dist/browser/modules/excel/row.js +35 -2
  6. package/dist/browser/modules/excel/stream/workbook-writer.browser.d.ts +8 -1
  7. package/dist/browser/modules/excel/stream/workbook-writer.browser.js +22 -2
  8. package/dist/browser/modules/excel/types.d.ts +81 -0
  9. package/dist/browser/modules/excel/utils/drawing-utils.d.ts +8 -0
  10. package/dist/browser/modules/excel/utils/drawing-utils.js +19 -2
  11. package/dist/browser/modules/excel/workbook.browser.d.ts +16 -0
  12. package/dist/browser/modules/excel/workbook.browser.js +32 -2
  13. package/dist/browser/modules/excel/worksheet.d.ts +31 -1
  14. package/dist/browser/modules/excel/worksheet.js +83 -0
  15. package/dist/browser/modules/excel/xlsx/xform/comment/vml-shape-xform.d.ts +7 -0
  16. package/dist/browser/modules/excel/xlsx/xform/comment/vml-shape-xform.js +42 -8
  17. package/dist/browser/modules/excel/xlsx/xform/core/content-types-xform.js +3 -1
  18. package/dist/browser/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +5 -0
  19. package/dist/browser/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +18 -1
  20. package/dist/browser/modules/excel/xlsx/xform/drawing/blip-xform.d.ts +6 -0
  21. package/dist/browser/modules/excel/xlsx/xform/drawing/blip-xform.js +38 -11
  22. package/dist/browser/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.d.ts +1 -0
  23. package/dist/browser/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.js +5 -0
  24. package/dist/browser/modules/excel/xlsx/xform/drawing/pic-xform.d.ts +2 -0
  25. package/dist/browser/modules/excel/xlsx/xform/drawing/pic-xform.js +2 -1
  26. package/dist/browser/modules/excel/xlsx/xform/drawing/shape-xform.d.ts +47 -0
  27. package/dist/browser/modules/excel/xlsx/xform/drawing/shape-xform.js +109 -0
  28. package/dist/browser/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.js +10 -1
  29. package/dist/browser/modules/excel/xlsx/xform/sheet/worksheet-xform.js +64 -1
  30. package/dist/browser/modules/pdf/builder/document-builder.js +22 -49
  31. package/dist/browser/modules/pdf/builder/pdf-editor.js +1 -1
  32. package/dist/browser/modules/pdf/core/pdf-stream.d.ts +28 -1
  33. package/dist/browser/modules/pdf/core/pdf-stream.js +38 -2
  34. package/dist/browser/modules/pdf/font/font-manager.d.ts +26 -0
  35. package/dist/browser/modules/pdf/font/font-manager.js +35 -18
  36. package/dist/browser/modules/pdf/render/page-renderer.d.ts +51 -3
  37. package/dist/browser/modules/pdf/render/page-renderer.js +111 -18
  38. package/dist/browser/modules/word/advanced/field-engine.js +45 -20
  39. package/dist/browser/modules/word/advanced/glossary.d.ts +10 -36
  40. package/dist/browser/modules/word/advanced/glossary.js +8 -9
  41. package/dist/browser/modules/word/advanced/math-convert.js +94 -12
  42. package/dist/browser/modules/word/advanced/ole-objects.d.ts +28 -0
  43. package/dist/browser/modules/word/advanced/ole-objects.js +122 -19
  44. package/dist/browser/modules/word/advanced/style-map.js +31 -10
  45. package/dist/browser/modules/word/builder/run-builders.d.ts +7 -1
  46. package/dist/browser/modules/word/builder/run-builders.js +7 -1
  47. package/dist/browser/modules/word/constants.d.ts +4 -0
  48. package/dist/browser/modules/word/constants.js +5 -1
  49. package/dist/browser/modules/word/convert/docx-to-semantic.d.ts +2 -1
  50. package/dist/browser/modules/word/convert/docx-to-semantic.js +135 -1
  51. package/dist/browser/modules/word/convert/html/html-import.d.ts +32 -1
  52. package/dist/browser/modules/word/convert/html/html-import.js +167 -14
  53. package/dist/browser/modules/word/convert/html/html.d.ts +2 -2
  54. package/dist/browser/modules/word/convert/html/html.js +1 -1
  55. package/dist/browser/modules/word/convert/markdown/markdown-import.d.ts +48 -18
  56. package/dist/browser/modules/word/convert/markdown/markdown-import.js +279 -69
  57. package/dist/browser/modules/word/convert/markdown/markdown.d.ts +1 -1
  58. package/dist/browser/modules/word/convert/odt/odt.js +407 -56
  59. package/dist/browser/modules/word/html.d.ts +2 -2
  60. package/dist/browser/modules/word/html.js +1 -1
  61. package/dist/browser/modules/word/index.base.d.ts +3 -3
  62. package/dist/browser/modules/word/index.base.js +1 -1
  63. package/dist/browser/modules/word/layout/layout-full.js +326 -19
  64. package/dist/browser/modules/word/layout/render-page.js +35 -8
  65. package/dist/browser/modules/word/markdown.d.ts +1 -1
  66. package/dist/browser/modules/word/query/compat.d.ts +10 -2
  67. package/dist/browser/modules/word/query/compat.js +29 -21
  68. package/dist/browser/modules/word/reader/docx-reader.js +105 -2
  69. package/dist/browser/modules/word/reader/math-parser.js +8 -2
  70. package/dist/browser/modules/word/security/cfb-reader.js +5 -5
  71. package/dist/browser/modules/word/types.d.ts +96 -1
  72. package/dist/browser/modules/word/writer/docx-packager.js +108 -2
  73. package/dist/browser/modules/word/writer/glossary-writer.d.ts +28 -0
  74. package/dist/browser/modules/word/writer/glossary-writer.js +121 -0
  75. package/dist/browser/modules/word/writer/header-footer-writer.js +105 -20
  76. package/dist/browser/modules/word/writer/math-writer.js +7 -2
  77. package/dist/browser/utils/font-metrics.d.ts +8 -0
  78. package/dist/browser/utils/font-metrics.js +43 -0
  79. package/dist/browser/utils/theme-colors.js +4 -1
  80. package/dist/cjs/modules/excel/note.js +5 -1
  81. package/dist/cjs/modules/excel/row.js +35 -2
  82. package/dist/cjs/modules/excel/stream/workbook-writer.browser.js +22 -2
  83. package/dist/cjs/modules/excel/utils/drawing-utils.js +19 -2
  84. package/dist/cjs/modules/excel/workbook.browser.js +31 -1
  85. package/dist/cjs/modules/excel/worksheet.js +83 -0
  86. package/dist/cjs/modules/excel/xlsx/xform/comment/vml-shape-xform.js +42 -8
  87. package/dist/cjs/modules/excel/xlsx/xform/core/content-types-xform.js +3 -1
  88. package/dist/cjs/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +5 -0
  89. package/dist/cjs/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +18 -1
  90. package/dist/cjs/modules/excel/xlsx/xform/drawing/blip-xform.js +38 -11
  91. package/dist/cjs/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.js +5 -0
  92. package/dist/cjs/modules/excel/xlsx/xform/drawing/pic-xform.js +2 -1
  93. package/dist/cjs/modules/excel/xlsx/xform/drawing/shape-xform.js +112 -0
  94. package/dist/cjs/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.js +10 -1
  95. package/dist/cjs/modules/excel/xlsx/xform/sheet/worksheet-xform.js +64 -1
  96. package/dist/cjs/modules/pdf/builder/document-builder.js +21 -48
  97. package/dist/cjs/modules/pdf/builder/pdf-editor.js +1 -1
  98. package/dist/cjs/modules/pdf/core/pdf-stream.js +38 -2
  99. package/dist/cjs/modules/pdf/font/font-manager.js +35 -18
  100. package/dist/cjs/modules/pdf/render/page-renderer.js +112 -18
  101. package/dist/cjs/modules/word/advanced/field-engine.js +45 -20
  102. package/dist/cjs/modules/word/advanced/glossary.js +8 -9
  103. package/dist/cjs/modules/word/advanced/math-convert.js +94 -12
  104. package/dist/cjs/modules/word/advanced/ole-objects.js +123 -19
  105. package/dist/cjs/modules/word/advanced/style-map.js +31 -10
  106. package/dist/cjs/modules/word/builder/run-builders.js +7 -1
  107. package/dist/cjs/modules/word/constants.js +5 -1
  108. package/dist/cjs/modules/word/convert/docx-to-semantic.js +135 -1
  109. package/dist/cjs/modules/word/convert/html/html-import.js +168 -14
  110. package/dist/cjs/modules/word/convert/html/html.js +2 -1
  111. package/dist/cjs/modules/word/convert/markdown/markdown-import.js +279 -69
  112. package/dist/cjs/modules/word/convert/odt/odt.js +407 -56
  113. package/dist/cjs/modules/word/html.js +2 -1
  114. package/dist/cjs/modules/word/index.base.js +4 -3
  115. package/dist/cjs/modules/word/layout/layout-full.js +325 -18
  116. package/dist/cjs/modules/word/layout/render-page.js +35 -8
  117. package/dist/cjs/modules/word/query/compat.js +29 -21
  118. package/dist/cjs/modules/word/reader/docx-reader.js +104 -1
  119. package/dist/cjs/modules/word/reader/math-parser.js +8 -2
  120. package/dist/cjs/modules/word/security/cfb-reader.js +5 -5
  121. package/dist/cjs/modules/word/writer/docx-packager.js +108 -2
  122. package/dist/cjs/modules/word/writer/glossary-writer.js +124 -0
  123. package/dist/cjs/modules/word/writer/header-footer-writer.js +105 -20
  124. package/dist/cjs/modules/word/writer/math-writer.js +7 -2
  125. package/dist/cjs/utils/font-metrics.js +44 -0
  126. package/dist/cjs/utils/theme-colors.js +4 -1
  127. package/dist/esm/modules/excel/note.js +5 -1
  128. package/dist/esm/modules/excel/row.js +35 -2
  129. package/dist/esm/modules/excel/stream/workbook-writer.browser.js +22 -2
  130. package/dist/esm/modules/excel/utils/drawing-utils.js +19 -2
  131. package/dist/esm/modules/excel/workbook.browser.js +32 -2
  132. package/dist/esm/modules/excel/worksheet.js +83 -0
  133. package/dist/esm/modules/excel/xlsx/xform/comment/vml-shape-xform.js +42 -8
  134. package/dist/esm/modules/excel/xlsx/xform/core/content-types-xform.js +3 -1
  135. package/dist/esm/modules/excel/xlsx/xform/drawing/absolute-anchor-xform.js +5 -0
  136. package/dist/esm/modules/excel/xlsx/xform/drawing/base-cell-anchor-xform.js +18 -1
  137. package/dist/esm/modules/excel/xlsx/xform/drawing/blip-xform.js +38 -11
  138. package/dist/esm/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.js +5 -0
  139. package/dist/esm/modules/excel/xlsx/xform/drawing/pic-xform.js +2 -1
  140. package/dist/esm/modules/excel/xlsx/xform/drawing/shape-xform.js +109 -0
  141. package/dist/esm/modules/excel/xlsx/xform/drawing/two-cell-anchor-xform.js +10 -1
  142. package/dist/esm/modules/excel/xlsx/xform/sheet/worksheet-xform.js +64 -1
  143. package/dist/esm/modules/pdf/builder/document-builder.js +22 -49
  144. package/dist/esm/modules/pdf/builder/pdf-editor.js +1 -1
  145. package/dist/esm/modules/pdf/core/pdf-stream.js +38 -2
  146. package/dist/esm/modules/pdf/font/font-manager.js +35 -18
  147. package/dist/esm/modules/pdf/render/page-renderer.js +111 -18
  148. package/dist/esm/modules/word/advanced/field-engine.js +45 -20
  149. package/dist/esm/modules/word/advanced/glossary.js +8 -9
  150. package/dist/esm/modules/word/advanced/math-convert.js +94 -12
  151. package/dist/esm/modules/word/advanced/ole-objects.js +122 -19
  152. package/dist/esm/modules/word/advanced/style-map.js +31 -10
  153. package/dist/esm/modules/word/builder/run-builders.js +7 -1
  154. package/dist/esm/modules/word/constants.js +5 -1
  155. package/dist/esm/modules/word/convert/docx-to-semantic.js +135 -1
  156. package/dist/esm/modules/word/convert/html/html-import.js +167 -14
  157. package/dist/esm/modules/word/convert/html/html.js +1 -1
  158. package/dist/esm/modules/word/convert/markdown/markdown-import.js +279 -69
  159. package/dist/esm/modules/word/convert/odt/odt.js +407 -56
  160. package/dist/esm/modules/word/html.js +1 -1
  161. package/dist/esm/modules/word/index.base.js +1 -1
  162. package/dist/esm/modules/word/layout/layout-full.js +326 -19
  163. package/dist/esm/modules/word/layout/render-page.js +35 -8
  164. package/dist/esm/modules/word/query/compat.js +29 -21
  165. package/dist/esm/modules/word/reader/docx-reader.js +105 -2
  166. package/dist/esm/modules/word/reader/math-parser.js +8 -2
  167. package/dist/esm/modules/word/security/cfb-reader.js +5 -5
  168. package/dist/esm/modules/word/writer/docx-packager.js +108 -2
  169. package/dist/esm/modules/word/writer/glossary-writer.js +121 -0
  170. package/dist/esm/modules/word/writer/header-footer-writer.js +105 -20
  171. package/dist/esm/modules/word/writer/math-writer.js +7 -2
  172. package/dist/esm/utils/font-metrics.js +43 -0
  173. package/dist/esm/utils/theme-colors.js +4 -1
  174. package/dist/iife/excelts.iife.js +496 -59
  175. package/dist/iife/excelts.iife.js.map +1 -1
  176. package/dist/iife/excelts.iife.min.js +39 -39
  177. package/dist/types/modules/excel/cell.d.ts +4 -0
  178. package/dist/types/modules/excel/stream/workbook-writer.browser.d.ts +8 -1
  179. package/dist/types/modules/excel/types.d.ts +81 -0
  180. package/dist/types/modules/excel/utils/drawing-utils.d.ts +8 -0
  181. package/dist/types/modules/excel/workbook.browser.d.ts +16 -0
  182. package/dist/types/modules/excel/worksheet.d.ts +31 -1
  183. package/dist/types/modules/excel/xlsx/xform/comment/vml-shape-xform.d.ts +7 -0
  184. package/dist/types/modules/excel/xlsx/xform/drawing/blip-xform.d.ts +6 -0
  185. package/dist/types/modules/excel/xlsx/xform/drawing/one-cell-anchor-xform.d.ts +1 -0
  186. package/dist/types/modules/excel/xlsx/xform/drawing/pic-xform.d.ts +2 -0
  187. package/dist/types/modules/excel/xlsx/xform/drawing/shape-xform.d.ts +47 -0
  188. package/dist/types/modules/pdf/core/pdf-stream.d.ts +28 -1
  189. package/dist/types/modules/pdf/font/font-manager.d.ts +26 -0
  190. package/dist/types/modules/pdf/render/page-renderer.d.ts +51 -3
  191. package/dist/types/modules/word/advanced/glossary.d.ts +10 -36
  192. package/dist/types/modules/word/advanced/ole-objects.d.ts +28 -0
  193. package/dist/types/modules/word/builder/run-builders.d.ts +7 -1
  194. package/dist/types/modules/word/constants.d.ts +4 -0
  195. package/dist/types/modules/word/convert/docx-to-semantic.d.ts +2 -1
  196. package/dist/types/modules/word/convert/html/html-import.d.ts +32 -1
  197. package/dist/types/modules/word/convert/html/html.d.ts +2 -2
  198. package/dist/types/modules/word/convert/markdown/markdown-import.d.ts +48 -18
  199. package/dist/types/modules/word/convert/markdown/markdown.d.ts +1 -1
  200. package/dist/types/modules/word/html.d.ts +2 -2
  201. package/dist/types/modules/word/index.base.d.ts +3 -3
  202. package/dist/types/modules/word/markdown.d.ts +1 -1
  203. package/dist/types/modules/word/query/compat.d.ts +10 -2
  204. package/dist/types/modules/word/types.d.ts +96 -1
  205. package/dist/types/modules/word/writer/glossary-writer.d.ts +28 -0
  206. package/dist/types/utils/font-metrics.d.ts +8 -0
  207. package/package.json +3 -1
@@ -5,6 +5,8 @@ interface ShapeModel {
5
5
  insetmode?: string;
6
6
  inset?: number[];
7
7
  };
8
+ width?: number;
9
+ height?: number;
8
10
  };
9
11
  refAddress?: any;
10
12
  }
@@ -20,6 +22,11 @@ declare class VmlShapeXform extends BaseXform {
20
22
  parseOpen(node: any): boolean;
21
23
  parseText(text: string): void;
22
24
  parseClose(name: string): boolean;
25
+ /**
26
+ * Extract a points-valued length (e.g. `width:120pt`) from a VML style
27
+ * string. Returns `undefined` when the property is absent or not in `pt`.
28
+ */
29
+ static parseStyleLength(style: string, prop: "width" | "height"): number | undefined;
23
30
  static V_SHAPE_ATTRIBUTES: (model: ShapeModel, index: number) => any;
24
31
  }
25
32
  export { VmlShapeXform };
@@ -1,6 +1,9 @@
1
1
  import { BaseXform } from "../base-xform.js";
2
2
  import { VmlClientDataXform } from "./vml-client-data-xform.js";
3
3
  import { VmlTextboxXform } from "./vml-textbox-xform.js";
4
+ /** Default comment box geometry in points (matches legacy Excel notes). */
5
+ const DEFAULT_NOTE_WIDTH_PT = 97.8;
6
+ const DEFAULT_NOTE_HEIGHT_PT = 59.1;
4
7
  class VmlShapeXform extends BaseXform {
5
8
  constructor() {
6
9
  super();
@@ -37,6 +40,21 @@ class VmlShapeXform extends BaseXform {
37
40
  editAs: "",
38
41
  protection: {}
39
42
  };
43
+ {
44
+ // Recover the comment box geometry from the VML style string
45
+ // (e.g. "...width:120pt;height:80pt;..."). Only surface width/height
46
+ // when they differ from the legacy defaults, so untouched notes keep
47
+ // a clean model (and stay byte-compatible with prior behaviour).
48
+ const style = node.attributes.style ?? "";
49
+ const width = VmlShapeXform.parseStyleLength(style, "width");
50
+ const height = VmlShapeXform.parseStyleLength(style, "height");
51
+ if (width !== undefined && width !== DEFAULT_NOTE_WIDTH_PT) {
52
+ this.model.width = width;
53
+ }
54
+ if (height !== undefined && height !== DEFAULT_NOTE_HEIGHT_PT) {
55
+ this.model.height = height;
56
+ }
57
+ }
40
58
  break;
41
59
  default:
42
60
  this.parser = this.map[node.name];
@@ -75,13 +93,29 @@ class VmlShapeXform extends BaseXform {
75
93
  return true;
76
94
  }
77
95
  }
96
+ /**
97
+ * Extract a points-valued length (e.g. `width:120pt`) from a VML style
98
+ * string. Returns `undefined` when the property is absent or not in `pt`.
99
+ */
100
+ static parseStyleLength(style, prop) {
101
+ const match = new RegExp(`(?:^|;)\\s*${prop}\\s*:\\s*([0-9.]+)pt`, "i").exec(style);
102
+ if (!match) {
103
+ return undefined;
104
+ }
105
+ const value = parseFloat(match[1]);
106
+ return Number.isFinite(value) ? value : undefined;
107
+ }
78
108
  }
79
- VmlShapeXform.V_SHAPE_ATTRIBUTES = (model, index) => ({
80
- id: `_x0000_s${1025 + index}`,
81
- type: "#_x0000_t202",
82
- style: "position:absolute; margin-left:105.3pt;margin-top:10.5pt;width:97.8pt;height:59.1pt;z-index:1;visibility:hidden",
83
- fillcolor: "infoBackground [80]",
84
- strokecolor: "none [81]",
85
- "o:insetmode": model.note.margins && model.note.margins.insetmode
86
- });
109
+ VmlShapeXform.V_SHAPE_ATTRIBUTES = (model, index) => {
110
+ const width = model.note?.width ?? DEFAULT_NOTE_WIDTH_PT;
111
+ const height = model.note?.height ?? DEFAULT_NOTE_HEIGHT_PT;
112
+ return {
113
+ id: `_x0000_s${1025 + index}`,
114
+ type: "#_x0000_t202",
115
+ style: `position:absolute; margin-left:105.3pt;margin-top:10.5pt;width:${width}pt;height:${height}pt;z-index:1;visibility:hidden`,
116
+ fillcolor: "infoBackground [80]",
117
+ strokecolor: "none [81]",
118
+ "o:insetmode": model.note.margins && model.note.margins.insetmode
119
+ };
120
+ };
87
121
  export { VmlShapeXform };
@@ -21,7 +21,9 @@ class ContentTypesXform extends BaseXform {
21
21
  mediaHash[imageType] = true;
22
22
  xmlStream.leafNode("Default", {
23
23
  Extension: imageType,
24
- ContentType: `image/${imageType}`
24
+ // SVG's IANA media type is "image/svg+xml"; everything else follows
25
+ // the "image/<ext>" convention.
26
+ ContentType: imageType === "svg" ? "image/svg+xml" : `image/${imageType}`
25
27
  });
26
28
  }
27
29
  }
@@ -3,6 +3,7 @@ import { BaseCellAnchorXform } from "./base-cell-anchor-xform.js";
3
3
  import { ExtXform } from "./ext-xform.js";
4
4
  import { GraphicFrameXform } from "./graphic-frame-xform.js";
5
5
  import { PicXform } from "./pic-xform.js";
6
+ import { ShapeXform } from "./shape-xform.js";
6
7
  import { StaticXform } from "../static-xform.js";
7
8
  /** https://en.wikipedia.org/wiki/Office_Open_XML_file_formats#DrawingML */
8
9
  const EMU_PER_PIXEL_AT_96_DPI = 9525;
@@ -70,6 +71,7 @@ class AbsoluteAnchorXform extends BaseCellAnchorXform {
70
71
  // `<xdr:absoluteAnchor><xdr:pos/><xdr:ext/><xdr:clientData/></xdr:absoluteAnchor>`
71
72
  // with no chart reference, so the anchor was ignored on open.
72
73
  "xdr:graphicFrame": new GraphicFrameXform(),
74
+ "xdr:userShape": new ShapeXform(),
73
75
  "xdr:clientData": new StaticXform({ tag: "xdr:clientData" })
74
76
  };
75
77
  }
@@ -94,6 +96,9 @@ class AbsoluteAnchorXform extends BaseCellAnchorXform {
94
96
  else if (model.graphicFrame) {
95
97
  this.map["xdr:graphicFrame"].render(xmlStream, model.graphicFrame);
96
98
  }
99
+ else if (model.shape?.kind === "userShape") {
100
+ this.map["xdr:userShape"].render(xmlStream, model.shape);
101
+ }
97
102
  this.map["xdr:clientData"].render(xmlStream, {});
98
103
  xmlStream.closeNode();
99
104
  }
@@ -47,8 +47,25 @@ class BaseCellAnchorXform extends BaseXform {
47
47
  const name = match[1];
48
48
  const mediaId = options.mediaIndex[name];
49
49
  const medium = options.media[mediaId];
50
+ if (!medium) {
51
+ return undefined;
52
+ }
53
+ // Resolve an SVG companion (asvg:svgBlip extension) back to its media
54
+ // index and record it on the raster media entry itself, so callers that
55
+ // look the image up by id (e.g. Workbook.getImage) surface the vector
56
+ // companion alongside the raster fallback.
57
+ if (model.svgRId) {
58
+ const svgRel = options.rels[model.svgRId];
59
+ const svgMatch = svgRel && svgRel.Target.match(/.*\/media\/(.+[.][a-zA-Z]{3,4})/);
60
+ if (svgMatch) {
61
+ const svgMediaId = options.mediaIndex[svgMatch[1]];
62
+ if (svgMediaId !== undefined) {
63
+ medium.svgMediaId = svgMediaId;
64
+ }
65
+ }
66
+ }
50
67
  // Preserve alphaModFix (transparency) from the picture model if present
51
- if (medium && model.alphaModFix !== undefined) {
68
+ if (model.alphaModFix !== undefined) {
52
69
  return { ...medium, alphaModFix: model.alphaModFix };
53
70
  }
54
71
  return medium;
@@ -8,6 +8,12 @@ interface BlipModel {
8
8
  * instead of an embedded one via `r:embed`.
9
9
  */
10
10
  external?: boolean;
11
+ /**
12
+ * Relationship id of an SVG companion. When set, the raster blip carries an
13
+ * `asvg:svgBlip` extension referencing the SVG media so Excel 2016+ renders
14
+ * the vector image while older consumers fall back to the raster blip.
15
+ */
16
+ svgRId?: string;
11
17
  }
12
18
  declare class BlipXform extends BaseXform<BlipModel> {
13
19
  constructor();
@@ -1,4 +1,9 @@
1
1
  import { BaseXform } from "../base-xform.js";
2
+ /** OOXML extension URI for the SVG blip (Office 2016 SVG feature). */
3
+ const SVG_BLIP_EXT_URI = "{96DAC541-7B7A-43D3-8B79-37D633B846F1}";
4
+ /** Namespace for the asvg:svgBlip element. */
5
+ const SVG_BLIP_NS = "http://schemas.microsoft.com/office/drawing/2016/SVG/main";
6
+ const REL_NS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
2
7
  class BlipXform extends BaseXform {
3
8
  constructor() {
4
9
  super();
@@ -10,23 +15,37 @@ class BlipXform extends BaseXform {
10
15
  render(xmlStream, model) {
11
16
  // External (linked) images use `r:link`; embedded images use `r:embed`.
12
17
  const relAttr = model.external ? "r:link" : "r:embed";
13
- if (model.alphaModFix !== undefined && model.alphaModFix < 100000) {
14
- // Render as open/close node with a:alphaModFix child
15
- xmlStream.openNode(this.tag, {
16
- "xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
18
+ const hasAlpha = model.alphaModFix !== undefined && model.alphaModFix < 100000;
19
+ const hasSvg = model.svgRId !== undefined;
20
+ // A bare blip can be a leaf node; an alpha modulation or an SVG extension
21
+ // both require child elements, so switch to the open/close form.
22
+ if (!hasAlpha && !hasSvg) {
23
+ xmlStream.leafNode(this.tag, {
24
+ "xmlns:r": REL_NS,
17
25
  [relAttr]: model.rId,
18
26
  cstate: "print"
19
27
  });
28
+ return;
29
+ }
30
+ xmlStream.openNode(this.tag, {
31
+ "xmlns:r": REL_NS,
32
+ [relAttr]: model.rId,
33
+ cstate: "print"
34
+ });
35
+ if (hasAlpha) {
20
36
  xmlStream.leafNode("a:alphaModFix", { amt: String(model.alphaModFix) });
21
- xmlStream.closeNode();
22
37
  }
23
- else {
24
- xmlStream.leafNode(this.tag, {
25
- "xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
26
- [relAttr]: model.rId,
27
- cstate: "print"
38
+ if (hasSvg) {
39
+ xmlStream.openNode("a:extLst");
40
+ xmlStream.openNode("a:ext", { uri: SVG_BLIP_EXT_URI });
41
+ xmlStream.leafNode("asvg:svgBlip", {
42
+ "xmlns:asvg": SVG_BLIP_NS,
43
+ "r:embed": model.svgRId
28
44
  });
45
+ xmlStream.closeNode(); // a:ext
46
+ xmlStream.closeNode(); // a:extLst
29
47
  }
48
+ xmlStream.closeNode(); // a:blip
30
49
  }
31
50
  parseOpen(node) {
32
51
  switch (node.name) {
@@ -46,6 +65,14 @@ class BlipXform extends BaseXform {
46
65
  this.model.alphaModFix = parseInt(node.attributes.amt, 10);
47
66
  }
48
67
  return true;
68
+ case "asvg:svgBlip": {
69
+ // Capture the SVG companion's relationship id for round-trip.
70
+ const embed = node.attributes["r:embed"];
71
+ if (embed !== undefined && this.model) {
72
+ this.model.svgRId = embed;
73
+ }
74
+ return true;
75
+ }
49
76
  default:
50
77
  return true;
51
78
  }
@@ -56,7 +83,7 @@ class BlipXform extends BaseXform {
56
83
  case this.tag:
57
84
  return false;
58
85
  default:
59
- // unprocessed internal nodes
86
+ // unprocessed internal nodes (a:extLst / a:ext / alphaModFix)
60
87
  return true;
61
88
  }
62
89
  }
@@ -6,6 +6,7 @@ interface OneCellModel {
6
6
  ext: any;
7
7
  };
8
8
  picture?: any;
9
+ shape?: any;
9
10
  /** Graphic frame model (for charts and other embedded objects) */
10
11
  graphicFrame?: any;
11
12
  }
@@ -3,6 +3,7 @@ import { CellPositionXform } from "./cell-position-xform.js";
3
3
  import { ExtXform } from "./ext-xform.js";
4
4
  import { GraphicFrameXform } from "./graphic-frame-xform.js";
5
5
  import { PicXform } from "./pic-xform.js";
6
+ import { ShapeXform } from "./shape-xform.js";
6
7
  import { StaticXform } from "../static-xform.js";
7
8
  class OneCellAnchorXform extends BaseCellAnchorXform {
8
9
  constructor() {
@@ -11,6 +12,7 @@ class OneCellAnchorXform extends BaseCellAnchorXform {
11
12
  "xdr:from": new CellPositionXform({ tag: "xdr:from" }),
12
13
  "xdr:ext": new ExtXform({ tag: "xdr:ext" }),
13
14
  "xdr:pic": new PicXform(),
15
+ "xdr:userShape": new ShapeXform(),
14
16
  "xdr:graphicFrame": new GraphicFrameXform(),
15
17
  "xdr:clientData": new StaticXform({ tag: "xdr:clientData" })
16
18
  };
@@ -36,6 +38,9 @@ class OneCellAnchorXform extends BaseCellAnchorXform {
36
38
  else if (model.graphicFrame) {
37
39
  this.map["xdr:graphicFrame"].render(xmlStream, model.graphicFrame);
38
40
  }
41
+ else if (model.shape?.kind === "userShape") {
42
+ this.map["xdr:userShape"].render(xmlStream, model.shape);
43
+ }
39
44
  this.map["xdr:clientData"].render(xmlStream, {});
40
45
  xmlStream.closeNode();
41
46
  }
@@ -6,6 +6,8 @@ interface PicModel {
6
6
  alphaModFix?: number;
7
7
  /** When true, render the picture as an external linked image (`r:link`). */
8
8
  external?: boolean;
9
+ /** Relationship id of an SVG companion (asvg:svgBlip extension). */
10
+ svgRId?: string;
9
11
  [key: string]: any;
10
12
  }
11
13
  declare class PicXform extends BaseXform {
@@ -25,7 +25,8 @@ class PicXform extends BaseXform {
25
25
  this.map["xdr:blipFill"].render(xmlStream, {
26
26
  rId: model.rId,
27
27
  alphaModFix: model.alphaModFix,
28
- external: model.external
28
+ external: model.external,
29
+ svgRId: model.svgRId
29
30
  });
30
31
  this.map["xdr:spPr"].render(xmlStream, model);
31
32
  xmlStream.closeNode();
@@ -0,0 +1,47 @@
1
+ import { BaseXform } from "../base-xform.js";
2
+ /** Fill specification for a drawing shape. */
3
+ export interface ShapeFill {
4
+ /** Solid fill colour as a hex RGB string (e.g. "FF0000"). Omit for no fill. */
5
+ color?: string;
6
+ }
7
+ /** Line (outline) specification for a drawing shape. */
8
+ export interface ShapeLine {
9
+ /** Line colour as a hex RGB string (e.g. "000000"). */
10
+ color?: string;
11
+ /** Line width in points. */
12
+ width?: number;
13
+ }
14
+ /**
15
+ * Model for a user-visible drawing shape (`<xdr:sp>`).
16
+ *
17
+ * Distinct from the form-control shape rendered by `SpXform` — this one is
18
+ * visible, carries a configurable preset geometry, solid fill, outline and an
19
+ * optional text label, and is NOT wrapped in an `a14` AlternateContent block.
20
+ */
21
+ export interface ShapeRenderModel {
22
+ /** Marks this as a user shape so the anchor routes to ShapeXform. */
23
+ kind: "userShape";
24
+ /** Unique drawing id. */
25
+ cNvPrId: number;
26
+ /** Display name (e.g. "Rectangle 1"). */
27
+ name: string;
28
+ /** Preset geometry name (e.g. "rect", "ellipse", "line", "roundRect"). */
29
+ shapeType: string;
30
+ fill?: ShapeFill;
31
+ line?: ShapeLine;
32
+ /** Optional text label centred in the shape. */
33
+ text?: string;
34
+ }
35
+ /**
36
+ * Renders a user-visible drawing shape. Geometry/position is governed by the
37
+ * enclosing anchor (`xfrm` is written as zero, matching how Excel anchors a
38
+ * shape to a cell range), while preset geometry, fill, outline and text are
39
+ * taken from the model. Write-only: shapes are not parsed back on read (the
40
+ * same limitation that already applies to all non-chart drawing content).
41
+ */
42
+ declare class ShapeXform extends BaseXform {
43
+ model: ShapeRenderModel;
44
+ get tag(): string;
45
+ render(xmlStream: any, model: ShapeRenderModel): void;
46
+ }
47
+ export { ShapeXform };
@@ -0,0 +1,109 @@
1
+ import { BaseXform } from "../base-xform.js";
2
+ const EMU_PER_POINT = 12700;
3
+ /**
4
+ * Normalize a user-supplied colour to the bare 6-digit RGB hex that OOXML's
5
+ * `<a:srgbClr val="...">` requires:
6
+ * - strips a leading `#`
7
+ * - accepts 8-digit ARGB (the form excelts uses for cell fills) and drops the
8
+ * leading alpha byte, since `srgbClr` carries no alpha channel
9
+ * - upper-cases
10
+ *
11
+ * Anything that isn't a 6- or 8-digit hex string is passed through unchanged so
12
+ * a caller using a less common form is not silently broken.
13
+ */
14
+ function normalizeColor(color) {
15
+ const hex = color.startsWith("#") ? color.slice(1) : color;
16
+ if (/^[0-9a-fA-F]{8}$/.test(hex)) {
17
+ // ARGB → RGB: drop the alpha byte (srgbClr has no alpha component).
18
+ return hex.slice(2).toUpperCase();
19
+ }
20
+ if (/^[0-9a-fA-F]{6}$/.test(hex)) {
21
+ return hex.toUpperCase();
22
+ }
23
+ return hex;
24
+ }
25
+ /**
26
+ * Renders a user-visible drawing shape. Geometry/position is governed by the
27
+ * enclosing anchor (`xfrm` is written as zero, matching how Excel anchors a
28
+ * shape to a cell range), while preset geometry, fill, outline and text are
29
+ * taken from the model. Write-only: shapes are not parsed back on read (the
30
+ * same limitation that already applies to all non-chart drawing content).
31
+ */
32
+ class ShapeXform extends BaseXform {
33
+ get tag() {
34
+ return "xdr:sp";
35
+ }
36
+ render(xmlStream, model) {
37
+ xmlStream.openNode("xdr:sp", { macro: "", textlink: "" });
38
+ // --- Non-visual shape properties ---
39
+ xmlStream.openNode("xdr:nvSpPr");
40
+ xmlStream.leafNode("xdr:cNvPr", { id: model.cNvPrId, name: model.name });
41
+ xmlStream.leafNode("xdr:cNvSpPr", {});
42
+ xmlStream.closeNode(); // xdr:nvSpPr
43
+ // --- Shape properties ---
44
+ xmlStream.openNode("xdr:spPr");
45
+ // Position/size is driven by the anchor; emit a zero xfrm placeholder.
46
+ xmlStream.openNode("a:xfrm");
47
+ xmlStream.leafNode("a:off", { x: 0, y: 0 });
48
+ xmlStream.leafNode("a:ext", { cx: 0, cy: 0 });
49
+ xmlStream.closeNode(); // a:xfrm
50
+ xmlStream.openNode("a:prstGeom", { prst: model.shapeType });
51
+ xmlStream.leafNode("a:avLst");
52
+ xmlStream.closeNode(); // a:prstGeom
53
+ // Fill: a colour produces a solidFill, otherwise an explicit noFill.
54
+ if (model.fill && model.fill.color) {
55
+ xmlStream.openNode("a:solidFill");
56
+ xmlStream.leafNode("a:srgbClr", { val: normalizeColor(model.fill.color) });
57
+ xmlStream.closeNode(); // a:solidFill
58
+ }
59
+ else {
60
+ xmlStream.leafNode("a:noFill");
61
+ }
62
+ // Line: an `a:ln` with width (pt → EMU) and/or a solid colour. When width
63
+ // is given without a colour, Excel applies its default outline colour at
64
+ // that width. When neither colour nor width is supplied, emit an explicit
65
+ // noFill line (no visible outline).
66
+ if (model.line && (model.line.color || model.line.width !== undefined)) {
67
+ const lnAttrs = {};
68
+ if (model.line.width !== undefined) {
69
+ lnAttrs.w = Math.round(model.line.width * EMU_PER_POINT);
70
+ }
71
+ xmlStream.openNode("a:ln", lnAttrs);
72
+ if (model.line.color) {
73
+ xmlStream.openNode("a:solidFill");
74
+ xmlStream.leafNode("a:srgbClr", { val: normalizeColor(model.line.color) });
75
+ xmlStream.closeNode(); // a:solidFill
76
+ }
77
+ xmlStream.closeNode(); // a:ln
78
+ }
79
+ else {
80
+ xmlStream.openNode("a:ln");
81
+ xmlStream.leafNode("a:noFill");
82
+ xmlStream.closeNode(); // a:ln
83
+ }
84
+ xmlStream.closeNode(); // xdr:spPr
85
+ // --- Text body ---
86
+ xmlStream.openNode("xdr:txBody");
87
+ xmlStream.leafNode("a:bodyPr", { vertOverflow: "clip", wrap: "square", anchor: "ctr" });
88
+ xmlStream.leafNode("a:lstStyle");
89
+ xmlStream.openNode("a:p");
90
+ xmlStream.openNode("a:pPr", { algn: "ctr" });
91
+ xmlStream.closeNode(); // a:pPr
92
+ if (model.text) {
93
+ xmlStream.openNode("a:r");
94
+ xmlStream.openNode("a:rPr", { lang: "en-US" });
95
+ xmlStream.closeNode(); // a:rPr
96
+ xmlStream.openNode("a:t");
97
+ xmlStream.writeText(model.text);
98
+ xmlStream.closeNode(); // a:t
99
+ xmlStream.closeNode(); // a:r
100
+ }
101
+ else {
102
+ xmlStream.leafNode("a:endParaRPr", { lang: "en-US" });
103
+ }
104
+ xmlStream.closeNode(); // a:p
105
+ xmlStream.closeNode(); // xdr:txBody
106
+ xmlStream.closeNode(); // xdr:sp
107
+ }
108
+ }
109
+ export { ShapeXform };
@@ -2,6 +2,7 @@ import { BaseCellAnchorXform } from "./base-cell-anchor-xform.js";
2
2
  import { CellPositionXform } from "./cell-position-xform.js";
3
3
  import { GraphicFrameXform } from "./graphic-frame-xform.js";
4
4
  import { PicXform } from "./pic-xform.js";
5
+ import { ShapeXform } from "./shape-xform.js";
5
6
  import { SpXform } from "./sp-xform.js";
6
7
  import { StaticXform } from "../static-xform.js";
7
8
  class TwoCellAnchorXform extends BaseCellAnchorXform {
@@ -24,6 +25,7 @@ class TwoCellAnchorXform extends BaseCellAnchorXform {
24
25
  "xdr:to": new CellPositionXform({ tag: "xdr:to" }),
25
26
  "xdr:pic": new PicXform(),
26
27
  "xdr:sp": new SpXform(),
28
+ "xdr:userShape": new ShapeXform(),
27
29
  "xdr:graphicFrame": new GraphicFrameXform(),
28
30
  "xdr:clientData": new StaticXform({ tag: "xdr:clientData" })
29
31
  };
@@ -125,7 +127,14 @@ class TwoCellAnchorXform extends BaseCellAnchorXform {
125
127
  this.map["xdr:graphicFrame"].render(xmlStream, model.graphicFrame);
126
128
  }
127
129
  else if (model.shape) {
128
- this.map["xdr:sp"].render(xmlStream, model.shape);
130
+ // A user-visible shape routes to the dedicated ShapeXform; the legacy
131
+ // form-control shape (no `kind`) stays on the SpXform path.
132
+ if (model.shape.kind === "userShape") {
133
+ this.map["xdr:userShape"].render(xmlStream, model.shape);
134
+ }
135
+ else {
136
+ this.map["xdr:sp"].render(xmlStream, model.shape);
137
+ }
129
138
  }
130
139
  this.map["xdr:clientData"].render(xmlStream, {});
131
140
  xmlStream.closeNode(); // xdr:twoCellAnchor
@@ -498,7 +498,70 @@ class WorkSheetXform extends BaseXform {
498
498
  });
499
499
  }
500
500
  }
501
- // Handle header watermark images VML header/footer image
501
+ // Handle user-drawn shapesanchored drawing parts with no media/rel.
502
+ const shapes = model.shapes ?? [];
503
+ if (shapes.length > 0) {
504
+ let { drawing } = model;
505
+ if (!drawing) {
506
+ drawing = model.drawing = {
507
+ rId: nextRid(rels),
508
+ name: `drawing${++options.drawingsCount}`,
509
+ anchors: [],
510
+ rels: []
511
+ };
512
+ options.drawings.push(drawing);
513
+ rels.push({
514
+ Id: drawing.rId,
515
+ Type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing",
516
+ Target: drawingRelTargetFromWorksheet(drawing.name)
517
+ });
518
+ }
519
+ for (const shape of shapes) {
520
+ const anchorRange = shape.anchorRange;
521
+ if (!anchorRange) {
522
+ continue;
523
+ }
524
+ // Mirror the three image anchoring modes. `getAnchorType` (drawing
525
+ // xform) dispatches on `pos`/`br`: absolute when `pos` is present,
526
+ // two-cell when `br` is present, one-cell otherwise (needs `ext`).
527
+ let range;
528
+ if (anchorRange.pos) {
529
+ range = { pos: anchorRange.pos, ext: anchorRange.ext, editAs: "absolute" };
530
+ }
531
+ else if (anchorRange.br) {
532
+ range = {
533
+ tl: anchorRange.tl,
534
+ br: anchorRange.br,
535
+ editAs: anchorRange.editAs ?? "oneCell"
536
+ };
537
+ }
538
+ else {
539
+ range = {
540
+ tl: anchorRange.tl,
541
+ ext: anchorRange.ext,
542
+ editAs: anchorRange.editAs ?? "oneCell"
543
+ };
544
+ }
545
+ // Allocate a cNvPr id from the same monotonic space as the anchor's
546
+ // position in the drawing so it never collides with image/chart ids
547
+ // (which derive from the anchor index).
548
+ const cNvPrId = drawing.anchors.length + 1;
549
+ drawing.anchors.push({
550
+ range,
551
+ shape: {
552
+ kind: "userShape",
553
+ cNvPrId,
554
+ name: shape.name ?? `Shape ${cNvPrId}`,
555
+ shapeType: shape.shapeType,
556
+ fill: shape.fillColor ? { color: shape.fillColor } : undefined,
557
+ line: shape.lineColor !== undefined || shape.lineWidth !== undefined
558
+ ? { color: shape.lineColor, width: shape.lineWidth }
559
+ : undefined,
560
+ text: shape.text
561
+ }
562
+ });
563
+ }
564
+ }
502
565
  if (headerImageMedia.length > 0) {
503
566
  const medium = headerImageMedia[0]; // Only one header image per sheet
504
567
  const bookImage = options.media[medium.imageId];
@@ -26,7 +26,7 @@ import { writePdfAMetadata, writePdfAOutputIntent } from "../core/pdfa.js";
26
26
  import { FontManager } from "../font/font-manager.js";
27
27
  import { iterateSystemFontCandidates } from "../font/system-fonts.js";
28
28
  import { parseTtf } from "../font/ttf-parser.js";
29
- import { wrapTextLines, emitTextWithMatrix, alphaGsName } from "../render/page-renderer.js";
29
+ import { emitTextBlock, alphaGsName } from "../render/page-renderer.js";
30
30
  import { writeImageXObject } from "./image-utils.js";
31
31
  // =============================================================================
32
32
  // Constants
@@ -95,56 +95,29 @@ export class PdfPageBuilder {
95
95
  const bold = options.bold ?? false;
96
96
  const italic = options.italic ?? false;
97
97
  const fontFamily = options.fontFamily ?? "Helvetica";
98
- // Resolve font
98
+ // Resolve the provisional Type1 resource and record the run's code
99
+ // points. The text is emitted as a deferred block so anchor alignment,
100
+ // word wrapping, and glyph encoding are all computed at build time —
101
+ // after fonts are finalised (a non-WinAnsi run may trigger a build-time
102
+ // auto-embed of a system CIDFont). Measuring against the provisional
103
+ // metrics here would misplace anchored text and break lines wrongly.
99
104
  const resourceName = this._fontManager.resolveFont(fontFamily, bold, italic);
100
105
  this._fontManager.trackText(text);
101
- const useType3 = this._fontManager.hasType3Fonts() && !this._fontManager.hasEmbeddedFont();
102
- // Resolve anchor into an adjusted x before any matrix math. We use the
103
- // same font/size combination the stream will render with, so the
104
- // alignment is correct for the actual glyphs (not a fallback
105
- // estimate). Single-line drawing only — wrapped text ignores anchor
106
- // because the wrapper re-splits by the caller's supplied x.
107
- const anchor = options.anchor ?? "start";
108
- const resolvedX = anchor === "start" || options.maxWidth
109
- ? options.x
110
- : options.x -
111
- this._fontManager.measureText(text, resourceName, fontSize) *
112
- (anchor === "middle" ? 0.5 : 1);
113
- if (options.maxWidth) {
114
- // Word-wrap (reuses the shared wrapTextLines from page-renderer)
115
- const measure = (s) => this._fontManager.measureText(s, resourceName, fontSize);
116
- const lines = wrapTextLines(text, measure, options.maxWidth);
117
- const leading = fontSize * lineHeightFactor;
118
- this._stream.save();
119
- this._applyAlpha(color.a);
120
- this._stream.setFillColor(color);
121
- for (let i = 0; i < lines.length; i++) {
122
- const lineY = options.y - i * leading;
123
- emitTextWithMatrix(this._stream, lines[i], 1, 0, 0, 1, options.x, lineY, resourceName, fontSize, this._fontManager, useType3);
124
- }
125
- this._stream.restore();
126
- }
127
- else {
128
- // Single line
129
- this._stream.save();
130
- this._applyAlpha(color.a);
131
- this._stream.setFillColor(color);
132
- const rotation = options.rotation ?? 0;
133
- if (rotation === 0) {
134
- emitTextWithMatrix(this._stream, text, 1, 0, 0, 1, resolvedX, options.y, resourceName, fontSize, this._fontManager, useType3);
135
- }
136
- else {
137
- // Build the rotation matrix around (x, y). `emitTextWithMatrix`
138
- // accepts the full 2×3 text matrix, so we pre-multiply the
139
- // rotation with the translation so one call positions and
140
- // rotates the glyph sequence in a single Tm op.
141
- const theta = (rotation * Math.PI) / 180;
142
- const cos = Math.cos(theta);
143
- const sin = Math.sin(theta);
144
- emitTextWithMatrix(this._stream, text, cos, sin, -sin, cos, resolvedX, options.y, resourceName, fontSize, this._fontManager, useType3);
145
- }
146
- this._stream.restore();
147
- }
106
+ this._stream.save();
107
+ this._applyAlpha(color.a);
108
+ this._stream.setFillColor(color);
109
+ emitTextBlock(this._stream, {
110
+ text,
111
+ x: options.x,
112
+ y: options.y,
113
+ type1ResourceName: resourceName,
114
+ fontSize,
115
+ anchor: options.anchor ?? "start",
116
+ maxWidth: options.maxWidth,
117
+ lineHeightFactor,
118
+ rotation: options.rotation ?? 0
119
+ }, this._fontManager);
120
+ this._stream.restore();
148
121
  return this;
149
122
  }
150
123
  /**