Skip to content

Commit

Permalink
feat(next-core): support capsize for google font fallback
Browse files Browse the repository at this point in the history
  • Loading branch information
kwonoj committed Apr 28, 2023
1 parent b3071ad commit 100efdd
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 24 deletions.
1 change: 1 addition & 0 deletions packages/next-swc/crates/napi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ rustls-tls = ["next-dev/rustls-tls"]

# Internal only. Enabled when building for the Next.js integration test suite.
__internal_nextjs_integration_test = [
"next-core/__internal_nextjs_integration_test",
"next-dev/__internal_nextjs_integration_test",
"next-dev/serializable"
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,22 @@ use turbo_binding::turbo::tasks::{

pub(crate) struct DefaultFallbackFont {
pub name: String,
pub az_avg_width: f64,
pub x_width_avg: f64,
pub units_per_em: u32,
}

// From https://github.com/vercel/next.js/blob/a3893bf69c83fb08e88c87bf8a21d987a0448c8e/packages/font/src/utils.ts#L4
pub(crate) static DEFAULT_SANS_SERIF_FONT: Lazy<DefaultFallbackFont> =
Lazy::new(|| DefaultFallbackFont {
name: "Arial".to_owned(),
az_avg_width: 934.5116279069767,
x_width_avg: 934.5116279069767,
units_per_em: 2048,
});

pub(crate) static DEFAULT_SERIF_FONT: Lazy<DefaultFallbackFont> =
Lazy::new(|| DefaultFallbackFont {
name: "Times New Roman".to_owned(),
az_avg_width: 854.3953488372093,
x_width_avg: 854.3953488372093,
units_per_em: 2048,
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use std::collections::HashMap;

use anyhow::{Context, Result};
use once_cell::sync::Lazy;
use regex::Regex;
use serde::{Deserialize, Serialize};
use turbo_binding::{turbo::tasks_fs::FileSystemPathVc, turbopack::core::issue::IssueSeverity};
use turbo_tasks::{
Expand All @@ -25,12 +27,15 @@ use crate::{
#[derive(Deserialize)]
#[serde(rename_all = "camelCase")]
pub(super) struct FontMetricsMapEntry {
family_name: String,
category: String,
cap_height: i32,
ascent: i32,
descent: i32,
line_gap: u32,
units_per_em: u32,
az_avg_width: f64,
x_height: i32,
x_width_avg: f64,
}

#[derive(Deserialize)]
Expand All @@ -53,7 +58,7 @@ pub(super) async fn get_font_fallback(
Some(fallback) => FontFallback::Manual(StringsVc::cell(fallback.clone())).cell(),
None => {
let metrics_json =
load_next_json(context, "/dist/server/google-font-metrics.json").await;
load_next_json(context, "/dist/server/capsize-font-metrics.json").await;
match metrics_json {
Ok(metrics_json) => {
let fallback = lookup_fallback(
Expand Down Expand Up @@ -101,14 +106,38 @@ pub(super) async fn get_font_fallback(
})
}

static FALLBACK_FONT_NAME: Lazy<Regex> = Lazy::new(|| Regex::new(r"(?:^\w|[A-Z]|\b\w)").unwrap());

fn format_fallback_font_name(font_family: &str) -> String {
let mut fallback_name = FALLBACK_FONT_NAME
.replace(font_family, |caps: &regex::Captures| {
caps.iter()
.enumerate()
.map(|(i, font_matches)| {
let font_matches = font_matches.unwrap().as_str();
if i == 0 {
font_matches.to_lowercase()
} else {
font_matches.to_uppercase()
}
})
.collect::<Vec<String>>()
.join("")
})
.to_string();
fallback_name.retain(|c| !c.is_whitespace());
fallback_name
}

fn lookup_fallback(
font_family: &str,
font_metrics_map: FontMetricsMap,
adjust: bool,
) -> Result<Fallback> {
let font_family = format_fallback_font_name(font_family);
let metrics = font_metrics_map
.0
.get(font_family)
.get(&font_family)
.context("Font not found in metrics")?;

let fallback = if metrics.category == "serif" {
Expand All @@ -119,9 +148,9 @@ fn lookup_fallback(

let metrics = if adjust {
// Derived from
// https://github.com/vercel/next.js/blob/a3893bf69c83fb08e88c87bf8a21d987a0448c8e/packages/next/src/server/font-utils.ts#L121
let main_font_avg_width = metrics.az_avg_width / metrics.units_per_em as f64;
let fallback_font_avg_width = fallback.az_avg_width / fallback.units_per_em as f64;
// https://github.com/vercel/next.js/blob/7bfd5829999b1d203e447d30de7e29108c31934a/packages/next/src/server/font-utils.ts#L131
let main_font_avg_width = metrics.x_width_avg / metrics.units_per_em as f64;
let fallback_font_avg_width = fallback.x_width_avg / fallback.units_per_em as f64;
let size_adjust = main_font_avg_width / fallback_font_avg_width;

let ascent = metrics.ascent as f64 / (metrics.units_per_em as f64 * size_adjust);
Expand Down Expand Up @@ -157,15 +186,17 @@ mod tests {
let font_metrics: FontMetricsMap = parse_json_with_source_context(
r#"
{
"Inter": {
"inter": {
"familyName": "Inter",
"category": "sans-serif",
"capHeight": 2048,
"ascent": 2728,
"descent": -680,
"lineGap": 0,
"xAvgCharWidth": 1838,
"unitsPerEm": 2816,
"azAvgWidth": 1383.0697674418604
}
"xHeight": 1536,
"xWidthAvg": 1335
}
}
"#,
)?;
Expand All @@ -175,10 +206,10 @@ mod tests {
Fallback {
font_family: "Arial".to_owned(),
adjustment: Some(FontAdjustment {
ascent: 0.9000259575934895,
descent: -0.2243466463209578,
ascent: 0.9324334770490376,
descent: -0.23242476700635833,
line_gap: 0.0,
size_adjust: 1.0763578448229054
size_adjust: 1.0389481114147647
})
}
);
Expand All @@ -190,15 +221,17 @@ mod tests {
let font_metrics: FontMetricsMap = parse_json_with_source_context(
r#"
{
"Roboto Slab": {
"robotoSlab": {
"familyName": "Roboto Slab",
"category": "serif",
"capHeight": 1456,
"ascent": 2146,
"descent": -555,
"lineGap": 0,
"xAvgCharWidth": 1239,
"unitsPerEm": 2048,
"azAvgWidth": 1005.6279069767442
}
"xHeight": 1082,
"xWidthAvg": 969
}
}
"#,
)?;
Expand All @@ -208,10 +241,10 @@ mod tests {
Fallback {
font_family: "Times New Roman".to_owned(),
adjustment: Some(FontAdjustment {
ascent: 0.8902691493151913,
descent: -0.23024202137461844,
ascent: 0.9239210539440684,
descent: -0.23894510015794873,
line_gap: 0.0,
size_adjust: 1.1770053621492147
size_adjust: 1.134135387462914
})
}
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ async fn get_font_adjustment(
))?
.units_per_em as f64;

let fallback_avg_width = fallback_font.az_avg_width / fallback_font.units_per_em as f64;
let fallback_avg_width = fallback_font.x_width_avg / fallback_font.units_per_em as f64;
let size_adjust = match az_avg_width {
Some(az_avg_width) => az_avg_width as f64 / units_per_em / fallback_avg_width,
None => 1.0,
Expand Down

0 comments on commit 100efdd

Please sign in to comment.