|
|
@ -26,12 +26,15 @@ var fontName = ""; |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
var kDisableFonts = false; |
|
|
|
var kDisableFonts = false; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
|
* Hold a map of decoded fonts and of the standard fourteen Type1 fonts and |
|
|
|
* Hold a map of decoded fonts and of the standard fourteen Type1 fonts and |
|
|
|
* their acronyms. |
|
|
|
* their acronyms. |
|
|
|
* TODO Add the standard fourteen Type1 fonts list by default |
|
|
|
* TODO Add the standard fourteen Type1 fonts list by default |
|
|
|
* http://cgit.freedesktop.org/poppler/poppler/tree/poppler/GfxFont.cc#n65
|
|
|
|
* http://cgit.freedesktop.org/poppler/poppler/tree/poppler/GfxFont.cc#n65
|
|
|
|
*/ |
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var kScalePrecision = 40; |
|
|
|
var Fonts = { |
|
|
|
var Fonts = { |
|
|
|
_active: null, |
|
|
|
_active: null, |
|
|
|
|
|
|
|
|
|
|
@ -39,8 +42,9 @@ var Fonts = { |
|
|
|
return this._active; |
|
|
|
return this._active; |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
set active(name) { |
|
|
|
setActive: function fonts_setActive(name, size) { |
|
|
|
this._active = this[name]; |
|
|
|
this._active = this[name]; |
|
|
|
|
|
|
|
this.ctx.font = (size * kScalePrecision) + 'px "' + name + '"'; |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
charsToUnicode: function fonts_chars2Unicode(chars) { |
|
|
|
charsToUnicode: function fonts_chars2Unicode(chars) { |
|
|
@ -77,6 +81,16 @@ var Fonts = { |
|
|
|
|
|
|
|
|
|
|
|
// Enter the translated string into the cache
|
|
|
|
// Enter the translated string into the cache
|
|
|
|
return active.cache[chars] = str; |
|
|
|
return active.cache[chars] = str; |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
get ctx() { |
|
|
|
|
|
|
|
var ctx = document.createElement("canvas").getContext("2d"); |
|
|
|
|
|
|
|
ctx.scale(1 / kScalePrecision, 1); |
|
|
|
|
|
|
|
return shadow(this, "ctx", ctx); |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
measureText: function fonts_measureText(text) { |
|
|
|
|
|
|
|
return this.ctx.measureText(text).width / kScalePrecision; |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
@ -426,7 +440,6 @@ var Font = (function () { |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
function replaceCMapTable(cmap, font, properties) { |
|
|
|
function replaceCMapTable(cmap, font, properties) { |
|
|
|
font.pos = cmap.length; |
|
|
|
|
|
|
|
var version = FontsUtils.bytesToInteger(font.getBytes(2)); |
|
|
|
var version = FontsUtils.bytesToInteger(font.getBytes(2)); |
|
|
|
var numTables = FontsUtils.bytesToInteger(font.getBytes(2)); |
|
|
|
var numTables = FontsUtils.bytesToInteger(font.getBytes(2)); |
|
|
|
|
|
|
|
|
|
|
@ -722,7 +735,7 @@ var Font = (function () { |
|
|
|
"\x00\x00" + // -reserved-
|
|
|
|
"\x00\x00" + // -reserved-
|
|
|
|
"\x00\x00" + // -reserved-
|
|
|
|
"\x00\x00" + // -reserved-
|
|
|
|
"\x00\x00" + // metricDataFormat
|
|
|
|
"\x00\x00" + // metricDataFormat
|
|
|
|
string16(charstrings.length) |
|
|
|
string16(charstrings.length + 1) // Number of HMetrics
|
|
|
|
); |
|
|
|
); |
|
|
|
createTableEntry(otf, offsets, "hhea", hhea); |
|
|
|
createTableEntry(otf, offsets, "hhea", hhea); |
|
|
|
|
|
|
|
|
|
|
@ -732,18 +745,18 @@ var Font = (function () { |
|
|
|
* while Windows use this data. So be careful if you hack on Linux and |
|
|
|
* while Windows use this data. So be careful if you hack on Linux and |
|
|
|
* have to touch the 'hmtx' table |
|
|
|
* have to touch the 'hmtx' table |
|
|
|
*/ |
|
|
|
*/ |
|
|
|
hmtx = "\x01\xF4\x00\x00"; // Fake .notdef
|
|
|
|
hmtx = "\x00\x00\x00\x00"; // Fake .notdef
|
|
|
|
var width = 0, lsb = 0; |
|
|
|
var width = 0, lsb = 0; |
|
|
|
for (var i = 0; i < charstrings.length; i++) { |
|
|
|
for (var i = 0; i < charstrings.length; i++) { |
|
|
|
width = charstrings[i].charstring[1]; |
|
|
|
var charstring = charstrings[i]; |
|
|
|
hmtx += string16(width) + string16(lsb); |
|
|
|
hmtx += string16(charstring.width) + string16(0); |
|
|
|
} |
|
|
|
} |
|
|
|
hmtx = stringToArray(hmtx); |
|
|
|
hmtx = stringToArray(hmtx); |
|
|
|
createTableEntry(otf, offsets, "hmtx", hmtx); |
|
|
|
createTableEntry(otf, offsets, "hmtx", hmtx); |
|
|
|
|
|
|
|
|
|
|
|
/** MAXP */ |
|
|
|
/** MAXP */ |
|
|
|
maxp = "\x00\x00\x50\x00" + // Version number
|
|
|
|
maxp = "\x00\x00\x50\x00" + // Version number
|
|
|
|
string16(charstrings.length + 1); // Num of glyphs (+1 to pass the sanitizer...)
|
|
|
|
string16(charstrings.length + 1); // Num of glyphs
|
|
|
|
maxp = stringToArray(maxp); |
|
|
|
maxp = stringToArray(maxp); |
|
|
|
createTableEntry(otf, offsets, "maxp", maxp); |
|
|
|
createTableEntry(otf, offsets, "maxp", maxp); |
|
|
|
|
|
|
|
|
|
|
@ -854,7 +867,7 @@ var FontsUtils = { |
|
|
|
bytes.set([value]); |
|
|
|
bytes.set([value]); |
|
|
|
return bytes[0]; |
|
|
|
return bytes[0]; |
|
|
|
} else if (bytesCount == 2) { |
|
|
|
} else if (bytesCount == 2) { |
|
|
|
bytes.set([value >> 8, value]); |
|
|
|
bytes.set([value >> 8, value & 0xff]); |
|
|
|
return [bytes[0], bytes[1]]; |
|
|
|
return [bytes[0], bytes[1]]; |
|
|
|
} else if (bytesCount == 4) { |
|
|
|
} else if (bytesCount == 4) { |
|
|
|
bytes.set([value >> 24, value >> 16, value >> 8, value]); |
|
|
|
bytes.set([value >> 24, value >> 16, value >> 8, value]); |
|
|
@ -995,16 +1008,8 @@ var Type1Parser = function() { |
|
|
|
"12": "div", |
|
|
|
"12": "div", |
|
|
|
|
|
|
|
|
|
|
|
// callothersubr is a mechanism to make calls on the postscript
|
|
|
|
// callothersubr is a mechanism to make calls on the postscript
|
|
|
|
// interpreter.
|
|
|
|
// interpreter, this is not supported by Type2 charstring but hopefully
|
|
|
|
// TODO When decodeCharstring encounter such a command it should
|
|
|
|
// most of the default commands can be ignored safely.
|
|
|
|
// directly do:
|
|
|
|
|
|
|
|
// - pop the previous charstring[] command into 'index'
|
|
|
|
|
|
|
|
// - pop the previous charstring[] command and ignore it, it is
|
|
|
|
|
|
|
|
// normally the number of element to push on the stack before
|
|
|
|
|
|
|
|
// the command but since everything will be pushed on the stack
|
|
|
|
|
|
|
|
// by the PS interpreter when it will read them that is safe to
|
|
|
|
|
|
|
|
// ignore this command
|
|
|
|
|
|
|
|
// - push the content of the OtherSubrs[index] inside charstring[]
|
|
|
|
|
|
|
|
"16": "callothersubr", |
|
|
|
"16": "callothersubr", |
|
|
|
|
|
|
|
|
|
|
|
"17": "pop", |
|
|
|
"17": "pop", |
|
|
@ -1024,8 +1029,13 @@ var Type1Parser = function() { |
|
|
|
"31": "hvcurveto" |
|
|
|
"31": "hvcurveto" |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var kEscapeCommand = 12; |
|
|
|
|
|
|
|
|
|
|
|
function decodeCharString(array) { |
|
|
|
function decodeCharString(array) { |
|
|
|
var charString = []; |
|
|
|
var charstring = []; |
|
|
|
|
|
|
|
var lsb = 0; |
|
|
|
|
|
|
|
var width = 0; |
|
|
|
|
|
|
|
var used = false; |
|
|
|
|
|
|
|
|
|
|
|
var value = ""; |
|
|
|
var value = ""; |
|
|
|
var count = array.length; |
|
|
|
var count = array.length; |
|
|
@ -1034,10 +1044,48 @@ var Type1Parser = function() { |
|
|
|
|
|
|
|
|
|
|
|
if (value < 32) { |
|
|
|
if (value < 32) { |
|
|
|
var command = null; |
|
|
|
var command = null; |
|
|
|
if (value == 12) { |
|
|
|
if (value == kEscapeCommand) { |
|
|
|
var escape = array[++i]; |
|
|
|
var escape = array[++i]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// TODO Clean this code
|
|
|
|
|
|
|
|
if (escape == 16) { |
|
|
|
|
|
|
|
var index = charstring.pop(); |
|
|
|
|
|
|
|
var argc = charstring.pop(); |
|
|
|
|
|
|
|
var data = charstring.pop(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// If the flex mechanishm is not used in a font program, Adobe
|
|
|
|
|
|
|
|
// state that that entries 0, 1 and 2 can simply be replace by
|
|
|
|
|
|
|
|
// {}, which means that we can simply ignore them.
|
|
|
|
|
|
|
|
if (index < 3) { |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// This is the same things about hint replacement, if it is not used
|
|
|
|
|
|
|
|
// entry 3 can be replaced by {3}
|
|
|
|
|
|
|
|
if (index == 3) { |
|
|
|
|
|
|
|
charstring.push(3); |
|
|
|
|
|
|
|
i++; |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
command = charStringDictionary["12"][escape]; |
|
|
|
command = charStringDictionary["12"][escape]; |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
|
|
|
|
// TODO Clean this code
|
|
|
|
|
|
|
|
if (value == 13) { |
|
|
|
|
|
|
|
if (charstring.length == 2) { |
|
|
|
|
|
|
|
width = charstring[1]; |
|
|
|
|
|
|
|
} else if (charstring.length == 4 && charstring[3] == "div") { |
|
|
|
|
|
|
|
width = charstring[1] / charstring[2]; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
error("Unsupported hsbw format: " + charstring); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
lsb = charstring[0]; |
|
|
|
|
|
|
|
charstring.push(lsb, "hmoveto"); |
|
|
|
|
|
|
|
charstring.splice(0, 1); |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
command = charStringDictionary[value]; |
|
|
|
command = charStringDictionary[value]; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
@ -1059,16 +1107,14 @@ var Type1Parser = function() { |
|
|
|
} else if (value <= 254) { |
|
|
|
} else if (value <= 254) { |
|
|
|
value = -((value - 251) * 256) - parseInt(array[++i]) - 108; |
|
|
|
value = -((value - 251) * 256) - parseInt(array[++i]) - 108; |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
var byte = array[++i]; |
|
|
|
value = (array[++i] & 0xff) << 24 | (array[++i] & 0xff) << 16 | |
|
|
|
var high = (byte >> 1); |
|
|
|
(array[++i] & 0xff) << 8 | (array[++i] & 0xff) << 0; |
|
|
|
value = (byte - high) << 24 | array[++i] << 16 | |
|
|
|
|
|
|
|
array[++i] << 8 | array[++i]; |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
charString.push(value); |
|
|
|
charstring.push(value); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
return charString; |
|
|
|
return { charstring: charstring, width: width, lsb: lsb }; |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
/** |
|
|
@ -1095,19 +1141,21 @@ var Type1Parser = function() { |
|
|
|
length = parseInt(length); |
|
|
|
length = parseInt(length); |
|
|
|
var data = eexecString.slice(i + 3, i + 3 + length); |
|
|
|
var data = eexecString.slice(i + 3, i + 3 + length); |
|
|
|
var encodedSubr = decrypt(data, kCharStringsEncryptionKey, 4); |
|
|
|
var encodedSubr = decrypt(data, kCharStringsEncryptionKey, 4); |
|
|
|
var subr = decodeCharString(encodedSubr); |
|
|
|
var str = decodeCharString(encodedSubr); |
|
|
|
|
|
|
|
|
|
|
|
subrs.push(subr); |
|
|
|
subrs.push(str.charstring); |
|
|
|
i += 3 + length; |
|
|
|
i += 3 + length; |
|
|
|
} else if (inGlyphs && c == 0x52) { |
|
|
|
} else if (inGlyphs && c == 0x52) { |
|
|
|
length = parseInt(length); |
|
|
|
length = parseInt(length); |
|
|
|
var data = eexecString.slice(i + 3, i + 3 + length); |
|
|
|
var data = eexecString.slice(i + 3, i + 3 + length); |
|
|
|
var encodedCharstring = decrypt(data, kCharStringsEncryptionKey, 4); |
|
|
|
var encodedCharstring = decrypt(data, kCharStringsEncryptionKey, 4); |
|
|
|
var subr = decodeCharString(encodedCharstring); |
|
|
|
var str = decodeCharString(encodedCharstring); |
|
|
|
|
|
|
|
|
|
|
|
glyphs.push({ |
|
|
|
glyphs.push({ |
|
|
|
glyph: glyph, |
|
|
|
glyph: glyph, |
|
|
|
data: subr |
|
|
|
data: str.charstring, |
|
|
|
|
|
|
|
lsb: str.lsb, |
|
|
|
|
|
|
|
width: str.width |
|
|
|
}); |
|
|
|
}); |
|
|
|
i += 3 + length; |
|
|
|
i += 3 + length; |
|
|
|
} else if (inGlyphs && c == 0x2F) { |
|
|
|
} else if (inGlyphs && c == 0x2F) { |
|
|
@ -1269,16 +1317,18 @@ CFF.prototype = { |
|
|
|
var charstrings = []; |
|
|
|
var charstrings = []; |
|
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < glyphs.length; i++) { |
|
|
|
for (var i = 0; i < glyphs.length; i++) { |
|
|
|
var glyph = glyphs[i].glyph; |
|
|
|
var glyph = glyphs[i]; |
|
|
|
var unicode = GlyphsUnicode[glyph]; |
|
|
|
var unicode = GlyphsUnicode[glyph.glyph]; |
|
|
|
if (!unicode) { |
|
|
|
if (!unicode) { |
|
|
|
if (glyph != ".notdef") |
|
|
|
if (glyph.glyph != ".notdef") |
|
|
|
warn(glyph + " does not have an entry in the glyphs unicode dictionary"); |
|
|
|
warn(glyph + " does not have an entry in the glyphs unicode dictionary"); |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
charstrings.push({ |
|
|
|
charstrings.push({ |
|
|
|
glyph: glyph, |
|
|
|
glyph: glyph, |
|
|
|
unicode: unicode, |
|
|
|
unicode: unicode, |
|
|
|
charstring: glyphs[i].data |
|
|
|
charstring: glyph.data, |
|
|
|
|
|
|
|
width: glyph.width, |
|
|
|
|
|
|
|
lsb: glyph.lsb |
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
}; |
|
|
|
}; |
|
|
@ -1320,46 +1370,11 @@ CFF.prototype = { |
|
|
|
var i = 0; |
|
|
|
var i = 0; |
|
|
|
while (true) { |
|
|
|
while (true) { |
|
|
|
var obj = charstring[i]; |
|
|
|
var obj = charstring[i]; |
|
|
|
if (obj == null) |
|
|
|
if (obj == undefined) { |
|
|
|
return []; |
|
|
|
error("unknow charstring command for " + i + " in " + charstring); |
|
|
|
|
|
|
|
} |
|
|
|
if (obj.charAt) { |
|
|
|
if (obj.charAt) { |
|
|
|
switch (obj) { |
|
|
|
switch (obj) { |
|
|
|
case "callothersubr": |
|
|
|
|
|
|
|
var index = charstring[i - 1]; |
|
|
|
|
|
|
|
var count = charstring[i - 2]; |
|
|
|
|
|
|
|
var data = charstring[i - 3]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// If the flex mechanishm is not used in a font program, Adobe
|
|
|
|
|
|
|
|
// state that that entries 0, 1 and 2 can simply be replace by
|
|
|
|
|
|
|
|
// {}, which means that we can simply ignore them.
|
|
|
|
|
|
|
|
if (index < 3) { |
|
|
|
|
|
|
|
i -= 3; |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// This is the same things about hint replacment, if it is not used
|
|
|
|
|
|
|
|
// entry 3 can be replaced by {}
|
|
|
|
|
|
|
|
if (index == 3) { |
|
|
|
|
|
|
|
if (!data) { |
|
|
|
|
|
|
|
charstring.splice(i - 2, 4, 3); |
|
|
|
|
|
|
|
i -= 3; |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
// 5 to remove the arguments, the callothersubr call and the pop command
|
|
|
|
|
|
|
|
charstring.splice(i - 3, 5, 3); |
|
|
|
|
|
|
|
i -= 3; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case "hsbw": |
|
|
|
|
|
|
|
var charWidthVector = charstring[1]; |
|
|
|
|
|
|
|
var leftSidebearing = charstring[0]; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
charstring.splice(i, 1, leftSidebearing, "hmoveto"); |
|
|
|
|
|
|
|
charstring.splice(0, 1); |
|
|
|
|
|
|
|
break; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
case "endchar": |
|
|
|
case "endchar": |
|
|
|
case "return": |
|
|
|
case "return": |
|
|
|
// CharString is ready to be re-encode to commands number at this point
|
|
|
|
// CharString is ready to be re-encode to commands number at this point
|
|
|
@ -1371,7 +1386,7 @@ CFF.prototype = { |
|
|
|
} else if (command.charAt) { |
|
|
|
} else if (command.charAt) { |
|
|
|
var cmd = this.commandsMap[command]; |
|
|
|
var cmd = this.commandsMap[command]; |
|
|
|
if (!cmd) |
|
|
|
if (!cmd) |
|
|
|
error(command); |
|
|
|
error("Unknow command: " + command); |
|
|
|
|
|
|
|
|
|
|
|
if (IsArray(cmd)) { |
|
|
|
if (IsArray(cmd)) { |
|
|
|
charstring.splice(j, 1, cmd[0], cmd[1]); |
|
|
|
charstring.splice(j, 1, cmd[0], cmd[1]); |
|
|
|