Merge pull request #108 from vingtetun/master

Fix some errors in the font headers and scale the canvas before drawing the font to have more precision
This commit is contained in:
Andreas Gal 2011-06-28 18:10:49 -07:00
commit 16e1eadf93
2 changed files with 95 additions and 77 deletions

161
fonts.js
View File

@ -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]);

9
pdf.js
View File

@ -3807,18 +3807,21 @@ var CanvasGraphics = (function() {
if (fontDescriptor && fontDescriptor.num) { if (fontDescriptor && fontDescriptor.num) {
var fontDescriptor = this.xref.fetchIfRef(fontDescriptor); var fontDescriptor = this.xref.fetchIfRef(fontDescriptor);
fontName = fontDescriptor.get("FontName").name.replace("+", "_"); fontName = fontDescriptor.get("FontName").name.replace("+", "_");
Fonts.active = fontName; Fonts.setActive(fontName, size);
} }
if (!fontName) { if (!fontName) {
// TODO: fontDescriptor is not available, fallback to default font // TODO: fontDescriptor is not available, fallback to default font
this.current.fontSize = size; this.current.fontSize = size;
this.ctx.font = this.current.fontSize + 'px sans-serif'; this.ctx.font = this.current.fontSize + 'px sans-serif';
Fonts.setActive("sans-serif", this.current.fontSize);
return; return;
} }
this.current.fontName = fontName;
this.current.fontSize = size; this.current.fontSize = size;
this.ctx.font = this.current.fontSize +'px "' + fontName + '", Symbol';
this.ctx.font = this.current.fontSize + 'px "' + fontName + '"';
if (this.ctx.$setFont) { if (this.ctx.$setFont) {
this.ctx.$setFont(fontName); this.ctx.$setFont(fontName);
} }
@ -3865,7 +3868,7 @@ var CanvasGraphics = (function() {
text = Fonts.charsToUnicode(text); text = Fonts.charsToUnicode(text);
this.ctx.translate(this.current.x, -1 * this.current.y); this.ctx.translate(this.current.x, -1 * this.current.y);
this.ctx.fillText(text, 0, 0); this.ctx.fillText(text, 0, 0);
this.current.x += this.ctx.measureText(text).width; this.current.x += Fonts.measureText(text);
} }
this.ctx.restore(); this.ctx.restore();