From fc35a70acbd18b63f8340277249f5ab4b6d27c11 Mon Sep 17 00:00:00 2001 From: Vivien Nicolas <21@vingtetun.org> Date: Thu, 23 Jun 2011 22:11:16 +0200 Subject: [PATCH 01/12] Add a createPostTable function and remove the useless join('') calls --- fonts.js | 142 +++++++++++++++++++++++-------------------------------- 1 file changed, 60 insertions(+), 82 deletions(-) diff --git a/fonts.js b/fonts.js index d5943b7a3..f783c459c 100644 --- a/fonts.js +++ b/fonts.js @@ -298,56 +298,59 @@ var Font = (function () { }; function createOS2Table() { - var OS2 = stringToArray( - "\x00\x03" + // version - "\x02\x24" + // xAvgCharWidth - "\x01\xF4" + // usWeightClass - "\x00\x05" + // usWidthClass - "\x00\x00" + // fstype - "\x02\x8A" + // ySubscriptXSize - "\x02\xBB" + // ySubscriptYSize - "\x00\x00" + // ySubscriptXOffset - "\x00\x8C" + // ySubscriptYOffset - "\x02\x8A" + // ySuperScriptXSize - "\x02\xBB" + // ySuperScriptYSize - "\x00\x00" + // ySuperScriptXOffset - "\x01\xDF" + // ySuperScriptYOffset - "\x00\x31" + // yStrikeOutSize - "\x01\x02" + // yStrikeOutPosition - "\x00\x00" + // sFamilyClass - "\x02\x00\x06\x03\x00\x00\x00\x00\x00\x00" + // Panose - "\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 0-31) - "\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 32-63) - "\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 64-95) - "\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 96-127) - "\x2A\x32\x31\x2A" + // achVendID - "\x00\x20" + // fsSelection - "\x00\x2D" + // usFirstCharIndex - "\x00\x7A" + // usLastCharIndex - "\x00\x03" + // sTypoAscender - "\x00\x20" + // sTypeDescender - "\x00\x38" + // sTypoLineGap - "\x00\x5A" + // usWinAscent - "\x02\xB4" + // usWinDescent - "\x00\xCE\x00\x00" + // ulCodePageRange1 (Bits 0-31) - "\x00\x01\x00\x00" + // ulCodePageRange2 (Bits 32-63) - "\x00\x00" + // sxHeight - "\x00\x00" + // sCapHeight - "\x00\x01" + // usDefaultChar - "\x00\xCD" + // usBreakChar - "\x00\x02" // usMaxContext - ); - return OS2; + return "\x00\x03" + // version + "\x02\x24" + // xAvgCharWidth + "\x01\xF4" + // usWeightClass + "\x00\x05" + // usWidthClass + "\x00\x00" + // fstype + "\x02\x8A" + // ySubscriptXSize + "\x02\xBB" + // ySubscriptYSize + "\x00\x00" + // ySubscriptXOffset + "\x00\x8C" + // ySubscriptYOffset + "\x02\x8A" + // ySuperScriptXSize + "\x02\xBB" + // ySuperScriptYSize + "\x00\x00" + // ySuperScriptXOffset + "\x01\xDF" + // ySuperScriptYOffset + "\x00\x31" + // yStrikeOutSize + "\x01\x02" + // yStrikeOutPosition + "\x00\x00" + // sFamilyClass + "\x02\x00\x06\x03\x00\x00\x00\x00\x00\x00" + // Panose + "\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 0-31) + "\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 32-63) + "\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 64-95) + "\xFF\xFF\xFF\xFF" + // ulUnicodeRange1 (Bits 96-127) + "\x2A\x32\x31\x2A" + // achVendID + "\x00\x20" + // fsSelection + "\x00\x2D" + // usFirstCharIndex + "\x00\x7A" + // usLastCharIndex + "\x00\x03" + // sTypoAscender + "\x00\x20" + // sTypeDescender + "\x00\x38" + // sTypoLineGap + "\x00\x5A" + // usWinAscent + "\x02\xB4" + // usWinDescent + "\x00\xCE\x00\x00" + // ulCodePageRange1 (Bits 0-31) + "\x00\x01\x00\x00" + // ulCodePageRange2 (Bits 32-63) + "\x00\x00" + // sxHeight + "\x00\x00" + // sCapHeight + "\x00\x01" + // usDefaultChar + "\x00\xCD" + // usBreakChar + "\x00\x02"; // usMaxContext + }; + + function createPostTable() { + TODO("Fill with real values from the font dict"); + + return "\x00\x03\x00\x00" + // Version number + "\x00\x00\x01\x00" + // italicAngle + "\x00\x00" + // underlinePosition + "\x00\x00" + // underlineThickness + "\x00\x00\x00\x00" + // isFixedPitch + "\x00\x00\x00\x00" + // minMemType42 + "\x00\x00\x00\x00" + // maxMemType42 + "\x00\x00\x00\x00" + // minMemType1 + "\x00\x00\x00\x00"; // maxMemType1 }; - /** - * A bunch of the OpenType code is duplicate between this class and the - * TrueType code, this is intentional and will merge in a future version - * where all the code relative to OpenType will probably have its own - * class and will take decision without the Fonts consent. - * But at the moment it allows to develop around the TrueType rewriting - * on the fly without messing up with the 'regular' Type1 to OTF conversion. - */ constructor.prototype = { name: null, font: null, @@ -405,7 +408,7 @@ var Font = (function () { var length = FontsUtils.bytesToInteger(font.getBytes(2)); var language = FontsUtils.bytesToInteger(font.getBytes(2)); - if ((format == 0 && numTables == 1) || + if ((format == 0 && numTables == 1) || (format == 6 && numTables == 1 && !properties.encoding.empty)) { // Format 0 alone is not allowed by the sanitizer so let's rewrite // that to a 3-1-4 Unicode BMP table @@ -512,10 +515,9 @@ var Font = (function () { createOpenTypeHeader("\x00\x01\x00\x00", ttf, offsets, numTables); // Insert the missing table - var OS2 = createOS2Table(); tables.push({ tag: "OS/2", - data: OS2 + data: stringToArray(createOS2Table) }); // Replace the old CMAP table with a shiny new one @@ -523,20 +525,9 @@ var Font = (function () { // Rewrite the 'post' table if needed if (!post) { - post = - "\x00\x03\x00\x00" + // Version number - "\x00\x00\x01\x00" + // italicAngle - "\x00\x00" + // underlinePosition - "\x00\x00" + // underlineThickness - "\x00\x00\x00\x00" + // isFixedPitch - "\x00\x00\x00\x00" + // minMemType42 - "\x00\x00\x00\x00" + // maxMemType42 - "\x00\x00\x00\x00" + // minMemType1 - "\x00\x00\x00\x00"; // maxMemType1 - - tables.unshift({ + tables.push({ tag: "post", - data: stringToArray(post) + data: stringToArray(createPostTable()) }); } @@ -586,10 +577,10 @@ var Font = (function () { function createNameTable(name) { var names = [ "See original licence", // Copyright - name, // Font family + name, // Font family "undefined", // Font subfamily (font weight) "uniqueID", // Unique ID - name, // Full font name + name, // Full font name "0.1", // Version "undefined", // Postscript name "undefined", // Trademark @@ -625,10 +616,10 @@ var Font = (function () { // Required Tables var CFF = - font.data, // PostScript Font Program + font.data, // PostScript Font Program OS2, // OS/2 and Windows Specific metrics cmap, // Character to glyphs mapping - head, // Font eader + head, // Font header hhea, // Horizontal header hmtx, // Horizontal metrics maxp, // Maximum profile @@ -728,17 +719,7 @@ var Font = (function () { createTableEntry(otf, offsets, "name", name); /** POST */ - // TODO: get those informations from the FontInfo structure - post = "\x00\x03\x00\x00" + // Version number - "\x00\x00\x01\x00" + // italicAngle - "\x00\x00" + // underlinePosition - "\x00\x00" + // underlineThickness - "\x00\x00\x00\x00" + // isFixedPitch - "\x00\x00\x00\x00" + // minMemType42 - "\x00\x00\x00\x00" + // maxMemType42 - "\x00\x00\x00\x00" + // minMemType1 - "\x00\x00\x00\x00"; // maxMemType1 - post = stringToArray(post); + post = stringToArray(createPostTable()); createTableEntry(otf, offsets, "post", post); // Once all the table entries header are written, dump the data! @@ -1480,7 +1461,6 @@ CFF.prototype = { } var charstringsIndex = this.createCFFIndexHeader([[0x40, 0x0E]].concat(glyphs), true); - charstringsIndex = charstringsIndex.join(" ").split(" "); // XXX why? //Top Dict Index var topDictIndex = [ @@ -1514,7 +1494,6 @@ CFF.prototype = { var privateOffset = charstringsOffset + charstringsIndex.length; topDictIndex = topDictIndex.concat(this.encodeNumber(privateOffset)); topDictIndex.push(18); // Private - topDictIndex = topDictIndex.join(" ").split(" "); var indexes = [ topDictIndex, stringsIndex, @@ -1544,7 +1523,6 @@ CFF.prototype = { 139, 12, 14, 28, 0, 55, 19 ]); - privateData = privateData.join(" ").split(" "); cff.set(privateData, currentOffset); currentOffset += privateData.length; From ef418de40bf4688698463871a0f1f1f442460173 Mon Sep 17 00:00:00 2001 From: Vivien Nicolas <21@vingtetun.org> Date: Thu, 23 Jun 2011 22:20:55 +0200 Subject: [PATCH 02/12] Fix bustage of the previous commit --- fonts.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fonts.js b/fonts.js index f783c459c..2c63adfd4 100644 --- a/fonts.js +++ b/fonts.js @@ -517,7 +517,7 @@ var Font = (function () { // Insert the missing table tables.push({ tag: "OS/2", - data: stringToArray(createOS2Table) + data: stringToArray(createOS2Table()) }); // Replace the old CMAP table with a shiny new one @@ -643,7 +643,7 @@ var Font = (function () { createTableEntry(otf, offsets, "CFF ", CFF); /** OS/2 */ - OS2 = createOS2Table(); + OS2 = stringToArray(createOS2Table()); createTableEntry(otf, offsets, "OS/2", OS2); //XXX Getting charstrings here seems wrong since this is another CFF glue From 1de54365611f0132f9ac1a5f10ea99f03ef91f94 Mon Sep 17 00:00:00 2001 From: Vivien Nicolas <21@vingtetun.org> Date: Thu, 23 Jun 2011 22:22:46 +0200 Subject: [PATCH 03/12] Remove the ignoreFont variable --- pdf.js | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/pdf.js b/pdf.js index addcea6de..f0d3bae65 100644 --- a/pdf.js +++ b/pdf.js @@ -2359,16 +2359,9 @@ var CanvasGraphics = (function() { error("FontFile not found for font: " + fontName); fontFile = xref.fetchIfRef(fontFile); - // Fonts with an embedded cmap but without any assignment in - // it are not yet supported, so ask the fonts loader to ignore - // them to not pay a stupid one sec latence. - var ignoreFont = false; - var encodingMap = {}; var charset = []; if (fontDict.has("Encoding")) { - ignoreFont = false; - var encoding = xref.fetchIfRef(fontDict.get("Encoding")); if (IsDict(encoding)) { // Build a map between codes and glyphs @@ -2433,7 +2426,6 @@ var CanvasGraphics = (function() { break; case "beginbfrange": - ignoreFont = false; case "begincodespacerange": token = ""; tokens = []; @@ -2488,8 +2480,7 @@ var CanvasGraphics = (function() { type: subType.name, encoding: encodingMap, charset: charset, - bbox: bbox, - ignore: ignoreFont + bbox: bbox }; return { From 4d155983c51127001df51c3e3598b642b6a6a7e8 Mon Sep 17 00:00:00 2001 From: Vivien Nicolas <21@vingtetun.org> Date: Thu, 23 Jun 2011 23:15:22 +0200 Subject: [PATCH 04/12] Remove some hardcoded glue --- fonts.js | 79 +++++++++++++++++++++++++++----------------------------- pdf.js | 15 ++++++----- 2 files changed, 47 insertions(+), 47 deletions(-) diff --git a/fonts.js b/fonts.js index 2c63adfd4..2c113d652 100644 --- a/fonts.js +++ b/fonts.js @@ -297,7 +297,7 @@ var Font = (function () { idDeltas + idRangeOffsets + glyphsIds); }; - function createOS2Table() { + function createOS2Table(properties) { return "\x00\x03" + // version "\x02\x24" + // xAvgCharWidth "\x01\xF4" + // usWeightClass @@ -326,29 +326,29 @@ var Font = (function () { "\x00\x03" + // sTypoAscender "\x00\x20" + // sTypeDescender "\x00\x38" + // sTypoLineGap - "\x00\x5A" + // usWinAscent - "\x02\xB4" + // usWinDescent - "\x00\xCE\x00\x00" + // ulCodePageRange1 (Bits 0-31) - "\x00\x01\x00\x00" + // ulCodePageRange2 (Bits 32-63) - "\x00\x00" + // sxHeight - "\x00\x00" + // sCapHeight - "\x00\x01" + // usDefaultChar - "\x00\xCD" + // usBreakChar - "\x00\x02"; // usMaxContext + string16(properties.ascent) + // usWinAscent + string16(properties.descent) + // usWinDescent + "\x00\xCE\x00\x00" + // ulCodePageRange1 (Bits 0-31) + "\x00\x01\x00\x00" + // ulCodePageRange2 (Bits 32-63) + string16(properties.xHeight) + // sxHeight + string16(properties.capHeight) + // sCapHeight + "\x00\x01" + // usDefaultChar + "\x00\xCD" + // usBreakChar + "\x00\x02"; // usMaxContext }; - function createPostTable() { + function createPostTable(properties) { TODO("Fill with real values from the font dict"); - return "\x00\x03\x00\x00" + // Version number - "\x00\x00\x01\x00" + // italicAngle - "\x00\x00" + // underlinePosition - "\x00\x00" + // underlineThickness - "\x00\x00\x00\x00" + // isFixedPitch - "\x00\x00\x00\x00" + // minMemType42 - "\x00\x00\x00\x00" + // maxMemType42 - "\x00\x00\x00\x00" + // minMemType1 - "\x00\x00\x00\x00"; // maxMemType1 + return "\x00\x03\x00\x00" + // Version number + string32(properties.italicAngle) + // italicAngle + "\x00\x00" + // underlinePosition + "\x00\x00" + // underlineThickness + "\x00\x00\x00\x00" + // isFixedPitch + "\x00\x00\x00\x00" + // minMemType42 + "\x00\x00\x00\x00" + // maxMemType42 + "\x00\x00\x00\x00" + // minMemType1 + "\x00\x00\x00\x00"; // maxMemType1 }; constructor.prototype = { @@ -412,6 +412,7 @@ var Font = (function () { (format == 6 && numTables == 1 && !properties.encoding.empty)) { // Format 0 alone is not allowed by the sanitizer so let's rewrite // that to a 3-1-4 Unicode BMP table + TODO("Use an other source of informations than charset here, it is not reliable"); var charset = properties.charset; var glyphs = []; for (var j = 0; j < charset.length; j++) { @@ -517,7 +518,7 @@ var Font = (function () { // Insert the missing table tables.push({ tag: "OS/2", - data: stringToArray(createOS2Table()) + data: stringToArray(createOS2Table(properties)) }); // Replace the old CMAP table with a shiny new one @@ -527,7 +528,7 @@ var Font = (function () { if (!post) { tables.push({ tag: "post", - data: stringToArray(createPostTable()) + data: stringToArray(createPostTable(properties)) }); } @@ -643,14 +644,12 @@ var Font = (function () { createTableEntry(otf, offsets, "CFF ", CFF); /** OS/2 */ - OS2 = stringToArray(createOS2Table()); + OS2 = stringToArray(createOS2Table(properties)); createTableEntry(otf, offsets, "OS/2", OS2); - //XXX Getting charstrings here seems wrong since this is another CFF glue - var charstrings = font.getOrderedCharStrings(properties.glyphs); - /** CMAP */ - cmap = createCMapTable(charstrings); + var charstrings = font.charstrings; + cmap = createCMapTable(font.charstrings); createTableEntry(otf, offsets, "cmap", cmap); /** HEAD */ @@ -719,7 +718,7 @@ var Font = (function () { createTableEntry(otf, offsets, "name", name); /** POST */ - post = stringToArray(createPostTable()); + post = stringToArray(createPostTable(properties)); createTableEntry(otf, offsets, "post", post); // Once all the table entries header are written, dump the data! @@ -1187,6 +1186,8 @@ var CFFStrings = [ "001.003","Black","Bold","Book","Light","Medium","Regular","Roman","Semibold" ]; +var type1Parser = new Type1Parser(); + var CFF = function(name, file, properties) { // Get the data block containing glyphs and subrs informations var length1 = file.dict.get("Length1"); @@ -1194,13 +1195,11 @@ var CFF = function(name, file, properties) { file.skip(length1); var eexecBlock = file.getBytes(length2); - // Decrypt the data blocks and retrieve the informations from it - var parser = new Type1Parser(); - var fontInfo = parser.extractFontProgram(eexecBlock); + // Decrypt the data blocks and retrieve it's content + var data = type1Parser.extractFontProgram(eexecBlock); - properties.subrs = fontInfo.subrs; - properties.glyphs = fontInfo.charstrings; - this.data = this.wrap(name, properties); + this.charstrings = this.getOrderedCharStrings(data.charstrings); + this.data = this.wrap(name, this.charstrings, data.subrs, properties); }; CFF.prototype = { @@ -1265,7 +1264,7 @@ CFF.prototype = { charstrings.push({ glyph: glyph, unicode: unicode, - charstring: glyphs[i].data.slice() + charstring: glyphs[i].data }); } }; @@ -1308,7 +1307,7 @@ CFF.prototype = { if (obj.charAt) { switch (obj) { case "callsubr": - var subr = subrs[charstring[i - 1]].slice(); + var subr = subrs[charstring[i - 1]]; if (subr.length > 1) { subr = this.flattenCharstring(glyph, subr, subrs); subr.pop(); @@ -1402,18 +1401,16 @@ CFF.prototype = { error("failing with i = " + i + " in charstring:" + charstring + "(" + charstring.length + ")"); }, - wrap: function wrap(name, properties) { - var charstrings = this.getOrderedCharStrings(properties.glyphs); - + wrap: function wrap(name, charstrings, subrs, properties) { // Starts the conversion of the Type1 charstrings to Type2 var charstringsCount = 0; var charstringsDataLength = 0; var glyphs = []; for (var i = 0; i < charstrings.length; i++) { - var charstring = charstrings[i].charstring.slice(); + var charstring = charstrings[i].charstring; var glyph = charstrings[i].glyph; - var flattened = this.flattenCharstring(glyph, charstring, properties.subrs); + var flattened = this.flattenCharstring(glyph, charstring, subrs); glyphs.push(flattened); charstringsCount++; charstringsDataLength += flattened.length; diff --git a/pdf.js b/pdf.js index f0d3bae65..c314b6e55 100644 --- a/pdf.js +++ b/pdf.js @@ -2384,9 +2384,8 @@ var CanvasGraphics = (function() { error("Unknown font encoding"); var index = 0; - for (var j = 0; j < encoding.length; j++) { + for (var j = 0; j < encoding.length; j++) encodingMap[index++] = GlyphsUnicode[encoding[j]]; - } var firstChar = xref.fetchIfRef(fontDict.get("FirstChar")); var widths = xref.fetchIfRef(fontDict.get("Widths")); @@ -2472,15 +2471,19 @@ var CanvasGraphics = (function() { } var subType = fontDict.get("Subtype"); - var bbox = descriptor.get("FontBBox"); - assertWellFormed(IsName(subType) && IsArray(bbox), - "invalid font Subtype or FontBBox"); + assertWellFormed(IsName(subType), "invalid font Subtype"); var properties = { type: subType.name, encoding: encodingMap, charset: charset, - bbox: bbox + bbox: descriptor.get("FontBBox"), + ascent: descriptor.get("Ascent"), + descent: descriptor.get("Descent"), + xHeight: descriptor.get("XHeight"), + capHeight: descriptor.get("CapHeight"), + flags: descriptor.get("Flags"), + italicAngle: descriptor.get("ItalicAngle") }; return { From df406d43a170de0da12b89637c3f541182d1dd46 Mon Sep 17 00:00:00 2001 From: Vivien Nicolas <21@vingtetun.org> Date: Fri, 24 Jun 2011 01:37:54 +0200 Subject: [PATCH 05/12] Remove some useless var --- fonts.js | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/fonts.js b/fonts.js index 2c113d652..971d8d257 100644 --- a/fonts.js +++ b/fonts.js @@ -649,7 +649,7 @@ var Font = (function () { /** CMAP */ var charstrings = font.charstrings; - cmap = createCMapTable(font.charstrings); + cmap = createCMapTable(charstrings); createTableEntry(otf, offsets, "cmap", cmap); /** HEAD */ @@ -1403,17 +1403,11 @@ CFF.prototype = { wrap: function wrap(name, charstrings, subrs, properties) { // Starts the conversion of the Type1 charstrings to Type2 - var charstringsCount = 0; - var charstringsDataLength = 0; - var glyphs = []; - for (var i = 0; i < charstrings.length; i++) { - var charstring = charstrings[i].charstring; - var glyph = charstrings[i].glyph; - - var flattened = this.flattenCharstring(glyph, charstring, subrs); - glyphs.push(flattened); - charstringsCount++; - charstringsDataLength += flattened.length; + var glyphs = charstrings.slice(); + var glyphsCount = glyphs.length; + for (var i = 0; i < glyphs.length; i++) { + var charstring = glyphs[i]; + glyphs[i] = this.flattenCharstring(charstring.glyph, charstring.charstring, subrs); } // Create a CFF font data @@ -1448,10 +1442,10 @@ CFF.prototype = { // Fill the charset header (first byte is the encoding) var charset = [0x00]; - for (var i = 0; i < glyphs.length; i++) { + for (var i = 0; i < glyphsCount; i++) { var index = CFFStrings.indexOf(charstrings[i].glyph); if (index == -1) - index = CFFStrings.length + strings.indexOf(glyph); + index = CFFStrings.length + strings.indexOf(charstrings[i].glyph); var bytes = FontsUtils.integerToBytes(index, 2); charset.push(bytes[0]); charset.push(bytes[1]); @@ -1483,7 +1477,7 @@ CFF.prototype = { topDictIndex = topDictIndex.concat([28, 0, 0, 16]) // Encoding - var charstringsOffset = charsetOffset + (charstringsCount * 2) + 1; + var charstringsOffset = charsetOffset + (glyphsCount * 2) + 1; topDictIndex = topDictIndex.concat(this.encodeNumber(charstringsOffset)); topDictIndex.push(17); // charstrings From 014fc4c0feaef1a20c0c956038efe5bae9d195df Mon Sep 17 00:00:00 2001 From: Vivien Nicolas <21@vingtetun.org> Date: Fri, 24 Jun 2011 02:45:58 +0200 Subject: [PATCH 06/12] Remove code to handle FlateStream differently now that we have a sane interface --- pdf.js | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/pdf.js b/pdf.js index a7de0bb35..f61250a0c 100644 --- a/pdf.js +++ b/pdf.js @@ -2458,13 +2458,7 @@ var CanvasGraphics = (function() { var tokens = []; var token = ""; - var length = cmapObj.length; - if (cmapObj instanceof FlateStream) { - cmapObj.readBlock(); - length = cmapObj.bufferLength; - } - - var cmap = cmapObj.getBytes(length); + var cmap = cmapObj.getBytes(cmapObj.length); for (var i =0; i < cmap.length; i++) { var byte = cmap[i]; if (byte == 0x20 || byte == 0x0A || byte == 0x3C || byte == 0x3E) { From 5443714d341377564d7f33df17eeb923ab1a45e5 Mon Sep 17 00:00:00 2001 From: Vivien Nicolas <21@vingtetun.org> Date: Fri, 24 Jun 2011 02:58:17 +0200 Subject: [PATCH 07/12] Fix Windows 'hmtx' bust table --- fonts.js | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/fonts.js b/fonts.js index 1169e21c6..6f5adbeef 100644 --- a/fonts.js +++ b/fonts.js @@ -710,11 +710,15 @@ var Font = (function () { createTableEntry(otf, offsets, "hhea", hhea); /** HMTX */ - hmtx = "\x01\xF4\x00\x00"; + /* For some reasons, probably related to how the backend handle fonts, + * Linux seems to ignore this file and prefer the data from the CFF itself + * while Windows use this data. So be careful if you hack on Linux and + * have to touch the 'hmtx' table + */ + hmtx = "\x01\xF4\x00\x00"; // Fake .notdef + var width = 0, lsb = 0; for (var i = 0; i < charstrings.length; i++) { - var charstring = charstrings[i].charstring; - var width = charstring[1]; - var lsb = charstring[0]; + width = charstrings[i].charstring[0]; hmtx += string16(width) + string16(lsb); } hmtx = stringToArray(hmtx); @@ -1314,7 +1318,7 @@ CFF.prototype = { "hvcurveto": 31, }, - flattenCharstring: function flattenCharstring(glyph, charstring, subrs) { + flattenCharstring: function flattenCharstring(charstring, subrs) { var i = 0; while (true) { var obj = charstring[i]; @@ -1326,7 +1330,7 @@ CFF.prototype = { case "callsubr": var subr = subrs[charstring[i - 1]]; if (subr.length > 1) { - subr = this.flattenCharstring(glyph, subr, subrs); + subr = this.flattenCharstring(subr, subrs); subr.pop(); charstring.splice(i - 1, 2, subr); } else { @@ -1420,11 +1424,11 @@ CFF.prototype = { wrap: function wrap(name, charstrings, subrs, properties) { // Starts the conversion of the Type1 charstrings to Type2 - var glyphs = charstrings.slice(); - var glyphsCount = glyphs.length; - for (var i = 0; i < glyphs.length; i++) { - var charstring = glyphs[i]; - glyphs[i] = this.flattenCharstring(charstring.glyph, charstring.charstring, subrs); + var glyphs = []; + var glyphsCount = charstrings.length; + for (var i = 0; i < glyphsCount; i++) { + var charstring = charstrings[i].charstring; + glyphs.push(this.flattenCharstring(charstring.slice(), subrs)); } // Create a CFF font data From bc7e2b0110273454abae614b335c9ac94e73739f Mon Sep 17 00:00:00 2001 From: Vivien Nicolas <21@vingtetun.org> Date: Fri, 24 Jun 2011 03:01:41 +0200 Subject: [PATCH 08/12] Don't read the lsb instead of the width --- fonts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fonts.js b/fonts.js index 6f5adbeef..a995c55eb 100644 --- a/fonts.js +++ b/fonts.js @@ -718,7 +718,7 @@ var Font = (function () { hmtx = "\x01\xF4\x00\x00"; // Fake .notdef var width = 0, lsb = 0; for (var i = 0; i < charstrings.length; i++) { - width = charstrings[i].charstring[0]; + width = charstrings[i].charstring[1]; hmtx += string16(width) + string16(lsb); } hmtx = stringToArray(hmtx); From 07ca9bfec54728bb9e368b60877695e6382c1555 Mon Sep 17 00:00:00 2001 From: Vivien Nicolas <21@vingtetun.org> Date: Fri, 24 Jun 2011 11:47:22 +0200 Subject: [PATCH 09/12] Start adding a FontLoader class to isolate the font-loaded hack --- fonts.js | 53 ++++++++++++++++++++++++++++++++++------------------- viewer.js | 35 +++++++---------------------------- 2 files changed, 41 insertions(+), 47 deletions(-) diff --git a/fonts.js b/fonts.js index a995c55eb..0a1974571 100644 --- a/fonts.js +++ b/fonts.js @@ -80,6 +80,35 @@ var Fonts = { } }; +var FontsLoader = { + bind: function(fonts) { + var worker = (typeof window == "undefined"); + var ready = true; + + for (var i = 0; i < fonts.length; i++) { + var font = fonts[i]; + if (Fonts[font.name]) { + ready = ready && !Fonts[font.name].loading; + continue; + } else { + ready = false; + } + + var obj = new Font(font.name, font.file, font.properties); + + var str = ""; + var data = Fonts[font.name].data; + var length = data.length; + for (var j = 0; j < length; j++) + str += String.fromCharCode(data[j]); + + worker ? obj.bindWorker(str) : obj.bindDOM(str); + } + return ready; + } +}; + + /** * 'Font' is the class the outside world should use, it encapsulate all the font * decoding logics whatever type it is (assuming the font type is supported). @@ -113,13 +142,14 @@ var Font = (function () { return; } + var data; switch (properties.type) { case "Type1": var cff = new CFF(name, file, properties); this.mimetype = "font/opentype"; // Wrap the CFF data inside an OTF font file - this.font = this.convert(name, cff, properties); + data = this.convert(name, cff, properties); break; case "TrueType": @@ -127,7 +157,7 @@ var Font = (function () { // Repair the TrueType file if it is can be damaged in the point of // view of the sanitizer - this.font = this.checkAndRepair(name, file, properties); + data = this.checkAndRepair(name, file, properties); break; default: @@ -135,28 +165,12 @@ var Font = (function () { break; } - var data = this.font; Fonts[name] = { data: data, properties: properties, loading: true, cache: Object.create(null) - } - - // Convert data to a string. - var dataStr = ""; - var length = data.length; - for (var i = 0; i < length; ++i) - dataStr += String.fromCharCode(data[i]); - - // Attach the font to the document. If this script is runnig in a worker, - // call `bindWorker`, which sends stuff over to the main thread. - if (typeof window != "undefined") { - this.bindDOM(dataStr); - } else { - this.bindWorker(dataStr); - } - + }; }; function stringToArray(str) { @@ -1420,6 +1434,7 @@ CFF.prototype = { i++; } error("failing with i = " + i + " in charstring:" + charstring + "(" + charstring.length + ")"); + return []; }, wrap: function wrap(name, charstrings, subrs, properties) { diff --git a/viewer.js b/viewer.js index 41aaf354c..2bcff50a6 100644 --- a/viewer.js +++ b/viewer.js @@ -3,7 +3,7 @@ "use strict"; -var pdfDocument, canvas, pageDisplay, pageNum, numPages, pageInterval; +var pdfDocument, canvas, pageDisplay, pageNum, numPages, pageTimeout; function load(userInput) { canvas = document.getElementById("canvas"); canvas.mozOpaque = true; @@ -52,7 +52,7 @@ function gotoPage(num) { } function displayPage(num) { - window.clearInterval(pageInterval); + window.clearTimeout(pageTimeout); document.getElementById("pageNumber").value = num; @@ -75,28 +75,12 @@ function displayPage(num) { page.compile(gfx, fonts); var t2 = Date.now(); - var fontsReady = true; - - // Inspect fonts and translate the missing one - var count = fonts.length; - for (var i = 0; i < count; i++) { - var font = fonts[i]; - if (Fonts[font.name]) { - fontsReady = fontsReady && !Fonts[font.name].loading; - continue; + function loadFont() { + if (!FontsLoader.bind(fonts)) { + pageTimeout = window.setTimeout(loadFont, 10); + return; } - new Font(font.name, font.file, font.properties); - fontsReady = false; - } - - function delayLoadFont() { - for (var i = 0; i < count; i++) { - if (Fonts[font.name].loading) - return; - } - window.clearInterval(pageInterval); - var t3 = Date.now(); page.display(gfx); @@ -106,12 +90,7 @@ function displayPage(num) { var infoDisplay = document.getElementById("info"); infoDisplay.innerHTML = "Time to load/compile/fonts/render: "+ (t1 - t0) + "/" + (t2 - t1) + "/" + (t3 - t2) + "/" + (t4 - t3) + " ms"; }; - - if (fontsReady) { - delayLoadFont(); - } else { - pageInterval = setInterval(delayLoadFont, 10); - } + loadFont(); } function nextPage() { From 26389c083ab26499ac3f9836ff4dcee82ebaaf7d Mon Sep 17 00:00:00 2001 From: Vivien Nicolas <21@vingtetun.org> Date: Fri, 24 Jun 2011 11:58:05 +0200 Subject: [PATCH 10/12] Fix a bunch of warnings from Firebug strict mode --- fonts.js | 27 +++++++++++++++------------ pdf.js | 56 ++++++++++++++++++++++++++++++-------------------------- 2 files changed, 45 insertions(+), 38 deletions(-) diff --git a/fonts.js b/fonts.js index 0a1974571..7e8aecd6d 100644 --- a/fonts.js +++ b/fonts.js @@ -599,16 +599,16 @@ var Font = (function () { return font.getBytes(); }, - convert: function font_convert(name, font, properties) { + convert: function font_convert(fontName, font, properties) { var otf = new Uint8Array(kMaxFontFileSize); function createNameTable(name) { var names = [ "See original licence", // Copyright - name, // Font family + fontName, // Font family "undefined", // Font subfamily (font weight) "uniqueID", // Unique ID - name, // Full font name + fontName, // Full font name "0.1", // Version "undefined", // Postscript name "undefined", // Trademark @@ -616,7 +616,7 @@ var Font = (function () { "undefined" // Designer ]; - var name = + var nameTable = "\x00\x00" + // format "\x00\x0A" + // Number of names Record "\x00\x7E"; // Storage @@ -633,13 +633,13 @@ var Font = (function () { "\x00\x00" + // name ID string16(str.length) + string16(strOffset); - name += nameRecord; + nameTable += nameRecord; strOffset += str.length; } - name += names.join(""); - return name; + nameTable += names.join(""); + return nameTable; } // Required Tables @@ -885,6 +885,9 @@ var FontsUtils = { bytes.set([value >> 24, value >> 16, value >> 8, value]); return [bytes[0], bytes[1], bytes[2], bytes[3]]; } + + error("This number of bytes " + bytesCount + " is not supported"); + return null; }, bytesToInteger: function fu_bytesToInteger(bytesArray) { @@ -1238,7 +1241,7 @@ var CFF = function(name, file, properties) { }; CFF.prototype = { - createCFFIndexHeader: function(objects, isByte) { + createCFFIndexHeader: function cff_createCFFIndexHeader(objects, isByte) { // First 2 bytes contains the number of objects contained into this index var count = objects.length; @@ -1275,18 +1278,18 @@ CFF.prototype = { return data; }, - encodeNumber: function(value) { + encodeNumber: function cff_encodeNumber(value) { var x = 0; if (value >= -32768 && value <= 32767) { return [ 28, value >> 8, value & 0xFF ]; } else if (value >= (-2147483647-1) && value <= 2147483647) { return [ 0xFF, value >> 24, Value >> 16, value >> 8, value & 0xFF ]; - } else { - error("Value: " + value + " is not allowed"); } + error("Value: " + value + " is not allowed"); + return null; }, - getOrderedCharStrings: function(glyphs) { + getOrderedCharStrings: function cff_getOrderedCharStrings(glyphs) { var charstrings = []; for (var i = 0; i < glyphs.length; i++) { diff --git a/pdf.js b/pdf.js index f61250a0c..c4ebb5f24 100644 --- a/pdf.js +++ b/pdf.js @@ -71,14 +71,14 @@ var Stream = (function() { get length() { return this.end - this.start; }, - getByte: function() { + getByte: function stream_getByte() { if (this.pos >= this.end) - return; + return null; return this.bytes[this.pos++]; }, // returns subarray of original buffer // should only be read - getBytes: function(length) { + getBytes: function stream_getBytes(length) { var bytes = this.bytes; var pos = this.pos; var strEnd = this.end; @@ -93,28 +93,28 @@ var Stream = (function() { this.pos = end; return bytes.subarray(pos, end); }, - lookChar: function() { + lookChar: function stream_lookChar() { if (this.pos >= this.end) - return; + return null; return String.fromCharCode(this.bytes[this.pos]); }, - getChar: function() { + getChar: function stream_getChar() { if (this.pos >= this.end) - return; + return null; return String.fromCharCode(this.bytes[this.pos++]); }, - skip: function(n) { + skip: function stream_skip(n) { if (!n) n = 1; this.pos += n; }, - reset: function() { + reset: function stream_reset() { this.pos = this.start; }, - moveStart: function() { + moveStart: function stream_moveStart() { this.start = this.pos; }, - makeSubStream: function(start, length, dict) { + makeSubStream: function stream_makeSubstream(start, length, dict) { return new Stream(this.bytes.buffer, start, length, dict); } }; @@ -146,7 +146,7 @@ var DecodeStream = (function() { } constructor.prototype = { - ensureBuffer: function(requested) { + ensureBuffer: function decodestream_ensureBuffer(requested) { var buffer = this.buffer; var current = buffer ? buffer.byteLength : 0; if (requested < current) @@ -159,16 +159,16 @@ var DecodeStream = (function() { buffer2[i] = buffer[i]; return this.buffer = buffer2; }, - getByte: function() { + getByte: function decodestream_getByte() { var pos = this.pos; while (this.bufferLength <= pos) { if (this.eof) - return; + return null; this.readBlock(); } return this.buffer[this.pos++]; }, - getBytes: function(length) { + getBytes: function decodestream_getBytes(length) { var pos = this.pos; if (length) { @@ -191,25 +191,25 @@ var DecodeStream = (function() { this.pos = end; return this.buffer.subarray(pos, end) }, - lookChar: function() { + lookChar: function decodestream_lookChar() { var pos = this.pos; while (this.bufferLength <= pos) { if (this.eof) - return; + return null; this.readBlock(); } return String.fromCharCode(this.buffer[this.pos]); }, - getChar: function() { + getChar: function decodestream_getChar() { var pos = this.pos; while (this.bufferLength <= pos) { if (this.eof) - return; + return null; this.readBlock(); } return String.fromCharCode(this.buffer[this.pos++]); }, - skip: function(n) { + skip: function decodestream_skip(n) { if (!n) n = 1; this.pos += n; @@ -635,6 +635,7 @@ var PredictorStream = (function() { var rowBytes = this.rowBytes = (columns * colors * bits + 7) >> 3; DecodeStream.call(this); + return this; } constructor.prototype = Object.create(DecodeStream.prototype); @@ -905,7 +906,9 @@ var Dict = (function() { constructor.prototype = { get: function(key) { - return this.map[key]; + if (key in this.map) + return this.map[key]; + return null; }, get2: function(key1, key2) { return this.get(key1) || this.get(key2); @@ -1590,7 +1593,7 @@ var XRef = (function() { } constructor.prototype = { - readXRefTable: function(parser) { + readXRefTable: function readXRefTable(parser) { var obj; while (true) { if (IsCmd(obj = parser.getObj(), "trailer")) @@ -1661,7 +1664,7 @@ var XRef = (function() { return dict; }, - readXRefStream: function(stream) { + readXRefStream: function readXRefStream(stream) { var streamParameters = stream.parameters; var length = streamParameters.get("Length"); var byteWidths = streamParameters.get("W"); @@ -1713,7 +1716,7 @@ var XRef = (function() { this.readXRef(prev); return streamParameters; }, - readXRef: function(startXRef) { + readXRef: function readXref(startXRef) { var stream = this.stream; stream.pos = startXRef; var parser = new Parser(new Lexer(stream), true); @@ -1731,6 +1734,7 @@ var XRef = (function() { return this.readXRefStream(obj); } error("Invalid XRef"); + return null; }, getEntry: function(i) { var e = this.entries[i]; @@ -2396,7 +2400,7 @@ var CanvasGraphics = (function() { if (!fd) // XXX deprecated "special treatment" for standard // fonts? What do we need to do here? - return; + return null; var descriptor = xref.fetch(fd); var fontName = descriptor.get("FontName"); @@ -3037,7 +3041,7 @@ var CanvasGraphics = (function() { this.restore(); TODO("Inverse pattern is painted"); - var pattern = this.ctx.createPattern(tmpCanvas, "repeat"); + pattern = this.ctx.createPattern(tmpCanvas, "repeat"); this.ctx.fillStyle = pattern; }, setStrokeGray: function(gray) { From c1c8ca4ef3a010725bc694041912df9a77ec2e7c Mon Sep 17 00:00:00 2001 From: Vivien Nicolas <21@vingtetun.org> Date: Fri, 24 Jun 2011 21:25:08 +0200 Subject: [PATCH 11/12] Do not add the font-loader canvas to the page dom to save some load time --- fonts.js | 58 +++++++++----------------------------------------------- 1 file changed, 9 insertions(+), 49 deletions(-) diff --git a/fonts.js b/fonts.js index 7e8aecd6d..c817b12e5 100644 --- a/fonts.js +++ b/fonts.js @@ -104,6 +104,7 @@ var FontsLoader = { worker ? obj.bindWorker(str) : obj.bindDOM(str); } + return ready; } }; @@ -766,96 +767,55 @@ var Font = (function () { return fontData; }, - bindWorker: function font_bind_worker(dataStr) { + bindWorker: function font_bindWorker(data) { postMessage({ action: "font", data: { - raw: dataStr, + raw: data, fontName: this.name, mimetype: this.mimetype } }); }, - bindDOM: function font_bind_dom(dataStr) { + bindDOM: function font_bindDom(data) { var fontName = this.name; /** Hack begin */ // Actually there is not event when a font has finished downloading so // the following code are a dirty hack to 'guess' when a font is ready + // This code could go away when bug 471915 has landed var canvas = document.createElement("canvas"); - var style = "border: 1px solid black; position:absolute; top: " + - (debug ? (100 * fontCount) : "-200") + "px; left: 2px; width: 340px; height: 100px"; - canvas.setAttribute("style", style); - canvas.setAttribute("width", 340); - canvas.setAttribute("heigth", 100); - document.body.appendChild(canvas); - - // Get the font size canvas think it will be for 'spaces' var ctx = canvas.getContext("2d"); - ctx.font = "bold italic 20px " + fontName + ", Symbol, Arial"; + ctx.font = "bold italic 20px " + fontName + ", Symbol"; var testString = " "; - // When debugging use the characters provided by the charsets to visually - // see what's happening instead of 'spaces' - var debug = false; - if (debug) { - var name = document.createElement("font"); - name.setAttribute("style", "position: absolute; left: 20px; top: " + - (100 * fontCount + 60) + "px"); - name.innerHTML = fontName; - document.body.appendChild(name); - - // Retrieve font charset - var charset = Fonts[fontName].properties.charset || []; - - // if the charset is too small make it repeat a few times - var count = 30; - while (count-- && charset.length <= 30) - charset = charset.concat(charset.slice()); - - for (var i = 0; i < charset.length; i++) { - var unicode = GlyphsUnicode[charset[i]]; - if (!unicode) - continue; - testString += String.fromCharCode(unicode); - } - - ctx.fillText(testString, 20, 20); - } - // Periodicaly check for the width of the testString, it will be // different once the real font has loaded var textWidth = ctx.measureText(testString).width; var interval = window.setInterval(function canvasInterval(self) { this.start = this.start || Date.now(); - ctx.font = "bold italic 20px " + fontName + ", Symbol, Arial"; + ctx.font = "bold italic 20px " + fontName + ", Symbol"; // For some reasons the font has not loaded, so mark it loaded for the // page to proceed but cry if ((Date.now() - this.start) >= kMaxWaitForFontFace) { window.clearInterval(interval); Fonts[fontName].loading = false; - warn("Is " + fontName + " for charset: " + charset + " loaded?"); + warn("Is " + fontName + " loaded?"); this.start = 0; } else if (textWidth != ctx.measureText(testString).width) { window.clearInterval(interval); Fonts[fontName].loading = false; this.start = 0; } - - if (debug) - ctx.fillText(testString, 20, 50); }, 30, this); /** Hack end */ - - // Convert the data string and add it to the page. - var base64 = window.btoa(dataStr); // Add the @font-face rule to the document - var url = "url(data:" + this.mimetype + ";base64," + base64 + ");"; + var url = "url(data:" + this.mimetype + ";base64," + window.btoa(data) + ");"; var rule = "@font-face { font-family:'" + fontName + "';src:" + url + "}"; var styleSheet = document.styleSheets[0]; styleSheet.insertRule(rule, styleSheet.length); From f13cbe883f32fea1b590a352b18ac0b18891ebf3 Mon Sep 17 00:00:00 2001 From: Vivien Nicolas <21@vingtetun.org> Date: Fri, 24 Jun 2011 21:46:48 +0200 Subject: [PATCH 12/12] Update the multi_page_viewer.js code to work with the new FontLoader API --- multi_page_viewer.js | 73 ++++++++++++-------------------------------- 1 file changed, 19 insertions(+), 54 deletions(-) diff --git a/multi_page_viewer.js b/multi_page_viewer.js index 3a02ea332..1631433ca 100644 --- a/multi_page_viewer.js +++ b/multi_page_viewer.js @@ -3,6 +3,8 @@ "use strict"; +var pageTimeout; + var PDFViewer = { queryParams: {}, @@ -75,81 +77,45 @@ var PDFViewer = { }, drawPage: function(num) { - if (!PDFViewer.pdf) { + if (!PDFViewer.pdf) return; - } - + var div = document.getElementById('pageContainer' + num); var canvas = document.createElement('canvas'); - + if (div && !div.hasChildNodes()) { - div.appendChild(canvas); - var page = PDFViewer.pdf.getPage(num); - + canvas.id = 'page' + num; canvas.mozOpaque = true; - + // Canvas dimensions must be specified in CSS pixels. CSS pixels // are always 96 dpi. These dimensions are 8.5in x 11in at 96dpi. canvas.width = PDFViewer.pageWidth(); canvas.height = PDFViewer.pageHeight(); - + div.appendChild(canvas); + var ctx = canvas.getContext('2d'); ctx.save(); ctx.fillStyle = 'rgb(255, 255, 255)'; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.restore(); - + var gfx = new CanvasGraphics(ctx); - var fonts = []; - + // page.compile will collect all fonts for us, once we have loaded them // we can trigger the actual page rendering with page.display + var fonts = []; page.compile(gfx, fonts); - - var areFontsReady = true; - - // Inspect fonts and translate the missing one - var fontCount = fonts.length; - - for (var i = 0; i < fontCount; i++) { - var font = fonts[i]; - - if (Fonts[font.name]) { - areFontsReady = areFontsReady && !Fonts[font.name].loading; - continue; + + var loadFont = function() { + if (!FontsLoader.bind(fonts)) { + pageTimeout = window.setTimeout(loadFont, 10); + return; } - - new Font(font.name, font.file, font.properties); - - areFontsReady = false; + page.display(gfx); } - - var pageInterval; - - var delayLoadFont = function() { - for (var i = 0; i < fontCount; i++) { - if (Fonts[font.name].loading) { - return; - } - } - - clearInterval(pageInterval); - - while (div.hasChildNodes()) { - div.removeChild(div.firstChild); - } - - PDFViewer.drawPage(num); - } - - if (!areFontsReady) { - pageInterval = setInterval(delayLoadFont, 10); - return; - } - - page.display(gfx); + loadFont(); } }, @@ -258,7 +224,6 @@ var PDFViewer = { }; window.onload = function() { - // Parse the URL query parameters into a cached object. PDFViewer.queryParams = function() { var qs = window.location.search.substring(1);