Skip to content

Commit

Permalink
Always use harfbuzz for subsetting, remove fonttools support
Browse files Browse the repository at this point in the history
  • Loading branch information
papandreou committed Apr 18, 2021
1 parent 34bfb79 commit a8c62f1
Show file tree
Hide file tree
Showing 7 changed files with 2,810 additions and 3,803 deletions.
10 changes: 2 additions & 8 deletions lib/parseCommandLineOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,6 @@ module.exports = function parseCommandLineOptions(argv) {
type: 'boolean',
default: false,
})
.options('harfbuzz', {
type: 'boolean',
describe:
'Experimental: Use the harfbuzz subsetter instead of pyftsubset. Requires node.js 10+ for wasm support',
default: false,
})
.options('silent', {
alias: 's',
describe: `Do not write anything to stdout`,
Expand Down Expand Up @@ -121,8 +115,8 @@ module.exports = function parseCommandLineOptions(argv) {
if (inlineFonts !== undefined) {
return '--[no-]inline-fonts is no longer supported as of subfont 6.0.0';
}
if (harfbuzz && parseInt(process.versions.node) < 10) {
return 'The --harfbuzz option requires node.js 10 or above';
if (harfbuzz !== undefined) {
return '--[no-]harfbuzz is no longer supported as of subfont 7.0.0 (harfbuzz is always used now)';
} else {
return true;
}
Expand Down
2 changes: 0 additions & 2 deletions lib/subfont.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@ module.exports = async function subfont(
relativeUrls = false,
fallbacks = true,
dynamic = false,
harfbuzz = false,
browsers,
},
console
Expand Down Expand Up @@ -232,7 +231,6 @@ module.exports = async function subfont(
jsPreload,
omitFallbacks: !fallbacks,
hrefType: relativeUrls ? 'relative' : 'rootRelative',
harfbuzz,
dynamic,
console,
});
Expand Down
213 changes: 52 additions & 161 deletions lib/subsetFonts.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const urltools = require('urltools');
const fontTracer = require('font-tracer');
const fontSnapper = require('font-snapper');
const fontverter = require('fontverter');
const subsetFont = require('subset-font');

const AssetGraph = require('assetgraph');
const compileQuery = require('assetgraph/lib/compileQuery');
Expand Down Expand Up @@ -290,19 +291,8 @@ function getSubsetPromiseId(fontUsage, format) {
async function getSubsetsForFontUsage(
assetGraph,
htmlAssetTextsWithProps,
formats,
harfbuzz
formats
) {
let subsetLocalFont;

if (harfbuzz) {
subsetLocalFont = require('subset-font');
} else {
try {
subsetLocalFont = require('./subsetLocalFont');
} catch (err) {}
}

const allFonts = [];

for (const item of htmlAssetTextsWithProps) {
Expand All @@ -317,161 +307,68 @@ async function getSubsetsForFontUsage(
}
}

if (subsetLocalFont) {
await assetGraph.populate({
followRelations: {
to: { url: { $or: allFonts } },
},
});

const originalFontBuffers = allFonts.reduce((result, fontUrl) => {
const fontAsset = assetGraph.findAssets({
url: fontUrl,
isLoaded: true,
})[0];

if (fontAsset) {
result[fontUrl] = fontAsset.rawSrc;
}

return result;
}, {});

const subsetPromiseMap = {};

for (const item of htmlAssetTextsWithProps) {
for (const fontUsage of item.fontUsages) {
const fontBuffer = originalFontBuffers[fontUsage.fontUrl];
const text = fontUsage.text;
for (const targetFormat of formats) {
const promiseId = getSubsetPromiseId(fontUsage, targetFormat);

if (!subsetPromiseMap[promiseId]) {
subsetPromiseMap[promiseId] = subsetLocalFont(fontBuffer, text, {
targetFormat,
}).catch((err) => {
const error = new Error(err.message);
error.asset = assetGraph.findAssets({
url: fontUsage.fontUrl,
})[0];
await assetGraph.populate({
followRelations: {
to: { url: { $or: allFonts } },
},
});

assetGraph.warn(error);
});
}
const originalFontBuffers = allFonts.reduce((result, fontUrl) => {
const fontAsset = assetGraph.findAssets({
url: fontUrl,
isLoaded: true,
})[0];

subsetPromiseMap[promiseId].then((fontBuffer) => {
if (fontBuffer) {
if (!fontUsage.subsets) {
fontUsage.subsets = {};
}
fontUsage.subsets[targetFormat] = fontBuffer;
const size = fontBuffer.length;
if (
!fontUsage.smallestSubsetSize ||
size < fontUsage.smallestSubsetSize
) {
fontUsage.smallestSubsetSize = size;
fontUsage.smallestSubsetFormat = targetFormat;
}
}
});
}
}
if (fontAsset) {
result[fontUrl] = fontAsset.rawSrc;
}

await Promise.all(Object.values(subsetPromiseMap));
} else {
const localFonts = [];
const fontCssUrlMap = {};

for (const item of htmlAssetTextsWithProps) {
for (const fontUsage of item.fontUsages) {
if (!fontUsage.fontUrl) {
continue;
}
return result;
}, {});

const fontAsset = assetGraph.findAssets({ url: fontUsage.fontUrl })[0];
const subsetPromiseMap = {};

if (fontAsset.hostname !== 'fonts.gstatic.com') {
localFonts.push(fontAsset);
continue;
}

const mapId = getSubsetPromiseId(fontUsage, 'truetype');

if (!fontCssUrlMap[mapId]) {
fontCssUrlMap[mapId] = `${getGoogleFontSubsetCssUrl(
fontUsage.props,
fontUsage.text
)}`;
for (const item of htmlAssetTextsWithProps) {
for (const fontUsage of item.fontUsages) {
const fontBuffer = originalFontBuffers[fontUsage.fontUrl];
const text = fontUsage.text;
for (const targetFormat of formats) {
const promiseId = getSubsetPromiseId(fontUsage, targetFormat);

if (!subsetPromiseMap[promiseId]) {
subsetPromiseMap[promiseId] = subsetFont(fontBuffer, text, {
targetFormat,
}).catch((err) => {
const error = new Error(err.message);
error.asset = assetGraph.findAssets({
url: fontUsage.fontUrl,
})[0];

assetGraph.warn(error);
});
}
}
}

if (localFonts.length > 0) {
const localFontDescriptions = localFonts
.map((a) => ` - ${a.urlOrDescription}`)
.join('\n');
assetGraph.info(
new Error(
`Local subsetting is not possible because fonttools are not installed. Falling back to only subsetting Google Fonts. Run \`pip install fonttools brotli zopfli\` to enable local font subsetting`
)
);
assetGraph.info(
new Error(`Unoptimised fonts:\n${localFontDescriptions}`)
);
}

const assetGraphForLoadingFonts = new AssetGraph();

const formatUrls = _.uniq(Object.values(fontCssUrlMap));
await assetGraphForLoadingFonts.loadAssets(Object.values(formatUrls));

await assetGraphForLoadingFonts.populate({
followRelations: {
type: 'CssFontFaceSrc',
},
});

for (const item of htmlAssetTextsWithProps) {
for (const fontUsage of item.fontUsages) {
for (const format of formats) {
const cssUrl =
fontCssUrlMap[getSubsetPromiseId(fontUsage, 'truetype')];
const cssAsset = assetGraphForLoadingFonts.findAssets({
url: cssUrl,
isLoaded: true,
})[0];
if (cssAsset) {
const fontRelation = cssAsset.outgoingRelations[0];
const fontAsset = fontRelation.to;

if (fontAsset.isLoaded) {
if (!fontUsage.subsets) {
fontUsage.subsets = {};
}

const buffer = await fontverter.convert(
fontAsset.rawSrc,
format,
'truetype'
);

fontUsage.subsets[format] = buffer;
const size = buffer.length;
if (
!fontUsage.smallestSubsetSize ||
size < fontUsage.smallestSubsetSize
) {
fontUsage.smallestSubsetSize = size;
fontUsage.smallestSubsetFormat = format;
}
subsetPromiseMap[promiseId].then((fontBuffer) => {
if (fontBuffer) {
if (!fontUsage.subsets) {
fontUsage.subsets = {};
}
fontUsage.subsets[targetFormat] = fontBuffer;
const size = fontBuffer.length;
if (
!fontUsage.smallestSubsetSize ||
size < fontUsage.smallestSubsetSize
) {
fontUsage.smallestSubsetSize = size;
fontUsage.smallestSubsetFormat = targetFormat;
}
}
}
});
}
}
}

await Promise.all(Object.values(subsetPromiseMap));
}

const fontOrder = ['woff2', 'woff', 'truetype'];
Expand Down Expand Up @@ -684,7 +581,6 @@ async function subsetFonts(
onlyInfo,
dynamic,
console = global.console,
harfbuzz,
} = {}
) {
if (!validFontDisplayValues.includes(fontDisplay)) {
Expand Down Expand Up @@ -913,12 +809,7 @@ async function subsetFonts(
}

// Generate subsets:
await getSubsetsForFontUsage(
assetGraph,
htmlAssetTextsWithProps,
formats,
harfbuzz
);
await getSubsetsForFontUsage(assetGraph, htmlAssetTextsWithProps, formats);

// Warn about missing glyphs
const missingGlyphsErrors = [];
Expand Down
90 changes: 0 additions & 90 deletions lib/subsetLocalFont.js

This file was deleted.

Loading

0 comments on commit a8c62f1

Please sign in to comment.