Merge pull request #147 from vingtetun/master
Code cleanup and fix and infinite loop issue on intelisa.pdf, page 37 (Issue #146)
This commit is contained in:
commit
7a306afe41
584
fonts.js
584
fonts.js
@ -36,13 +36,13 @@ var kDisableFonts = false;
|
|||||||
* http://cgit.freedesktop.org/poppler/poppler/tree/poppler/GfxFont.cc#n65
|
* http://cgit.freedesktop.org/poppler/poppler/tree/poppler/GfxFont.cc#n65
|
||||||
*/
|
*/
|
||||||
|
|
||||||
var Fonts = (function () {
|
var Fonts = (function Fonts() {
|
||||||
var kScalePrecision = 40;
|
var kScalePrecision = 40;
|
||||||
var fonts = Object.create(null);
|
var fonts = Object.create(null);
|
||||||
|
|
||||||
if (!isWorker) {
|
if (!isWorker) {
|
||||||
var ctx = document.createElement("canvas").getContext("2d");
|
var ctx = document.createElement("canvas").getContext("2d");
|
||||||
ctx.scale(1 / kScalePrecision, 1);
|
ctx.scale(1 / kScalePrecision, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Font(name, data, properties) {
|
function Font(name, data, properties) {
|
||||||
@ -536,25 +536,27 @@ var Font = (function () {
|
|||||||
glyphs.push({ unicode: 0x0000 });
|
glyphs.push({ unicode: 0x0000 });
|
||||||
var ranges = getRanges(glyphs);
|
var ranges = getRanges(glyphs);
|
||||||
|
|
||||||
var headerSize = (12 * 2 + (ranges.length * 4 * 2));
|
var numTables = 1;
|
||||||
|
var cmap = "\x00\x00" + // version
|
||||||
|
string16(numTables) + // numTables
|
||||||
|
"\x00\x03" + // platformID
|
||||||
|
"\x00\x01" + // encodingID
|
||||||
|
string32(4 + numTables * 8); // start of the table record
|
||||||
|
|
||||||
|
var headerSize = (12 * 2 + (ranges.length * 5 * 2));
|
||||||
var segCount = ranges.length + 1;
|
var segCount = ranges.length + 1;
|
||||||
var segCount2 = segCount * 2;
|
var segCount2 = segCount * 2;
|
||||||
var searchRange = FontsUtils.getMaxPower2(segCount) * 2;
|
var searchRange = FontsUtils.getMaxPower2(segCount) * 2;
|
||||||
var searchEntry = Math.log(segCount) / Math.log(2);
|
var searchEntry = Math.log(segCount) / Math.log(2);
|
||||||
var rangeShift = 2 * segCount - searchRange;
|
var rangeShift = 2 * segCount - searchRange;
|
||||||
|
|
||||||
var cmap = "\x00\x00" + // version
|
var format314 = "\x00\x04" + // format
|
||||||
"\x00\x01" + // numTables
|
string16(headerSize) + // length
|
||||||
"\x00\x03" + // platformID
|
"\x00\x00" + // language
|
||||||
"\x00\x01" + // encodingID
|
string16(segCount2) +
|
||||||
"\x00\x00\x00\x0C" + // start of the table record
|
string16(searchRange) +
|
||||||
"\x00\x04" + // format
|
string16(searchEntry) +
|
||||||
string16(headerSize) + // length
|
string16(rangeShift);
|
||||||
"\x00\x00" + // languages
|
|
||||||
string16(segCount2) +
|
|
||||||
string16(searchRange) +
|
|
||||||
string16(searchEntry) +
|
|
||||||
string16(rangeShift);
|
|
||||||
|
|
||||||
// Fill up the 4 parallel arrays describing the segments.
|
// Fill up the 4 parallel arrays describing the segments.
|
||||||
var startCount = "";
|
var startCount = "";
|
||||||
@ -567,25 +569,27 @@ var Font = (function () {
|
|||||||
var range = ranges[i];
|
var range = ranges[i];
|
||||||
var start = range[0];
|
var start = range[0];
|
||||||
var end = range[1];
|
var end = range[1];
|
||||||
var delta = (((start - 1) - bias) ^ 0xffff);
|
var delta = (bias - start) % 0xffff;
|
||||||
bias += (end - start + 1);
|
bias += (end - start + 1);
|
||||||
|
|
||||||
startCount += string16(start);
|
startCount += string16(start);
|
||||||
endCount += string16(end);
|
endCount += string16(end);
|
||||||
idDeltas += string16(delta);
|
idDeltas += string16(delta);
|
||||||
idRangeOffsets += string16(0);
|
idRangeOffsets += string16(0);
|
||||||
|
|
||||||
for (var j = 0; j < range.length; j++)
|
for (var j = start; j <= end; j++) {
|
||||||
glyphsIds += String.fromCharCode(range[j]);
|
glyphsIds += string16(j);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
startCount += "\xFF\xFF";
|
|
||||||
endCount += "\xFF\xFF";
|
endCount += "\xFF\xFF";
|
||||||
|
startCount += "\xFF\xFF";
|
||||||
idDeltas += "\x00\x01";
|
idDeltas += "\x00\x01";
|
||||||
idRangeOffsets += "\x00\x00";
|
idRangeOffsets += "\x00\x00";
|
||||||
|
format314 += endCount + "\x00\x00" + startCount +
|
||||||
|
idDeltas + idRangeOffsets + glyphsIds;
|
||||||
|
|
||||||
return stringToArray(cmap + endCount + "\x00\x00" + startCount +
|
return stringToArray(cmap + format314);
|
||||||
idDeltas + idRangeOffsets + glyphsIds);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
function createOS2Table(properties) {
|
function createOS2Table(properties) {
|
||||||
@ -625,7 +629,7 @@ var Font = (function () {
|
|||||||
"\x02\x24" + // xAvgCharWidth
|
"\x02\x24" + // xAvgCharWidth
|
||||||
"\x01\xF4" + // usWeightClass
|
"\x01\xF4" + // usWeightClass
|
||||||
"\x00\x05" + // usWidthClass
|
"\x00\x05" + // usWidthClass
|
||||||
"\x00\x02" + // fstype
|
"\x00\x00" + // fstype (0 to let the font loads via font-face on IE)
|
||||||
"\x02\x8A" + // ySubscriptXSize
|
"\x02\x8A" + // ySubscriptXSize
|
||||||
"\x02\xBB" + // ySubscriptYSize
|
"\x02\xBB" + // ySubscriptYSize
|
||||||
"\x00\x00" + // ySubscriptXOffset
|
"\x00\x00" + // ySubscriptXOffset
|
||||||
@ -720,6 +724,8 @@ var Font = (function () {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function replaceCMapTable(cmap, font, properties) {
|
function replaceCMapTable(cmap, font, properties) {
|
||||||
|
font.pos = (font.start ? font.start : 0) + 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));
|
||||||
|
|
||||||
@ -1110,9 +1116,7 @@ var Font = (function () {
|
|||||||
var url = "url(data:" + this.mimetype + ";base64," + window.btoa(data) + ");";
|
var url = "url(data:" + this.mimetype + ";base64," + window.btoa(data) + ");";
|
||||||
var rule = "@font-face { font-family:'" + fontName + "';src:" + url + "}";
|
var rule = "@font-face { font-family:'" + fontName + "';src:" + url + "}";
|
||||||
var styleSheet = document.styleSheets[0];
|
var styleSheet = document.styleSheets[0];
|
||||||
styleSheet.insertRule(rule, styleSheet.length);
|
styleSheet.insertRule(rule, styleSheet.cssRules.length);
|
||||||
|
|
||||||
return rule;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1387,15 +1391,36 @@ var Type1Parser = function() {
|
|||||||
* Returns an object containing a Subrs array and a CharStrings array
|
* Returns an object containing a Subrs array and a CharStrings array
|
||||||
* extracted from and eexec encrypted block of data
|
* extracted from and eexec encrypted block of data
|
||||||
*/
|
*/
|
||||||
this.extractFontProgram = function t1_extractFontProgram(stream) {
|
function readNumberArray(str, index) {
|
||||||
var eexecString = decrypt(stream, kEexecEncryptionKey, 4);
|
var start = ++index;
|
||||||
var subrs = [], glyphs = [];
|
var count = 0;
|
||||||
var inGlyphs = false;
|
while (str[index++] != "]")
|
||||||
var inSubrs = false;
|
count++;
|
||||||
var glyph = "";
|
|
||||||
|
|
||||||
|
var array = str.substr(start, count).split(" ");
|
||||||
|
for (var i = 0; i < array.length; i++)
|
||||||
|
array[i] = parseFloat(array[i] || 0);
|
||||||
|
return array;
|
||||||
|
};
|
||||||
|
|
||||||
|
this.extractFontProgram = function t1_extractFontProgram(stream) {
|
||||||
|
var eexec = decrypt(stream, kEexecEncryptionKey, 4);
|
||||||
|
var eexecString = "";
|
||||||
|
for (var i = 0; i < eexec.length; i++)
|
||||||
|
eexecString += String.fromCharCode(eexec[i]);
|
||||||
|
|
||||||
|
var glyphsSection = false, subrsSection = false;
|
||||||
|
var extracted = {
|
||||||
|
subrs: [],
|
||||||
|
charstrings: [],
|
||||||
|
properties: {
|
||||||
|
stemSnapH: [0, 0],
|
||||||
|
stemSnapV: [0, 0]
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var glyph = "";
|
||||||
var token = "";
|
var token = "";
|
||||||
var index = 0;
|
|
||||||
var length = 0;
|
var length = 0;
|
||||||
|
|
||||||
var c = "";
|
var c = "";
|
||||||
@ -1403,53 +1428,95 @@ var Type1Parser = function() {
|
|||||||
for (var i = 0; i < count; i++) {
|
for (var i = 0; i < count; i++) {
|
||||||
var c = eexecString[i];
|
var c = eexecString[i];
|
||||||
|
|
||||||
if (inSubrs && c == 0x52) {
|
if ((glyphsSection || subrsSection) && c == "R") {
|
||||||
length = parseInt(length);
|
var data = eexec.slice(i + 3, i + 3 + length);
|
||||||
var data = eexecString.slice(i + 3, i + 3 + length);
|
var encoded = decrypt(data, kCharStringsEncryptionKey, 4);
|
||||||
var encodedSubr = decrypt(data, kCharStringsEncryptionKey, 4);
|
var str = decodeCharString(encoded);
|
||||||
var str = decodeCharString(encodedSubr);
|
|
||||||
|
|
||||||
subrs.push(str.charstring);
|
if (glyphsSection) {
|
||||||
i += 3 + length;
|
extracted.charstrings.push({
|
||||||
} else if (inGlyphs && c == 0x52) {
|
|
||||||
length = parseInt(length);
|
|
||||||
var data = eexecString.slice(i + 3, i + 3 + length);
|
|
||||||
var encodedCharstring = decrypt(data, kCharStringsEncryptionKey, 4);
|
|
||||||
var str = decodeCharString(encodedCharstring);
|
|
||||||
|
|
||||||
glyphs.push({
|
|
||||||
glyph: glyph,
|
glyph: glyph,
|
||||||
data: str.charstring,
|
data: str.charstring,
|
||||||
lsb: str.lsb,
|
lsb: str.lsb,
|
||||||
width: str.width
|
width: str.width
|
||||||
});
|
});
|
||||||
i += 3 + length;
|
} else {
|
||||||
} else if (inGlyphs && c == 0x2F) {
|
extracted.subrs.push(str.charstring);
|
||||||
|
}
|
||||||
|
i += length + 3;
|
||||||
|
} else if (c == " " || c == "\n") {
|
||||||
|
length = parseInt(token);
|
||||||
token = "";
|
token = "";
|
||||||
glyph = "";
|
|
||||||
|
|
||||||
while ((c = eexecString[++i]) != 0x20)
|
|
||||||
glyph += String.fromCharCode(c);
|
|
||||||
} else if (!inSubrs && !inGlyphs && c == 0x2F && eexecString[i+1] == 0x53) {
|
|
||||||
while ((c = eexecString[++i]) != 0x20) {};
|
|
||||||
inSubrs = true;
|
|
||||||
} else if (c == 0x20) {
|
|
||||||
index = length;
|
|
||||||
length = token;
|
|
||||||
token = "";
|
|
||||||
} else if (c == 0x2F && eexecString[i+1] == 0x43 && eexecString[i+2] == 0x68) {
|
|
||||||
while ((c = eexecString[++i]) != 0x20) {};
|
|
||||||
inSubrs = false;
|
|
||||||
inGlyphs = true;
|
|
||||||
} else {
|
} else {
|
||||||
token += String.fromCharCode(c);
|
token += c;
|
||||||
|
if (!glyphsSection) {
|
||||||
|
switch (token) {
|
||||||
|
case "/CharString":
|
||||||
|
glyphsSection = true;
|
||||||
|
break;
|
||||||
|
case "/Subrs":
|
||||||
|
subrsSection = true;
|
||||||
|
break;
|
||||||
|
case "/StdHW":
|
||||||
|
extracted.properties.stdHW = readNumberArray(eexecString, i + 2)[0];
|
||||||
|
break;
|
||||||
|
case "/StdVW":
|
||||||
|
extracted.properties.stdVW = readNumberArray(eexecString, i + 2)[0];
|
||||||
|
break;
|
||||||
|
case "/StemSnapH":
|
||||||
|
extracted.properties.stemSnapH = readNumberArray(eexecString, i + 2);
|
||||||
|
break;
|
||||||
|
case "/StemSnapV":
|
||||||
|
extracted.properties.stemSnapV = readNumberArray(eexecString, i + 2);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (c == "/") {
|
||||||
|
token = glyph = "";
|
||||||
|
while ((c = eexecString[++i]) != " ")
|
||||||
|
glyph += c;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return {
|
|
||||||
subrs: subrs,
|
return extracted;
|
||||||
charstrings: glyphs
|
},
|
||||||
|
|
||||||
|
this.extractFontHeader = function t1_extractFontProgram(stream) {
|
||||||
|
var headerString = "";
|
||||||
|
for (var i = 0; i < stream.length; i++)
|
||||||
|
headerString += String.fromCharCode(stream[i]);
|
||||||
|
|
||||||
|
var info = {
|
||||||
|
textMatrix: null
|
||||||
|
};
|
||||||
|
|
||||||
|
var token = "";
|
||||||
|
var count = headerString.length;
|
||||||
|
for (var i = 0; i < count; i++) {
|
||||||
|
var c = headerString[i];
|
||||||
|
if (c == " " || c == "\n") {
|
||||||
|
switch (token) {
|
||||||
|
case "/FontMatrix":
|
||||||
|
var matrix = readNumberArray(headerString, i + 1);
|
||||||
|
|
||||||
|
// The FontMatrix is in unitPerEm, so make it pixels
|
||||||
|
for (var j = 0; j < matrix.length; j++)
|
||||||
|
matrix[j] *= 1000;
|
||||||
|
|
||||||
|
// Make the angle into the right direction
|
||||||
|
matrix[2] *= -1;
|
||||||
|
|
||||||
|
info.textMatrix = matrix;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
token = "";
|
||||||
|
} else {
|
||||||
|
token += c;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
return info;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -1520,14 +1587,24 @@ var CFF = function(name, file, properties) {
|
|||||||
// Get the data block containing glyphs and subrs informations
|
// Get the data block containing glyphs and subrs informations
|
||||||
var length1 = file.dict.get("Length1");
|
var length1 = file.dict.get("Length1");
|
||||||
var length2 = file.dict.get("Length2");
|
var length2 = file.dict.get("Length2");
|
||||||
file.skip(length1);
|
|
||||||
var eexecBlock = file.getBytes(length2);
|
var headerBlock = file.getBytes(length1);
|
||||||
|
var header = type1Parser.extractFontHeader(headerBlock);
|
||||||
|
for (var info in header)
|
||||||
|
properties[info] = header[info];
|
||||||
|
|
||||||
// Decrypt the data blocks and retrieve it's content
|
// Decrypt the data blocks and retrieve it's content
|
||||||
|
var eexecBlock = file.getBytes(length2);
|
||||||
var data = type1Parser.extractFontProgram(eexecBlock);
|
var data = type1Parser.extractFontProgram(eexecBlock);
|
||||||
|
for (var info in data.properties)
|
||||||
|
properties[info] = data.properties[info];
|
||||||
|
|
||||||
this.charstrings = this.getOrderedCharStrings(data.charstrings);
|
var charstrings = this.getOrderedCharStrings(data.charstrings);
|
||||||
this.data = this.wrap(name, this.charstrings, data.subrs, properties);
|
var type2Charstrings = this.getType2Charstrings(charstrings);
|
||||||
|
var subrs = this.getType2Subrs(data.subrs);
|
||||||
|
|
||||||
|
this.charstrings = charstrings;
|
||||||
|
this.data = this.wrap(name, type2Charstrings, this.charstrings, subrs, properties);
|
||||||
};
|
};
|
||||||
|
|
||||||
CFF.prototype = {
|
CFF.prototype = {
|
||||||
@ -1538,24 +1615,24 @@ CFF.prototype = {
|
|||||||
// If there is no object, just create an array saying that with another
|
// If there is no object, just create an array saying that with another
|
||||||
// offset byte.
|
// offset byte.
|
||||||
if (count == 0)
|
if (count == 0)
|
||||||
return [0x00, 0x00, 0x00];
|
return "\x00\x00\x00";
|
||||||
|
|
||||||
var data = [];
|
var data = "";
|
||||||
var bytes = FontsUtils.integerToBytes(count, 2);
|
var bytes = FontsUtils.integerToBytes(count, 2);
|
||||||
for (var i = 0; i < bytes.length; i++)
|
for (var i = 0; i < bytes.length; i++)
|
||||||
data.push(bytes[i]);
|
data += String.fromCharCode(bytes[i]);
|
||||||
|
|
||||||
// Next byte contains the offset size use to reference object in the file
|
// 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
|
// Actually we're using 0x04 to be sure to be able to store everything
|
||||||
// without thinking of it while coding.
|
// without thinking of it while coding.
|
||||||
data.push(0x04);
|
data += "\x04";
|
||||||
|
|
||||||
// Add another offset after this one because we need a new offset
|
// Add another offset after this one because we need a new offset
|
||||||
var relativeOffset = 1;
|
var relativeOffset = 1;
|
||||||
for (var i = 0; i < count + 1; i++) {
|
for (var i = 0; i < count + 1; i++) {
|
||||||
var bytes = FontsUtils.integerToBytes(relativeOffset, 4);
|
var bytes = FontsUtils.integerToBytes(relativeOffset, 4);
|
||||||
for (var j = 0; j < bytes.length; j++)
|
for (var j = 0; j < bytes.length; j++)
|
||||||
data.push(bytes[j]);
|
data += String.fromCharCode(bytes[j]);
|
||||||
|
|
||||||
if (objects[i])
|
if (objects[i])
|
||||||
relativeOffset += objects[i].length;
|
relativeOffset += objects[i].length;
|
||||||
@ -1563,17 +1640,22 @@ CFF.prototype = {
|
|||||||
|
|
||||||
for (var i =0; i < count; i++) {
|
for (var i =0; i < count; i++) {
|
||||||
for (var j = 0; j < objects[i].length; j++)
|
for (var j = 0; j < objects[i].length; j++)
|
||||||
data.push(isByte ? objects[i][j] : objects[i].charCodeAt(j));
|
data += isByte ? String.fromCharCode(objects[i][j]) : objects[i][j];
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
},
|
},
|
||||||
|
|
||||||
encodeNumber: function cff_encodeNumber(value) {
|
encodeNumber: function cff_encodeNumber(value) {
|
||||||
var x = 0;
|
|
||||||
if (value >= -32768 && value <= 32767) {
|
if (value >= -32768 && value <= 32767) {
|
||||||
return [ 28, value >> 8, value & 0xFF ];
|
return "\x1c" +
|
||||||
|
String.fromCharCode(value >> 8) +
|
||||||
|
String.fromCharCode(value & 0xFF);
|
||||||
} else if (value >= (-2147483647-1) && value <= 2147483647) {
|
} else if (value >= (-2147483647-1) && value <= 2147483647) {
|
||||||
return [ 0xFF, value >> 24, Value >> 16, value >> 8, value & 0xFF ];
|
return "\xff" +
|
||||||
|
String.fromCharCode(value >> 24) +
|
||||||
|
String.fromCharCode(value >> 16) +
|
||||||
|
String.fromCharCode(value >> 8) +
|
||||||
|
String.fromCharCode(value & 0xFF);
|
||||||
}
|
}
|
||||||
error("Value: " + value + " is not allowed");
|
error("Value: " + value + " is not allowed");
|
||||||
return null;
|
return null;
|
||||||
@ -1605,12 +1687,40 @@ CFF.prototype = {
|
|||||||
return charstrings;
|
return charstrings;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getType2Charstrings: function cff_getType2Charstrings(type1Charstrings) {
|
||||||
|
var type2Charstrings = [];
|
||||||
|
var count = type1Charstrings.length;
|
||||||
|
for (var i = 0; i < count; i++) {
|
||||||
|
var charstring = type1Charstrings[i].charstring;
|
||||||
|
type2Charstrings.push(this.flattenCharstring(charstring.slice(), this.commandsMap));
|
||||||
|
}
|
||||||
|
return type2Charstrings;
|
||||||
|
},
|
||||||
|
|
||||||
|
getType2Subrs: function cff_getType2Charstrings(type1Subrs) {
|
||||||
|
var bias = 0;
|
||||||
|
var count = type1Subrs.length;
|
||||||
|
if (count < 1240)
|
||||||
|
bias = 107;
|
||||||
|
else if (count < 33900)
|
||||||
|
bias = 1131;
|
||||||
|
else
|
||||||
|
bias = 32768;
|
||||||
|
|
||||||
|
// Add a bunch of empty subrs to deal with the Type2 bias
|
||||||
|
var type2Subrs = [];
|
||||||
|
for (var i = 0; i < bias; i++)
|
||||||
|
type2Subrs.push([0x0B]);
|
||||||
|
|
||||||
|
for (var i = 0; i < count; i++)
|
||||||
|
type2Subrs.push(this.flattenCharstring(type1Subrs[i], this.commandsMap));
|
||||||
|
|
||||||
|
return type2Subrs;
|
||||||
|
},
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Flatten the commands by interpreting the postscript code and replacing
|
* Flatten the commands by interpreting the postscript code and replacing
|
||||||
* every 'callsubr', 'callothersubr' by the real commands.
|
* every 'callsubr', 'callothersubr' by the real commands.
|
||||||
*
|
|
||||||
* TODO This function also do a string to command number transformation
|
|
||||||
* that can probably be avoided if the Type1 decodeCharstring code is smarter
|
|
||||||
*/
|
*/
|
||||||
commandsMap: {
|
commandsMap: {
|
||||||
"hstem": 1,
|
"hstem": 1,
|
||||||
@ -1632,194 +1742,134 @@ CFF.prototype = {
|
|||||||
"hvcurveto": 31,
|
"hvcurveto": 31,
|
||||||
},
|
},
|
||||||
|
|
||||||
flattenCharstring: function flattenCharstring(charstring) {
|
flattenCharstring: function flattenCharstring(charstring, map) {
|
||||||
var i = 0;
|
for (var i = 0; i < charstring.length; i++) {
|
||||||
while (true) {
|
var command = charstring[i];
|
||||||
var obj = charstring[i];
|
if (command.charAt) {
|
||||||
if (obj == undefined) {
|
var cmd = map[command];
|
||||||
error("unknow charstring command for " + i + " in " + charstring);
|
assert(cmd, "Unknow command: " + command);
|
||||||
}
|
|
||||||
if (obj.charAt) {
|
|
||||||
switch (obj) {
|
|
||||||
case "endchar":
|
|
||||||
case "return":
|
|
||||||
// CharString is ready to be re-encode to commands number at this point
|
|
||||||
for (var j = 0; j < charstring.length; j++) {
|
|
||||||
var command = charstring[j];
|
|
||||||
if (parseFloat(command) == command) {
|
|
||||||
charstring.splice(j, 1, 28, command >> 8, command);
|
|
||||||
j+= 2;
|
|
||||||
} else if (command.charAt) {
|
|
||||||
var cmd = this.commandsMap[command];
|
|
||||||
if (!cmd)
|
|
||||||
error("Unknow command: " + command);
|
|
||||||
|
|
||||||
if (IsArray(cmd)) {
|
if (IsArray(cmd)) {
|
||||||
charstring.splice(j, 1, cmd[0], cmd[1]);
|
charstring.splice(i++, 1, cmd[0], cmd[1]);
|
||||||
j += 1;
|
} else {
|
||||||
} else {
|
charstring[i] = cmd;
|
||||||
charstring[j] = cmd;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return charstring;
|
|
||||||
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
charstring.splice(i, 1, 28, command >> 8, command & 0xff);
|
||||||
|
i+= 2;
|
||||||
}
|
}
|
||||||
i++;
|
|
||||||
}
|
}
|
||||||
error("failing with i = " + i + " in charstring:" + charstring + "(" + charstring.length + ")");
|
return charstring;
|
||||||
return [];
|
|
||||||
},
|
},
|
||||||
|
|
||||||
wrap: function wrap(name, charstrings, subrs, properties) {
|
wrap: function wrap(name, glyphs, charstrings, subrs, properties) {
|
||||||
// Starts the conversion of the Type1 charstrings to Type2
|
var fields = {
|
||||||
var glyphs = [];
|
"header": "\x01\x00\x04\x04", // major version, minor version, header size, offset size
|
||||||
var glyphsCount = charstrings.length;
|
|
||||||
for (var i = 0; i < glyphsCount; i++) {
|
"names": this.createCFFIndexHeader([name]),
|
||||||
var charstring = charstrings[i].charstring;
|
|
||||||
glyphs.push(this.flattenCharstring(charstring.slice()));
|
"topDict": (function topDict(self) {
|
||||||
|
return function() {
|
||||||
|
var dict =
|
||||||
|
"\x00\x01\x01\x01\x30" +
|
||||||
|
"\xf8\x1b\x00" + // version
|
||||||
|
"\xf8\x1b\x01" + // Notice
|
||||||
|
"\xf8\x1b\x02" + // FullName
|
||||||
|
"\xf8\x1b\x03" + // FamilyName
|
||||||
|
"\xf8\x1b\x04" + // Weight
|
||||||
|
"\x1c\x00\x00\x10"; // Encoding
|
||||||
|
|
||||||
|
var boundingBox = properties.bbox;
|
||||||
|
for (var i = 0; i < boundingBox.length; i++)
|
||||||
|
dict += self.encodeNumber(boundingBox[i]);
|
||||||
|
dict += "\x05"; // FontBBox;
|
||||||
|
|
||||||
|
var offset = fields.header.length +
|
||||||
|
fields.names.length +
|
||||||
|
(dict.length + (4 + 4 + 7)) +
|
||||||
|
fields.strings.length +
|
||||||
|
fields.globalSubrs.length;
|
||||||
|
dict += self.encodeNumber(offset) + "\x0f"; // Charset
|
||||||
|
|
||||||
|
offset = offset + (glyphs.length * 2) + 1;
|
||||||
|
dict += self.encodeNumber(offset) + "\x11"; // Charstrings
|
||||||
|
|
||||||
|
dict += self.encodeNumber(fields.private.length);
|
||||||
|
var offset = offset + fields.charstrings.length;
|
||||||
|
dict += self.encodeNumber(offset) + "\x12"; // Private
|
||||||
|
|
||||||
|
return 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 charset = "\x00"; // Encoding
|
||||||
|
|
||||||
|
var count = glyphs.length;
|
||||||
|
for (var i = 0; i < count; i++) {
|
||||||
|
var index = CFFStrings.indexOf(charstrings[i].glyph.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;
|
||||||
|
|
||||||
|
var bytes = FontsUtils.integerToBytes(index, 2);
|
||||||
|
charset += String.fromCharCode(bytes[0]) + String.fromCharCode(bytes[1]);
|
||||||
|
}
|
||||||
|
return charset;
|
||||||
|
})(this),
|
||||||
|
|
||||||
|
"charstrings": this.createCFFIndexHeader([[0x8B, 0x0E]].concat(glyphs), true),
|
||||||
|
|
||||||
|
"private": (function(self) {
|
||||||
|
var data =
|
||||||
|
"\x8b\x14" + // defaultWidth
|
||||||
|
"\x8b\x15" + // nominalWidth
|
||||||
|
self.encodeNumber(properties.stdHW) + "\x0a" + // StdHW
|
||||||
|
self.encodeNumber(properties.stdVW) + "\x0b"; // StdVW
|
||||||
|
|
||||||
|
var stemH = properties.stemSnapH;
|
||||||
|
for (var i = 0; i < stemH.length; i++)
|
||||||
|
data += self.encodeNumber(stemH[i]);
|
||||||
|
data += "\x0c\x0c"; // StemSnapH
|
||||||
|
|
||||||
|
var stemV = properties.stemSnapV;
|
||||||
|
for (var i = 0; i < stemV.length; i++)
|
||||||
|
data += self.encodeNumber(stemV[i]);
|
||||||
|
data += "\x0c\x0d"; // StemSnapV
|
||||||
|
|
||||||
|
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; i < field.length; i++)
|
||||||
|
cff.push(field.charCodeAt(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a CFF font data
|
return cff;
|
||||||
var cff = new Uint8Array(kMaxFontFileSize);
|
|
||||||
var currentOffset = 0;
|
|
||||||
|
|
||||||
// Font header (major version, minor version, header size, offset size)
|
|
||||||
var header = [0x01, 0x00, 0x04, 0x04];
|
|
||||||
currentOffset += header.length;
|
|
||||||
cff.set(header);
|
|
||||||
|
|
||||||
// Names Index
|
|
||||||
var nameIndex = this.createCFFIndexHeader([name]);
|
|
||||||
cff.set(nameIndex, currentOffset);
|
|
||||||
currentOffset += nameIndex.length;
|
|
||||||
|
|
||||||
// Calculate strings before writing the TopDICT index in order
|
|
||||||
// to calculate correct relative offsets for storing 'charset'
|
|
||||||
// and 'charstrings' data
|
|
||||||
var version = "";
|
|
||||||
var notice = "";
|
|
||||||
var fullName = "";
|
|
||||||
var familyName = "";
|
|
||||||
var weight = "";
|
|
||||||
var strings = [version, notice, fullName,
|
|
||||||
familyName, weight];
|
|
||||||
var stringsIndex = this.createCFFIndexHeader(strings);
|
|
||||||
var stringsDataLength = stringsIndex.length;
|
|
||||||
|
|
||||||
// Create the global subroutines index
|
|
||||||
var globalSubrsIndex = this.createCFFIndexHeader([]);
|
|
||||||
|
|
||||||
// Fill the charset header (first byte is the encoding)
|
|
||||||
var charset = [0x00];
|
|
||||||
for (var i = 0; i < glyphsCount; i++) {
|
|
||||||
var index = CFFStrings.indexOf(charstrings[i].glyph);
|
|
||||||
if (index == -1)
|
|
||||||
index = CFFStrings.length + strings.indexOf(charstrings[i].glyph);
|
|
||||||
var bytes = FontsUtils.integerToBytes(index, 2);
|
|
||||||
charset.push(bytes[0]);
|
|
||||||
charset.push(bytes[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
var charstringsIndex = this.createCFFIndexHeader([[0x8B, 0x0E]].concat(glyphs), true);
|
|
||||||
|
|
||||||
//Top Dict Index
|
|
||||||
var topDictIndex = [
|
|
||||||
0x00, 0x01, 0x01, 0x01, 0x30,
|
|
||||||
248, 27, 0, // version
|
|
||||||
248, 28, 1, // Notice
|
|
||||||
248, 29, 2, // FullName
|
|
||||||
248, 30, 3, // FamilyName
|
|
||||||
248, 31, 4 // Weight
|
|
||||||
];
|
|
||||||
|
|
||||||
var fontBBox = properties.bbox;
|
|
||||||
for (var i = 0; i < fontBBox.length; i++)
|
|
||||||
topDictIndex = topDictIndex.concat(this.encodeNumber(fontBBox[i]));
|
|
||||||
topDictIndex.push(5) // FontBBox;
|
|
||||||
|
|
||||||
var charsetOffset = currentOffset +
|
|
||||||
(topDictIndex.length + (4 + 4 + 4 + 7)) +
|
|
||||||
stringsIndex.length +
|
|
||||||
globalSubrsIndex.length;
|
|
||||||
topDictIndex = topDictIndex.concat(this.encodeNumber(charsetOffset));
|
|
||||||
topDictIndex.push(15); // charset
|
|
||||||
|
|
||||||
topDictIndex = topDictIndex.concat([28, 0, 0, 16]) // Encoding
|
|
||||||
|
|
||||||
var charstringsOffset = charsetOffset + (glyphsCount * 2) + 1;
|
|
||||||
topDictIndex = topDictIndex.concat(this.encodeNumber(charstringsOffset));
|
|
||||||
topDictIndex.push(17); // charstrings
|
|
||||||
|
|
||||||
topDictIndex = topDictIndex.concat([28, 0, 55])
|
|
||||||
var privateOffset = charstringsOffset + charstringsIndex.length;
|
|
||||||
topDictIndex = topDictIndex.concat(this.encodeNumber(privateOffset));
|
|
||||||
topDictIndex.push(18); // Private
|
|
||||||
|
|
||||||
var indexes = [
|
|
||||||
topDictIndex, stringsIndex,
|
|
||||||
globalSubrsIndex, charset,
|
|
||||||
charstringsIndex
|
|
||||||
];
|
|
||||||
|
|
||||||
for (var i = 0; i < indexes.length; i++) {
|
|
||||||
var index = indexes[i];
|
|
||||||
cff.set(index, currentOffset);
|
|
||||||
currentOffset += index.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Private Data
|
|
||||||
var defaultWidth = this.encodeNumber(0);
|
|
||||||
var privateData = [].concat(
|
|
||||||
defaultWidth, [20],
|
|
||||||
[139, 21], // nominalWidth
|
|
||||||
[
|
|
||||||
119, 159, 248, 97, 159, 247, 87, 159, 6,
|
|
||||||
30, 10, 3, 150, 37, 255, 12, 9,
|
|
||||||
139, 12,
|
|
||||||
10, 172, 10,
|
|
||||||
172, 150, 143, 146, 150, 146, 12, 12,
|
|
||||||
247, 32, 11,
|
|
||||||
247, 10, 161, 147, 154, 150, 143, 12, 13,
|
|
||||||
139, 12, 14,
|
|
||||||
28, 0, 55, 19 // Subrs offset
|
|
||||||
]);
|
|
||||||
cff.set(privateData, currentOffset);
|
|
||||||
currentOffset += privateData.length;
|
|
||||||
|
|
||||||
// Local Subrs
|
|
||||||
var flattenedSubrs = [];
|
|
||||||
|
|
||||||
var bias = 0;
|
|
||||||
var subrsCount = subrs.length;
|
|
||||||
if (subrsCount < 1240)
|
|
||||||
bias = 107;
|
|
||||||
else if (subrsCount < 33900)
|
|
||||||
bias = 1131;
|
|
||||||
else
|
|
||||||
bias = 32768;
|
|
||||||
|
|
||||||
// Add a bunch of empty subrs to deal with the Type2 bias
|
|
||||||
for (var i = 0; i < bias; i++)
|
|
||||||
flattenedSubrs.push([0x0B]);
|
|
||||||
|
|
||||||
for (var i = 0; i < subrsCount; i++) {
|
|
||||||
var subr = subrs[i];
|
|
||||||
flattenedSubrs.push(this.flattenCharstring(subr));
|
|
||||||
}
|
|
||||||
|
|
||||||
var subrsData = this.createCFFIndexHeader(flattenedSubrs, true);
|
|
||||||
cff.set(subrsData, currentOffset);
|
|
||||||
currentOffset += subrsData.length;
|
|
||||||
|
|
||||||
var fontData = [];
|
|
||||||
for (var i = 0; i < currentOffset; i++)
|
|
||||||
fontData.push(cff[i]);
|
|
||||||
|
|
||||||
return fontData;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
10
pdf.js
10
pdf.js
@ -3404,7 +3404,7 @@ var CanvasGraphics = (function() {
|
|||||||
BX: "beginCompat",
|
BX: "beginCompat",
|
||||||
EX: "endCompat",
|
EX: "endCompat",
|
||||||
},
|
},
|
||||||
|
|
||||||
translateFont: function(fontDict, xref, resources) {
|
translateFont: function(fontDict, xref, resources) {
|
||||||
var fd = fontDict.get("FontDescriptor");
|
var fd = fontDict.get("FontDescriptor");
|
||||||
if (!fd)
|
if (!fd)
|
||||||
@ -3544,7 +3544,8 @@ var CanvasGraphics = (function() {
|
|||||||
capHeight: descriptor.get("CapHeight"),
|
capHeight: descriptor.get("CapHeight"),
|
||||||
flags: descriptor.get("Flags"),
|
flags: descriptor.get("Flags"),
|
||||||
italicAngle: descriptor.get("ItalicAngle"),
|
italicAngle: descriptor.get("ItalicAngle"),
|
||||||
fixedPitch: false
|
fixedPitch: false,
|
||||||
|
textMatrix: IDENTITY_MATRIX
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -3869,6 +3870,11 @@ var CanvasGraphics = (function() {
|
|||||||
} else {
|
} else {
|
||||||
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);
|
||||||
|
|
||||||
|
var font = Fonts.lookup(this.current.fontName);
|
||||||
|
if (font)
|
||||||
|
this.ctx.transform.apply(this.ctx, font.properties.textMatrix);
|
||||||
|
|
||||||
this.ctx.fillText(text, 0, 0);
|
this.ctx.fillText(text, 0, 0);
|
||||||
this.current.x += Fonts.measureText(text);
|
this.current.x += Fonts.measureText(text);
|
||||||
}
|
}
|
||||||
|
@ -12,8 +12,6 @@
|
|||||||
* CharString or to understand the structure of the CFF format.
|
* CharString or to understand the structure of the CFF format.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Build a charset by assigning the glyph name and the human readable form
|
* Build a charset by assigning the glyph name and the human readable form
|
||||||
* of the glyph data.
|
* of the glyph data.
|
||||||
@ -380,6 +378,9 @@ var Type2Parser = function(aFilePath) {
|
|||||||
* writeToFile(fontData, "/tmp/pdf.js." + fontCount + ".cff");
|
* writeToFile(fontData, "/tmp/pdf.js." + fontCount + ".cff");
|
||||||
*/
|
*/
|
||||||
function writeToFile(aBytes, aFilePath) {
|
function writeToFile(aBytes, aFilePath) {
|
||||||
|
if (!("netscape" in window))
|
||||||
|
return;
|
||||||
|
|
||||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||||
var Cc = Components.classes,
|
var Cc = Components.classes,
|
||||||
Ci = Components.interfaces;
|
Ci = Components.interfaces;
|
||||||
@ -388,7 +389,7 @@ function writeToFile(aBytes, aFilePath) {
|
|||||||
|
|
||||||
var stream = Cc["@mozilla.org/network/file-output-stream;1"]
|
var stream = Cc["@mozilla.org/network/file-output-stream;1"]
|
||||||
.createInstance(Ci.nsIFileOutputStream);
|
.createInstance(Ci.nsIFileOutputStream);
|
||||||
stream.init(file, 0x04 | 0x08 | 0x20, 600, 0);
|
stream.init(file, 0x04 | 0x08 | 0x20, 0x180, 0);
|
||||||
|
|
||||||
var bos = Cc["@mozilla.org/binaryoutputstream;1"]
|
var bos = Cc["@mozilla.org/binaryoutputstream;1"]
|
||||||
.createInstance(Ci.nsIBinaryOutputStream);
|
.createInstance(Ci.nsIBinaryOutputStream);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user