Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update font avg #41734

Merged
merged 6 commits into from Oct 24, 2022
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
34 changes: 19 additions & 15 deletions packages/font/src/google/loader.ts
@@ -1,17 +1,19 @@
import type { AdjustFontFallback, FontLoader } from 'next/font'
// @ts-ignore
import { calculateSizeAdjustValues } from 'next/dist/server/font-utils'
// @ts-ignore
import * as Log from 'next/dist/build/output/log'
// @ts-ignore
import chalk from 'next/dist/compiled/chalk'
// @ts-ignore
// eslint-disable-next-line import/no-extraneous-dependencies
import fontFromBuffer from '@next/font/dist/fontkit'
import {
fetchCSSFromGoogleFonts,
fetchFontFile,
getFontAxes,
getUrl,
validateData,
} from './utils'
import { calculateFallbackFontValues } from '../utils'

const cssCache = new Map<string, Promise<string>>()
const fontCache = new Map<string, any>()
Expand Down Expand Up @@ -64,6 +66,7 @@ const downloadGoogleFonts: FontLoader = async ({
const fontFiles: Array<{
googleFontFileUrl: string
preloadFontFile: boolean
isLatin: boolean
}> = []
let currentSubset = ''
for (const line of fontFaceDeclarations.split('\n')) {
Expand All @@ -78,17 +81,24 @@ const downloadGoogleFonts: FontLoader = async ({
googleFontFileUrl,
preloadFontFile:
!!preload && (callSubsets ?? subsets).includes(currentSubset),
isLatin: currentSubset === 'latin',
})
}
}
}

// Download font files
let latinFont: any
const downloadedFiles = await Promise.all(
fontFiles.map(async ({ googleFontFileUrl, preloadFontFile }) => {
fontFiles.map(async ({ googleFontFileUrl, preloadFontFile, isLatin }) => {
let cachedFontRequest = fontCache.get(googleFontFileUrl)
const fontFileBuffer =
cachedFontRequest ?? (await fetchFontFile(googleFontFileUrl))
if (isLatin) {
try {
latinFont = fontFromBuffer(fontFileBuffer)
} catch {}
}
if (!cachedFontRequest) {
fontCache.set(googleFontFileUrl, fontFileBuffer)
} else {
Expand Down Expand Up @@ -121,19 +131,13 @@ const downloadGoogleFonts: FontLoader = async ({

// Add fallback font
let adjustFontFallbackMetrics: AdjustFontFallback | undefined
if (adjustFontFallback) {
if (adjustFontFallback && latinFont) {
try {
const { ascent, descent, lineGap, fallbackFont, sizeAdjust } =
calculateSizeAdjustValues(
require('next/dist/server/google-font-metrics.json')[fontFamily]
)
adjustFontFallbackMetrics = {
fallbackFont,
ascentOverride: `${ascent}%`,
descentOverride: `${descent}%`,
lineGapOverride: `${lineGap}%`,
sizeAdjust: `${sizeAdjust}%`,
}
adjustFontFallbackMetrics = calculateFallbackFontValues(
latinFont,
require('next/dist/server/google-font-metrics.json')[fontFamily]
.category
)
} catch {
Log.error(
`Failed to find font override values for font \`${fontFamily}\``
Expand Down
4 changes: 4 additions & 0 deletions packages/font/src/google/utils.ts
@@ -1,3 +1,4 @@
import fs from 'fs'
// @ts-ignore
import fetch from 'next/dist/compiled/node-fetch'
import fontData from './font-data.json'
Expand Down Expand Up @@ -161,6 +162,9 @@ export async function fetchCSSFromGoogleFonts(url: string, fontFamily: string) {

export async function fetchFontFile(url: string) {
if (process.env.NEXT_FONT_GOOGLE_MOCKED_RESPONSES) {
if (url.startsWith('/')) {
return fs.readFileSync(url)
}
return Buffer.from(url)
}
const arrayBuffer = await fetch(url).then((r: any) => r.arrayBuffer())
Expand Down
32 changes: 6 additions & 26 deletions packages/font/src/local/loader.ts
@@ -1,12 +1,11 @@
// @ts-ignore
import { calculateSizeAdjustValues } from 'next/dist/server/font-utils'
// @ts-ignore
// eslint-disable-next-line import/no-extraneous-dependencies
import fontFromBuffer from '@next/font/dist/fontkit'
import type { AdjustFontFallback, FontLoader } from 'next/font'

import { promisify } from 'util'
import { calcAzWidth, validateData } from './utils'
import { validateData } from './utils'
import { calculateFallbackFontValues } from '../utils'

const fetchFonts: FontLoader = async ({
functionName,
Expand Down Expand Up @@ -44,29 +43,10 @@ const fetchFonts: FontLoader = async ({
// Add fallback font
let adjustFontFallbackMetrics: AdjustFontFallback | undefined
if (fontMetadata && adjustFontFallback !== false) {
const {
ascent,
descent,
lineGap,
fallbackFont,
sizeAdjust: fallbackSizeAdjust,
} = calculateSizeAdjustValues({
category:
adjustFontFallback === 'Times New Roman' ? 'serif' : 'sans-serif',
ascent: fontMetadata.ascent,
descent: fontMetadata.descent,
lineGap: fontMetadata.lineGap,
unitsPerEm: fontMetadata.unitsPerEm,
xAvgCharWidth: (fontMetadata as any)['OS/2']?.xAvgCharWidth,
azAvgWidth: calcAzWidth(fontMetadata),
})
adjustFontFallbackMetrics = {
fallbackFont,
ascentOverride: `${ascent}%`,
descentOverride: `${descent}%`,
lineGapOverride: `${lineGap}%`,
sizeAdjust: `${fallbackSizeAdjust}%`,
}
adjustFontFallbackMetrics = calculateFallbackFontValues(
fontMetadata,
adjustFontFallback === 'Times New Roman' ? 'serif' : 'sans-serif'
)
}

const fontFaceProperties = [
Expand Down
11 changes: 0 additions & 11 deletions packages/font/src/local/utils.ts
@@ -1,5 +1,3 @@
import type { Font } from 'fontkit'

const allowedDisplayValues = ['auto', 'block', 'swap', 'fallback', 'optional']

const formatValues = (values: string[]) =>
Expand Down Expand Up @@ -93,12 +91,3 @@ export function validateData(functionName: string, data: any): FontOptions {
declarations,
}
}

// Calculating the a-z average width
export function calcAzWidth(font: Font) {
const widths = font
.glyphsForString('abcdefghijklmnopqrstuvwxyz')
.map((glyph) => glyph.advanceWidth)
const totalWidth = widths.reduce((sum, width) => sum + width, 0)
return totalWidth / widths.length
}
57 changes: 57 additions & 0 deletions packages/font/src/utils.ts
@@ -0,0 +1,57 @@
import type { Font } from 'fontkit'
import type { AdjustFontFallback } from 'next/font'

const DEFAULT_SANS_SERIF_FONT = {
name: 'Arial',
azAvgWidth: 934.5116279069767,
unitsPerEm: 2048,
}
const DEFAULT_SERIF_FONT = {
name: 'Times New Roman',
azAvgWidth: 854.3953488372093,
unitsPerEm: 2048,
}

function calcAverageWidth(font: Font): number | undefined {
const avgCharacters = 'aaabcdeeeefghiijklmnnoopqrrssttuvwxyz '
const hasAllChars = font
.glyphsForString(avgCharacters)
.flatMap((glyph) => glyph.codePoints)
.every((codePoint) => font.hasGlyphForCodePoint(codePoint))

if (!hasAllChars) return undefined

const widths = font
.glyphsForString(avgCharacters)
.map((glyph) => glyph.advanceWidth)
const totalWidth = widths.reduce((sum, width) => sum + width, 0)
return totalWidth / widths.length
}

function formatOverrideValue(val: number) {
return Math.abs(val * 100).toFixed(2) + '%'
}

export function calculateFallbackFontValues(
font: Font,
category = 'serif'
): AdjustFontFallback {
const fallbackFont =
category === 'serif' ? DEFAULT_SERIF_FONT : DEFAULT_SANS_SERIF_FONT

const azAvgWidth = calcAverageWidth(font)
const { ascent, descent, lineGap, unitsPerEm } = font

const fallbackFontAvgWidth = fallbackFont.azAvgWidth / fallbackFont.unitsPerEm
let sizeAdjust = azAvgWidth
? azAvgWidth / unitsPerEm / fallbackFontAvgWidth
: 1

return {
ascentOverride: formatOverrideValue(ascent / (unitsPerEm * sizeAdjust)),
descentOverride: formatOverrideValue(descent / (unitsPerEm * sizeAdjust)),
lineGapOverride: formatOverrideValue(lineGap / (unitsPerEm * sizeAdjust)),
fallbackFont: fallbackFont.name,
sizeAdjust: formatOverrideValue(sizeAdjust),
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
22 changes: 18 additions & 4 deletions test/e2e/next-font/google-font-mocked-responses.js
@@ -1,3 +1,5 @@
const path = require('path')

module.exports = {
'https://fonts.googleapis.com/css2?family=Open+Sans:wght@300..800&display=optional': `
/* cyrillic-ext */
Expand Down Expand Up @@ -499,7 +501,10 @@ module.exports = {
font-style: normal;
font-weight: 400;
font-display: optional;
src: url(https://fonts.gstatic.com/s/fraunces/v24/6NUh8FyLNQOQZAnv9bYEvDiIdE9Ea92uemAk_WBq8U_9v0c2Wa0K7iN7hzFUPJH58nib1603gg7S2nfgRYIctxuTBv7Tp05GNyXkb24.woff2) format('woff2');
src: url(${path.join(
__dirname,
'./fonts/6NUh8FyLNQOQZAnv9bYEvDiIdE9Ea92uemAk_WBq8U_9v0c2Wa0K7iN7hzFUPJH58nib1603gg7S2nfgRYIctxuTBv7Tp05GNyXkb24.woff2'
)}) format('woff2');
unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB;
}
/* latin-ext */
Expand All @@ -508,7 +513,10 @@ module.exports = {
font-style: normal;
font-weight: 400;
font-display: optional;
src: url(https://fonts.gstatic.com/s/fraunces/v24/6NUh8FyLNQOQZAnv9bYEvDiIdE9Ea92uemAk_WBq8U_9v0c2Wa0K7iN7hzFUPJH58nib1603gg7S2nfgRYIctxuTB_7Tp05GNyXkb24.woff2) format('woff2');
src: url(${path.join(
__dirname,
'./fonts/6NUh8FyLNQOQZAnv9bYEvDiIdE9Ea92uemAk_WBq8U_9v0c2Wa0K7iN7hzFUPJH58nib1603gg7S2nfgRYIctxuTB_7Tp05GNyXkb24.woff2'
)}) format('woff2');
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
/* latin */
Expand All @@ -517,7 +525,10 @@ module.exports = {
font-style: normal;
font-weight: 400;
font-display: optional;
src: url(https://fonts.gstatic.com/s/fraunces/v24/6NUh8FyLNQOQZAnv9bYEvDiIdE9Ea92uemAk_WBq8U_9v0c2Wa0K7iN7hzFUPJH58nib1603gg7S2nfgRYIctxuTCf7Tp05GNyXk.woff2) format('woff2');
src: url(${path.join(
__dirname,
'./fonts/6NUh8FyLNQOQZAnv9bYEvDiIdE9Ea92uemAk_WBq8U_9v0c2Wa0K7iN7hzFUPJH58nib1603gg7S2nfgRYIctxuTCf7Tp05GNyXk.woff2'
)}) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}`,
'https://fonts.googleapis.com/css2?family=Indie+Flower:wght@400&display=optional': `/* latin */
Expand All @@ -526,7 +537,10 @@ module.exports = {
font-style: normal;
font-weight: 400;
font-display: optional;
src: url(https://fonts.gstatic.com/s/indieflower/v17/m8JVjfNVeKWVnh3QMuKkFcZVaUuH99GUDg.woff2) format('woff2');
src: url(${path.join(
__dirname,
'./fonts/m8JVjfNVeKWVnh3QMuKkFcZVaUuH99GUDg.woff2'
)}) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}`,
}