Browse Source

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
Andreas Gal 14 years ago
parent
commit
16e1eadf93
  1. 161
      fonts.js
  2. 11
      pdf.js

161
fonts.js

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

11
pdf.js

@ -641,7 +641,7 @@ var PredictorStream = (function() {
var pixBytes = this.pixBytes = (colors * bits + 7) >> 3; var pixBytes = this.pixBytes = (colors * bits + 7) >> 3;
// add an extra pixByte to represent the pixel left of column 0 // add an extra pixByte to represent the pixel left of column 0
var rowBytes = this.rowBytes = (columns * colors * bits + 7) >> 3; var rowBytes = this.rowBytes = (columns * colors * bits + 7) >> 3;
DecodeStream.call(this); DecodeStream.call(this);
return this; return this;
} }
@ -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();

Loading…
Cancel
Save