@fontmin-rs/wasm 1.0.2-rc.1 → 1.1.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.
Binary file
package/dist/index.d.mts CHANGED
@@ -50,17 +50,129 @@ interface FontInfo {
50
50
  metadata: FontMetadata;
51
51
  size: number;
52
52
  }
53
+ interface FontCollectionFaceInfo extends FontInfo {
54
+ capabilities: FontCapabilityReport;
55
+ /** Zero-based face index used by extractCollectionFace. */
56
+ index: number;
57
+ /** Absolute SFNT directory offset in the collection. */
58
+ offset: number;
59
+ }
60
+ type CapabilitySupport = 'subset' | 'passthrough' | 'unsupported';
61
+ type ColorFontTechnology = 'colr-cpal' | 'cbdt-cblc' | 'sbix' | 'svg';
62
+ interface ColorFontTechnologyCapability {
63
+ detail: string;
64
+ subsetSupport: CapabilitySupport;
65
+ tables: string[];
66
+ technology: ColorFontTechnology;
67
+ version?: number;
68
+ }
69
+ interface ColorFontCapabilityReport {
70
+ isColorFont: boolean;
71
+ subsetSupport?: CapabilitySupport;
72
+ technologies: ColorFontTechnologyCapability[];
73
+ }
74
+ interface FontCapabilityReport {
75
+ color: ColorFontCapabilityReport;
76
+ format: FontFormat;
77
+ }
78
+ interface FontCollectionInfo {
79
+ faces: FontCollectionFaceInfo[];
80
+ majorVersion: number;
81
+ minorVersion: number;
82
+ size: number;
83
+ }
53
84
  interface SubsetOptions extends CoverageOptions {
85
+ /** Original glyph IDs to retain in addition to Unicode selection. */
86
+ gids?: number[];
87
+ /** PostScript glyph names to retain in addition to other selectors. */
88
+ glyphNames?: string[];
54
89
  /** Retain the original glyph-zero outline; false emits the required empty glyph-zero slot. */
55
90
  keepNotdef?: boolean;
91
+ /** Preserve original glyph IDs and leave null entries for empty mapping slots. */
92
+ retainGids?: boolean;
93
+ /** Retain PostScript glyph names in a rewritten version 2 `post` table. */
94
+ retainGlyphNames?: boolean;
95
+ /** Retain non-Unicode, non-symbol cmap records after remapping. */
96
+ retainLegacyCmap?: boolean;
97
+ /** Retain the Windows symbol cmap record after remapping. */
98
+ retainSymbolCmap?: boolean;
56
99
  /** Drop layout, remap supported data, or reject known contextual loss. */
57
100
  layout?: LayoutSubsetMode;
101
+ /** Four-byte OpenType feature tags to retain, or all features when omitted. */
102
+ layoutFeatures?: string[];
103
+ /** Four-byte OpenType script tags to retain, or all scripts when omitted. */
104
+ layoutScripts?: string[];
105
+ /** OpenType language tags to retain; `default` selects DefaultLangSys. */
106
+ layoutLanguages?: string[];
107
+ /** OpenType name IDs to retain, or all name IDs when omitted. */
108
+ nameIds?: number[];
109
+ /** Platform-specific name language IDs to retain, or all languages when omitted. */
110
+ nameLanguages?: number[];
111
+ /** Optional OpenType tables to remove after subsetting. */
112
+ dropTables?: string[];
113
+ /** Optional source tables to copy verbatim into the subset. */
114
+ passThroughTables?: string[];
58
115
  missingGlyphs?: MissingGlyphPolicy;
59
116
  /** Retain the cvt, fpgm, and prep TrueType program tables while trimming. */
60
117
  preserveHinting?: boolean;
61
118
  /** Skip subsetting and return the validated source bytes unchanged when false. */
62
119
  trim?: boolean;
63
120
  }
121
+ interface GidMapping {
122
+ newGid: number;
123
+ oldGid: number;
124
+ }
125
+ interface UnicodeGidMapping {
126
+ oldGid: number;
127
+ unicode: number;
128
+ }
129
+ interface GlyphNameGidMapping {
130
+ glyphName: string;
131
+ oldGid: number;
132
+ }
133
+ interface SubsetReport {
134
+ cffCharstringsVerbatim: boolean;
135
+ droppedContextSubtables: number;
136
+ glyphsRetained: number;
137
+ glyphNameToOldGid: GlyphNameGidMapping[];
138
+ missingGids: number[];
139
+ missingGlyphNames: string[];
140
+ newToOld: (number | null)[];
141
+ oldToNew: GidMapping[];
142
+ originalSize: number;
143
+ requestedGids: number[];
144
+ requestedGlyphNames: string[];
145
+ subsetSize: number;
146
+ supportedGids: number[];
147
+ supportedGlyphNames: string[];
148
+ tablesRetained: string[];
149
+ unicodeToOldGid: UnicodeGidMapping[];
150
+ }
151
+ interface SubsetResult {
152
+ data: Uint8Array;
153
+ report: SubsetReport;
154
+ }
155
+ /** Resolved selectors bound to one source font by SHA-256. */
156
+ interface SubsetPlan {
157
+ coverage: CoverageReport;
158
+ glyphNameToOldGid: GlyphNameGidMapping[];
159
+ missingGids: number[];
160
+ missingGlyphNames: string[];
161
+ options: SubsetOptions;
162
+ /** SHA-256 integrity digest of the canonical plan payload. */
163
+ planSha256: string;
164
+ requestedGids: number[];
165
+ requestedGlyphNames: string[];
166
+ schemaVersion: number;
167
+ /** Original glyph IDs used to seed glyph and layout closure. */
168
+ seedGids: number[];
169
+ sourceGlyphs: number;
170
+ sourceSha256: string;
171
+ sourceSize: number;
172
+ supportedGids: number[];
173
+ supportedGlyphNames: string[];
174
+ unicodeToOldGid: UnicodeGidMapping[];
175
+ }
64
176
  interface WoffOptions {
65
177
  compressionLevel?: number;
66
178
  deflate?: boolean;
@@ -81,6 +193,25 @@ interface Otf2TtfOptions {
81
193
  preserveHinting?: boolean;
82
194
  variationCoordinates?: Record<string, number>;
83
195
  }
196
+ interface InstanceOptions {
197
+ /** Axis values in fvar user units. Unspecified axes use their defaults. */
198
+ variationCoordinates?: Record<string, number>;
199
+ }
200
+ interface AxisRange {
201
+ /** Inclusive lower bound in fvar user units. */
202
+ min: number;
203
+ /** Inclusive upper bound in fvar user units. */
204
+ max: number;
205
+ /** New default, or the original default clamped into the range when omitted. */
206
+ default?: number;
207
+ }
208
+ type AxisSetting = number | AxisRange;
209
+ interface VariationSpaceOptions {
210
+ /** Axis tags mapped to a pin or retained range. Unlisted axes stay variable. */
211
+ axes: Record<string, AxisSetting>;
212
+ /** Convert fully pinned CFF2 outlines to CFF1 for older renderers. */
213
+ downgradeCff2?: boolean;
214
+ }
84
215
  interface Svg2TtfOptions {
85
216
  /** Compatibility option accepted without generating TrueType hint instructions. */
86
217
  hinting?: boolean;
@@ -124,7 +255,12 @@ interface CssOptions {
124
255
  //#endregion
125
256
  //#region src/native.d.ts
126
257
  declare function subsetTtf(input: Uint8Array, options?: SubsetOptions): Promise<Uint8Array>;
258
+ declare function subsetTtfWithReport(input: Uint8Array, options?: SubsetOptions): Promise<SubsetResult>;
259
+ declare function createTtfSubsetPlan(input: Uint8Array, options?: SubsetOptions): Promise<SubsetPlan>;
260
+ declare function subsetTtfWithPlan(input: Uint8Array, plan: SubsetPlan): Promise<SubsetResult>;
127
261
  declare function analyzeCoverage(input: Uint8Array, options?: CoverageOptions): Promise<CoverageReport>;
262
+ declare function instantiateFont(input: Uint8Array, options?: InstanceOptions): Promise<Uint8Array>;
263
+ declare function reduceVariationSpace(input: Uint8Array, options: VariationSpaceOptions): Promise<Uint8Array>;
128
264
  declare function ttfToWoff(input: Uint8Array, options?: WoffOptions): Promise<Uint8Array>;
129
265
  declare function woffToTtf(input: Uint8Array): Promise<Uint8Array>;
130
266
  declare function ttfToWoff2(input: Uint8Array, options?: Ttf2Woff2Options): Promise<Uint8Array>;
@@ -137,8 +273,49 @@ declare function svgFontToTtf(input: string, options?: Svg2TtfOptions): Promise<
137
273
  declare function svgsToTtf(inputs: SvgIcon[], options?: Svgs2TtfOptions): Promise<Uint8Array>;
138
274
  declare function otfToTtf(input: Uint8Array, options?: Otf2TtfOptions): Promise<Uint8Array>;
139
275
  declare function inspect(input: Uint8Array): Promise<FontInfo>;
276
+ declare function inspectCapabilities(input: Uint8Array): Promise<FontCapabilityReport>;
277
+ declare function inspectCollection(input: Uint8Array): Promise<FontCollectionInfo>;
278
+ declare function extractCollectionFace(input: Uint8Array, faceIndex: number): Promise<Uint8Array>;
140
279
  declare function generateFontFaceCss(sources: CssFontSource[], options?: CssOptions): Promise<string>;
141
280
  //#endregion
281
+ //#region ../../packages/fontmin/src/runtime-neutral/auto-delivery.d.ts
282
+ type DeliveryLanguagePreset = 'ar' | 'el' | 'en' | 'hi' | 'ja' | 'ko' | 'ru' | 'zh-Hans' | 'zh-Hant';
283
+ interface AutoDeliveryPlanOptions {
284
+ frequencyText?: string;
285
+ languages?: DeliveryLanguagePreset[];
286
+ maxSlices?: number;
287
+ targetBytes?: number;
288
+ tolerance?: number;
289
+ }
290
+ interface AutoDeliveryPlanSlice {
291
+ codePoints: number[];
292
+ estimatedBytes: number;
293
+ name: string;
294
+ unicodeRanges: string[];
295
+ }
296
+ interface AutoDeliveryPlan {
297
+ codePointCount: number;
298
+ languages: DeliveryLanguagePreset[];
299
+ slices: AutoDeliveryPlanSlice[];
300
+ targetBytes: number;
301
+ tolerance: number;
302
+ }
303
+ /**
304
+ * Detect supported delivery-language presets from representative page text.
305
+ *
306
+ * @param text - Static or frequency-weighted business text.
307
+ * @returns Presets in first-observed order, with Han assigned to a detected
308
+ * Japanese, Korean, or Traditional Chinese context before Simplified Chinese.
309
+ */
310
+ declare function detectDeliveryLanguages(text: string): DeliveryLanguagePreset[];
311
+ /**
312
+ * Compact code points into canonical CSS Unicode ranges.
313
+ *
314
+ * @param codePoints - Unicode scalar values in any order.
315
+ * @returns Sorted `U+XXXX[-YYYY]` ranges.
316
+ */
317
+ declare function unicodeRangesFromCodePoints(codePoints: readonly number[]): string[];
318
+ //#endregion
142
319
  //#region src/plugins.d.ts
143
320
  type MaybePromise<T> = T | Promise<T>;
144
321
  interface DeliverySlice {
@@ -148,6 +325,12 @@ interface DeliverySlice {
148
325
  interface DeliverySlicesOptions {
149
326
  slices: DeliverySlice[];
150
327
  }
328
+ interface AutoDeliveryPluginOptions extends AutoDeliveryPlanOptions {
329
+ measureFormat?: 'ttf' | 'woff' | 'woff2';
330
+ subset?: SubsetOptions;
331
+ woff2Quality?: number;
332
+ woffCompressionLevel?: number;
333
+ }
151
334
  interface GlyphOptions extends SubsetOptions {
152
335
  clone?: boolean;
153
336
  }
@@ -166,6 +349,9 @@ interface Ttf2SvgPluginOptions extends Ttf2SvgOptions {
166
349
  interface Otf2TtfPluginOptions extends Otf2TtfOptions {
167
350
  clone?: boolean;
168
351
  }
352
+ interface VariationSpacePluginOptions extends VariationSpaceOptions {
353
+ clone?: boolean;
354
+ }
169
355
  interface Svg2TtfPluginOptions extends Svg2TtfOptions {
170
356
  clone?: boolean;
171
357
  }
@@ -177,6 +363,8 @@ interface ModernWebOptions extends GlyphOptions, Otf2TtfPluginOptions, Ttf2WoffP
177
363
  fontFamily?: string;
178
364
  fontPath?: string;
179
365
  local?: boolean;
366
+ variationAxes?: VariationSpaceOptions['axes'];
367
+ downgradeCff2?: boolean;
180
368
  }
181
369
  interface FontminCompatPresetOptions extends GlyphOptions, Otf2TtfPluginOptions, Ttf2EotPluginOptions, Ttf2SvgPluginOptions, Ttf2WoffPluginOptions, Ttf2Woff2PluginOptions {
182
370
  fontDisplay?: CssOptions['fontDisplay'];
@@ -198,11 +386,13 @@ interface BrowserPlugin<Options extends object = object> {
198
386
  }
199
387
  declare function glyph(options?: GlyphOptions): BrowserPlugin<GlyphOptions>;
200
388
  declare function deliverySlices(slices: DeliverySlice[]): BrowserPlugin<DeliverySlicesOptions>;
389
+ declare function autoDeliverySlices(options?: AutoDeliveryPluginOptions): BrowserPlugin<AutoDeliveryPluginOptions>;
201
390
  declare function ttf2woff(options?: Ttf2WoffPluginOptions): BrowserPlugin<Ttf2WoffPluginOptions>;
202
391
  declare function ttf2woff2(options?: Ttf2Woff2PluginOptions): BrowserPlugin<Ttf2Woff2PluginOptions>;
203
392
  declare function ttf2eot(options?: Ttf2EotPluginOptions): BrowserPlugin<Ttf2EotPluginOptions>;
204
393
  declare function ttf2svg(options?: Ttf2SvgPluginOptions): BrowserPlugin<Ttf2SvgPluginOptions>;
205
394
  declare function otf2ttf(options?: Otf2TtfPluginOptions): BrowserPlugin<Otf2TtfPluginOptions>;
395
+ declare function variationSpace(options: VariationSpacePluginOptions): BrowserPlugin<VariationSpacePluginOptions>;
206
396
  declare function svg2ttf(options?: Svg2TtfPluginOptions): BrowserPlugin<Svg2TtfPluginOptions>;
207
397
  declare function svgs2ttf(options?: Svgs2TtfPluginOptions): BrowserPlugin<Svgs2TtfPluginOptions>;
208
398
  declare function css(options?: CssOptions): BrowserPlugin<CssOptions>;
@@ -222,4 +412,4 @@ interface BrowserOptimizeConfig {
222
412
  }
223
413
  declare function optimizeBrowser(config: BrowserOptimizeConfig): Promise<BrowserAsset[]>;
224
414
  //#endregion
225
- export { type BrowserAsset, type BrowserOptimizeConfig, type BrowserPlugin, type BrowserPluginContext, type CoverageOptions, type CoverageReport, type CssFontSource, type CssGlyph, type CssOptions, type DeliverySlice, type DeliverySlicesOptions, type FontFormat, type FontInfo, type FontMetadata, type FontminCompatPresetOptions, type FontminDiagnosticCode, FontminDiagnosticError, type GlyphOptions, type LayoutSubsetMode, type MaybePromise, type MissingGlyphPolicy, type ModernWebOptions, type Otf2TtfOptions, type Otf2TtfPluginOptions, type OutputFormat, type SubsetOptions, type Svg2TtfOptions, type Svg2TtfPluginOptions, type SvgIcon, type Svgs2TtfOptions, type Svgs2TtfPluginOptions, type Ttf2EotOptions, type Ttf2EotPluginOptions, type Ttf2SvgOptions, type Ttf2SvgPluginOptions, type Ttf2Woff2Options, type Ttf2Woff2PluginOptions, type Ttf2WoffPluginOptions, type WoffOptions, analyzeCoverage, css, deliverySlices, eotToTtf, fontminCompatPreset, generateFontFaceCss, glyph, initWasm, inspect, isWasmInitialized, modernWeb, optimizeBrowser, otf2ttf, otfToTtf, subsetTtf, svg2ttf, svgFontToTtf, svgs2ttf, svgsToTtf, ttf2eot, ttf2svg, ttf2woff, ttf2woff2, ttfToEot, ttfToSvg, ttfToWoff, ttfToWoff2, validateWoff2, woff2ToTtf, woffToTtf };
415
+ export { type AutoDeliveryPlan, type AutoDeliveryPlanOptions, type AutoDeliveryPlanSlice, type AutoDeliveryPluginOptions, type AxisRange, type AxisSetting, type BrowserAsset, type BrowserOptimizeConfig, type BrowserPlugin, type BrowserPluginContext, type CapabilitySupport, type ColorFontCapabilityReport, type ColorFontTechnology, type ColorFontTechnologyCapability, type CoverageOptions, type CoverageReport, type CssFontSource, type CssGlyph, type CssOptions, type DeliveryLanguagePreset, type DeliverySlice, type DeliverySlicesOptions, type FontCapabilityReport, type FontCollectionFaceInfo, type FontCollectionInfo, type FontFormat, type FontInfo, type FontMetadata, type FontminCompatPresetOptions, type FontminDiagnosticCode, FontminDiagnosticError, type GidMapping, type GlyphNameGidMapping, type GlyphOptions, type InstanceOptions, type LayoutSubsetMode, type MaybePromise, type MissingGlyphPolicy, type ModernWebOptions, type Otf2TtfOptions, type Otf2TtfPluginOptions, type OutputFormat, type SubsetOptions, type SubsetPlan, type SubsetReport, type SubsetResult, type Svg2TtfOptions, type Svg2TtfPluginOptions, type SvgIcon, type Svgs2TtfOptions, type Svgs2TtfPluginOptions, type Ttf2EotOptions, type Ttf2EotPluginOptions, type Ttf2SvgOptions, type Ttf2SvgPluginOptions, type Ttf2Woff2Options, type Ttf2Woff2PluginOptions, type Ttf2WoffPluginOptions, type UnicodeGidMapping, type VariationSpaceOptions, type VariationSpacePluginOptions, type WoffOptions, analyzeCoverage, autoDeliverySlices, createTtfSubsetPlan, css, deliverySlices, detectDeliveryLanguages, eotToTtf, extractCollectionFace, fontminCompatPreset, generateFontFaceCss, glyph, initWasm, inspect, inspectCapabilities, inspectCollection, instantiateFont, isWasmInitialized, modernWeb, optimizeBrowser, otf2ttf, otfToTtf, reduceVariationSpace, subsetTtf, subsetTtfWithPlan, subsetTtfWithReport, svg2ttf, svgFontToTtf, svgs2ttf, svgsToTtf, ttf2eot, ttf2svg, ttf2woff, ttf2woff2, ttfToEot, ttfToSvg, ttfToWoff, ttfToWoff2, unicodeRangesFromCodePoints, validateWoff2, variationSpace, woff2ToTtf, woffToTtf };
package/dist/index.mjs CHANGED
@@ -174,16 +174,51 @@ async function binary(operation, input, options = {}) {
174
174
  return withFontminDiagnostics(() => bytes(wasm.transform(operation, input, options)));
175
175
  }
176
176
  async function subsetTtf(input, options = {}) {
177
- if ((options.missingGlyphs ?? "warn") === "warn") {
177
+ if ((options.missingGlyphs ?? "warn") === "warn" && hasUnicodeSelection(options)) {
178
178
  const warning = missingGlyphWarning(await analyzeCoverage(input, coverageOptions(options)));
179
179
  if (warning !== void 0) console.warn(warning);
180
180
  }
181
181
  return binary("subsetTtf", input, options);
182
182
  }
183
+ async function subsetTtfWithReport(input, options = {}) {
184
+ const wasm = await getWasmModule();
185
+ const result = withFontminDiagnostics(() => wasm.transform("subsetTtfWithReport", input, options));
186
+ return {
187
+ data: bytes(result.data),
188
+ report: {
189
+ ...result.report,
190
+ newToOld: result.report.newToOld.map((gid) => gid ?? null)
191
+ }
192
+ };
193
+ }
194
+ async function createTtfSubsetPlan(input, options = {}) {
195
+ const wasm = await getWasmModule();
196
+ return withFontminDiagnostics(() => wasm.transform("createTtfSubsetPlan", input, options));
197
+ }
198
+ async function subsetTtfWithPlan(input, plan) {
199
+ const wasm = await getWasmModule();
200
+ const result = withFontminDiagnostics(() => wasm.transform("subsetTtfWithPlan", input, plan));
201
+ return {
202
+ data: bytes(result.data),
203
+ report: {
204
+ ...result.report,
205
+ newToOld: result.report.newToOld.map((gid) => gid ?? null)
206
+ }
207
+ };
208
+ }
209
+ function hasUnicodeSelection(options) {
210
+ return options.basicText === true || (options.text?.length ?? 0) > 0 || (options.unicodes?.length ?? 0) > 0 || (options.unicodeRanges?.length ?? 0) > 0;
211
+ }
183
212
  async function analyzeCoverage(input, options = {}) {
184
213
  const wasm = await getWasmModule();
185
214
  return withFontminDiagnostics(() => wasm.transform("analyzeCoverage", input, options));
186
215
  }
216
+ async function instantiateFont(input, options = {}) {
217
+ return binary("instantiateFont", input, options);
218
+ }
219
+ async function reduceVariationSpace(input, options) {
220
+ return binary("reduceVariationSpace", input, options);
221
+ }
187
222
  async function ttfToWoff(input, options = {}) {
188
223
  return binary("ttfToWoff", input, options);
189
224
  }
@@ -225,6 +260,25 @@ async function inspect(input) {
225
260
  const wasm = await getWasmModule();
226
261
  return withFontminDiagnostics(() => wasm.transform("inspect", input, {}));
227
262
  }
263
+ async function inspectCapabilities(input) {
264
+ const wasm = await getWasmModule();
265
+ const report = withFontminDiagnostics(() => wasm.transform("inspectCapabilities", input, {}));
266
+ if (report.color.subsetSupport === void 0) {
267
+ const { subsetSupport: _subsetSupport, ...color } = report.color;
268
+ return {
269
+ ...report,
270
+ color
271
+ };
272
+ }
273
+ return report;
274
+ }
275
+ async function inspectCollection(input) {
276
+ const wasm = await getWasmModule();
277
+ return withFontminDiagnostics(() => wasm.transform("inspectCollection", input, {}));
278
+ }
279
+ async function extractCollectionFace(input, faceIndex) {
280
+ return binary("extractCollectionFace", input, { faceIndex });
281
+ }
228
282
  async function generateFontFaceCss(sources, options = {}) {
229
283
  const wasm = await getWasmModule();
230
284
  return withFontminDiagnostics(() => wasm.generate_css(sources, options));
@@ -238,6 +292,418 @@ function coverageOptions(options) {
238
292
  return coverage;
239
293
  }
240
294
  //#endregion
295
+ //#region ../../packages/fontmin/src/runtime-neutral/auto-delivery.ts
296
+ const DEFAULT_TARGET_BYTES = 100 * 1024;
297
+ const DEFAULT_TOLERANCE = .15;
298
+ const DEFAULT_MAX_SLICES = 32;
299
+ const GROUPS = {
300
+ arabic: ranges([
301
+ [1536, 1791],
302
+ [1872, 1919],
303
+ [2160, 2207],
304
+ [2208, 2303],
305
+ [64336, 65023],
306
+ [65136, 65279]
307
+ ]),
308
+ bopomofo: ranges([[12544, 12591], [12704, 12735]]),
309
+ cyrillic: ranges([
310
+ [1024, 1327],
311
+ [7296, 7311],
312
+ [11744, 11775],
313
+ [42560, 42655]
314
+ ]),
315
+ devanagari: ranges([[2304, 2431], [43232, 43263]]),
316
+ greek: ranges([[880, 1023], [7936, 8191]]),
317
+ han: ranges([
318
+ [11904, 12255],
319
+ [13312, 19903],
320
+ [19968, 40959],
321
+ [63744, 64255],
322
+ [131072, 191471],
323
+ [196608, 205743]
324
+ ]),
325
+ hangul: ranges([
326
+ [4352, 4607],
327
+ [12592, 12687],
328
+ [43360, 43391],
329
+ [44032, 55215],
330
+ [55216, 55295]
331
+ ]),
332
+ kana: ranges([
333
+ [12352, 12543],
334
+ [12784, 12799],
335
+ [110592, 110959]
336
+ ]),
337
+ latin: ranges([
338
+ [32, 126],
339
+ [160, 687],
340
+ [7680, 7935]
341
+ ]),
342
+ punctuation: ranges([
343
+ [8192, 8303],
344
+ [12288, 12351],
345
+ [65040, 65055],
346
+ [65072, 65103],
347
+ [65280, 65519]
348
+ ])
349
+ };
350
+ const LANGUAGE_GROUPS = {
351
+ ar: ["arabic"],
352
+ el: ["greek"],
353
+ en: ["latin"],
354
+ hi: ["devanagari"],
355
+ ja: [
356
+ "punctuation",
357
+ "kana",
358
+ "han"
359
+ ],
360
+ ko: [
361
+ "punctuation",
362
+ "hangul",
363
+ "han"
364
+ ],
365
+ ru: ["cyrillic"],
366
+ "zh-Hans": ["punctuation", "han"],
367
+ "zh-Hant": [
368
+ "punctuation",
369
+ "bopomofo",
370
+ "han"
371
+ ]
372
+ };
373
+ /**
374
+ * Detect supported delivery-language presets from representative page text.
375
+ *
376
+ * @param text - Static or frequency-weighted business text.
377
+ * @returns Presets in first-observed order, with Han assigned to a detected
378
+ * Japanese, Korean, or Traditional Chinese context before Simplified Chinese.
379
+ */
380
+ function detectDeliveryLanguages(text) {
381
+ const detected = [];
382
+ const seen = /* @__PURE__ */ new Set();
383
+ let pendingHan = false;
384
+ for (const character of text) {
385
+ const codePoint = character.codePointAt(0);
386
+ if (codePoint === void 0) continue;
387
+ const language = languageOf(codePoint);
388
+ if (language === "han") pendingHan = true;
389
+ else if (language !== void 0 && !seen.has(language)) {
390
+ seen.add(language);
391
+ detected.push(language);
392
+ }
393
+ }
394
+ if (pendingHan && ![
395
+ "ja",
396
+ "ko",
397
+ "zh-Hant"
398
+ ].some((tag) => seen.has(tag))) detected.push("zh-Hans");
399
+ return detected;
400
+ }
401
+ /**
402
+ * Plan byte-bounded delivery slices over code points supported by a font.
403
+ *
404
+ * @param supportedCodePoints - Unicode cmap coverage from one or more faces.
405
+ * @param options - Language, frequency, byte target, and request constraints.
406
+ * @param measure - Returns the actual encoded byte size for a proposed slice.
407
+ * @returns A deterministic, measured delivery plan.
408
+ */
409
+ async function planAutoDeliverySlices(supportedCodePoints, options, measure) {
410
+ const normalized = normalizeOptions(options);
411
+ const languages = resolveLanguages(normalized);
412
+ const groups = planningGroups(supportedCodePoints, languages, normalized);
413
+ if (groups.length === 0) throw new Error("auto delivery presets matched no supported code points");
414
+ if (groups.length > normalized.maxSlices) throw new Error(`auto delivery requires at least ${groups.length} slices for the selected languages`);
415
+ const merged = await mergeSmallGroups(await splitOversizedGroups(await Promise.all(groups.map(async (group) => measureGroup(group, measure))), normalized, measure), normalized, measure);
416
+ const groupCounts = /* @__PURE__ */ new Map();
417
+ for (const group of merged) groupCounts.set(group.name, (groupCounts.get(group.name) ?? 0) + 1);
418
+ const groupIndexes = /* @__PURE__ */ new Map();
419
+ const slices = merged.map((group) => {
420
+ const index = (groupIndexes.get(group.name) ?? 0) + 1;
421
+ const count = groupCounts.get(group.name) ?? 1;
422
+ groupIndexes.set(group.name, index);
423
+ return {
424
+ codePoints: [...group.codePoints].toSorted((left, right) => left - right),
425
+ estimatedBytes: group.estimatedBytes,
426
+ name: count === 1 ? group.name : `${group.name}-${String(index).padStart(String(count).length, "0")}`,
427
+ unicodeRanges: unicodeRangesFromCodePoints(group.codePoints)
428
+ };
429
+ });
430
+ return {
431
+ codePointCount: new Set(slices.flatMap((slice) => slice.codePoints)).size,
432
+ languages,
433
+ slices,
434
+ targetBytes: normalized.targetBytes,
435
+ tolerance: normalized.tolerance
436
+ };
437
+ }
438
+ function normalizeOptions(options) {
439
+ const targetBytes = options.targetBytes ?? DEFAULT_TARGET_BYTES;
440
+ const tolerance = options.tolerance ?? DEFAULT_TOLERANCE;
441
+ const maxSlices = options.maxSlices ?? DEFAULT_MAX_SLICES;
442
+ if (!Number.isInteger(targetBytes) || targetBytes <= 0) throw new TypeError("auto delivery targetBytes must be a positive integer");
443
+ if (!Number.isFinite(tolerance) || tolerance < 0 || tolerance >= 1) throw new TypeError("auto delivery tolerance must be in [0, 1)");
444
+ if (!Number.isInteger(maxSlices) || maxSlices <= 0 || maxSlices > 256) throw new TypeError("auto delivery maxSlices must be an integer in [1, 256]");
445
+ return {
446
+ frequencyText: options.frequencyText ?? "",
447
+ languages: [...options.languages ?? []],
448
+ maxSlices,
449
+ targetBytes,
450
+ tolerance
451
+ };
452
+ }
453
+ function resolveLanguages(options) {
454
+ const languages = options.languages.length === 0 ? detectDeliveryLanguages(options.frequencyText) : options.languages;
455
+ const resolved = languages.length === 0 ? ["en"] : languages;
456
+ return [...new Set(resolved)];
457
+ }
458
+ function planningGroups(supportedCodePoints, languages, options) {
459
+ const supported = new Set(supportedCodePoints.filter((codePoint) => isValidUnicodeScalar$1(codePoint)).toSorted((left, right) => left - right));
460
+ const languageGroups = groupsForLanguages(languages);
461
+ const selected = new Set(languageGroups.flatMap((group) => [...supported].filter((codePoint) => includesCodePoint(group, codePoint))));
462
+ const frequency = frequencyOrder(options.frequencyText).filter((codePoint) => selected.has(codePoint));
463
+ const prioritized = new Set(frequency);
464
+ const groups = [];
465
+ if (frequency.length > 0) groups.push({
466
+ codePoints: frequency,
467
+ name: "priority"
468
+ });
469
+ const assigned = new Set(prioritized);
470
+ for (const group of languageGroups) {
471
+ const codePoints = [...supported].filter((codePoint) => !assigned.has(codePoint) && includesCodePoint(group, codePoint));
472
+ for (const codePoint of codePoints) assigned.add(codePoint);
473
+ if (codePoints.length > 0) groups.push({
474
+ codePoints,
475
+ name: group.name
476
+ });
477
+ }
478
+ return groups;
479
+ }
480
+ function groupsForLanguages(languages) {
481
+ return [...new Set(languages.flatMap((language) => LANGUAGE_GROUPS[language]))].map((name) => ({
482
+ name,
483
+ ranges: GROUPS[name]
484
+ }));
485
+ }
486
+ async function splitOversizedGroups(initial, options, measure) {
487
+ const groups = [...initial];
488
+ const maximumBytes = options.targetBytes * (1 + options.tolerance);
489
+ while (groups.length < options.maxSlices) {
490
+ const candidateIndex = groups.map((group, index) => ({
491
+ group,
492
+ index
493
+ })).filter(({ group }) => group.estimatedBytes > maximumBytes && group.codePoints.length > 1).toSorted((left, right) => right.group.estimatedBytes - left.group.estimatedBytes || left.index - right.index)[0]?.index;
494
+ if (candidateIndex === void 0) break;
495
+ const candidate = groups[candidateIndex];
496
+ if (candidate === void 0) break;
497
+ const midpoint = Math.ceil(candidate.codePoints.length / 2);
498
+ const replacements = await Promise.all([measureGroup({
499
+ codePoints: candidate.codePoints.slice(0, midpoint),
500
+ name: candidate.name
501
+ }, measure), measureGroup({
502
+ codePoints: candidate.codePoints.slice(midpoint),
503
+ name: candidate.name
504
+ }, measure)]);
505
+ groups.splice(candidateIndex, 1, ...replacements);
506
+ }
507
+ return groups;
508
+ }
509
+ async function mergeSmallGroups(initial, options, measure) {
510
+ const groups = [...initial];
511
+ const minimumBytes = options.targetBytes * (1 - options.tolerance);
512
+ const maximumBytes = options.targetBytes * (1 + options.tolerance);
513
+ let index = 0;
514
+ while (index < groups.length - 1) {
515
+ const current = groups[index];
516
+ const next = groups[index + 1];
517
+ if (current === void 0 || next === void 0) break;
518
+ if (current.name !== next.name || current.estimatedBytes >= minimumBytes) {
519
+ index += 1;
520
+ continue;
521
+ }
522
+ const merged = await measureGroup({
523
+ codePoints: [...current.codePoints, ...next.codePoints],
524
+ name: current.name
525
+ }, measure);
526
+ if (merged.estimatedBytes <= maximumBytes) groups.splice(index, 2, merged);
527
+ else index += 1;
528
+ }
529
+ return groups;
530
+ }
531
+ async function measureGroup(group, measure) {
532
+ const estimatedBytes = await measure(group.codePoints);
533
+ if (!Number.isInteger(estimatedBytes) || estimatedBytes < 0) throw new TypeError("auto delivery measure must return a non-negative integer");
534
+ return {
535
+ ...group,
536
+ estimatedBytes
537
+ };
538
+ }
539
+ function frequencyOrder(text) {
540
+ const frequencies = /* @__PURE__ */ new Map();
541
+ let index = 0;
542
+ for (const character of text) {
543
+ const codePoint = character.codePointAt(0);
544
+ if (codePoint !== void 0) {
545
+ const current = frequencies.get(codePoint);
546
+ frequencies.set(codePoint, {
547
+ count: (current?.count ?? 0) + 1,
548
+ index: current?.index ?? index
549
+ });
550
+ index += 1;
551
+ }
552
+ }
553
+ return [...frequencies.entries()].toSorted(([, left], [, right]) => right.count - left.count || left.index - right.index).map(([codePoint]) => codePoint);
554
+ }
555
+ /**
556
+ * Compact code points into canonical CSS Unicode ranges.
557
+ *
558
+ * @param codePoints - Unicode scalar values in any order.
559
+ * @returns Sorted `U+XXXX[-YYYY]` ranges.
560
+ */
561
+ function unicodeRangesFromCodePoints(codePoints) {
562
+ const sorted = [...new Set(codePoints)].toSorted((left, right) => left - right);
563
+ const compact = [];
564
+ for (let index = 0; index < sorted.length;) {
565
+ const start = sorted[index];
566
+ if (start === void 0) break;
567
+ let end = start;
568
+ while (sorted[index + 1] === end + 1) {
569
+ end += 1;
570
+ index += 1;
571
+ }
572
+ compact.push(start === end ? `U+${unicodeHex(start)}` : `U+${unicodeHex(start)}-${unicodeHex(end)}`);
573
+ index += 1;
574
+ }
575
+ return compact;
576
+ }
577
+ function languageOf(codePoint) {
578
+ if (includesRanges(GROUPS.kana, codePoint)) return "ja";
579
+ if (includesRanges(GROUPS.hangul, codePoint)) return "ko";
580
+ if (includesRanges(GROUPS.bopomofo, codePoint)) return "zh-Hant";
581
+ if (includesRanges(GROUPS.han, codePoint)) return "han";
582
+ if (includesRanges(GROUPS.arabic, codePoint)) return "ar";
583
+ if (includesRanges(GROUPS.devanagari, codePoint)) return "hi";
584
+ if (includesRanges(GROUPS.cyrillic, codePoint)) return "ru";
585
+ if (includesRanges(GROUPS.greek, codePoint)) return "el";
586
+ if (includesRanges(GROUPS.latin, codePoint)) return "en";
587
+ }
588
+ function includesCodePoint(group, codePoint) {
589
+ return includesRanges(group.ranges, codePoint);
590
+ }
591
+ function includesRanges(values, codePoint) {
592
+ return values.some((range) => codePoint >= range.start && codePoint <= range.end);
593
+ }
594
+ function ranges(values) {
595
+ return values.map(([start, end]) => ({
596
+ end,
597
+ start
598
+ }));
599
+ }
600
+ function isValidUnicodeScalar$1(codePoint) {
601
+ return Number.isInteger(codePoint) && codePoint >= 0 && codePoint <= 1114111 && (codePoint < 55296 || codePoint > 57343);
602
+ }
603
+ function unicodeHex(codePoint) {
604
+ return codePoint.toString(16).toUpperCase().padStart(4, "0");
605
+ }
606
+ //#endregion
607
+ //#region ../../packages/fontmin/src/runtime-neutral/sfnt-unicode.ts
608
+ /**
609
+ * Read mapped Unicode scalar values from the Unicode cmap subtables of an SFNT.
610
+ *
611
+ * @param input - TTF or OTF bytes.
612
+ * @returns Sorted, unique code points whose mapped glyph ID is non-zero.
613
+ */
614
+ function unicodeCodePointsFromSfnt(input) {
615
+ const view = new DataView(input.buffer, input.byteOffset, input.byteLength);
616
+ const cmapOffset = tableOffset(view, input, "cmap");
617
+ if (cmapOffset === void 0 || cmapOffset + 4 > view.byteLength) return [];
618
+ const recordCount = view.getUint16(cmapOffset + 2);
619
+ const codePoints = /* @__PURE__ */ new Set();
620
+ const visited = /* @__PURE__ */ new Set();
621
+ for (let index = 0; index < recordCount; index += 1) {
622
+ const recordOffset = cmapOffset + 4 + index * 8;
623
+ if (recordOffset + 8 > view.byteLength) break;
624
+ const platform = view.getUint16(recordOffset);
625
+ const encoding = view.getUint16(recordOffset + 2);
626
+ if (platform !== 0 && !(platform === 3 && (encoding === 1 || encoding === 10))) continue;
627
+ const subtableOffset = cmapOffset + view.getUint32(recordOffset + 4);
628
+ if (visited.has(subtableOffset) || subtableOffset + 2 > view.byteLength) continue;
629
+ visited.add(subtableOffset);
630
+ collectSubtable(view, subtableOffset, codePoints);
631
+ }
632
+ return [...codePoints].toSorted((left, right) => left - right);
633
+ }
634
+ function tableOffset(view, input, requestedTag) {
635
+ if (view.byteLength < 12) return;
636
+ const tableCount = view.getUint16(4);
637
+ const decoder = new TextDecoder();
638
+ for (let index = 0; index < tableCount; index += 1) {
639
+ const recordOffset = 12 + index * 16;
640
+ if (recordOffset + 16 > view.byteLength) return;
641
+ if (decoder.decode(input.subarray(recordOffset, recordOffset + 4)) === requestedTag) {
642
+ const offset = view.getUint32(recordOffset + 8);
643
+ return offset < view.byteLength ? offset : void 0;
644
+ }
645
+ }
646
+ }
647
+ function collectSubtable(view, offset, codePoints) {
648
+ const format = view.getUint16(offset);
649
+ if (format === 0) collectFormat0(view, offset, codePoints);
650
+ else if (format === 4) collectFormat4(view, offset, codePoints);
651
+ else if (format === 12 || format === 13) collectFormat12Or13(view, offset, codePoints, format);
652
+ }
653
+ function collectFormat0(view, offset, codePoints) {
654
+ if (offset + 262 > view.byteLength) return;
655
+ for (let codePoint = 0; codePoint < 256; codePoint += 1) if (view.getUint8(offset + 6 + codePoint) !== 0) codePoints.add(codePoint);
656
+ }
657
+ function collectFormat4(view, offset, codePoints) {
658
+ if (offset + 16 > view.byteLength) return;
659
+ const length = view.getUint16(offset + 2);
660
+ const end = Math.min(offset + length, view.byteLength);
661
+ const segmentCount = view.getUint16(offset + 6) / 2;
662
+ const endCodesOffset = offset + 14;
663
+ const startCodesOffset = endCodesOffset + segmentCount * 2 + 2;
664
+ const deltasOffset = startCodesOffset + segmentCount * 2;
665
+ const rangeOffsetsOffset = deltasOffset + segmentCount * 2;
666
+ if (rangeOffsetsOffset + segmentCount * 2 > end) return;
667
+ for (let segment = 0; segment < segmentCount; segment += 1) {
668
+ const start = view.getUint16(startCodesOffset + segment * 2);
669
+ const segmentEnd = view.getUint16(endCodesOffset + segment * 2);
670
+ const delta = view.getInt16(deltasOffset + segment * 2);
671
+ const rangeOffsetPosition = rangeOffsetsOffset + segment * 2;
672
+ const rangeOffset = view.getUint16(rangeOffsetPosition);
673
+ for (let codePoint = start; codePoint <= segmentEnd && codePoint < 65535; codePoint += 1) {
674
+ let glyphId;
675
+ if (rangeOffset === 0) glyphId = uint16(codePoint + delta);
676
+ else {
677
+ const glyphOffset = rangeOffsetPosition + rangeOffset + (codePoint - start) * 2;
678
+ if (glyphOffset + 2 > end) continue;
679
+ const rawGlyphId = view.getUint16(glyphOffset);
680
+ glyphId = rawGlyphId === 0 ? 0 : uint16(rawGlyphId + delta);
681
+ }
682
+ if (glyphId !== 0 && isValidUnicodeScalar(codePoint)) codePoints.add(codePoint);
683
+ }
684
+ }
685
+ }
686
+ function collectFormat12Or13(view, offset, codePoints, format) {
687
+ if (offset + 16 > view.byteLength) return;
688
+ const length = view.getUint32(offset + 4);
689
+ const end = Math.min(offset + length, view.byteLength);
690
+ const groupCount = view.getUint32(offset + 12);
691
+ for (let group = 0; group < groupCount; group += 1) {
692
+ const groupOffset = offset + 16 + group * 12;
693
+ if (groupOffset + 12 > end) break;
694
+ const start = view.getUint32(groupOffset);
695
+ const groupEnd = Math.min(view.getUint32(groupOffset + 4), 1114111);
696
+ const startGlyphId = view.getUint32(groupOffset + 8);
697
+ for (let codePoint = start; codePoint <= groupEnd; codePoint += 1) if ((format === 12 ? startGlyphId + codePoint - start : startGlyphId) !== 0 && isValidUnicodeScalar(codePoint)) codePoints.add(codePoint);
698
+ }
699
+ }
700
+ function isValidUnicodeScalar(codePoint) {
701
+ return codePoint < 55296 || codePoint > 57343;
702
+ }
703
+ function uint16(value) {
704
+ return (value % 65536 + 65536) % 65536;
705
+ }
706
+ //#endregion
241
707
  //#region src/plugins.ts
242
708
  function plugin(name, options) {
243
709
  return {
@@ -250,9 +716,18 @@ function glyph(options = {}) {
250
716
  basicText: false,
251
717
  keepNotdef: true,
252
718
  layout: "conservative",
719
+ layoutFeatures: [],
720
+ layoutScripts: [],
721
+ layoutLanguages: [],
722
+ nameIds: [],
723
+ nameLanguages: [],
724
+ dropTables: [],
725
+ passThroughTables: [],
253
726
  missingGlyphs: "warn",
254
727
  preserveHinting: false,
255
728
  trim: true,
729
+ gids: [],
730
+ glyphNames: [],
256
731
  unicodes: [],
257
732
  ...options
258
733
  });
@@ -263,6 +738,9 @@ function deliverySlices(slices) {
263
738
  unicodeRanges: [...slice.unicodeRanges]
264
739
  })) });
265
740
  }
741
+ function autoDeliverySlices(options = {}) {
742
+ return plugin("autoUnicodeSlices", structuredClone(options));
743
+ }
266
744
  function normalizeDeliverySlices(options) {
267
745
  return normalizeDeliverySlices$1(options.slices);
268
746
  }
@@ -281,6 +759,9 @@ function ttf2svg(options = {}) {
281
759
  function otf2ttf(options = {}) {
282
760
  return plugin("otf2ttf", options);
283
761
  }
762
+ function variationSpace(options) {
763
+ return plugin("variationSpace", options);
764
+ }
284
765
  function svg2ttf(options = {}) {
285
766
  return plugin("svg2ttf", options);
286
767
  }
@@ -312,6 +793,11 @@ function modernWeb(options = {}) {
312
793
  if (typeof options.preserveHinting === "boolean") otfOptions.preserveHinting = options.preserveHinting;
313
794
  if (options.variationCoordinates !== void 0) otfOptions.variationCoordinates = options.variationCoordinates;
314
795
  return [
796
+ ...options.variationAxes === void 0 ? [] : [variationSpace({
797
+ axes: options.variationAxes,
798
+ clone: false,
799
+ ...options.downgradeCff2 === void 0 ? {} : { downgradeCff2: options.downgradeCff2 }
800
+ })],
315
801
  otf2ttf(otfOptions),
316
802
  glyph(subset),
317
803
  ttf2woff(options),
@@ -341,6 +827,29 @@ function fontminCompatPreset(options = {}) {
341
827
  async function optimizeBrowser(config) {
342
828
  let assets = config.assets.map(formatAsset);
343
829
  for (const plugin of config.plugins ?? []) {
830
+ if (plugin.name === "variationSpace") {
831
+ const options = optionsOf(plugin);
832
+ assets = await flatMapAssets(assets, async (asset) => {
833
+ if (![
834
+ "eot",
835
+ "otf",
836
+ "ttf",
837
+ "woff",
838
+ "woff2"
839
+ ].includes(asset.format)) return [asset];
840
+ const contents = await reduceVariationSpace(asset.contents, options);
841
+ const format = new TextDecoder().decode(contents.subarray(0, 4)) === "OTTO" ? "otf" : "ttf";
842
+ const normalizedFileName = replaceExtension(asset.fileName, format);
843
+ const reduced = {
844
+ ...asset,
845
+ contents,
846
+ fileName: options.clone === true ? appendFileNameSuffix(normalizedFileName, "reduced") : normalizedFileName,
847
+ format
848
+ };
849
+ return options.clone === true ? [asset, reduced] : [reduced];
850
+ });
851
+ continue;
852
+ }
344
853
  if (plugin.name === "glyph") {
345
854
  const options = optionsOf(plugin);
346
855
  assets = await flatMapAssets(assets, async (asset) => {
@@ -369,6 +878,10 @@ async function optimizeBrowser(config) {
369
878
  });
370
879
  continue;
371
880
  }
881
+ if (plugin.name === "autoUnicodeSlices") {
882
+ assets = await runAutoDeliverySlices(assets, optionsOf(plugin));
883
+ continue;
884
+ }
372
885
  if (plugin.name === "css") {
373
886
  const options = optionsOf(plugin);
374
887
  const css = await generateFontFaceCss(assets.filter((asset) => [
@@ -442,6 +955,56 @@ async function optimizeBrowser(config) {
442
955
  }
443
956
  return assets;
444
957
  }
958
+ async function runAutoDeliverySlices(assets, options) {
959
+ const sources = assets.flatMap((asset, index) => asset.format === "ttf" ? [{
960
+ asset,
961
+ codePoints: new Set(unicodeCodePointsFromSfnt(asset.contents)),
962
+ index
963
+ }] : []);
964
+ if (sources.length === 0) return assets;
965
+ const supported = [...new Set(sources.flatMap((source) => [...source.codePoints]))];
966
+ const cache = /* @__PURE__ */ new Map();
967
+ const subsetFor = async (source, codePoints) => {
968
+ const key = `${source.index}:${codePoints.join(",")}`;
969
+ const cached = cache.get(key);
970
+ if (cached !== void 0) return cached;
971
+ const contents = await subsetTtf(source.asset.contents, {
972
+ ...options.subset,
973
+ missingGlyphs: "ignore",
974
+ unicodeRanges: unicodeRangesFromCodePoints(codePoints)
975
+ });
976
+ cache.set(key, contents);
977
+ return contents;
978
+ };
979
+ const plan = await planAutoDeliverySlices(supported, options, async (codePoints) => {
980
+ const sizes = await Promise.all(sources.filter((source) => codePoints.some((codePoint) => source.codePoints.has(codePoint))).map(async (source) => measureAutoDeliverySubset(await subsetFor(source, codePoints), options)));
981
+ return Math.max(...sizes);
982
+ });
983
+ const output = [];
984
+ for (const [index, asset] of assets.entries()) {
985
+ const source = sources.find((candidate) => candidate.index === index);
986
+ if (source === void 0) {
987
+ output.push(asset);
988
+ continue;
989
+ }
990
+ for (const slice of plan.slices) {
991
+ const codePoints = slice.codePoints.filter((codePoint) => source.codePoints.has(codePoint));
992
+ if (codePoints.length > 0) output.push({
993
+ ...asset,
994
+ contents: await subsetFor(source, codePoints),
995
+ fileName: appendFileNameSuffix(asset.fileName, slice.name),
996
+ unicodeRanges: unicodeRangesFromCodePoints(codePoints)
997
+ });
998
+ }
999
+ }
1000
+ return output;
1001
+ }
1002
+ async function measureAutoDeliverySubset(contents, options) {
1003
+ const format = options.measureFormat ?? "woff2";
1004
+ if (format === "ttf") return contents.byteLength;
1005
+ if (format === "woff") return (await ttfToWoff(contents, options.woffCompressionLevel === void 0 ? {} : { compressionLevel: options.woffCompressionLevel })).byteLength;
1006
+ return (await ttfToWoff2(contents, options.woff2Quality === void 0 ? {} : { quality: options.woff2Quality })).byteLength;
1007
+ }
445
1008
  async function convert(asset, plugin, conversion) {
446
1009
  if (conversion.name === "ttf2woff") return converted(asset, conversion.outputFormat, await ttfToWoff(asset.contents, optionsOf(plugin)));
447
1010
  if (conversion.name === "ttf2woff2") return converted(asset, conversion.outputFormat, await ttfToWoff2(asset.contents, optionsOf(plugin)));
@@ -481,4 +1044,4 @@ function toKebabCase(value) {
481
1044
  return value.trim().replaceAll(/(?<lower>[a-z])(?<upper>[A-Z])/gu, "$<lower>-$<upper>").replaceAll(/\s+/gu, "-").toLowerCase();
482
1045
  }
483
1046
  //#endregion
484
- export { FontminDiagnosticError, analyzeCoverage, css, deliverySlices, eotToTtf, fontminCompatPreset, generateFontFaceCss, glyph, initWasm, inspect, isWasmInitialized, modernWeb, optimizeBrowser, otf2ttf, otfToTtf, subsetTtf, svg2ttf, svgFontToTtf, svgs2ttf, svgsToTtf, ttf2eot, ttf2svg, ttf2woff, ttf2woff2, ttfToEot, ttfToSvg, ttfToWoff, ttfToWoff2, validateWoff2, woff2ToTtf, woffToTtf };
1047
+ export { FontminDiagnosticError, analyzeCoverage, autoDeliverySlices, createTtfSubsetPlan, css, deliverySlices, detectDeliveryLanguages, eotToTtf, extractCollectionFace, fontminCompatPreset, generateFontFaceCss, glyph, initWasm, inspect, inspectCapabilities, inspectCollection, instantiateFont, isWasmInitialized, modernWeb, optimizeBrowser, otf2ttf, otfToTtf, reduceVariationSpace, subsetTtf, subsetTtfWithPlan, subsetTtfWithReport, svg2ttf, svgFontToTtf, svgs2ttf, svgsToTtf, ttf2eot, ttf2svg, ttf2woff, ttf2woff2, ttfToEot, ttfToSvg, ttfToWoff, ttfToWoff2, unicodeRangesFromCodePoints, validateWoff2, variationSpace, woff2ToTtf, woffToTtf };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fontmin-rs/wasm",
3
- "version": "1.0.2-rc.1",
3
+ "version": "1.1.0",
4
4
  "description": "Browser WASM runtime for fontmin-rs.",
5
5
  "license": "MIT",
6
6
  "repository": {