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:
commit
16e1eadf93
161
fonts.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]);
|
||||||
|
9
pdf.js
9
pdf.js
@ -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…
Reference in New Issue
Block a user