@turntrout/subfont 1.7.0 → 1.7.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.
@@ -1,352 +1,322 @@
1
- const fs = require('fs/promises');
2
- const pathModule = require('path');
3
- const crypto = require('crypto');
4
- const { getVariationAxisBounds } = require('./variationAxes');
5
- const collectFeatureGlyphIds = require('./collectFeatureGlyphIds');
6
- const subsetFontWithGlyphs = require('./subsetFontWithGlyphs');
7
-
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports._SubsetDiskCache = void 0;
37
+ exports.getSubsetPromiseId = getSubsetPromiseId;
38
+ exports.getSubsetsForFontUsage = getSubsetsForFontUsage;
39
+ exports._subsetCacheKey = subsetCacheKey;
40
+ const fs = __importStar(require("fs/promises"));
41
+ const pathModule = require("path");
42
+ const crypto = __importStar(require("crypto"));
43
+ const variationAxes_1 = require("./variationAxes");
44
+ const collectFeatureGlyphIds = require("./collectFeatureGlyphIds");
45
+ const subsetFontWithGlyphs = require("./subsetFontWithGlyphs");
8
46
  // Bump when subsetting behaviour changes to invalidate stale disk-cache
9
47
  // entries (e.g. after adding hinting removal or table stripping).
10
48
  const SUBSET_CACHE_VERSION = '2';
11
-
12
49
  // Cache the SHA-256 hash state after feeding SUBSET_CACHE_VERSION + fontBuffer.
13
50
  // For a font with 2 target formats this halves the hashing work on large buffers.
14
51
  // Uses WeakMap so entries are garbage-collected when the buffer is released.
15
52
  const fontBufferHashPrefixes = new WeakMap();
16
53
  function getFontBufferHashPrefix(fontBuffer) {
17
- if (!fontBufferHashPrefixes.has(fontBuffer)) {
18
- const hash = crypto.createHash('sha256');
19
- hash.update(SUBSET_CACHE_VERSION);
20
- hash.update(fontBuffer);
21
- fontBufferHashPrefixes.set(fontBuffer, hash);
22
- }
23
- return fontBufferHashPrefixes.get(fontBuffer);
54
+ let cached = fontBufferHashPrefixes.get(fontBuffer);
55
+ if (!cached) {
56
+ cached = crypto.createHash('sha256');
57
+ cached.update(SUBSET_CACHE_VERSION);
58
+ cached.update(fontBuffer);
59
+ fontBufferHashPrefixes.set(fontBuffer, cached);
60
+ }
61
+ return cached;
24
62
  }
25
-
26
- function subsetCacheKey(
27
- fontBuffer,
28
- text,
29
- targetFormat,
30
- variationAxes,
31
- featureGlyphIds
32
- ) {
33
- // Clone the pre-computed prefix (version + font buffer) and append
34
- // the remaining fields. hash.copy() is O(1) — just copies the
35
- // internal digest state, avoiding re-hashing the entire font buffer.
36
- const hash = getFontBufferHashPrefix(fontBuffer).copy();
37
- hash.update(text);
38
- hash.update(targetFormat);
39
- if (variationAxes) hash.update(JSON.stringify(variationAxes));
40
- if (featureGlyphIds) hash.update(JSON.stringify(featureGlyphIds));
41
- return hash.digest('hex');
63
+ function subsetCacheKey(fontBuffer, text, targetFormat, variationAxes, featureGlyphIds) {
64
+ // Clone the pre-computed prefix (version + font buffer) and append
65
+ // the remaining fields. hash.copy() is O(1) — just copies the
66
+ // internal digest state, avoiding re-hashing the entire font buffer.
67
+ const hash = getFontBufferHashPrefix(fontBuffer).copy();
68
+ hash.update(text);
69
+ hash.update(targetFormat);
70
+ if (variationAxes)
71
+ hash.update(JSON.stringify(variationAxes));
72
+ if (featureGlyphIds)
73
+ hash.update(JSON.stringify(featureGlyphIds));
74
+ return hash.digest('hex');
42
75
  }
43
-
44
76
  class SubsetDiskCache {
45
- constructor(cacheDir, console) {
46
- this._cacheDir = cacheDir;
47
- this._console = console;
48
- this._ensured = false;
49
- this._warnedWrite = false;
50
- }
51
-
52
- async _ensureDir() {
53
- if (!this._ensured) {
54
- // Only attempt once — persistent failures (bad path, permissions)
55
- // are far more common than transient ones, and retrying just
56
- // produces repeated warnings.
57
- this._ensured = true;
58
- try {
59
- await fs.mkdir(this._cacheDir, { recursive: true });
60
- } catch (err) {
61
- if (this._console) {
62
- this._console.warn(
63
- `subfont: cache directory ${this._cacheDir} could not be created: ${err.message}`
64
- );
77
+ _cacheDir;
78
+ _console;
79
+ _ensured;
80
+ _warnedWrite;
81
+ constructor(cacheDir, console) {
82
+ this._cacheDir = cacheDir;
83
+ this._console = console ?? null;
84
+ this._ensured = false;
85
+ this._warnedWrite = false;
86
+ }
87
+ async _ensureDir() {
88
+ if (!this._ensured) {
89
+ // Only attempt once — persistent failures (bad path, permissions)
90
+ // are far more common than transient ones, and retrying just
91
+ // produces repeated warnings.
92
+ this._ensured = true;
93
+ try {
94
+ await fs.mkdir(this._cacheDir, { recursive: true });
95
+ }
96
+ catch (err) {
97
+ if (this._console) {
98
+ this._console.warn(`subfont: cache directory ${this._cacheDir} could not be created: ${err.message}`);
99
+ }
100
+ }
65
101
  }
66
- }
67
102
  }
68
- }
69
-
70
- async get(key) {
71
- const filePath = pathModule.join(this._cacheDir, key);
72
- try {
73
- return await fs.readFile(filePath);
74
- } catch {
75
- return undefined;
103
+ async get(key) {
104
+ const filePath = pathModule.join(this._cacheDir, key);
105
+ try {
106
+ return await fs.readFile(filePath);
107
+ }
108
+ catch {
109
+ return undefined;
110
+ }
76
111
  }
77
- }
78
-
79
- async set(key, buffer) {
80
- await this._ensureDir();
81
- const filePath = pathModule.join(this._cacheDir, key);
82
- try {
83
- await fs.writeFile(filePath, buffer);
84
- } catch (err) {
85
- // If the directory was removed after init, retry once
86
- if (err.code === 'ENOENT') {
112
+ async set(key, buffer) {
113
+ await this._ensureDir();
114
+ const filePath = pathModule.join(this._cacheDir, key);
87
115
  try {
88
- await fs.mkdir(this._cacheDir, { recursive: true });
89
- await fs.writeFile(filePath, buffer);
90
- return;
91
- } catch {
92
- // Fall through to warning below
116
+ await fs.writeFile(filePath, buffer);
117
+ }
118
+ catch (err) {
119
+ const errno = err;
120
+ // If the directory was removed after init, retry once
121
+ if (errno.code === 'ENOENT') {
122
+ try {
123
+ await fs.mkdir(this._cacheDir, { recursive: true });
124
+ await fs.writeFile(filePath, buffer);
125
+ return;
126
+ }
127
+ catch {
128
+ // Fall through to warning below
129
+ }
130
+ }
131
+ if (this._warnedWrite)
132
+ return;
133
+ this._warnedWrite = true;
134
+ if (this._console) {
135
+ this._console.warn(`subfont: failed to write cache entry ${key}: ${errno.message}`);
136
+ }
93
137
  }
94
- }
95
- if (this._warnedWrite) return;
96
- this._warnedWrite = true;
97
- if (this._console) {
98
- this._console.warn(
99
- `subfont: failed to write cache entry ${key}: ${err.message}`
100
- );
101
- }
102
138
  }
103
- }
104
139
  }
105
-
140
+ exports._SubsetDiskCache = SubsetDiskCache;
106
141
  function getSubsetPromiseId(fontUsage, format, variationAxes = null) {
107
- return [
108
- fontUsage.text,
109
- fontUsage.fontUrl,
110
- format,
111
- JSON.stringify(variationAxes),
112
- ].join('\x1d');
142
+ return [
143
+ fontUsage.text,
144
+ fontUsage.fontUrl,
145
+ format,
146
+ JSON.stringify(variationAxes),
147
+ ].join('\x1d');
113
148
  }
114
-
115
- async function getSubsetsForFontUsage(
116
- assetGraph,
117
- htmlOrSvgAssetTextsWithProps,
118
- formats,
119
- seenAxisValuesByFontUrlAndAxisName,
120
- cacheDir = null,
121
- console = null,
122
- debug = false
123
- ) {
124
- const diskCache = cacheDir ? new SubsetDiskCache(cacheDir, console) : null;
125
- const cacheStats = diskCache ? { hits: 0, misses: 0 } : null;
126
-
127
- // Collect one canonical fontUsage per font URL
128
- const canonicalFontUsageByUrl = new Map();
129
- for (const item of htmlOrSvgAssetTextsWithProps) {
130
- for (const fontUsage of item.fontUsages) {
131
- if (
132
- fontUsage.fontUrl &&
133
- !canonicalFontUsageByUrl.has(fontUsage.fontUrl)
134
- ) {
135
- canonicalFontUsageByUrl.set(fontUsage.fontUrl, fontUsage);
136
- }
137
- }
138
- }
139
-
140
- const allFontUrls = [...canonicalFontUsageByUrl.keys()];
141
-
142
- // Load font assets
143
- await assetGraph.populate({
144
- followRelations: {
145
- to: { url: { $or: allFontUrls } },
146
- },
147
- });
148
-
149
- const fontAssetsByUrl = new Map();
150
- const originalFontBuffers = new Map();
151
- for (const fontUrl of allFontUrls) {
152
- const fontAsset = assetGraph.findAssets({
153
- url: fontUrl,
154
- isLoaded: true,
155
- })[0];
156
- if (fontAsset) {
157
- fontAssetsByUrl.set(fontUrl, fontAsset);
158
- originalFontBuffers.set(fontUrl, fontAsset.rawSrc);
159
- }
160
- }
161
-
162
- // Compute variation axis bounds for all fonts in parallel
163
- const fontUrlsWithAssets = allFontUrls.filter((url) =>
164
- fontAssetsByUrl.has(url)
165
- );
166
- const boundsResults = await Promise.all(
167
- fontUrlsWithAssets.map((fontUrl) =>
168
- getVariationAxisBounds(
169
- fontAssetsByUrl,
170
- fontUrl,
171
- seenAxisValuesByFontUrlAndAxisName
172
- )
173
- )
174
- );
175
- const variationAxisBoundsCache = new Map();
176
- for (let i = 0; i < fontUrlsWithAssets.length; i++) {
177
- variationAxisBoundsCache.set(fontUrlsWithAssets[i], boundsResults[i]);
178
- }
179
-
180
- const subsetPromiseMap = new Map();
181
- const subsetInfoByFontUrl = new Map();
182
-
183
- // Process fonts concurrently — each font's feature glyph collection
184
- // and subset queuing run in parallel, so fonts without feature settings
185
- // don't wait behind fonts that need collectFeatureGlyphIds.
186
- await Promise.all(
187
- [...canonicalFontUsageByUrl].map(async ([fontUrl, fontUsage]) => {
188
- const fontBuffer = originalFontBuffers.get(fontUrl);
189
- if (!fontBuffer) return;
190
- const text = fontUsage.text;
191
-
192
- const bounds = variationAxisBoundsCache.get(fontUrl);
193
- const subsetInfo = bounds
194
- ? {
195
- variationAxes: bounds.variationAxes,
196
- fullyInstanced: bounds.fullyInstanced,
197
- numAxesPinned: bounds.numAxesPinned,
198
- numAxesReduced: bounds.numAxesReduced,
199
- }
200
- : {
201
- variationAxes: undefined,
202
- fullyInstanced: false,
203
- numAxesPinned: 0,
204
- numAxesReduced: 0,
205
- };
206
- subsetInfoByFontUrl.set(fontUrl, subsetInfo);
207
-
208
- let featureGlyphIds;
209
- if (fontUsage.hasFontFeatureSettings && fontBuffer) {
210
- try {
211
- featureGlyphIds = await collectFeatureGlyphIds(
212
- fontBuffer,
213
- text,
214
- fontUsage.fontFeatureTags
215
- );
216
- } catch (err) {
217
- // Feature glyph collection failed — continue without feature
218
- // glyphs rather than blocking all fonts (Promise.all would
219
- // reject entirely if this propagated).
220
- err.asset = err.asset || fontAssetsByUrl.get(fontUrl);
221
- assetGraph.warn(err);
149
+ async function getSubsetsForFontUsage(assetGraph, htmlOrSvgAssetTextsWithProps, formats, seenAxisValuesByFontUrlAndAxisName, cacheDir = null, console = null, debug = false) {
150
+ const diskCache = cacheDir ? new SubsetDiskCache(cacheDir, console) : null;
151
+ const cacheStats = diskCache ? { hits: 0, misses: 0 } : null;
152
+ // Collect one canonical fontUsage per font URL
153
+ const canonicalFontUsageByUrl = new Map();
154
+ for (const item of htmlOrSvgAssetTextsWithProps) {
155
+ for (const fontUsage of item.fontUsages) {
156
+ if (fontUsage.fontUrl &&
157
+ !canonicalFontUsageByUrl.has(fontUsage.fontUrl)) {
158
+ canonicalFontUsageByUrl.set(fontUsage.fontUrl, fontUsage);
159
+ }
222
160
  }
223
- }
224
-
225
- for (const targetFormat of formats) {
226
- const promiseId = getSubsetPromiseId(
227
- fontUsage,
228
- targetFormat,
229
- subsetInfo.variationAxes
230
- );
231
-
232
- if (!subsetPromiseMap.has(promiseId)) {
233
- const cacheKey = diskCache
234
- ? subsetCacheKey(
235
- fontBuffer,
236
- text,
237
- targetFormat,
238
- subsetInfo.variationAxes,
239
- featureGlyphIds
240
- )
241
- : null;
242
- const cachedResult = diskCache ? await diskCache.get(cacheKey) : null;
243
-
244
- if (cachedResult) {
245
- if (cacheStats) cacheStats.hits++;
246
- subsetPromiseMap.set(promiseId, Promise.resolve(cachedResult));
247
- } else {
248
- if (cacheStats) cacheStats.misses++;
249
- const subsetCall = subsetFontWithGlyphs(fontBuffer, text, {
250
- targetFormat,
251
- glyphIds: featureGlyphIds,
252
- variationAxes: subsetInfo.variationAxes,
253
- });
254
-
255
- subsetPromiseMap.set(
256
- promiseId,
257
- subsetCall
258
- .then(async (result) => {
259
- if (diskCache && result) {
260
- // Fire-and-forget: cache writes are best-effort.
261
- // Errors are handled inside set(); the catch is a
262
- // safety net against unhandled rejections.
263
- diskCache.set(cacheKey, result).catch(() => {});
264
- }
265
- return result;
266
- })
267
- .catch((err) => {
268
- err.asset = err.asset || fontAssetsByUrl.get(fontUrl);
269
- assetGraph.warn(err);
270
- return null;
271
- })
272
- );
273
- }
161
+ }
162
+ const allFontUrls = [...canonicalFontUsageByUrl.keys()];
163
+ // Load font assets
164
+ await assetGraph.populate({
165
+ followRelations: {
166
+ to: { url: { $or: allFontUrls } },
167
+ },
168
+ });
169
+ const fontAssetsByUrl = new Map();
170
+ const originalFontBuffers = new Map();
171
+ for (const fontUrl of allFontUrls) {
172
+ const fontAsset = assetGraph.findAssets({
173
+ url: fontUrl,
174
+ isLoaded: true,
175
+ })[0];
176
+ if (fontAsset) {
177
+ fontAssetsByUrl.set(fontUrl, fontAsset);
178
+ originalFontBuffers.set(fontUrl, fontAsset.rawSrc);
274
179
  }
275
- }
276
- })
277
- );
278
-
279
- // Await all subset promises
280
- const resolvedSubsets = new Map(
281
- await Promise.all(
282
- [...subsetPromiseMap].map(async ([key, promise]) => [key, await promise])
283
- )
284
- );
285
-
286
- if (cacheStats && debug && console) {
287
- const total = cacheStats.hits + cacheStats.misses;
288
- const pct = total > 0 ? Math.round((cacheStats.hits * 100) / total) : 0;
289
- console.log(
290
- `[subfont timing] subset disk cache: ${cacheStats.hits} hit${cacheStats.hits === 1 ? '' : 's'}, ${cacheStats.misses} miss${cacheStats.misses === 1 ? '' : 'es'} (${pct}% hit rate)`
291
- );
292
- }
293
-
294
- // Assign subset results to canonical font usages
295
- for (const [, fontUsage] of canonicalFontUsageByUrl) {
296
- const info = subsetInfoByFontUrl.get(fontUsage.fontUrl);
297
- for (const targetFormat of formats) {
298
- const promiseId = getSubsetPromiseId(
299
- fontUsage,
300
- targetFormat,
301
- info.variationAxes
302
- );
303
- const subsetBuffer = resolvedSubsets.get(promiseId);
304
- if (subsetBuffer) {
305
- if (!fontUsage.subsets) {
306
- fontUsage.subsets = {};
180
+ }
181
+ // Compute variation axis bounds for all fonts in parallel
182
+ const fontUrlsWithAssets = allFontUrls.filter((url) => fontAssetsByUrl.has(url));
183
+ const boundsResults = await Promise.all(fontUrlsWithAssets.map((fontUrl) => (0, variationAxes_1.getVariationAxisBounds)(fontAssetsByUrl, fontUrl, seenAxisValuesByFontUrlAndAxisName)));
184
+ const variationAxisBoundsCache = new Map();
185
+ for (let i = 0; i < fontUrlsWithAssets.length; i++) {
186
+ variationAxisBoundsCache.set(fontUrlsWithAssets[i], boundsResults[i]);
187
+ }
188
+ const subsetPromiseMap = new Map();
189
+ const subsetInfoByFontUrl = new Map();
190
+ // Process fonts concurrently — each font's feature glyph collection
191
+ // and subset queuing run in parallel, so fonts without feature settings
192
+ // don't wait behind fonts that need collectFeatureGlyphIds.
193
+ await Promise.all([...canonicalFontUsageByUrl].map(async ([fontUrl, fontUsage]) => {
194
+ const fontBuffer = originalFontBuffers.get(fontUrl);
195
+ if (!fontBuffer)
196
+ return;
197
+ const text = fontUsage.text;
198
+ const bounds = variationAxisBoundsCache.get(fontUrl);
199
+ const subsetInfo = bounds
200
+ ? {
201
+ variationAxes: bounds.variationAxes,
202
+ fullyInstanced: bounds.fullyInstanced,
203
+ numAxesPinned: bounds.numAxesPinned,
204
+ numAxesReduced: bounds.numAxesReduced,
205
+ }
206
+ : {
207
+ variationAxes: undefined,
208
+ fullyInstanced: false,
209
+ numAxesPinned: 0,
210
+ numAxesReduced: 0,
211
+ };
212
+ subsetInfoByFontUrl.set(fontUrl, subsetInfo);
213
+ let featureGlyphIds;
214
+ if (fontUsage.hasFontFeatureSettings && fontBuffer) {
215
+ try {
216
+ featureGlyphIds = await collectFeatureGlyphIds(fontBuffer, text, fontUsage.fontFeatureTags);
217
+ }
218
+ catch (rawErr) {
219
+ // Feature glyph collection failed — continue without feature
220
+ // glyphs rather than blocking all fonts (Promise.all would
221
+ // reject entirely if this propagated).
222
+ const err = rawErr;
223
+ err.asset = err.asset || fontAssetsByUrl.get(fontUrl);
224
+ assetGraph.warn(err);
225
+ }
307
226
  }
308
- fontUsage.subsets[targetFormat] = subsetBuffer;
309
- const size = subsetBuffer.length;
310
- if (
311
- !fontUsage.smallestSubsetSize ||
312
- size < fontUsage.smallestSubsetSize
313
- ) {
314
- fontUsage.smallestSubsetSize = size;
315
- fontUsage.smallestSubsetFormat = targetFormat;
316
- fontUsage.variationAxes = info.variationAxes;
317
- fontUsage.fullyInstanced = info.fullyInstanced;
318
- fontUsage.numAxesPinned = info.numAxesPinned;
319
- fontUsage.numAxesReduced = info.numAxesReduced;
227
+ for (const targetFormat of formats) {
228
+ const promiseId = getSubsetPromiseId(fontUsage, targetFormat, subsetInfo.variationAxes);
229
+ if (!subsetPromiseMap.has(promiseId)) {
230
+ const cacheKey = diskCache
231
+ ? subsetCacheKey(fontBuffer, text, targetFormat, subsetInfo.variationAxes, featureGlyphIds)
232
+ : null;
233
+ const cachedResult = diskCache && cacheKey ? await diskCache.get(cacheKey) : null;
234
+ if (cachedResult) {
235
+ if (cacheStats)
236
+ cacheStats.hits++;
237
+ subsetPromiseMap.set(promiseId, Promise.resolve(cachedResult));
238
+ }
239
+ else {
240
+ if (cacheStats)
241
+ cacheStats.misses++;
242
+ const subsetCall = subsetFontWithGlyphs(fontBuffer, text, {
243
+ targetFormat,
244
+ glyphIds: featureGlyphIds,
245
+ variationAxes: subsetInfo.variationAxes,
246
+ });
247
+ subsetPromiseMap.set(promiseId, subsetCall
248
+ .then(async (result) => {
249
+ if (diskCache && result && cacheKey) {
250
+ // Fire-and-forget: cache writes are best-effort.
251
+ // Errors are handled inside set(); the catch is a
252
+ // safety net against unhandled rejections.
253
+ diskCache.set(cacheKey, result).catch(() => { });
254
+ }
255
+ return result;
256
+ })
257
+ .catch((rawErr) => {
258
+ const err = rawErr;
259
+ err.asset = err.asset || fontAssetsByUrl.get(fontUrl);
260
+ assetGraph.warn(err);
261
+ return null;
262
+ }));
263
+ }
264
+ }
320
265
  }
321
- }
266
+ }));
267
+ // Await all subset promises
268
+ const resolvedSubsets = new Map(await Promise.all([...subsetPromiseMap].map(async ([key, promise]) => [key, await promise])));
269
+ if (cacheStats && debug && console) {
270
+ const total = cacheStats.hits + cacheStats.misses;
271
+ const pct = total > 0 ? Math.round((cacheStats.hits * 100) / total) : 0;
272
+ console.log(`[subfont timing] subset disk cache: ${cacheStats.hits} hit${cacheStats.hits === 1 ? '' : 's'}, ${cacheStats.misses} miss${cacheStats.misses === 1 ? '' : 'es'} (${pct}% hit rate)`);
322
273
  }
323
- }
324
-
325
- // Propagate subsets to non-canonical font usages
326
- for (const item of htmlOrSvgAssetTextsWithProps) {
327
- for (const fontUsage of item.fontUsages) {
328
- if (!fontUsage.fontUrl) continue;
329
- const canonical = canonicalFontUsageByUrl.get(fontUsage.fontUrl);
330
- if (canonical && canonical !== fontUsage && canonical.subsets) {
274
+ // Assign subset results to canonical font usages
275
+ for (const [, fontUsage] of canonicalFontUsageByUrl) {
331
276
  const info = subsetInfoByFontUrl.get(fontUsage.fontUrl);
332
- fontUsage.subsets = canonical.subsets;
333
- fontUsage.smallestSubsetSize = canonical.smallestSubsetSize;
334
- fontUsage.smallestSubsetFormat = canonical.smallestSubsetFormat;
335
- fontUsage.variationAxes = info.variationAxes;
336
- fontUsage.fullyInstanced = info.fullyInstanced;
337
- fontUsage.numAxesPinned = info.numAxesPinned;
338
- fontUsage.numAxesReduced = info.numAxesReduced;
339
- }
277
+ if (!info)
278
+ continue;
279
+ for (const targetFormat of formats) {
280
+ const promiseId = getSubsetPromiseId(fontUsage, targetFormat, info.variationAxes);
281
+ const subsetBuffer = resolvedSubsets.get(promiseId);
282
+ if (subsetBuffer) {
283
+ if (!fontUsage.subsets) {
284
+ fontUsage.subsets = {};
285
+ }
286
+ fontUsage.subsets[targetFormat] = subsetBuffer;
287
+ const size = subsetBuffer.length;
288
+ if (!fontUsage.smallestSubsetSize ||
289
+ size < fontUsage.smallestSubsetSize) {
290
+ fontUsage.smallestSubsetSize = size;
291
+ fontUsage.smallestSubsetFormat = targetFormat;
292
+ fontUsage.variationAxes = info.variationAxes;
293
+ fontUsage.fullyInstanced = info.fullyInstanced;
294
+ fontUsage.numAxesPinned = info.numAxesPinned;
295
+ fontUsage.numAxesReduced = info.numAxesReduced;
296
+ }
297
+ }
298
+ }
299
+ }
300
+ // Propagate subsets to non-canonical font usages
301
+ for (const item of htmlOrSvgAssetTextsWithProps) {
302
+ for (const fontUsage of item.fontUsages) {
303
+ if (!fontUsage.fontUrl)
304
+ continue;
305
+ const canonical = canonicalFontUsageByUrl.get(fontUsage.fontUrl);
306
+ if (canonical && canonical !== fontUsage && canonical.subsets) {
307
+ const info = subsetInfoByFontUrl.get(fontUsage.fontUrl);
308
+ if (!info)
309
+ continue;
310
+ fontUsage.subsets = canonical.subsets;
311
+ fontUsage.smallestSubsetSize = canonical.smallestSubsetSize;
312
+ fontUsage.smallestSubsetFormat = canonical.smallestSubsetFormat;
313
+ fontUsage.variationAxes = info.variationAxes;
314
+ fontUsage.fullyInstanced = info.fullyInstanced;
315
+ fontUsage.numAxesPinned = info.numAxesPinned;
316
+ fontUsage.numAxesReduced = info.numAxesReduced;
317
+ }
318
+ }
340
319
  }
341
- }
342
-
343
- return fontAssetsByUrl;
320
+ return fontAssetsByUrl;
344
321
  }
345
-
346
- module.exports = {
347
- getSubsetPromiseId,
348
- getSubsetsForFontUsage,
349
- // Exported for testing
350
- _subsetCacheKey: subsetCacheKey,
351
- _SubsetDiskCache: SubsetDiskCache,
352
- };
322
+ //# sourceMappingURL=subsetGeneration.js.map