@shbernal/ts-xlsx 3.0.0 → 3.2.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 (277) hide show
  1. package/README.md +34 -24
  2. package/dist/bytes.d.ts +10 -2
  3. package/dist/bytes.js +22 -0
  4. package/dist/core/address.d.ts +96 -2
  5. package/dist/core/address.js +87 -33
  6. package/dist/core/array-formula-ranges.d.ts +33 -0
  7. package/dist/core/array-formula-ranges.js +62 -0
  8. package/dist/core/autofilter.d.ts +4 -3
  9. package/dist/core/autofilter.js +31 -30
  10. package/dist/core/axis-handle.d.ts +2 -0
  11. package/dist/core/axis-handle.js +5 -2
  12. package/dist/core/cell.d.ts +4 -3
  13. package/dist/core/cell.js +4 -6
  14. package/dist/core/clone.d.ts +29 -0
  15. package/dist/core/clone.js +24 -0
  16. package/dist/core/color-resolution.js +4 -6
  17. package/dist/core/column.d.ts +1 -0
  18. package/dist/core/column.js +6 -2
  19. package/dist/core/comment-thread.d.ts +7 -3
  20. package/dist/core/comment-thread.js +2 -1
  21. package/dist/core/conditional-formatting-overlay.d.ts +11 -1
  22. package/dist/core/conditional-formatting-overlay.js +30 -3
  23. package/dist/core/conditional-formatting.d.ts +107 -11
  24. package/dist/core/conditional-formatting.js +76 -19
  25. package/dist/core/containers.d.ts +12 -0
  26. package/dist/core/containers.js +5 -0
  27. package/dist/core/data-validation-overlay.d.ts +7 -1
  28. package/dist/core/data-validation-overlay.js +19 -4
  29. package/dist/core/data-validation.d.ts +22 -0
  30. package/dist/core/data-validation.js +30 -4
  31. package/dist/core/date-format.d.ts +20 -0
  32. package/dist/core/date-format.js +170 -0
  33. package/dist/core/date.d.ts +43 -8
  34. package/dist/core/date.js +19 -3
  35. package/dist/core/formula-references.d.ts +26 -0
  36. package/dist/core/formula-references.js +173 -0
  37. package/dist/core/formula-scan.d.ts +2 -0
  38. package/dist/core/formula-scan.js +58 -0
  39. package/dist/core/formula.d.ts +76 -17
  40. package/dist/core/formula.js +87 -80
  41. package/dist/core/function-values.d.ts +4 -0
  42. package/dist/core/function-values.js +58 -0
  43. package/dist/core/future-functions.d.ts +3 -0
  44. package/dist/core/{modern-functions.js → future-functions.js} +119 -88
  45. package/dist/core/grid-edits.d.ts +30 -3
  46. package/dist/core/grid-edits.js +248 -81
  47. package/dist/core/grid-shift.d.ts +58 -9
  48. package/dist/core/grid-shift.js +26 -4
  49. package/dist/core/hyperlink.d.ts +39 -0
  50. package/dist/core/hyperlink.js +88 -0
  51. package/dist/core/image.d.ts +49 -14
  52. package/dist/core/image.js +16 -5
  53. package/dist/core/internal.d.ts +10 -2
  54. package/dist/core/limits.d.ts +25 -0
  55. package/dist/core/limits.js +4 -0
  56. package/dist/core/merge.d.ts +18 -4
  57. package/dist/core/merge.js +33 -20
  58. package/dist/core/page-setup.d.ts +26 -11
  59. package/dist/core/pivot-table.d.ts +49 -10
  60. package/dist/core/pivot-table.js +112 -60
  61. package/dist/core/protection.d.ts +66 -4
  62. package/dist/core/protection.js +6 -11
  63. package/dist/core/range.js +34 -30
  64. package/dist/core/row-input.js +2 -2
  65. package/dist/core/row.d.ts +1 -0
  66. package/dist/core/row.js +6 -2
  67. package/dist/core/structured-reference.d.ts +7 -0
  68. package/dist/core/structured-reference.js +7 -0
  69. package/dist/core/style.d.ts +85 -70
  70. package/dist/core/style.js +92 -28
  71. package/dist/core/table-style.d.ts +1 -1
  72. package/dist/core/table-style.js +4 -4
  73. package/dist/core/table.d.ts +64 -16
  74. package/dist/core/table.js +87 -40
  75. package/dist/core/theme.d.ts +2 -0
  76. package/dist/core/theme.js +9 -5
  77. package/dist/core/value.d.ts +60 -28
  78. package/dist/core/value.js +48 -43
  79. package/dist/core/workbook-media.d.ts +34 -0
  80. package/dist/core/workbook-media.js +44 -0
  81. package/dist/core/workbook-styles.d.ts +83 -1
  82. package/dist/core/workbook-styles.js +4 -0
  83. package/dist/core/workbook-theme.js +5 -3
  84. package/dist/core/workbook-vba.js +3 -2
  85. package/dist/core/workbook.d.ts +41 -3
  86. package/dist/core/workbook.js +46 -41
  87. package/dist/core/worksheet-comments.d.ts +2 -1
  88. package/dist/core/worksheet-comments.js +20 -13
  89. package/dist/core/worksheet-merges.d.ts +49 -0
  90. package/dist/core/worksheet-merges.js +65 -0
  91. package/dist/core/worksheet-model.d.ts +10 -2
  92. package/dist/core/worksheet-model.js +28 -9
  93. package/dist/core/worksheet-pictures.d.ts +3 -3
  94. package/dist/core/worksheet-pictures.js +7 -6
  95. package/dist/core/worksheet.d.ts +165 -27
  96. package/dist/core/worksheet.js +206 -89
  97. package/dist/customui/ribbon.js +2 -1
  98. package/dist/entries/core.d.ts +21 -14
  99. package/dist/entries/core.js +14 -7
  100. package/dist/entries/errors.d.ts +1 -0
  101. package/dist/entries/errors.js +1 -0
  102. package/dist/entries/xlsx.d.ts +1 -1
  103. package/dist/errors.d.ts +64 -0
  104. package/dist/errors.js +22 -0
  105. package/dist/hex.d.ts +8 -0
  106. package/dist/hex.js +3 -0
  107. package/dist/io/cell-metadata/metadata.d.ts +42 -0
  108. package/dist/io/cell-metadata/metadata.js +35 -0
  109. package/dist/io/cell-metadata/rich-values.d.ts +11 -0
  110. package/dist/io/cell-metadata/rich-values.js +80 -0
  111. package/dist/io/csv/delimiter.d.ts +21 -0
  112. package/dist/io/csv/delimiter.js +18 -0
  113. package/dist/io/csv/errors.d.ts +14 -0
  114. package/dist/io/csv/errors.js +5 -0
  115. package/dist/io/csv/read.d.ts +6 -1
  116. package/dist/io/csv/read.js +51 -22
  117. package/dist/io/csv/write.d.ts +19 -5
  118. package/dist/io/csv/write.js +24 -44
  119. package/dist/io/opc/inflate.js +14 -0
  120. package/dist/io/opc/namespaces.d.ts +16 -0
  121. package/dist/io/opc/namespaces.js +4 -0
  122. package/dist/io/opc/part-paths.d.ts +9 -0
  123. package/dist/io/opc/part-paths.js +5 -5
  124. package/dist/io/opc/read-opc.d.ts +58 -14
  125. package/dist/io/opc/read-opc.js +49 -21
  126. package/dist/io/opc/read-options.d.ts +5 -1
  127. package/dist/io/opc/sniff-format.d.ts +3 -3
  128. package/dist/io/opc/sniff-format.js +2 -2
  129. package/dist/io/opc/strict-relationships.d.ts +2 -0
  130. package/dist/io/opc/strict-relationships.js +12 -0
  131. package/dist/io/read-policy/column-budget.d.ts +26 -0
  132. package/dist/io/read-policy/column-budget.js +18 -0
  133. package/dist/io/read-policy/read-repair.d.ts +74 -0
  134. package/dist/io/read-policy/read-repair.js +119 -0
  135. package/dist/io/style/cell-style-resolution.d.ts +25 -0
  136. package/dist/io/style/cell-style-resolution.js +30 -0
  137. package/dist/io/style/xf-style.d.ts +23 -1
  138. package/dist/io/style/xf-style.js +8 -0
  139. package/dist/io/xlsb/formula.d.ts +2 -2
  140. package/dist/io/xlsb/formula.js +52 -35
  141. package/dist/io/xlsb/primitives.js +8 -14
  142. package/dist/io/xlsb/ptg-functions.d.ts +19 -10
  143. package/dist/io/xlsb/ptg-functions.js +495 -596
  144. package/dist/io/xlsb/read-metadata.d.ts +7 -0
  145. package/dist/io/xlsb/read-metadata.js +69 -0
  146. package/dist/io/xlsb/read-styles.js +45 -81
  147. package/dist/io/xlsb/read-worksheet.d.ts +26 -1
  148. package/dist/io/xlsb/read-worksheet.js +148 -106
  149. package/dist/io/xlsb/read.d.ts +6 -3
  150. package/dist/io/xlsb/read.js +70 -31
  151. package/dist/io/xlsb/record-stream.d.ts +30 -0
  152. package/dist/io/xlsb/record-stream.js +20 -0
  153. package/dist/io/xlsb/record-types.d.ts +14 -5
  154. package/dist/io/xlsb/record-types.js +14 -5
  155. package/dist/io/xlsb/sheet-protection.d.ts +20 -0
  156. package/dist/io/xlsb/sheet-protection.js +60 -0
  157. package/dist/io/xlsx/cell-accumulator.d.ts +24 -9
  158. package/dist/io/xlsx/cell-accumulator.js +74 -21
  159. package/dist/io/xlsx/cell-metadata.d.ts +39 -0
  160. package/dist/io/xlsx/cell-metadata.js +162 -0
  161. package/dist/io/xlsx/cell-value.d.ts +19 -5
  162. package/dist/io/xlsx/cell-value.js +45 -30
  163. package/dist/io/xlsx/color-xml.js +3 -2
  164. package/dist/io/xlsx/column-span.d.ts +11 -0
  165. package/dist/io/xlsx/column-span.js +9 -0
  166. package/dist/io/xlsx/comments.d.ts +9 -30
  167. package/dist/io/xlsx/comments.js +25 -83
  168. package/dist/io/xlsx/conditional-formatting.d.ts +43 -23
  169. package/dist/io/xlsx/conditional-formatting.js +437 -138
  170. package/dist/io/xlsx/data-validation.d.ts +12 -7
  171. package/dist/io/xlsx/data-validation.js +67 -53
  172. package/dist/io/xlsx/edit-vba.d.ts +7 -2
  173. package/dist/io/xlsx/edit-vba.js +52 -52
  174. package/dist/io/xlsx/font-xml.d.ts +7 -0
  175. package/dist/io/xlsx/font-xml.js +104 -0
  176. package/dist/io/xlsx/hyperlinks.d.ts +19 -21
  177. package/dist/io/xlsx/hyperlinks.js +12 -32
  178. package/dist/io/xlsx/images.d.ts +49 -16
  179. package/dist/io/xlsx/images.js +149 -50
  180. package/dist/io/xlsx/namespaces.d.ts +24 -0
  181. package/dist/io/xlsx/namespaces.js +11 -0
  182. package/dist/io/xlsx/package-plan.d.ts +60 -15
  183. package/dist/io/xlsx/package-plan.js +83 -18
  184. package/dist/io/xlsx/part-names.d.ts +6 -8
  185. package/dist/io/xlsx/part-names.js +3 -7
  186. package/dist/io/xlsx/pivot.d.ts +5 -5
  187. package/dist/io/xlsx/pivot.js +26 -24
  188. package/dist/io/xlsx/preserved-splices.d.ts +6 -0
  189. package/dist/io/xlsx/preserved-splices.js +74 -0
  190. package/dist/io/xlsx/read-comments.d.ts +29 -0
  191. package/dist/io/xlsx/read-comments.js +75 -0
  192. package/dist/io/xlsx/read-parts.d.ts +59 -0
  193. package/dist/io/xlsx/read-parts.js +259 -0
  194. package/dist/io/xlsx/read-pivot.js +11 -4
  195. package/dist/io/xlsx/{rich-runs.d.ts → read-rich-runs.d.ts} +8 -5
  196. package/dist/io/xlsx/{rich-runs.js → read-rich-runs.js} +24 -4
  197. package/dist/io/xlsx/read-rows.d.ts +35 -11
  198. package/dist/io/xlsx/read-rows.js +73 -49
  199. package/dist/io/xlsx/read-shared-strings.js +3 -2
  200. package/dist/io/xlsx/read-styles.d.ts +0 -30
  201. package/dist/io/xlsx/read-styles.js +45 -105
  202. package/dist/io/xlsx/read-workbook-xml.d.ts +42 -0
  203. package/dist/io/xlsx/read-workbook-xml.js +175 -0
  204. package/dist/io/xlsx/read-worksheet.d.ts +9 -3
  205. package/dist/io/xlsx/read-worksheet.js +70 -177
  206. package/dist/io/xlsx/read.d.ts +3 -14
  207. package/dist/io/xlsx/read.js +74 -411
  208. package/dist/io/xlsx/relationships.d.ts +3 -0
  209. package/dist/io/xlsx/relationships.js +4 -0
  210. package/dist/io/xlsx/rich-text.js +1 -1
  211. package/dist/io/xlsx/row-position.d.ts +20 -0
  212. package/dist/io/xlsx/row-position.js +11 -0
  213. package/dist/io/xlsx/row-xml.d.ts +139 -0
  214. package/dist/io/xlsx/row-xml.js +222 -0
  215. package/dist/io/xlsx/shared-formulas.js +2 -2
  216. package/dist/io/xlsx/sheet-properties.d.ts +10 -1
  217. package/dist/io/xlsx/sheet-properties.js +182 -20
  218. package/dist/io/xlsx/strict-parts.d.ts +7 -0
  219. package/dist/io/xlsx/strict-parts.js +171 -0
  220. package/dist/io/xlsx/style-elements.d.ts +41 -0
  221. package/dist/io/xlsx/style-elements.js +136 -0
  222. package/dist/io/xlsx/styles.d.ts +17 -3
  223. package/dist/io/xlsx/styles.js +13 -209
  224. package/dist/io/xlsx/tables.d.ts +10 -2
  225. package/dist/io/xlsx/tables.js +55 -52
  226. package/dist/io/xlsx/theme-xml.d.ts +32 -4
  227. package/dist/io/xlsx/theme-xml.js +106 -71
  228. package/dist/io/xlsx/threaded-comments.d.ts +14 -11
  229. package/dist/io/xlsx/threaded-comments.js +16 -8
  230. package/dist/io/xlsx/workbook-xml.d.ts +45 -6
  231. package/dist/io/xlsx/workbook-xml.js +62 -54
  232. package/dist/io/xlsx/worksheet-xml.d.ts +26 -70
  233. package/dist/io/xlsx/worksheet-xml.js +69 -282
  234. package/dist/io/xlsx/write-stream.d.ts +90 -19
  235. package/dist/io/xlsx/write-stream.js +80 -25
  236. package/dist/io/xlsx/write.d.ts +12 -6
  237. package/dist/io/xlsx/write.js +226 -123
  238. package/dist/rel-type.d.ts +25 -0
  239. package/dist/rel-type.js +9 -0
  240. package/dist/token-set.d.ts +13 -0
  241. package/dist/token-set.js +4 -1
  242. package/dist/vba/bytes.d.ts +16 -5
  243. package/dist/vba/bytes.js +27 -6
  244. package/dist/vba/cfb-format.d.ts +55 -0
  245. package/dist/vba/cfb-format.js +35 -0
  246. package/dist/vba/cfb-writer.d.ts +8 -0
  247. package/dist/vba/cfb-writer.js +40 -40
  248. package/dist/vba/cfb.d.ts +21 -4
  249. package/dist/vba/cfb.js +124 -90
  250. package/dist/vba/codepage.js +2 -1
  251. package/dist/vba/dir-records.d.ts +6 -0
  252. package/dist/vba/dir-records.js +10 -2
  253. package/dist/vba/errors.d.ts +11 -4
  254. package/dist/vba/index.d.ts +0 -2
  255. package/dist/vba/index.js +0 -2
  256. package/dist/vba/ms-ovba.d.ts +6 -2
  257. package/dist/vba/ms-ovba.js +66 -22
  258. package/dist/vba/project-editor.js +129 -119
  259. package/dist/vba/project.d.ts +15 -1
  260. package/dist/vba/project.js +38 -13
  261. package/dist/vba/vba-encoding.d.ts +4 -10
  262. package/dist/vba/vba-encoding.js +3 -17
  263. package/dist/xml/xml-attrs.d.ts +37 -0
  264. package/dist/xml/xml-attrs.js +30 -0
  265. package/dist/xml/xml-chars.d.ts +40 -0
  266. package/dist/xml/xml-chars.js +12 -0
  267. package/dist/xml/xml-namespaces.d.ts +31 -0
  268. package/dist/xml/xml-namespaces.js +50 -0
  269. package/dist/xml/xml-read.d.ts +101 -11
  270. package/dist/xml/xml-read.js +150 -18
  271. package/dist/xml/xml-scan.d.ts +25 -34
  272. package/dist/xml/xml-scan.js +69 -46
  273. package/dist/xml/xml.d.ts +59 -26
  274. package/dist/xml/xml.js +45 -23
  275. package/package.json +15 -9
  276. package/skills/ts-xlsx-upstream/SKILL.md +21 -13
  277. package/dist/core/modern-functions.d.ts +0 -1
@@ -1,31 +1,16 @@
1
- import { tryDecodeRange } from '../../core/address.js';
2
- import { isHyperlinkValue, isRichTextValue } from '../../core/value.js';
1
+ import { boundedRect, tryDecodeRange } from '../../core/address.js';
3
2
  import {} from '../../xml/xml-read.js';
4
3
  import { localName } from '../../xml/xml-scan.js';
5
4
  import { escapeAttr, textAttr } from '../../xml/xml.js';
6
- export function collectHyperlinks(sheet) {
7
- const links = [];
8
- for (const { cells } of sheet.rows()) {
9
- for (const cell of cells) {
10
- const value = cell.value;
11
- if (isHyperlinkValue(value)) {
12
- links.push({
13
- ref: value.range ?? cell.address,
14
- target: value.hyperlink,
15
- ...(value.tooltip !== undefined ? { tooltip: value.tooltip } : {}),
16
- });
17
- }
18
- }
19
- }
20
- return links;
21
- }
5
+ import { relAttr } from '../opc/namespaces.js';
6
+ import { REL } from './relationships.js';
22
7
  export function planHyperlinks(links, rels) {
23
8
  return links.map((link) => {
24
9
  const tooltip = link.tooltip !== undefined ? { tooltip: link.tooltip } : {};
25
10
  if (link.target.startsWith('#')) {
26
11
  return { ref: link.ref, location: link.target.slice(1), ...tooltip };
27
12
  }
28
- return { ref: link.ref, relId: rels.next(), target: link.target, ...tooltip };
13
+ return { ref: link.ref, relId: rels.addExternal(REL.hyperlink, link.target), ...tooltip };
29
14
  });
30
15
  }
31
16
  export function hyperlinksXml(links) {
@@ -45,15 +30,16 @@ export function sheetHyperlinkPass() {
45
30
  const links = [];
46
31
  return {
47
32
  handlers: {
48
- onOpen(name, attrs) {
33
+ onOpen(name, attrs, _selfClosing, scope) {
49
34
  if (localName(name) !== 'hyperlink')
50
35
  return;
51
36
  const ref = attrs.ref;
52
37
  if (ref === undefined)
53
38
  return;
39
+ const rid = relAttr(scope, attrs, 'id');
54
40
  links.push({
55
41
  ref,
56
- ...(attrs['r:id'] !== undefined ? { rid: attrs['r:id'] } : {}),
42
+ ...(rid !== undefined ? { rid } : {}),
57
43
  ...(attrs.location !== undefined ? { location: attrs.location } : {}),
58
44
  ...(attrs.tooltip !== undefined ? { tooltip: attrs.tooltip } : {}),
59
45
  });
@@ -68,19 +54,13 @@ export function applyHyperlinks(sheet, links, targetOf) {
68
54
  if (target === undefined)
69
55
  continue;
70
56
  const decoded = tryDecodeRange(link.ref);
71
- if (decoded === undefined || decoded.tl.col === undefined || decoded.tl.row === undefined)
57
+ if (decoded === undefined || boundedRect(decoded) === undefined)
72
58
  continue;
73
- const cell = sheet.getCell(decoded.tl.address);
74
- const cellValue = cell.value;
75
- const text = typeof cellValue === 'string' ? cellValue : isRichTextValue(cellValue) ? cellValue : '';
76
- const spansRange = decoded.tl.address !== decoded.br.address;
77
- const value = {
78
- hyperlink: target,
79
- text,
59
+ sheet.addHyperlink({
60
+ ref: link.ref,
61
+ target,
80
62
  ...(link.tooltip !== undefined ? { tooltip: link.tooltip } : {}),
81
- ...(spansRange ? { range: link.ref } : {}),
82
- };
83
- cell.value = value;
63
+ });
84
64
  }
85
65
  }
86
66
  function resolveTarget(link, targetOf) {
@@ -1,20 +1,32 @@
1
- import { type AnchorPoint, type Extent, type ImageAnchor, type ImageEditAs } from '../../core/image.ts';
1
+ import { type AnchorPoint, type Extent, type ImageAnchor, type ImageCrop, type ImageEditAs, type PictureProperties } from '../../core/image.ts';
2
2
  /** The content type for a media part's `<Default Extension>` entry in `[Content_Types].xml`. */
3
3
  export declare function imageContentType(extension: string): string;
4
- /** One image placed in a drawing: where it sits and the drawing-local relationship id that ties it
5
- * to its media bytes. */
4
+ /** One image placed in a drawing: where it sits, the drawing-local relationship id that ties it to its
5
+ * media bytes, and the picture's own properties. */
6
6
  export interface DrawingImage {
7
7
  readonly anchor: ImageAnchor;
8
8
  /** The `r:embed` id referencing this image's entry in the drawing's own `.rels`. */
9
9
  readonly embedId: string;
10
+ readonly properties: PictureProperties;
11
+ /** The `r:id` of the hyperlink relationship in the drawing's own `.rels`, when the picture is a link. */
12
+ readonly hyperlinkId?: string;
10
13
  }
11
14
  /** The `xl/drawings/drawing{n}.xml` part: one anchor per image, two-cell or one-cell by its shape. */
12
15
  export declare function drawingXml(images: readonly DrawingImage[]): string;
13
- /** The drawing's `_rels/drawing{n}.xml.rels`: one image relationship per anchor, in `embedId` order
14
- * (`rId1`, `rId2`, …), each pointing at the media part the anchor shows. */
15
- export declare function drawingRelsXml(mediaTargets: readonly string[]): string;
16
+ /**
17
+ * A drawing kept from a file with pictures added to it: each picture's anchor goes after the drawing's
18
+ * own content, so it draws on top, as a picture inserted last does in Excel.
19
+ *
20
+ * A drawing numbers its shapes by `cNvPr id`, unique within it, so the pictures take ids past the
21
+ * highest one there. Their `r:embed` and link ids are the caller's, allocated past the relationships
22
+ * the drawing already holds.
23
+ *
24
+ * @throws {AuthoringError} if the kept part has no `<wsDr>` root to add a picture to.
25
+ */
26
+ export declare function mergePictures(drawing: string, images: readonly DrawingImage[]): string;
16
27
  /** An image anchor parsed from a drawing part, with the `r:embed` id that names its media. A two-cell
17
- * anchor carries `to` (and may carry `editAs`); a one-cell anchor carries `ext` instead. */
28
+ * anchor carries `to` and its `editAs`, the schema default filled in; a one-cell anchor carries `ext`
29
+ * instead. */
18
30
  export interface ParsedImageAnchor {
19
31
  readonly from: AnchorPoint;
20
32
  readonly to?: AnchorPoint;
@@ -22,13 +34,34 @@ export interface ParsedImageAnchor {
22
34
  readonly editAs?: ImageEditAs;
23
35
  readonly rotation?: number;
24
36
  readonly embed: string;
37
+ readonly description?: string;
38
+ readonly title?: string;
39
+ readonly crop?: ImageCrop;
40
+ /** The `r:id` of the picture's `a:hlinkClick`, for the caller to resolve against the drawing's rels. */
41
+ readonly hyperlinkId?: string;
42
+ readonly tooltip?: string;
25
43
  }
26
- /** Parse a drawing part into its image anchors (both `<xdr:twoCellAnchor>` and `<xdr:oneCellAnchor>`).
27
- * Anchors that are not pictures (a chart, a shape) carry no `<a:blip r:embed>` and are skipped, so a
28
- * mixed drawing yields only its images. */
29
- export declare function parseDrawing(xml: string): ParsedImageAnchor[];
30
- /** Whether a drawing part holds anchor content beyond plain pictures: a chart, shape, connector, or
31
- * group. Excel packs every one of a sheet's anchors into a single drawing part, so a sheet with both a
32
- * picture and a chart yields a mixed drawing; modeling only its pictures and re-serialising from them
33
- * would drop the chart. The reader uses this to fall back to whole-drawing byte-preservation instead. */
34
- export declare function drawingHasUnmodeledContent(xml: string): boolean;
44
+ /** A drawing part as the reader sees it: the picture anchors it could model, and whether those anchors
45
+ * are the whole of it. */
46
+ export interface ParsedDrawing {
47
+ readonly anchors: readonly ParsedImageAnchor[];
48
+ /**
49
+ * Whether every anchor is a two-cell or one-cell anchor holding exactly one embedded picture. When it
50
+ * is not, writing the drawing back from `anchors` would leave the rest out, so the reader keeps the
51
+ * whole part byte for byte instead.
52
+ */
53
+ readonly fullyModeled: boolean;
54
+ }
55
+ /**
56
+ * Parse a drawing part into its picture anchors (`<xdr:twoCellAnchor>` and `<xdr:oneCellAnchor>`), and
57
+ * decide in the same scan whether they are the whole drawing. It is fully modeled only when every
58
+ * anchor holds exactly one picture whose bytes are embedded; anything else in it, including a picture
59
+ * that links its image rather than embedding it, has no place in the image model.
60
+ *
61
+ * This used to be two scans: one listing the content the model skips, and this one. The list missed
62
+ * an absolute anchor and a linked picture, so a drawing holding either counted as modeled and lost them
63
+ * on write. What an anchor holds is now judged by what the parse found in it, one embedded picture or
64
+ * not, so no kind of anchor content can slip past unnamed; the list left names only the anchor kinds
65
+ * and wrappers this parse does not otherwise open an anchor for.
66
+ */
67
+ export declare function parseDrawing(xml: string): ParsedDrawing;
@@ -1,11 +1,11 @@
1
1
  import { isImageEditAs, isOneCellAnchor, } from '../../core/image.js';
2
- import { parseXml, TextCapture } from '../../xml/xml-read.js';
3
- import { enumToken, localName, numFinite } from '../../xml/xml-scan.js';
4
- import { checkedToken, numAttr, numberText, XML_DECLARATION } from '../../xml/xml.js';
5
- import { RELATIONSHIPS_NS } from '../opc/namespaces.js';
6
- import { relationship, relationshipsPart } from '../opc/rels.js';
2
+ import { assertWritableNumber, AuthoringError } from '../../errors.js';
3
+ import { enumToken, numFinite, numInteger } from '../../xml/xml-attrs.js';
4
+ import { elementRange, openElements, parseXml, TextCapture } from '../../xml/xml-read.js';
5
+ import { localName } from '../../xml/xml-scan.js';
6
+ import { checkedToken, escapeAttr, intAttr, numAttr, numberText, textAttr, XML_DECLARATION, } from '../../xml/xml.js';
7
+ import { relAttr, RELATIONSHIPS_NS } from '../opc/namespaces.js';
7
8
  import { DRAWINGML_NS, XDR_NS } from './namespaces.js';
8
- const IMAGE_REL_TYPE = `${RELATIONSHIPS_NS}/image`;
9
9
  const IMAGE_CONTENT_TYPES = new Map([
10
10
  ['png', 'image/png'],
11
11
  ['jpg', 'image/jpeg'],
@@ -22,81 +22,185 @@ export function imageContentType(extension) {
22
22
  const ext = extension.toLowerCase();
23
23
  return IMAGE_CONTENT_TYPES.get(ext) ?? `image/${ext}`;
24
24
  }
25
+ const ANCHOR_NAMESPACES = ` xmlns:xdr="${XDR_NS}" xmlns:a="${DRAWINGML_NS}" xmlns:r="${RELATIONSHIPS_NS}"`;
25
26
  export function drawingXml(images) {
26
- const anchors = images.map((image, i) => anchorXml(image, i + 1)).join('');
27
- return (XML_DECLARATION +
28
- `<xdr:wsDr xmlns:xdr="${XDR_NS}" xmlns:a="${DRAWINGML_NS}" xmlns:r="${RELATIONSHIPS_NS}">` +
29
- anchors +
30
- '</xdr:wsDr>');
27
+ const anchors = images.map((image, i) => anchorXml(image, i + 1, '')).join('');
28
+ return `${XML_DECLARATION}<xdr:wsDr${ANCHOR_NAMESPACES}>${anchors}</xdr:wsDr>`;
31
29
  }
32
- function anchorXml(image, id) {
30
+ export function mergePictures(drawing, images) {
31
+ const root = elementRange(drawing, ['wsDr']);
32
+ if (root === undefined) {
33
+ throw new AuthoringError('cannot add a picture to a kept drawing that has no <wsDr> root');
34
+ }
35
+ let lastId = 0;
36
+ for (const { attrs } of openElements(drawing, 'cNvPr')) {
37
+ lastId = Math.max(lastId, numInteger(attrs.id, 0) ?? 0);
38
+ }
39
+ const anchors = images
40
+ .map((image, i) => anchorXml(image, lastId + i + 1, ANCHOR_NAMESPACES))
41
+ .join('');
42
+ if (root.contentStart === root.end) {
43
+ const open = drawing.slice(root.start, root.end).replace(/\s*\/>$/, '>');
44
+ return `${drawing.slice(0, root.start)}${open}${anchors}</${root.name}>${drawing.slice(root.end)}`;
45
+ }
46
+ return drawing.slice(0, root.contentEnd) + anchors + drawing.slice(root.contentEnd);
47
+ }
48
+ function anchorXml(image, id, namespaces) {
33
49
  const { anchor } = image;
50
+ const pic = picXml(image, id, anchor.rotation);
34
51
  return isOneCellAnchor(anchor)
35
- ? oneCellAnchorXml(anchor.from, anchor.ext, anchor.rotation, image.embedId, id)
36
- : twoCellAnchorXml(anchor.from, anchor.to, anchor.editAs ?? 'oneCell', anchor.rotation, image.embedId, id);
52
+ ? oneCellAnchorXml(anchor.from, anchor.ext, pic, namespaces)
53
+ : twoCellAnchorXml(anchor.from, anchor.to, anchor.editAs ?? 'oneCell', pic, namespaces);
37
54
  }
38
- function twoCellAnchorXml(from, to, editAs, rotation, embedId, id) {
39
- return (`<xdr:twoCellAnchor editAs="${checkedToken(editAs, isImageEditAs, 'image anchor edit mode')}">` +
55
+ function twoCellAnchorXml(from, to, editAs, pic, namespaces) {
56
+ return (`<xdr:twoCellAnchor${namespaces} editAs="${checkedToken(editAs, isImageEditAs, 'image anchor edit mode')}">` +
40
57
  `<xdr:from>${anchorPointXml(from)}</xdr:from>` +
41
58
  `<xdr:to>${anchorPointXml(to)}</xdr:to>` +
42
- picXml(embedId, id, rotation) +
59
+ pic +
43
60
  '<xdr:clientData/>' +
44
61
  '</xdr:twoCellAnchor>');
45
62
  }
46
- function oneCellAnchorXml(from, ext, rotation, embedId, id) {
47
- return ('<xdr:oneCellAnchor>' +
63
+ function oneCellAnchorXml(from, ext, pic, namespaces) {
64
+ return (`<xdr:oneCellAnchor${namespaces}>` +
48
65
  `<xdr:from>${anchorPointXml(from)}</xdr:from>` +
49
66
  `<xdr:ext${numAttr('cx', ext.cx)}${numAttr('cy', ext.cy)}/>` +
50
- picXml(embedId, id, rotation) +
67
+ pic +
51
68
  '<xdr:clientData/>' +
52
69
  '</xdr:oneCellAnchor>');
53
70
  }
54
- function picXml(embedId, id, rotation) {
71
+ function picXml(image, id, rotation) {
72
+ const { embedId, properties, hyperlinkId } = image;
55
73
  const xfrm = rotation !== undefined ? `<a:xfrm${numAttr('rot', rotation)}/>` : '';
74
+ const attributes = textAttr('descr', properties.description) + textAttr('title', properties.title);
75
+ const link = hyperlinkId === undefined
76
+ ? ''
77
+ : `<a:hlinkClick r:id="${escapeAttr(hyperlinkId)}"` +
78
+ `${textAttr('tooltip', properties.hyperlink?.tooltip)}/>`;
79
+ const cNvPr = link === ''
80
+ ? `<xdr:cNvPr id="${id}" name="Picture ${id}"${attributes}/>`
81
+ : `<xdr:cNvPr id="${id}" name="Picture ${id}"${attributes}>${link}</xdr:cNvPr>`;
56
82
  return ('<xdr:pic>' +
57
- `<xdr:nvPicPr><xdr:cNvPr id="${id}" name="Picture ${id}"/>` +
83
+ `<xdr:nvPicPr>${cNvPr}` +
58
84
  '<xdr:cNvPicPr><a:picLocks noChangeAspect="1"/></xdr:cNvPicPr></xdr:nvPicPr>' +
59
85
  `<xdr:blipFill><a:blip r:embed="${embedId}"/>` +
86
+ srcRectXml(properties.crop) +
60
87
  '<a:stretch><a:fillRect/></a:stretch></xdr:blipFill>' +
61
88
  `<xdr:spPr>${xfrm}<a:prstGeom prst="rect"><a:avLst/></a:prstGeom></xdr:spPr>` +
62
89
  '</xdr:pic>');
63
90
  }
91
+ const CROP_UNITS = 100_000;
92
+ const INT_MAX = 2_147_483_647;
93
+ function srcRectXml(crop) {
94
+ if (crop === undefined)
95
+ return '';
96
+ const edge = (name, fraction) => {
97
+ if (fraction === undefined)
98
+ return '';
99
+ assertWritableNumber(fraction);
100
+ const units = Math.round(fraction * CROP_UNITS);
101
+ if (units > INT_MAX) {
102
+ throw new AuthoringError(`cannot write the picture crop ${name}="${units}": past xsd:int`);
103
+ }
104
+ return units === 0 ? '' : intAttr(name, units, -INT_MAX - 1);
105
+ };
106
+ const attributes = edge('l', crop.left) + edge('t', crop.top) + edge('r', crop.right) + edge('b', crop.bottom);
107
+ return attributes === '' ? '' : `<a:srcRect${attributes}/>`;
108
+ }
109
+ function cropEdge(value) {
110
+ if (value === undefined)
111
+ return undefined;
112
+ const percent = value.endsWith('%') ? numFinite(value.slice(0, -1)) : undefined;
113
+ const units = percent === undefined ? numInteger(value) : Math.round(percent * 1000);
114
+ if (units === undefined || units === 0 || units > INT_MAX || units < -INT_MAX - 1) {
115
+ return undefined;
116
+ }
117
+ return units / CROP_UNITS;
118
+ }
64
119
  function anchorPointXml(point) {
65
120
  return (`<xdr:col>${numberText(point.col)}</xdr:col>` +
66
121
  `<xdr:colOff>${numberText(point.colOff ?? 0)}</xdr:colOff>` +
67
122
  `<xdr:row>${numberText(point.row)}</xdr:row>` +
68
123
  `<xdr:rowOff>${numberText(point.rowOff ?? 0)}</xdr:rowOff>`);
69
124
  }
70
- export function drawingRelsXml(mediaTargets) {
71
- return relationshipsPart(mediaTargets.map((target, i) => relationship(`rId${i + 1}`, IMAGE_REL_TYPE, target)));
72
- }
73
125
  function blankPoint() {
74
126
  return { col: 0, row: 0, colOff: 0, rowOff: 0 };
75
127
  }
128
+ const UNMODELED_CONTENT = new Set([
129
+ 'graphicFrame',
130
+ 'sp',
131
+ 'cxnSp',
132
+ 'grpSp',
133
+ 'contentPart',
134
+ 'absoluteAnchor',
135
+ 'AlternateContent',
136
+ ]);
76
137
  export function parseDrawing(xml) {
77
138
  const anchors = [];
139
+ let fullyModeled = true;
78
140
  let from = null;
79
141
  let to = null;
80
142
  let ext;
81
143
  let editAs;
82
144
  let rotation;
83
145
  let embed;
146
+ let pictures = 0;
147
+ let properties = {};
84
148
  let target = null;
85
149
  let picDepth = 0;
86
150
  const coord = new TextCapture(COORDINATES);
87
151
  parseXml(xml, {
88
- onOpen(name, attrs, selfClosing) {
152
+ onOpen(name, attrs, selfClosing, scope) {
89
153
  const local = localName(name);
90
- if (local === 'twoCellAnchor' || local === 'oneCellAnchor') {
154
+ if (UNMODELED_CONTENT.has(local)) {
155
+ fullyModeled = false;
156
+ }
157
+ else if (local === 'twoCellAnchor' || local === 'oneCellAnchor') {
91
158
  from = blankPoint();
92
159
  to = local === 'twoCellAnchor' ? blankPoint() : null;
93
160
  ext = undefined;
94
161
  rotation = undefined;
95
162
  embed = undefined;
96
- editAs = enumToken(attrs.editAs, isImageEditAs);
163
+ pictures = 0;
164
+ properties = {};
165
+ editAs =
166
+ local === 'twoCellAnchor'
167
+ ? (enumToken(attrs.editAs, isImageEditAs) ?? 'twoCell')
168
+ : undefined;
97
169
  }
98
170
  else if (local === 'pic') {
99
171
  picDepth++;
172
+ pictures++;
173
+ }
174
+ else if (local === 'cNvPr' && picDepth > 0) {
175
+ if (attrs.descr !== undefined && attrs.descr !== '')
176
+ properties.description = attrs.descr;
177
+ if (attrs.title !== undefined && attrs.title !== '')
178
+ properties.title = attrs.title;
179
+ }
180
+ else if (local === 'hlinkClick' && picDepth > 0) {
181
+ const id = relAttr(scope, attrs, 'id');
182
+ if (id !== undefined && id !== '') {
183
+ properties.hyperlinkId = id;
184
+ if (attrs.tooltip !== undefined)
185
+ properties.tooltip = attrs.tooltip;
186
+ }
187
+ }
188
+ else if (local === 'srcRect' && picDepth > 0) {
189
+ const crop = {};
190
+ const left = cropEdge(attrs.l);
191
+ const top = cropEdge(attrs.t);
192
+ const right = cropEdge(attrs.r);
193
+ const bottom = cropEdge(attrs.b);
194
+ if (left !== undefined)
195
+ crop.left = left;
196
+ if (top !== undefined)
197
+ crop.top = top;
198
+ if (right !== undefined)
199
+ crop.right = right;
200
+ if (bottom !== undefined)
201
+ crop.bottom = bottom;
202
+ if (Object.keys(crop).length > 0)
203
+ properties.crop = crop;
100
204
  }
101
205
  else if (local === 'xfrm' && picDepth > 0) {
102
206
  const rot = numFinite(attrs.rot);
@@ -116,9 +220,11 @@ export function parseDrawing(xml) {
116
220
  ext = { cx, cy };
117
221
  }
118
222
  else if (local === 'blip') {
119
- const value = attrs['r:embed'] ?? attrs.embed;
223
+ const value = relAttr(scope, attrs, 'embed');
120
224
  if (value !== undefined)
121
225
  embed = value;
226
+ if (relAttr(scope, attrs, 'link') !== undefined)
227
+ fullyModeled = false;
122
228
  }
123
229
  else if (target !== null) {
124
230
  coord.open(local, selfClosing);
@@ -142,15 +248,19 @@ export function parseDrawing(xml) {
142
248
  picDepth--;
143
249
  }
144
250
  else if (local === 'twoCellAnchor' || local === 'oneCellAnchor') {
145
- if (from !== null && embed !== undefined) {
146
- const rot = rotation !== undefined ? { rotation } : {};
147
- if (to !== null) {
148
- const mode = editAs !== undefined ? { editAs } : {};
149
- anchors.push({ from: { ...from }, to: { ...to }, ...mode, ...rot, embed });
150
- }
151
- else if (ext !== undefined) {
152
- anchors.push({ from: { ...from }, ext, ...rot, embed });
153
- }
251
+ const rot = rotation !== undefined ? { rotation } : {};
252
+ if (from === null || embed === undefined || pictures !== 1) {
253
+ fullyModeled = false;
254
+ }
255
+ else if (to !== null) {
256
+ const mode = editAs !== undefined ? { editAs } : {};
257
+ anchors.push({ from: { ...from }, to: { ...to }, ...mode, ...rot, embed, ...properties });
258
+ }
259
+ else if (ext !== undefined) {
260
+ anchors.push({ from: { ...from }, ext, ...rot, embed, ...properties });
261
+ }
262
+ else {
263
+ fullyModeled = false;
154
264
  }
155
265
  from = null;
156
266
  to = null;
@@ -158,18 +268,7 @@ export function parseDrawing(xml) {
158
268
  }
159
269
  },
160
270
  });
161
- return anchors;
162
- }
163
- const UNMODELED_DRAWING_CONTENT = new Set(['graphicFrame', 'sp', 'cxnSp', 'grpSp']);
164
- export function drawingHasUnmodeledContent(xml) {
165
- let found = false;
166
- parseXml(xml, {
167
- onOpen(name) {
168
- if (!found && UNMODELED_DRAWING_CONTENT.has(localName(name)))
169
- found = true;
170
- },
171
- });
172
- return found;
271
+ return { anchors, fullyModeled };
173
272
  }
174
273
  const COORDINATES = new Set(['col', 'colOff', 'row', 'rowOff']);
175
274
  function setCoordinate(point, coord, value) {
@@ -9,6 +9,7 @@
9
9
  * The package-level URIs (`.rels`, content types, the relationship vocabulary) describe the
10
10
  * container rather than the spreadsheet inside it, and live in `../opc/namespaces.ts`.
11
11
  */
12
+ import type { NamespaceScope } from '../../xml/xml-namespaces.ts';
12
13
  /**
13
14
  * SpreadsheetML main namespace: the default `xmlns` of the workbook,
14
15
  * worksheet, styles, sharedStrings, comments, table and pivot parts.
@@ -53,3 +54,26 @@ export declare const DATABAR_LINK_EXT_URI = "{B025F937-C7B1-47D3-B67F-A62EFF666E
53
54
  export declare const DATA_VALIDATION_EXT_URI = "{CCE6A557-97BC-4b89-ADB6-D9C93CAAB3DF}";
54
55
  export declare const SLICER_LIST_EXT_URI = "{A8765BA9-456A-4dab-B4F3-ACF838C121DE}";
55
56
  export declare const SLICER_CACHES_EXT_URI = "{BBE1A952-AA13-448e-AADC-164F8A28A991}";
57
+ /** The extension a dynamic-array formula's cell-metadata block carries its properties in. */
58
+ export declare const DYNAMIC_ARRAY_PROPERTIES_EXT_URI = "{bdbb8cdc-fa1e-496e-a857-3c3f30c029c3}";
59
+ /** The 2017 dynamic-array namespace (`xda:`), which scopes those properties in `xl/metadata.xml`. */
60
+ export declare const DYNAMIC_ARRAY_NS = "http://schemas.microsoft.com/office/spreadsheetml/2017/dynamicarray";
61
+ /** The extension a rich-value metadata block names its rich value in (`<xlrd:rvb i>`). */
62
+ export declare const RICH_VALUE_BLOCK_EXT_URI = "{3e2802c4-a4d2-4d8b-9148-e3be6c30e623}";
63
+ /** The 2017 rich-data namespace (`xlrd:`): the rich values, their structures, and the blocks naming them. */
64
+ export declare const RICH_DATA_NS = "http://schemas.microsoft.com/office/spreadsheetml/2017/richdata";
65
+ /**
66
+ * Whether an element belongs to the standard SpreadsheetML vocabulary rather than to an extension.
67
+ *
68
+ * This used to be spelled `!name.includes(':')`, on the assumption that only extension elements carry
69
+ * a prefix. A worksheet may bind the *main* namespace to a prefix instead, which is legal and which
70
+ * real toolchains emit, and then every element has a colon in it: such a file had every one of its
71
+ * data validations and conditional formats read as an unknown extension and dropped, silently.
72
+ *
73
+ * An unprefixed element counts even when the part declares no default namespace. A part with no
74
+ * `xmlns` at all is not conforming OOXML, but this scanner does not reject it, such parts were read
75
+ * before, and a fix that gains files by resolving namespaces should not lose files that have none.
76
+ */
77
+ export declare function isMainNamespaceElement(scope: NamespaceScope, name: string): boolean;
78
+ /** The complement of {@link isMainNamespaceElement}: an element of one of the extension vocabularies. */
79
+ export declare function isExtensionElement(scope: NamespaceScope, name: string): boolean;
@@ -11,3 +11,14 @@ export const DATABAR_LINK_EXT_URI = '{B025F937-C7B1-47D3-B67F-A62EFF666E3E}';
11
11
  export const DATA_VALIDATION_EXT_URI = '{CCE6A557-97BC-4b89-ADB6-D9C93CAAB3DF}';
12
12
  export const SLICER_LIST_EXT_URI = '{A8765BA9-456A-4dab-B4F3-ACF838C121DE}';
13
13
  export const SLICER_CACHES_EXT_URI = '{BBE1A952-AA13-448e-AADC-164F8A28A991}';
14
+ export const DYNAMIC_ARRAY_PROPERTIES_EXT_URI = '{bdbb8cdc-fa1e-496e-a857-3c3f30c029c3}';
15
+ export const DYNAMIC_ARRAY_NS = 'http://schemas.microsoft.com/office/spreadsheetml/2017/dynamicarray';
16
+ export const RICH_VALUE_BLOCK_EXT_URI = '{3e2802c4-a4d2-4d8b-9148-e3be6c30e623}';
17
+ export const RICH_DATA_NS = 'http://schemas.microsoft.com/office/spreadsheetml/2017/richdata';
18
+ export function isMainNamespaceElement(scope, name) {
19
+ const namespace = scope.elementNamespace(name);
20
+ return namespace === SPREADSHEETML_NS || (namespace === undefined && !name.includes(':'));
21
+ }
22
+ export function isExtensionElement(scope, name) {
23
+ return !isMainNamespaceElement(scope, name);
24
+ }
@@ -5,29 +5,60 @@ import type { Table } from '../../core/table.ts';
5
5
  import type { Workbook } from '../../core/workbook.ts';
6
6
  import type { Worksheet } from '../../core/worksheet.ts';
7
7
  import type { CommentCell } from './comments.ts';
8
- import type { DrawingImage } from './images.ts';
9
- export declare class SheetRelIds {
8
+ import type { HyperlinkPlan } from './hyperlinks.ts';
9
+ import { type DrawingImage } from './images.ts';
10
+ /** One relationship a generated `.rels` part declares, its target already relative to the owner. */
11
+ export interface PlannedRelationship {
12
+ readonly id: string;
13
+ readonly type: string;
14
+ readonly target: string;
15
+ readonly external?: boolean;
16
+ }
17
+ /**
18
+ * The relationships one part owns, recorded as their ids are handed out.
19
+ *
20
+ * An id and the relationship it names are one fact, so they are written down in one call. When the
21
+ * ids came from a counter and the `.rels` part was listed somewhere else, a sheet's set was spelled
22
+ * three times: once where ids were drawn, once in the rels renderer in another order, and once more in
23
+ * a nine-way condition deciding whether the rels part existed at all. A tenth kind of part needed all
24
+ * three edits, and missing the last dropped the `.rels` part while the sheet XML still cited the id.
25
+ * Now the ledger is the rels part, and a part with an empty ledger has none.
26
+ *
27
+ * Ids run `rId1`, `rId2`, … in the order parts are added, so no id is derived by summing the ones
28
+ * before it, and the `.rels` part lists them in that same order. One ledger per owning part; ids are
29
+ * scoped to it. A ledger adding to a part kept from a file skips the ids that part already holds.
30
+ */
31
+ export declare class RelationshipLedger {
10
32
  #private;
11
- /** The next relationship id (`rId1`, `rId2`, …), advancing the counter. */
12
- next(): string;
33
+ /**
34
+ * @param owner the package path of the part whose `.rels` this is, `''` for the package root.
35
+ * @param taken the ids the part's `.rels` already holds, none of which a new relationship may take.
36
+ */
37
+ constructor(owner: string, taken?: ReadonlySet<string>);
38
+ /** Record a relationship to a package part, named by its package path, and return its id. */
39
+ add(type: string, partPath: string): string;
40
+ /** Record a relationship to a target outside the package (a hyperlink's URL) and return its id. */
41
+ addExternal(type: string, url: string): string;
42
+ /** Record a relationship to a place inside this document (a picture link's `#Sheet1!C3`), kept as
43
+ * written rather than resolved as a part path, and return its id. */
44
+ addInDocument(type: string, location: string): string;
45
+ /** Every relationship recorded, in id order. */
46
+ get relationships(): readonly PlannedRelationship[];
13
47
  }
14
48
  export interface PivotPlan {
15
49
  readonly number: number;
16
50
  readonly cacheId: string;
17
51
  readonly table: PivotTable;
18
- readonly sheetRelId: string;
19
52
  workbookRelId: string;
20
53
  }
21
54
  export interface CommentPlan {
22
55
  readonly number: number;
23
56
  readonly comments: readonly CommentCell[];
24
57
  readonly vmlRelId: string;
25
- readonly commentsRelId: string;
26
58
  }
27
59
  export interface ThreadedCommentPlan {
28
60
  readonly number: number;
29
61
  readonly threads: readonly CommentThread[];
30
- readonly relId: string;
31
62
  }
32
63
  export interface PrinterSettingsPlan {
33
64
  readonly number: number;
@@ -41,8 +72,6 @@ export interface TablePlan {
41
72
  }
42
73
  export interface BackgroundPlan {
43
74
  readonly relId: string;
44
- readonly mediaNumber: number;
45
- readonly extension: string;
46
75
  }
47
76
  export interface PreservedPartPlan {
48
77
  readonly path: string;
@@ -84,11 +113,8 @@ export interface PreservedPlan {
84
113
  export interface DrawingPlan {
85
114
  readonly number: number;
86
115
  readonly relId: string;
87
- readonly images: readonly ImagePlan[];
88
- }
89
- export interface ImagePlan extends DrawingImage {
90
- readonly mediaNumber: number;
91
- readonly extension: string;
116
+ readonly images: readonly DrawingImage[];
117
+ readonly relationships: readonly PlannedRelationship[];
92
118
  }
93
119
  export interface MediaPart {
94
120
  readonly number: number;
@@ -108,4 +134,23 @@ export interface MediaPlan {
108
134
  resolve(id: number): ResolvedMedia;
109
135
  }
110
136
  export declare function planMedia(workbook: Workbook, sheets: readonly Worksheet[]): MediaPlan;
111
- export declare function planPreservedParts(workbook: Workbook, generatedDrawingCount: number, generatedMediaCount: number): PreservedPlan;
137
+ /**
138
+ * The pictures a sheet adds to the drawing it kept from a file, laid out once that drawing's path in the
139
+ * package is known: each picture's media and link relationships go into `ledger`, the drawing's own,
140
+ * which already skips the ids the drawing holds.
141
+ */
142
+ export type PicturesJoiningDrawing = (ledger: RelationshipLedger) => readonly DrawingImage[];
143
+ export declare function planPreservedParts(workbook: Workbook, generatedDrawingCount: number, generatedMediaCount: number, generatedPivotCount: number, pictures: ReadonlyMap<string, PicturesJoiningDrawing>): PreservedPlan;
144
+ export interface SheetPlan {
145
+ /** Every relationship the sheet's `.rels` part declares, in id order; empty when it needs none. */
146
+ readonly relationships: readonly PlannedRelationship[];
147
+ readonly tables: TablePlan[];
148
+ readonly drawing: DrawingPlan | null;
149
+ readonly comments: CommentPlan | null;
150
+ readonly threadedComments: ThreadedCommentPlan | null;
151
+ readonly printerSettings: PrinterSettingsPlan | null;
152
+ readonly hyperlinks: HyperlinkPlan[];
153
+ readonly background: BackgroundPlan | null;
154
+ readonly preservedRefs: PreservedReferencePlan[];
155
+ readonly pivots: PivotPlan[];
156
+ }