Skip to content

Commit

Permalink
Merge pull request #580 from Connum/feature/parse-cff2
Browse files Browse the repository at this point in the history
implement CFF2 parsing
  • Loading branch information
Connum authored Mar 4, 2023
2 parents c5c2c06 + 97e67bb commit ca3b438
Show file tree
Hide file tree
Showing 6 changed files with 542 additions and 118 deletions.
4 changes: 2 additions & 2 deletions src/glyphset.js
Original file line number Diff line number Diff line change
Expand Up @@ -141,12 +141,12 @@ function ttfGlyphLoader(font, index, parseGlyph, data, position, buildPath) {
* @param {string} charstring
* @return {opentype.Glyph}
*/
function cffGlyphLoader(font, index, parseCFFCharstring, charstring) {
function cffGlyphLoader(font, index, parseCFFCharstring, charstring, version) {
return function() {
const glyph = new Glyph({index: index, font: font});

glyph.path = function() {
const path = parseCFFCharstring(font, glyph, charstring);
const path = parseCFFCharstring(font, glyph, charstring, version);
path.unitsPerEm = font.unitsPerEm;
return path;
};
Expand Down
9 changes: 8 additions & 1 deletion src/opentype.js
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,7 @@ function parseBuffer(buffer, opt={}) {
}

let cffTableEntry;
let cff2TableEntry;
let fvarTableEntry;
let statTableEntry;
let gvarTableEntry;
Expand Down Expand Up @@ -319,6 +320,9 @@ function parseBuffer(buffer, opt={}) {
case 'CFF ':
cffTableEntry = tableEntry;
break;
case 'CFF2':
cff2TableEntry = tableEntry;
break;
case 'kern':
kernTableEntry = tableEntry;
break;
Expand Down Expand Up @@ -350,8 +354,11 @@ function parseBuffer(buffer, opt={}) {
} else if (cffTableEntry) {
const cffTable = uncompressTable(data, cffTableEntry);
cff.parse(cffTable.data, cffTable.offset, font, opt);
} else if (cff2TableEntry) {
const cffTable2 = uncompressTable(data, cff2TableEntry);
cff.parse(cffTable2.data, cffTable2.offset, font, opt);
} else {
throw new Error('Font doesn\'t contain TrueType or CFF outlines.');
throw new Error('Font doesn\'t contain TrueType, CFF or CFF2 outlines.');
}

const hmtxTable = uncompressTable(data, hmtxTableEntry);
Expand Down
83 changes: 83 additions & 0 deletions src/parse.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,11 @@ const typeOffsets = {
tag: 4
};

const masks = {
LONG_WORDS: 0x8000,
WORD_DELTA_COUNT_MASK: 0x7FFF
};

// A stateful parser that changes the offset whenever a value is retrieved.
// The data is a DataView.
function Parser(data, offset) {
Expand Down Expand Up @@ -551,6 +556,7 @@ Parser.uShortList = Parser.prototype.parseUShortList;
Parser.uLong = Parser.offset32 = Parser.prototype.parseULong;
Parser.uLongList = Parser.prototype.parseULongList;
Parser.fixed = Parser.prototype.parseFixed;
Parser.f2Dot14 = Parser.prototype.parseF2Dot14;
Parser.struct = Parser.prototype.parseStruct;
Parser.coverage = Parser.prototype.parseCoverage;
Parser.classDef = Parser.prototype.parseClassDef;
Expand Down Expand Up @@ -615,6 +621,83 @@ Parser.prototype.parseFeatureVariationsList = function() {
}) || [];
};

// VariationStore, ItemVariationStore, VariationRegionList, regionAxes, ItemVariationSubtables ...
// https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats
// https://learn.microsoft.com/en-us/typography/opentype/spec/otvarcommonformats#item-variation-store-header-and-item-variation-data-subtables

Parser.prototype.parseVariationStore = function() {
const vsOffset = this.relativeOffset;
const length = this.parseUShort();
const variationStore = {
itemVariationStore: this.parseItemVariationStore()
};
this.relativeOffset = vsOffset + length + 2; // + 2 for length field
return variationStore;
};

Parser.prototype.parseItemVariationStore = function() {
const itemStoreOffset = this.relativeOffset;
const iVStore = {
format: this.parseUShort(),
variationRegions: [],
itemVariationSubtables: []
};

const variationRegionListOffset = this.parseOffset32();
const itemVariationDataCount = this.parseUShort();
const itemVariationDataOffsets = this.parseULongList(itemVariationDataCount);

this.relativeOffset = itemStoreOffset + variationRegionListOffset;
iVStore.variationRegions = this.parseVariationRegionList();

for( let i = 0; i < itemVariationDataCount; i++ ) {
const subtableOffset = itemVariationDataOffsets[i];
this.relativeOffset = itemStoreOffset + subtableOffset;
iVStore.itemVariationSubtables.push(this.parseItemVariationSubtable());
}

return iVStore;
};

Parser.prototype.parseVariationRegionList = function() {
const axisCount = this.parseUShort();
const regionCount = this.parseUShort();
return this.parseRecordList(regionCount, {
regionAxes: Parser.recordList(axisCount, {
startCoord: Parser.f2Dot14,
peakCoord: Parser.f2Dot14,
endCoord: Parser.f2Dot14,
})
});
};

Parser.prototype.parseItemVariationSubtable = function() {
const itemCount = this.parseUShort();
const wordDeltaCount = this.parseUShort();

const subtable = {
regionIndexes: this.parseUShortList(),
deltaSets: this.parseDeltaSets(itemCount, wordDeltaCount)
};

return subtable;
};

Parser.prototype.parseDeltaSets = function(itemCount, wordDeltaCount) {
const deltas = [];
const longFlag = wordDeltaCount & masks.LONG_WORDS;
const wordCount = wordDeltaCount & masks.WORD_DELTA_COUNT_MASK;

const wordParser = (longFlag ? this.parseULong : this.parseUShort).bind(this);
const restParser = (longFlag ? this.parseUShort : this.parseByte).bind(this);
for( let i = 0; i < itemCount; i++ ) {
const deltaParser = i < wordCount ? wordParser : restParser;
deltas.push(deltaParser());
}

return deltas;
};

export default {
getByte,
getCard8: getByte,
Expand Down
Loading

0 comments on commit ca3b438

Please sign in to comment.