Skip to content

Commit

Permalink
Move the missing glyph detection to a function
Browse files Browse the repository at this point in the history
  • Loading branch information
papandreou committed Aug 5, 2022
1 parent 0513770 commit 38bf36e
Showing 1 changed file with 91 additions and 88 deletions.
179 changes: 91 additions & 88 deletions lib/subsetFonts.js
Original file line number Diff line number Diff line change
Expand Up @@ -574,6 +574,96 @@ function cssAssetIsEmpty(cssAsset) {
);
}

function warnAboutMissingGlyphs(htmlOrSvgAssetTextsWithProps, assetGraph) {
const missingGlyphsErrors = [];

for (const {
htmlOrSvgAsset,
fontUsages,
accumulatedFontFaceDeclarations,
} of htmlOrSvgAssetTextsWithProps) {
for (const fontUsage of fontUsages) {
if (fontUsage.subsets) {
const characterSet = fontkit.create(
Object.values(fontUsage.subsets)[0]
).characterSet;

let missedAny = false;
for (const char of [...fontUsage.pageText]) {
// Turns out that browsers don't mind that these are missing:
if (char === '\t' || char === '\n') {
continue;
}

const codePoint = char.codePointAt(0);

const isMissing = !characterSet.includes(codePoint);

if (isMissing) {
let location;
const charIdx = htmlOrSvgAsset.text.indexOf(char);

if (charIdx === -1) {
location = `${htmlOrSvgAsset.urlOrDescription} (generated content)`;
} else {
const position = new LinesAndColumns(
htmlOrSvgAsset.text
).locationForIndex(charIdx);
location = `${htmlOrSvgAsset.urlOrDescription}:${
position.line + 1
}:${position.column + 1}`;
}

missingGlyphsErrors.push({
codePoint,
char,
htmlOrSvgAsset,
fontUsage,
location,
});
missedAny = true;
}
}
if (missedAny) {
const fontFaces = accumulatedFontFaceDeclarations.filter((fontFace) =>
fontUsage.fontFamilies.has(fontFace['font-family'])
);
for (const fontFace of fontFaces) {
const cssFontFaceSrc = fontFace.relations[0];
const fontFaceDeclaration = cssFontFaceSrc.node;
if (
!fontFaceDeclaration.some((node) => node.prop === 'unicode-range')
) {
fontFaceDeclaration.append({
prop: 'unicode-range',
value: unicodeRange(fontUsage.codepoints.original),
});
cssFontFaceSrc.from.markDirty();
}
}
}
}
}
}

if (missingGlyphsErrors.length) {
const errorLog = missingGlyphsErrors.map(
({ char, fontUsage, location }) =>
`- \\u{${char.codePointAt(0).toString(16)}} (${char}) in font-family '${
fontUsage.props['font-family']
}' (${fontUsage.props['font-weight']}/${
fontUsage.props['font-style']
}) at ${location}`
);

const message = `Missing glyph fallback detected.
When your primary webfont doesn't contain the glyphs you use, browsers that don't support unicode-range will load your fallback fonts, which will be a potential waste of bandwidth.
These glyphs are used on your site, but they don't exist in the font you applied to them:`;

assetGraph.info(new Error(`${message}\n${errorLog.join('\n')}`));
}
}

async function subsetFonts(
assetGraph,
{
Expand Down Expand Up @@ -844,94 +934,7 @@ async function subsetFonts(
formats
);

// Warn about missing glyphs
const missingGlyphsErrors = [];

for (const {
htmlOrSvgAsset,
fontUsages,
accumulatedFontFaceDeclarations,
} of htmlOrSvgAssetTextsWithProps) {
for (const fontUsage of fontUsages) {
if (fontUsage.subsets) {
const characterSet = fontkit.create(
Object.values(fontUsage.subsets)[0]
).characterSet;

let missedAny = false;
for (const char of [...fontUsage.pageText]) {
// Turns out that browsers don't mind that these are missing:
if (char === '\t' || char === '\n') {
continue;
}

const codePoint = char.codePointAt(0);

const isMissing = !characterSet.includes(codePoint);

if (isMissing) {
let location;
const charIdx = htmlOrSvgAsset.text.indexOf(char);

if (charIdx === -1) {
location = `${htmlOrSvgAsset.urlOrDescription} (generated content)`;
} else {
const position = new LinesAndColumns(
htmlOrSvgAsset.text
).locationForIndex(charIdx);
location = `${htmlOrSvgAsset.urlOrDescription}:${
position.line + 1
}:${position.column + 1}`;
}

missingGlyphsErrors.push({
codePoint,
char,
htmlOrSvgAsset,
fontUsage,
location,
});
missedAny = true;
}
}
if (missedAny) {
const fontFaces = accumulatedFontFaceDeclarations.filter((fontFace) =>
fontUsage.fontFamilies.has(fontFace['font-family'])
);
for (const fontFace of fontFaces) {
const cssFontFaceSrc = fontFace.relations[0];
const fontFaceDeclaration = cssFontFaceSrc.node;
if (
!fontFaceDeclaration.some((node) => node.prop === 'unicode-range')
) {
fontFaceDeclaration.append({
prop: 'unicode-range',
value: unicodeRange(fontUsage.codepoints.original),
});
cssFontFaceSrc.from.markDirty();
}
}
}
}
}
}

if (missingGlyphsErrors.length) {
const errorLog = missingGlyphsErrors.map(
({ char, fontUsage, location }) =>
`- \\u{${char.codePointAt(0).toString(16)}} (${char}) in font-family '${
fontUsage.props['font-family']
}' (${fontUsage.props['font-weight']}/${
fontUsage.props['font-style']
}) at ${location}`
);

const message = `Missing glyph fallback detected.
When your primary webfont doesn't contain the glyphs you use, browsers that don't support unicode-range will load your fallback fonts, which will be a potential waste of bandwidth.
These glyphs are used on your site, but they don't exist in the font you applied to them:`;

assetGraph.info(new Error(`${message}\n${errorLog.join('\n')}`));
}
warnAboutMissingGlyphs(htmlOrSvgAssetTextsWithProps, assetGraph);

// Insert subsets:

Expand Down

0 comments on commit 38bf36e

Please sign in to comment.