Use CFF compiler for building Type1 font.
This commit is contained in:
parent
4b4601d1fb
commit
a235ec1441
283
src/fonts.js
283
src/fonts.js
@ -5050,61 +5050,6 @@ var Type1Font = function Type1Font(name, file, properties) {
|
||||
};
|
||||
|
||||
Type1Font.prototype = {
|
||||
createCFFIndexHeader: function Type1Font_createCFFIndexHeader(objects,
|
||||
isByte) {
|
||||
// First 2 bytes contains the number of objects contained into this index
|
||||
var count = objects.length;
|
||||
|
||||
// If there is no object, just create an array saying that with another
|
||||
// offset byte.
|
||||
if (count == 0)
|
||||
return '\x00\x00\x00';
|
||||
|
||||
var data = String.fromCharCode((count >> 8) & 0xFF, count & 0xff);
|
||||
|
||||
// Next byte contains the offset size use to reference object in the file
|
||||
// Actually we're using 0x04 to be sure to be able to store everything
|
||||
// without thinking of it while coding.
|
||||
data += '\x04';
|
||||
|
||||
// Add another offset after this one because we need a new offset
|
||||
var relativeOffset = 1;
|
||||
for (var i = 0; i < count + 1; i++) {
|
||||
data += String.fromCharCode((relativeOffset >>> 24) & 0xFF,
|
||||
(relativeOffset >> 16) & 0xFF,
|
||||
(relativeOffset >> 8) & 0xFF,
|
||||
relativeOffset & 0xFF);
|
||||
|
||||
if (objects[i])
|
||||
relativeOffset += objects[i].length;
|
||||
}
|
||||
|
||||
for (var i = 0; i < count; i++) {
|
||||
for (var j = 0, jj = objects[i].length; j < jj; j++)
|
||||
data += isByte ? String.fromCharCode(objects[i][j] & 0xFF) :
|
||||
objects[i][j];
|
||||
}
|
||||
return data;
|
||||
},
|
||||
|
||||
encodeNumber: function Type1Font_encodeNumber(value) {
|
||||
// some of the fonts has ouf-of-range values
|
||||
// they are just arithmetic overflows
|
||||
// make sanitizer happy
|
||||
value |= 0;
|
||||
if (value >= -32768 && value <= 32767) {
|
||||
return '\x1c' +
|
||||
String.fromCharCode((value >> 8) & 0xFF) +
|
||||
String.fromCharCode(value & 0xFF);
|
||||
} else {
|
||||
return '\x1d' +
|
||||
String.fromCharCode((value >> 24) & 0xFF) +
|
||||
String.fromCharCode((value >> 16) & 0xFF) +
|
||||
String.fromCharCode((value >> 8) & 0xFF) +
|
||||
String.fromCharCode(value & 0xFF);
|
||||
}
|
||||
},
|
||||
|
||||
getOrderedCharStrings: function Type1Font_getOrderedCharStrings(glyphs,
|
||||
properties) {
|
||||
var charstrings = [];
|
||||
@ -5223,9 +5168,9 @@ Type1Font.prototype = {
|
||||
if (command > 32000) {
|
||||
var divisor = charstring[i + 1];
|
||||
command /= divisor;
|
||||
charstring.splice(i, 3, 28, command >> 8, command & 0xff);
|
||||
charstring.splice(i, 3, 28, (command >> 8) & 0xff, command & 0xff);
|
||||
} else {
|
||||
charstring.splice(i, 1, 28, command >> 8, command & 0xff);
|
||||
charstring.splice(i, 1, 28, (command >> 8) & 0xff, command & 0xff);
|
||||
}
|
||||
i += 2;
|
||||
}
|
||||
@ -5234,150 +5179,88 @@ Type1Font.prototype = {
|
||||
},
|
||||
|
||||
wrap: function Type1Font_wrap(name, glyphs, charstrings, subrs, properties) {
|
||||
var comp = new CFFCompiler();
|
||||
// TODO: remove this function after refactoring wrap to use the CFFCompiler.
|
||||
function encodeNumber(num) {
|
||||
var val = comp.encodeNumber(num);
|
||||
var ret = '';
|
||||
for (var i = 0; i < val.length; i++) {
|
||||
ret += String.fromCharCode(val[i]);
|
||||
}
|
||||
return ret;
|
||||
var cff = new CFF();
|
||||
cff.header = new CFFHeader(1, 0, 4, 4);
|
||||
|
||||
cff.names = [name];
|
||||
|
||||
var topDict = new CFFTopDict();
|
||||
topDict.setByName('version', 0);
|
||||
topDict.setByName('Notice', 1);
|
||||
topDict.setByName('FullName', 2);
|
||||
topDict.setByName('FamilyName', 3);
|
||||
topDict.setByName('Weight', 4);
|
||||
topDict.setByName('Encoding', null); // placeholder
|
||||
topDict.setByName('FontBBox', properties.bbox);
|
||||
topDict.setByName('charset', null); // placeholder
|
||||
topDict.setByName('CharStrings', null); // placeholder
|
||||
topDict.setByName('Private', null); // placeholder
|
||||
cff.topDict = topDict;
|
||||
|
||||
var strings = new CFFStrings();
|
||||
strings.add('Version 0.11'); // Version
|
||||
strings.add('See original notice'); // Notice
|
||||
strings.add(name); // FullName
|
||||
strings.add(name); // FamilyName
|
||||
strings.add('Medium'); // Weight
|
||||
cff.strings = strings;
|
||||
|
||||
cff.globalSubrIndex = new CFFIndex();
|
||||
|
||||
var count = glyphs.length;
|
||||
var charsetArray = [0];
|
||||
for (var i = 0; i < count; i++) {
|
||||
var index = CFFStandardStrings.indexOf(charstrings[i].glyph);
|
||||
// Some characters like asterikmath && circlecopyrt are
|
||||
// missing from the original strings, for the moment let's
|
||||
// map them to .notdef and see later if it cause any
|
||||
// problems
|
||||
if (index == -1)
|
||||
index = 0;
|
||||
|
||||
charsetArray.push((index >> 8) & 0xff, index & 0xff);
|
||||
}
|
||||
cff.charset = new CFFCharset(false, 0, [], charsetArray);
|
||||
|
||||
var fields = {
|
||||
// major version, minor version, header size, offset size
|
||||
'header': '\x01\x00\x04\x04',
|
||||
|
||||
'names': this.createCFFIndexHeader([name]),
|
||||
|
||||
'topDict': (function topDict(self) {
|
||||
return function cffWrapTopDict() {
|
||||
var header = '\x00\x01\x01\x01';
|
||||
var dict =
|
||||
'\xf8\x1b\x00' + // version
|
||||
'\xf8\x1c\x01' + // Notice
|
||||
'\xf8\x1d\x02' + // FullName
|
||||
'\xf8\x1e\x03' + // FamilyName
|
||||
'\xf8\x1f\x04' + // Weight
|
||||
'\x1c\x00\x00\x10'; // Encoding
|
||||
|
||||
var boundingBox = properties.bbox;
|
||||
for (var i = 0, ii = boundingBox.length; i < ii; i++)
|
||||
dict += encodeNumber(boundingBox[i]);
|
||||
dict += '\x05'; // FontBBox;
|
||||
|
||||
var offset = fields.header.length +
|
||||
fields.names.length +
|
||||
(header.length + 1) +
|
||||
(dict.length + (4 + 4)) +
|
||||
fields.strings.length +
|
||||
fields.globalSubrs.length;
|
||||
|
||||
// If the offset if over 32767, encodeNumber is going to return
|
||||
// 5 bytes to encode the position instead of 3.
|
||||
if ((offset + fields.charstrings.length) > 32767) {
|
||||
offset += 9;
|
||||
} else {
|
||||
offset += 7;
|
||||
}
|
||||
|
||||
dict += self.encodeNumber(offset) + '\x0f'; // Charset
|
||||
|
||||
offset = offset + (glyphs.length * 2) + 1;
|
||||
dict += self.encodeNumber(offset) + '\x11'; // Charstrings
|
||||
|
||||
offset = offset + fields.charstrings.length;
|
||||
dict += self.encodeNumber(fields.privateData.length);
|
||||
dict += self.encodeNumber(offset) + '\x12'; // Private
|
||||
|
||||
return header + String.fromCharCode(dict.length + 1) + dict;
|
||||
};
|
||||
})(this),
|
||||
|
||||
'strings': (function strings(self) {
|
||||
var strings = [
|
||||
'Version 0.11', // Version
|
||||
'See original notice', // Notice
|
||||
name, // FullName
|
||||
name, // FamilyName
|
||||
'Medium' // Weight
|
||||
];
|
||||
return self.createCFFIndexHeader(strings);
|
||||
})(this),
|
||||
|
||||
'globalSubrs': this.createCFFIndexHeader([]),
|
||||
|
||||
'charset': (function charset(self) {
|
||||
var charsetString = '\x00'; // Encoding
|
||||
|
||||
var count = glyphs.length;
|
||||
for (var i = 0; i < count; i++) {
|
||||
var index = CFFStandardStrings.indexOf(charstrings[i].glyph);
|
||||
// Some characters like asterikmath && circlecopyrt are
|
||||
// missing from the original strings, for the moment let's
|
||||
// map them to .notdef and see later if it cause any
|
||||
// problems
|
||||
if (index == -1)
|
||||
index = 0;
|
||||
|
||||
charsetString += String.fromCharCode(index >> 8, index & 0xff);
|
||||
}
|
||||
return charsetString;
|
||||
})(this),
|
||||
|
||||
'charstrings': this.createCFFIndexHeader([[0x8B, 0x0E]].concat(glyphs),
|
||||
true),
|
||||
|
||||
'privateData': (function cffWrapPrivate(self) {
|
||||
var data =
|
||||
'\x8b\x14' + // defaultWidth
|
||||
'\x8b\x15'; // nominalWidth
|
||||
var fieldMap = {
|
||||
BlueValues: '\x06',
|
||||
OtherBlues: '\x07',
|
||||
FamilyBlues: '\x08',
|
||||
FamilyOtherBlues: '\x09',
|
||||
StemSnapH: '\x0c\x0c',
|
||||
StemSnapV: '\x0c\x0d',
|
||||
BlueShift: '\x0c\x0a',
|
||||
BlueFuzz: '\x0c\x0b',
|
||||
BlueScale: '\x0c\x09',
|
||||
LanguageGroup: '\x0c\x11',
|
||||
ExpansionFactor: '\x0c\x12'
|
||||
};
|
||||
for (var field in fieldMap) {
|
||||
if (!properties.privateData.hasOwnProperty(field))
|
||||
continue;
|
||||
var value = properties.privateData[field];
|
||||
|
||||
if (isArray(value)) {
|
||||
for (var i = 0, ii = value.length; i < ii; i++)
|
||||
data += encodeNumber(value[i]);
|
||||
} else {
|
||||
data += encodeNumber(value);
|
||||
}
|
||||
data += fieldMap[field];
|
||||
}
|
||||
|
||||
data += self.encodeNumber(data.length + 4) + '\x13'; // Subrs offset
|
||||
|
||||
return data;
|
||||
})(this),
|
||||
|
||||
'localSubrs': this.createCFFIndexHeader(subrs, true)
|
||||
};
|
||||
fields.topDict = fields.topDict();
|
||||
|
||||
|
||||
var cff = [];
|
||||
for (var index in fields) {
|
||||
var field = fields[index];
|
||||
for (var i = 0, ii = field.length; i < ii; i++)
|
||||
cff.push(field.charCodeAt(i));
|
||||
var charStringsIndex = new CFFIndex();
|
||||
charStringsIndex.add([0x8B, 0x0E]); // .notdef
|
||||
for (var i = 0; i < count; i++) {
|
||||
charStringsIndex.add(glyphs[i]);
|
||||
}
|
||||
cff.charStrings = charStringsIndex;
|
||||
|
||||
return cff;
|
||||
var privateDict = new CFFPrivateDict();
|
||||
privateDict.setByName('Subrs', null); // placeholder
|
||||
var fields = [
|
||||
// TODO: missing StdHW, StdVW, ForceBold
|
||||
'BlueValues',
|
||||
'OtherBlues',
|
||||
'FamilyBlues',
|
||||
'FamilyOtherBlues',
|
||||
'StemSnapH',
|
||||
'StemSnapV',
|
||||
'BlueShift',
|
||||
'BlueFuzz',
|
||||
'BlueScale',
|
||||
'LanguageGroup',
|
||||
'ExpansionFactor'
|
||||
];
|
||||
for (var i = 0, ii = fields.length; i < ii; i++) {
|
||||
var field = fields[i];
|
||||
if (!properties.privateData.hasOwnProperty(field))
|
||||
continue;
|
||||
privateDict.setByName(field, properties.privateData[field]);
|
||||
}
|
||||
cff.topDict.privateDict = privateDict;
|
||||
|
||||
var subrIndex = new CFFIndex();
|
||||
for (var i = 0, ii = subrs.length; i < ii; i++) {
|
||||
subrIndex.add(subrs[i]);
|
||||
}
|
||||
privateDict.subrsIndex = subrIndex;
|
||||
|
||||
var compiler = new CFFCompiler(cff);
|
||||
return compiler.compile();
|
||||
}
|
||||
};
|
||||
|
||||
@ -6187,6 +6070,12 @@ var CFFDict = (function CFFDictClosure() {
|
||||
this.values[key] = value;
|
||||
return true;
|
||||
},
|
||||
setByName: function CFFDict_setByName(name, value) {
|
||||
if (!(name in this.nameToKeyMap)) {
|
||||
error('Invalid dictionary name "' + name + '"');
|
||||
}
|
||||
this.values[this.nameToKeyMap[name]] = value;
|
||||
},
|
||||
hasName: function CFFDict_hasName(name) {
|
||||
return this.nameToKeyMap[name] in this.values;
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user