Skip to content

Commit

Permalink
Add some utilities for detecting and converting fonts
Browse files Browse the repository at this point in the history
  • Loading branch information
papandreou committed Jul 16, 2020
1 parent 425daa7 commit d8cf34d
Show file tree
Hide file tree
Showing 5 changed files with 269 additions and 11 deletions.
36 changes: 36 additions & 0 deletions lib/convertFontBuffer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
const wawoff2 = require('wawoff2');
const woffTool = require('woff2sfnt-sfnt2woff');

const supportedFormats = new Set(['truetype', 'woff', 'woff2']);
const detectFontFormat = require('./detectFontFormat');

async function convertFontBuffer(buffer, toFormat, fromFormat) {
if (!supportedFormats.has(toFormat)) {
throw new Error(`Cannot convert to ${toFormat}`);
}
if (fromFormat) {
if (!supportedFormats.has(fromFormat)) {
throw new Error(`Cannot convert from ${toFormat}`);
}
} else {
fromFormat = detectFontFormat(buffer);
}

if (fromFormat === toFormat) {
return buffer;
}
if (fromFormat === 'woff') {
buffer = woffTool.toSfnt(buffer);
} else if (fromFormat === 'woff2') {
buffer = Buffer.from(await wawoff2.decompress(buffer));
}

if (toFormat === 'woff') {
buffer = woffTool.toWoff(buffer);
} else if (toFormat === 'woff2') {
buffer = Buffer.from(await wawoff2.compress(buffer));
}
return buffer;
}

module.exports = convertFontBuffer;
18 changes: 18 additions & 0 deletions lib/detectFontFormat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
function detectFontFormat(buffer) {
const signature = buffer.toString('ascii', 0, 4);
if (signature === 'wOFF') {
return 'woff';
} else if (signature === 'wOF2') {
return 'woff2';
} else if (
signature === 'true' ||
signature === 'OTTO' ||
signature === '\x00\x01\x00\x00'
) {
return 'truetype';
} else {
throw new Error(`Unrecognized font signature: ${signature}`);
}
}

module.exports = detectFontFormat;
19 changes: 8 additions & 11 deletions lib/subsetFonts.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@ const unquote = require('./unquote');
const normalizeFontPropertyValue = require('./normalizeFontPropertyValue');
const getCssRulesByProperty = require('./getCssRulesByProperty');
const unicodeRange = require('./unicodeRange');

const wawoff2 = require('wawoff2');
const woffTool = require('woff2sfnt-sfnt2woff');
const convertFontBuffer = require('./convertFontBuffer');

const googleFontsCssUrlRegex = /^(?:https?:)?\/\/fonts\.googleapis\.com\/css/;

Expand Down Expand Up @@ -439,15 +437,14 @@ async function getSubsetsForFontUsage(
fontUsage.subsets = {};
}

let rawSrc = fontAsset.rawSrc;
if (format === 'woff') {
rawSrc = woffTool.toWoff(rawSrc);
} else if (format === 'woff2') {
rawSrc = Buffer.from(await wawoff2.compress(rawSrc));
}
const buffer = await convertFontBuffer(
fontAsset.rawSrc,
format,
'truetype'
);

fontUsage.subsets[format] = rawSrc;
const size = rawSrc.length;
fontUsage.subsets[format] = buffer;
const size = buffer.length;
if (
!fontUsage.smallestSubsetSize ||
size < fontUsage.smallestSubsetSize
Expand Down
153 changes: 153 additions & 0 deletions test/convertFontBuffer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
const convertFontBuffer = require('../lib/convertFontBuffer');
const detectFontFormat = require('../lib/detectFontFormat');
const fs = require('fs').promises;
const pathModule = require('path');
const expect = require('unexpected').clone();

describe('convertFontBuffer', function () {
before(async function () {
this.truetype = await fs.readFile(
pathModule.resolve(
__dirname,
'..',
'testdata',
'subsetFonts',
'Roboto-400.ttf'
)
);
this.woff = await fs.readFile(
pathModule.resolve(
__dirname,
'..',
'testdata',
'subsetFonts',
'Roboto-400.woff'
)
);
this.woff2 = await fs.readFile(
pathModule.resolve(
__dirname,
'..',
'testdata',
'subsetFonts',
'Roboto-400.woff2'
)
);
});

describe('when the source format is not given', function () {
it('should throw if the source format could not be detected', async function () {
expect(
() => convertFontBuffer(Buffer.from('abcd'), 'truetype'),
'to error',
'Unrecognized font signature: abcd'
);
});

it('should convert a truetype font to truetype', async function () {
const buffer = await convertFontBuffer(this.truetype, 'truetype');
expect(detectFontFormat(buffer), 'to equal', 'truetype');
expect(buffer, 'to be', this.truetype); // Should be a noop
});

it('should convert a truetype font to woff', async function () {
const buffer = await convertFontBuffer(this.truetype, 'woff');
expect(detectFontFormat(buffer), 'to equal', 'woff');
});

it('should convert a truetype font to woff2', async function () {
const buffer = await convertFontBuffer(this.truetype, 'woff2');
expect(detectFontFormat(buffer), 'to equal', 'woff2');
});

it('should convert a woff font to truetype', async function () {
const buffer = await convertFontBuffer(this.woff, 'truetype');
expect(detectFontFormat(buffer), 'to equal', 'truetype');
});

it('should convert a woff font to woff', async function () {
const buffer = await convertFontBuffer(this.woff, 'woff');
expect(detectFontFormat(buffer), 'to equal', 'woff');
expect(buffer, 'to be', this.woff); // Should be a noop
});

it('should convert a woff font to woff2', async function () {
const buffer = await convertFontBuffer(this.woff, 'woff2');
expect(detectFontFormat(buffer), 'to equal', 'woff2');
});

it('should convert a woff2 font to truetype', async function () {
const buffer = await convertFontBuffer(this.woff2, 'truetype');
expect(detectFontFormat(buffer), 'to equal', 'truetype');
});

it('should convert a woff2 font to woff', async function () {
const buffer = await convertFontBuffer(this.woff2, 'woff');
expect(detectFontFormat(buffer), 'to equal', 'woff');
});

it('should convert a woff2 font to woff2', async function () {
const buffer = await convertFontBuffer(this.woff2, 'woff2');
expect(detectFontFormat(buffer), 'to equal', 'woff2');
expect(buffer, 'to be', this.woff2); // Should be a noop
});
});

describe('when the source format is given', function () {
it('should convert a truetype font to truetype', async function () {
const buffer = await convertFontBuffer(
this.truetype,
'truetype',
'truetype'
);
expect(detectFontFormat(buffer), 'to equal', 'truetype');
expect(buffer, 'to be', this.truetype); // Should be a noop
});

it('should convert a truetype font to woff', async function () {
const buffer = await convertFontBuffer(this.truetype, 'woff', 'truetype');
expect(detectFontFormat(buffer), 'to equal', 'woff');
});

it('should convert a truetype font to woff2', async function () {
const buffer = await convertFontBuffer(
this.truetype,
'woff2',
'truetype'
);
expect(detectFontFormat(buffer), 'to equal', 'woff2');
});

it('should convert a woff font to truetype', async function () {
const buffer = await convertFontBuffer(this.woff, 'truetype', 'woff');
expect(detectFontFormat(buffer), 'to equal', 'truetype');
});

it('should convert a woff font to woff', async function () {
const buffer = await convertFontBuffer(this.woff, 'woff', 'woff');
expect(detectFontFormat(buffer), 'to equal', 'woff');
expect(buffer, 'to be', this.woff); // Should be a noop
});

it('should convert a woff font to woff2', async function () {
const buffer = await convertFontBuffer(this.woff, 'woff2', 'woff');
expect(detectFontFormat(buffer), 'to equal', 'woff2');
});

it('should convert a woff2 font to truetype', async function () {
const buffer = await convertFontBuffer(this.woff2, 'truetype', 'woff2');
expect(detectFontFormat(buffer), 'to equal', 'truetype');
});

it('should convert a woff2 font to woff', async function () {
const buffer = await convertFontBuffer(this.woff2, 'woff', 'woff2');
expect(detectFontFormat(buffer), 'to equal', 'woff');
});

it('should convert a woff2 font to woff2', async function () {
const buffer = await convertFontBuffer(this.woff2, 'woff2', 'woff2');
expect(detectFontFormat(buffer), 'to equal', 'woff2');
expect(buffer, 'to be', this.woff2); // Should be a noop
});
});
});
54 changes: 54 additions & 0 deletions test/detectFontFormat.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
const convertFontBuffer = require('../lib/convertFontBuffer');
const detectFontFormat = require('../lib/detectFontFormat');
const fs = require('fs').promises;
const pathModule = require('path');
const expect = require('unexpected').clone();

describe('detectFontFormat', function () {
it('should throw if the contents of the buffer could not be recognized', async function () {
expect(
() => convertFontBuffer(Buffer.from('abcd'), 'truetype'),
'to error',
'Unrecognized font signature: abcd'
);
});

it('should detect a truetype font', async function () {
const buffer = await fs.readFile(
pathModule.resolve(
__dirname,
'..',
'testdata',
'subsetFonts',
'Roboto-400.ttf'
)
);
expect(detectFontFormat(buffer), 'to equal', 'truetype');
});

it('should detect a woff font', async function () {
const buffer = await fs.readFile(
pathModule.resolve(
__dirname,
'..',
'testdata',
'subsetFonts',
'Roboto-400.woff'
)
);
expect(detectFontFormat(buffer), 'to equal', 'woff');
});

it('should detect a woff2 font', async function () {
const buffer = await fs.readFile(
pathModule.resolve(
__dirname,
'..',
'testdata',
'subsetFonts',
'Roboto-400.woff2'
)
);
expect(detectFontFormat(buffer), 'to equal', 'woff2');
});
});

0 comments on commit d8cf34d

Please sign in to comment.