reamkit 1.33.1 → 1.34.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 (74) hide show
  1. package/README.md +13 -10
  2. package/dist/esm/core/converter/facade.js +1 -0
  3. package/dist/esm/core/converter/ream.d.ts +6 -6
  4. package/dist/esm/core/converter/ream.js +11 -5
  5. package/dist/esm/core/crypto/index.js +1 -2
  6. package/dist/esm/core/document-model/index.d.ts +1 -1
  7. package/dist/esm/core/document-model/types.d.ts +15 -0
  8. package/dist/esm/core/drawingml/text-warp.d.ts +34 -0
  9. package/dist/esm/core/drawingml/text-warp.js +49 -1
  10. package/dist/esm/core/font/index.d.ts +1 -1
  11. package/dist/esm/core/font/index.js +8 -0
  12. package/dist/esm/core/font/indic-shaper.d.ts +47 -0
  13. package/dist/esm/core/font/indic-shaper.js +1146 -0
  14. package/dist/esm/core/font/indic-table.d.ts +12 -0
  15. package/dist/esm/core/font/indic-table.js +463 -0
  16. package/dist/esm/core/font/measure.d.ts +45 -4
  17. package/dist/esm/core/font/measure.js +253 -14
  18. package/dist/esm/core/font/opentype-lookups.d.ts +214 -0
  19. package/dist/esm/core/font/opentype-lookups.js +1112 -0
  20. package/dist/esm/core/fonts/families.d.ts +4 -0
  21. package/dist/esm/core/fonts/families.js +43 -12
  22. package/dist/esm/core/fonts/provider.js +1 -0
  23. package/dist/esm/core/fonts/remote-fonts.d.ts +4 -2
  24. package/dist/esm/core/fonts/remote-fonts.js +36 -0
  25. package/dist/esm/core/fonts/scripts.d.ts +19 -0
  26. package/dist/esm/core/fonts/scripts.js +96 -1
  27. package/dist/esm/core/numbering/apply.js +12 -8
  28. package/dist/esm/core/style-cascade/index.d.ts +1 -1
  29. package/dist/esm/core/style-cascade/index.js +2 -2
  30. package/dist/esm/core/style-cascade/resolver.d.ts +9 -0
  31. package/dist/esm/core/style-cascade/resolver.js +31 -3
  32. package/dist/esm/core/style-cascade/table.js +3 -8
  33. package/dist/esm/excel/xlsx-to-pdf.js +1 -0
  34. package/dist/esm/index.js +1 -0
  35. package/dist/esm/layout/line-paint.d.ts +5 -4
  36. package/dist/esm/layout/line-paint.js +6 -5
  37. package/dist/esm/layout/page-doc.d.ts +26 -0
  38. package/dist/esm/layout/styled-layout.d.ts +8 -1
  39. package/dist/esm/layout/styled-layout.js +459 -112
  40. package/dist/esm/layout/undrawn.d.ts +18 -0
  41. package/dist/esm/layout/undrawn.js +59 -0
  42. package/dist/esm/pdf/cid-font.d.ts +5 -0
  43. package/dist/esm/pdf/cid-font.js +8 -2
  44. package/dist/esm/pdf/styled-page-emitter.js +117 -28
  45. package/dist/esm/pdf/text-page-renderer.js +1 -0
  46. package/dist/esm/pdf-reader/content.d.ts +1 -1
  47. package/dist/esm/pdf-reader/content.js +48 -13
  48. package/dist/esm/pdf-reader/embedded-fonts.js +1 -0
  49. package/dist/esm/pdf-reader/encodings.d.ts +9 -0
  50. package/dist/esm/pdf-reader/encodings.js +66 -1
  51. package/dist/esm/pdf-reader/face-outlines.js +1 -0
  52. package/dist/esm/pdf-reader/text.d.ts +10 -0
  53. package/dist/esm/pdf-reader/text.js +25 -3
  54. package/dist/esm/pdf-reader/vector.js +2 -2
  55. package/dist/esm/pptx/embedded-fonts.js +1 -0
  56. package/dist/esm/pptx/slide-parser.js +22 -7
  57. package/dist/esm/pptx/sp-helpers.d.ts +3 -2
  58. package/dist/esm/pptx/sp-helpers.js +9 -4
  59. package/dist/esm/svg/svg-text.js +30 -7
  60. package/dist/esm/word/doc/doc-reader.js +148 -57
  61. package/dist/esm/word/doc/doc-text.d.ts +80 -11
  62. package/dist/esm/word/doc/doc-text.js +438 -75
  63. package/dist/esm/word/document-parser.js +48 -28
  64. package/dist/esm/word/docx-to-pdf.d.ts +5 -5
  65. package/dist/esm/word/docx-to-pdf.js +9 -7
  66. package/dist/esm/word/docx-writer.js +3 -5
  67. package/dist/esm/word/font-embed.js +1 -0
  68. package/dist/esm/word/font-table.js +1 -0
  69. package/dist/esm/word/numbering-parser.js +3 -0
  70. package/dist/esm/word/paragraph-properties.js +2 -2
  71. package/dist/esm/word/run-properties.js +2 -2
  72. package/dist/esm/word/xml-helpers.d.ts +18 -0
  73. package/dist/esm/word/xml-helpers.js +48 -1
  74. package/package.json +3 -3
@@ -4,6 +4,10 @@ import { FlowDoc } from '../ir/flow.js';
4
4
  * The curated substitute families a document's text needs, always including the
5
5
  * sans default as the fallback for unstyled runs, math and chart labels.
6
6
  *
7
+ * §17.3.2.26 — a run names a family per slot, and the text decides which slots
8
+ * are drawn from (see {@link fontSlotOf}): the hAnsi family of a document all
9
+ * in ASCII is never asked for, and is not fetched.
10
+ *
7
11
  * @param flow The parsed document.
8
12
  * @returns One {@link FamilyKey} per distinct family the document names.
9
13
  */
@@ -1,33 +1,64 @@
1
1
  import { resolveFamilyKey } from "./remote-fonts.js";
2
+ import { fontSlotOf } from "./scripts.js";
2
3
  //#region src/core/fonts/families.ts
4
+ var SLOTS = [
5
+ "ascii",
6
+ "hAnsi",
7
+ "eastAsia",
8
+ "cs"
9
+ ];
3
10
  /**
4
11
  * The curated substitute families a document's text needs, always including the
5
12
  * sans default as the fallback for unstyled runs, math and chart labels.
6
13
  *
14
+ * §17.3.2.26 — a run names a family per slot, and the text decides which slots
15
+ * are drawn from (see {@link fontSlotOf}): the hAnsi family of a document all
16
+ * in ASCII is never asked for, and is not fetched.
17
+ *
7
18
  * @param flow The parsed document.
8
19
  * @returns One {@link FamilyKey} per distinct family the document names.
9
20
  */
10
21
  function familiesInFlow(flow) {
11
- const names = /* @__PURE__ */ new Set();
12
- const add = (name) => {
13
- if (name !== void 0 && name.trim() !== "") names.add(name.trim().toLowerCase());
22
+ const names = {
23
+ ascii: /* @__PURE__ */ new Set(),
24
+ hAnsi: /* @__PURE__ */ new Set(),
25
+ eastAsia: /* @__PURE__ */ new Set(),
26
+ cs: /* @__PURE__ */ new Set()
27
+ };
28
+ const used = /* @__PURE__ */ new Set(["ascii"]);
29
+ const add = (props) => {
30
+ for (const slot of SLOTS) {
31
+ const name = props?.fontFamily?.[slot];
32
+ if (name !== void 0 && name.trim() !== "") names[slot].add(name.trim().toLowerCase());
33
+ }
34
+ if (props?.rtl === true) used.add("cs");
35
+ };
36
+ const read = (text) => {
37
+ for (const ch of text) {
38
+ if (used.size === SLOTS.length) return;
39
+ const cp = ch.codePointAt(0) ?? 0;
40
+ if (cp > 127) used.add(fontSlotOf(cp));
41
+ }
14
42
  };
15
- add(flow.styles.defaultRunProperties.fontFamily?.ascii);
43
+ add(flow.styles.defaultRunProperties);
16
44
  for (const style of flow.styles.styles.values()) {
17
- add(style.runProperties.fontFamily?.ascii);
18
- add(style.paragraphProperties.runProperties?.fontFamily?.ascii);
19
- add(style.tableLayer?.runProperties?.fontFamily?.ascii);
20
- for (const c of style.tableConditions ?? []) add(c.layer.runProperties?.fontFamily?.ascii);
45
+ add(style.runProperties);
46
+ add(style.paragraphProperties.runProperties);
47
+ add(style.tableLayer?.runProperties);
48
+ for (const c of style.tableConditions ?? []) add(c.layer.runProperties);
21
49
  }
22
- for (const abstract of flow.numbering?.abstractNums.values() ?? []) for (const level of abstract.levels.values()) add(level.runProperties.fontFamily?.ascii);
50
+ for (const abstract of flow.numbering?.abstractNums.values() ?? []) for (const level of abstract.levels.values()) add(level.runProperties);
23
51
  const shapeText = (shape) => {
24
52
  if (shape.text) visit(shape.text.content);
25
53
  for (const child of shape.children ?? []) shapeText(child.shape);
26
54
  };
27
55
  const visit = (elements) => {
28
56
  for (const el of elements) if (el.kind === "paragraph") {
29
- add(el.paragraph.properties.runProperties?.fontFamily?.ascii);
30
- for (const run of el.paragraph.runs) add(run.properties.fontFamily?.ascii);
57
+ add(el.paragraph.properties.runProperties);
58
+ for (const run of el.paragraph.runs) {
59
+ add(run.properties);
60
+ read(run.text);
61
+ }
31
62
  } else if (el.kind === "table") {
32
63
  for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
33
64
  visit(el.table.overlay?.drawings ?? []);
@@ -39,7 +70,7 @@ function familiesInFlow(flow) {
39
70
  for (const note of flow.endnotes?.values() ?? []) visit(note);
40
71
  for (const comment of flow.comments?.values() ?? []) visit(comment.content);
41
72
  const keys = /* @__PURE__ */ new Set(["arimo"]);
42
- for (const name of names) keys.add(resolveFamilyKey(name));
73
+ for (const slot of used) for (const name of names[slot]) keys.add(resolveFamilyKey(name));
43
74
  return keys;
44
75
  }
45
76
  //#endregion
@@ -1,5 +1,6 @@
1
1
  import { fetchFontSet, resolveFamilyKey } from "./remote-fonts.js";
2
2
  import { pickVariant } from "../font/font-registry.js";
3
+ import "../font/index.js";
3
4
  //#region src/core/fonts/provider.ts
4
5
  /** The shared `none` answer — a provider that cannot satisfy a request. */
5
6
  var NO_FONT = { kind: "none" };
@@ -5,13 +5,15 @@ export type FamilyKey = 'arimo' | 'tinos' | 'cousine' | 'carlito' | 'caladea';
5
5
  * A WRITING SYSTEM the curated five cannot draw at all. They are Latin families
6
6
  * — Greek, Cyrillic and Hebrew ride along, everything else is a notdef box —
7
7
  * and 2145 characters of the corpus fall outside them: Han, Kana, Hangul,
8
- * Arabic, the geometric symbols a form's checkbox is made of.
8
+ * Arabic, the geometric symbols a form's checkbox is made of. The scripts of
9
+ * India are outside them too, and a line of Hindi, Tamil or Bengali came out
10
+ * as a row of boxes.
9
11
  *
10
12
  * These are fetched only when a document actually holds such a character, and
11
13
  * only in the regular weight: Noto Sans SC is ten megabytes, and a bold run in
12
14
  * it is better stroked (see `SyntheticFace`) than downloaded four times over.
13
15
  */
14
- export type ScriptKey = 'jp' | 'kr' | 'sc' | 'arabic' | 'hebrew' | 'thai' | 'symbols1' | 'symbols2';
16
+ export type ScriptKey = 'jp' | 'kr' | 'sc' | 'arabic' | 'hebrew' | 'thai' | 'devanagari' | 'bengali' | 'gurmukhi' | 'gujarati' | 'oriya' | 'tamil' | 'telugu' | 'kannada' | 'malayalam' | 'symbols1' | 'symbols2';
15
17
  /** Either kind of substitute — a Latin family or a per-script face. */
16
18
  export type SubstituteKey = FamilyKey | ScriptKey;
17
19
  /** Whether a substitute key names a writing system rather than a Latin family. */
@@ -31,6 +31,42 @@ var SCRIPTS = {
31
31
  pkg: "noto-sans-thai",
32
32
  file: "NotoSansThai"
33
33
  },
34
+ devanagari: {
35
+ pkg: "noto-sans-devanagari",
36
+ file: "NotoSansDevanagari"
37
+ },
38
+ bengali: {
39
+ pkg: "noto-sans-bengali",
40
+ file: "NotoSansBengali"
41
+ },
42
+ gurmukhi: {
43
+ pkg: "noto-sans-gurmukhi",
44
+ file: "NotoSansGurmukhi"
45
+ },
46
+ gujarati: {
47
+ pkg: "noto-sans-gujarati",
48
+ file: "NotoSansGujarati"
49
+ },
50
+ oriya: {
51
+ pkg: "noto-sans-oriya",
52
+ file: "NotoSansOriya"
53
+ },
54
+ tamil: {
55
+ pkg: "noto-sans-tamil",
56
+ file: "NotoSansTamil"
57
+ },
58
+ telugu: {
59
+ pkg: "noto-sans-telugu",
60
+ file: "NotoSansTelugu"
61
+ },
62
+ kannada: {
63
+ pkg: "noto-sans-kannada",
64
+ file: "NotoSansKannada"
65
+ },
66
+ malayalam: {
67
+ pkg: "noto-sans-malayalam",
68
+ file: "NotoSansMalayalam"
69
+ },
34
70
  symbols1: {
35
71
  pkg: "noto-sans-symbols",
36
72
  file: "NotoSansSymbols"
@@ -9,6 +9,25 @@ import { ScriptKey } from './remote-fonts.js';
9
9
  * {@link scriptsInFlow}); defaults to Simplified.
10
10
  */
11
11
  export declare function scriptForCodepoint(cp: number, hanFace?: ScriptKey): ScriptKey | undefined;
12
+ /**
13
+ * §17.3.2.26 — the slot of `w:rFonts` a character is set in: `ascii`,
14
+ * `hAnsi` (high ANSI), `eastAsia` or `cs` (complex script).
15
+ */
16
+ export type FontSlot = 'ascii' | 'hAnsi' | 'eastAsia' | 'cs';
17
+ /**
18
+ * §17.3.2.26 — the slot of a run's fonts Word sets a character in: ASCII in
19
+ * `ascii`, East Asian text in `eastAsia`, complex script in `cs`, and the rest
20
+ * above ASCII — Cyrillic, Greek, accented Latin, curly quotes — in `hAnsi`. A
21
+ * `w:rtl` run is complex script whole (§17.3.2.30), its digits and Latin too.
22
+ *
23
+ * Measured in Word for Mac on a run whose ascii font is Verdana and whose
24
+ * hAnsi one is Courier New: "split" reports Verdana, "Кириллица" Courier New.
25
+ *
26
+ * @param cp The code point.
27
+ * @param rtl Whether the run is right-to-left.
28
+ * @returns The slot.
29
+ */
30
+ export declare function fontSlotOf(cp: number, rtl?: boolean): FontSlot;
12
31
  /**
13
32
  * The writing systems a document holds text in, beside the Latin one.
14
33
  *
@@ -20,6 +20,56 @@ var RANGES = [
20
20
  2303,
21
21
  "arabic"
22
22
  ],
23
+ [
24
+ 2304,
25
+ 2403,
26
+ "devanagari"
27
+ ],
28
+ [
29
+ 2406,
30
+ 2431,
31
+ "devanagari"
32
+ ],
33
+ [
34
+ 2432,
35
+ 2559,
36
+ "bengali"
37
+ ],
38
+ [
39
+ 2560,
40
+ 2687,
41
+ "gurmukhi"
42
+ ],
43
+ [
44
+ 2688,
45
+ 2815,
46
+ "gujarati"
47
+ ],
48
+ [
49
+ 2816,
50
+ 2943,
51
+ "oriya"
52
+ ],
53
+ [
54
+ 2944,
55
+ 3071,
56
+ "tamil"
57
+ ],
58
+ [
59
+ 3072,
60
+ 3199,
61
+ "telugu"
62
+ ],
63
+ [
64
+ 3200,
65
+ 3327,
66
+ "kannada"
67
+ ],
68
+ [
69
+ 3328,
70
+ 3455,
71
+ "malayalam"
72
+ ],
23
73
  [
24
74
  3584,
25
75
  3711,
@@ -30,6 +80,11 @@ var RANGES = [
30
80
  4607,
31
81
  "kr"
32
82
  ],
83
+ [
84
+ 7376,
85
+ 7423,
86
+ "devanagari"
87
+ ],
33
88
  [
34
89
  8592,
35
90
  8703,
@@ -80,6 +135,11 @@ var RANGES = [
80
135
  40959,
81
136
  "han"
82
137
  ],
138
+ [
139
+ 43232,
140
+ 43263,
141
+ "devanagari"
142
+ ],
83
143
  [
84
144
  44032,
85
145
  55215,
@@ -125,6 +185,41 @@ function scriptForCodepoint(cp, hanFace = "sc") {
125
185
  if (cp <= hi) return key === "han" ? hanFace : key;
126
186
  }
127
187
  }
188
+ /** The scripts Word sets in a run's complex-script font, `w:cs`. */
189
+ var COMPLEX_SCRIPTS = /* @__PURE__ */ new Set([
190
+ "hebrew",
191
+ "arabic",
192
+ "thai",
193
+ "devanagari",
194
+ "bengali",
195
+ "gurmukhi",
196
+ "gujarati",
197
+ "oriya",
198
+ "tamil",
199
+ "telugu",
200
+ "kannada",
201
+ "malayalam"
202
+ ]);
203
+ /**
204
+ * §17.3.2.26 — the slot of a run's fonts Word sets a character in: ASCII in
205
+ * `ascii`, East Asian text in `eastAsia`, complex script in `cs`, and the rest
206
+ * above ASCII — Cyrillic, Greek, accented Latin, curly quotes — in `hAnsi`. A
207
+ * `w:rtl` run is complex script whole (§17.3.2.30), its digits and Latin too.
208
+ *
209
+ * Measured in Word for Mac on a run whose ascii font is Verdana and whose
210
+ * hAnsi one is Courier New: "split" reports Verdana, "Кириллица" Courier New.
211
+ *
212
+ * @param cp The code point.
213
+ * @param rtl Whether the run is right-to-left.
214
+ * @returns The slot.
215
+ */
216
+ function fontSlotOf(cp, rtl = false) {
217
+ if (rtl) return "cs";
218
+ if (cp <= 127) return "ascii";
219
+ const script = scriptForCodepoint(cp);
220
+ if (script !== void 0 && COMPLEX_SCRIPTS.has(script)) return "cs";
221
+ return script === "kr" || script === "jp" || script === "sc" ? "eastAsia" : "hAnsi";
222
+ }
128
223
  /**
129
224
  * The writing systems a document holds text in, beside the Latin one.
130
225
  *
@@ -181,4 +276,4 @@ function scriptsInFlow(flow) {
181
276
  };
182
277
  }
183
278
  //#endregion
184
- export { scriptForCodepoint, scriptsInFlow };
279
+ export { fontSlotOf, scriptForCodepoint, scriptsInFlow };
@@ -1,6 +1,6 @@
1
1
  import { pt } from "../ir/units.js";
2
2
  import { NumberingState, effectiveAbstract } from "./state.js";
3
- import { resolveParagraphProperties } from "../style-cascade/resolver.js";
3
+ import { mergeRunPartial, resolveParagraphProperties } from "../style-cascade/resolver.js";
4
4
  import "../style-cascade/index.js";
5
5
  //#region src/core/numbering/apply.ts
6
6
  /** The height a picture bullet takes when neither the level nor the paragraph
@@ -36,6 +36,7 @@ function applyNumbering(body, numbering, styles) {
36
36
  const pic = level?.picBullet;
37
37
  const bulletCapPt = level?.runProperties.fontSizePt ?? p.properties.runProperties?.fontSizePt ?? DEFAULT_BULLET_HEIGHT_PT;
38
38
  const bulletScale = pic ? Math.min(1, bulletCapPt / Math.max(1, pic.heightPt)) : 1;
39
+ const suffix = level?.suffix ?? "tab";
39
40
  const markerRuns = pic ? [{
40
41
  text: "",
41
42
  properties: {},
@@ -45,18 +46,21 @@ function applyNumbering(body, numbering, styles) {
45
46
  width: pt(pic.widthPt * bulletScale),
46
47
  height: pt(pic.heightPt * bulletScale)
47
48
  }
48
- }, {
49
+ }, ...suffix === "tab" ? [{
49
50
  text: " ",
50
51
  properties: {},
51
52
  listMarker: true
52
- }] : [{
53
- text: `${marker.replaceAll(SOFT_HYPHEN, "-")}\t`,
54
- properties: {
55
- ...p.properties.runProperties,
56
- ...level?.runProperties
57
- },
53
+ }] : []] : [{
54
+ text: `${marker.replaceAll(SOFT_HYPHEN, "-")}${suffix === "tab" ? " " : ""}`,
55
+ properties: mergeRunPartial(p.properties.runProperties ?? {}, level?.runProperties ?? {}),
58
56
  listMarker: true
59
57
  }];
58
+ if (suffix === "space") markerRuns.push({
59
+ text: " ",
60
+ properties: markerRuns[0].properties,
61
+ listMarker: true,
62
+ listMarkerSpace: true
63
+ });
60
64
  const newProps = mergeIndentFromLevel(p.properties, level?.paragraphProperties);
61
65
  return {
62
66
  ...p,
@@ -1,4 +1,4 @@
1
- export { EMPTY_STYLE_SHEET, resolveBodyStyles, resolveHeadersFootersStyles, resolveParagraphProperties, resolveRunProperties, } from './resolver.js';
1
+ export { EMPTY_STYLE_SHEET, mergeRunPartial, resolveBodyStyles, resolveHeadersFootersStyles, resolveParagraphProperties, resolveRunProperties, } from './resolver.js';
2
2
  export type { ResolvedRunProperties, ResolvedParagraphProperties, } from './types.js';
3
3
  export { DEFAULT_RESOLVED_RUN, DEFAULT_RESOLVED_PARAGRAPH } from './types.js';
4
4
  export { resolveTableStyles } from './table.js';
@@ -1,4 +1,4 @@
1
1
  import { DEFAULT_RESOLVED_PARAGRAPH, DEFAULT_RESOLVED_RUN } from "./types.js";
2
- import { EMPTY_STYLE_SHEET, resolveBodyStyles, resolveHeadersFootersStyles, resolveParagraphProperties, resolveRunProperties } from "./resolver.js";
2
+ import { EMPTY_STYLE_SHEET, mergeRunPartial, resolveBodyStyles, resolveHeadersFootersStyles, resolveParagraphProperties, resolveRunProperties } from "./resolver.js";
3
3
  import { resolveTableStyles } from "./table.js";
4
- export { DEFAULT_RESOLVED_PARAGRAPH, DEFAULT_RESOLVED_RUN, EMPTY_STYLE_SHEET, resolveBodyStyles, resolveHeadersFootersStyles, resolveParagraphProperties, resolveRunProperties, resolveTableStyles };
4
+ export { DEFAULT_RESOLVED_PARAGRAPH, DEFAULT_RESOLVED_RUN, EMPTY_STYLE_SHEET, mergeRunPartial, resolveBodyStyles, resolveHeadersFootersStyles, resolveParagraphProperties, resolveRunProperties, resolveTableStyles };
@@ -24,6 +24,15 @@ export declare function resolveRunProperties(runDirect: RunProperties, paragraph
24
24
  * @param sheet The stylesheet supplying defaults and style chains.
25
25
  */
26
26
  export declare function resolveParagraphProperties(paragraphDirect: ParagraphProperties, sheet: StyleSheet): ResolvedParagraphProperties;
27
+ /**
28
+ * Lay one layer of run properties over another (§17.7.2): each property the
29
+ * higher layer states wins, and each slot of its `w:rFonts` on its own.
30
+ *
31
+ * @param base The lower layer.
32
+ * @param override The higher layer.
33
+ * @returns The two as one layer.
34
+ */
35
+ export declare function mergeRunPartial(base: RunProperties, override: RunProperties): RunProperties;
27
36
  /**
28
37
  * Resolve the style cascade across an entire body so the tree carries final
29
38
  * effective run/paragraph properties (FlowDoc transform, ir-design stage 6).
@@ -131,7 +131,7 @@ function mergeRun(base, override) {
131
131
  smallCaps: override.smallCaps ?? base.smallCaps,
132
132
  fontSizePt: override.fontSizePt ?? base.fontSizePt,
133
133
  colorHex: override.colorHex ?? base.colorHex,
134
- fontFamily: override.fontFamily ?? base.fontFamily,
134
+ fontFamily: mergeFontFamily(base.fontFamily, override.fontFamily),
135
135
  verticalAlign: override.verticalAlign ?? base.verticalAlign,
136
136
  rtl: override.rtl ?? base.rtl,
137
137
  ...lang !== void 0 ? { lang } : {},
@@ -185,8 +185,36 @@ function mergePar(base, override) {
185
185
  ...beforeAuto !== void 0 ? { spacingBeforeAuto: beforeAuto } : {}
186
186
  };
187
187
  }
188
+ /**
189
+ * Lay one layer of run properties over another (§17.7.2): each property the
190
+ * higher layer states wins, and each slot of its `w:rFonts` on its own.
191
+ *
192
+ * @param base The lower layer.
193
+ * @param override The higher layer.
194
+ * @returns The two as one layer.
195
+ */
188
196
  function mergeRunPartial(base, override) {
189
- return copyDefined(base, override);
197
+ const out = copyDefined(base, override);
198
+ const fontFamily = mergeFontFamily(base.fontFamily, override.fontFamily);
199
+ return fontFamily === void 0 || fontFamily === out.fontFamily ? out : {
200
+ ...out,
201
+ fontFamily
202
+ };
203
+ }
204
+ function mergeFontFamily(base, override) {
205
+ if (override === void 0) return base;
206
+ if (base === void 0) return override;
207
+ const ascii = override.ascii ?? base.ascii;
208
+ const hAnsi = override.hAnsi ?? base.hAnsi;
209
+ const eastAsia = override.eastAsia ?? base.eastAsia;
210
+ const cs = override.cs ?? base.cs;
211
+ if (ascii === override.ascii && hAnsi === override.hAnsi && eastAsia === override.eastAsia && cs === override.cs) return override;
212
+ return {
213
+ ...ascii !== void 0 ? { ascii } : {},
214
+ ...hAnsi !== void 0 ? { hAnsi } : {},
215
+ ...eastAsia !== void 0 ? { eastAsia } : {},
216
+ ...cs !== void 0 ? { cs } : {}
217
+ };
190
218
  }
191
219
  function mergeParPartial(base, override) {
192
220
  const out = copyDefined(base, override);
@@ -291,4 +319,4 @@ function resolveHeadersFootersStyles(hf, sheet) {
291
319
  return hf;
292
320
  }
293
321
  //#endregion
294
- export { EMPTY_STYLE_SHEET, resolveBodyStyles, resolveHeadersFootersStyles, resolveParagraphProperties, resolveRunProperties };
322
+ export { EMPTY_STYLE_SHEET, mergeRunPartial, resolveBodyStyles, resolveHeadersFootersStyles, resolveParagraphProperties, resolveRunProperties };
@@ -1,3 +1,4 @@
1
+ import { mergeRunPartial } from "./resolver.js";
1
2
  //#region src/core/style-cascade/table.ts
2
3
  /**
3
4
  * Apply table styles across a body (ECMA-376 §17.7.6): for each table, fold its
@@ -88,10 +89,7 @@ function applyLayerToCell(tableCell, layer, directBorders, pos) {
88
89
  };
89
90
  if (layer.runProperties) p.properties = {
90
91
  ...p.properties,
91
- inheritedRun: {
92
- ...p.properties.inheritedRun,
93
- ...layer.runProperties
94
- }
92
+ inheritedRun: mergeRunPartial(p.properties.inheritedRun ?? {}, layer.runProperties)
95
93
  };
96
94
  }
97
95
  }
@@ -170,10 +168,7 @@ function mergeLayer(base, override) {
170
168
  } } : {},
171
169
  ...(override.indentPt ?? base.indentPt) !== void 0 ? { indentPt: override.indentPt ?? base.indentPt } : {},
172
170
  ...override.shading ?? base.shading ? { shading: override.shading ?? base.shading } : {},
173
- ...override.runProperties ?? base.runProperties ? { runProperties: {
174
- ...base.runProperties,
175
- ...definedOnly(override.runProperties ?? {})
176
- } } : {},
171
+ ...override.runProperties ?? base.runProperties ? { runProperties: mergeRunPartial(base.runProperties ?? {}, override.runProperties ?? {}) } : {},
177
172
  ...override.paragraphProperties ?? base.paragraphProperties ? { paragraphProperties: {
178
173
  ...base.paragraphProperties,
179
174
  ...definedOnly(override.paragraphProperties ?? {})
@@ -1,6 +1,7 @@
1
1
  import { fetchFontSet } from "../core/fonts/remote-fonts.js";
2
2
  import { FontRegistry } from "../core/font/font-registry.js";
3
3
  import { createFontMeasure } from "../core/font/measure.js";
4
+ import "../core/font/index.js";
4
5
  import { signPdf } from "../pdf/signature.js";
5
6
  import { renderStyledPdf, renderStyledPdfEncrypted } from "../pdf/styled-page-renderer.js";
6
7
  import "../pdf/index.js";
package/dist/esm/index.js CHANGED
@@ -2,6 +2,7 @@ import { fetchFontSet, resolveFamilyKey } from "./core/fonts/remote-fonts.js";
2
2
  import { parseTtf } from "./core/font/ttf-parser.js";
3
3
  import { subsetTtf } from "./core/font/ttf-subset.js";
4
4
  import { FontRegistry } from "./core/font/font-registry.js";
5
+ import "./core/font/index.js";
5
6
  import { eighthPtToPt, emuToPt, halfPtToPt, inchToPt, mmToPt, pt, pxToPt, twipsToPt } from "./core/ir/units.js";
6
7
  import { ResourceStore } from "./core/ir/resources.js";
7
8
  import { FEATURES } from "./core/ir/features.js";
@@ -28,10 +28,11 @@ export declare function lineVisualOrder(line: Line): Array<number>;
28
28
  export declare function computeJustifyExtra(line: Line): number;
29
29
  /**
30
30
  * Whether a line is ONE text matrix and a run of glyphs: nothing in it placed
31
- * on its own — a tab, a stretched space, a picture, a formula, a right-to-left
32
- * run, a rise, a faked italic — and nothing drawn beside its glyphs, a run's
33
- * shading, a highlight or a rule. Only such a line can be turned by its matrix
34
- * alone; any other is placed token by token in page space.
31
+ * on its own — a tab, a list marker's space, a stretched space, a picture, a
32
+ * formula, a right-to-left run, a rise, a faked italic — and nothing drawn
33
+ * beside its glyphs, a run's shading, a highlight or a rule. Only such a line
34
+ * can be turned by its matrix alone; any other is placed token by token in
35
+ * page space.
35
36
  *
36
37
  * @param line The laid-out line.
37
38
  * @returns True when a single text matrix sets the whole of it.
@@ -70,16 +70,17 @@ function computeJustifyExtra(line) {
70
70
  }
71
71
  /**
72
72
  * Whether a line is ONE text matrix and a run of glyphs: nothing in it placed
73
- * on its own — a tab, a stretched space, a picture, a formula, a right-to-left
74
- * run, a rise, a faked italic — and nothing drawn beside its glyphs, a run's
75
- * shading, a highlight or a rule. Only such a line can be turned by its matrix
76
- * alone; any other is placed token by token in page space.
73
+ * on its own — a tab, a list marker's space, a stretched space, a picture, a
74
+ * formula, a right-to-left run, a rise, a faked italic — and nothing drawn
75
+ * beside its glyphs, a run's shading, a highlight or a rule. Only such a line
76
+ * can be turned by its matrix alone; any other is placed token by token in
77
+ * page space.
77
78
  *
78
79
  * @param line The laid-out line.
79
80
  * @returns True when a single text matrix sets the whole of it.
80
81
  */
81
82
  function settableInOneMatrix(line) {
82
- return computeJustifyExtra(line) === 0 && line.tokens.every((t) => t.kind === "text" && t.tab !== true && t.bidiLevel % 2 === 0 && t.risePt === void 0 && t.synthetic?.italic !== true && t.highlight !== true && t.resolvedRun.shadingColorHex === void 0 && t.resolvedRun.underline === "none" && !t.resolvedRun.strike);
83
+ return computeJustifyExtra(line) === 0 && line.tokens.every((t) => t.kind === "text" && t.tab !== true && t.listMarkerSpace !== true && t.bidiLevel % 2 === 0 && t.risePt === void 0 && t.synthetic?.italic !== true && t.highlight !== true && t.resolvedRun.shadingColorHex === void 0 && t.resolvedRun.underline === "none" && !t.resolvedRun.strike);
83
84
  }
84
85
  /**
85
86
  * §14.1.2.10 — the wash a picture is drawn through, as the flat veil it is.
@@ -16,6 +16,11 @@ export interface FontResource {
16
16
  readonly measure: FontMeasure;
17
17
  /** Glyphs collected by the usage walk — the emit phase subsets to these. */
18
18
  readonly gids: ReadonlySet<number>;
19
+ /**
20
+ * The text of each collected glyph that no character maps to — a conjunct,
21
+ * a reph, a vowel sign's contextual form — for the PDF's /ToUnicode.
22
+ */
23
+ readonly glyphText?: ReadonlyMap<number, string>;
19
24
  }
20
25
  /**
21
26
  * The face a run asked for and the registry could not give — what the page has
@@ -60,11 +65,23 @@ export interface TextToken {
60
65
  * element separate from the item body.
61
66
  */
62
67
  readonly listMarker?: true;
68
+ /**
69
+ * §17.9.29 — the token is the space a list level's `w:suff="space"` puts
70
+ * after its marker: a gap of fixed width with nothing drawn in it, which a
71
+ * justified line does not stretch.
72
+ */
73
+ readonly listMarkerSpace?: true;
63
74
  /**
64
75
  * §17.3.3.1 — the token is the break of a `w:br w:type="column"`: what
65
76
  * follows it belongs in the next column.
66
77
  */
67
78
  readonly columnBreak?: true;
79
+ /**
80
+ * §17.3.3.1 — the token is a `w:br w:type="page"`: an empty mark of where it
81
+ * stands, and what follows it belongs on the next page. Layout takes it out
82
+ * of the line before the line is wrapped.
83
+ */
84
+ readonly pageBreak?: true;
68
85
  /**
69
86
  * §17.3.1.38 — the token IS a tab: its width is the distance to the stop it
70
87
  * advances to, resolved once the line is known, and its text is whatever
@@ -76,6 +93,15 @@ export interface TextToken {
76
93
  * fills a soft highlight behind it (E-COMMENTS CM2c).
77
94
  */
78
95
  readonly highlight?: true;
96
+ /**
97
+ * Per code point of `text`, the colour it is drawn in, where the token holds
98
+ * a syllable of the scripts of India that a run boundary cut: the syllable
99
+ * is shaped whole in the run it begins in, and each character keeps the
100
+ * colour of the run it was typed in. A glyph standing for several characters
101
+ * takes the colour of the first. Absent when every character takes
102
+ * `resolvedRun.colorHex`.
103
+ */
104
+ readonly charColorsHex?: ReadonlyArray<string>;
79
105
  readonly resolvedRun: ResolvedRunProperties;
80
106
  readonly font: FontResource;
81
107
  /** What the chosen face lacks and the emitter must fake (see {@link SyntheticFace}). */
@@ -2,7 +2,7 @@ import { BodyElement, Chart, Comment, DocumentInfo, Numbering, Section, SectionP
2
2
  import { FontRegistry } from '../core/font/index.js';
3
3
  import { SubstituteKey } from '../core/fonts/index.js';
4
4
  import { Hyphenator } from '../core/hyphenation/index.js';
5
- import { Pt, ResourceStore } from '../core/ir/index.js';
5
+ import { Loss, Pt, ResourceStore } from '../core/ir/index.js';
6
6
  import { LaidOutDocument, PageItem } from './page-doc.js';
7
7
  import { AttachedFile } from '../pdf/embedded-file.js';
8
8
  import { SignaturePlaceholder } from '../pdf/signature.js';
@@ -221,6 +221,13 @@ export interface StyledRenderOptions {
221
221
  * signature.
222
222
  */
223
223
  readonly signaturePlaceholder?: SignaturePlaceholder;
224
+ /**
225
+ * Sink the layout writes its {@link Loss} entries into: the characters no
226
+ * face on hand has a glyph for, which the page draws as empty boxes (see
227
+ * `undrawnLoss`). The converter always supplies one and returns it with the
228
+ * output; omitted ⇒ the boxes are drawn all the same, and nothing says so.
229
+ */
230
+ readonly losses?: Array<Loss>;
224
231
  }
225
232
  export type { DocumentInfo } from '../core/document-model/index.js';
226
233
  /** A4 page width in points (the page-geometry fallback). */