summaryrefslogtreecommitdiff
path: root/packages/astro/src/assets/fonts/implementations/font-metrics-resolver.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/astro/src/assets/fonts/implementations/font-metrics-resolver.ts')
-rw-r--r--packages/astro/src/assets/fonts/implementations/font-metrics-resolver.ts73
1 files changed, 73 insertions, 0 deletions
diff --git a/packages/astro/src/assets/fonts/implementations/font-metrics-resolver.ts b/packages/astro/src/assets/fonts/implementations/font-metrics-resolver.ts
new file mode 100644
index 000000000..4541479d0
--- /dev/null
+++ b/packages/astro/src/assets/fonts/implementations/font-metrics-resolver.ts
@@ -0,0 +1,73 @@
+import { fromBuffer, type Font } from '@capsizecss/unpack';
+import type { CssRenderer, FontFetcher, FontMetricsResolver } from '../definitions.js';
+import type { FontFaceMetrics } from '../types.js';
+import { renderFontSrc } from '../utils.js';
+
+// Source: https://github.com/unjs/fontaine/blob/main/src/metrics.ts
+function filterRequiredMetrics({
+ ascent,
+ descent,
+ lineGap,
+ unitsPerEm,
+ xWidthAvg,
+}: Pick<Font, 'ascent' | 'descent' | 'lineGap' | 'unitsPerEm' | 'xWidthAvg'>) {
+ return {
+ ascent,
+ descent,
+ lineGap,
+ unitsPerEm,
+ xWidthAvg,
+ };
+}
+
+// Source: https://github.com/unjs/fontaine/blob/f00f84032c5d5da72c8798eae4cd68d3ddfbf340/src/css.ts#L7
+function toPercentage(value: number, fractionDigits = 4) {
+ const percentage = value * 100;
+ return `${+percentage.toFixed(fractionDigits)}%`;
+}
+
+export function createCapsizeFontMetricsResolver({
+ fontFetcher,
+ cssRenderer,
+}: {
+ fontFetcher: FontFetcher;
+ cssRenderer: CssRenderer;
+}): FontMetricsResolver {
+ const cache: Record<string, FontFaceMetrics | null> = {};
+
+ return {
+ async getMetrics(name, { hash, url }) {
+ cache[name] ??= filterRequiredMetrics(await fromBuffer(await fontFetcher.fetch(hash, url)));
+ return cache[name];
+ },
+ // Source: https://github.com/unjs/fontaine/blob/f00f84032c5d5da72c8798eae4cd68d3ddfbf340/src/css.ts#L170
+ generateFontFace({
+ metrics,
+ fallbackMetrics,
+ name: fallbackName,
+ font: fallbackFontName,
+ properties,
+ }) {
+ // Calculate size adjust
+ const preferredFontXAvgRatio = metrics.xWidthAvg / metrics.unitsPerEm;
+ const fallbackFontXAvgRatio = fallbackMetrics.xWidthAvg / fallbackMetrics.unitsPerEm;
+ const sizeAdjust = preferredFontXAvgRatio / fallbackFontXAvgRatio;
+
+ const adjustedEmSquare = metrics.unitsPerEm * sizeAdjust;
+
+ // Calculate metric overrides for preferred font
+ const ascentOverride = metrics.ascent / adjustedEmSquare;
+ const descentOverride = Math.abs(metrics.descent) / adjustedEmSquare;
+ const lineGapOverride = metrics.lineGap / adjustedEmSquare;
+
+ return cssRenderer.generateFontFace(fallbackName, {
+ src: renderFontSrc([{ name: fallbackFontName }]),
+ 'size-adjust': toPercentage(sizeAdjust),
+ 'ascent-override': toPercentage(ascentOverride),
+ 'descent-override': toPercentage(descentOverride),
+ 'line-gap-override': toPercentage(lineGapOverride),
+ ...properties,
+ });
+ },
+ };
+}