opentype.js
Advanced tools
Comparing version 0.6.1 to 0.6.2
{ | ||
"name": "opentype.js", | ||
"version": "0.6.1", | ||
"version": "0.6.2", | ||
"main": "dist/opentype.js", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
{ | ||
"name": "opentype.js", | ||
"description": "OpenType font parser", | ||
"version": "0.6.1", | ||
"version": "0.6.2", | ||
"author": { | ||
@@ -6,0 +6,0 @@ "name": "Frederik De Bleser", |
@@ -1,3 +0,7 @@ | ||
0.6.1 (February 20, 2015) | ||
0.6.2 (March 11, 2016) | ||
========================= | ||
* Improve table writing to support nested subtables. Thanks @fpirsch! | ||
0.6.1 (February 20, 2016) | ||
========================= | ||
* Left side bearing is now correctly reported. | ||
@@ -4,0 +8,0 @@ * Simplified code for including ascender / descender values. |
@@ -70,4 +70,6 @@ // opentype.js | ||
var tag = parse.getTag(data, p); | ||
var checksum = parse.getULong(data, p + 4); | ||
var offset = parse.getULong(data, p + 8); | ||
tableEntries.push({tag: tag, offset: offset, compression: false}); | ||
var length = parse.getULong(data, p + 12); | ||
tableEntries.push({tag: tag, checksum: checksum, offset: offset, length: length, compression: false}); | ||
p += 16; | ||
@@ -74,0 +76,0 @@ } |
@@ -5,3 +5,2 @@ // Table metadata | ||
var check = require('./check'); | ||
var encode = require('./types').encode; | ||
@@ -31,23 +30,2 @@ var sizeOf = require('./types').sizeOf; | ||
Table.prototype.sizeOf = function() { | ||
var v = 0; | ||
for (var i = 0; i < this.fields.length; i += 1) { | ||
var field = this.fields[i]; | ||
var value = this[field.name]; | ||
if (value === undefined) { | ||
value = field.value; | ||
} | ||
if (typeof value.sizeOf === 'function') { | ||
v += value.sizeOf(); | ||
} else { | ||
var sizeOfFunction = sizeOf[field.type]; | ||
check.assert(typeof sizeOfFunction === 'function', 'Could not find sizeOf function for field' + field.name); | ||
v += sizeOfFunction(value); | ||
} | ||
} | ||
return v; | ||
}; | ||
Table.prototype.encode = function() { | ||
@@ -57,2 +35,6 @@ return encode.TABLE(this); | ||
exports.Table = Table; | ||
Table.prototype.sizeOf = function() { | ||
return sizeOf.TABLE(this); | ||
}; | ||
exports.Record = exports.Table = Table; |
@@ -860,3 +860,3 @@ // The `CFF` table contains the glyph outlines in PostScript format. | ||
function makeHeader() { | ||
return new table.Table('Header', [ | ||
return new table.Record('Header', [ | ||
{name: 'major', type: 'Card8', value: 1}, | ||
@@ -870,3 +870,3 @@ {name: 'minor', type: 'Card8', value: 0}, | ||
function makeNameIndex(fontNames) { | ||
var t = new table.Table('Name INDEX', [ | ||
var t = new table.Record('Name INDEX', [ | ||
{name: 'names', type: 'INDEX', value: []} | ||
@@ -902,3 +902,3 @@ ]); | ||
function makeTopDict(attrs, strings) { | ||
var t = new table.Table('Top DICT', [ | ||
var t = new table.Record('Top DICT', [ | ||
{name: 'dict', type: 'DICT', value: {}} | ||
@@ -911,3 +911,3 @@ ]); | ||
function makeTopDictIndex(topDict) { | ||
var t = new table.Table('Top DICT INDEX', [ | ||
var t = new table.Record('Top DICT INDEX', [ | ||
{name: 'topDicts', type: 'INDEX', value: []} | ||
@@ -920,3 +920,3 @@ ]); | ||
function makeStringIndex(strings) { | ||
var t = new table.Table('String INDEX', [ | ||
var t = new table.Record('String INDEX', [ | ||
{name: 'strings', type: 'INDEX', value: []} | ||
@@ -934,3 +934,3 @@ ]); | ||
// Currently we don't use subroutines. | ||
return new table.Table('Global Subr INDEX', [ | ||
return new table.Record('Global Subr INDEX', [ | ||
{name: 'subrs', type: 'INDEX', value: []} | ||
@@ -941,3 +941,3 @@ ]); | ||
function makeCharsets(glyphNames, strings) { | ||
var t = new table.Table('Charsets', [ | ||
var t = new table.Record('Charsets', [ | ||
{name: 'format', type: 'Card8', value: 0} | ||
@@ -1024,3 +1024,3 @@ ]); | ||
function makeCharStringsIndex(glyphs) { | ||
var t = new table.Table('CharStrings INDEX', [ | ||
var t = new table.Record('CharStrings INDEX', [ | ||
{name: 'charStrings', type: 'INDEX', value: []} | ||
@@ -1039,3 +1039,3 @@ ]); | ||
function makePrivateDict(attrs, strings) { | ||
var t = new table.Table('Private DICT', [ | ||
var t = new table.Record('Private DICT', [ | ||
{name: 'dict', type: 'DICT', value: {}} | ||
@@ -1049,10 +1049,10 @@ ]); | ||
var t = new table.Table('CFF ', [ | ||
{name: 'header', type: 'TABLE'}, | ||
{name: 'nameIndex', type: 'TABLE'}, | ||
{name: 'topDictIndex', type: 'TABLE'}, | ||
{name: 'stringIndex', type: 'TABLE'}, | ||
{name: 'globalSubrIndex', type: 'TABLE'}, | ||
{name: 'charsets', type: 'TABLE'}, | ||
{name: 'charStringsIndex', type: 'TABLE'}, | ||
{name: 'privateDict', type: 'TABLE'} | ||
{name: 'header', type: 'RECORD'}, | ||
{name: 'nameIndex', type: 'RECORD'}, | ||
{name: 'topDictIndex', type: 'RECORD'}, | ||
{name: 'stringIndex', type: 'RECORD'}, | ||
{name: 'globalSubrIndex', type: 'RECORD'}, | ||
{name: 'charsets', type: 'RECORD'}, | ||
{name: 'charStringsIndex', type: 'RECORD'}, | ||
{name: 'privateDict', type: 'RECORD'} | ||
]); | ||
@@ -1059,0 +1059,0 @@ |
@@ -32,12 +32,12 @@ // The `fvar` table stores font variation axes and instances. | ||
function makeFvarAxis(axis, names) { | ||
function makeFvarAxis(n, axis, names) { | ||
var nameID = addName(axis.name, names); | ||
return new table.Table('fvarAxis', [ | ||
{name: 'tag', type: 'TAG', value: axis.tag}, | ||
{name: 'minValue', type: 'FIXED', value: axis.minValue << 16}, | ||
{name: 'defaultValue', type: 'FIXED', value: axis.defaultValue << 16}, | ||
{name: 'maxValue', type: 'FIXED', value: axis.maxValue << 16}, | ||
{name: 'flags', type: 'USHORT', value: 0}, | ||
{name: 'nameID', type: 'USHORT', value: nameID} | ||
]); | ||
return [ | ||
{name: 'tag_' + n, type: 'TAG', value: axis.tag}, | ||
{name: 'minValue_' + n, type: 'FIXED', value: axis.minValue << 16}, | ||
{name: 'defaultValue_' + n, type: 'FIXED', value: axis.defaultValue << 16}, | ||
{name: 'maxValue_' + n, type: 'FIXED', value: axis.maxValue << 16}, | ||
{name: 'flags_' + n, type: 'USHORT', value: 0}, | ||
{name: 'nameID_' + n, type: 'USHORT', value: nameID} | ||
]; | ||
} | ||
@@ -57,7 +57,7 @@ | ||
function makeFvarInstance(inst, axes, names) { | ||
function makeFvarInstance(n, inst, axes, names) { | ||
var nameID = addName(inst.name, names); | ||
var fields = [ | ||
{name: 'nameID', type: 'USHORT', value: nameID}, | ||
{name: 'flags', type: 'USHORT', value: 0} | ||
{name: 'nameID_' + n, type: 'USHORT', value: nameID}, | ||
{name: 'flags_' + n, type: 'USHORT', value: 0} | ||
]; | ||
@@ -68,3 +68,3 @@ | ||
fields.push({ | ||
name: 'axis ' + axisTag, | ||
name: 'axis_' + n + ' ' + axisTag, | ||
type: 'FIXED', | ||
@@ -75,3 +75,3 @@ value: inst.coordinates[axisTag] << 16 | ||
return new table.Table('fvarInstance', fields); | ||
return fields; | ||
} | ||
@@ -106,14 +106,7 @@ | ||
for (var i = 0; i < fvar.axes.length; i++) { | ||
result.fields.push({ | ||
name: 'axis ' + i, | ||
type: 'TABLE', | ||
value: makeFvarAxis(fvar.axes[i], names)}); | ||
result.fields = result.fields.concat(makeFvarAxis(i, fvar.axes[i], names)); | ||
} | ||
for (var j = 0; j < fvar.instances.length; j++) { | ||
result.fields.push({ | ||
name: 'instance ' + j, | ||
type: 'TABLE', | ||
value: makeFvarInstance(fvar.instances[j], fvar.axes, names) | ||
}); | ||
result.fields = result.fields.concat(makeFvarInstance(j, fvar.instances[j], fvar.axes, names)); | ||
} | ||
@@ -120,0 +113,0 @@ |
@@ -689,3 +689,3 @@ // The `name` naming table. | ||
function makeNameRecord(platformID, encodingID, languageID, nameID, length, offset) { | ||
return new table.Table('NameRecord', [ | ||
return new table.Record('NameRecord', [ | ||
{name: 'platformID', type: 'USHORT', value: platformID}, | ||
@@ -823,3 +823,3 @@ {name: 'encodingID', type: 'USHORT', value: encodingID}, | ||
for (var r = 0; r < nameRecords.length; r++) { | ||
t.fields.push({name: 'record_' + r, type: 'TABLE', value: nameRecords[r]}); | ||
t.fields.push({name: 'record_' + r, type: 'RECORD', value: nameRecords[r]}); | ||
} | ||
@@ -826,0 +826,0 @@ |
@@ -45,3 +45,3 @@ // The `sfnt` wrapper provides organization for the tables in the font. | ||
function makeTableRecord(tag, checkSum, offset, length) { | ||
return new table.Table('Table Record', [ | ||
return new table.Record('Table Record', [ | ||
{name: 'tag', type: 'TAG', value: tag !== undefined ? tag : ''}, | ||
@@ -83,4 +83,4 @@ {name: 'checkSum', type: 'ULONG', value: checkSum !== undefined ? checkSum : 0}, | ||
var tableRecord = makeTableRecord(t.tableName, computeCheckSum(t.encode()), offset, tableLength); | ||
recordFields.push({name: tableRecord.tag + ' Table Record', type: 'TABLE', value: tableRecord}); | ||
tableFields.push({name: t.tableName + ' table', type: 'TABLE', value: t}); | ||
recordFields.push({name: tableRecord.tag + ' Table Record', type: 'RECORD', value: tableRecord}); | ||
tableFields.push({name: t.tableName + ' table', type: 'RECORD', value: t}); | ||
offset += tableLength; | ||
@@ -87,0 +87,0 @@ check.argument(!isNaN(offset), 'Something went wrong calculating the offset.'); |
@@ -569,7 +569,10 @@ // Data types used in the OpenType font file. | ||
var length = table.fields.length; | ||
var subtables = []; | ||
var subtableOffsets = []; | ||
var i; | ||
for (var i = 0; i < length; i += 1) { | ||
for (i = 0; i < length; i += 1) { | ||
var field = table.fields[i]; | ||
var encodingFunction = encode[field.type]; | ||
check.argument(encodingFunction !== undefined, 'No encoding function for field type ' + field.type); | ||
check.argument(encodingFunction !== undefined, 'No encoding function for field type ' + field.type + ' (' + field.name + ')'); | ||
var value = table[field.name]; | ||
@@ -581,5 +584,21 @@ if (value === undefined) { | ||
var bytes = encodingFunction(value); | ||
d = d.concat(bytes); | ||
if (field.type === 'TABLE') { | ||
subtableOffsets.push(d.length); | ||
d = d.concat([0, 0]); | ||
subtables.push(bytes); | ||
} else { | ||
d = d.concat(bytes); | ||
} | ||
} | ||
for (i = 0; i < subtables.length; i += 1) { | ||
var o = subtableOffsets[i]; | ||
var offset = d.length; | ||
check.argument(offset < 65536, 'Table ' + table.tableName + ' too big.'); | ||
d[o] = offset >> 8; | ||
d[o + 1] = offset & 0xff; | ||
d = d.concat(subtables[i]); | ||
} | ||
return d; | ||
@@ -595,3 +614,3 @@ }; | ||
var sizeOfFunction = sizeOf[field.type]; | ||
check.argument(sizeOfFunction !== undefined, 'No sizeOf function for field type ' + field.type); | ||
check.argument(sizeOfFunction !== undefined, 'No sizeOf function for field type ' + field.type + ' (' + field.name + ')'); | ||
var value = table[field.name]; | ||
@@ -603,2 +622,7 @@ if (value === undefined) { | ||
numBytes += sizeOfFunction(value); | ||
// Subtables take 2 more bytes for offsets. | ||
if (field.type === 'TABLE') { | ||
numBytes += 2; | ||
} | ||
} | ||
@@ -609,2 +633,5 @@ | ||
encode.RECORD = encode.TABLE; | ||
sizeOf.RECORD = sizeOf.TABLE; | ||
// Merge in a list of bytes. | ||
@@ -611,0 +638,0 @@ encode.LITERAL = function(v) { |
@@ -31,11 +31,8 @@ 'use strict'; | ||
var text = testutil.unhex(name[1]); | ||
var record = new table.Table('NameRecord', [ | ||
{name: 'platformID', type: 'USHORT', value: name[2]}, | ||
{name: 'encodingID', type: 'USHORT', value: name[3]}, | ||
{name: 'languageID', type: 'USHORT', value: name[4]}, | ||
{name: 'nameID', type: 'USHORT', value: name[0]}, | ||
{name: 'length', type: 'USHORT', value: text.byteLength}, | ||
{name: 'offset', type: 'USHORT', value: stringPool.length} | ||
]); | ||
t.fields.push({name: 'record_' + i, type: 'TABLE', value: record}); | ||
t.fields.push({name: 'platformID_' + i, type: 'USHORT', value: name[2]}); | ||
t.fields.push({name: 'encodingID_' + i, type: 'USHORT', value: name[3]}); | ||
t.fields.push({name: 'languageID_' + i, type: 'USHORT', value: name[4]}); | ||
t.fields.push({name: 'nameID_' + i, type: 'USHORT', value: name[0]}); | ||
t.fields.push({name: 'length_' + i, type: 'USHORT', value: text.byteLength}); | ||
t.fields.push({name: 'offset_' + i, type: 'USHORT', value: stringPool.length}); | ||
for (var j = 0; j < text.byteLength; j++) { | ||
@@ -47,2 +44,3 @@ stringPool.push(text.getUint8(j)); | ||
t.fields.push({name: 'strings', type: 'LITERAL', value: stringPool}); | ||
var bytes = types.encode.TABLE(t); | ||
@@ -49,0 +47,0 @@ var data = new DataView(new ArrayBuffer(bytes.length), 0); |
@@ -458,2 +458,85 @@ 'use strict'; | ||
it('can handle subTABLEs', function() { | ||
var table = { | ||
fields: [ | ||
{name: 'version', type: 'FIXED', value: 0x01234567}, | ||
{name: 'subtable', type: 'TABLE', value: { | ||
fields: [ | ||
{name: 'flags', type: 'USHORT', value: 0xBEEF} | ||
] | ||
}} | ||
] | ||
}; | ||
assert.equal(hex(encode.TABLE(table)), '01 23 45 67 00 06 BE EF'); | ||
assert.equal(sizeOf.TABLE(table), 8); | ||
}); | ||
it('can handle deeply nested TABLEs', function() { | ||
// First 58 bytes of Roboto-Black.ttf GSUB table. | ||
var expected = '00 01 00 00 00 0A 00 20 00 3A ' + // header | ||
'00 01 44 46 4C 54 00 08 00 04 00 00 00 00 FF FF 00 02 00 00 00 01 ' + // script list | ||
'00 02 6C 69 67 61 00 0E 73 6D 63 70 00 14 00 00 00 01 00 01 00 00 00 01 00 00'; // feature list | ||
var table = { | ||
fields: [ | ||
{name: 'version', type: 'FIXED', value: 0x00010000}, | ||
{name: 'scriptList', type: 'TABLE'}, | ||
{name: 'featureList', type: 'TABLE'}, | ||
{name: 'lookupList', type: 'TABLE'} | ||
], | ||
scriptList: { fields: [ | ||
{name: 'scriptCount', type: 'USHORT', value: 1}, | ||
{name: 'scriptTag_0', type: 'TAG', value: 'DFLT'}, | ||
{name: 'script_0', type: 'TABLE', value: { fields: [ | ||
{name: 'defaultLangSys', type: 'TABLE', value: { fields: [ | ||
{name: 'lookupOrder', type: 'USHORT', value: 0}, | ||
{name: 'reqFeatureIndex', type: 'USHORT', value: 0xffff}, | ||
{name: 'featureCount', type: 'USHORT', value: 2}, | ||
{name: 'featureIndex_0', type: 'USHORT', value: 0}, | ||
{name: 'featureIndex_1', type: 'USHORT', value: 1} | ||
]}}, | ||
{name: 'langSysCount', type: 'USHORT', value: 0} | ||
]}} | ||
]}, | ||
featureList: { fields: [ | ||
{name: 'featureCount', type: 'USHORT', value: 2}, | ||
{name: 'featureTag_0', type: 'TAG', value: 'liga'}, | ||
{name: 'feature_0', type: 'TABLE', value: { fields: [ | ||
{name: 'featureParams', type: 'USHORT', value: 0}, | ||
{name: 'lookupCount', type: 'USHORT', value: 1}, | ||
{name: 'lookupListIndex', type: 'USHORT', value: 1} | ||
]}}, | ||
{name: 'featureTag_1', type: 'TAG', value: 'smcp'}, | ||
{name: 'feature_1', type: 'TABLE', value: { fields: [ | ||
{name: 'featureParams', type: 'USHORT', value: 0}, | ||
{name: 'lookupCount', type: 'USHORT', value: 1}, | ||
{name: 'lookupListIndex', type: 'USHORT', value: 0} | ||
]}} | ||
]}, | ||
lookupList: { fields: [] } | ||
}; | ||
assert.equal(hex(encode.TABLE(table)), expected); | ||
assert.equal(sizeOf.TABLE(table), 58); | ||
}); | ||
it('can handle RECORD', function() { | ||
var table = { | ||
fields: [ | ||
{name: 'version', type: 'FIXED', value: 0x01234567}, | ||
{name: 'record', type: 'RECORD'} | ||
] | ||
}; | ||
table.record = { | ||
fields: [ | ||
{name: 'flags_0', type: 'USHORT', value: 0xDEAF}, | ||
{name: 'flags_1', type: 'USHORT', value: 0xCAFE} | ||
] | ||
}; | ||
assert.equal(hex(encode.TABLE(table)), '01 23 45 67 DE AF CA FE'); | ||
assert.equal(sizeOf.TABLE(table), 8); | ||
}); | ||
it('can handle LITERAL', function() { | ||
@@ -460,0 +543,0 @@ assert.equal(hex(encode.LITERAL([])), ''); |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
670715
63
12815