Skip to content

Commit 54aa415

Browse files
Add subset support to createFontStack
1 parent b90b809 commit 54aa415

File tree

3 files changed

+73
-11
lines changed

3 files changed

+73
-11
lines changed

packages/core/src/createFontStack.ts

+52-8
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { AtRule } from 'csstype';
22
import { round } from './round';
3-
import type { FontMetrics } from './types';
3+
import type { FontMetrics, SupportedSubset } from './types';
44

55
const toPercentString = (value: number) => `${round(value * 100)}%`;
66

@@ -9,21 +9,48 @@ export const toCssProperty = (property: string) =>
99

1010
type FontStackMetrics = Pick<
1111
FontMetrics,
12-
'familyName' | 'ascent' | 'descent' | 'lineGap' | 'unitsPerEm' | 'xWidthAvg'
12+
| 'familyName'
13+
| 'ascent'
14+
| 'descent'
15+
| 'lineGap'
16+
| 'unitsPerEm'
17+
| 'xWidthAvg'
18+
| 'subsets'
1319
>;
1420

21+
// Support old metrics pre-`subsets` alongside the newer core package with `subset` support.
22+
const resolveXWidthAvg = (
23+
metrics: FontStackMetrics,
24+
subset: SupportedSubset,
25+
) => {
26+
if (metrics?.subsets?.[subset]) {
27+
return metrics.subsets[subset].xWidthAvg;
28+
}
29+
30+
if (subset !== 'latin') {
31+
throw new Error(
32+
`The subset "${subset}" is not available in the metrics provided for "${metrics.familyName}"`,
33+
);
34+
}
35+
36+
return metrics.xWidthAvg;
37+
};
38+
1539
interface OverrideValuesParams {
1640
metrics: FontStackMetrics;
1741
fallbackMetrics: FontStackMetrics;
42+
subset: SupportedSubset;
1843
}
1944
const calculateOverrideValues = ({
2045
metrics,
2146
fallbackMetrics,
47+
subset,
2248
}: OverrideValuesParams): AtRule.FontFace => {
2349
// Calculate size adjust
24-
const preferredFontXAvgRatio = metrics.xWidthAvg / metrics.unitsPerEm;
50+
const preferredFontXAvgRatio =
51+
resolveXWidthAvg(metrics, subset) / metrics.unitsPerEm;
2552
const fallbackFontXAvgRatio =
26-
fallbackMetrics.xWidthAvg / fallbackMetrics.unitsPerEm;
53+
resolveXWidthAvg(fallbackMetrics, subset) / fallbackMetrics.unitsPerEm;
2754

2855
const sizeAdjust =
2956
preferredFontXAvgRatio && fallbackFontXAvgRatio
@@ -131,6 +158,12 @@ type CreateFontStackOptions = {
131158
* support explicit overrides.
132159
*/
133160
fontFaceProperties?: AdditionalFontFaceProperties;
161+
/**
162+
* The unicode subset to use for calculating the `size-adjust` property.
163+
*
164+
* Default: `latin`
165+
*/
166+
subset?: SupportedSubset;
134167
};
135168
type FontFaceFormatString = {
136169
/**
@@ -145,6 +178,18 @@ type FontFaceFormatObject = {
145178
fontFaceFormat?: 'styleObject';
146179
};
147180

181+
const resolveOptions = (options: Parameters<typeof createFontStack>[1]) => {
182+
const fontFaceFormat = options?.fontFaceFormat ?? 'styleString';
183+
const subset = options?.subset ?? 'latin';
184+
const fontFaceProperties = options?.fontFaceProperties ?? {};
185+
186+
return {
187+
fontFaceFormat,
188+
subset,
189+
fontFaceProperties,
190+
} as const;
191+
};
192+
148193
export function createFontStack(
149194
fontStackMetrics: FontStackMetrics[],
150195
options?: CreateFontStackOptions & FontFaceFormatString,
@@ -157,10 +202,8 @@ export function createFontStack(
157202
[metrics, ...fallbackMetrics]: FontStackMetrics[],
158203
optionsArg: CreateFontStackOptions = {},
159204
) {
160-
const { fontFaceFormat, fontFaceProperties } = {
161-
fontFaceFormat: 'styleString',
162-
...optionsArg,
163-
};
205+
const { fontFaceFormat, fontFaceProperties, subset } =
206+
resolveOptions(optionsArg);
164207
const { familyName } = metrics;
165208

166209
const fontFamilies: string[] = [quoteIfNeeded(familyName)];
@@ -182,6 +225,7 @@ export function createFontStack(
182225
...calculateOverrideValues({
183226
metrics,
184227
fallbackMetrics: fallback,
228+
subset,
185229
}),
186230
...(fontFaceProperties?.sizeAdjust
187231
? { sizeAdjust: fontFaceProperties.sizeAdjust }

packages/core/src/types.ts

+10-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import type weightings from '../../unpack/src/weightings';
2+
export type SupportedSubset = keyof typeof weightings;
3+
14
export interface FontMetrics {
25
/** The font family name as authored by font creator */
36
familyName: string;
@@ -19,8 +22,14 @@ export interface FontMetrics {
1922
capHeight: number;
2023
/** The height of the main body of lower case letters above baseline */
2124
xHeight: number;
22-
/** The average width of lowercase characters (currently derived from latin character frequencies in English language) */
25+
/**
26+
* The average width of character glyphs in the font for the Latin unicode subset.
27+
*
28+
* Calculated based on character frequencies in written text.
29+
* */
2330
xWidthAvg: number;
31+
/** A lookup of the `xWidthAvg` metric by unicode subset */
32+
subsets: Record<SupportedSubset, { xWidthAvg: number }>;
2433
}
2534

2635
export type ComputedValues = {

packages/metrics/scripts/generate.ts

+11-2
Original file line numberDiff line numberDiff line change
@@ -102,14 +102,23 @@ const buildFiles = async ({
102102
xWidthAvg: number;`
103103
: ''
104104
}
105+
subsets: {
106+
${Object.keys(subsets).map(
107+
(s) => `${s}: {
108+
xWidthAvg: number;
109+
}`,
110+
).join(`,
111+
`)}
112+
}
105113
}
106114
export const fontMetrics: ${typeName};
107115
export default fontMetrics;
108-
`;
116+
}\n
117+
`;
109118

110119
await writeMetricsFile(`${fileName}.cjs`, cjsOutput);
111120
await writeMetricsFile(`${fileName}.mjs`, mjsOutput);
112-
await writeMetricsFile(`${fileName}.d.ts`, `${typesOutput}\n}\n`);
121+
await writeMetricsFile(`${fileName}.d.ts`, typesOutput);
113122
};
114123

115124
(async () => {

0 commit comments

Comments
 (0)