@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
@@ -6,6 +6,7 @@ const base_cell_anchor_xform_1 = require("./base-cell-anchor-xform.js");
6
6
  const ext_xform_1 = require("./ext-xform.js");
7
7
  const graphic_frame_xform_1 = require("./graphic-frame-xform.js");
8
8
  const pic_xform_1 = require("./pic-xform.js");
9
+ const shape_xform_1 = require("./shape-xform.js");
9
10
  const static_xform_1 = require("../static-xform.js");
10
11
  /** https://en.wikipedia.org/wiki/Office_Open_XML_file_formats#DrawingML */
11
12
  const EMU_PER_PIXEL_AT_96_DPI = 9525;
@@ -73,6 +74,7 @@ class AbsoluteAnchorXform extends base_cell_anchor_xform_1.BaseCellAnchorXform {
73
74
  // `<xdr:absoluteAnchor><xdr:pos/><xdr:ext/><xdr:clientData/></xdr:absoluteAnchor>`
74
75
  // with no chart reference, so the anchor was ignored on open.
75
76
  "xdr:graphicFrame": new graphic_frame_xform_1.GraphicFrameXform(),
77
+ "xdr:userShape": new shape_xform_1.ShapeXform(),
76
78
  "xdr:clientData": new static_xform_1.StaticXform({ tag: "xdr:clientData" })
77
79
  };
78
80
  }
@@ -97,6 +99,9 @@ class AbsoluteAnchorXform extends base_cell_anchor_xform_1.BaseCellAnchorXform {
97
99
  else if (model.graphicFrame) {
98
100
  this.map["xdr:graphicFrame"].render(xmlStream, model.graphicFrame);
99
101
  }
102
+ else if (model.shape?.kind === "userShape") {
103
+ this.map["xdr:userShape"].render(xmlStream, model.shape);
104
+ }
100
105
  this.map["xdr:clientData"].render(xmlStream, {});
101
106
  xmlStream.closeNode();
102
107
  }
@@ -50,8 +50,25 @@ class BaseCellAnchorXform extends base_xform_1.BaseXform {
50
50
  const name = match[1];
51
51
  const mediaId = options.mediaIndex[name];
52
52
  const medium = options.media[mediaId];
53
+ if (!medium) {
54
+ return undefined;
55
+ }
56
+ // Resolve an SVG companion (asvg:svgBlip extension) back to its media
57
+ // index and record it on the raster media entry itself, so callers that
58
+ // look the image up by id (e.g. Workbook.getImage) surface the vector
59
+ // companion alongside the raster fallback.
60
+ if (model.svgRId) {
61
+ const svgRel = options.rels[model.svgRId];
62
+ const svgMatch = svgRel && svgRel.Target.match(/.*\/media\/(.+[.][a-zA-Z]{3,4})/);
63
+ if (svgMatch) {
64
+ const svgMediaId = options.mediaIndex[svgMatch[1]];
65
+ if (svgMediaId !== undefined) {
66
+ medium.svgMediaId = svgMediaId;
67
+ }
68
+ }
69
+ }
53
70
  // Preserve alphaModFix (transparency) from the picture model if present
54
- if (medium && model.alphaModFix !== undefined) {
71
+ if (model.alphaModFix !== undefined) {
55
72
  return { ...medium, alphaModFix: model.alphaModFix };
56
73
  }
57
74
  return medium;
@@ -2,6 +2,11 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.BlipXform = void 0;
4
4
  const base_xform_1 = require("../base-xform.js");
5
+ /** OOXML extension URI for the SVG blip (Office 2016 SVG feature). */
6
+ const SVG_BLIP_EXT_URI = "{96DAC541-7B7A-43D3-8B79-37D633B846F1}";
7
+ /** Namespace for the asvg:svgBlip element. */
8
+ const SVG_BLIP_NS = "http://schemas.microsoft.com/office/drawing/2016/SVG/main";
9
+ const REL_NS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
5
10
  class BlipXform extends base_xform_1.BaseXform {
6
11
  constructor() {
7
12
  super();
@@ -13,23 +18,37 @@ class BlipXform extends base_xform_1.BaseXform {
13
18
  render(xmlStream, model) {
14
19
  // External (linked) images use `r:link`; embedded images use `r:embed`.
15
20
  const relAttr = model.external ? "r:link" : "r:embed";
16
- if (model.alphaModFix !== undefined && model.alphaModFix < 100000) {
17
- // Render as open/close node with a:alphaModFix child
18
- xmlStream.openNode(this.tag, {
19
- "xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
21
+ const hasAlpha = model.alphaModFix !== undefined && model.alphaModFix < 100000;
22
+ const hasSvg = model.svgRId !== undefined;
23
+ // A bare blip can be a leaf node; an alpha modulation or an SVG extension
24
+ // both require child elements, so switch to the open/close form.
25
+ if (!hasAlpha && !hasSvg) {
26
+ xmlStream.leafNode(this.tag, {
27
+ "xmlns:r": REL_NS,
20
28
  [relAttr]: model.rId,
21
29
  cstate: "print"
22
30
  });
31
+ return;
32
+ }
33
+ xmlStream.openNode(this.tag, {
34
+ "xmlns:r": REL_NS,
35
+ [relAttr]: model.rId,
36
+ cstate: "print"
37
+ });
38
+ if (hasAlpha) {
23
39
  xmlStream.leafNode("a:alphaModFix", { amt: String(model.alphaModFix) });
24
- xmlStream.closeNode();
25
40
  }
26
- else {
27
- xmlStream.leafNode(this.tag, {
28
- "xmlns:r": "http://schemas.openxmlformats.org/officeDocument/2006/relationships",
29
- [relAttr]: model.rId,
30
- cstate: "print"
41
+ if (hasSvg) {
42
+ xmlStream.openNode("a:extLst");
43
+ xmlStream.openNode("a:ext", { uri: SVG_BLIP_EXT_URI });
44
+ xmlStream.leafNode("asvg:svgBlip", {
45
+ "xmlns:asvg": SVG_BLIP_NS,
46
+ "r:embed": model.svgRId
31
47
  });
48
+ xmlStream.closeNode(); // a:ext
49
+ xmlStream.closeNode(); // a:extLst
32
50
  }
51
+ xmlStream.closeNode(); // a:blip
33
52
  }
34
53
  parseOpen(node) {
35
54
  switch (node.name) {
@@ -49,6 +68,14 @@ class BlipXform extends base_xform_1.BaseXform {
49
68
  this.model.alphaModFix = parseInt(node.attributes.amt, 10);
50
69
  }
51
70
  return true;
71
+ case "asvg:svgBlip": {
72
+ // Capture the SVG companion's relationship id for round-trip.
73
+ const embed = node.attributes["r:embed"];
74
+ if (embed !== undefined && this.model) {
75
+ this.model.svgRId = embed;
76
+ }
77
+ return true;
78
+ }
52
79
  default:
53
80
  return true;
54
81
  }
@@ -59,7 +86,7 @@ class BlipXform extends base_xform_1.BaseXform {
59
86
  case this.tag:
60
87
  return false;
61
88
  default:
62
- // unprocessed internal nodes
89
+ // unprocessed internal nodes (a:extLst / a:ext / alphaModFix)
63
90
  return true;
64
91
  }
65
92
  }
@@ -6,6 +6,7 @@ const cell_position_xform_1 = require("./cell-position-xform.js");
6
6
  const ext_xform_1 = require("./ext-xform.js");
7
7
  const graphic_frame_xform_1 = require("./graphic-frame-xform.js");
8
8
  const pic_xform_1 = require("./pic-xform.js");
9
+ const shape_xform_1 = require("./shape-xform.js");
9
10
  const static_xform_1 = require("../static-xform.js");
10
11
  class OneCellAnchorXform extends base_cell_anchor_xform_1.BaseCellAnchorXform {
11
12
  constructor() {
@@ -14,6 +15,7 @@ class OneCellAnchorXform extends base_cell_anchor_xform_1.BaseCellAnchorXform {
14
15
  "xdr:from": new cell_position_xform_1.CellPositionXform({ tag: "xdr:from" }),
15
16
  "xdr:ext": new ext_xform_1.ExtXform({ tag: "xdr:ext" }),
16
17
  "xdr:pic": new pic_xform_1.PicXform(),
18
+ "xdr:userShape": new shape_xform_1.ShapeXform(),
17
19
  "xdr:graphicFrame": new graphic_frame_xform_1.GraphicFrameXform(),
18
20
  "xdr:clientData": new static_xform_1.StaticXform({ tag: "xdr:clientData" })
19
21
  };
@@ -39,6 +41,9 @@ class OneCellAnchorXform extends base_cell_anchor_xform_1.BaseCellAnchorXform {
39
41
  else if (model.graphicFrame) {
40
42
  this.map["xdr:graphicFrame"].render(xmlStream, model.graphicFrame);
41
43
  }
44
+ else if (model.shape?.kind === "userShape") {
45
+ this.map["xdr:userShape"].render(xmlStream, model.shape);
46
+ }
42
47
  this.map["xdr:clientData"].render(xmlStream, {});
43
48
  xmlStream.closeNode();
44
49
  }
@@ -28,7 +28,8 @@ class PicXform extends base_xform_1.BaseXform {
28
28
  this.map["xdr:blipFill"].render(xmlStream, {
29
29
  rId: model.rId,
30
30
  alphaModFix: model.alphaModFix,
31
- external: model.external
31
+ external: model.external,
32
+ svgRId: model.svgRId
32
33
  });
33
34
  this.map["xdr:spPr"].render(xmlStream, model);
34
35
  xmlStream.closeNode();
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ShapeXform = void 0;
4
+ const base_xform_1 = require("../base-xform.js");
5
+ const EMU_PER_POINT = 12700;
6
+ /**
7
+ * Normalize a user-supplied colour to the bare 6-digit RGB hex that OOXML's
8
+ * `<a:srgbClr val="...">` requires:
9
+ * - strips a leading `#`
10
+ * - accepts 8-digit ARGB (the form excelts uses for cell fills) and drops the
11
+ * leading alpha byte, since `srgbClr` carries no alpha channel
12
+ * - upper-cases
13
+ *
14
+ * Anything that isn't a 6- or 8-digit hex string is passed through unchanged so
15
+ * a caller using a less common form is not silently broken.
16
+ */
17
+ function normalizeColor(color) {
18
+ const hex = color.startsWith("#") ? color.slice(1) : color;
19
+ if (/^[0-9a-fA-F]{8}$/.test(hex)) {
20
+ // ARGB → RGB: drop the alpha byte (srgbClr has no alpha component).
21
+ return hex.slice(2).toUpperCase();
22
+ }
23
+ if (/^[0-9a-fA-F]{6}$/.test(hex)) {
24
+ return hex.toUpperCase();
25
+ }
26
+ return hex;
27
+ }
28
+ /**
29
+ * Renders a user-visible drawing shape. Geometry/position is governed by the
30
+ * enclosing anchor (`xfrm` is written as zero, matching how Excel anchors a
31
+ * shape to a cell range), while preset geometry, fill, outline and text are
32
+ * taken from the model. Write-only: shapes are not parsed back on read (the
33
+ * same limitation that already applies to all non-chart drawing content).
34
+ */
35
+ class ShapeXform extends base_xform_1.BaseXform {
36
+ get tag() {
37
+ return "xdr:sp";
38
+ }
39
+ render(xmlStream, model) {
40
+ xmlStream.openNode("xdr:sp", { macro: "", textlink: "" });
41
+ // --- Non-visual shape properties ---
42
+ xmlStream.openNode("xdr:nvSpPr");
43
+ xmlStream.leafNode("xdr:cNvPr", { id: model.cNvPrId, name: model.name });
44
+ xmlStream.leafNode("xdr:cNvSpPr", {});
45
+ xmlStream.closeNode(); // xdr:nvSpPr
46
+ // --- Shape properties ---
47
+ xmlStream.openNode("xdr:spPr");
48
+ // Position/size is driven by the anchor; emit a zero xfrm placeholder.
49
+ xmlStream.openNode("a:xfrm");
50
+ xmlStream.leafNode("a:off", { x: 0, y: 0 });
51
+ xmlStream.leafNode("a:ext", { cx: 0, cy: 0 });
52
+ xmlStream.closeNode(); // a:xfrm
53
+ xmlStream.openNode("a:prstGeom", { prst: model.shapeType });
54
+ xmlStream.leafNode("a:avLst");
55
+ xmlStream.closeNode(); // a:prstGeom
56
+ // Fill: a colour produces a solidFill, otherwise an explicit noFill.
57
+ if (model.fill && model.fill.color) {
58
+ xmlStream.openNode("a:solidFill");
59
+ xmlStream.leafNode("a:srgbClr", { val: normalizeColor(model.fill.color) });
60
+ xmlStream.closeNode(); // a:solidFill
61
+ }
62
+ else {
63
+ xmlStream.leafNode("a:noFill");
64
+ }
65
+ // Line: an `a:ln` with width (pt → EMU) and/or a solid colour. When width
66
+ // is given without a colour, Excel applies its default outline colour at
67
+ // that width. When neither colour nor width is supplied, emit an explicit
68
+ // noFill line (no visible outline).
69
+ if (model.line && (model.line.color || model.line.width !== undefined)) {
70
+ const lnAttrs = {};
71
+ if (model.line.width !== undefined) {
72
+ lnAttrs.w = Math.round(model.line.width * EMU_PER_POINT);
73
+ }
74
+ xmlStream.openNode("a:ln", lnAttrs);
75
+ if (model.line.color) {
76
+ xmlStream.openNode("a:solidFill");
77
+ xmlStream.leafNode("a:srgbClr", { val: normalizeColor(model.line.color) });
78
+ xmlStream.closeNode(); // a:solidFill
79
+ }
80
+ xmlStream.closeNode(); // a:ln
81
+ }
82
+ else {
83
+ xmlStream.openNode("a:ln");
84
+ xmlStream.leafNode("a:noFill");
85
+ xmlStream.closeNode(); // a:ln
86
+ }
87
+ xmlStream.closeNode(); // xdr:spPr
88
+ // --- Text body ---
89
+ xmlStream.openNode("xdr:txBody");
90
+ xmlStream.leafNode("a:bodyPr", { vertOverflow: "clip", wrap: "square", anchor: "ctr" });
91
+ xmlStream.leafNode("a:lstStyle");
92
+ xmlStream.openNode("a:p");
93
+ xmlStream.openNode("a:pPr", { algn: "ctr" });
94
+ xmlStream.closeNode(); // a:pPr
95
+ if (model.text) {
96
+ xmlStream.openNode("a:r");
97
+ xmlStream.openNode("a:rPr", { lang: "en-US" });
98
+ xmlStream.closeNode(); // a:rPr
99
+ xmlStream.openNode("a:t");
100
+ xmlStream.writeText(model.text);
101
+ xmlStream.closeNode(); // a:t
102
+ xmlStream.closeNode(); // a:r
103
+ }
104
+ else {
105
+ xmlStream.leafNode("a:endParaRPr", { lang: "en-US" });
106
+ }
107
+ xmlStream.closeNode(); // a:p
108
+ xmlStream.closeNode(); // xdr:txBody
109
+ xmlStream.closeNode(); // xdr:sp
110
+ }
111
+ }
112
+ exports.ShapeXform = ShapeXform;
@@ -5,6 +5,7 @@ const base_cell_anchor_xform_1 = require("./base-cell-anchor-xform.js");
5
5
  const cell_position_xform_1 = require("./cell-position-xform.js");
6
6
  const graphic_frame_xform_1 = require("./graphic-frame-xform.js");
7
7
  const pic_xform_1 = require("./pic-xform.js");
8
+ const shape_xform_1 = require("./shape-xform.js");
8
9
  const sp_xform_1 = require("./sp-xform.js");
9
10
  const static_xform_1 = require("../static-xform.js");
10
11
  class TwoCellAnchorXform extends base_cell_anchor_xform_1.BaseCellAnchorXform {
@@ -27,6 +28,7 @@ class TwoCellAnchorXform extends base_cell_anchor_xform_1.BaseCellAnchorXform {
27
28
  "xdr:to": new cell_position_xform_1.CellPositionXform({ tag: "xdr:to" }),
28
29
  "xdr:pic": new pic_xform_1.PicXform(),
29
30
  "xdr:sp": new sp_xform_1.SpXform(),
31
+ "xdr:userShape": new shape_xform_1.ShapeXform(),
30
32
  "xdr:graphicFrame": new graphic_frame_xform_1.GraphicFrameXform(),
31
33
  "xdr:clientData": new static_xform_1.StaticXform({ tag: "xdr:clientData" })
32
34
  };
@@ -128,7 +130,14 @@ class TwoCellAnchorXform extends base_cell_anchor_xform_1.BaseCellAnchorXform {
128
130
  this.map["xdr:graphicFrame"].render(xmlStream, model.graphicFrame);
129
131
  }
130
132
  else if (model.shape) {
131
- this.map["xdr:sp"].render(xmlStream, model.shape);
133
+ // A user-visible shape routes to the dedicated ShapeXform; the legacy
134
+ // form-control shape (no `kind`) stays on the SpXform path.
135
+ if (model.shape.kind === "userShape") {
136
+ this.map["xdr:userShape"].render(xmlStream, model.shape);
137
+ }
138
+ else {
139
+ this.map["xdr:sp"].render(xmlStream, model.shape);
140
+ }
132
141
  }
133
142
  this.map["xdr:clientData"].render(xmlStream, {});
134
143
  xmlStream.closeNode(); // xdr:twoCellAnchor
@@ -501,7 +501,70 @@ class WorkSheetXform extends base_xform_1.BaseXform {
501
501
  });
502
502
  }
503
503
  }
504
- // Handle header watermark images VML header/footer image
504
+ // Handle user-drawn shapesanchored drawing parts with no media/rel.
505
+ const shapes = model.shapes ?? [];
506
+ if (shapes.length > 0) {
507
+ let { drawing } = model;
508
+ if (!drawing) {
509
+ drawing = model.drawing = {
510
+ rId: nextRid(rels),
511
+ name: `drawing${++options.drawingsCount}`,
512
+ anchors: [],
513
+ rels: []
514
+ };
515
+ options.drawings.push(drawing);
516
+ rels.push({
517
+ Id: drawing.rId,
518
+ Type: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing",
519
+ Target: (0, ooxml_paths_1.drawingRelTargetFromWorksheet)(drawing.name)
520
+ });
521
+ }
522
+ for (const shape of shapes) {
523
+ const anchorRange = shape.anchorRange;
524
+ if (!anchorRange) {
525
+ continue;
526
+ }
527
+ // Mirror the three image anchoring modes. `getAnchorType` (drawing
528
+ // xform) dispatches on `pos`/`br`: absolute when `pos` is present,
529
+ // two-cell when `br` is present, one-cell otherwise (needs `ext`).
530
+ let range;
531
+ if (anchorRange.pos) {
532
+ range = { pos: anchorRange.pos, ext: anchorRange.ext, editAs: "absolute" };
533
+ }
534
+ else if (anchorRange.br) {
535
+ range = {
536
+ tl: anchorRange.tl,
537
+ br: anchorRange.br,
538
+ editAs: anchorRange.editAs ?? "oneCell"
539
+ };
540
+ }
541
+ else {
542
+ range = {
543
+ tl: anchorRange.tl,
544
+ ext: anchorRange.ext,
545
+ editAs: anchorRange.editAs ?? "oneCell"
546
+ };
547
+ }
548
+ // Allocate a cNvPr id from the same monotonic space as the anchor's
549
+ // position in the drawing so it never collides with image/chart ids
550
+ // (which derive from the anchor index).
551
+ const cNvPrId = drawing.anchors.length + 1;
552
+ drawing.anchors.push({
553
+ range,
554
+ shape: {
555
+ kind: "userShape",
556
+ cNvPrId,
557
+ name: shape.name ?? `Shape ${cNvPrId}`,
558
+ shapeType: shape.shapeType,
559
+ fill: shape.fillColor ? { color: shape.fillColor } : undefined,
560
+ line: shape.lineColor !== undefined || shape.lineWidth !== undefined
561
+ ? { color: shape.lineColor, width: shape.lineWidth }
562
+ : undefined,
563
+ text: shape.text
564
+ }
565
+ });
566
+ }
567
+ }
505
568
  if (headerImageMedia.length > 0) {
506
569
  const medium = headerImageMedia[0]; // Only one header image per sheet
507
570
  const bookImage = options.media[medium.imageId];
@@ -132,56 +132,29 @@ class PdfPageBuilder {
132
132
  const bold = options.bold ?? false;
133
133
  const italic = options.italic ?? false;
134
134
  const fontFamily = options.fontFamily ?? "Helvetica";
135
- // Resolve font
135
+ // Resolve the provisional Type1 resource and record the run's code
136
+ // points. The text is emitted as a deferred block so anchor alignment,
137
+ // word wrapping, and glyph encoding are all computed at build time —
138
+ // after fonts are finalised (a non-WinAnsi run may trigger a build-time
139
+ // auto-embed of a system CIDFont). Measuring against the provisional
140
+ // metrics here would misplace anchored text and break lines wrongly.
136
141
  const resourceName = this._fontManager.resolveFont(fontFamily, bold, italic);
137
142
  this._fontManager.trackText(text);
138
- const useType3 = this._fontManager.hasType3Fonts() && !this._fontManager.hasEmbeddedFont();
139
- // Resolve anchor into an adjusted x before any matrix math. We use the
140
- // same font/size combination the stream will render with, so the
141
- // alignment is correct for the actual glyphs (not a fallback
142
- // estimate). Single-line drawing only — wrapped text ignores anchor
143
- // because the wrapper re-splits by the caller's supplied x.
144
- const anchor = options.anchor ?? "start";
145
- const resolvedX = anchor === "start" || options.maxWidth
146
- ? options.x
147
- : options.x -
148
- this._fontManager.measureText(text, resourceName, fontSize) *
149
- (anchor === "middle" ? 0.5 : 1);
150
- if (options.maxWidth) {
151
- // Word-wrap (reuses the shared wrapTextLines from page-renderer)
152
- const measure = (s) => this._fontManager.measureText(s, resourceName, fontSize);
153
- const lines = (0, page_renderer_1.wrapTextLines)(text, measure, options.maxWidth);
154
- const leading = fontSize * lineHeightFactor;
155
- this._stream.save();
156
- this._applyAlpha(color.a);
157
- this._stream.setFillColor(color);
158
- for (let i = 0; i < lines.length; i++) {
159
- const lineY = options.y - i * leading;
160
- (0, page_renderer_1.emitTextWithMatrix)(this._stream, lines[i], 1, 0, 0, 1, options.x, lineY, resourceName, fontSize, this._fontManager, useType3);
161
- }
162
- this._stream.restore();
163
- }
164
- else {
165
- // Single line
166
- this._stream.save();
167
- this._applyAlpha(color.a);
168
- this._stream.setFillColor(color);
169
- const rotation = options.rotation ?? 0;
170
- if (rotation === 0) {
171
- (0, page_renderer_1.emitTextWithMatrix)(this._stream, text, 1, 0, 0, 1, resolvedX, options.y, resourceName, fontSize, this._fontManager, useType3);
172
- }
173
- else {
174
- // Build the rotation matrix around (x, y). `emitTextWithMatrix`
175
- // accepts the full 2×3 text matrix, so we pre-multiply the
176
- // rotation with the translation so one call positions and
177
- // rotates the glyph sequence in a single Tm op.
178
- const theta = (rotation * Math.PI) / 180;
179
- const cos = Math.cos(theta);
180
- const sin = Math.sin(theta);
181
- (0, page_renderer_1.emitTextWithMatrix)(this._stream, text, cos, sin, -sin, cos, resolvedX, options.y, resourceName, fontSize, this._fontManager, useType3);
182
- }
183
- this._stream.restore();
184
- }
143
+ this._stream.save();
144
+ this._applyAlpha(color.a);
145
+ this._stream.setFillColor(color);
146
+ (0, page_renderer_1.emitTextBlock)(this._stream, {
147
+ text,
148
+ x: options.x,
149
+ y: options.y,
150
+ type1ResourceName: resourceName,
151
+ fontSize,
152
+ anchor: options.anchor ?? "start",
153
+ maxWidth: options.maxWidth,
154
+ lineHeightFactor,
155
+ rotation: options.rotation ?? 0
156
+ }, this._fontManager);
157
+ this._stream.restore();
185
158
  return this;
186
159
  }
187
160
  /**
@@ -180,7 +180,7 @@ class PdfEditorPage {
180
180
  }
181
181
  /** @internal */
182
182
  _hasOverlay() {
183
- return (this._overlay._stream.toString().length > 0 ||
183
+ return (this._overlay._stream.hasContent() ||
184
184
  this._overlay._images.length > 0 ||
185
185
  this._overlay._builderAnnotations.length > 0 ||
186
186
  this._overlay._formFields.length > 0);
@@ -28,6 +28,16 @@ const pdf_object_1 = require("./pdf-object");
28
28
  */
29
29
  class PdfContentStream {
30
30
  constructor() {
31
+ /**
32
+ * Content stream fragments in draw order. Most entries are plain operator
33
+ * strings produced eagerly. A function entry is a *deferred* fragment:
34
+ * its body is only evaluated at serialization time. This is required for
35
+ * text whose final byte encoding depends on font decisions (embedded
36
+ * CIDFont vs. Type1/WinAnsi vs. Type3 fallback) that are not finalised
37
+ * until `PdfDocumentBuilder.build()`. Deferring keeps the fragment at its
38
+ * exact draw-order position (preserving z-order) while letting the actual
39
+ * encoding run after the font manager's state is settled.
40
+ */
31
41
  this.parts = [];
32
42
  }
33
43
  // ===========================================================================
@@ -41,6 +51,17 @@ class PdfContentStream {
41
51
  this.parts.push(operator);
42
52
  return this;
43
53
  }
54
+ /**
55
+ * Append a deferred fragment whose body is evaluated only at serialization
56
+ * time. Used by text drawing so the final byte encoding can be chosen after
57
+ * the document's fonts are resolved at build time. The fragment occupies its
58
+ * draw-order slot immediately, so z-order relative to other operators is
59
+ * preserved.
60
+ */
61
+ deferred(produce) {
62
+ this.parts.push(produce);
63
+ return this;
64
+ }
44
65
  // ===========================================================================
45
66
  // Graphics State
46
67
  // ===========================================================================
@@ -440,10 +461,25 @@ class PdfContentStream {
440
461
  // Serialization
441
462
  // ===========================================================================
442
463
  /**
443
- * Get the content stream as a string.
464
+ * Whether any fragment has been appended. Unlike `toString().length > 0`,
465
+ * this does NOT evaluate deferred fragments, so it is safe to call before
466
+ * fonts are resolved (e.g. when probing for overlay content during an
467
+ * editor save, prior to `writeFontResources`). A deferred text fragment
468
+ * counts as content even though its bytes are not produced yet.
469
+ */
470
+ hasContent() {
471
+ return this.parts.length > 0;
472
+ }
473
+ /**
474
+ * Get the content stream as a string. Deferred fragments (see `deferred`)
475
+ * are evaluated here, after font resolution has completed at build time.
444
476
  */
445
477
  toString() {
446
- return this.parts.join("\n");
478
+ const out = [];
479
+ for (const part of this.parts) {
480
+ out.push(typeof part === "function" ? part() : part);
481
+ }
482
+ return out.join("\n");
447
483
  }
448
484
  /**
449
485
  * Get the content stream as a Uint8Array (UTF-8 encoded).
@@ -186,30 +186,47 @@ class FontManager {
186
186
  getEmbeddedResourceName() {
187
187
  return this.embeddedResourceName;
188
188
  }
189
+ /**
190
+ * Resolve the resource name a draw-time-resolved Type1 resource should
191
+ * actually render (and be measured) with, given the font manager's
192
+ * *current* state. If an embedded font exists (possibly auto-discovered
193
+ * at build time, after the text was drawn against a Type1 resource), the
194
+ * embedded resource name is returned so both measurement and encoding go
195
+ * through the CIDFont. Otherwise the original Type1 resource name is kept;
196
+ * `measureText` handles Type3-fallback widths internally from that name.
197
+ *
198
+ * Centralises the routing rule shared by the deferred text renderer and
199
+ * any deferred measurement (anchor alignment, word wrapping) so the two
200
+ * never disagree.
201
+ */
202
+ resolveRenderResourceName(type1ResourceName) {
203
+ return this.embeddedFont ? this.embeddedResourceName : type1ResourceName;
204
+ }
189
205
  /**
190
206
  * Record that a text string will be rendered, tracking its code points.
191
207
  * Must be called for every text string before writing the PDF.
208
+ *
209
+ * Two sets are maintained because font selection may be decided *after*
210
+ * drawing (e.g. `PdfDocumentBuilder.build()` auto-discovers and embeds a
211
+ * system font once it sees the accumulated non-WinAnsi code points):
212
+ *
213
+ * - `usedCodePoints` — every code point seen, always. If an embedded
214
+ * font ends up being used (whether registered up front or
215
+ * auto-discovered at build time), the subset must cover all of these,
216
+ * including plain ASCII, so the CIDFont can encode the full run.
217
+ * - `type3CodePoints` — non-WinAnsi code points only. Drives the
218
+ * build-time decision to auto-embed a system font, and the Type3
219
+ * fallback when none is available.
192
220
  */
193
221
  trackText(text) {
194
- if (this.embeddedFont) {
195
- for (let i = 0; i < text.length; i++) {
196
- const cp = text.codePointAt(i);
197
- this.usedCodePoints.add(cp);
198
- if (cp > 0xffff) {
199
- i++; // skip low surrogate
200
- }
222
+ for (let i = 0; i < text.length; i++) {
223
+ const cp = text.codePointAt(i);
224
+ if (cp > 0xffff) {
225
+ i++; // skip low surrogate
201
226
  }
202
- }
203
- else {
204
- // No embedded font — track non-WinAnsi chars for Type3 fallback
205
- for (let i = 0; i < text.length; i++) {
206
- const cp = text.codePointAt(i);
207
- if (cp > 0xffff) {
208
- i++;
209
- }
210
- if (!(0, pdf_stream_1.isWinAnsiCodePoint)(cp)) {
211
- this.type3CodePoints.add(cp);
212
- }
227
+ this.usedCodePoints.add(cp);
228
+ if (!(0, pdf_stream_1.isWinAnsiCodePoint)(cp)) {
229
+ this.type3CodePoints.add(cp);
213
230
  }
214
231
  }
215
232
  }