Skip to content

Commit

Permalink
Merge branch 'master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
Connum committed Mar 2, 2023
2 parents 1709c52 + 90bf641 commit e79850f
Show file tree
Hide file tree
Showing 17 changed files with 656 additions and 57 deletions.
2 changes: 1 addition & 1 deletion docs/font-inspector.html
Original file line number Diff line number Diff line change
Expand Up @@ -264,7 +264,7 @@ <h1>Free Software</h1>
reader.readAsArrayBuffer(file);
}

var fontFileName = 'fonts/Roboto-Black.ttf';
var fontFileName = 'fonts/FiraSansMedium.woff';

document.getElementById('font-name').innerHTML = fontFileName.split('/')[1];

Expand Down
2 changes: 1 addition & 1 deletion docs/glyph-inspector.html
Original file line number Diff line number Diff line change
Expand Up @@ -420,7 +420,7 @@ <h1>Free Software</h1>
}
}

var fontFileName = 'fonts/Roboto-Black.ttf';
var fontFileName = 'fonts/FiraSansMedium.woff';
document.getElementById('font-name').innerHTML = fontFileName.split('/')[1];

var fileButton = document.getElementById('file');
Expand Down
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@
"test": "npm run build && npm run dist && mocha --require reify --recursive && npm run lint",
"lint": "eslint src",
"lint-fix": "eslint src --fix",
"start": "esbuild --bundle src/opentype.js --outdir=dist --external:fs --target=es2018 --format=iife --out-extension:.js=.js --global-name=opentype --footer:js=\"(function (root, factory) {\nif (typeof define === 'function' && define.amd)define(factory);\nelse if (typeof module === 'object' && module.exports)module.exports = factory();\nelse root.opentype = factory();\n}(typeof self !== 'undefined' ? self : this, () => ({...opentype,'default':opentype})));\" --watch --servedir=. --footer:js=\"new EventSource('/esbuild').addEventListener('change', () => location.reload())\"",
"b:umd": "esbuild --bundle src/opentype.js --outdir=dist --external:fs --target=es2018 --format=iife --out-extension:.js=.js --global-name=opentype --footer:js=\"(function (root, factory) {\nif (typeof define === 'function' && define.amd)define(factory);\nelse if (typeof module === 'object' && module.exports)module.exports = factory();\nelse root.opentype = factory();\n}(typeof self !== 'undefined' ? self : this, () => ({...opentype,'default':opentype})));\"",
"d:umd": "esbuild --bundle src/opentype.js --outdir=dist --external:fs --target=es2018 --format=iife --out-extension:.js=.min.js --global-name=opentype --footer:js=\"(function (root, factory) {\nif (typeof define === 'function' && define.amd)define(factory);\nelse if (typeof module === 'object' && module.exports)module.exports = factory();\nelse root.opentype = factory();\n}(typeof self !== 'undefined' ? self : this, () => ({...opentype,'default':opentype})));\" --minify --sourcemap",
"start": "esbuild --bundle src/opentype.js --outdir=dist --external:fs --target=es2018 --format=iife --out-extension:.js=.js --global-name=opentype --footer:js=\"(function (root, factory) { if (typeof define === 'function' && define.amd)define(factory); else if (typeof module === 'object' && module.exports)module.exports = factory(); else root.opentype = factory(); }(typeof self !== 'undefined' ? self : this, () => ({...opentype,'default':opentype})));\" --watch --servedir=. --footer:js=\"new EventSource('/esbuild').addEventListener('change', () => location.reload())\"",
"b:umd": "esbuild --bundle src/opentype.js --outdir=dist --external:fs --target=es2018 --format=iife --out-extension:.js=.js --global-name=opentype --footer:js=\"(function (root, factory) { if (typeof define === 'function' && define.amd)define(factory); else if (typeof module === 'object' && module.exports)module.exports = factory(); else root.opentype = factory(); }(typeof self !== 'undefined' ? self : this, () => ({...opentype,'default':opentype})));\"",
"d:umd": "esbuild --bundle src/opentype.js --outdir=dist --external:fs --target=es2018 --format=iife --out-extension:.js=.min.js --global-name=opentype --footer:js=\"(function (root, factory) { if (typeof define === 'function' && define.amd)define(factory); else if (typeof module === 'object' && module.exports)module.exports = factory(); else root.opentype = factory(); }(typeof self !== 'undefined' ? self : this, () => ({...opentype,'default':opentype})));\" --minify --sourcemap",
"b:esm": "esbuild --bundle src/opentype.js --outdir=dist --external:fs --target=es2018 --format=esm --out-extension:.js=.module.js",
"d:esm": "esbuild --bundle src/opentype.js --outdir=dist --external:fs --target=es2018 --format=esm --out-extension:.js=.module.min.js --minify --sourcemap"
},
Expand Down
39 changes: 39 additions & 0 deletions src/opentype.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ import cpal from './tables/cpal.js';
import colr from './tables/colr.js';
import cmap from './tables/cmap.js';
import cff from './tables/cff.js';
import stat from './tables/stat.js';
import fvar from './tables/fvar.js';
import gvar from './tables/gvar.js';
import avar from './tables/avar.js';
import glyf from './tables/glyf.js';
import gdef from './tables/gdef.js';
import gpos from './tables/gpos.js';
Expand Down Expand Up @@ -212,6 +215,9 @@ function parseBuffer(buffer, opt={}) {

let cffTableEntry;
let fvarTableEntry;
let statTableEntry;
let gvarTableEntry;
let avarTableEntry;
let glyfTableEntry;
let gdefTableEntry;
let gposTableEntry;
Expand All @@ -227,6 +233,9 @@ function parseBuffer(buffer, opt={}) {
const tableEntry = tableEntries[i];
let table;
switch (tableEntry.tag) {
case 'avar':
avarTableEntry = tableEntry;
break;
case 'cmap':
table = uncompressTable(data, tableEntry);
font.tables.cmap = cmap.parse(table.data, table.offset);
Expand All @@ -240,6 +249,12 @@ function parseBuffer(buffer, opt={}) {
case 'fvar':
fvarTableEntry = tableEntry;
break;
case 'STAT':
statTableEntry = tableEntry;
break;
case 'gvar':
gvarTableEntry = tableEntry;
break;
case 'fpgm' :
table = uncompressTable(data, tableEntry);
p = new parse.Parser(table.data, table.offset);
Expand Down Expand Up @@ -371,6 +386,30 @@ function parseBuffer(buffer, opt={}) {
font.tables.fvar = fvar.parse(fvarTable.data, fvarTable.offset, font.names);
}

if (statTableEntry) {
const statTable = uncompressTable(data, statTableEntry);
font.tables.stat = stat.parse(statTable.data, statTable.offset, font.tables.fvar);
}

if (gvarTableEntry) {
if (!fvarTableEntry) {
console.warn('This font provides a gvar table, but no fvar table, which is required for variable fonts.');
}
if (!glyfTableEntry) {
console.warn('This font provides a gvar table, but no glyf table. Glyph variation only works with TrueType outlines.');
}
const gvarTable = uncompressTable(data, gvarTableEntry);
font.tables.gvar = gvar.parse(gvarTable.data, gvarTable.offset, font.names);
}

if (avarTableEntry) {
if (!fvarTableEntry) {
console.warn('This font provides an avar table, but no fvar table, which is required for variable fonts.');
}
const avarTable = uncompressTable(data, avarTableEntry);
font.tables.avar = avar.parse(avarTable.data, avarTable.offset, font.tables.fvar);
}

if (metaTableEntry) {
const metaTable = uncompressTable(data, metaTableEntry);
font.tables.meta = meta.parse(metaTable.data, metaTable.offset);
Expand Down
1 change: 1 addition & 0 deletions src/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,7 @@ Parser.uShort = Parser.offset16 = Parser.prototype.parseUShort;
Parser.uShortList = Parser.prototype.parseUShortList;
Parser.uLong = Parser.offset32 = Parser.prototype.parseULong;
Parser.uLongList = Parser.prototype.parseULongList;
Parser.fixed = Parser.prototype.parseFixed;
Parser.struct = Parser.prototype.parseStruct;
Parser.coverage = Parser.prototype.parseCoverage;
Parser.classDef = Parser.prototype.parseClassDef;
Expand Down
11 changes: 1 addition & 10 deletions src/substitution.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import check from './check.js';
import Layout from './layout.js';
import { arraysEqual } from './util.js';

/**
* @exports opentype.Substitution
Expand All @@ -15,16 +16,6 @@ function Substitution(font) {
Layout.call(this, font, 'gsub');
}

// Check if 2 arrays of primitives are equal.
function arraysEqual(ar1, ar2) {
const n = ar1.length;
if (n !== ar2.length) { return false; }
for (let i = 0; i < n; i++) {
if (ar1[i] !== ar2[i]) { return false; }
}
return true;
}

// Find the first subtable of a lookup table in a particular format.
function getSubstFormat(lookupTable, format, defaultSubtable) {
const subtables = lookupTable.subtables;
Expand Down
90 changes: 90 additions & 0 deletions src/tables/avar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// The `avar` table stores information on how to modify a variation along a variation axis
// https://learn.microsoft.com/en-us/typography/opentype/spec/avar

import check from '../check.js';
import { Parser } from '../parse.js';
import table from '../table.js';

function makeAvarAxisValueMap(n, axisValueMap) {
return new table.Record('axisValueMap_' + n, [
{name: 'fromCoordinate_' + n, type: 'F2DOT14', value: axisValueMap.fromCoordinate},
{name: 'toCoordinate_' + n, type: 'F2DOT14', value: axisValueMap.toCoordinate}
]);
}

function makeAvarSegmentMap(n, axis) {
const returnTable = new table.Record('segmentMap_' + n, [
{name: 'positionMapCount_' + n, type: 'USHORT', value: axis.axisValueMaps.length}
]);

let axisValueMaps = [];
for (let i = 0; i < axis.axisValueMaps.length; i++) {
const valueMap = makeAvarAxisValueMap(`${n}_${i}`, axis.axisValueMaps[i]);
axisValueMaps = axisValueMaps.concat(valueMap.fields);
}

returnTable.fields = returnTable.fields.concat(axisValueMaps);

return returnTable;
}

function makeAvarTable(avar, fvar) {
check.argument(avar.axisSegmentMaps.length === fvar.axes.length, 'avar axis count must correspond to fvar axis count');

const result = new table.Table('avar', [
{name: 'majorVersion', type: 'USHORT', value: 1},
{name: 'minorVersion', type: 'USHORT', value: 0},
{name: 'reserved', type: 'USHORT', value: 0},
{name: 'axisCount', type: 'USHORT', value: avar.axisSegmentMaps.length},
]);

for (let i = 0; i < avar.axisSegmentMaps.length; i++) {
const axisRecord = makeAvarSegmentMap(i, avar.axisSegmentMaps[i]);
result.fields = result.fields.concat(axisRecord.fields);
}

return result;
}

function parseAvarTable(data, start, fvar) {
if (!start) {
start = 0;
}

const p = new Parser(data, start);
const tableVersionMajor = p.parseUShort();
const tableVersionMinor = p.parseUShort();

if (tableVersionMajor !== 1) {
console.warn(`Unsupported avar table version ${tableVersionMajor}.${tableVersionMinor}`);
}

p.skip('uShort', 1); // reserved
const axisCount = p.parseUShort();

check.argument(axisCount === fvar.axes.length, 'avar axis count must correspond to fvar axis count');

const axisSegmentMaps = [];
for (let i = 0; i < axisCount; i++) {
const axisValueMaps = [];
const positionMapCount = p.parseUShort();
for (let j = 0; j < positionMapCount; j++) {
const fromCoordinate = p.parseF2Dot14();
const toCoordinate = p.parseF2Dot14();
axisValueMaps.push({
fromCoordinate,
toCoordinate
});
}
axisSegmentMaps.push({
axisValueMaps
});
}

return {
version: [tableVersionMajor, tableVersionMinor],
axisSegmentMaps
};
}

export default { make: makeAvarTable, parse: parseAvarTable };
34 changes: 18 additions & 16 deletions src/tables/fvar.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,28 @@
import check from '../check.js';
import parse from '../parse.js';
import table from '../table.js';
import { objectsEqual } from '../util.js';

function addName(name, names) {
const nameString = JSON.stringify(name);
let nameID = 256;
for (let nameKey in names) {
let n = parseInt(nameKey);
if (!n || n < 256) {
continue;
}

if (JSON.stringify(names[nameKey]) === nameString) {
return n;
}

if (nameID <= n) {
nameID = n + 1;
for (let platform in names) {
for (let nameKey in names[platform]) {
let n = parseInt(nameKey);
if (!n || n < 256) {
continue;
}

if (objectsEqual(names[platform][nameKey], name)) {
return n;
}

if (nameID <= n) {
nameID = n + 1;
}
}
names[platform][nameID] = name;
}

names[nameID] = name;
return nameID;
}

Expand All @@ -47,7 +49,7 @@ function parseFvarAxis(data, start, names) {
axis.defaultValue = p.parseFixed();
axis.maxValue = p.parseFixed();
p.skip('uShort', 1); // reserved for flags; no values defined
axis.name = names[p.parseUShort()] || {};
axis.name = (names.macintosh || names.windows || names.unicode)[p.parseUShort()] || {};
return axis;
}

Expand All @@ -73,7 +75,7 @@ function makeFvarInstance(n, inst, axes, names) {
function parseFvarInstance(data, start, axes, names) {
const inst = {};
const p = new parse.Parser(data, start);
inst.name = names[p.parseUShort()] || {};
inst.name = (names.macintosh || names.windows || names.unicode)[p.parseUShort()] || {};
p.skip('uShort', 1); // reserved for flags; no values defined

inst.coordinates = {};
Expand Down
16 changes: 16 additions & 0 deletions src/tables/gvar.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// The `gvar` table stores information on how to modify glyf outlines across the variation space
// https://learn.microsoft.com/en-us/typography/opentype/spec/gvar

// import check from '../check';
// import parse from '../parse';
// import table from '../table';

function makeGvarTable() {
console.warn('Writing of gvar tables is not yet supported.');
}

function parseGvarTable(/*data, start, names*/) {
console.warn('Parsing of gvar tables is not yet supported.');
}

export default { make: makeGvarTable, parse: parseGvarTable };
36 changes: 29 additions & 7 deletions src/tables/sfnt.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ import gsub from './gsub.js';
import meta from './meta.js';
import colr from './colr.js';
import cpal from './cpal.js';
import fvar from './fvar.js';
import stat from './stat.js';
import avar from './avar.js';

function log2(v) {
return Math.log(v) / Math.log(2) | 0;
Expand Down Expand Up @@ -313,6 +316,9 @@ function fontToSfntTable(font) {
names.windows.preferredSubfamily = fontNamesWindows.fontSubFamily || fontNamesUnicode.fontSubFamily || fontNamesMacintosh.fontSubFamily;
}

// we have to handle fvar before name, because it may modify name IDs
const fvarTable = font.tables.fvar ? fvar.make(font.tables.fvar, font.names) : undefined;

const languageTags = [];
const nameTable = _name.make(names, languageTags);
const ltagTable = (languageTags.length > 0 ? ltag.make(languageTags) : undefined);
Expand All @@ -335,16 +341,32 @@ function fontToSfntTable(font) {
if (ltagTable) {
tables.push(ltagTable);
}

// Optional tables
if (font.tables.gsub) {
tables.push(gsub.make(font.tables.gsub));
}
if (font.tables.cpal) {
tables.push(cpal.make(font.tables.cpal));
const optionalTables = {
gsub,
cpal,
colr,
stat,
avar
};

const optionalTableArgs = {
avar: [font.tables.fvar]
};

// fvar table is already handled above
if (fvarTable) {
tables.push(fvarTable);
}
if (font.tables.colr) {
tables.push(colr.make(font.tables.colr));

for (let tableName in optionalTables) {
const table = font.tables[tableName];
if (table) {
tables.push(optionalTables[tableName].make.call(font, table, ...(optionalTableArgs[tableName] || [])));
}
}

if (metaTable) {
tables.push(metaTable);
}
Expand Down
Loading

0 comments on commit e79850f

Please sign in to comment.