@docx4j/core-ts 0.1.0 → 0.1.2

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 (172) hide show
  1. package/README.md +410 -10
  2. package/dist/model/content/Body.d.mts +109 -4
  3. package/dist/model/content/Body.mjs +300 -29
  4. package/dist/model/content/Comment.d.mts +78 -0
  5. package/dist/model/content/Comment.mjs +429 -0
  6. package/dist/model/content/ContentControl.d.mts +173 -0
  7. package/dist/model/content/ContentControl.mjs +638 -0
  8. package/dist/model/content/Font.d.mts +48 -8
  9. package/dist/model/content/Font.mjs +55 -13
  10. package/dist/model/content/InlinePicture.d.mts +103 -0
  11. package/dist/model/content/InlinePicture.mjs +361 -0
  12. package/dist/model/content/List.d.mts +253 -0
  13. package/dist/model/content/List.mjs +778 -0
  14. package/dist/model/content/Paragraph.d.mts +213 -16
  15. package/dist/model/content/Paragraph.mjs +611 -57
  16. package/dist/model/content/Range.d.mts +48 -4
  17. package/dist/model/content/Range.mjs +161 -4
  18. package/dist/model/content/Table.d.mts +130 -0
  19. package/dist/model/content/Table.mjs +429 -0
  20. package/dist/model/content/TrackedChange.d.mts +96 -0
  21. package/dist/model/content/TrackedChange.mjs +352 -0
  22. package/dist/model/content/comments.d.mts +122 -0
  23. package/dist/model/content/comments.mjs +248 -0
  24. package/dist/model/content/index.d.mts +13 -3
  25. package/dist/model/content/index.mjs +15 -3
  26. package/dist/model/content/ooxml.d.mts +17 -0
  27. package/dist/model/content/ooxml.mjs +214 -0
  28. package/dist/model/content/styles.d.mts +21 -0
  29. package/dist/model/content/styles.mjs +86 -0
  30. package/dist/model/content/tracking.d.mts +104 -0
  31. package/dist/model/content/tracking.mjs +313 -0
  32. package/dist/model/content/tree.d.mts +56 -9
  33. package/dist/model/content/tree.mjs +104 -31
  34. package/dist/model/customxml/CustomXmlPart.d.mts +158 -0
  35. package/dist/model/customxml/CustomXmlPart.mjs +415 -0
  36. package/dist/model/customxml/CustomXmlPartCollection.d.mts +68 -0
  37. package/dist/model/customxml/CustomXmlPartCollection.mjs +183 -0
  38. package/dist/model/customxml/XmlMapping.d.mts +46 -0
  39. package/dist/model/customxml/XmlMapping.mjs +98 -0
  40. package/dist/model/customxml/bindings.d.mts +41 -0
  41. package/dist/model/customxml/bindings.mjs +230 -0
  42. package/dist/model/customxml/index.d.mts +7 -0
  43. package/dist/model/customxml/index.mjs +9 -0
  44. package/dist/model/customxml/insert.d.mts +20 -0
  45. package/dist/model/customxml/insert.mjs +22 -0
  46. package/dist/model/customxml/kinds.d.mts +103 -0
  47. package/dist/model/customxml/kinds.mjs +267 -0
  48. package/dist/model/customxml/xpath.d.mts +52 -0
  49. package/dist/model/customxml/xpath.mjs +205 -0
  50. package/dist/model/fonts/CJKToEnglish.d.mts +2 -0
  51. package/dist/model/fonts/CJKToEnglish.mjs +16 -0
  52. package/dist/model/fonts/FontFallback.d.mts +58 -0
  53. package/dist/model/fonts/FontFallback.mjs +219 -0
  54. package/dist/model/fonts/IdentityPlusMapper.d.mts +7 -0
  55. package/dist/model/fonts/IdentityPlusMapper.mjs +19 -0
  56. package/dist/model/fonts/LanguageTagToScriptMapping.d.mts +7 -0
  57. package/dist/model/fonts/LanguageTagToScriptMapping.mjs +67 -0
  58. package/dist/model/fonts/Mapper.d.mts +190 -0
  59. package/dist/model/fonts/Mapper.mjs +481 -0
  60. package/dist/model/fonts/PhysicalFont.d.mts +34 -0
  61. package/dist/model/fonts/PhysicalFont.mjs +62 -0
  62. package/dist/model/fonts/RunFontSelector.d.mts +194 -0
  63. package/dist/model/fonts/RunFontSelector.mjs +441 -0
  64. package/dist/model/fonts/ThemeFonts.d.mts +17 -0
  65. package/dist/model/fonts/ThemeFonts.mjs +76 -0
  66. package/dist/model/fonts/defaultTheme.d.mts +37 -0
  67. package/dist/model/fonts/defaultTheme.mjs +57 -0
  68. package/dist/model/fonts/families.generated.d.mts +11 -0
  69. package/dist/model/fonts/families.generated.mjs +17 -0
  70. package/dist/model/fonts/fontsInUse.d.mts +39 -0
  71. package/dist/model/fonts/fontsInUse.mjs +183 -0
  72. package/dist/model/fonts/index.d.mts +14 -0
  73. package/dist/model/fonts/index.mjs +29 -0
  74. package/dist/model/fonts/lookup.d.mts +5 -0
  75. package/dist/model/fonts/lookup.mjs +12 -0
  76. package/dist/model/fonts/registry.d.mts +31 -0
  77. package/dist/model/fonts/registry.mjs +103 -0
  78. package/dist/model/fonts/scripts.d.mts +2 -0
  79. package/dist/model/fonts/scripts.mjs +78 -0
  80. package/dist/model/fonts/substitutions.generated.d.mts +30 -0
  81. package/dist/model/fonts/substitutions.generated.mjs +12 -0
  82. package/dist/model/fonts/themes.generated.d.mts +6 -0
  83. package/dist/model/fonts/themes.generated.mjs +11 -0
  84. package/dist/model/index.d.mts +5 -0
  85. package/dist/model/index.mjs +13 -1
  86. package/dist/model/listnumbering/Emulator.d.mts +187 -0
  87. package/dist/model/listnumbering/Emulator.mjs +332 -0
  88. package/dist/model/listnumbering/definitions.d.mts +251 -0
  89. package/dist/model/listnumbering/definitions.mjs +571 -0
  90. package/dist/model/listnumbering/formats.d.mts +25 -0
  91. package/dist/model/listnumbering/formats.mjs +320 -0
  92. package/dist/model/listnumbering/index.d.mts +4 -0
  93. package/dist/model/listnumbering/index.mjs +15 -0
  94. package/dist/model/listnumbering/state.d.mts +97 -0
  95. package/dist/model/listnumbering/state.mjs +179 -0
  96. package/dist/model/properties/PropertyResolver.d.mts +216 -0
  97. package/dist/model/properties/PropertyResolver.mjs +524 -0
  98. package/dist/model/properties/catalogue.d.mts +179 -0
  99. package/dist/model/properties/catalogue.mjs +860 -0
  100. package/dist/model/properties/index.d.mts +4 -0
  101. package/dist/model/properties/index.mjs +9 -0
  102. package/dist/model/properties/log.d.mts +7 -0
  103. package/dist/model/properties/log.mjs +25 -0
  104. package/dist/model/properties/styleUtil.d.mts +112 -0
  105. package/dist/model/properties/styleUtil.mjs +359 -0
  106. package/dist/node/index.d.mts +1 -0
  107. package/dist/node/index.mjs +12 -0
  108. package/dist/office-js/document.d.mts +111 -0
  109. package/dist/office-js/document.mjs +267 -0
  110. package/dist/office-js/enums.d.mts +187 -0
  111. package/dist/office-js/enums.mjs +176 -0
  112. package/dist/office-js/errors.d.mts +29 -0
  113. package/dist/office-js/errors.mjs +39 -0
  114. package/dist/office-js/extras.d.mts +5 -0
  115. package/dist/office-js/extras.mjs +29 -0
  116. package/dist/office-js/fragment.d.mts +10 -0
  117. package/dist/office-js/fragment.mjs +23 -0
  118. package/dist/office-js/index.d.mts +183 -0
  119. package/dist/office-js/index.mjs +54 -0
  120. package/dist/office-js/proxy.d.mts +72 -0
  121. package/dist/office-js/proxy.mjs +279 -0
  122. package/dist/office-js/run.d.mts +32 -0
  123. package/dist/office-js/run.mjs +66 -0
  124. package/dist/office-js/supported.d.mts +7 -0
  125. package/dist/office-js/supported.generated.d.mts +1 -0
  126. package/dist/office-js/supported.generated.mjs +235 -0
  127. package/dist/office-js/supported.mjs +53 -0
  128. package/dist/office-js/toApiScript.d.mts +25 -0
  129. package/dist/office-js/toApiScript.mjs +394 -0
  130. package/dist/opc/ContentTypes.d.mts +11 -0
  131. package/dist/opc/ContentTypes.mjs +14 -0
  132. package/dist/opc/DirectoryPartStore.d.mts +47 -0
  133. package/dist/opc/DirectoryPartStore.mjs +123 -0
  134. package/dist/opc/exceptions.d.mts +30 -0
  135. package/dist/opc/exceptions.mjs +38 -0
  136. package/dist/opc/index.d.mts +1 -1
  137. package/dist/opc/index.mjs +1 -1
  138. package/dist/packages/OpcPackage.d.mts +19 -1
  139. package/dist/packages/OpcPackage.mjs +29 -0
  140. package/dist/packages/PresentationMLPackage.d.mts +71 -1
  141. package/dist/packages/PresentationMLPackage.mjs +179 -0
  142. package/dist/packages/SpreadsheetMLPackage.d.mts +30 -1
  143. package/dist/packages/SpreadsheetMLPackage.mjs +78 -0
  144. package/dist/packages/WordprocessingMLPackage.d.mts +124 -2
  145. package/dist/packages/WordprocessingMLPackage.mjs +309 -2
  146. package/dist/packages/index.d.mts +1 -1
  147. package/dist/packages/index.mjs +1 -1
  148. package/dist/parts/DefaultXmlPart.d.mts +18 -0
  149. package/dist/parts/DefaultXmlPart.mjs +34 -5
  150. package/dist/parts/Namespaces.d.mts +18 -0
  151. package/dist/parts/Namespaces.mjs +20 -0
  152. package/dist/parts/Part.d.mts +6 -0
  153. package/dist/parts/Part.mjs +13 -0
  154. package/dist/parts/PartRegistry.mjs +13 -1
  155. package/dist/parts/XmlPart.d.mts +33 -0
  156. package/dist/parts/XmlPart.mjs +105 -2
  157. package/dist/parts/pml/defaults.d.mts +6 -0
  158. package/dist/parts/pml/defaults.mjs +53 -0
  159. package/dist/parts/pml/index.d.mts +66 -3
  160. package/dist/parts/pml/index.mjs +206 -7
  161. package/dist/parts/pml/placeholders.d.mts +71 -0
  162. package/dist/parts/pml/placeholders.mjs +141 -0
  163. package/dist/parts/sml/index.d.mts +80 -1
  164. package/dist/parts/sml/index.mjs +154 -1
  165. package/dist/parts/wml/comments.d.mts +1 -0
  166. package/dist/parts/wml/comments.mjs +179 -0
  167. package/dist/parts/wml/defaultNumbering.d.mts +1 -0
  168. package/dist/parts/wml/defaultNumbering.mjs +216 -0
  169. package/dist/parts/wml/index.d.mts +121 -0
  170. package/dist/parts/wml/index.mjs +291 -0
  171. package/dist/xml/dom.mjs +1 -2
  172. package/package.json +25 -4
@@ -1,20 +1,55 @@
1
1
  import type * as wml from '@docx4j/generated-objects-ts/modules/org_docx4j_wml';
2
2
  import { type UnderlineType } from '@docx4j/generated-objects-ts/builders/wml';
3
+ import type { ChangeTracker } from './tracking.mjs';
3
4
  export type { UnderlineType };
4
5
  /** Something with run properties: a run, or the paragraph mark. */
5
6
  export interface RPrHolder {
6
7
  rPr?: wml.RPr;
7
8
  }
9
+ /** What a `Font` needs to write a formatting revision (CR-002 phase F); undefined when tracking is off. */
10
+ export interface FontTracking {
11
+ tracker: ChangeTracker;
12
+ /** The runs that are this author's own insertion: Word changes those in place, with no w:rPrChange. */
13
+ ownInsertions: ReadonlySet<object>;
14
+ }
8
15
  /**
9
- * A subset of Office JS `Word.Font`, over the direct run properties (w:rPr) of one or more
10
- * runs. Reads report the first run's direct formatting (effective formatting through styles
11
- * is CR-001 Phase B); writes apply to every run in scope. Both go through the objects
12
- * package's one name-to-w:rPr mapping (`builders/wml` applyRunOptions / readRunOptions),
13
- * which its `r(text, opts)` builder shares.
16
+ * A subset of Office JS `Word.Font` over the run properties (`w:rPr`) of one or more runs.
17
+ *
18
+ * **Reads report effective formatting** (CR-001 Phase B step 2): the first run in scope
19
+ * resolved through the document defaults, the paragraph style and the character style, which
20
+ * is what Office JS reports. Pass an `effective` supplier for that; without one the view falls
21
+ * back to direct formatting, which is what `getFont({ direct: true })` asks for.
22
+ *
23
+ * **Writes are always direct**, and apply to every run in scope. Both directions go through
24
+ * the objects package's one name-to-`w:rPr` mapping (`builders/wml` applyRunOptions /
25
+ * readRunOptions), which its `r(text, opts)` builder shares.
26
+ *
27
+ * `name` is effective too since step 4: it is `RunFontSelector.asciiFontName` of the resolved
28
+ * properties, so a run whose font comes from the theme (`w:asciiTheme`) reads the face the
29
+ * theme names, and one that names no font anywhere reads the document default rather than ''.
30
+ * `getFont({ direct: true }).name` is still `w:rFonts/@w:ascii` of the run itself, and a
31
+ * package whose selector has not been built yet falls back to that.
14
32
  */
15
33
  export declare class Font {
16
34
  private readonly holders;
17
- constructor(holders: () => RPrHolder[]);
35
+ /** The change tracker and this author's own insertions, when the package is tracking changes. */
36
+ private readonly tracking?;
37
+ /** The effective `w:rPr` of the first run in scope; absent for a direct-formatting view. */
38
+ private readonly effective?;
39
+ /** The document font of that effective `w:rPr` with its theme reference resolved
40
+ * (`RunFontSelector.asciiFontName`); absent for a direct-formatting view, and undefined
41
+ * where the package has no selector yet. */
42
+ private readonly asciiFontName?;
43
+ constructor(holders: () => RPrHolder[],
44
+ /** The change tracker and this author's own insertions, when the package is tracking changes. */
45
+ tracking?: (() => FontTracking | undefined) | undefined,
46
+ /** The effective `w:rPr` of the first run in scope; absent for a direct-formatting view. */
47
+ effective?: ((rPr: wml.RPr | undefined) => wml.RPr | undefined) | undefined,
48
+ /** The document font of that effective `w:rPr` with its theme reference resolved
49
+ * (`RunFontSelector.asciiFontName`); absent for a direct-formatting view, and undefined
50
+ * where the package has no selector yet. */
51
+ asciiFontName?: ((rPr: wml.RPr | undefined) => string | undefined) | undefined);
52
+ private effectiveRPr;
18
53
  private read;
19
54
  private apply;
20
55
  get bold(): boolean;
@@ -31,10 +66,15 @@ export declare class Font {
31
66
  set subscript(v: boolean);
32
67
  get superscript(): boolean;
33
68
  set superscript(v: boolean);
34
- /** The ASCII font name (w:rFonts/@w:ascii); '' when not set directly. */
69
+ /**
70
+ * The document font formatting this run's Latin text: `w:rFonts/@w:ascii` of the effective
71
+ * properties, the face `w:asciiTheme` names, else `w:hAnsi`, else the document default
72
+ * (docx4j `RunFontSelector.asciiFontName`). '' only in a direct-formatting view with no
73
+ * `w:ascii`, or where the package's `RunFontSelector` has not been built yet.
74
+ */
35
75
  get name(): string;
36
76
  set name(v: string);
37
- /** Size in points (w:sz is half-points); 0 when not set directly. */
77
+ /** Size in points (w:sz is half-points); 0 only in a direct-formatting view with no w:sz. */
38
78
  get size(): number;
39
79
  set size(v: number);
40
80
  /** '#RRGGBB', or the raw value ('auto', a theme colour name) when it is not a hex triple; '' when not set. */
@@ -1,23 +1,53 @@
1
1
  import { applyRunOptions, readRunOptions } from '@docx4j/generated-objects-ts/builders/wml';
2
2
  /**
3
- * A subset of Office JS `Word.Font`, over the direct run properties (w:rPr) of one or more
4
- * runs. Reads report the first run's direct formatting (effective formatting through styles
5
- * is CR-001 Phase B); writes apply to every run in scope. Both go through the objects
6
- * package's one name-to-w:rPr mapping (`builders/wml` applyRunOptions / readRunOptions),
7
- * which its `r(text, opts)` builder shares.
3
+ * A subset of Office JS `Word.Font` over the run properties (`w:rPr`) of one or more runs.
4
+ *
5
+ * **Reads report effective formatting** (CR-001 Phase B step 2): the first run in scope
6
+ * resolved through the document defaults, the paragraph style and the character style, which
7
+ * is what Office JS reports. Pass an `effective` supplier for that; without one the view falls
8
+ * back to direct formatting, which is what `getFont({ direct: true })` asks for.
9
+ *
10
+ * **Writes are always direct**, and apply to every run in scope. Both directions go through
11
+ * the objects package's one name-to-`w:rPr` mapping (`builders/wml` applyRunOptions /
12
+ * readRunOptions), which its `r(text, opts)` builder shares.
13
+ *
14
+ * `name` is effective too since step 4: it is `RunFontSelector.asciiFontName` of the resolved
15
+ * properties, so a run whose font comes from the theme (`w:asciiTheme`) reads the face the
16
+ * theme names, and one that names no font anywhere reads the document default rather than ''.
17
+ * `getFont({ direct: true }).name` is still `w:rFonts/@w:ascii` of the run itself, and a
18
+ * package whose selector has not been built yet falls back to that.
8
19
  */
9
20
  export class Font {
10
- constructor(holders) {
21
+ constructor(holders,
22
+ /** The change tracker and this author's own insertions, when the package is tracking changes. */
23
+ tracking,
24
+ /** The effective `w:rPr` of the first run in scope; absent for a direct-formatting view. */
25
+ effective,
26
+ /** The document font of that effective `w:rPr` with its theme reference resolved
27
+ * (`RunFontSelector.asciiFontName`); absent for a direct-formatting view, and undefined
28
+ * where the package has no selector yet. */
29
+ asciiFontName) {
11
30
  this.holders = holders;
31
+ this.tracking = tracking;
32
+ this.effective = effective;
33
+ this.asciiFontName = asciiFontName;
12
34
  }
13
- read() {
35
+ effectiveRPr() {
14
36
  var _a;
15
- return readRunOptions((_a = this.holders()[0]) === null || _a === void 0 ? void 0 : _a.rPr);
37
+ const direct = (_a = this.holders()[0]) === null || _a === void 0 ? void 0 : _a.rPr;
38
+ return this.effective ? this.effective(direct) : direct;
39
+ }
40
+ read() {
41
+ return readRunOptions(this.effectiveRPr());
16
42
  }
17
43
  apply(opts) {
18
- var _a;
44
+ var _a, _b;
45
+ const tracking = (_a = this.tracking) === null || _a === void 0 ? void 0 : _a.call(this);
19
46
  for (const h of this.holders()) {
20
- (_a = h.rPr) !== null && _a !== void 0 ? _a : (h.rPr = { TYPE_NAME: 'org_docx4j_wml.RPr' });
47
+ (_b = h.rPr) !== null && _b !== void 0 ? _b : (h.rPr = { TYPE_NAME: 'org_docx4j_wml.RPr' });
48
+ // the properties as they are now become w:rPrChange, unless this run is our own insertion
49
+ if (tracking && !tracking.ownInsertions.has(h))
50
+ tracking.tracker.recordRPrChange(h.rPr);
21
51
  applyRunOptions(h.rPr, opts);
22
52
  }
23
53
  }
@@ -35,10 +65,22 @@ export class Font {
35
65
  set subscript(v) { this.apply({ subscript: v }); }
36
66
  get superscript() { return this.read().superscript; }
37
67
  set superscript(v) { this.apply({ superscript: v }); }
38
- /** The ASCII font name (w:rFonts/@w:ascii); '' when not set directly. */
39
- get name() { return this.read().name; }
68
+ /**
69
+ * The document font formatting this run's Latin text: `w:rFonts/@w:ascii` of the effective
70
+ * properties, the face `w:asciiTheme` names, else `w:hAnsi`, else the document default
71
+ * (docx4j `RunFontSelector.asciiFontName`). '' only in a direct-formatting view with no
72
+ * `w:ascii`, or where the package's `RunFontSelector` has not been built yet.
73
+ */
74
+ get name() {
75
+ if (this.asciiFontName !== undefined) {
76
+ const resolved = this.asciiFontName(this.effectiveRPr());
77
+ if (resolved !== undefined)
78
+ return resolved;
79
+ }
80
+ return this.read().name;
81
+ }
40
82
  set name(v) { this.apply({ name: v }); }
41
- /** Size in points (w:sz is half-points); 0 when not set directly. */
83
+ /** Size in points (w:sz is half-points); 0 only in a direct-formatting view with no w:sz. */
42
84
  get size() { return this.read().size; }
43
85
  set size(v) { this.apply({ size: v }); }
44
86
  /** '#RRGGBB', or the raw value ('auto', a theme colour name) when it is not a hex triple; '' when not set. */
@@ -0,0 +1,103 @@
1
+ import type * as wml from '@docx4j/generated-objects-ts/modules/org_docx4j_wml';
2
+ import type * as wp from '@docx4j/generated-objects-ts/modules/org_docx4j_dml_wordprocessingDrawing';
3
+ import { IMAGE_CONTENT_TYPES_BY_EXTENSION } from '../../opc/ContentTypes.mjs';
4
+ import { ImagePart } from '../../parts/BinaryPart.mjs';
5
+ import type { Part } from '../../parts/Part.mjs';
6
+ import { type Element } from './tree.mjs';
7
+ import type { Paragraph } from './Paragraph.mjs';
8
+ /** Office JS `Word.ImageFormat`, the subset Word writes into a docx. */
9
+ export type ImageFormat = 'Unsupported' | 'Bmp' | 'Emf' | 'Gif' | 'Jpeg' | 'Png' | 'Svg' | 'Tiff' | 'Wmf';
10
+ /** What a header reader answers: pixels and the resolution the file declares. */
11
+ export interface ImageInfo {
12
+ contentType: string;
13
+ /** The file extension a part of this kind is given ('png'). */
14
+ extension: string;
15
+ widthPx: number;
16
+ heightPx: number;
17
+ /** Horizontal and vertical resolution; 96 when the format carries none (docx4j's default). */
18
+ dpiX: number;
19
+ dpiY: number;
20
+ }
21
+ /**
22
+ * The size and kind of an image, from its header: PNG (IHDR and pHYs), JPEG (SOFn and the JFIF
23
+ * APP0 densities), GIF (the logical screen descriptor) and BMP (the DIB header, with its pixels
24
+ * per metre). docx4j reads these through XML Graphics Commons' ImageInfo; this is the same
25
+ * information, and the same 96 dpi default when a file declares none.
26
+ */
27
+ export declare function imageInfoOf(bytes: Uint8Array): ImageInfo;
28
+ /** The natural size of an image in EMU: pixels over the resolution it declares. */
29
+ export declare function naturalSizeEmu(info: ImageInfo): {
30
+ cx: number;
31
+ cy: number;
32
+ };
33
+ /** Options for `insertInlinePicture`; Office JS has none, this is the extension surface. */
34
+ export interface InlinePictureOptions {
35
+ /** wp:docPr/@name, the filename hint docx4j asks for. */
36
+ name?: string;
37
+ /** wp:docPr/@descr, Office JS altTextDescription. */
38
+ altTextDescription?: string;
39
+ /** wp:docPr/@title, Office JS altTextTitle; not written when absent. */
40
+ altTextTitle?: string;
41
+ /** The width in points; the height follows the aspect ratio unless it is given too. */
42
+ width?: number;
43
+ height?: number;
44
+ }
45
+ /** The run holding a new picture, plus the image part it added (the caller inserts the run). */
46
+ export interface NewPicture {
47
+ run: Element<wml.R>;
48
+ drawing: Element<wml.Drawing>;
49
+ imagePart: ImagePart;
50
+ relId: string;
51
+ }
52
+ /**
53
+ * Adds the image as a part of `source` (the main document part, or the header or footer the body
54
+ * belongs to) with a relationship, and builds the run that shows it. docx4j:
55
+ * `BinaryPartAbstractImage.createImagePart` then `createImageInline`.
56
+ */
57
+ export declare function addImage(source: Part, base64: string, scope: object, maxCx: number | undefined, options?: InlinePictureOptions): NewPicture;
58
+ /** The writable width of a section in EMU (page width less the margins), for scaling. */
59
+ export declare function writableWidthEmu(container: object): number | undefined;
60
+ /**
61
+ * A subset of Office JS `Word.InlinePicture` over a `w:drawing` holding a `wp:inline`. A view:
62
+ * the drawing, the run it is in and the paragraph. Sizes are points, as Office JS reports them.
63
+ */
64
+ export declare class InlinePicture {
65
+ /** The `w:drawing` element pair. */
66
+ readonly element: Element<wml.Drawing>;
67
+ /** The run holding the drawing, and the array holding the run. */
68
+ readonly run: Element<wml.R>;
69
+ readonly paragraph: Paragraph;
70
+ constructor(
71
+ /** The `w:drawing` element pair. */
72
+ element: Element<wml.Drawing>,
73
+ /** The run holding the drawing, and the array holding the run. */
74
+ run: Element<wml.R>, paragraph: Paragraph);
75
+ /** The wp:inline; a floating picture (wp:anchor) is not an InlinePicture, and throws. */
76
+ get inline(): wp.Inline;
77
+ /** Width in points, as Office JS. */
78
+ get width(): number;
79
+ set width(points: number);
80
+ get height(): number;
81
+ set height(points: number);
82
+ /** wp:docPr/@descr (Word's alt text). */
83
+ get altTextDescription(): string;
84
+ set altTextDescription(text: string);
85
+ /** wp:docPr/@title (typed since objects 0.1.3; before that the attribute was not marshalled, CR-002 section 8). */
86
+ get altTextTitle(): string;
87
+ set altTextTitle(text: string);
88
+ /** The kind of image, from the content type of the part the picture embeds. */
89
+ get imageFormat(): ImageFormat;
90
+ /** The relationship id of the embedded image (r:embed), if any (extension). */
91
+ get relId(): string | undefined;
92
+ /** The image's bytes as base64 (Office JS getBase64ImageSrc). */
93
+ getBase64(): Promise<string>;
94
+ /** Office JS's name for `getBase64()`. */
95
+ getBase64ImageSrc(): Promise<string>;
96
+ /** The image part this picture embeds (extension). */
97
+ imagePart(): ImagePart | undefined;
98
+ /** Removes the picture; the run goes too when the picture was all it held. */
99
+ delete(): void;
100
+ private pic;
101
+ private resize;
102
+ }
103
+ export { IMAGE_CONTENT_TYPES_BY_EXTENSION };
@@ -0,0 +1,361 @@
1
+ import { inlinePicture } from '@docx4j/generated-objects-ts/builders/wml';
2
+ import { Docx4JException } from '../../opc/exceptions.mjs';
3
+ import { ContentTypes, IMAGE_CONTENT_TYPES_BY_EXTENSION } from '../../opc/ContentTypes.mjs';
4
+ import { base64Decode, base64Encode } from '../../xml/dom.mjs';
5
+ import { ImagePart } from '../../parts/BinaryPart.mjs';
6
+ import { PartName } from '../../opc/PartName.mjs';
7
+ import { find, runOf } from './tree.mjs';
8
+ /** English Metric Units per inch, and per twip (docx4j UnitsOfMeasurement). */
9
+ const EMU_PER_INCH = 914400;
10
+ const EMU_PER_TWIP = 635;
11
+ /** docx4j's ImageInfo default when a format carries no resolution. */
12
+ const DEFAULT_DPI = 96;
13
+ const EMU_PER_POINT = 12700;
14
+ /**
15
+ * The size and kind of an image, from its header: PNG (IHDR and pHYs), JPEG (SOFn and the JFIF
16
+ * APP0 densities), GIF (the logical screen descriptor) and BMP (the DIB header, with its pixels
17
+ * per metre). docx4j reads these through XML Graphics Commons' ImageInfo; this is the same
18
+ * information, and the same 96 dpi default when a file declares none.
19
+ */
20
+ export function imageInfoOf(bytes) {
21
+ const png = pngInfo(bytes);
22
+ if (png)
23
+ return png;
24
+ const jpeg = jpegInfo(bytes);
25
+ if (jpeg)
26
+ return jpeg;
27
+ const gif = gifInfo(bytes);
28
+ if (gif)
29
+ return gif;
30
+ const bmp = bmpInfo(bytes);
31
+ if (bmp)
32
+ return bmp;
33
+ const head = Array.from(bytes.subarray(0, 8)).map((b) => b.toString(16).padStart(2, '0')).join(' ');
34
+ throw new Docx4JException(`Unsupported image format (first bytes ${head}); PNG, JPEG, GIF and BMP are read here`);
35
+ }
36
+ function be16(b, at) { return (b[at] << 8) | b[at + 1]; }
37
+ function be32(b, at) { return ((b[at] << 24) >>> 0) + (b[at + 1] << 16) + (b[at + 2] << 8) + b[at + 3]; }
38
+ function le16(b, at) { return b[at] | (b[at + 1] << 8); }
39
+ function le32(b, at) { return (b[at] | (b[at + 1] << 8) | (b[at + 2] << 16) | (b[at + 3] << 24)) >>> 0; }
40
+ function sle32(b, at) { return b[at] | (b[at + 1] << 8) | (b[at + 2] << 16) | (b[at + 3] << 24); }
41
+ const PNG_SIGNATURE = [0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a];
42
+ function pngInfo(b) {
43
+ if (b.length < 24 || PNG_SIGNATURE.some((v, i) => b[i] !== v))
44
+ return undefined;
45
+ const info = {
46
+ contentType: ContentTypes.IMAGE_PNG, extension: 'png',
47
+ widthPx: be32(b, 16), heightPx: be32(b, 20), dpiX: DEFAULT_DPI, dpiY: DEFAULT_DPI,
48
+ };
49
+ // pHYs: pixels per unit, unit 1 = metre.
50
+ for (let at = 8; at + 8 <= b.length;) {
51
+ const length = be32(b, at);
52
+ const type = String.fromCharCode(b[at + 4], b[at + 5], b[at + 6], b[at + 7]);
53
+ if (type === 'pHYs' && at + 8 + 9 <= b.length) {
54
+ if (b[at + 8 + 8] === 1) {
55
+ info.dpiX = Math.round(be32(b, at + 8) * 0.0254) || DEFAULT_DPI;
56
+ info.dpiY = Math.round(be32(b, at + 12) * 0.0254) || DEFAULT_DPI;
57
+ }
58
+ break;
59
+ }
60
+ if (type === 'IDAT' || type === 'IEND')
61
+ break;
62
+ at += 12 + length;
63
+ }
64
+ return info;
65
+ }
66
+ function jpegInfo(b) {
67
+ if (b.length < 4 || b[0] !== 0xff || b[1] !== 0xd8)
68
+ return undefined;
69
+ const info = {
70
+ contentType: ContentTypes.IMAGE_JPEG, extension: 'jpeg',
71
+ widthPx: 0, heightPx: 0, dpiX: DEFAULT_DPI, dpiY: DEFAULT_DPI,
72
+ };
73
+ for (let at = 2; at + 4 <= b.length;) {
74
+ if (b[at] !== 0xff) {
75
+ at++;
76
+ continue;
77
+ }
78
+ const marker = b[at + 1];
79
+ if (marker === 0xd8 || marker === 0x01 || (marker >= 0xd0 && marker <= 0xd7)) {
80
+ at += 2;
81
+ continue;
82
+ }
83
+ if (marker === 0xd9 || marker === 0xda)
84
+ break;
85
+ const length = be16(b, at + 2);
86
+ const data = at + 4;
87
+ if (marker === 0xe0 && b.length >= data + 12 && String.fromCharCode(b[data], b[data + 1], b[data + 2], b[data + 3]) === 'JFIF') {
88
+ const units = b[data + 7];
89
+ const x = be16(b, data + 8);
90
+ const y = be16(b, data + 10);
91
+ if (units === 1 && x && y) {
92
+ info.dpiX = x;
93
+ info.dpiY = y;
94
+ }
95
+ else if (units === 2 && x && y) {
96
+ info.dpiX = Math.round(x * 2.54);
97
+ info.dpiY = Math.round(y * 2.54);
98
+ }
99
+ }
100
+ // SOF0..SOF15, less the marker-only and non-frame ones
101
+ if (marker >= 0xc0 && marker <= 0xcf && marker !== 0xc4 && marker !== 0xc8 && marker !== 0xcc) {
102
+ info.heightPx = be16(b, data + 1);
103
+ info.widthPx = be16(b, data + 3);
104
+ if (info.widthPx && info.heightPx)
105
+ return info;
106
+ }
107
+ at += 2 + length;
108
+ }
109
+ return info.widthPx && info.heightPx ? info : undefined;
110
+ }
111
+ function gifInfo(b) {
112
+ if (b.length < 10 || b[0] !== 0x47 || b[1] !== 0x49 || b[2] !== 0x46)
113
+ return undefined;
114
+ return { contentType: ContentTypes.IMAGE_GIF, extension: 'gif', widthPx: le16(b, 6), heightPx: le16(b, 8), dpiX: DEFAULT_DPI, dpiY: DEFAULT_DPI };
115
+ }
116
+ function bmpInfo(b) {
117
+ if (b.length < 26 || b[0] !== 0x42 || b[1] !== 0x4d)
118
+ return undefined;
119
+ const headerSize = le32(b, 14);
120
+ const info = { contentType: ContentTypes.IMAGE_BMP, extension: 'bmp', widthPx: 0, heightPx: 0, dpiX: DEFAULT_DPI, dpiY: DEFAULT_DPI };
121
+ if (headerSize === 12) {
122
+ info.widthPx = le16(b, 18);
123
+ info.heightPx = le16(b, 20);
124
+ return info;
125
+ }
126
+ info.widthPx = sle32(b, 18);
127
+ info.heightPx = Math.abs(sle32(b, 22));
128
+ if (b.length >= 46) {
129
+ const x = sle32(b, 38);
130
+ const y = sle32(b, 42);
131
+ if (x > 0)
132
+ info.dpiX = Math.round(x * 0.0254) || DEFAULT_DPI;
133
+ if (y > 0)
134
+ info.dpiY = Math.round(y * 0.0254) || DEFAULT_DPI;
135
+ }
136
+ return info;
137
+ }
138
+ /** The natural size of an image in EMU: pixels over the resolution it declares. */
139
+ export function naturalSizeEmu(info) {
140
+ return {
141
+ cx: Math.max(1, Math.round((info.widthPx / (info.dpiX || DEFAULT_DPI)) * EMU_PER_INCH)),
142
+ cy: Math.max(1, Math.round((info.heightPx / (info.dpiY || DEFAULT_DPI)) * EMU_PER_INCH)),
143
+ };
144
+ }
145
+ /**
146
+ * Adds the image as a part of `source` (the main document part, or the header or footer the body
147
+ * belongs to) with a relationship, and builds the run that shows it. docx4j:
148
+ * `BinaryPartAbstractImage.createImagePart` then `createImageInline`.
149
+ */
150
+ export function addImage(source, base64, scope, maxCx, options = {}) {
151
+ var _a, _b;
152
+ const pkg = source.package;
153
+ if (!pkg)
154
+ throw new Docx4JException('This part is not in a package yet; add it before adding an image');
155
+ const bytes = base64Decode(base64);
156
+ const info = imageInfoOf(bytes);
157
+ const imagePart = new ImagePart(freeImageName(pkg, info.extension), info.contentType);
158
+ imagePart.setBytes(bytes);
159
+ const rel = source.addTargetPart(imagePart);
160
+ let { cx, cy } = naturalSizeEmu(info);
161
+ if (options.width !== undefined) {
162
+ const wanted = Math.round(options.width * EMU_PER_POINT);
163
+ cy = options.height !== undefined ? Math.round(options.height * EMU_PER_POINT) : Math.max(1, Math.round((cy * wanted) / cx));
164
+ cx = wanted;
165
+ }
166
+ else if (options.height !== undefined) {
167
+ const wanted = Math.round(options.height * EMU_PER_POINT);
168
+ cx = Math.max(1, Math.round((cx * wanted) / cy));
169
+ cy = wanted;
170
+ }
171
+ else if (maxCx !== undefined && cx > maxCx) {
172
+ // docx4j CxCy.scale: an image wider than the text area is scaled down, keeping the ratio.
173
+ cy = Math.max(1, Math.round((cy * maxCx) / cx));
174
+ cx = maxCx;
175
+ }
176
+ const id = nextDrawingId(scope);
177
+ const name = (_a = options.name) !== null && _a !== void 0 ? _a : `Picture ${id}`;
178
+ const drawing = inlinePicture(rel.id, {
179
+ cx, cy, id, name,
180
+ descr: (_b = options.altTextDescription) !== null && _b !== void 0 ? _b : '',
181
+ ...(options.altTextTitle === undefined ? {} : { title: options.altTextTitle }),
182
+ });
183
+ return { run: runOf([drawing]), drawing, imagePart, relId: rel.id };
184
+ }
185
+ /** `/word/media/imageN.<ext>`, the first N free in the package (docx4j getNewPartName). */
186
+ function freeImageName(pkg, extension) {
187
+ for (let i = 1;; i++) {
188
+ const name = PartName.of(`/word/media/image${i}.${extension}`);
189
+ if (!pkg.getPart(name))
190
+ return name;
191
+ }
192
+ }
193
+ /** An id no other drawing in this part uses: wp:docPr and pic:cNvPr are the same type. */
194
+ function nextDrawingId(scope) {
195
+ let max = 0;
196
+ for (const props of find(scope, 'org_docx4j_dml.CTNonVisualDrawingProps')) {
197
+ if (typeof props.id === 'number' && props.id > max)
198
+ max = props.id;
199
+ }
200
+ return max + 1;
201
+ }
202
+ /** The writable width of a section in EMU (page width less the margins), for scaling. */
203
+ export function writableWidthEmu(container) {
204
+ var _a, _b, _c, _d;
205
+ const sectPr = container.sectPr;
206
+ const pgSz = sectPr === null || sectPr === void 0 ? void 0 : sectPr.pgSz;
207
+ if (!(pgSz === null || pgSz === void 0 ? void 0 : pgSz.w))
208
+ return undefined;
209
+ const left = (_b = (_a = sectPr === null || sectPr === void 0 ? void 0 : sectPr.pgMar) === null || _a === void 0 ? void 0 : _a.left) !== null && _b !== void 0 ? _b : 0;
210
+ const right = (_d = (_c = sectPr === null || sectPr === void 0 ? void 0 : sectPr.pgMar) === null || _c === void 0 ? void 0 : _c.right) !== null && _d !== void 0 ? _d : 0;
211
+ const twips = pgSz.w - left - right;
212
+ return twips > 0 ? twips * EMU_PER_TWIP : undefined;
213
+ }
214
+ const FORMAT_BY_CONTENT_TYPE = {
215
+ [ContentTypes.IMAGE_PNG]: 'Png', [ContentTypes.IMAGE_X_PNG]: 'Png', [ContentTypes.IMAGE_JPEG]: 'Jpeg',
216
+ [ContentTypes.IMAGE_GIF]: 'Gif', [ContentTypes.IMAGE_BMP]: 'Bmp', [ContentTypes.IMAGE_TIFF]: 'Tiff',
217
+ [ContentTypes.IMAGE_SVG]: 'Svg', [ContentTypes.IMAGE_EMF]: 'Emf', [ContentTypes.IMAGE_EMF2]: 'Emf',
218
+ [ContentTypes.IMAGE_WMF]: 'Wmf',
219
+ };
220
+ /**
221
+ * A subset of Office JS `Word.InlinePicture` over a `w:drawing` holding a `wp:inline`. A view:
222
+ * the drawing, the run it is in and the paragraph. Sizes are points, as Office JS reports them.
223
+ */
224
+ export class InlinePicture {
225
+ constructor(
226
+ /** The `w:drawing` element pair. */
227
+ element,
228
+ /** The run holding the drawing, and the array holding the run. */
229
+ run, paragraph) {
230
+ this.element = element;
231
+ this.run = run;
232
+ this.paragraph = paragraph;
233
+ }
234
+ /** The wp:inline; a floating picture (wp:anchor) is not an InlinePicture, and throws. */
235
+ get inline() {
236
+ var _a;
237
+ const first = (_a = this.element.value.anchorOrInline) === null || _a === void 0 ? void 0 : _a[0];
238
+ if (!first || first.TYPE_NAME !== 'org_docx4j_dml_wordprocessingDrawing.Inline') {
239
+ throw new Docx4JException('This drawing is not an inline picture (wp:anchor is a floating shape)');
240
+ }
241
+ return first;
242
+ }
243
+ /** Width in points, as Office JS. */
244
+ get width() {
245
+ return this.inline.extent.cx / EMU_PER_POINT;
246
+ }
247
+ set width(points) {
248
+ this.resize(Math.round(points * EMU_PER_POINT), undefined);
249
+ }
250
+ get height() {
251
+ return this.inline.extent.cy / EMU_PER_POINT;
252
+ }
253
+ set height(points) {
254
+ this.resize(undefined, Math.round(points * EMU_PER_POINT));
255
+ }
256
+ /** wp:docPr/@descr (Word's alt text). */
257
+ get altTextDescription() {
258
+ var _a;
259
+ return (_a = this.inline.docPr.descr) !== null && _a !== void 0 ? _a : '';
260
+ }
261
+ set altTextDescription(text) {
262
+ this.inline.docPr.descr = text;
263
+ const p = this.pic();
264
+ if (p)
265
+ p.nvPicPr.cNvPr.descr = text;
266
+ }
267
+ /** wp:docPr/@title (typed since objects 0.1.3; before that the attribute was not marshalled, CR-002 section 8). */
268
+ get altTextTitle() {
269
+ var _a;
270
+ return (_a = this.inline.docPr.title) !== null && _a !== void 0 ? _a : '';
271
+ }
272
+ set altTextTitle(text) {
273
+ this.inline.docPr.title = text;
274
+ }
275
+ /** The kind of image, from the content type of the part the picture embeds. */
276
+ get imageFormat() {
277
+ var _a;
278
+ const part = this.imagePart();
279
+ return (_a = (part && FORMAT_BY_CONTENT_TYPE[part.contentType])) !== null && _a !== void 0 ? _a : 'Unsupported';
280
+ }
281
+ /** The relationship id of the embedded image (r:embed), if any (extension). */
282
+ get relId() {
283
+ var _a, _b;
284
+ return (_b = (_a = this.pic()) === null || _a === void 0 ? void 0 : _a.blipFill.blip) === null || _b === void 0 ? void 0 : _b.embed;
285
+ }
286
+ /** The image's bytes as base64 (Office JS getBase64ImageSrc). */
287
+ async getBase64() {
288
+ const part = this.imagePart();
289
+ if (!part)
290
+ throw new Docx4JException('This picture has no image part (a linked or missing image)');
291
+ return base64Encode(await part.getBytes());
292
+ }
293
+ /** Office JS's name for `getBase64()`. */
294
+ getBase64ImageSrc() {
295
+ return this.getBase64();
296
+ }
297
+ /** The image part this picture embeds (extension). */
298
+ imagePart() {
299
+ var _a;
300
+ const relId = this.relId;
301
+ const source = this.paragraph.parentBody.part;
302
+ if (!relId || !source)
303
+ return undefined;
304
+ const part = (_a = source.relationshipsPart) === null || _a === void 0 ? void 0 : _a.getPart(relId);
305
+ return part instanceof ImagePart ? part : undefined;
306
+ }
307
+ /** Removes the picture; the run goes too when the picture was all it held. */
308
+ delete() {
309
+ var _a;
310
+ const content = this.run.value.content;
311
+ const at = (_a = content === null || content === void 0 ? void 0 : content.indexOf(this.element)) !== null && _a !== void 0 ? _a : -1;
312
+ if (content && at >= 0)
313
+ content.splice(at, 1);
314
+ if (!content || content.length === 0) {
315
+ const holder = runHolderOf(this.paragraph.p, this.run);
316
+ const i = holder ? holder.indexOf(this.run) : -1;
317
+ if (holder && i >= 0)
318
+ holder.splice(i, 1);
319
+ }
320
+ }
321
+ pic() {
322
+ const any = this.inline.graphic.graphicData.any;
323
+ const first = any === null || any === void 0 ? void 0 : any[0];
324
+ return first && typeof first === 'object' && 'value' in first ? first.value : undefined;
325
+ }
326
+ resize(cx, cy) {
327
+ var _a, _b;
328
+ const extent = this.inline.extent;
329
+ const ratio = extent.cy / extent.cx;
330
+ const w = cx !== null && cx !== void 0 ? cx : Math.max(1, Math.round(cy / ratio));
331
+ const h = cy !== null && cy !== void 0 ? cy : Math.max(1, Math.round(cx * ratio));
332
+ extent.cx = w;
333
+ extent.cy = h;
334
+ const ext = (_b = (_a = this.pic()) === null || _a === void 0 ? void 0 : _a.spPr.xfrm) === null || _b === void 0 ? void 0 : _b.ext;
335
+ if (ext) {
336
+ ext.cx = w;
337
+ ext.cy = h;
338
+ }
339
+ }
340
+ }
341
+ /** The array holding a run inside a paragraph (the paragraph's content, or a holder's). */
342
+ function runHolderOf(p, run) {
343
+ const seek = (items) => {
344
+ var _a, _b, _c;
345
+ if (!items)
346
+ return undefined;
347
+ if (items.includes(run))
348
+ return items;
349
+ for (const item of items) {
350
+ const v = item.value;
351
+ if (typeof v !== 'object' || v === null)
352
+ continue;
353
+ const nested = seek((_b = (_a = v.content) !== null && _a !== void 0 ? _a : v.customXmlOrSmartTagOrSdt) !== null && _b !== void 0 ? _b : (_c = v.sdtContent) === null || _c === void 0 ? void 0 : _c.content);
354
+ if (nested)
355
+ return nested;
356
+ }
357
+ return undefined;
358
+ };
359
+ return seek(p.content);
360
+ }
361
+ export { IMAGE_CONTENT_TYPES_BY_EXTENSION };