diff --git a/packages/next-swc/Cargo.lock b/packages/next-swc/Cargo.lock index 122866f2ba..e07bfebcc1 100644 --- a/packages/next-swc/Cargo.lock +++ b/packages/next-swc/Cargo.lock @@ -48,6 +48,55 @@ dependencies = [ "memchr", ] +[[package]] +name = "aliasable" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "250f629c0161ad8107cf89319e990051fae62832fd343083bea452d93e2205fd" + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94fb8275041c72129eb51b7d0322c29b8387a0386127718b096429201a5d6ece" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "allsorts" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e926a9819dcf2211da0c19f5ca06a8f5c883e3bdb5ccc51afead3a7d995f023" +dependencies = [ + "bitflags 1.3.2", + "bitreader", + "brotli-decompressor", + "byteorder", + "encoding_rs", + "flate2", + "glyph-names", + "itertools", + "lazy_static", + "libc", + "log", + "num-traits", + "ouroboros", + "pathfinder_geometry", + "rustc-hash", + "tinyvec", + "ucd-trie", + "unicode-canonical-combining-class", + "unicode-general-category", + "unicode-joining-type", +] + [[package]] name = "android_system_properties" version = "0.1.5" @@ -486,6 +535,15 @@ version = "2.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "487f1e0fcbe47deb8b0574e646def1c903389d95241dd1bbcc6ce4a715dfc0c1" +[[package]] +name = "bitreader" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d84ea71c85d1fe98fe67a9b9988b1695bc24c0b0d3bfb18d4c510f44b4b09941" +dependencies = [ + "cfg-if 1.0.0", +] + [[package]] name = "blake3" version = "1.3.3" @@ -523,6 +581,16 @@ dependencies = [ "futures-lite", ] +[[package]] +name = "brotli-decompressor" +version = "2.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b6561fd3f895a11e8f72af2cb7d22e08366bebc2b6b57f7744c4bda27034744" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + [[package]] name = "browserslist-rs" version = "0.12.3" @@ -1611,6 +1679,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841" dependencies = [ "crc32fast", + "libz-sys", "miniz_oxide", ] @@ -1906,6 +1975,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "glyph-names" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3531d702d6c1a3ba92a5fb55a404c7b8c476c8e7ca249951077afcbe4bc807f" + [[package]] name = "h2" version = "0.3.16" @@ -3076,6 +3151,7 @@ dependencies = [ name = "next-core" version = "0.1.0" dependencies = [ + "allsorts", "anyhow", "auto-hash-map", "futures", @@ -3486,6 +3562,29 @@ version = "6.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ceedf44fb00f2d1984b0bc98102627ce622e083e49a5bacdb3e514fa4238e267" +[[package]] +name = "ouroboros" +version = "0.15.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1358bd1558bd2a083fed428ffeda486fbfb323e698cdda7794259d592ca72db" +dependencies = [ + "aliasable", + "ouroboros_macro", +] + +[[package]] +name = "ouroboros_macro" +version = "0.15.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f7d21ccd03305a674437ee1248f3ab5d4b1db095cf1caf49f1713ddf61956b7" +dependencies = [ + "Inflector", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 1.0.109", +] + [[package]] name = "output_vt100" version = "0.1.3" @@ -3563,6 +3662,25 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" +[[package]] +name = "pathfinder_geometry" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b7e7b4ea703700ce73ebf128e1450eb69c3a8329199ffbfb9b2a0418e5ad3" +dependencies = [ + "log", + "pathfinder_simd", +] + +[[package]] +name = "pathfinder_simd" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39fe46acc5503595e5949c17b818714d26fdf9b4920eacf3b2947f0199f4a6ff" +dependencies = [ + "rustc_version 0.3.3", +] + [[package]] name = "patricia_tree" version = "0.5.5" @@ -4205,6 +4323,15 @@ dependencies = [ "semver 0.9.0", ] +[[package]] +name = "rustc_version" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dfe2087c51c460008730de8b57e6a320782fbfb312e1f4d520e6c6fae155ee" +dependencies = [ + "semver 0.11.0", +] + [[package]] name = "rustc_version" version = "0.4.0" @@ -4362,7 +4489,16 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" dependencies = [ - "semver-parser", + "semver-parser 0.7.0", +] + +[[package]] +name = "semver" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f301af10236f6df4160f7c3f04eec6dbc70ace82d23326abad5edee88801c6b6" +dependencies = [ + "semver-parser 0.10.2", ] [[package]] @@ -4380,6 +4516,15 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" +[[package]] +name = "semver-parser" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0bef5b7f9e0df16536d3961cfb6e84331c065b4066afb39768d0e319411f7" +dependencies = [ + "pest", +] + [[package]] name = "sentry" version = "0.27.0" @@ -7176,6 +7321,18 @@ version = "0.3.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92888ba5573ff080736b3648696b70cafad7d250551175acbaa4e0385b3e1460" +[[package]] +name = "unicode-canonical-combining-class" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6925586af9268182c711e47c0853ed84131049efaca41776d0ca97f983865c32" + +[[package]] +name = "unicode-general-category" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2281c8c1d221438e373249e065ca4989c4c36952c211ff21a0ee91c44a3869e7" + [[package]] name = "unicode-id" version = "0.3.3" @@ -7188,6 +7345,12 @@ version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" +[[package]] +name = "unicode-joining-type" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22f8cb47ccb8bc750808755af3071da4a10dcd147b68fc874b7ae4b12543f6f5" + [[package]] name = "unicode-linebreak" version = "0.1.4" diff --git a/packages/next-swc/Cargo.toml b/packages/next-swc/Cargo.toml index 368f7ce04f..9ab15703ae 100644 --- a/packages/next-swc/Cargo.toml +++ b/packages/next-swc/Cargo.toml @@ -91,6 +91,8 @@ chromiumoxide = { version = "0.4.0", features = [ # sync with chromiumoxide's tungstenite requirement. tungstenite = "0.17.3" +# flate2_zlib requires zlib, use flate2_rust +allsorts = { version = "0.14.0", default_features = false, features = ["outline", "flate2_rust"] } anyhow = "1.0.69" assert_cmd = "2.0.8" async-compression = { version = "0.3.13", default-features = false, features = [ diff --git a/packages/next-swc/crates/next-core/Cargo.toml b/packages/next-swc/crates/next-core/Cargo.toml index 22c25b655e..048f9f5ffe 100644 --- a/packages/next-swc/crates/next-core/Cargo.toml +++ b/packages/next-swc/crates/next-core/Cargo.toml @@ -9,6 +9,7 @@ edition = "2021" bench = false [dependencies] +allsorts = { workspace = true } anyhow = { workspace = true } auto-hash-map = { workspace = true } futures = { workspace = true } diff --git a/packages/next-swc/crates/next-core/src/next_font/local/font_fallback.rs b/packages/next-swc/crates/next-core/src/next_font/local/font_fallback.rs index 8ba72c8496..a21fa9e053 100644 --- a/packages/next-swc/crates/next-core/src/next_font/local/font_fallback.rs +++ b/packages/next-swc/crates/next-core/src/next_font/local/font_fallback.rs @@ -1,14 +1,32 @@ -use anyhow::Result; +use allsorts::{ + font_data::{DynamicFontTableProvider, FontData}, + Font, +}; +use anyhow::{bail, Context, Result}; use turbo_tasks::primitives::{StringVc, StringsVc, U32Vc}; +use turbo_tasks_fs::{FileContent, FileSystemPathVc}; -use super::{options::NextFontLocalOptionsVc, request::AdjustFontFallback}; +use super::{ + options::{FontDescriptor, FontDescriptors, FontWeight, NextFontLocalOptionsVc}, + request::AdjustFontFallback, +}; use crate::next_font::{ - font_fallback::{AutomaticFontFallback, FontFallback, FontFallbacksVc}, + font_fallback::{ + AutomaticFontFallback, DefaultFallbackFont, FontAdjustment, FontFallback, FontFallbacksVc, + DEFAULT_SANS_SERIF_FONT, DEFAULT_SERIF_FONT, + }, util::{get_scoped_font_family, FontFamilyType}, }; +// From +// https://github.com/vercel/next.js/blob/7457be0c74e64b4d0617943ed27f4d557cc916be/packages/font/src/local/get-fallback-metrics-from-font-file.ts#L34 +static AVG_CHARACTERS: &str = "aaabcdeeeefghiijklmnnoopqrrssttuvwxyz "; +static NORMAL_WEIGHT: f64 = 400.0; +static BOLD_WEIGHT: f64 = 700.0; + #[turbo_tasks::function] pub(super) async fn get_font_fallbacks( + context: FileSystemPathVc, options_vc: NextFontLocalOptionsVc, request_hash: U32Vc, ) -> Result { @@ -26,7 +44,9 @@ pub(super) async fn get_font_fallbacks( AutomaticFontFallback { scoped_font_family, local_font_family: StringVc::cell("Arial".to_owned()), - adjustment: None, + adjustment: Some( + get_font_adjustment(context, options_vc, &DEFAULT_SANS_SERIF_FONT).await?, + ), } .cell(), ) @@ -37,7 +57,9 @@ pub(super) async fn get_font_fallbacks( AutomaticFontFallback { scoped_font_family, local_font_family: StringVc::cell("Times New Roman".to_owned()), - adjustment: None, + adjustment: Some( + get_font_adjustment(context, options_vc, &DEFAULT_SERIF_FONT).await?, + ), } .cell(), ) @@ -52,3 +74,365 @@ pub(super) async fn get_font_fallbacks( Ok(FontFallbacksVc::cell(font_fallbacks)) } + +async fn get_font_adjustment( + context: FileSystemPathVc, + options: NextFontLocalOptionsVc, + fallback_font: &DefaultFallbackFont, +) -> Result { + let options = &*options.await?; + let main_descriptor = pick_font_for_fallback_generation(&options.fonts)?; + let font_file = &*context.join(&main_descriptor.path).read().await?; + let font_file_rope = match font_file { + FileContent::NotFound => bail!("Expected font file content"), + FileContent::Content(file) => file.content(), + }; + + let font_file_binary = font_file_rope.to_bytes()?; + let scope = allsorts::binary::read::ReadScope::new(&font_file_binary); + let mut font = Font::new(scope.read::()?.table_provider(0)?)?.context(format!( + "Unable to read font metrics from font file at {}", + &main_descriptor.path, + ))?; + + let az_avg_width = calc_average_width(&mut font); + let units_per_em = font + .head_table()? + .context(format!( + "Unable to read font scale from font file at {}", + &main_descriptor.path + ))? + .units_per_em as f64; + + let fallback_avg_width = fallback_font.az_avg_width / 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, + }; + + Ok(FontAdjustment { + ascent: font.hhea_table.ascender as f64 / (units_per_em * size_adjust), + descent: font.hhea_table.descender as f64 / (units_per_em * size_adjust), + line_gap: font.hhea_table.line_gap as f64 / (units_per_em * size_adjust), + size_adjust, + }) +} + +fn calc_average_width(font: &mut Font) -> Option { + let has_all_glyphs = AVG_CHARACTERS.chars().all(|c| { + font.lookup_glyph_index(c, allsorts::font::MatchingPresentation::NotRequired, None) + .0 + > 0 + }); + if !has_all_glyphs { + return None; + } + + Some( + font.map_glyphs( + AVG_CHARACTERS, + allsorts::tag::LATN, + allsorts::font::MatchingPresentation::NotRequired, + ) + .iter() + .map(|g| font.horizontal_advance(g.glyph_index).unwrap()) + .sum::() as f32 + / AVG_CHARACTERS.len() as f32, + ) +} + +/// From https://github.com/vercel/next.js/blob/dbdf47cf617b8d7213ffe1ff28318ea8eb88c623/packages/font/src/local/pick-font-file-for-fallback-generation.ts#L59 +/// +/// If multiple font files are provided for a font family, we need to pick +/// one to use for the automatic fallback generation. This function returns +/// the font file that is most likely to be used for the bulk of the text on +/// a page. +/// +/// There are some assumptions here about the text on a page when picking the +/// font file: +/// - Most of the text will have normal weight, use the one closest to 400 +/// - Most of the text will have normal style, prefer normal over italic +/// - If two font files have the same distance from normal weight, the thinner +/// one will most likely be the bulk of the text +fn pick_font_for_fallback_generation( + font_descriptors: &FontDescriptors, +) -> Result<&FontDescriptor> { + match font_descriptors { + FontDescriptors::One(descriptor) => Ok(descriptor), + FontDescriptors::Many(descriptors) => { + let mut used_descriptor = descriptors + .first() + .context("At least one font is required")?; + + for current_descriptor in descriptors.iter().skip(1) { + let used_font_distance = get_distance_from_normal_weight(&used_descriptor.weight)?; + let current_font_distance = + get_distance_from_normal_weight(¤t_descriptor.weight)?; + + // Prefer normal style if they have the same weight + if used_font_distance == current_font_distance + && current_descriptor.style != Some("italic".to_owned()) + { + used_descriptor = current_descriptor; + continue; + } + + let abs_used_distance = used_font_distance.abs(); + let abs_current_distance = current_font_distance.abs(); + + // Use closest absolute distance to normal weight + if abs_current_distance < abs_used_distance { + used_descriptor = current_descriptor; + continue; + } + + // Prefer the thinner font if both have the same absolute + if abs_used_distance == abs_current_distance + && current_font_distance < used_font_distance + { + used_descriptor = current_descriptor; + continue; + } + } + + Ok(used_descriptor) + } + } +} + +/// From https://github.com/vercel/next.js/blob/dbdf47cf617b8d7213ffe1ff28318ea8eb88c623/packages/font/src/local/pick-font-file-for-fallback-generation.ts#L18 +/// +/// Get the distance from normal (400) weight for the provided weight. +/// If it's not a variable font we can just return the distance. +/// If it's a variable font we need to compare its weight range to 400. +fn get_distance_from_normal_weight(weight: &Option) -> Result { + let Some(weight) = weight else { + return Ok(0.0) + }; + + Ok(match weight { + FontWeight::Fixed(val) => parse_weight_string(val)? - NORMAL_WEIGHT, + FontWeight::Variable(start, end) => { + let start = parse_weight_string(start)?; + let end = parse_weight_string(end)?; + + // Normal weight is within variable font range + if NORMAL_WEIGHT > start && NORMAL_WEIGHT < end { + 0.0 + } else { + let start_distance = start - NORMAL_WEIGHT; + let end_distance = end - NORMAL_WEIGHT; + + if start_distance.abs() < end_distance.abs() { + start_distance + } else { + end_distance + } + } + } + }) +} + +/// From https://github.com/vercel/next.js/blob/dbdf47cf617b8d7213ffe1ff28318ea8eb88c623/packages/font/src/local/pick-font-file-for-fallback-generation.ts#L6 +/// +/// Convert the weight string to a number so it can be used for comparison. +/// Weights can be defined as a number, 'normal' or 'bold'. https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-weight +fn parse_weight_string(weight_str: &str) -> Result { + if weight_str == "normal" { + Ok(NORMAL_WEIGHT) + } else if weight_str == "bold" { + Ok(BOLD_WEIGHT) + } else { + match weight_str.parse::() { + Ok(parsed) => Ok(parsed), + Err(_) => { + bail!( + "Invalid weight value in src array: `{}`. Expected `normal`, `bold` or a \ + number", + weight_str + ) + } + } + } +} + +// From https://github.com/vercel/next.js/blob/7457be0c74e64b4d0617943ed27f4d557cc916be/packages/font/src/local/pick-font-file-for-fallback-generation.test.ts +#[cfg(test)] +mod tests { + use anyhow::Result; + + use crate::next_font::local::{ + font_fallback::pick_font_for_fallback_generation, + options::{FontDescriptor, FontDescriptors, FontWeight}, + }; + + fn generate_font_descriptor(weight: &FontWeight, style: &Option) -> FontDescriptor { + FontDescriptor { + ext: "ttf".to_owned(), + path: "foo.ttf".to_owned(), + style: style.clone(), + weight: Some(weight.clone()), + } + } + + #[test] + fn test_picks_weight_closest_to_400() -> Result<()> { + assert_eq!( + pick_font_for_fallback_generation(&FontDescriptors::Many(vec![ + generate_font_descriptor(&FontWeight::Fixed("300".to_owned()), &None), + generate_font_descriptor(&FontWeight::Fixed("600".to_owned()), &None) + ]))?, + &generate_font_descriptor(&FontWeight::Fixed("300".to_owned()), &None) + ); + + assert_eq!( + pick_font_for_fallback_generation(&FontDescriptors::Many(vec![ + generate_font_descriptor(&FontWeight::Fixed("200".to_owned()), &None), + generate_font_descriptor(&FontWeight::Fixed("500".to_owned()), &None) + ]))?, + &generate_font_descriptor(&FontWeight::Fixed("500".to_owned()), &None) + ); + + assert_eq!( + pick_font_for_fallback_generation(&FontDescriptors::Many(vec![ + generate_font_descriptor(&FontWeight::Fixed("normal".to_owned()), &None), + generate_font_descriptor(&FontWeight::Fixed("700".to_owned()), &None) + ]))?, + &generate_font_descriptor(&FontWeight::Fixed("normal".to_owned()), &None) + ); + + assert_eq!( + pick_font_for_fallback_generation(&FontDescriptors::Many(vec![ + generate_font_descriptor(&FontWeight::Fixed("bold".to_owned()), &None), + generate_font_descriptor(&FontWeight::Fixed("900".to_owned()), &None) + ]))?, + &generate_font_descriptor(&FontWeight::Fixed("bold".to_owned()), &None) + ); + + Ok(()) + } + + #[test] + fn test_picks_thinner_weight_if_same_distance_to_400() -> Result<()> { + assert_eq!( + pick_font_for_fallback_generation(&FontDescriptors::Many(vec![ + generate_font_descriptor(&FontWeight::Fixed("300".to_owned()), &None), + generate_font_descriptor(&FontWeight::Fixed("500".to_owned()), &None) + ]))?, + &generate_font_descriptor(&FontWeight::Fixed("300".to_owned()), &None) + ); + + Ok(()) + } + + #[test] + fn test_picks_variable_closest_to_400() -> Result<()> { + assert_eq!( + pick_font_for_fallback_generation(&FontDescriptors::Many(vec![ + generate_font_descriptor( + &FontWeight::Variable("100".to_owned(), "300".to_owned()), + &None + ), + generate_font_descriptor( + &FontWeight::Variable("600".to_owned(), "900".to_owned()), + &None + ) + ]))?, + &generate_font_descriptor( + &FontWeight::Variable("100".to_owned(), "300".to_owned()), + &None + ) + ); + + assert_eq!( + pick_font_for_fallback_generation(&FontDescriptors::Many(vec![ + generate_font_descriptor( + &FontWeight::Variable("100".to_owned(), "200".to_owned()), + &None + ), + generate_font_descriptor( + &FontWeight::Variable("500".to_owned(), "800".to_owned()), + &None + ) + ]))?, + &generate_font_descriptor( + &FontWeight::Variable("500".to_owned(), "800".to_owned()), + &None + ) + ); + + assert_eq!( + pick_font_for_fallback_generation(&FontDescriptors::Many(vec![ + generate_font_descriptor( + &FontWeight::Variable("100".to_owned(), "900".to_owned()), + &None + ), + generate_font_descriptor( + &FontWeight::Variable("300".to_owned(), "399".to_owned()), + &None + ) + ]))?, + &generate_font_descriptor( + &FontWeight::Variable("100".to_owned(), "900".to_owned()), + &None + ) + ); + + Ok(()) + } + + #[test] + fn test_prefer_normal_over_italic() -> Result<()> { + assert_eq!( + pick_font_for_fallback_generation(&FontDescriptors::Many(vec![ + generate_font_descriptor( + &FontWeight::Fixed("400".to_owned()), + &Some("normal".to_owned()) + ), + generate_font_descriptor( + &FontWeight::Fixed("400".to_owned()), + &Some("italic".to_owned()) + ) + ]))?, + &generate_font_descriptor( + &FontWeight::Fixed("400".to_owned()), + &Some("normal".to_owned()) + ) + ); + + Ok(()) + } + + #[test] + fn test_errors_on_invalid_weight() -> Result<()> { + match pick_font_for_fallback_generation(&FontDescriptors::Many(vec![ + generate_font_descriptor( + &FontWeight::Variable("normal".to_owned(), "bold".to_owned()), + &None, + ), + generate_font_descriptor( + &FontWeight::Variable("400".to_owned(), "bold".to_owned()), + &None, + ), + generate_font_descriptor( + &FontWeight::Variable("normal".to_owned(), "700".to_owned()), + &None, + ), + generate_font_descriptor( + &FontWeight::Variable("100".to_owned(), "abc".to_owned()), + &None, + ), + ])) { + Ok(_) => panic!(), + Err(err) => { + assert_eq!( + err.to_string(), + "Invalid weight value in src array: `abc`. Expected `normal`, `bold` or a \ + number" + ) + } + } + + Ok(()) + } +} diff --git a/packages/next-swc/crates/next-core/src/next_font/local/mod.rs b/packages/next-swc/crates/next-core/src/next_font/local/mod.rs index 8078658806..73265f154c 100644 --- a/packages/next-swc/crates/next-core/src/next_font/local/mod.rs +++ b/packages/next-swc/crates/next-core/src/next_font/local/mod.rs @@ -75,7 +75,7 @@ impl ImportMappingReplacement for NextFontLocalReplacer { let request_hash = get_request_hash(*query_vc); let options_vc = font_options_from_query_map(*query_vc); - let font_fallbacks = get_font_fallbacks(options_vc, request_hash); + let font_fallbacks = get_font_fallbacks(context, options_vc, request_hash); let properties = &*get_font_css_properties(options_vc, font_fallbacks, request_hash).await?; let file_content = formatdoc!( @@ -164,7 +164,7 @@ impl ImportMappingReplacement for NextFontLocalCssModuleReplacer { "/{}.module.css", get_request_id(options.font_family(), request_hash).await? )); - let fallback = get_font_fallbacks(options, request_hash); + let fallback = get_font_fallbacks(context, options, request_hash); let stylesheet = build_stylesheet( font_options_from_query_map(*query_vc),