Skip to content

Commit a235ec1

Browse files
committed
Use CFF compiler for building Type1 font.
1 parent 4b4601d commit a235ec1

File tree

1 file changed

+85
-196
lines changed

1 file changed

+85
-196
lines changed

src/fonts.js

Lines changed: 85 additions & 196 deletions
Original file line numberDiff line numberDiff line change
@@ -5050,61 +5050,6 @@ var Type1Font = function Type1Font(name, file, properties) {
50505050
};
50515051

50525052
Type1Font.prototype = {
5053-
createCFFIndexHeader: function Type1Font_createCFFIndexHeader(objects,
5054-
isByte) {
5055-
// First 2 bytes contains the number of objects contained into this index
5056-
var count = objects.length;
5057-
5058-
// If there is no object, just create an array saying that with another
5059-
// offset byte.
5060-
if (count == 0)
5061-
return '\x00\x00\x00';
5062-
5063-
var data = String.fromCharCode((count >> 8) & 0xFF, count & 0xff);
5064-
5065-
// Next byte contains the offset size use to reference object in the file
5066-
// Actually we're using 0x04 to be sure to be able to store everything
5067-
// without thinking of it while coding.
5068-
data += '\x04';
5069-
5070-
// Add another offset after this one because we need a new offset
5071-
var relativeOffset = 1;
5072-
for (var i = 0; i < count + 1; i++) {
5073-
data += String.fromCharCode((relativeOffset >>> 24) & 0xFF,
5074-
(relativeOffset >> 16) & 0xFF,
5075-
(relativeOffset >> 8) & 0xFF,
5076-
relativeOffset & 0xFF);
5077-
5078-
if (objects[i])
5079-
relativeOffset += objects[i].length;
5080-
}
5081-
5082-
for (var i = 0; i < count; i++) {
5083-
for (var j = 0, jj = objects[i].length; j < jj; j++)
5084-
data += isByte ? String.fromCharCode(objects[i][j] & 0xFF) :
5085-
objects[i][j];
5086-
}
5087-
return data;
5088-
},
5089-
5090-
encodeNumber: function Type1Font_encodeNumber(value) {
5091-
// some of the fonts has ouf-of-range values
5092-
// they are just arithmetic overflows
5093-
// make sanitizer happy
5094-
value |= 0;
5095-
if (value >= -32768 && value <= 32767) {
5096-
return '\x1c' +
5097-
String.fromCharCode((value >> 8) & 0xFF) +
5098-
String.fromCharCode(value & 0xFF);
5099-
} else {
5100-
return '\x1d' +
5101-
String.fromCharCode((value >> 24) & 0xFF) +
5102-
String.fromCharCode((value >> 16) & 0xFF) +
5103-
String.fromCharCode((value >> 8) & 0xFF) +
5104-
String.fromCharCode(value & 0xFF);
5105-
}
5106-
},
5107-
51085053
getOrderedCharStrings: function Type1Font_getOrderedCharStrings(glyphs,
51095054
properties) {
51105055
var charstrings = [];
@@ -5223,9 +5168,9 @@ Type1Font.prototype = {
52235168
if (command > 32000) {
52245169
var divisor = charstring[i + 1];
52255170
command /= divisor;
5226-
charstring.splice(i, 3, 28, command >> 8, command & 0xff);
5171+
charstring.splice(i, 3, 28, (command >> 8) & 0xff, command & 0xff);
52275172
} else {
5228-
charstring.splice(i, 1, 28, command >> 8, command & 0xff);
5173+
charstring.splice(i, 1, 28, (command >> 8) & 0xff, command & 0xff);
52295174
}
52305175
i += 2;
52315176
}
@@ -5234,150 +5179,88 @@ Type1Font.prototype = {
52345179
},
52355180

52365181
wrap: function Type1Font_wrap(name, glyphs, charstrings, subrs, properties) {
5237-
var comp = new CFFCompiler();
5238-
// TODO: remove this function after refactoring wrap to use the CFFCompiler.
5239-
function encodeNumber(num) {
5240-
var val = comp.encodeNumber(num);
5241-
var ret = '';
5242-
for (var i = 0; i < val.length; i++) {
5243-
ret += String.fromCharCode(val[i]);
5244-
}
5245-
return ret;
5182+
var cff = new CFF();
5183+
cff.header = new CFFHeader(1, 0, 4, 4);
5184+
5185+
cff.names = [name];
5186+
5187+
var topDict = new CFFTopDict();
5188+
topDict.setByName('version', 0);
5189+
topDict.setByName('Notice', 1);
5190+
topDict.setByName('FullName', 2);
5191+
topDict.setByName('FamilyName', 3);
5192+
topDict.setByName('Weight', 4);
5193+
topDict.setByName('Encoding', null); // placeholder
5194+
topDict.setByName('FontBBox', properties.bbox);
5195+
topDict.setByName('charset', null); // placeholder
5196+
topDict.setByName('CharStrings', null); // placeholder
5197+
topDict.setByName('Private', null); // placeholder
5198+
cff.topDict = topDict;
5199+
5200+
var strings = new CFFStrings();
5201+
strings.add('Version 0.11'); // Version
5202+
strings.add('See original notice'); // Notice
5203+
strings.add(name); // FullName
5204+
strings.add(name); // FamilyName
5205+
strings.add('Medium'); // Weight
5206+
cff.strings = strings;
5207+
5208+
cff.globalSubrIndex = new CFFIndex();
5209+
5210+
var count = glyphs.length;
5211+
var charsetArray = [0];
5212+
for (var i = 0; i < count; i++) {
5213+
var index = CFFStandardStrings.indexOf(charstrings[i].glyph);
5214+
// Some characters like asterikmath && circlecopyrt are
5215+
// missing from the original strings, for the moment let's
5216+
// map them to .notdef and see later if it cause any
5217+
// problems
5218+
if (index == -1)
5219+
index = 0;
5220+
5221+
charsetArray.push((index >> 8) & 0xff, index & 0xff);
52465222
}
5223+
cff.charset = new CFFCharset(false, 0, [], charsetArray);
52475224

5248-
var fields = {
5249-
// major version, minor version, header size, offset size
5250-
'header': '\x01\x00\x04\x04',
5251-
5252-
'names': this.createCFFIndexHeader([name]),
5253-
5254-
'topDict': (function topDict(self) {
5255-
return function cffWrapTopDict() {
5256-
var header = '\x00\x01\x01\x01';
5257-
var dict =
5258-
'\xf8\x1b\x00' + // version
5259-
'\xf8\x1c\x01' + // Notice
5260-
'\xf8\x1d\x02' + // FullName
5261-
'\xf8\x1e\x03' + // FamilyName
5262-
'\xf8\x1f\x04' + // Weight
5263-
'\x1c\x00\x00\x10'; // Encoding
5264-
5265-
var boundingBox = properties.bbox;
5266-
for (var i = 0, ii = boundingBox.length; i < ii; i++)
5267-
dict += encodeNumber(boundingBox[i]);
5268-
dict += '\x05'; // FontBBox;
5269-
5270-
var offset = fields.header.length +
5271-
fields.names.length +
5272-
(header.length + 1) +
5273-
(dict.length + (4 + 4)) +
5274-
fields.strings.length +
5275-
fields.globalSubrs.length;
5276-
5277-
// If the offset if over 32767, encodeNumber is going to return
5278-
// 5 bytes to encode the position instead of 3.
5279-
if ((offset + fields.charstrings.length) > 32767) {
5280-
offset += 9;
5281-
} else {
5282-
offset += 7;
5283-
}
5284-
5285-
dict += self.encodeNumber(offset) + '\x0f'; // Charset
5286-
5287-
offset = offset + (glyphs.length * 2) + 1;
5288-
dict += self.encodeNumber(offset) + '\x11'; // Charstrings
5289-
5290-
offset = offset + fields.charstrings.length;
5291-
dict += self.encodeNumber(fields.privateData.length);
5292-
dict += self.encodeNumber(offset) + '\x12'; // Private
5293-
5294-
return header + String.fromCharCode(dict.length + 1) + dict;
5295-
};
5296-
})(this),
5297-
5298-
'strings': (function strings(self) {
5299-
var strings = [
5300-
'Version 0.11', // Version
5301-
'See original notice', // Notice
5302-
name, // FullName
5303-
name, // FamilyName
5304-
'Medium' // Weight
5305-
];
5306-
return self.createCFFIndexHeader(strings);
5307-
})(this),
5308-
5309-
'globalSubrs': this.createCFFIndexHeader([]),
5310-
5311-
'charset': (function charset(self) {
5312-
var charsetString = '\x00'; // Encoding
5313-
5314-
var count = glyphs.length;
5315-
for (var i = 0; i < count; i++) {
5316-
var index = CFFStandardStrings.indexOf(charstrings[i].glyph);
5317-
// Some characters like asterikmath && circlecopyrt are
5318-
// missing from the original strings, for the moment let's
5319-
// map them to .notdef and see later if it cause any
5320-
// problems
5321-
if (index == -1)
5322-
index = 0;
5323-
5324-
charsetString += String.fromCharCode(index >> 8, index & 0xff);
5325-
}
5326-
return charsetString;
5327-
})(this),
5328-
5329-
'charstrings': this.createCFFIndexHeader([[0x8B, 0x0E]].concat(glyphs),
5330-
true),
5331-
5332-
'privateData': (function cffWrapPrivate(self) {
5333-
var data =
5334-
'\x8b\x14' + // defaultWidth
5335-
'\x8b\x15'; // nominalWidth
5336-
var fieldMap = {
5337-
BlueValues: '\x06',
5338-
OtherBlues: '\x07',
5339-
FamilyBlues: '\x08',
5340-
FamilyOtherBlues: '\x09',
5341-
StemSnapH: '\x0c\x0c',
5342-
StemSnapV: '\x0c\x0d',
5343-
BlueShift: '\x0c\x0a',
5344-
BlueFuzz: '\x0c\x0b',
5345-
BlueScale: '\x0c\x09',
5346-
LanguageGroup: '\x0c\x11',
5347-
ExpansionFactor: '\x0c\x12'
5348-
};
5349-
for (var field in fieldMap) {
5350-
if (!properties.privateData.hasOwnProperty(field))
5351-
continue;
5352-
var value = properties.privateData[field];
5353-
5354-
if (isArray(value)) {
5355-
for (var i = 0, ii = value.length; i < ii; i++)
5356-
data += encodeNumber(value[i]);
5357-
} else {
5358-
data += encodeNumber(value);
5359-
}
5360-
data += fieldMap[field];
5361-
}
5362-
5363-
data += self.encodeNumber(data.length + 4) + '\x13'; // Subrs offset
5364-
5365-
return data;
5366-
})(this),
5367-
5368-
'localSubrs': this.createCFFIndexHeader(subrs, true)
5369-
};
5370-
fields.topDict = fields.topDict();
5371-
5225+
var charStringsIndex = new CFFIndex();
5226+
charStringsIndex.add([0x8B, 0x0E]); // .notdef
5227+
for (var i = 0; i < count; i++) {
5228+
charStringsIndex.add(glyphs[i]);
5229+
}
5230+
cff.charStrings = charStringsIndex;
5231+
5232+
var privateDict = new CFFPrivateDict();
5233+
privateDict.setByName('Subrs', null); // placeholder
5234+
var fields = [
5235+
// TODO: missing StdHW, StdVW, ForceBold
5236+
'BlueValues',
5237+
'OtherBlues',
5238+
'FamilyBlues',
5239+
'FamilyOtherBlues',
5240+
'StemSnapH',
5241+
'StemSnapV',
5242+
'BlueShift',
5243+
'BlueFuzz',
5244+
'BlueScale',
5245+
'LanguageGroup',
5246+
'ExpansionFactor'
5247+
];
5248+
for (var i = 0, ii = fields.length; i < ii; i++) {
5249+
var field = fields[i];
5250+
if (!properties.privateData.hasOwnProperty(field))
5251+
continue;
5252+
privateDict.setByName(field, properties.privateData[field]);
5253+
}
5254+
cff.topDict.privateDict = privateDict;
53725255

5373-
var cff = [];
5374-
for (var index in fields) {
5375-
var field = fields[index];
5376-
for (var i = 0, ii = field.length; i < ii; i++)
5377-
cff.push(field.charCodeAt(i));
5256+
var subrIndex = new CFFIndex();
5257+
for (var i = 0, ii = subrs.length; i < ii; i++) {
5258+
subrIndex.add(subrs[i]);
53785259
}
5260+
privateDict.subrsIndex = subrIndex;
53795261

5380-
return cff;
5262+
var compiler = new CFFCompiler(cff);
5263+
return compiler.compile();
53815264
}
53825265
};
53835266

@@ -6187,6 +6070,12 @@ var CFFDict = (function CFFDictClosure() {
61876070
this.values[key] = value;
61886071
return true;
61896072
},
6073+
setByName: function CFFDict_setByName(name, value) {
6074+
if (!(name in this.nameToKeyMap)) {
6075+
error('Invalid dictionary name "' + name + '"');
6076+
}
6077+
this.values[this.nameToKeyMap[name]] = value;
6078+
},
61906079
hasName: function CFFDict_hasName(name) {
61916080
return this.nameToKeyMap[name] in this.values;
61926081
},

0 commit comments

Comments
 (0)