reamkit 1.22.0 → 1.24.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.
- package/dist/esm/core/bmp.d.ts +33 -0
- package/dist/esm/core/bmp.js +276 -0
- package/dist/esm/core/converter/ream.d.ts +16 -0
- package/dist/esm/core/converter/ream.js +59 -9
- package/dist/esm/core/document-model/types.d.ts +55 -0
- package/dist/esm/core/drawingml/chart-geometry.js +14 -7
- package/dist/esm/core/drawingml/colors.d.ts +4 -2
- package/dist/esm/core/drawingml/colors.js +15 -0
- package/dist/esm/core/drawingml/diagram/colors.d.ts +52 -0
- package/dist/esm/core/drawingml/diagram/colors.js +125 -0
- package/dist/esm/core/drawingml/diagram/data-model.d.ts +47 -0
- package/dist/esm/core/drawingml/diagram/data-model.js +119 -0
- package/dist/esm/core/drawingml/diagram/layout-engine.d.ts +46 -0
- package/dist/esm/core/drawingml/diagram/layout-engine.js +948 -0
- package/dist/esm/core/drawingml/diagram/run.d.ts +37 -0
- package/dist/esm/core/drawingml/diagram/run.js +50 -0
- package/dist/esm/core/drawingml/diagram/to-drawing.d.ts +16 -0
- package/dist/esm/core/drawingml/diagram/to-drawing.js +86 -0
- package/dist/esm/core/drawingml/preset-geometry.js +116 -6
- package/dist/esm/core/drawingml/text-warp.d.ts +58 -0
- package/dist/esm/core/drawingml/text-warp.js +355 -0
- package/dist/esm/core/drawingml/theme-parser.d.ts +38 -0
- package/dist/esm/core/drawingml/theme-parser.js +73 -4
- package/dist/esm/core/font/measure.d.ts +12 -0
- package/dist/esm/core/font/measure.js +36 -0
- package/dist/esm/core/fonts/families.d.ts +10 -0
- package/dist/esm/core/fonts/families.js +44 -0
- package/dist/esm/core/fonts/index.d.ts +3 -2
- package/dist/esm/core/fonts/remote-fonts.d.ts +50 -0
- package/dist/esm/core/fonts/remote-fonts.js +114 -8
- package/dist/esm/core/fonts/scripts.d.ts +26 -0
- package/dist/esm/core/fonts/scripts.js +177 -0
- package/dist/esm/core/images.d.ts +30 -2
- package/dist/esm/core/images.js +121 -5
- package/dist/esm/core/metafile/blit.d.ts +33 -0
- package/dist/esm/core/metafile/blit.js +67 -0
- package/dist/esm/core/metafile/dib.d.ts +61 -0
- package/dist/esm/core/metafile/dib.js +205 -0
- package/dist/esm/core/metafile/emf.js +280 -38
- package/dist/esm/core/metafile/picture.d.ts +61 -1
- package/dist/esm/core/metafile/picture.js +70 -1
- package/dist/esm/core/metafile/wmf.js +202 -13
- package/dist/esm/core/ole/escher-blip.d.ts +12 -0
- package/dist/esm/core/ole/escher-blip.js +98 -0
- package/dist/esm/{pdf-reader → core}/png-encode.js +1 -1
- package/dist/esm/excel/print-model.js +5 -3
- package/dist/esm/excel/sheet-drawing.js +1 -1
- package/dist/esm/excel/sheet-to-flow.js +16 -2
- package/dist/esm/excel/styles-parser.js +1 -1
- package/dist/esm/excel/xls/biff-reader.js +53 -18
- package/dist/esm/excel/xls/biff-styles.d.ts +5 -2
- package/dist/esm/excel/xls/biff-styles.js +9 -7
- package/dist/esm/excel/xls/escher.js +90 -24
- package/dist/esm/html/html-writer.js +2 -1
- package/dist/esm/layout/page-doc.d.ts +74 -0
- package/dist/esm/layout/page-doc.js +14 -1
- package/dist/esm/layout/styled-layout.d.ts +2 -2
- package/dist/esm/layout/styled-layout.js +475 -48
- package/dist/esm/pdf/shading.js +8 -6
- package/dist/esm/pdf/styled-page-emitter.js +77 -5
- package/dist/esm/pdf/vector-graphics.js +1 -1
- package/dist/esm/pdf-reader/image-decode.js +1 -1
- package/dist/esm/pptx/embedded-fonts.d.ts +31 -0
- package/dist/esm/pptx/embedded-fonts.js +104 -0
- package/dist/esm/pptx/placeholder-cascade.d.ts +73 -6
- package/dist/esm/pptx/placeholder-cascade.js +114 -18
- package/dist/esm/pptx/ppt/ppt-reader.js +191 -13
- package/dist/esm/pptx/ppt/ppt-text.d.ts +107 -1
- package/dist/esm/pptx/ppt/ppt-text.js +873 -144
- package/dist/esm/pptx/pptx-reader.d.ts +8 -0
- package/dist/esm/pptx/pptx-reader.js +117 -15
- package/dist/esm/pptx/preset-table-styles.d.ts +9 -0
- package/dist/esm/pptx/preset-table-styles.js +27 -0
- package/dist/esm/pptx/slide-parser.d.ts +11 -2
- package/dist/esm/pptx/slide-parser.js +203 -44
- package/dist/esm/pptx/sp-helpers.d.ts +9 -4
- package/dist/esm/pptx/sp-helpers.js +48 -9
- package/dist/esm/pptx/table-style.d.ts +16 -1
- package/dist/esm/pptx/table-style.js +104 -10
- package/dist/esm/svg/svg-writer.js +1 -0
- package/dist/esm/word/document-parser.d.ts +11 -2
- package/dist/esm/word/document-parser.js +10 -7
- package/dist/esm/word/docx-reader.js +32 -16
- package/dist/esm/word/docx-to-pdf.d.ts +8 -7
- package/dist/esm/word/docx-to-pdf.js +25 -7
- package/dist/esm/word/docx-writer.js +4 -0
- package/dist/esm/word/drawing-parser.d.ts +20 -0
- package/dist/esm/word/drawing-parser.js +67 -22
- package/dist/esm/word/numbering-parser.d.ts +2 -1
- package/dist/esm/word/numbering-parser.js +6 -6
- package/dist/esm/word/paragraph-properties.d.ts +2 -1
- package/dist/esm/word/paragraph-properties.js +2 -2
- package/dist/esm/word/run-properties.d.ts +5 -2
- package/dist/esm/word/run-properties.js +13 -8
- package/dist/esm/word/styles-parser.d.ts +2 -1
- package/dist/esm/word/styles-parser.js +12 -12
- package/dist/esm/word/table-parser.js +6 -3
- package/dist/esm/word/theme-fonts.d.ts +10 -0
- package/dist/esm/word/theme-fonts.js +28 -0
- package/package.json +1 -1
- /package/dist/esm/{pdf-reader → core}/png-encode.d.ts +0 -0
|
@@ -0,0 +1,355 @@
|
|
|
1
|
+
//#region src/core/drawingml/text-warp.ts
|
|
2
|
+
/** Distance from the box's centre line, `0` at the middle and `1` at either edge. */
|
|
3
|
+
var fromCentre = (u) => Math.abs(2 * u - 1);
|
|
4
|
+
/** A band of constant height `1 - drop` whose top edge is `top`. */
|
|
5
|
+
var band = (top, drop) => ({
|
|
6
|
+
top,
|
|
7
|
+
bottom: top + (1 - drop)
|
|
8
|
+
});
|
|
9
|
+
/**
|
|
10
|
+
* §20.1.10.76 ST_TextShapeType — the warp each preset applies, keyed by its
|
|
11
|
+
* name. `adj` arrives as the `a:avLst` adjustment in hundred-thousandths, or
|
|
12
|
+
* the preset's own default when the file states none.
|
|
13
|
+
*/
|
|
14
|
+
var ENVELOPES = new Map([
|
|
15
|
+
["textPlain", {
|
|
16
|
+
defaultAdj: 0,
|
|
17
|
+
at: () => ({
|
|
18
|
+
top: 0,
|
|
19
|
+
bottom: 1
|
|
20
|
+
})
|
|
21
|
+
}],
|
|
22
|
+
["textStop", {
|
|
23
|
+
defaultAdj: 12500,
|
|
24
|
+
at: (u, adj) => {
|
|
25
|
+
const t = adj * Math.max(0, (fromCentre(u) - .375) / .625);
|
|
26
|
+
return {
|
|
27
|
+
top: t,
|
|
28
|
+
bottom: 1 - t
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
}],
|
|
32
|
+
["textTriangle", {
|
|
33
|
+
defaultAdj: 5e4,
|
|
34
|
+
at: (u, adj) => ({
|
|
35
|
+
top: adj * fromCentre(u),
|
|
36
|
+
bottom: 1
|
|
37
|
+
})
|
|
38
|
+
}],
|
|
39
|
+
["textTriangleInverted", {
|
|
40
|
+
defaultAdj: 5e4,
|
|
41
|
+
at: (u, adj) => ({
|
|
42
|
+
top: 0,
|
|
43
|
+
bottom: 1 - adj * fromCentre(u)
|
|
44
|
+
})
|
|
45
|
+
}],
|
|
46
|
+
["textChevron", {
|
|
47
|
+
defaultAdj: 25e3,
|
|
48
|
+
at: (u, adj) => band(adj * fromCentre(u), adj)
|
|
49
|
+
}],
|
|
50
|
+
["textChevronInverted", {
|
|
51
|
+
defaultAdj: 25e3,
|
|
52
|
+
at: (u, adj) => band(adj * (1 - fromCentre(u)), adj)
|
|
53
|
+
}],
|
|
54
|
+
["textCanUp", {
|
|
55
|
+
defaultAdj: 14e3,
|
|
56
|
+
at: (u, adj) => band(adj * fromCentre(u) ** 4, adj)
|
|
57
|
+
}],
|
|
58
|
+
["textCanDown", {
|
|
59
|
+
defaultAdj: 14e3,
|
|
60
|
+
at: (u, adj) => band(adj * (1 - fromCentre(u) ** 4), adj)
|
|
61
|
+
}],
|
|
62
|
+
["textWave1", {
|
|
63
|
+
defaultAdj: 6250,
|
|
64
|
+
at: (u, adj) => band(adj * (1 - Math.sin(2 * Math.PI * u)), 2 * adj)
|
|
65
|
+
}],
|
|
66
|
+
["textWave2", {
|
|
67
|
+
defaultAdj: 6250,
|
|
68
|
+
at: (u, adj) => band(adj * (1 + Math.sin(2 * Math.PI * u)), 2 * adj)
|
|
69
|
+
}],
|
|
70
|
+
["textDoubleWave1", {
|
|
71
|
+
defaultAdj: 6250,
|
|
72
|
+
at: (u, adj) => band(adj * (1 - Math.sin(4 * Math.PI * u)), 2 * adj)
|
|
73
|
+
}],
|
|
74
|
+
["textWave4", {
|
|
75
|
+
defaultAdj: 6250,
|
|
76
|
+
at: (u, adj) => band(adj * (1 + Math.sin(4 * Math.PI * u)), 2 * adj)
|
|
77
|
+
}],
|
|
78
|
+
["textInflate", {
|
|
79
|
+
defaultAdj: 13600,
|
|
80
|
+
at: (u, adj) => {
|
|
81
|
+
const t = adj * fromCentre(u) ** 2;
|
|
82
|
+
return {
|
|
83
|
+
top: t,
|
|
84
|
+
bottom: 1 - t
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
}],
|
|
88
|
+
["textDeflate", {
|
|
89
|
+
defaultAdj: 37500,
|
|
90
|
+
at: (u, adj) => {
|
|
91
|
+
const t = adj * (1 - fromCentre(u) ** 2.23);
|
|
92
|
+
return {
|
|
93
|
+
top: t,
|
|
94
|
+
bottom: 1 - t
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
}],
|
|
98
|
+
["textInflateTop", {
|
|
99
|
+
defaultAdj: 31800,
|
|
100
|
+
at: (u, adj) => ({
|
|
101
|
+
top: adj * fromCentre(u) ** 2,
|
|
102
|
+
bottom: 1
|
|
103
|
+
})
|
|
104
|
+
}],
|
|
105
|
+
["textDeflateTop", {
|
|
106
|
+
defaultAdj: 47e3,
|
|
107
|
+
at: (u, adj) => ({
|
|
108
|
+
top: adj * (1 - fromCentre(u) ** 2),
|
|
109
|
+
bottom: 1
|
|
110
|
+
})
|
|
111
|
+
}],
|
|
112
|
+
["textInflateBottom", {
|
|
113
|
+
defaultAdj: 31800,
|
|
114
|
+
at: (u, adj) => ({
|
|
115
|
+
top: 0,
|
|
116
|
+
bottom: 1 - adj * fromCentre(u) ** 2
|
|
117
|
+
})
|
|
118
|
+
}],
|
|
119
|
+
["textDeflateBottom", {
|
|
120
|
+
defaultAdj: 47e3,
|
|
121
|
+
at: (u, adj) => ({
|
|
122
|
+
top: 0,
|
|
123
|
+
bottom: 1 - adj * (1 - fromCentre(u) ** 2)
|
|
124
|
+
})
|
|
125
|
+
}],
|
|
126
|
+
["textDeflateInflate", {
|
|
127
|
+
defaultAdj: 19800,
|
|
128
|
+
at: (u, adj) => ({
|
|
129
|
+
top: 0,
|
|
130
|
+
bottom: .268 + adj * fromCentre(u) ** 2
|
|
131
|
+
})
|
|
132
|
+
}],
|
|
133
|
+
["textDeflateInflateDeflate", {
|
|
134
|
+
defaultAdj: 4600,
|
|
135
|
+
at: (u, adj) => ({
|
|
136
|
+
top: 0,
|
|
137
|
+
bottom: .258 + adj * fromCentre(u) ** 2
|
|
138
|
+
})
|
|
139
|
+
}],
|
|
140
|
+
["textFadeRight", {
|
|
141
|
+
defaultAdj: 33333,
|
|
142
|
+
at: (u, adj) => ({
|
|
143
|
+
top: adj * u,
|
|
144
|
+
bottom: 1 - adj * u
|
|
145
|
+
})
|
|
146
|
+
}],
|
|
147
|
+
["textFadeLeft", {
|
|
148
|
+
defaultAdj: 33333,
|
|
149
|
+
at: (u, adj) => ({
|
|
150
|
+
top: adj * (1 - u),
|
|
151
|
+
bottom: 1 - adj * (1 - u)
|
|
152
|
+
})
|
|
153
|
+
}],
|
|
154
|
+
["textFadeUp", {
|
|
155
|
+
defaultAdj: 1e5,
|
|
156
|
+
at: (u, adj) => ({
|
|
157
|
+
top: adj * fromCentre(u) ** 2,
|
|
158
|
+
bottom: 1
|
|
159
|
+
})
|
|
160
|
+
}],
|
|
161
|
+
["textFadeDown", {
|
|
162
|
+
defaultAdj: 1e5,
|
|
163
|
+
at: (u, adj) => ({
|
|
164
|
+
top: 0,
|
|
165
|
+
bottom: 1 - adj * fromCentre(u) ** 2
|
|
166
|
+
})
|
|
167
|
+
}],
|
|
168
|
+
["textSlantUp", {
|
|
169
|
+
defaultAdj: 55500,
|
|
170
|
+
at: (u, adj) => band(adj * (1 - u), adj)
|
|
171
|
+
}],
|
|
172
|
+
["textSlantDown", {
|
|
173
|
+
defaultAdj: 44400,
|
|
174
|
+
at: (u, adj) => band(adj * u, adj)
|
|
175
|
+
}],
|
|
176
|
+
["textCascadeUp", {
|
|
177
|
+
defaultAdj: 55500,
|
|
178
|
+
at: (u, adj) => ({
|
|
179
|
+
top: .138 * (1 - u),
|
|
180
|
+
bottom: 1 - adj + adj * (1 - u)
|
|
181
|
+
})
|
|
182
|
+
}],
|
|
183
|
+
["textCascadeDown", {
|
|
184
|
+
defaultAdj: 55500,
|
|
185
|
+
at: (u, adj) => ({
|
|
186
|
+
top: .138 * u,
|
|
187
|
+
bottom: 1 - adj + adj * u
|
|
188
|
+
})
|
|
189
|
+
}],
|
|
190
|
+
["textCurveUp", {
|
|
191
|
+
defaultAdj: 45600,
|
|
192
|
+
at: (u, adj) => curve(1 - u, adj)
|
|
193
|
+
}],
|
|
194
|
+
["textCurveDown", {
|
|
195
|
+
defaultAdj: 45600,
|
|
196
|
+
at: (u, adj) => curve(u, adj)
|
|
197
|
+
}]
|
|
198
|
+
]);
|
|
199
|
+
/**
|
|
200
|
+
* The ring presets, which do not hang text between two edges at all: they wind
|
|
201
|
+
* it once round an ellipse inscribed in the box. Both start at the box's left
|
|
202
|
+
* edge; `Inside` climbs over the top with each letter's head pointing out of
|
|
203
|
+
* the ring, `Outside` dips under the bottom with each letter's head pointing
|
|
204
|
+
* into it — which is why half of either one reads upside down.
|
|
205
|
+
*
|
|
206
|
+
* The band the letters occupy is a fixed share of the ring whatever the text:
|
|
207
|
+
* eight letters and thirty-two fill exactly the same annulus, so it is the
|
|
208
|
+
* preset and not the string that fixes the inner radius.
|
|
209
|
+
*/
|
|
210
|
+
var RING_INNER = .34;
|
|
211
|
+
var RINGS = new Map([["textRingInside", {
|
|
212
|
+
clockwise: true,
|
|
213
|
+
headOut: true
|
|
214
|
+
}], ["textRingOutside", {
|
|
215
|
+
clockwise: false,
|
|
216
|
+
headOut: false
|
|
217
|
+
}]]);
|
|
218
|
+
/**
|
|
219
|
+
* The `textCurve*` pair: the baseline is an arc over the box and the band
|
|
220
|
+
* narrows towards the end the text runs to.
|
|
221
|
+
*
|
|
222
|
+
* @param u Position across the box, already turned the way the preset runs.
|
|
223
|
+
* @param adj How much of the band's height the taper takes by the far end.
|
|
224
|
+
* @returns The two edges at `u`.
|
|
225
|
+
*/
|
|
226
|
+
function curve(u, adj) {
|
|
227
|
+
const bottom = 1 - .24 * (2 * u - 1) ** 2;
|
|
228
|
+
return {
|
|
229
|
+
top: bottom - (.758 - adj * u ** 1.7),
|
|
230
|
+
bottom
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Whether a preset bends text at all. `textNoShape` is the "no warp" member of
|
|
235
|
+
* the enumeration and by far its most common value — two thirds of the decks
|
|
236
|
+
* that mention a warp state only this one — and a body carrying it is an
|
|
237
|
+
* ordinary text box, NOT WordArt stretched to its shape.
|
|
238
|
+
*
|
|
239
|
+
* @param preset The `a:prstTxWarp @prst` value.
|
|
240
|
+
* @returns Whether {@link textWarpEdges} can bend text through this preset.
|
|
241
|
+
*/
|
|
242
|
+
function isTextWarp(preset) {
|
|
243
|
+
return ENVELOPES.has(preset) || RINGS.has(preset);
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* The matrix that places one glyph of a warped body.
|
|
247
|
+
*
|
|
248
|
+
* Maps the glyph's own text space — x along its advance, y UP from its
|
|
249
|
+
* baseline — into the page's top-left frame, as `[a, b, c, d, e, f]` with
|
|
250
|
+
* `px = a·x + c·y + e` and `py = b·x + d·y + f`. Upright text therefore has a
|
|
251
|
+
* negative `d`: the glyph's y runs against the page's.
|
|
252
|
+
*
|
|
253
|
+
* @param frame The two frames, from the laid-out line.
|
|
254
|
+
* @param glyphX The glyph's left edge in the un-warped block.
|
|
255
|
+
* @param advance How wide the glyph is there.
|
|
256
|
+
* @param baselineY The line's baseline in the un-warped block.
|
|
257
|
+
* @returns The six numbers, or `undefined` where the warp leaves no room to
|
|
258
|
+
* draw — the pinched end of a fade, or a preset this file cannot bend.
|
|
259
|
+
*/
|
|
260
|
+
function warpGlyphMatrix(frame, glyphX, advance, baselineY) {
|
|
261
|
+
const u = (glyphX + advance / 2 - frame.srcX) / frame.srcWidth;
|
|
262
|
+
const v = (baselineY - frame.srcTop) / frame.srcHeight;
|
|
263
|
+
const ring = RINGS.get(frame.preset);
|
|
264
|
+
if (ring) return ringMatrix(frame, ring, u, v, glyphX);
|
|
265
|
+
return envelopeMatrix(frame, u, v, glyphX, advance);
|
|
266
|
+
}
|
|
267
|
+
/** The glyph matrix for a warp that hangs text between a top and a bottom edge. */
|
|
268
|
+
function envelopeMatrix(frame, u, v, glyphX, advance) {
|
|
269
|
+
const edges = textWarpEdges(frame.preset, u, frame.adjust);
|
|
270
|
+
if (!edges) return void 0;
|
|
271
|
+
const height = edges.bottom - edges.top;
|
|
272
|
+
if (!(height > 0)) return void 0;
|
|
273
|
+
const sx = frame.boxWidth / frame.srcWidth;
|
|
274
|
+
const sy = height * frame.boxHeight / frame.srcHeight;
|
|
275
|
+
const step = .004;
|
|
276
|
+
const lo = Math.max(0, u - step);
|
|
277
|
+
const hi = Math.min(1, u + step);
|
|
278
|
+
const at = (q) => {
|
|
279
|
+
const e = textWarpEdges(frame.preset, q, frame.adjust);
|
|
280
|
+
return e ? e.top + v * (e.bottom - e.top) : void 0;
|
|
281
|
+
};
|
|
282
|
+
const before = at(lo);
|
|
283
|
+
const after = at(hi);
|
|
284
|
+
const slope = before !== void 0 && after !== void 0 && hi > lo ? (after - before) * frame.boxHeight / ((hi - lo) * frame.boxWidth) : 0;
|
|
285
|
+
const x0 = frame.boxX + (glyphX - frame.srcX) / frame.srcWidth * frame.boxWidth;
|
|
286
|
+
const mid = edges.top + v * height;
|
|
287
|
+
const y0 = frame.boxY + mid * frame.boxHeight - slope * advance * sx / 2;
|
|
288
|
+
return [
|
|
289
|
+
sx,
|
|
290
|
+
slope * sx,
|
|
291
|
+
0,
|
|
292
|
+
-sy,
|
|
293
|
+
x0,
|
|
294
|
+
y0
|
|
295
|
+
];
|
|
296
|
+
}
|
|
297
|
+
/**
|
|
298
|
+
* The glyph matrix for a ring: one turn round the ellipse inscribed in the box.
|
|
299
|
+
*
|
|
300
|
+
* The band is a CONSTANT inset from that ellipse, not a scaled copy of it. A
|
|
301
|
+
* scaled copy would be the natural reading — a circular annulus squashed to the
|
|
302
|
+
* box's proportions — but it makes the band four times deeper at the ring's
|
|
303
|
+
* sides than at its top on a box four times wider than tall, and the letters
|
|
304
|
+
* there come out as spikes radiating from the centre. Held at a constant depth
|
|
305
|
+
* the ring's outer edge still touches the box all the way round, which is what
|
|
306
|
+
* both references draw and what a box of capitals measures.
|
|
307
|
+
*/
|
|
308
|
+
function ringMatrix(frame, ring, _u, v, glyphX) {
|
|
309
|
+
const a = frame.boxWidth / 2;
|
|
310
|
+
const b = frame.boxHeight / 2;
|
|
311
|
+
if (!(a > 0 && b > 0 && frame.srcWidth > 0 && frame.srcHeight > 0)) return void 0;
|
|
312
|
+
const uLeft = (glyphX - frame.srcX) / frame.srcWidth;
|
|
313
|
+
const turn = ring.clockwise ? -1 : 1;
|
|
314
|
+
const psi = Math.PI + turn * 2 * Math.PI * uLeft;
|
|
315
|
+
const cos = Math.cos(psi);
|
|
316
|
+
const sin = Math.sin(psi);
|
|
317
|
+
const rawX = -turn * a * sin;
|
|
318
|
+
const rawY = -turn * b * cos;
|
|
319
|
+
const speed = Math.hypot(rawX, rawY);
|
|
320
|
+
if (!(speed > 0)) return void 0;
|
|
321
|
+
const tx = rawX / speed;
|
|
322
|
+
const ty = rawY / speed;
|
|
323
|
+
const inX = -turn * -ty;
|
|
324
|
+
const inY = -turn * tx;
|
|
325
|
+
const head = ring.headOut ? -1 : 1;
|
|
326
|
+
const depth = (1 - RING_INNER) * b;
|
|
327
|
+
const sT = 2 * Math.PI * speed / frame.srcWidth;
|
|
328
|
+
const sN = depth / frame.srcHeight;
|
|
329
|
+
const inset = (ring.headOut ? v : 1 - v) * depth;
|
|
330
|
+
return [
|
|
331
|
+
sT * tx,
|
|
332
|
+
sT * ty,
|
|
333
|
+
sN * head * inX,
|
|
334
|
+
sN * head * inY,
|
|
335
|
+
frame.boxX + a + a * cos + inX * inset,
|
|
336
|
+
frame.boxY + b - b * sin + inY * inset
|
|
337
|
+
];
|
|
338
|
+
}
|
|
339
|
+
/**
|
|
340
|
+
* The two edges of a warp at one position across its box.
|
|
341
|
+
*
|
|
342
|
+
* @param preset The `a:prstTxWarp @prst` value.
|
|
343
|
+
* @param u Position across the box, `0` at its left edge and `1` at its right.
|
|
344
|
+
* @param adj The `a:avLst` `adj` guide in hundred-thousandths, when the file states one.
|
|
345
|
+
* @returns The top and bottom edges, `0`…`1` down the box, or `undefined` for a
|
|
346
|
+
* preset this file does not bend (an unknown name, or `textNoShape`).
|
|
347
|
+
*/
|
|
348
|
+
function textWarpEdges(preset, u, adj) {
|
|
349
|
+
const env = ENVELOPES.get(preset);
|
|
350
|
+
if (!env) return void 0;
|
|
351
|
+
const a = (adj ?? env.defaultAdj) / 1e5;
|
|
352
|
+
return env.at(Math.min(1, Math.max(0, u)), a);
|
|
353
|
+
}
|
|
354
|
+
//#endregion
|
|
355
|
+
export { isTextWarp, warpGlyphMatrix };
|
|
@@ -1,4 +1,42 @@
|
|
|
1
1
|
import { PoNode } from '../po-helpers.js';
|
|
2
|
+
/**
|
|
3
|
+
* §20.1.4.1.16 `a:fontScheme` — the two typefaces a theme names, each in three
|
|
4
|
+
* scripts. A document does not repeat them: it refers to them by TOKEN
|
|
5
|
+
* (`+mj-lt`, `+mn-ea`, …), which every reader is expected to resolve.
|
|
6
|
+
*/
|
|
7
|
+
export interface ThemeFonts {
|
|
8
|
+
/** `a:majorFont` — headings. */
|
|
9
|
+
readonly major: ThemeFontSlots;
|
|
10
|
+
/** `a:minorFont` — body text. */
|
|
11
|
+
readonly minor: ThemeFontSlots;
|
|
12
|
+
}
|
|
13
|
+
/** One font of a scheme, in the three scripts a run may pick from. */
|
|
14
|
+
export interface ThemeFontSlots {
|
|
15
|
+
readonly latin?: string;
|
|
16
|
+
readonly ea?: string;
|
|
17
|
+
readonly cs?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Parse a theme's font scheme (§20.1.4.1.16).
|
|
21
|
+
*
|
|
22
|
+
* @param themeXml The raw theme part bytes, UTF-8.
|
|
23
|
+
* @returns The two fonts; slots the theme leaves empty are absent.
|
|
24
|
+
*/
|
|
25
|
+
export declare function parseThemeFonts(themeXml: Uint8Array): ThemeFonts;
|
|
26
|
+
/**
|
|
27
|
+
* Resolve a typeface written as a theme TOKEN to the name it stands for.
|
|
28
|
+
*
|
|
29
|
+
* A slide states its fonts as `+mn-lt` far more often than by name, and left
|
|
30
|
+
* unresolved that string travels into the model as if it WERE a typeface: no
|
|
31
|
+
* substitution table knows it, so a deck whose theme is Times came out in a
|
|
32
|
+
* grotesque (45541_Header).
|
|
33
|
+
*
|
|
34
|
+
* @param typeface The `@typeface` as the file writes it.
|
|
35
|
+
* @param fonts The theme's font scheme, when the part was read.
|
|
36
|
+
* @returns The resolved name; the input unchanged when it is not a token, and
|
|
37
|
+
* `undefined` when it is a token the theme leaves empty.
|
|
38
|
+
*/
|
|
39
|
+
export declare function resolveThemeFont(typeface: string | undefined, fonts: ThemeFonts | undefined): string | undefined;
|
|
2
40
|
/**
|
|
3
41
|
* Parse a DrawingML theme part (ECMA-376 §20.1.6.2, `a:clrScheme`) into a
|
|
4
42
|
* `name → hex` colour map. Reads the twelve scheme slots — `dk1`/`lt1`/`dk2`/
|
|
@@ -26,6 +26,69 @@ var SCHEME_SLOTS = new Set([
|
|
|
26
26
|
"folHlink"
|
|
27
27
|
]);
|
|
28
28
|
/**
|
|
29
|
+
* Parse a theme's font scheme (§20.1.4.1.16).
|
|
30
|
+
*
|
|
31
|
+
* @param themeXml The raw theme part bytes, UTF-8.
|
|
32
|
+
* @returns The two fonts; slots the theme leaves empty are absent.
|
|
33
|
+
*/
|
|
34
|
+
function parseThemeFonts(themeXml) {
|
|
35
|
+
const scheme = poFindByPath(parser.parse(decoder.decode(themeXml)), [
|
|
36
|
+
"a:theme",
|
|
37
|
+
"a:themeElements",
|
|
38
|
+
"a:fontScheme"
|
|
39
|
+
]);
|
|
40
|
+
const read = (tag) => {
|
|
41
|
+
const font = scheme ? poChildren(scheme).find((c) => poIs(c, tag)) : void 0;
|
|
42
|
+
const slot = (name) => {
|
|
43
|
+
const node = font ? poChildren(font).find((c) => poIs(c, name)) : void 0;
|
|
44
|
+
const face = node ? poAttr(node, "typeface")?.trim() : void 0;
|
|
45
|
+
return face === void 0 || face === "" ? void 0 : face;
|
|
46
|
+
};
|
|
47
|
+
const [latin, ea, cs] = [
|
|
48
|
+
slot("a:latin"),
|
|
49
|
+
slot("a:ea"),
|
|
50
|
+
slot("a:cs")
|
|
51
|
+
];
|
|
52
|
+
return {
|
|
53
|
+
...latin !== void 0 ? { latin } : {},
|
|
54
|
+
...ea !== void 0 ? { ea } : {},
|
|
55
|
+
...cs !== void 0 ? { cs } : {}
|
|
56
|
+
};
|
|
57
|
+
};
|
|
58
|
+
return {
|
|
59
|
+
major: read("a:majorFont"),
|
|
60
|
+
minor: read("a:minorFont")
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
var THEME_FONT_TOKENS = new Map([
|
|
64
|
+
["+mj-lt", ["major", "latin"]],
|
|
65
|
+
["+mn-lt", ["minor", "latin"]],
|
|
66
|
+
["+mj-ea", ["major", "ea"]],
|
|
67
|
+
["+mn-ea", ["minor", "ea"]],
|
|
68
|
+
["+mj-cs", ["major", "cs"]],
|
|
69
|
+
["+mn-cs", ["minor", "cs"]]
|
|
70
|
+
]);
|
|
71
|
+
/**
|
|
72
|
+
* Resolve a typeface written as a theme TOKEN to the name it stands for.
|
|
73
|
+
*
|
|
74
|
+
* A slide states its fonts as `+mn-lt` far more often than by name, and left
|
|
75
|
+
* unresolved that string travels into the model as if it WERE a typeface: no
|
|
76
|
+
* substitution table knows it, so a deck whose theme is Times came out in a
|
|
77
|
+
* grotesque (45541_Header).
|
|
78
|
+
*
|
|
79
|
+
* @param typeface The `@typeface` as the file writes it.
|
|
80
|
+
* @param fonts The theme's font scheme, when the part was read.
|
|
81
|
+
* @returns The resolved name; the input unchanged when it is not a token, and
|
|
82
|
+
* `undefined` when it is a token the theme leaves empty.
|
|
83
|
+
*/
|
|
84
|
+
function resolveThemeFont(typeface, fonts) {
|
|
85
|
+
if (typeface === void 0 || !typeface.startsWith("+")) return typeface;
|
|
86
|
+
const slot = THEME_FONT_TOKENS.get(typeface.toLowerCase());
|
|
87
|
+
if (!slot || !fonts) return void 0;
|
|
88
|
+
const [which, script] = slot;
|
|
89
|
+
return fonts[which][script];
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
29
92
|
* Parse a DrawingML theme part (ECMA-376 §20.1.6.2, `a:clrScheme`) into a
|
|
30
93
|
* `name → hex` colour map. Reads the twelve scheme slots — `dk1`/`lt1`/`dk2`/
|
|
31
94
|
* `lt2`, `accent1`–`accent6`, `hlink`/`folHlink` — taking each slot's
|
|
@@ -105,7 +168,13 @@ function parseThemeFillStyles(themeXml) {
|
|
|
105
168
|
"a:fmtScheme",
|
|
106
169
|
"a:fillStyleLst"
|
|
107
170
|
]);
|
|
108
|
-
return list ?
|
|
171
|
+
return list ? fillsOf(list) : [];
|
|
172
|
+
}
|
|
173
|
+
function fillsOf(list) {
|
|
174
|
+
return poChildren(list).filter((c) => {
|
|
175
|
+
const tag = poTag(c);
|
|
176
|
+
return tag !== void 0 && tag !== "#text";
|
|
177
|
+
});
|
|
109
178
|
}
|
|
110
179
|
/**
|
|
111
180
|
* Parse a theme's BACKGROUND fill styles (§20.1.4.1.7 `a:bgFillStyleLst`), as
|
|
@@ -123,7 +192,7 @@ function parseThemeBgFillStyles(themeXml) {
|
|
|
123
192
|
"a:fmtScheme",
|
|
124
193
|
"a:bgFillStyleLst"
|
|
125
194
|
]);
|
|
126
|
-
return list ?
|
|
195
|
+
return list ? fillsOf(list) : [];
|
|
127
196
|
}
|
|
128
197
|
/**
|
|
129
198
|
* Parse a theme's effect styles (§20.1.4.1.15 `a:effectStyleLst`), as the raw
|
|
@@ -141,7 +210,7 @@ function parseThemeEffectStyles(themeXml) {
|
|
|
141
210
|
"a:fmtScheme",
|
|
142
211
|
"a:effectStyleLst"
|
|
143
212
|
]);
|
|
144
|
-
return list ?
|
|
213
|
+
return list ? fillsOf(list) : [];
|
|
145
214
|
}
|
|
146
215
|
var EMU_PER_POINT = 12700;
|
|
147
216
|
function colorOf(slot) {
|
|
@@ -154,4 +223,4 @@ function colorOf(slot) {
|
|
|
154
223
|
}
|
|
155
224
|
}
|
|
156
225
|
//#endregion
|
|
157
|
-
export { parseTheme, parseThemeBgFillStyles, parseThemeEffectStyles, parseThemeFillStyles, parseThemeLineWidths };
|
|
226
|
+
export { parseTheme, parseThemeBgFillStyles, parseThemeEffectStyles, parseThemeFillStyles, parseThemeFonts, parseThemeLineWidths, resolveThemeFont };
|
|
@@ -10,6 +10,18 @@ export interface FontMeasure {
|
|
|
10
10
|
readonly pdfWidthForGid: (gid: number) => number;
|
|
11
11
|
/** Shaped width of `text` at `fontSize`, in points. */
|
|
12
12
|
readonly textWidthPt: (text: string, fontSize: number) => number;
|
|
13
|
+
/**
|
|
14
|
+
* How far this text's INK actually reaches from its baseline, in points:
|
|
15
|
+
* `above` up to its highest glyph edge, `below` down to its lowest. Not the
|
|
16
|
+
* font's line box — "HHH" reaches a cap height and no further, and only a
|
|
17
|
+
* string carrying a descender reaches under the baseline at all. WordArt
|
|
18
|
+
* stretches the ink it draws to fill its shape (§20.1.9.10), so it is the
|
|
19
|
+
* ink and not the line box that has to be measured.
|
|
20
|
+
*/
|
|
21
|
+
readonly textInkPt: (text: string, fontSize: number) => {
|
|
22
|
+
above: number;
|
|
23
|
+
below: number;
|
|
24
|
+
};
|
|
13
25
|
/** Encode `text` as a hex string of 4-digit glyph ids (Identity-H addressing). */
|
|
14
26
|
readonly encodeTextAsCidHex: (text: string) => string;
|
|
15
27
|
/**
|
|
@@ -52,9 +52,45 @@ function createFontMeasure(parsed, kern = true) {
|
|
|
52
52
|
if (hex !== "") parts.push(`<${hex}>`);
|
|
53
53
|
return `[${parts.join(" ")}] TJ`;
|
|
54
54
|
};
|
|
55
|
+
const glyf = parsed.tables.get("glyf");
|
|
56
|
+
const glyphInk = (gid) => {
|
|
57
|
+
if (!glyf || gid < 0 || gid >= parsed.numGlyphs) return void 0;
|
|
58
|
+
const start = parsed.glyphOffsets[gid] ?? 0;
|
|
59
|
+
if ((parsed.glyphOffsets[gid + 1] ?? 0) - start < 10) return void 0;
|
|
60
|
+
const at = glyf.offset + start;
|
|
61
|
+
const i16 = (o) => {
|
|
62
|
+
const v = (parsed.raw[at + o] ?? 0) << 8 | (parsed.raw[at + o + 1] ?? 0);
|
|
63
|
+
return v >= 32768 ? v - 65536 : v;
|
|
64
|
+
};
|
|
65
|
+
return {
|
|
66
|
+
yMin: i16(4),
|
|
67
|
+
yMax: i16(8)
|
|
68
|
+
};
|
|
69
|
+
};
|
|
70
|
+
const textInkPt = (text, fontSize) => {
|
|
71
|
+
const shaped = shapeText(text, parsed.glyphForCodepoint, parsed.advanceWidths, parsed.ligatures, kerning, parsed.joiningForms);
|
|
72
|
+
let yMax = -Infinity;
|
|
73
|
+
let yMin = Infinity;
|
|
74
|
+
for (const gid of shaped.gids) {
|
|
75
|
+
const ink = glyphInk(gid);
|
|
76
|
+
if (!ink) continue;
|
|
77
|
+
if (ink.yMax > yMax) yMax = ink.yMax;
|
|
78
|
+
if (ink.yMin < yMin) yMin = ink.yMin;
|
|
79
|
+
}
|
|
80
|
+
if (!Number.isFinite(yMax) || !Number.isFinite(yMin)) {
|
|
81
|
+
yMax = parsed.capHeight || parsed.ascender;
|
|
82
|
+
yMin = 0;
|
|
83
|
+
}
|
|
84
|
+
const k = fontSize / parsed.unitsPerEm;
|
|
85
|
+
return {
|
|
86
|
+
above: yMax * k,
|
|
87
|
+
below: -yMin * k
|
|
88
|
+
};
|
|
89
|
+
};
|
|
55
90
|
return {
|
|
56
91
|
pdfWidthForGid,
|
|
57
92
|
textWidthPt,
|
|
93
|
+
textInkPt,
|
|
58
94
|
encodeTextAsCidHex,
|
|
59
95
|
showText
|
|
60
96
|
};
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { FamilyKey } from './remote-fonts.js';
|
|
2
|
+
import { FlowDoc } from '../ir/flow.js';
|
|
3
|
+
/**
|
|
4
|
+
* The curated substitute families a document's text needs, always including the
|
|
5
|
+
* sans default as the fallback for unstyled runs, math and chart labels.
|
|
6
|
+
*
|
|
7
|
+
* @param flow The parsed document.
|
|
8
|
+
* @returns One {@link FamilyKey} per distinct family the document names.
|
|
9
|
+
*/
|
|
10
|
+
export declare function familiesInFlow(flow: FlowDoc): Set<FamilyKey>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { resolveFamilyKey } from "./remote-fonts.js";
|
|
2
|
+
//#region src/core/fonts/families.ts
|
|
3
|
+
/**
|
|
4
|
+
* The curated substitute families a document's text needs, always including the
|
|
5
|
+
* sans default as the fallback for unstyled runs, math and chart labels.
|
|
6
|
+
*
|
|
7
|
+
* @param flow The parsed document.
|
|
8
|
+
* @returns One {@link FamilyKey} per distinct family the document names.
|
|
9
|
+
*/
|
|
10
|
+
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());
|
|
14
|
+
};
|
|
15
|
+
add(flow.styles.defaultRunProperties.fontFamily?.ascii);
|
|
16
|
+
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);
|
|
21
|
+
}
|
|
22
|
+
for (const abstract of flow.numbering?.abstractNums.values() ?? []) for (const level of abstract.levels.values()) add(level.runProperties.fontFamily?.ascii);
|
|
23
|
+
const shapeText = (shape) => {
|
|
24
|
+
if (shape.text) visit(shape.text.content);
|
|
25
|
+
for (const child of shape.children ?? []) shapeText(child.shape);
|
|
26
|
+
};
|
|
27
|
+
const visit = (elements) => {
|
|
28
|
+
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);
|
|
31
|
+
} else if (el.kind === "table") for (const row of el.table.rows) for (const cell of row.cells) visit(cell.content);
|
|
32
|
+
else if (el.kind === "shape") shapeText(el.shape);
|
|
33
|
+
};
|
|
34
|
+
visit(flow.body);
|
|
35
|
+
for (const band of flow.headersFooters?.values() ?? []) visit(band);
|
|
36
|
+
for (const note of flow.footnotes?.values() ?? []) visit(note);
|
|
37
|
+
for (const note of flow.endnotes?.values() ?? []) visit(note);
|
|
38
|
+
for (const comment of flow.comments?.values() ?? []) visit(comment.content);
|
|
39
|
+
const keys = new Set(["arimo"]);
|
|
40
|
+
for (const name of names) keys.add(resolveFamilyKey(name));
|
|
41
|
+
return keys;
|
|
42
|
+
}
|
|
43
|
+
//#endregion
|
|
44
|
+
export { familiesInFlow };
|
|
@@ -1,2 +1,3 @@
|
|
|
1
|
-
export { fetchFontSet, resolveFamilyKey, clearFontCache } from './remote-fonts.js';
|
|
2
|
-
export
|
|
1
|
+
export { fetchFontSet, fetchScriptFont, isScriptKey, resolveFamilyKey, resolveFamilyStyle, clearFontCache, } from './remote-fonts.js';
|
|
2
|
+
export { scriptForCodepoint, scriptsInFlow } from './scripts.js';
|
|
3
|
+
export type { FamilyKey, FamilyStyle, FetchFontSetOptions, FetchLike, ScriptKey, SubstituteKey, } from './remote-fonts.js';
|
|
@@ -1,11 +1,61 @@
|
|
|
1
1
|
import { FontBytesByVariant } from '../font/index.js';
|
|
2
2
|
/** A curated open substitute family (the Croscore + Carlito/Caladea set, like LibreOffice). */
|
|
3
3
|
export type FamilyKey = 'arimo' | 'tinos' | 'cousine' | 'carlito' | 'caladea';
|
|
4
|
+
/**
|
|
5
|
+
* A WRITING SYSTEM the curated five cannot draw at all. They are Latin families
|
|
6
|
+
* — Greek, Cyrillic and Hebrew ride along, everything else is a notdef box —
|
|
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.
|
|
9
|
+
*
|
|
10
|
+
* These are fetched only when a document actually holds such a character, and
|
|
11
|
+
* only in the regular weight: Noto Sans SC is ten megabytes, and a bold run in
|
|
12
|
+
* it is better stroked (see `SyntheticFace`) than downloaded four times over.
|
|
13
|
+
*/
|
|
14
|
+
export type ScriptKey = 'jp' | 'kr' | 'sc' | 'arabic' | 'hebrew' | 'thai' | 'symbols';
|
|
15
|
+
/** Either kind of substitute — a Latin family or a per-script face. */
|
|
16
|
+
export type SubstituteKey = FamilyKey | ScriptKey;
|
|
17
|
+
/** Whether a substitute key names a writing system rather than a Latin family. */
|
|
18
|
+
export declare function isScriptKey(key: SubstituteKey): key is ScriptKey;
|
|
19
|
+
/**
|
|
20
|
+
* Fetch the ONE face a writing system is drawn with.
|
|
21
|
+
*
|
|
22
|
+
* @param script The writing system.
|
|
23
|
+
* @param fetchImpl Injectable `fetch` (defaults to the global one).
|
|
24
|
+
* @returns The regular face, or `undefined` when the download fails.
|
|
25
|
+
*/
|
|
26
|
+
export declare function fetchScriptFont(script: ScriptKey, fetchImpl?: FetchLike): Promise<FontBytesByVariant | undefined>;
|
|
27
|
+
/** A family name, read: which substitute it maps to and what it says about the face. */
|
|
28
|
+
export interface FamilyStyle {
|
|
29
|
+
readonly key: FamilyKey;
|
|
30
|
+
/** The NAME asks for a heavy face (`Arial Black`, `DIN-Bold`). */
|
|
31
|
+
readonly bold?: boolean;
|
|
32
|
+
/** …or a slanted one (`Frutiger Oblique`). */
|
|
33
|
+
readonly italic?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* …or a narrow one (`Arial Narrow`), as the fraction of the normal advance it
|
|
36
|
+
* sets at. No curated family ships a condensed cut, so the substitute is
|
|
37
|
+
* squeezed instead — Arial Narrow's own widths are 82 % of Arial's, which is
|
|
38
|
+
* also what LibreOffice's Liberation Sans Narrow reproduces.
|
|
39
|
+
*/
|
|
40
|
+
readonly widthScale?: number;
|
|
41
|
+
}
|
|
4
42
|
/**
|
|
5
43
|
* Map a document-referenced font family to a curated open substitute: an exact
|
|
6
44
|
* metric twin when one is known (e.g. Calibri → Carlito), otherwise a
|
|
7
45
|
* serif/mono/sans style fallback.
|
|
8
46
|
*
|
|
47
|
+
* The name may also carry the FACE — `Times New Roman Bold` is the Times
|
|
48
|
+
* family, and read whole it matches no twin at all and fell through to the
|
|
49
|
+
* generic sans, which is how a Times heading came out in a grotesque.
|
|
50
|
+
*
|
|
51
|
+
* @param name The referenced family name (case-insensitive); empty ⇒ Arimo.
|
|
52
|
+
* @returns The chosen family and what the name said about the face.
|
|
53
|
+
*/
|
|
54
|
+
export declare function resolveFamilyStyle(name: string | undefined): FamilyStyle;
|
|
55
|
+
/**
|
|
56
|
+
* The curated substitute for a family name — {@link resolveFamilyStyle} without
|
|
57
|
+
* the face it also carries.
|
|
58
|
+
*
|
|
9
59
|
* @param name The referenced family name (case-insensitive); empty ⇒ Arimo.
|
|
10
60
|
* @returns The chosen {@link FamilyKey}.
|
|
11
61
|
*/
|