@openpresentation/opf-pptx 0.16.0 → 0.16.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.
- package/dist/index.js +46 -2
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3171,13 +3171,57 @@ function slideTextMeasurement(presentation, index, options) {
|
|
|
3171
3171
|
return chosenFamilyMeasurement(adapted);
|
|
3172
3172
|
}
|
|
3173
3173
|
|
|
3174
|
+
// DrawingML has only a bold flag: a run written for the chosen family (no provider face such as Roboto SemiBold) draws that
|
|
3175
|
+
// family's bold face at 600 and above and its regular face below, whatever weight the engine asked for.
|
|
3176
|
+
const nativeDrawnWeight = weight => weight >= 600 ? 700 : 400;
|
|
3177
|
+
const FACE_WEIGHTS = [100, 200, 300, 400, 500, 600, 700, 800, 900];
|
|
3178
|
+
const styleName = (weight, italic) => `weight ${weight} ${italic ? 'italic' : 'upright'}`;
|
|
3179
|
+
|
|
3180
|
+
// RR-59 (opf-pptx#214): a provider's font-unavailable for a family whose faces are loaded, only not at the requested weight and
|
|
3181
|
+
// style, names the family, weight and style that are missing and the ones that are loaded. Any other error is returned unchanged.
|
|
3182
|
+
function missingFaceError(error, style, measurement, drawn) {
|
|
3183
|
+
if (error?.code !== 'font-unavailable' || typeof style?.fontFamily !== 'string') return error;
|
|
3184
|
+
const weight = style.fontWeight ?? 400, italic = !!style.italic, loaded = [];
|
|
3185
|
+
for (const candidateItalic of [false, true]) for (const candidate of FACE_WEIGHTS) {
|
|
3186
|
+
if (candidate === weight && candidateItalic === italic) continue;
|
|
3187
|
+
try { const face = measurement.resolveStyle({...style, fontWeight: candidate, italic: candidateItalic}); loaded.push({weight: candidate, italic: candidateItalic, family: face?.fontFace?.family ?? face?.fontFamily}); }
|
|
3188
|
+
catch { /* not loaded at this weight and style */ }
|
|
3189
|
+
}
|
|
3190
|
+
if (!loaded.length) return error;
|
|
3191
|
+
const policy = error.details?.substitutionPolicy, replacement = error.details?.replacement;
|
|
3192
|
+
const families = [...new Set(loaded.map(face => face.family).filter(Boolean))], wanted = styleName(drawn ?? weight, italic);
|
|
3193
|
+
const message = `No local font face for '${style.fontFamily}' at ${styleName(weight, italic)}${policy ? ` under substitutionPolicy '${policy}'` : ''}`
|
|
3194
|
+
+ `${drawn !== undefined && drawn !== weight ? `, nor at ${wanted}, the face PowerPoint draws the run with` : ''}. '${style.fontFamily}' resolves only at ${loaded.map(face => styleName(face.weight, face.italic)).join(', ')}`
|
|
3195
|
+
+ `${families.length ? ` (${families.join(', ')})` : ''}. Load ${typeof replacement === 'string' ? `its replacement '${replacement}' or licensed '${style.fontFamily}' files` : `a '${style.fontFamily}' face`} at ${wanted} (loadFonts({faces})).`;
|
|
3196
|
+
const Type = typeof error.constructor === 'function' && error.constructor !== Error ? error.constructor : undefined;
|
|
3197
|
+
const details = {...error.details, fontFamily: style.fontFamily, fontWeight: weight, italic, loadedStyles: loaded.map(({weight, italic}) => ({weight, italic})), cause: error.message};
|
|
3198
|
+
let improved;
|
|
3199
|
+
try { improved = Type ? new Type('font-unavailable', message, details) : undefined; } catch { improved = undefined; }
|
|
3200
|
+
if (!(improved instanceof Error) || improved.code !== 'font-unavailable') improved = Object.assign(new Error(message), {name: error.name, code: 'font-unavailable', details});
|
|
3201
|
+
improved.cause = error;
|
|
3202
|
+
return improved;
|
|
3203
|
+
}
|
|
3204
|
+
|
|
3174
3205
|
function chosenFamilyMeasurement(measurement) {
|
|
3175
3206
|
if (!measurement || typeof measurement.resolveStyle !== 'function') return measurement;
|
|
3176
3207
|
const resolveFont = typeof measurement.resolveFont === 'function' ? style => measurement.resolveFont(style) : undefined;
|
|
3177
3208
|
const wrapped = {
|
|
3178
3209
|
measure: (text, size, style) => measurement.measure(text, size, style),
|
|
3179
|
-
resolveStyle:
|
|
3180
|
-
|
|
3210
|
+
resolveStyle: input => {
|
|
3211
|
+
// RR-59 (opf-pptx#214): engine-chosen weights (the image placeholder label and media caption at 600, core's metric value at
|
|
3212
|
+
// 800, metric labels and timeline event text at 500) are not always loaded: a metric replacement holds at 400 and 700 only
|
|
3213
|
+
// (Aptos -> Intos). The run is written for the chosen family with a bold flag, so it is measured at the weight PowerPoint
|
|
3214
|
+
// draws it in, which the policy does resolve. A family the policy cannot resolve at that weight either still fails, and
|
|
3215
|
+
// a style that resolves keeps its own resolution, so no output that exported before changes.
|
|
3216
|
+
let style = input, resolved;
|
|
3217
|
+
try { resolved = measurement.resolveStyle(style); }
|
|
3218
|
+
catch (error) {
|
|
3219
|
+
const weight = style?.fontWeight ?? 400, drawn = nativeDrawnWeight(weight);
|
|
3220
|
+
if (error?.code !== 'font-unavailable' || drawn === weight) throw missingFaceError(error, style, measurement);
|
|
3221
|
+
style = {...style, fontWeight: drawn};
|
|
3222
|
+
try { resolved = measurement.resolveStyle(style); }
|
|
3223
|
+
catch { throw missingFaceError(error, input, measurement, drawn); }
|
|
3224
|
+
}
|
|
3181
3225
|
const requested = style?.fontFamily;
|
|
3182
3226
|
// Theme tokens are provider business; the exporter always passes concrete families.
|
|
3183
3227
|
if (typeof requested !== 'string' || requested.startsWith('+')) return resolved;
|