From 172dadb0084bbb122bbe7e463bb045769f75ac85 Mon Sep 17 00:00:00 2001 From: Vivien Nicolas <21@vingtetun.org> Date: Fri, 17 Jun 2011 13:55:42 +0200 Subject: [PATCH 1/2] Clean up some debug leftovers --- fonts.js | 158 +++++++++++++++++++++---------------------------------- pdf.js | 17 ++++-- 2 files changed, 71 insertions(+), 104 deletions(-) diff --git a/fonts.js b/fonts.js index 5390abba4..b9ef5b330 100644 --- a/fonts.js +++ b/fonts.js @@ -31,7 +31,7 @@ var fontCount = 0; var Fonts = { _active: null, get active() { - return this._active || { encoding: [] }; + return this._active; }, set active(aName) { @@ -39,8 +39,11 @@ var Fonts = { }, unicodeFromCode: function fonts_unicodeFromCode(aCode) { - var unicode = GlyphsUnicode[this.active.encoding[aCode]]; - return unicode ? unicode : aCode; + var active = this._active; + if (!active || !active.properties.encoding) + return aCode; + + return GlyphsUnicode[active.properties.encoding[aCode]]; } }; @@ -49,16 +52,10 @@ var Fonts = { * decoding logics whatever type it is (assuming the font type is supported). * * For example to read a Type1 font and to attach it to the document: - * var type1Font = new Font("MyFontName", binaryData, aFontEncoding, "Type1"); + * var type1Font = new Font("MyFontName", binaryFile, propertiesObject); * type1Font.bind(); - * - * As an improvment the last parameter can be replaced by an automatic guess - * of the font type based on the first byte of the file. - * - * FIXME There is now too many parameters, this should be turned into an - * object containing all the required informations about the font */ -var Font = function(aName, aFile, aEncoding, aCharset, aBBox, aType) { +var Font = function(aName, aFile, aProperties) { this.name = aName; // If the font has already been decoded simply return @@ -68,41 +65,40 @@ var Font = function(aName, aFile, aEncoding, aCharset, aBBox, aType) { } fontCount++; - var start = Date.now(); - switch (aType) { + switch (aProperties.type) { case "Type1": - var cff = new CFF(aName, aBBox, aFile); + var cff = new CFF(aName, aFile, aProperties); this.mimetype = "font/otf"; // Wrap the CFF data inside an OTF font file - this.font = this.cover(cff); + this.font = this.cover(cff, aProperties); break; case "TrueType": + // TrueType is disabled for the moment since the sanitizer prevent it + // from loading because of an overdated cmap table return Fonts[aName] = { data: null, - encoding: {}, - charset: null, + properties: { + encoding: {}, + charset: null + }, loading: false }; - // TrueType is disabled for the moment since the sanitizer prevent it - // from loading this.mimetype = "font/ttf"; var ttf = new TrueType(aFile); this.font = ttf.data; break; default: - warn("Font " + aType + " is not supported"); + warn("Font " + aProperties.type + " is not supported"); break; } - var end = Date.now(); Fonts[aName] = { data: this.font, - encoding: aEncoding, - charset: aCharset ? aCharset.slice() : null, + properties: aProperties, loading: true } @@ -204,7 +200,7 @@ Font.prototype = { if (debug) ctx.fillText(testString, 20, 50); - }, 20, this); + }, 50, this); /** Hack end */ @@ -355,7 +351,7 @@ Font.prototype = { idDeltas, idRangeOffsets, glyphsIdsArray); }, - cover: function font_cover(aFont) { + cover: function font_cover(aFont, aProperties) { var otf = new Uint8Array(kMaxFontFileSize); // Required Tables @@ -431,7 +427,7 @@ Font.prototype = { this._createTableEntry(otf, offsets, "OS/2", OS2); //XXX Getting charstrings here seems wrong since this is another CFF glue - var charstrings = aFont.getOrderedCharStrings(aFont.glyphs); + var charstrings = aFont.getOrderedCharStrings(aProperties.glyphs); /** CMAP */ cmap = this._createCMAPTable(charstrings); @@ -715,22 +711,11 @@ var TrueType = function(aFile) { ttf.set(tableData, offsets.currentOffset); offsets.currentOffset += tableData.length; - if (0) { - var data = []; - for (var j = 0; j < tableData.length; j++) - d.push(tableData[j]); - log("data for table: " + table.tag + ": " + data); - } - // 4-byte aligned data while (offsets.currentOffset & 3) offsets.currentOffset++; } - var fontData = []; - for (var i = 0; i < ttf.length; i++) - fontData.push(ttf[i]); - this.data = ttf; return; } else if (requiredTables.lenght) { @@ -844,17 +829,11 @@ TrueType.prototype = { /** - * This dictionary holds decoded fonts data. + * Type1Parser encapsulate the needed code for parsing a Type1 font + * program. + * Some of its logic depends on the Type2 charstrings structure. */ var Type1Parser = function() { - // Turn on this flag for additional debugging logs - var debug = false; - - var dump = function(aData) { - if (debug) - log(aData); - }; - /* * Decrypt a Sequence of Ciphertext Bytes to Produce the Original Sequence * of Plaintext Bytes. The function took a key as a parameter which can be @@ -863,8 +842,7 @@ var Type1Parser = function() { var kEexecEncryptionKey = 55665; var kCharStringsEncryptionKey = 4330; - function decrypt(aStream, aKey, aDiscardNumber, aByteArray) { - var start = Date.now(); + function decrypt(aStream, aKey, aDiscardNumber) { var r = aKey, c1 = 52845, c2 = 22719; var decryptedString = []; @@ -872,14 +850,9 @@ var Type1Parser = function() { var count = aStream.length; for (var i = 0; i < count; i++) { value = aStream[i]; - if (aByteArray) - decryptedString[i] = value ^ (r >> 8); - else - decryptedString[i] = String.fromCharCode(value ^ (r >> 8)); + decryptedString[i] = value ^ (r >> 8); r = ((value + r) * c1 + c2) & ((1 << 16) - 1); } - var end = Date.now(); - dump("Time to decrypt string of length " + count + " is " + (end - start)); return decryptedString.slice(aDiscardNumber); }; @@ -1014,8 +987,7 @@ var Type1Parser = function() { } else if (!command) { break; } else if (command == -1) { - log("decodeCharstring: " + charString); - error("Support for Type1 command " + value + " (" + escape + ") is not implemented"); + error("Support for Type1 command " + value + " (" + escape + ") is not implemented in charstring: " + charString); } value = command; @@ -1042,8 +1014,8 @@ var Type1Parser = function() { * Returns an object containing a Subrs array and a CharStrings array * extracted from and eexec encrypted block of data */ - this.extractFontInfo = function(aStream) { - var eexecString = decrypt(aStream, kEexecEncryptionKey, 4, true); + this.extractFontProgram = function t1_extractFontProgram(aStream) { + var eexecString = decrypt(aStream, kEexecEncryptionKey, 4); var subrs = [], glyphs = []; var inSubrs = inGlyphs = false; var glyph = ""; @@ -1052,15 +1024,15 @@ var Type1Parser = function() { var index = 0; var length = 0; - var count = eexecString.length; var c = ""; + var count = eexecString.length; for (var i = 0; i < count; i++) { var c = eexecString[i]; if (inSubrs && c == 0x52) { length = parseInt(length); var data = eexecString.slice(i + 3, i + 3 + length); - var encodedSubr = decrypt(data, kCharStringsEncryptionKey, 4, true); + var encodedSubr = decrypt(data, kCharStringsEncryptionKey, 4); var subr = decodeCharString(encodedSubr); subrs.push(subr); @@ -1068,7 +1040,7 @@ var Type1Parser = function() { } else if (inGlyphs && c == 0x52) { length = parseInt(length); var data = eexecString.slice(i + 3, i + 3 + length); - var encodedCharstring = decrypt(data, kCharStringsEncryptionKey, 4, true); + var encodedCharstring = decrypt(data, kCharStringsEncryptionKey, 4); var subr = decodeCharString(encodedCharstring); glyphs.push({ @@ -1104,36 +1076,37 @@ var Type1Parser = function() { } }; -var CFF = function(aFontName, aFontBBox, aFontFile) { +/** + * Take a Type1 file as input and wrap it into a Compact Font Format (CFF) + * wrapping Type2 charstrings. + */ +var CFF = function(aName, aFile, aProperties) { // Get the data block containing glyphs and subrs informations - var length1 = aFontFile.dict.get("Length1"); - var length2 = aFontFile.dict.get("Length2"); - aFontFile.skip(length1); - var eexecBlock = aFontFile.getBytes(length2); + var length1 = aFile.dict.get("Length1"); + var length2 = aFile.dict.get("Length2"); + aFile.skip(length1); + var eexecBlock = aFile.getBytes(length2); - // Extract informations from it - var start = Date.now(); + // Decrypt the data blocks and retrieve the informations from it var parser = new Type1Parser(); - var fontInfo = parser.extractFontInfo(eexecBlock); - fontInfo.name = aFontName; - fontInfo.bbox = aFontBBox; + var fontInfo = parser.extractFontProgram(eexecBlock); - // XXX This hold the glyph data as if, this should be improved - this.glyphs = fontInfo.charstrings; - - this.data = this.convertToCFF(fontInfo); - var end = Date.now(); + aProperties.subrs = fontInfo.subrs; + aProperties.glyphs = fontInfo.charstrings; + this.data = this.wrap(aName, aProperties); }; CFF.prototype = { createCFFIndexHeader: function(aObjects, aIsByte) { - var data = []; - // First 2 bytes contains the number of objects contained into this index var count = aObjects.length; - if (count ==0) + + // If there is no object, just create an array saying that with another + // offset byte. + if (count == 0) return [0x00, 0x00, 0x00]; + var data = []; var bytes = FontsUtils.integerToBytes(count, 2); for (var i = 0; i < bytes.length; i++) data.push(bytes[i]); @@ -1164,10 +1137,9 @@ CFF.prototype = { encodeNumber: function(aValue) { var x = 0; if (aValue >= -32768 && aValue <= 32767) { - return [ 28, aValue >> 8, aValue ]; + return [ 28, aValue >> 8, aValue & 0xFF ]; } else if (aValue >= (-2147483647-1) && aValue <= 2147483647) { - return [ - 0xFF, aValue >> 24, Value >> 16, aValue >> 8, aValue ]; + return [ 0xFF, aValue >> 24, Value >> 16, aValue >> 8, aValue & 0xFF ]; } else { error("Value: " + aValue + " is not allowed"); } @@ -1220,7 +1192,6 @@ CFF.prototype = { }, flattenCharstring: function(aGlyph, aCharstring, aSubrs) { - var original = aCharstring.slice(); var i = 0; while (true) { var obj = aCharstring[i]; @@ -1324,17 +1295,10 @@ CFF.prototype = { error("failing with i = " + i + " in charstring:" + aCharstring + "(" + aCharstring.length + ")"); }, - convertToCFF: function(aFontInfo) { - var debug = false; - function dump(aMsg) { - if (debug) - log(aMsg); - }; - - var charstrings = this.getOrderedCharStrings(aFontInfo.charstrings); + wrap: function(aName, aProperties) { + var charstrings = this.getOrderedCharStrings(aProperties.glyphs); // Starts the conversion of the Type1 charstrings to Type2 - var start = Date.now(); var charstringsCount = 0; var charstringsDataLength = 0; var glyphs = []; @@ -1342,16 +1306,12 @@ CFF.prototype = { var charstring = charstrings[i].charstring.slice(); var glyph = charstrings[i].glyph; - var flattened = this.flattenCharstring(glyph, charstring, aFontInfo.subrs); + var flattened = this.flattenCharstring(glyph, charstring, aProperties.subrs); glyphs.push(flattened); charstringsCount++; charstringsDataLength += flattened.length; } - var end = Date.now(); - dump("There is " + charstringsCount + " glyphs (size: " + charstringsDataLength + ")"); - dump("Time to flatten the strings is : " + (end -start)); - // Create a CFF font data var cff = new Uint8Array(kMaxFontFileSize); var currentOffset = 0; @@ -1362,7 +1322,7 @@ CFF.prototype = { cff.set(header); // Names Index - var nameIndex = this.createCFFIndexHeader([aFontInfo.name]); + var nameIndex = this.createCFFIndexHeader([aName]); cff.set(nameIndex, currentOffset); currentOffset += nameIndex.length; @@ -1406,7 +1366,7 @@ CFF.prototype = { 248, 31, 4 // Weight ]; - var fontBBox = aFontInfo.bbox; + var fontBBox = aProperties.bbox; for (var i = 0; i < fontBBox.length; i++) topDictIndex = topDictIndex.concat(this.encodeNumber(fontBBox[i])); topDictIndex.push(5) // FontBBox; diff --git a/pdf.js b/pdf.js index fe636bcf9..b8f32226b 100644 --- a/pdf.js +++ b/pdf.js @@ -1737,6 +1737,7 @@ var CanvasGraphics = (function() { // Generate the custom cmap of the font if needed var encodingMap = {}; if (fontDict.has("Encoding")) { + var encoding = xref.fetchIfRef(fontDict.get("Encoding")); if (IsDict(encoding)) { // Build an map between codes and glyphs @@ -1749,6 +1750,7 @@ var CanvasGraphics = (function() { // Get the font charset var charset = descriptor.get("CharSet").split("/"); + } else if (IsName(encoding)) { var encoding = Encodings[encoding]; var widths = xref.fetchIfRef(fontDict.get("Widths")); @@ -1757,15 +1759,20 @@ var CanvasGraphics = (function() { var charset = []; for (var j = 0; j < widths.length; j++) { var index = widths[j]; - if (index) - charset.push(encoding[j + firstchar]); + if (!index) + continue; + charset.push(encoding[j + firstchar]); } } } - var fontBBox = descriptor.get("FontBBox"); - var subtype = fontDict.get("Subtype").name; - new Font(fontName, fontFile, encodingMap, charset, fontBBox, subtype); + var properties = { + type: fontDict.get("Subtype").name, + encoding: encodingMap, + charset: charset, + bbox: descriptor.get("FontBBox") + }; + new Font(fontName, fontFile, properties); } return Fonts[fontName]; }, From 4bd3308b7838ef346c450205c16ea4f398eb50dc Mon Sep 17 00:00:00 2001 From: Vivien Nicolas <21@vingtetun.org> Date: Fri, 17 Jun 2011 20:29:34 +0200 Subject: [PATCH 2/2] Do not assume charset is defined and move some fonts timing to the right place --- pdf.js | 77 ++++++++++++++++++++++++++++++--------------------------- test.js | 55 +++++++++++++++++++++++++---------------- 2 files changed, 75 insertions(+), 57 deletions(-) diff --git a/pdf.js b/pdf.js index b8f32226b..1a1714647 100644 --- a/pdf.js +++ b/pdf.js @@ -1729,52 +1729,57 @@ var CanvasGraphics = (function() { var fontName = descriptor.get("FontName").name; fontName = fontName.replace("+", "_"); - var font = Fonts[fontName]; - if (!font) { - var fontFile = descriptor.get2("FontFile", "FontFile2"); - fontFile = xref.fetchIfRef(fontFile); + var fontFile = descriptor.get2("FontFile", "FontFile2"); + if (!fontFile) + errort("FontFile not found for font: " + fontName); + fontFile = xref.fetchIfRef(fontFile); - // Generate the custom cmap of the font if needed - var encodingMap = {}; - if (fontDict.has("Encoding")) { + // Generate the custom cmap of the font if needed + var encodingMap = {}; + if (fontDict.has("Encoding")) { + var encoding = xref.fetchIfRef(fontDict.get("Encoding")); + if (IsDict(encoding)) { + // Build an map between codes and glyphs + var differences = encoding.get("Differences"); + var index = 0; + for (var j = 0; j < differences.length; j++) { + var data = differences[j]; + IsNum(data) ? index = data : encodingMap[index++] = data; + } - var encoding = xref.fetchIfRef(fontDict.get("Encoding")); - if (IsDict(encoding)) { - // Build an map between codes and glyphs - var differences = encoding.get("Differences"); - var index = 0; - for (var j = 0; j < differences.length; j++) { - var data = differences[j]; - IsNum(data) ? index = data : encodingMap[index++] = data; - } + // Get the font charset if any + var charset = descriptor.get("CharSet"); + if (charset) + charset = charset.split("/"); - // Get the font charset - var charset = descriptor.get("CharSet").split("/"); + } else if (IsName(encoding)) { + var encoding = Encodings[encoding]; + var widths = xref.fetchIfRef(fontDict.get("Widths")); + var firstchar = xref.fetchIfRef(fontDict.get("FirstChar")); - } else if (IsName(encoding)) { - var encoding = Encodings[encoding]; - var widths = xref.fetchIfRef(fontDict.get("Widths")); - var firstchar = xref.fetchIfRef(fontDict.get("FirstChar")); + var charset = []; + for (var j = 0; j < widths.length; j++) { + var index = widths[j]; + if (!index) + continue; - var charset = []; - for (var j = 0; j < widths.length; j++) { - var index = widths[j]; - if (!index) - continue; - charset.push(encoding[j + firstchar]); - } - } - } + charset.push(encoding[j + firstchar]); + } + } + } - var properties = { + var properties = { type: fontDict.get("Subtype").name, encoding: encodingMap, charset: charset, bbox: descriptor.get("FontBBox") - }; - new Font(fontName, fontFile, properties); - } - return Fonts[fontName]; + }; + + return { + name: fontName, + file: fontFile, + properties: properties + } }, beginDrawing: function(mediaBox) { diff --git a/test.js b/test.js index cbfd70c65..59d8167a2 100644 --- a/test.js +++ b/test.js @@ -74,30 +74,43 @@ function displayPage(num) { page.compile(gfx, fonts); var t2 = Date.now(); - var interval = 0; - for (var i = 0; i < fonts.length; i++) { - if (fonts[i].loading) { - interval = 10; - break; + 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; } + + 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; + } + clearInterval(pageInterval); + + var t3 = Date.now(); + + page.display(gfx); + + var t4 = Date.now(); + + var infoDisplay = document.getElementById("info"); + infoDisplay.innerHTML = "Time to load/compile/fonts/render: "+ (t1 - t0) + "/" + (t2 - t1) + "/" + (t3 - t2) + "/" + (t4 - t3) + " ms"; }; - // FIXME This need to be replaced by an event - pageInterval = setInterval(function() { - for (var i = 0; i < fonts.length; i++) { - if (fonts[i].loading) - return; - } - var t3 = Date.now(); - - clearInterval(pageInterval); - page.display(gfx); - - var t4 = Date.now(); - - var infoDisplay = document.getElementById("info"); - infoDisplay.innerHTML = "Time to load/compile/fonts/render: "+ (t1 - t0) + "/" + (t2 - t1) + "/" + (t3 - t2) + "/" + (t4 - t3) + " ms"; - }, interval); + if (fontsReady) { + delayLoadFont(); + } else { + pageInterval = setInterval(delayLoadFont, 10); + } } function nextPage() {