@docx4j/core-ts 0.1.0 → 0.1.1

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 (168) 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 +1 -0
  131. package/dist/opc/ContentTypes.mjs +1 -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/PartRegistry.mjs +1 -0
  151. package/dist/parts/XmlPart.d.mts +33 -0
  152. package/dist/parts/XmlPart.mjs +105 -2
  153. package/dist/parts/pml/defaults.d.mts +6 -0
  154. package/dist/parts/pml/defaults.mjs +53 -0
  155. package/dist/parts/pml/index.d.mts +66 -3
  156. package/dist/parts/pml/index.mjs +206 -7
  157. package/dist/parts/pml/placeholders.d.mts +71 -0
  158. package/dist/parts/pml/placeholders.mjs +141 -0
  159. package/dist/parts/sml/index.d.mts +19 -1
  160. package/dist/parts/sml/index.mjs +60 -1
  161. package/dist/parts/wml/comments.d.mts +1 -0
  162. package/dist/parts/wml/comments.mjs +179 -0
  163. package/dist/parts/wml/defaultNumbering.d.mts +1 -0
  164. package/dist/parts/wml/defaultNumbering.mjs +216 -0
  165. package/dist/parts/wml/index.d.mts +121 -0
  166. package/dist/parts/wml/index.mjs +291 -0
  167. package/dist/xml/dom.mjs +1 -2
  168. package/package.json +25 -4
@@ -0,0 +1,219 @@
1
+ import { VCL_ENTRIES } from './substitutions.generated.mjs';
2
+ import { scriptOf } from './scripts.mjs';
3
+ /** The key convention `FontSubstitutions.xml` uses: the name lower-cased, everything but
4
+ * letters and digits removed. */
5
+ export function shortKey(fontName) {
6
+ let key = '';
7
+ for (const c of fontName) {
8
+ // Character.isLetterOrDigit over the whole of Unicode, which is what docx4j's key() uses
9
+ if (/[\p{L}\p{Nd}]/u.test(c))
10
+ key += c.toLowerCase();
11
+ }
12
+ return key;
13
+ }
14
+ /**
15
+ * The `FontSubstitutions.xml` entry for this name, or undefined. The table knows families,
16
+ * not the foundry and weight suffixes a document adds to them ("Calisto MT", "Segoe UI
17
+ * Light"), so trailing words are dropped one at a time until one matches.
18
+ */
19
+ export function vclEntryFor(documentFontName) {
20
+ if (documentFontName === undefined || documentFontName === null)
21
+ return undefined;
22
+ const direct = VCL_ENTRIES[shortKey(documentFontName)];
23
+ if (direct)
24
+ return direct;
25
+ const words = documentFontName.trim().split(/\s+/);
26
+ for (let drop = 1; drop < words.length; drop++) {
27
+ const entry = VCL_ENTRIES[shortKey(words.slice(0, words.length - drop).join(''))];
28
+ if (entry)
29
+ return entry;
30
+ }
31
+ return undefined;
32
+ }
33
+ /** Whether `FontSubstitutions.xml` calls this font condensed (Arial Narrow and the like): no
34
+ * ordinary stand-in has its widths, and docx4j's corpus showed the document default is the
35
+ * lesser evil, so these are deliberately left unmapped. */
36
+ export function isCondensed(documentFontName) {
37
+ const entry = vclEntryFor(documentFontName);
38
+ if (entry && entry.fontWidth.toLowerCase().includes('condensed'))
39
+ return true;
40
+ const n = (documentFontName !== null && documentFontName !== void 0 ? documentFontName : '').toLowerCase();
41
+ return n.includes('narrow') || n.includes('condensed');
42
+ }
43
+ /** `.+-(bold|italic|...)(mt|ps)?`: a PostScript name, which no system has a family by, so
44
+ * Word does not resolve it either and falls back to the document default. */
45
+ const POSTSCRIPT_NAME = /^.+-(bold|italic|bolditalic|regular|roman|light|medium|semibold|demibold|black|oblique)(mt|ps)?$/;
46
+ /** Families measured to be better off falling back to the document default than to a stand-in
47
+ * of their own class, because the stand-in's widths are further from theirs than the
48
+ * default's are. Kept short and evidence-based, as docx4j's is. */
49
+ function leaveUnmapped(documentFontName) {
50
+ if (documentFontName === undefined)
51
+ return false;
52
+ const n = documentFontName.toLowerCase();
53
+ // Lato is a narrow humanist sans; Arimo (Arial's widths) set a Lato document a page longer
54
+ if (n.startsWith('lato'))
55
+ return true;
56
+ return POSTSCRIPT_NAME.test(n);
57
+ }
58
+ /** Whether this family is one docx4j deliberately leaves to the document default rather than
59
+ * standing in for it: a condensed face, or one of the measured exceptions. */
60
+ export function leftToTheDocumentDefault(documentFontName) {
61
+ return isCondensed(documentFontName) || leaveUnmapped(documentFontName);
62
+ }
63
+ /** The class `FontSubstitutions.xml` gives this font, or undefined where it does not say. */
64
+ function classFromTable(documentFontName) {
65
+ const entry = vclEntryFor(documentFontName);
66
+ if (!entry || entry.fontType.length === 0)
67
+ return undefined;
68
+ const type = entry.fontType.toLowerCase();
69
+ // CJK, Symbol, Special, Decorative and Script faces have no stand-in of "their class"
70
+ if (type.includes('cjk') || type.includes('symbol') || type.includes('special')
71
+ || type.includes('decorative') || type.includes('script') || type.includes('ctl'))
72
+ return 'UNKNOWN';
73
+ if (type.includes('fixed') || type.includes('typewriter'))
74
+ return 'MONO';
75
+ if (type.includes('sansserif'))
76
+ return 'SANS';
77
+ if (type.includes('serif'))
78
+ return 'SERIF';
79
+ return undefined;
80
+ }
81
+ const MONO_WORDS = ['cousine', 'monospace', 'courier', 'consol', 'typewriter', 'terminal',
82
+ 'fixedsys', 'inconsolata', 'menlo'];
83
+ // NB no bare "gothic": in a Microsoft font name it is as often Japanese (MS Gothic, Yu Gothic)
84
+ // as it is a sans. "urw gothic" is the whole family name of the Avant Garde clone.
85
+ const SANS_WORDS = ['arimo', 'carlito', 'cantarell', 'helvetica', 'arial', 'urw gothic',
86
+ 'selawik', 'verdana', 'tahoma', 'segoe', 'gadugi', 'trebuchet', 'calibri', 'grotesk',
87
+ 'grotesque', 'futura', 'frutiger', 'myriad', 'univers', 'avenir', 'lato', 'roboto',
88
+ 'franklin', 'open sans'];
89
+ // "p052" and "c059" are the URW Palatino and Century Schoolbook clones, "gelasio" Georgia's.
90
+ const SERIF_WORDS = ['tinos', 'caladea', 'charter', 'times', 'georgia', 'gelasio', 'garamond',
91
+ 'palatino', 'p052', 'c059', 'bookman', 'book antiqua', 'cambria', 'constantia', 'century',
92
+ 'baskerville', 'caslon', 'utopia', 'minion', 'sylfaen', 'roman'];
93
+ /**
94
+ * The class a name says, sans before serif so that "HelveticaNeue LT 55 Roman" is a sans.
95
+ *
96
+ * @param generic whether a name which only ends in "Sans" or "Serif" counts. It does when the
97
+ * question is what class an installed font is (Liberation Sans, DejaVu Serif), but not when
98
+ * it is whether to stand in for a document font: measured, that guess was worth less than the
99
+ * document default.
100
+ */
101
+ export function classFromName(documentFontName, generic = true) {
102
+ if (documentFontName === undefined)
103
+ return 'UNKNOWN';
104
+ const n = documentFontName.toLowerCase();
105
+ for (const mono of MONO_WORDS)
106
+ if (n.includes(mono))
107
+ return 'MONO';
108
+ // "mono" only as a word: Monotype Corsiva is a script face, not a monospace one
109
+ for (const word of n.split(/[^a-z0-9]+/))
110
+ if (word === 'mono')
111
+ return 'MONO';
112
+ for (const sans of SANS_WORDS)
113
+ if (n.includes(sans))
114
+ return 'SANS';
115
+ for (const serif of SERIF_WORDS)
116
+ if (n.includes(serif))
117
+ return 'SERIF';
118
+ if (generic) {
119
+ if (n.includes('sans'))
120
+ return 'SANS';
121
+ if (n.includes('serif'))
122
+ return 'SERIF';
123
+ }
124
+ return 'UNKNOWN';
125
+ }
126
+ /** The font's class, from `FontSubstitutions.xml` where it knows the font, else from its name. */
127
+ export function classOf(documentFontName) {
128
+ var _a;
129
+ return (_a = classFromTable(documentFontName)) !== null && _a !== void 0 ? _a : classFromName(documentFontName);
130
+ }
131
+ /** The class to stand in for: the table's word, else what the name says for certain. */
132
+ export function substitutionClass(documentFontName) {
133
+ var _a;
134
+ return (_a = classFromTable(documentFontName)) !== null && _a !== void 0 ? _a : classFromName(documentFontName, false);
135
+ }
136
+ const SANS_DEFAULTS = ['Arimo Regular', 'Arimo', 'Liberation Sans', 'Nimbus Sans', 'DejaVu Sans',
137
+ 'Noto Sans', 'FreeSans'];
138
+ const SERIF_DEFAULTS = ['Tinos Regular', 'Tinos', 'Liberation Serif', 'Nimbus Roman',
139
+ 'DejaVu Serif', 'Noto Serif', 'FreeSerif'];
140
+ const MONO_DEFAULTS = ['Cousine Regular', 'Cousine', 'Liberation Mono', 'Nimbus Mono PS',
141
+ 'DejaVu Sans Mono', 'Noto Mono', 'FreeMono'];
142
+ function defaultsFor(fontClass) {
143
+ switch (fontClass) {
144
+ case 'SANS': return SANS_DEFAULTS;
145
+ case 'SERIF': return SERIF_DEFAULTS;
146
+ case 'MONO': return MONO_DEFAULTS;
147
+ default: return [];
148
+ }
149
+ }
150
+ /** The families `FontSubstitutions.xml` offers for this font, in its order, as short keys. */
151
+ function vclSubstituteKeys(documentFontName) {
152
+ const entry = vclEntryFor(documentFontName);
153
+ if (!entry || entry.substFonts.length === 0)
154
+ return [];
155
+ return entry.substFonts.split(';').map(shortKey).filter((k) => k.length > 0);
156
+ }
157
+ /**
158
+ * A physical font of this document font's class, without regard to what it can render. Used
159
+ * where the characters are Latin (or Greek/Cyrillic), which any of these covers.
160
+ *
161
+ * `FontSubstitutions.xml`'s own list first - it is ordered by closeness - then the class
162
+ * defaults. Undefined where the font's class is unknown, or it is condensed or one of the
163
+ * measured exceptions, or the registry has nothing of that class.
164
+ */
165
+ export function selectByClass(registry, documentFontName) {
166
+ if (documentFontName === undefined)
167
+ return undefined;
168
+ if (isCondensed(documentFontName) || leaveUnmapped(documentFontName))
169
+ return undefined;
170
+ const fontClass = substitutionClass(documentFontName);
171
+ if (fontClass === 'UNKNOWN')
172
+ return undefined;
173
+ const byKey = new Map();
174
+ for (const font of registry.all())
175
+ byKey.set(shortKey(font.name), font);
176
+ for (const candidate of vclSubstituteKeys(documentFontName)) {
177
+ const pf = byKey.get(candidate);
178
+ if (pf && classOf(pf.name) === fontClass)
179
+ return pf;
180
+ }
181
+ for (const candidate of defaultsFor(fontClass)) {
182
+ const pf = registry.get(candidate);
183
+ if (pf)
184
+ return pf;
185
+ }
186
+ return undefined;
187
+ }
188
+ // ---------------------------------------------------------------------- coverage groups
189
+ //
190
+ // Used by `RunFontSelector.spanScript`, which cuts a span where the script changes.
191
+ /** U+2190-U+2BFF, the symbol blocks: Arrows, Mathematical Operators, Miscellaneous Technical,
192
+ * Box Drawing, Block Elements, Geometric Shapes, Miscellaneous Symbols, Dingbats, Braille and
193
+ * the supplemental arrow and maths blocks. Every one of them is `COMMON`, so they are their
194
+ * own group rather than shared: the space and the digits beside an arrow are not dragged into
195
+ * the symbol font with it. */
196
+ export function isSymbol(cp) {
197
+ return cp >= 0x2190 && cp <= 0x2BFF;
198
+ }
199
+ /** U+1F000-U+1FAFF, the emoji blocks (`COMMON`, and like the symbols a group of their own). */
200
+ export function isEmoji(cp) {
201
+ return cp >= 0x1F000 && cp <= 0x1FAFF;
202
+ }
203
+ /** The East Asian punctuation and form blocks, which are `COMMON` - a fullwidth comma U+FF0C,
204
+ * an ideographic comma U+3001 - and which a Latin face does not have. */
205
+ export function isEastAsianForm(cp) {
206
+ return (cp >= 0x2E80 && cp <= 0x33FF) || (cp >= 0xFE10 && cp <= 0xFE4F)
207
+ || (cp >= 0xFF00 && cp <= 0xFFEF);
208
+ }
209
+ export const SYMBOL_GROUP = 'SYMBOL';
210
+ export const EMOJI_GROUP = 'EMOJI';
211
+ /** The key a code point groups under: its Unicode script, except that the symbol and emoji
212
+ * blocks are their own groups. */
213
+ export function coverageGroupOf(cp) {
214
+ if (isSymbol(cp))
215
+ return SYMBOL_GROUP;
216
+ if (isEmoji(cp))
217
+ return EMOJI_GROUP;
218
+ return scriptOf(cp);
219
+ }
@@ -0,0 +1,7 @@
1
+ import type * as wml from '@docx4j/generated-objects-ts/modules/org_docx4j_wml';
2
+ import { Mapper } from './Mapper.mjs';
3
+ import type { PhysicalFont } from './PhysicalFont.mjs';
4
+ export declare class IdentityPlusMapper extends Mapper {
5
+ /** A variant of the name: "Noto Sans Symbols" is registered as "Noto Sans Symbols Regular". */
6
+ protected resolveDocumentFont(documentFontName: string, _fontTableEntry: wml.Fonts.Font | undefined): PhysicalFont | undefined;
7
+ }
@@ -0,0 +1,19 @@
1
+ import { Mapper } from './Mapper.mjs';
2
+ /** The order a family whose plain face is absent is asked for. Bold before italic since
3
+ * docx4j 17.1.1: with italic first, "Franklin Gothic Demi" was set upright in Franklin Gothic
4
+ * Demi Italic, and a bold upright is nearer a missing regular than an italic is. */
5
+ const VARIANTS = [' regular', ' bold', ' italic', ' bold italic'];
6
+ export class IdentityPlusMapper extends Mapper {
7
+ /** A variant of the name: "Noto Sans Symbols" is registered as "Noto Sans Symbols Regular". */
8
+ resolveDocumentFont(documentFontName, _fontTableEntry) {
9
+ this.resolvedVia = undefined;
10
+ for (const variant of VARIANTS) {
11
+ const pf = this.registry.get(documentFontName + variant);
12
+ if (pf !== undefined) {
13
+ this.resolvedVia = `a variant of the name: ${documentFontName}${variant}`;
14
+ return pf;
15
+ }
16
+ }
17
+ return undefined;
18
+ }
19
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * The theme's `script` for a language tag, or undefined where the tag selects none (and the
3
+ * theme's `a:latin`, `a:ea` or `a:cs` answers instead). Chinese is decided on the region as
4
+ * well: mainland China and Singapore use simplified characters (`Hans`), everywhere else
5
+ * traditional (`Hant`).
6
+ */
7
+ export declare function getScriptForLanguageTag(langTag: string | undefined): string | undefined;
@@ -0,0 +1,67 @@
1
+ // docx4j `org.docx4j.fonts.LanguageTagToScriptMapping`.
2
+ //
3
+ // Maps `w:themeFontLang` in the settings part - `<w:themeFontLang w:val="en-US"
4
+ // w:eastAsia="ko-KR"/>` - to the `script` attribute of an `a:font` in the theme part's font
5
+ // scheme (`<a:font script="Jpan" typeface="MS 明朝"/>`), so that `ThemePart.getFont` can pick
6
+ // the face for the document's language.
7
+ //
8
+ // **By exact subtag.** Until docx4j 17.1.1 this was a substring test over comma-separated
9
+ // lists, so Estonian (`et`) matched inside `eth` and took the Ethiopic face (Nyala), Mongolian
10
+ // (`mn`) inside `mni` the Bengali one, and Wolof (`wo`) inside `bwo`. CR-016 probe
11
+ // `fonts-theme-lang` measured Word: with `<w:themeFontLang w:val="et-EE"/>` and docDefaults
12
+ // naming `minorHAnsi`, Word sets the text in Calibri, the theme's Latin face, on every line.
13
+ /** The script a primary language subtag selects in a theme's font list. */
14
+ const SCRIPT_BY_LANGUAGE = new Map();
15
+ function put(script, ...langs) {
16
+ for (const lang of langs)
17
+ SCRIPT_BY_LANGUAGE.set(lang, script);
18
+ }
19
+ put('Jpan', 'ja');
20
+ put('Hang', 'ko');
21
+ put('Arab', 'ar');
22
+ put('Hebr', 'he', 'yi', 'iw');
23
+ put('Thai', 'th');
24
+ put('Ethi', 'ti', 'bwo', 'eth', 'kxh', 'mdy');
25
+ put('Beng', 'bn', 'as', 'mni');
26
+ put('Gujr', 'gu');
27
+ put('Khmr', 'km');
28
+ put('Knda', 'kn');
29
+ put('Guru', 'pa');
30
+ put('Cans', 'iu');
31
+ put('Cher', 'chr');
32
+ // Yiii (Microsoft Yi Baiti) omitted; see http://en.wikipedia.org/wiki/Yi_script
33
+ put('Tibt', 'bo');
34
+ put('Thaa', 'dv');
35
+ put('Deva', 'hi', 'ks', 'kok', 'mr', 'ne', 'sa', 'sd');
36
+ put('Telu', 'te');
37
+ put('Taml', 'ta');
38
+ put('Syrc', 'syr');
39
+ put('Orya', 'or');
40
+ put('Mlym', 'ml');
41
+ put('Laoo', 'lo');
42
+ put('Sinh', 'si');
43
+ // Mong (Mongolian Baiti) and Uigh (Microsoft Uighur) omitted, as in docx4j
44
+ put('Viet', 'vi', 'lha', 'nut');
45
+ put('Geor', 'ka');
46
+ /**
47
+ * The theme's `script` for a language tag, or undefined where the tag selects none (and the
48
+ * theme's `a:latin`, `a:ea` or `a:cs` answers instead). Chinese is decided on the region as
49
+ * well: mainland China and Singapore use simplified characters (`Hans`), everywhere else
50
+ * traditional (`Hant`).
51
+ */
52
+ export function getScriptForLanguageTag(langTag) {
53
+ if (langTag === undefined || langTag === null)
54
+ return undefined;
55
+ let lang = langTag.trim();
56
+ let pos = lang.indexOf('-');
57
+ if (pos < 0)
58
+ pos = lang.indexOf('_');
59
+ if (pos > -1)
60
+ lang = lang.slice(0, pos);
61
+ lang = lang.toLowerCase();
62
+ if (lang === 'zh') {
63
+ const tag = langTag.toLowerCase();
64
+ return tag === 'zh-cn' || tag === 'zh-sg' ? 'Hans' : 'Hant';
65
+ }
66
+ return SCRIPT_BY_LANGUAGE.get(lang);
67
+ }
@@ -0,0 +1,190 @@
1
+ import type * as wml from '@docx4j/generated-objects-ts/modules/org_docx4j_wml';
2
+ import { PhysicalFont } from './PhysicalFont.mjs';
3
+ import { type FontRegistry } from './registry.mjs';
4
+ /** Which pass answered for a document font (docx4j `FontDecision.Source`). */
5
+ export type FontDecisionSource =
6
+ /** The environment has a font of that name. */
7
+ 'INSTALLED'
8
+ /** The document embeds it (`w:embedRegular` and the rest). */
9
+ | 'EMBEDDED'
10
+ /** A metric-compatible clone from `font-substitutes.xml`: Word's advances. */
11
+ | 'METRIC_CLONE'
12
+ /** No clone exists; the closest face measured against Word's own PDF. */
13
+ | 'MEASURED_STAND_IN'
14
+ /** The document's own `w:altName`, resolved. */
15
+ | 'ALT_NAME'
16
+ /** A face of the same class: the widths are not the document font's. */
17
+ | 'CLASS'
18
+ /** What Word itself draws a font it cannot find. */
19
+ | 'WORD_DEFAULT'
20
+ /** The mapper's own answer: a variant of the name. */
21
+ | 'MAPPER_OWN'
22
+ /** A symbol font, drawn in the face that has the glyphs. */
23
+ | 'SYMBOL'
24
+ /** Nothing mapped it. */
25
+ | 'UNMAPPED';
26
+ /** What was decided for one document font, and why. */
27
+ export interface FontDecision {
28
+ /** The name as the document has it. */
29
+ readonly documentFont: string;
30
+ readonly source: FontDecisionSource;
31
+ /** How it got there - the `w:altName` chain, the class, Word's default family - or null
32
+ * where the source says it all. */
33
+ readonly via: string | null;
34
+ /** What is known of the substitute's width error, or null. */
35
+ readonly widthError: string | null;
36
+ /** The physical font as the mapping now stands; null where nothing mapped it. */
37
+ readonly physicalFont: PhysicalFont | null;
38
+ }
39
+ /** What the report says where nothing was measured. */
40
+ export declare const UNKNOWN_ERROR = "unknown";
41
+ /** Word's own fallback where a document font resolves to nothing at all. */
42
+ export declare const FONT_FALLBACK = "Times New Roman";
43
+ /** docx4j `WordLineMetrics.isEastAsianFamily`: the table's optional field 7. */
44
+ export declare function isEastAsianFamily(documentFont: string | undefined): boolean;
45
+ /**
46
+ * Whether docx4j's tables know this family: `MicrosoftFonts.xml`, `word-line-metrics` (512
47
+ * Microsoft and Office cloud families) or `FontSubstitutions.xml`.
48
+ *
49
+ * A known family the environment merely lacks is substituted for its widths (the passes before
50
+ * `addWordDefaultSubstitutes`), since Word on the author's machine had the font; an unknown one
51
+ * is what Word itself could not find either. "Known" is `substitutionClass`, **not** `classOf`,
52
+ * which will also guess a class from a name that merely ends in "Sans": docx4j measured a
53
+ * corporate face that fell between the two and was drawn in the document default's serif
54
+ * throughout. A family deliberately left to the document default is "known" for this purpose
55
+ * too, the measurement that put it there being that the default beat a stand-in.
56
+ */
57
+ export declare function isKnownFamily(documentFontName: string | undefined): boolean;
58
+ /**
59
+ * Whether this document font has a bold face of its own: the `MicrosoftFonts.xml` entry where
60
+ * there is one (Franklin Gothic Book has none), else a family whose name ends in a weight word
61
+ * (Calibri Light, Segoe UI Semibold, Arial Black) is a single weight in Windows' font model and
62
+ * has none; any other family is taken to have one.
63
+ */
64
+ export declare function hasBoldFace(documentFontName: string | undefined): boolean;
65
+ /** The font table keyed by lower-cased name. */
66
+ export declare function fontTableOf(fonts: wml.Fonts | undefined): Map<string, wml.Fonts.Font>;
67
+ /**
68
+ * The font Word draws an unknown font in, by its fontTable entry: measured (docx4j CR-016
69
+ * probe `fonts-unresolvable`, Word 365) a face whose entry says `w:family="roman"` is drawn in
70
+ * **Cambria**, one saying `swiss` in **Calibri**, one with an entry that names no family in
71
+ * Calibri, one with no fontTable entry at all in Cambria; an exact panose does not change that
72
+ * (Arial's gave Calibri), and the document's default font is never used.
73
+ */
74
+ export declare function wordDefaultFor(documentFontName: string, table: Map<string, wml.Fonts.Font>): string;
75
+ /**
76
+ * Maps the document's font names to physical fonts, over a {@link FontRegistry} the caller
77
+ * supplies. One per package, as in docx4j.
78
+ */
79
+ export declare abstract class Mapper {
80
+ /** The fonts the consumer has; the docx4j font jars' faces where nothing is given, which
81
+ * is the environment the parity goldens were made in. */
82
+ readonly registry: FontRegistry;
83
+ constructor(
84
+ /** The fonts the consumer has; the docx4j font jars' faces where nothing is given, which
85
+ * is the environment the parity goldens were made in. */
86
+ registry?: FontRegistry);
87
+ /** The mapping, keyed by the lower-cased document font name. */
88
+ private readonly fontMappings;
89
+ /** The document's embedded faces, keyed by the name the font table part uses. */
90
+ private readonly regularForms;
91
+ private readonly boldForms;
92
+ private readonly italicForms;
93
+ private readonly boldItalicForms;
94
+ private readonly decisions;
95
+ /** The document fonts `addWordDefaultSubstitutes` mapped: Word substitutes such a font whole,
96
+ * bold face included, so `addNoBoldFaceAliases` leaves them alone. */
97
+ private readonly wordDefaulted;
98
+ private readonly lineMetricsAliases;
99
+ /** The physical font a document font maps to, by case-insensitive name; undefined for a slot
100
+ * nothing names (docx4j `Mapper.get`, which tolerates null since 17.1.1). */
101
+ get(key: string | undefined | null): PhysicalFont | undefined;
102
+ put(key: string, pf: PhysicalFont): void;
103
+ get size(): number;
104
+ /** What was decided for this document font, or undefined where no pass met it. */
105
+ getDecision(documentFont: string | undefined): FontDecision | undefined;
106
+ /** Every decision, ordered by the document font's name (case-insensitively), so that two
107
+ * runs over the same document give the same list. */
108
+ getDecisions(): FontDecision[];
109
+ /** Whose Word line metrics a document font takes, where a pass resolved it through another
110
+ * family; undefined where it takes its own. Per mapper, i.e. per package: docx4j's was a
111
+ * JVM-wide map until 17.1.1, and one document's `w:altName` answered for every later one. */
112
+ lineMetricsFamily(documentFont: string | undefined): string | undefined;
113
+ registerLineMetricsAlias(documentFont: string | undefined, family: string | undefined): void;
114
+ protected decide(documentFont: string, source: FontDecisionSource, via?: string | null, widthError?: string | null): void;
115
+ registerRegularForm(name: string, pf: PhysicalFont | undefined): void;
116
+ registerBoldForm(name: string, pf: PhysicalFont | undefined): void;
117
+ registerItalicForm(name: string, pf: PhysicalFont | undefined): void;
118
+ registerBoldItalicForm(name: string, pf: PhysicalFont | undefined): void;
119
+ /** Whether the document embeds this font. */
120
+ isEmbedded(name: string): boolean;
121
+ /** The installed font of this name, else the document's embedded form of it (docx4j
122
+ * CR-016 Decisions 2: the installed font wins, for all four faces and in both mappers). */
123
+ protected installedOrEmbedded(name: string): PhysicalFont | undefined;
124
+ /**
125
+ * The mapper's own answer for a document font the environment has neither under that name
126
+ * nor embedded; undefined where it has none. {@link resolvedVia} says how it was reached.
127
+ */
128
+ protected resolveDocumentFont(_documentFontName: string, _fontTableEntry: wml.Fonts.Font | undefined): PhysicalFont | undefined;
129
+ /** How the mapper's own answer was reached, set by {@link resolveDocumentFont} as it answers. */
130
+ protected resolvedVia: string | undefined;
131
+ /** An entry for each of the document's fonts: steps 1 to 3 of the precedence. */
132
+ populateFontMappings(documentFontNames: Iterable<string>, fonts: wml.Fonts | undefined): void;
133
+ /**
134
+ * `font-substitutes.xml`: for each document font the table knows, the first of its open
135
+ * substitutes this environment has. The measurements that chose each substitute are in
136
+ * docx4j's `Mapper.addMetricallyCompatibleSubstitutes` javadoc, and the table's `error`
137
+ * attributes quote them.
138
+ */
139
+ addMetricallyCompatibleSubstitutes(): void;
140
+ /** Map this document font to an open substitute, unless the environment has the font itself
141
+ * or the document embeds it (the embedded font is what the author intended, and it is the
142
+ * only thing certain to be available). */
143
+ protected addMetricallyCompatibleSubstitute(documentFont: string, substitute: string): void;
144
+ /**
145
+ * `w:altName` in `word/fontTable.xml` (ECMA-376 17.8.3.1): "the name of an alternate font
146
+ * which shall be used if the font specified is not available". That is the document author's
147
+ * own answer to a missing font, and Word takes it.
148
+ *
149
+ * After the metric clones - a clone of the document's own font is a better answer than any
150
+ * alias - and before the class pass, which is only a guess from the name. The chain is
151
+ * followed: the alternate may itself be absent and name an alternate (CR-016 probe
152
+ * `fonts-unresolvable` (e), (f)); a few hops, never a cycle.
153
+ */
154
+ addAltNameSubstitutes(documentFontNames: Iterable<string> | undefined, fonts: wml.Fonts | undefined): void;
155
+ /** Whether {@link addClassBasedSubstitutes} applies to this mapper. True for every mapper
156
+ * since docx4j 17.1.1. */
157
+ wantsClassBasedSubstitutes(): boolean;
158
+ /**
159
+ * Map whatever is still unmapped to a font of the same class. Without it an unmapped font
160
+ * falls back to whatever the document's *default* font maps to, which is a Times clone
161
+ * standing in for a sans as often as not. Deliberately conservative: a condensed face is
162
+ * left unmapped, its widths being further from an ordinary condensed face's than the
163
+ * default's are.
164
+ */
165
+ addClassBasedSubstitutes(documentFontNames: Iterable<string> | undefined): void;
166
+ /** The mapper's own guesses for what the measured passes left (`BestMatchingMapper`'s panose
167
+ * match and `FontSubstitutions.xml`). Nothing here: that mapper needs font files. */
168
+ addMapperSubstitutes(_documentFontNames: Iterable<string> | undefined, _fonts: wml.Fonts | undefined): void;
169
+ /**
170
+ * Word's own answer for a font it cannot find, for whatever is still unmapped: Cambria or
171
+ * Calibri (or Courier New) by the fontTable entry's family ({@link wordDefaultFor}), mapped
172
+ * to whatever this mapper maps that font to. Applied only to a family none of docx4j's
173
+ * tables know ({@link isKnownFamily}).
174
+ */
175
+ addWordDefaultSubstitutes(documentFontNames: Iterable<string> | undefined, fonts: wml.Fonts | undefined): void;
176
+ /**
177
+ * For each document font with no bold face of its own, an alias of its mapped physical font
178
+ * reporting none, so that the consumer synthesises bold at the regular advances as Word does.
179
+ * Last of the passes: it re-maps, and it is not a source of its own - the decision says so by
180
+ * reporting a synthetic bold face.
181
+ */
182
+ addNoBoldFaceAliases(documentFontNames: Iterable<string> | undefined): void;
183
+ /**
184
+ * Every pass in order, as docx4j's `WordprocessingMLPackage.setFontMapper` runs them.
185
+ *
186
+ * @param documentFontNames the document's font names (`MainDocumentPart.fontsInUse()`)
187
+ * @param fonts the font table part's content, for `w:altName` and `w:family`
188
+ */
189
+ populate(documentFontNames: Iterable<string>, fonts: wml.Fonts | undefined): void;
190
+ }