diff --git a/LICENSE b/LICENSE index d96b927a3..f8a848205 100644 --- a/LICENSE +++ b/LICENSE @@ -8,6 +8,7 @@ Justin D'Arcangelo Yury Delendik Kalervo Kujala + Adil Allawi <@ironymark> Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), diff --git a/Makefile b/Makefile index 0ca269642..fb4ffe9cb 100644 --- a/Makefile +++ b/Makefile @@ -55,30 +55,30 @@ browser-test: --browserManifestFile=$(PDF_BROWSERS) \ --manifestFile=$(PDF_TESTS) -# make shell-test -# -# This target runs all of the tests that can be run in a JS shell. -# The shell used is taken from the JS_SHELL environment variable. If -# that variable is not defined, the script will attempt to use the copy -# of Rhino that comes with the Closure compiler used for producing the -# website. -SHELL_TARGET = $(NULL) -ifeq ($(JS_SHELL),) -JS_SHELL := "java -cp $(BUILD_DIR)/compiler.jar" -JS_SHELL += "com.google.javascript.jscomp.mozilla.rhino.tools.shell.Main" -SHELL_TARGET = compiler -endif - -shell-test: shell-msg $(SHELL_TARGET) font-test -shell-msg: -ifeq ($(SHELL_TARGET), compiler) - @echo "No JS_SHELL env variable present." - @echo "The default is to find a copy of Rhino and try that." -endif - @echo "JS shell command is: $(JS_SHELL)" - -font-test: - @echo "font test stub." +# # make shell-test +# # +# # This target runs all of the tests that can be run in a JS shell. +# # The shell used is taken from the JS_SHELL environment variable. If +# # that variable is not defined, the script will attempt to use the copy +# # of Rhino that comes with the Closure compiler used for producing the +# # website. +# SHELL_TARGET = $(NULL) +# ifeq ($(JS_SHELL),) +# JS_SHELL := "java -cp $(BUILD_DIR)/compiler.jar" +# JS_SHELL += "com.google.javascript.jscomp.mozilla.rhino.tools.shell.Main" +# SHELL_TARGET = compiler +# endif +# +# shell-test: shell-msg $(SHELL_TARGET) font-test +# shell-msg: +# ifeq ($(SHELL_TARGET), compiler) +# @echo "No JS_SHELL env variable present." +# @echo "The default is to find a copy of Rhino and try that." +# endif +# @echo "JS shell command is: $(JS_SHELL)" +# +# font-test: +# @echo "font test stub." # make lint # @@ -133,18 +133,18 @@ $(GH_PAGES)/web/%: web/% $(GH_PAGES)/web/images/%: web/images/% @cp $< $@ -# make compiler -# -# This target downloads the Closure compiler, and places it in the -# build directory. This target is also useful when the user doesn't -# have a JS shell available--we can have them use the Rhino shell that -# comes with Closure. -COMPILER_URL = http://closure-compiler.googlecode.com/files/compiler-latest.zip - -compiler: $(BUILD_DIR)/compiler.zip -$(BUILD_DIR)/compiler.zip: | $(BUILD_DIR) - curl $(COMPILER_URL) > $(BUILD_DIR)/compiler.zip; - cd $(BUILD_DIR); unzip compiler.zip compiler.jar; +# # make compiler +# # +# # This target downloads the Closure compiler, and places it in the +# # build directory. This target is also useful when the user doesn't +# # have a JS shell available--we can have them use the Rhino shell that +# # comes with Closure. +# COMPILER_URL = http://closure-compiler.googlecode.com/files/compiler-latest.zip +# +# compiler: $(BUILD_DIR)/compiler.zip +# $(BUILD_DIR)/compiler.zip: | $(BUILD_DIR) +# curl $(COMPILER_URL) > $(BUILD_DIR)/compiler.zip; +# cd $(BUILD_DIR); unzip compiler.zip compiler.jar; # make firefox-extension # diff --git a/README.md b/README.md index 0d439b139..c6cf92ede 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,84 @@ # pdf.js -pdf.js is a technology demonstrator prototype to explore whether the HTML5 -platform is complete enough to faithfully and efficiently render the ISO -32000-1:2008 Portable Document Format (PDF) without native code assistance. -pdf.js is not currently part of the Mozilla project, and there is no plan -yet to integrate it into Firefox. We will explore that possibility once -pdf.js is production ready. Until then we aim to publish a Firefox -PDF reader extension powered by pdf.js. + +## Overview + +pdf.js is an HTML5 technology experiment that explores building a faithful +and efficient Portable Document Format (PDF) renderer without native code +assistance. + +pdf.js is community-driven and supported by Mozilla Labs. Our goal is to +create a general-purpose, web standards-based platform for parsing and +rendering PDFs, and eventually release a PDF reader extension powered by +pdf.js. Integration with Firefox is a possibility if the experiment proves +successful. + + + +## Getting started + +**Online demo** + +For an online demo, visit: + + http://andreasgal.github.com/pdf.js/web/viewer.html + +This demo provides an interactive interface for displaying and browsing PDFs +using the pdf.js API. + +**Hello world** + +For a "hello world" example, take a look at: + + examples/helloworld/ + +This example illustrates the bare minimum ingredients for integrating pdf.js +in a custom project. + + + +## Running the Tests + +pdf.js comes with browser-level regression tests that allow one to probe +whether it's able to successfully parse PDFs, as well as compare its output +against reference images, pixel-by-pixel. + +To run the tests, first configure the browser manifest file at: + + test/resources/browser_manifests/browser_manifest.json + +Sample manifests for different platforms are provided in that directory. + +To run all the bundled tests, type: + + $ make test + +and cross your fingers. Different types of tests are available, see the test +manifest file at: + + test/test_manifest.json + +The test type `eq` tests whether the output images are identical to reference +images. The test type `load` simply tests whether the file loads without +raising any errors. + + +## Contributing + +pdf.js is a community-driver project, so contributors are always welcome. +Simply fork our repo and contribute away. A great place to start is our +open issues. + +For better consistency and long-term stability, please do look around the +code and try to follow our conventions. + + +## Additional resources Our demo site is here: - http://andreasgal.github.com/pdf.js/ + http://andreasgal.github.com/pdf.js/web/viewer.html You can read more about pdf.js here: @@ -19,14 +86,19 @@ You can read more about pdf.js here: http://blog.mozilla.com/cjones/2011/06/15/overview-of-pdf-js-guts/ -follow us on twitter: @pdfjs +Follow us on twitter: @pdfjs http://twitter.com/#!/pdfjs -join our mailing list: +Join our mailing list: dev-pdf-js@lists.mozilla.org + +Subscribe either using lists.mozilla.org or Google Groups: + + https://lists.mozilla.org/listinfo/dev-pdf-js + https://groups.google.com/group/mozilla.dev.pdf-js/topics -and talk to us on IRC: +Talk to us on IRC: #pdfjs on irc.mozilla.org diff --git a/examples/helloworld/README.md b/examples/helloworld/README.md new file mode 100644 index 000000000..8395733f3 --- /dev/null +++ b/examples/helloworld/README.md @@ -0,0 +1,16 @@ +## "Hello World" overview + +This example is a minimalistic application of the pdf.js project. The file +`helloworld.pdf` is from the GNUpdf project (see [Introduction to PDF at GNUpdf](http://gnupdf.org/Introduction_to_PDF), and contains a simple and +human-readable PDF. + + +## Getting started + +Point your browser to `index.html`. Voila. Take a peek at `hello.js` to see +how to make basic calls to `pdf.js`. + + +## Additional resources + ++ [GNUpdf - Introduction to PDF](http://gnupdf.org/Introduction_to_PDF) diff --git a/examples/helloworld/hello.js b/examples/helloworld/hello.js new file mode 100644 index 000000000..21799c33a --- /dev/null +++ b/examples/helloworld/hello.js @@ -0,0 +1,49 @@ +// +// See README for overview +// + + +// +// Ajax GET request, for binary files +// (like jQuery's $.get(), but supports the binary type ArrayBuffer) +// +var ajaxGet = function(url, callback){ + var xhr = new XMLHttpRequest(); + xhr.open('GET', url); + xhr.mozResponseType = xhr.responseType = 'arraybuffer'; + xhr.expected = (document.URL.indexOf('file:') === 0) ? 0 : 200; + xhr.onreadystatechange = function() { + if (xhr.readyState === 4 && xhr.status === xhr.expected) { + var data = (xhr.mozResponseArrayBuffer || xhr.mozResponse || + xhr.responseArrayBuffer || xhr.response); + + callback(data); + } + }; + xhr.send(null); +} + +// +// This is where the fun happens +// +ajaxGet('helloworld.pdf', function(data){ + // + // Instantiate PDFDoc with PDF data + // + var pdf = new PDFDoc(new Stream(data)); + var page = pdf.getPage(1); + var scale = 1.5; + + // + // Prepare canvas using PDF page dimensions + // + var canvas = document.getElementById('the-canvas'); + var context = canvas.getContext('2d'); + canvas.height = page.height * scale; + canvas.width = page.width * scale; + + // + // Render PDF page into canvas context + // + page.startRendering(context); +}); diff --git a/examples/helloworld/helloworld.pdf b/examples/helloworld/helloworld.pdf new file mode 100644 index 000000000..d98b4e1db --- /dev/null +++ b/examples/helloworld/helloworld.pdf @@ -0,0 +1,68 @@ +%PDF-1.7 + +1 0 obj % entry point +<< + /Type /Catalog + /Pages 2 0 R +>> +endobj + +2 0 obj +<< + /Type /Pages + /MediaBox [ 0 0 200 200 ] + /Count 1 + /Kids [ 3 0 R ] +>> +endobj + +3 0 obj +<< + /Type /Page + /Parent 2 0 R + /Resources << + /Font << + /F1 4 0 R + >> + >> + /Contents 5 0 R +>> +endobj + +4 0 obj +<< + /Type /Font + /Subtype /Type1 + /BaseFont /Times-Roman +>> +endobj + +5 0 obj % page content +<< + /Length 44 +>> +stream +BT +70 50 TD +/F1 12 Tf +(Hello, world!) Tj +ET +endstream +endobj + +xref +0 6 +0000000000 65535 f +0000000010 00000 n +0000000079 00000 n +0000000173 00000 n +0000000301 00000 n +0000000380 00000 n +trailer +<< + /Size 6 + /Root 1 0 R +>> +startxref +492 +%%EOF \ No newline at end of file diff --git a/examples/helloworld/index.html b/examples/helloworld/index.html new file mode 100644 index 000000000..c353b6a89 --- /dev/null +++ b/examples/helloworld/index.html @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + diff --git a/fonts.js b/fonts.js old mode 100755 new mode 100644 index aae775dc3..105df5ec6 --- a/fonts.js +++ b/fonts.js @@ -12,6 +12,10 @@ var kMaxWaitForFontFace = 1000; // Unicode Private Use Area var kCmapGlyphOffset = 0xE000; +// PDF Glyph Space Units are one Thousandth of a TextSpace Unit +// except for Type 3 fonts +var kPDFGlyphSpaceUnits = 1000; + // Until hinting is fully supported this constant can be used var kHintingEnabled = false; @@ -436,6 +440,7 @@ var Font = (function Font() { // name ArialBlack for example will be replaced by Helvetica. this.black = (name.search(/Black/g) != -1); + this.defaultWidth = properties.defaultWidth; this.loadedName = fontName.split('-')[0]; this.loading = false; return; @@ -472,6 +477,7 @@ var Font = (function Font() { this.data = data; this.type = properties.type; this.textMatrix = properties.textMatrix; + this.defaultWidth = properties.defaultWidth; this.loadedName = getUniqueName(); this.composite = properties.composite; this.loading = true; @@ -534,6 +540,10 @@ var Font = (function Font() { }; function createOpenTypeHeader(sfnt, file, numTables) { + // Windows hates the Mac TrueType sfnt version number + if (sfnt == 'true') + sfnt = string32(0x00010000); + // sfnt version (4 bytes) var header = sfnt; @@ -587,19 +597,24 @@ var Font = (function Font() { var codes = []; var length = glyphs.length; for (var n = 0; n < length; ++n) - codes.push(String.fromCharCode(glyphs[n].unicode)); - codes.sort(); + codes.push({ unicode: glyphs[n].unicode, code: n }); + codes.sort(function(a, b) { + return a.unicode - b.unicode; + }); // Split the sorted codes into ranges. var ranges = []; for (var n = 0; n < length; ) { - var start = codes[n++].charCodeAt(0); + var start = codes[n].unicode; + var startCode = codes[n].code; + ++n; var end = start; - while (n < length && end + 1 == codes[n].charCodeAt(0)) { + while (n < length && end + 1 == codes[n].unicode) { ++end; ++n; } - ranges.push([start, end]); + var endCode = codes[n - 1].code; + ranges.push([start, end, startCode, endCode]); } return ranges; @@ -628,21 +643,38 @@ var Font = (function Font() { var idRangeOffsets = ''; var glyphsIds = ''; var bias = 0; - for (var i = 0; i < segCount - 1; i++) { - var range = ranges[i]; - var start = range[0]; - var end = range[1]; - var offset = (segCount - i) * 2 + bias * 2; - bias += (end - start + 1); - - startCount += string16(start); - endCount += string16(end); - idDeltas += string16(0); - idRangeOffsets += string16(offset); - } - for (var i = 0; i < glyphs.length; i++) - glyphsIds += string16(deltas ? deltas[i] : i + 1); + if (deltas) { + for (var i = 0; i < segCount - 1; i++) { + var range = ranges[i]; + var start = range[0]; + var end = range[1]; + var offset = (segCount - i) * 2 + bias * 2; + bias += (end - start + 1); + + startCount += string16(start); + endCount += string16(end); + idDeltas += string16(0); + idRangeOffsets += string16(offset); + + var startCode = range[2]; + var endCode = range[3]; + for (var j = startCode; j <= endCode; ++j) + glyphsIds += string16(deltas[j]); + } + } else { + for (var i = 0; i < segCount - 1; i++) { + var range = ranges[i]; + var start = range[0]; + var end = range[1]; + var startCode = range[2]; + + startCount += string16(start); + endCount += string16(end); + idDeltas += string16((startCode - start + 1) & 0xFFFF); + idRangeOffsets += string16(0); + } + } endCount += '\xFF\xFF'; startCount += '\xFF\xFF'; @@ -663,7 +695,9 @@ var Font = (function Font() { format314); }; - function createOS2Table(properties) { + function createOS2Table(properties, override) { + var override = override || {}; + var ulUnicodeRange1 = 0; var ulUnicodeRange2 = 0; var ulUnicodeRange3 = 0; @@ -694,6 +728,24 @@ var Font = (function Font() { } } + var unitsPerEm = override.unitsPerEm || kPDFGlyphSpaceUnits; + var typoAscent = override.ascent || properties.ascent; + var typoDescent = override.descent || properties.descent; + var winAscent = override.yMax || typoAscent; + var winDescent = -override.yMin || -typoDescent; + + // if there is a units per em value but no other override + // then scale the calculated ascent + if (unitsPerEm != kPDFGlyphSpaceUnits && + 'undefined' == typeof(override.ascent)) { + // if the font units differ to the PDF glyph space units + // then scale up the values + typoAscent = Math.round(typoAscent * unitsPerEm / kPDFGlyphSpaceUnits); + typoDescent = Math.round(typoDescent * unitsPerEm / kPDFGlyphSpaceUnits); + winAscent = typoAscent; + winDescent = -typoDescent; + } + return '\x00\x03' + // version '\x02\x24' + // xAvgCharWidth '\x01\xF4' + // usWeightClass @@ -722,11 +774,11 @@ var Font = (function Font() { string16(firstCharIndex || properties.firstChar) + // usFirstCharIndex string16(lastCharIndex || properties.lastChar) + // usLastCharIndex - string16(properties.ascent) + // sTypoAscender - string16(properties.descent) + // sTypoDescender + string16(typoAscent) + // sTypoAscender + string16(typoDescent) + // sTypoDescender '\x00\x64' + // sTypoLineGap (7%-10% of the unitsPerEM value) - string16(properties.ascent) + // usWinAscent - string16(-properties.descent) + // usWinDescent + string16(winAscent) + // usWinAscent + string16(winDescent) + // usWinDescent '\x00\x00\x00\x00' + // ulCodePageRange1 (Bits 0-31) '\x00\x00\x00\x00' + // ulCodePageRange2 (Bits 32-63) string16(properties.xHeight) + // sxHeight @@ -833,9 +885,11 @@ var Font = (function Font() { var data = file.getBytes(length); file.pos = previousPosition; - if (tag == 'head') + if (tag == 'head') { // clearing checksum adjustment data[8] = data[9] = data[10] = data[11] = 0; + data[17] |= 0x20; //Set font optimized for cleartype flag + } return { tag: tag, @@ -1001,6 +1055,49 @@ var Font = (function Font() { } }; + function sanitizeGlyphLocations(loca, glyf, numGlyphs, + isGlyphLocationsLong) { + var itemSize, itemDecode, itemEncode; + if (isGlyphLocationsLong) { + itemSize = 4; + itemDecode = function(data, offset) { + return (data[offset] << 24) | (data[offset + 1] << 16) | + (data[offset + 2] << 8) | data[offset + 3]; + }; + itemEncode = function(data, offset, value) { + data[offset] = (value >>> 24) & 0xFF; + data[offset + 1] = (value >> 16) & 0xFF; + data[offset + 2] = (value >> 8) & 0xFF; + data[offset + 3] = value & 0xFF; + }; + } else { + itemSize = 2; + itemDecode = function(data, offset) { + return (data[offset] << 9) | (data[offset + 1] << 1); + }; + itemEncode = function(data, offset, value) { + data[offset] = (value >> 9) & 0xFF; + data[offset + 1] = (value >> 1) & 0xFF; + }; + } + var locaData = loca.data; + var startOffset = itemDecode(locaData, 0); + var firstOffset = itemDecode(locaData, itemSize); + if (firstOffset - startOffset < 12 || startOffset > 0) { + // removing first glyph + glyf.data = glyf.data.subarray(firstOffset); + glyf.length -= firstOffset; + + itemEncode(locaData, 0, 0); + var i, pos = itemSize; + for (i = 1; i <= numGlyphs; ++i) { + itemEncode(locaData, pos, + itemDecode(locaData, pos) - firstOffset); + pos += itemSize; + } + } + } + // Check that required tables are present var requiredTables = ['OS/2', 'cmap', 'head', 'hhea', 'hmtx', 'maxp', 'name', 'post']; @@ -1008,7 +1105,7 @@ var Font = (function Font() { var header = readOpenTypeHeader(font); var numTables = header.numTables; - var cmap, maxp, hhea, hmtx, vhea, vmtx; + var cmap, maxp, hhea, hmtx, vhea, vmtx, head, loca, glyf; var tables = []; for (var i = 0; i < numTables; i++) { var table = readTableEntry(font); @@ -1022,6 +1119,8 @@ var Font = (function Font() { hhea = table; else if (table.tag == 'hmtx') hmtx = table; + else if (table.tag == 'head') + head = table; requiredTables.splice(index, 1); } else { @@ -1029,6 +1128,10 @@ var Font = (function Font() { vmtx = table; else if (table.tag == 'vhea') vhea = table; + else if (table.tag == 'loca') + loca = table; + else if (table.tag == 'glyf') + glyf = table; } tables.push(table); } @@ -1048,9 +1151,19 @@ var Font = (function Font() { createOpenTypeHeader(header.version, ttf, numTables); if (requiredTables.indexOf('OS/2') != -1) { + // extract some more font properties from the OpenType head and + // hhea tables; yMin and descent value are always negative + var override = { + unitsPerEm: int16([head.data[18], head.data[19]]), + yMax: int16([head.data[42], head.data[43]]), + yMin: int16([head.data[38], head.data[39]]) - 0x10000, + ascent: int16([hhea.data[4], hhea.data[5]]), + descent: int16([hhea.data[6], hhea.data[7]]) - 0x10000 + }; + tables.push({ tag: 'OS/2', - data: stringToArray(createOS2Table(properties)) + data: stringToArray(createOS2Table(properties, override)) }); } @@ -1063,6 +1176,11 @@ var Font = (function Font() { sanitizeMetrics(font, hhea, hmtx, numGlyphs); sanitizeMetrics(font, vhea, vmtx, numGlyphs); + if (head && loca && glyf) { + var isGlyphLocationsLong = int16([head.data[50], head.data[51]]); + sanitizeGlyphLocations(loca, glyf, numGlyphs, isGlyphLocationsLong); + } + // Sanitizer reduces the glyph advanceWidth to the maxAdvanceWidth // Sometimes it's 0. That needs to be fixed if (hhea.data[10] == 0 && hhea.data[11] == 0) { @@ -1087,7 +1205,7 @@ var Font = (function Font() { tables.push(cmap); } - var encoding = properties.encoding; + var encoding = properties.encoding, i; if (!encoding[0]) { // the font is directly characters to glyphs with no encoding // so create an identity encoding @@ -1095,18 +1213,28 @@ var Font = (function Font() { for (i = 0; i < numGlyphs; i++) { var width = widths[i]; encoding[i] = { - unicode: i + kCmapGlyphOffset, + unicode: i <= 0x1f || (i >= 127 && i <= 255) ? + i + kCmapGlyphOffset : i, width: IsNum(width) ? width : properties.defaultWidth }; } } else { - for (var code in encoding) - encoding[code].unicode += kCmapGlyphOffset; + for (i in encoding) { + if (encoding.hasOwnProperty(i)) { + var unicode = encoding[i].unicode; + if (unicode <= 0x1f || (unicode >= 127 && unicode <= 255)) + encoding[i].unicode = unicode += kCmapGlyphOffset; + } + } } var glyphs = []; - for (var i = 1; i < numGlyphs; i++) - glyphs.push({ unicode: i + kCmapGlyphOffset }); + for (i = 1; i < numGlyphs; i++) { + glyphs.push({ + unicode: i <= 0x1f || (i >= 127 && i <= 255) ? + i + kCmapGlyphOffset : i + }); + } cmap.data = createCMapTable(glyphs); } else { replaceCMapTable(cmap, font, properties); @@ -1292,7 +1420,8 @@ var Font = (function Font() { var rule = "@font-face { font-family:'" + fontName + "';src:" + url + '}'; var styleSheet = document.styleSheets[0]; if (!styleSheet) { - document.documentElement.firstChild.appendChild( document.createElement('style') ); + document.documentElement.firstChild.appendChild( + document.createElement('style')); styleSheet = document.styleSheets[0]; } styleSheet.insertRule(rule, styleSheet.cssRules.length); @@ -1300,15 +1429,15 @@ var Font = (function Font() { return rule; }, - charsToUnicode: function fonts_chars2Unicode(chars) { + charsToGlyphs: function fonts_chars2Glyphs(chars) { var charsCache = this.charsCache; - var str; + var glyphs; // if we translated this string before, just grab it from the cache if (charsCache) { - str = charsCache[chars]; - if (str) - return str; + glyphs = charsCache[chars]; + if (glyphs) + return glyphs; } // lazily create the translation cache @@ -1319,7 +1448,8 @@ var Font = (function Font() { var encoding = this.encoding; if (!encoding) return chars; - str = ''; + + glyphs = []; if (this.composite) { // composite fonts have multi-byte strings convert the string from @@ -1330,38 +1460,39 @@ var Font = (function Font() { // loop should never end on the last byte for (var i = 0; i < length; i++) { var charcode = int16([chars.charCodeAt(i++), chars.charCodeAt(i)]); - var unicode = encoding[charcode]; - if ('undefined' == typeof(unicode)) { + var glyph = encoding[charcode]; + if ('undefined' == typeof(glyph)) { warn('Unencoded charcode ' + charcode); - unicode = charcode; - } else { - unicode = unicode.unicode; + glyph = { + unicode: charcode, + width: this.defaultWidth + }; } - str += String.fromCharCode(unicode); + glyphs.push(glyph); + // placing null after each word break charcode (ASCII SPACE) + if (charcode == 0x20) + glyphs.push(null); } } else { for (var i = 0; i < chars.length; ++i) { var charcode = chars.charCodeAt(i); - var unicode = encoding[charcode]; - if ('undefined' == typeof(unicode)) { + var glyph = encoding[charcode]; + if ('undefined' == typeof(glyph)) { warn('Unencoded charcode ' + charcode); - unicode = charcode; - } else { - unicode = unicode.unicode; - } - - // Handle surrogate pairs - if (unicode > 0xFFFF) { - str += String.fromCharCode(unicode & 0xFFFF); - unicode >>= 16; + glyph = { + unicode: charcode, + width: this.defaultWidth + }; } - str += String.fromCharCode(unicode); + glyphs.push(glyph); + if (charcode == 0x20) + glyphs.push(null); } } // Enter the translated string into the cache - return (charsCache[chars] = str); + return (charsCache[chars] = glyphs); } }; diff --git a/pdf.js b/pdf.js index 41d7d2a71..0b810a3ee 100644 --- a/pdf.js +++ b/pdf.js @@ -799,59 +799,58 @@ var PredictorStream = (function() { prevRow = currentRow; switch (predictor) { - case 0: - break; - case 1: - for (var i = 0; i < pixBytes; ++i) - currentRow[i] = rawBytes[i]; - for (; i < rowBytes; ++i) - currentRow[i] = (currentRow[i - pixBytes] + rawBytes[i]) & 0xFF; - break; - case 2: - for (var i = 0; i < rowBytes; ++i) - currentRow[i] = (prevRow[i] + rawBytes[i]) & 0xFF; - break; - case 3: - for (var i = 0; i < pixBytes; ++i) - currentRow[i] = (prevRow[i] >> 1) + rawBytes[i]; - for (; i < rowBytes; ++i) { - currentRow[i] = (((prevRow[i] + currentRow[i - pixBytes]) >> 1) + - rawBytes[i]) & 0xFF; - } - break; - case 4: - // we need to save the up left pixels values. the simplest way - // is to create a new buffer - for (var i = 0; i < pixBytes; ++i) - currentRow[i] = rawBytes[i]; - for (; i < rowBytes; ++i) { - var up = prevRow[i]; - var upLeft = prevRow[i - pixBytes]; - var left = currentRow[i - pixBytes]; - var p = left + up - upLeft; - - var pa = p - left; - if (pa < 0) - pa = -pa; - var pb = p - up; - if (pb < 0) - pb = -pb; - var pc = p - upLeft; - if (pc < 0) - pc = -pc; - - var c = rawBytes[i]; - if (pa <= pb && pa <= pc) - currentRow[i] = left + c; - else if (pb <= pc) - currentRow[i] = up + c; - else - currentRow[i] = upLeft + c; - } - break; - default: - error('Unsupported predictor: ' + predictor); - break; + case 0: + break; + case 1: + for (var i = 0; i < pixBytes; ++i) + currentRow[i] = rawBytes[i]; + for (; i < rowBytes; ++i) + currentRow[i] = (currentRow[i - pixBytes] + rawBytes[i]) & 0xFF; + break; + case 2: + for (var i = 0; i < rowBytes; ++i) + currentRow[i] = (prevRow[i] + rawBytes[i]) & 0xFF; + break; + case 3: + for (var i = 0; i < pixBytes; ++i) + currentRow[i] = (prevRow[i] >> 1) + rawBytes[i]; + for (; i < rowBytes; ++i) { + currentRow[i] = (((prevRow[i] + currentRow[i - pixBytes]) >> 1) + + rawBytes[i]) & 0xFF; + } + break; + case 4: + // we need to save the up left pixels values. the simplest way + // is to create a new buffer + for (var i = 0; i < pixBytes; ++i) + currentRow[i] = rawBytes[i]; + for (; i < rowBytes; ++i) { + var up = prevRow[i]; + var upLeft = prevRow[i - pixBytes]; + var left = currentRow[i - pixBytes]; + var p = left + up - upLeft; + + var pa = p - left; + if (pa < 0) + pa = -pa; + var pb = p - up; + if (pb < 0) + pb = -pb; + var pc = p - upLeft; + if (pc < 0) + pc = -pc; + + var c = rawBytes[i]; + if (pa <= pb && pa <= pc) + currentRow[i] = left + c; + else if (pb <= pc) + currentRow[i] = up + c; + else + currentRow[i] = upLeft + c; + } + break; + default: + error('Unsupported predictor: ' + predictor); } this.bufferLength += rowBytes; }; @@ -1695,129 +1694,128 @@ var CCITTFaxStream = (function() { while (codingLine[this.codingPos] < columns) { code1 = this.getTwoDimCode(); switch (code1) { - case twoDimPass: - this.addPixels(refLine[refPos + 1], blackPixels); - if (refLine[refPos + 1] < columns) - refPos += 2; - break; - case twoDimHoriz: - code1 = code2 = 0; - if (blackPixels) { - do { - code1 += (code3 = this.getBlackCode()); - } while (code3 >= 64); - do { - code2 += (code3 = this.getWhiteCode()); - } while (code3 >= 64); - } else { - do { - code1 += (code3 = this.getWhiteCode()); - } while (code3 >= 64); - do { - code2 += (code3 = this.getBlackCode()); - } while (code3 >= 64); - } - this.addPixels(codingLine[this.codingPos] + - code1, blackPixels); - if (codingLine[this.codingPos] < columns) { - this.addPixels(codingLine[this.codingPos] + code2, - blackPixels ^ 1); - } - while (refLine[refPos] <= codingLine[this.codingPos] && - refLine[refPos] < columns) { - refPos += 2; - } - break; - case twoDimVertR3: - this.addPixels(refLine[refPos] + 3, blackPixels); - blackPixels ^= 1; - if (codingLine[this.codingPos] < columns) { - ++refPos; - while (refLine[refPos] <= codingLine[this.codingPos] && - refLine[refPos] < columns) + case twoDimPass: + this.addPixels(refLine[refPos + 1], blackPixels); + if (refLine[refPos + 1] < columns) refPos += 2; - } - break; - case twoDimVertR2: - this.addPixels(refLine[refPos] + 2, blackPixels); - blackPixels ^= 1; - if (codingLine[this.codingPos] < columns) { - ++refPos; + break; + case twoDimHoriz: + code1 = code2 = 0; + if (blackPixels) { + do { + code1 += (code3 = this.getBlackCode()); + } while (code3 >= 64); + do { + code2 += (code3 = this.getWhiteCode()); + } while (code3 >= 64); + } else { + do { + code1 += (code3 = this.getWhiteCode()); + } while (code3 >= 64); + do { + code2 += (code3 = this.getBlackCode()); + } while (code3 >= 64); + } + this.addPixels(codingLine[this.codingPos] + + code1, blackPixels); + if (codingLine[this.codingPos] < columns) { + this.addPixels(codingLine[this.codingPos] + code2, + blackPixels ^ 1); + } while (refLine[refPos] <= codingLine[this.codingPos] && refLine[refPos] < columns) { refPos += 2; } - } - break; - case twoDimVertR1: - this.addPixels(refLine[refPos] + 1, blackPixels); - blackPixels ^= 1; - if (codingLine[this.codingPos] < columns) { - ++refPos; - while (refLine[refPos] <= codingLine[this.codingPos] && - refLine[refPos] < columns) - refPos += 2; - } - break; - case twoDimVert0: - this.addPixels(refLine[refPos], blackPixels); - blackPixels ^= 1; - if (codingLine[this.codingPos] < columns) { - ++refPos; - while (refLine[refPos] <= codingLine[this.codingPos] && - refLine[refPos] < columns) - refPos += 2; - } - break; - case twoDimVertL3: - this.addPixelsNeg(refLine[refPos] - 3, blackPixels); - blackPixels ^= 1; - if (codingLine[this.codingPos] < columns) { - if (refPos > 0) - --refPos; - else + break; + case twoDimVertR3: + this.addPixels(refLine[refPos] + 3, blackPixels); + blackPixels ^= 1; + if (codingLine[this.codingPos] < columns) { ++refPos; - while (refLine[refPos] <= codingLine[this.codingPos] && - refLine[refPos] < columns) - refPos += 2; - } - break; - case twoDimVertL2: - this.addPixelsNeg(refLine[refPos] - 2, blackPixels); - blackPixels ^= 1; - if (codingLine[this.codingPos] < columns) { - if (refPos > 0) - --refPos; - else + while (refLine[refPos] <= codingLine[this.codingPos] && + refLine[refPos] < columns) + refPos += 2; + } + break; + case twoDimVertR2: + this.addPixels(refLine[refPos] + 2, blackPixels); + blackPixels ^= 1; + if (codingLine[this.codingPos] < columns) { ++refPos; - while (refLine[refPos] <= codingLine[this.codingPos] && - refLine[refPos] < columns) - refPos += 2; - } - break; - case twoDimVertL1: - this.addPixelsNeg(refLine[refPos] - 1, blackPixels); - blackPixels ^= 1; - if (codingLine[this.codingPos] < columns) { - if (refPos > 0) - --refPos; - else + while (refLine[refPos] <= codingLine[this.codingPos] && + refLine[refPos] < columns) { + refPos += 2; + } + } + break; + case twoDimVertR1: + this.addPixels(refLine[refPos] + 1, blackPixels); + blackPixels ^= 1; + if (codingLine[this.codingPos] < columns) { ++refPos; - - while (refLine[refPos] <= codingLine[this.codingPos] && - refLine[refPos] < columns) - refPos += 2; - } - break; - case EOF: - this.addPixels(columns, 0); - this.eof = true; - break; - default: - warn('bad 2d code'); - this.addPixels(columns, 0); - this.err = true; - break; + while (refLine[refPos] <= codingLine[this.codingPos] && + refLine[refPos] < columns) + refPos += 2; + } + break; + case twoDimVert0: + this.addPixels(refLine[refPos], blackPixels); + blackPixels ^= 1; + if (codingLine[this.codingPos] < columns) { + ++refPos; + while (refLine[refPos] <= codingLine[this.codingPos] && + refLine[refPos] < columns) + refPos += 2; + } + break; + case twoDimVertL3: + this.addPixelsNeg(refLine[refPos] - 3, blackPixels); + blackPixels ^= 1; + if (codingLine[this.codingPos] < columns) { + if (refPos > 0) + --refPos; + else + ++refPos; + while (refLine[refPos] <= codingLine[this.codingPos] && + refLine[refPos] < columns) + refPos += 2; + } + break; + case twoDimVertL2: + this.addPixelsNeg(refLine[refPos] - 2, blackPixels); + blackPixels ^= 1; + if (codingLine[this.codingPos] < columns) { + if (refPos > 0) + --refPos; + else + ++refPos; + while (refLine[refPos] <= codingLine[this.codingPos] && + refLine[refPos] < columns) + refPos += 2; + } + break; + case twoDimVertL1: + this.addPixelsNeg(refLine[refPos] - 1, blackPixels); + blackPixels ^= 1; + if (codingLine[this.codingPos] < columns) { + if (refPos > 0) + --refPos; + else + ++refPos; + + while (refLine[refPos] <= codingLine[this.codingPos] && + refLine[refPos] < columns) + refPos += 2; + } + break; + case EOF: + this.addPixels(columns, 0); + this.eof = true; + break; + default: + warn('bad 2d code'); + this.addPixels(columns, 0); + this.err = true; } } } else { @@ -2488,79 +2486,77 @@ var Lexer = (function() { do { ch = stream.getChar(); switch (ch) { - case undefined: - warn('Unterminated string'); - done = true; - break; - case '(': - ++numParen; - str += ch; - break; - case ')': - if (--numParen == 0) { - done = true; - } else { - str += ch; - } - break; - case '\\': - ch = stream.getChar(); - switch (ch) { case undefined: warn('Unterminated string'); done = true; break; - case 'n': - str += '\n'; - break; - case 'r': - str += '\r'; - break; - case 't': - str += '\t'; - break; - case 'b': - str += '\b'; - break; - case 'f': - str += '\f'; - break; - case '\\': case '(': - case ')': + ++numParen; str += ch; break; - case '0': case '1': case '2': case '3': - case '4': case '5': case '6': case '7': - var x = ch - '0'; - ch = stream.lookChar(); - if (ch >= '0' && ch <= '7') { - stream.skip(); - x = (x << 3) + (ch - '0'); - ch = stream.lookChar(); - if (ch >= '0' && ch <= '7') { - stream.skip(); - x = (x << 3) + (ch - '0'); - } + case ')': + if (--numParen == 0) { + done = true; + } else { + str += ch; } - - str += String.fromCharCode(x); break; - case '\r': - ch = stream.lookChar(); - if (ch == '\n') - stream.skip(); - break; - case '\n': + case '\\': + ch = stream.getChar(); + switch (ch) { + case undefined: + warn('Unterminated string'); + done = true; + break; + case 'n': + str += '\n'; + break; + case 'r': + str += '\r'; + break; + case 't': + str += '\t'; + break; + case 'b': + str += '\b'; + break; + case 'f': + str += '\f'; + break; + case '\\': + case '(': + case ')': + str += ch; + break; + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + var x = ch - '0'; + ch = stream.lookChar(); + if (ch >= '0' && ch <= '7') { + stream.skip(); + x = (x << 3) + (ch - '0'); + ch = stream.lookChar(); + if (ch >= '0' && ch <= '7') { + stream.skip(); + x = (x << 3) + (ch - '0'); + } + } + + str += String.fromCharCode(x); + break; + case '\r': + ch = stream.lookChar(); + if (ch == '\n') + stream.skip(); + break; + case '\n': + break; + default: + str += ch; + } break; default: str += ch; - break; - } - break; - default: - str += ch; - break; } } while (!done); return str; @@ -2641,41 +2637,41 @@ var Lexer = (function() { // start reading token switch (ch) { - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': - case '+': case '-': case '.': - return this.getNumber(ch); - case '(': - return this.getString(); - case '/': - return this.getName(ch); - // array punctuation - case '[': - case ']': - return new Cmd(ch); - // hex string or dict punctuation - case '<': - ch = stream.lookChar(); - if (ch == '<') { + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + case '+': case '-': case '.': + return this.getNumber(ch); + case '(': + return this.getString(); + case '/': + return this.getName(ch); + // array punctuation + case '[': + case ']': + return new Cmd(ch); + // hex string or dict punctuation + case '<': + ch = stream.lookChar(); + if (ch == '<') { + // dict punctuation + stream.skip(); + return new Cmd('<<'); + } + return this.getHexString(ch); // dict punctuation - stream.skip(); - return new Cmd('<<'); - } - return this.getHexString(ch); - // dict punctuation - case '>': - ch = stream.lookChar(); - if (ch == '>') { - stream.skip(); - return new Cmd('>>'); - } - case '{': - case '}': - return new Cmd(ch); - // fall through - case ')': - error('Illegal character: ' + ch); - return Error; + case '>': + ch = stream.lookChar(); + if (ch == '>') { + stream.skip(); + return new Cmd('>>'); + } + case '{': + case '}': + return new Cmd(ch); + // fall through + case ')': + error('Illegal character: ' + ch); + return Error; } // command @@ -3139,17 +3135,16 @@ var XRef = (function() { entry.offset = offset; entry.gen = generation; switch (type) { - case 0: - entry.free = true; - break; - case 1: - entry.uncompressed = true; - break; - case 2: - break; - default: - error('Invalid XRef entry type: ' + type); - break; + case 0: + entry.free = true; + break; + case 1: + entry.uncompressed = true; + break; + case 2: + break; + default: + error('Invalid XRef entry type: ' + type); } if (!this.entries[first + i]) this.entries[first + i] = entry; @@ -3224,7 +3219,14 @@ var XRef = (function() { error('bad XRef entry'); } if (this.encrypt && !suppressEncryption) { - e = parser.getObj(this.encrypt.createCipherTransform(num, gen)); + try { + e = parser.getObj(this.encrypt.createCipherTransform(num, gen)); + } catch (ex) { + // almost all streams must be encrypted, but sometimes + // they are not probably due to some broken generators + // re-trying without encryption + return this.fetch(ref, true); + } } else { e = parser.getObj(); } @@ -3476,7 +3478,6 @@ var Page = (function() { break; default: TODO('other link types'); - break; } } else if (annotation.has('Dest')) { // simple destination link @@ -3706,7 +3707,9 @@ var PDFDoc = (function() { if (find(stream, 'startxref', 1024, true)) { stream.skip(9); var ch; - while (Lexer.isSpace(ch = stream.getChar())) {} + do { + ch = stream.getChar(); + } while (Lexer.isSpace(ch)); var str = ''; while ((ch - '0') <= 9) { str += ch; @@ -4294,7 +4297,6 @@ var PartialEvaluator = (function() { }; } } else if (type == 'CIDFontType0') { - encoding = xref.fetchIfRef(dict.get('Encoding')); if (IsName(encoding)) { // Encoding is a predefined CMap if (encoding.name == 'Identity-H') { @@ -4347,19 +4349,25 @@ var PartialEvaluator = (function() { baseEncoding = Encodings.StandardEncoding.slice(); break; default: - warn('Unknown type of font: ' + type); - break; + warn('Unknown type of font: ' + type); } } // merge in the differences var firstChar = properties.firstChar; var lastChar = properties.lastChar; + var widths = properties.widths || []; var glyphs = {}; for (var i = firstChar; i <= lastChar; i++) { - var glyph = differences[i] || baseEncoding[i]; + var glyph = differences[i]; + if (!glyph) { + glyph = baseEncoding[i]; + // skipping already specified by difference glyphs + if (differences.indexOf(glyph) >= 0) + continue; + } var index = GlyphsUnicode[glyph] || i; - var width = properties.widths[i] || properties.widths[glyph]; + var width = widths[i] || widths[glyph]; map[i] = { unicode: index, width: IsNum(width) ? width : properties.defaultWidth @@ -4381,7 +4389,7 @@ var PartialEvaluator = (function() { if (type == 'TrueType' && dict.has('ToUnicode') && differences) { var cmapObj = dict.get('ToUnicode'); if (IsRef(cmapObj)) { - cmapObj = xref.fetch(cmapObj, true); + cmapObj = xref.fetch(cmapObj); } if (IsName(cmapObj)) { error('ToUnicode file cmap translation not implemented'); @@ -4442,7 +4450,6 @@ var PartialEvaluator = (function() { token = parseInt(token, 10); // a number tokens.push(token); token = ''; - break; } switch (byte) { case 0x5B: @@ -4453,9 +4460,9 @@ var PartialEvaluator = (function() { // collect array items var items = [], item; while (tokens.length && - (item = tokens.pop()) != beginArrayToken) - items.unshift(item); - tokens.push(items); + (item = tokens.pop()) != beginArrayToken) + items.unshift(item); + tokens.push(items); break; } } else if (byte == 0x3E) { @@ -4530,7 +4537,7 @@ var PartialEvaluator = (function() { type: type.name, encoding: map, differences: [], - widths: widths, + widths: widths || {}, defaultWidth: defaultWidth, firstChar: 0, lastChar: 256 @@ -4921,7 +4928,7 @@ var CanvasGraphics = (function() { font = font.get(fontRef.name); font = this.xref.fetchIfRef(font); if (!font) - return; + error('Referenced font is not found'); var fontObj = font.fontObj; this.current.font = fontObj; @@ -4973,21 +4980,15 @@ var CanvasGraphics = (function() { showText: function(text) { var ctx = this.ctx; var current = this.current; - var originalText = text; + var font = current.font; ctx.save(); ctx.transform.apply(ctx, current.textMatrix); ctx.scale(1, -1); - ctx.translate(current.x, -1 * current.y); + ctx.transform.apply(ctx, font.textMatrix || IDENTITY_MATRIX); - var font = current.font; - if (font) { - ctx.transform.apply(ctx, font.textMatrix || IDENTITY_MATRIX); - text = font.charsToUnicode(text); - } - - var composite = font.composite; + var glyphs = font.charsToGlyphs(text); var fontSize = current.fontSize; var charSpacing = current.charSpacing; var wordSpacing = current.wordSpacing; @@ -4995,24 +4996,23 @@ var CanvasGraphics = (function() { ctx.scale(1 / textHScale, 1); var width = 0; - for (var i = 0; i < text.length; i++) { - if (composite) { - var position = i * 2 + 1; - var charcode = (originalText.charCodeAt(position - 1) << 8) + - originalText.charCodeAt(position); - } else { - var charcode = originalText.charCodeAt(i); + for (var i = 0; i < glyphs.length; i++) { + var glyph = glyphs[i]; + if (glyph === null) { + // word break + width += wordSpacing; + continue; } - var encoding = font.encoding[charcode]; - var charWidth = (encoding ? encoding.width : font.defaultWidth); - charWidth *= (fontSize * 0.001); + var unicode = glyph.unicode; + var char = unicode >= 0x10000 ? + String.fromCharCode(0xD800 | ((unicode - 0x10000) >> 10), + 0xDC00 | (unicode & 0x3FF)) : String.fromCharCode(unicode); + + var charWidth = glyph.width * fontSize * 0.001; charWidth += charSpacing; - if (charcode == 32) - charWidth += wordSpacing; - ctx.fillText(text.charAt(i), 0, 0); - ctx.translate(charWidth, 0); + ctx.fillText(char, width, 0); width += charWidth; } current.x += width; @@ -5399,67 +5399,67 @@ var ColorSpace = (function() { this.mode = mode; switch (mode) { - case 'DeviceGray': - case 'G': - return new DeviceGrayCS(); - case 'DeviceRGB': - case 'RGB': - return new DeviceRgbCS(); - case 'DeviceCMYK': - case 'CMYK': - return new DeviceCmykCS(); - case 'Pattern': - return new PatternCS(null); - default: - error('unrecognized colorspace ' + mode); + case 'DeviceGray': + case 'G': + return new DeviceGrayCS(); + case 'DeviceRGB': + case 'RGB': + return new DeviceRgbCS(); + case 'DeviceCMYK': + case 'CMYK': + return new DeviceCmykCS(); + case 'Pattern': + return new PatternCS(null); + default: + error('unrecognized colorspace ' + mode); } } else if (IsArray(cs)) { var mode = cs[0].name; this.mode = mode; switch (mode) { - case 'DeviceGray': - case 'G': - return new DeviceGrayCS(); - case 'DeviceRGB': - case 'RGB': - return new DeviceRgbCS(); - case 'DeviceCMYK': - case 'CMYK': - return new DeviceCmykCS(); - case 'CalGray': - return new DeviceGrayCS(); - case 'CalRGB': - return new DeviceRgbCS(); - case 'ICCBased': - var stream = xref.fetchIfRef(cs[1]); - var dict = stream.dict; - var numComps = dict.get('N'); - if (numComps == 1) + case 'DeviceGray': + case 'G': return new DeviceGrayCS(); - if (numComps == 3) + case 'DeviceRGB': + case 'RGB': return new DeviceRgbCS(); - if (numComps == 4) + case 'DeviceCMYK': + case 'CMYK': return new DeviceCmykCS(); - break; - case 'Pattern': - var baseCS = cs[1]; - if (baseCS) - baseCS = ColorSpace.parse(baseCS, xref, res); - return new PatternCS(baseCS); - case 'Indexed': - var base = ColorSpace.parse(cs[1], xref, res); - var hiVal = cs[2] + 1; - var lookup = xref.fetchIfRef(cs[3]); - return new IndexedCS(base, hiVal, lookup); - case 'Separation': - var alt = ColorSpace.parse(cs[2], xref, res); - var tintFn = new PDFFunction(xref, xref.fetchIfRef(cs[3])); - return new SeparationCS(alt, tintFn); - case 'Lab': - case 'DeviceN': - default: - error('unimplemented color space object "' + mode + '"'); + case 'CalGray': + return new DeviceGrayCS(); + case 'CalRGB': + return new DeviceRgbCS(); + case 'ICCBased': + var stream = xref.fetchIfRef(cs[1]); + var dict = stream.dict; + var numComps = dict.get('N'); + if (numComps == 1) + return new DeviceGrayCS(); + if (numComps == 3) + return new DeviceRgbCS(); + if (numComps == 4) + return new DeviceCmykCS(); + break; + case 'Pattern': + var baseCS = cs[1]; + if (baseCS) + baseCS = ColorSpace.parse(baseCS, xref, res); + return new PatternCS(baseCS); + case 'Indexed': + var base = ColorSpace.parse(cs[1], xref, res); + var hiVal = cs[2] + 1; + var lookup = xref.fetchIfRef(cs[3]); + return new IndexedCS(base, hiVal, lookup); + case 'Separation': + var alt = ColorSpace.parse(cs[2], xref, res); + var tintFn = new PDFFunction(xref, xref.fetchIfRef(cs[3])); + return new SeparationCS(alt, tintFn); + case 'Lab': + case 'DeviceN': + default: + error('unimplemented color space object "' + mode + '"'); } } else { error('unrecognized color space object: "' + cs + '"'); @@ -5742,26 +5742,26 @@ var Pattern = (function() { var typeNum = dict.get('PatternType'); switch (typeNum) { - case 1: - var base = cs.base; - var color; - if (base) { - var baseComps = base.numComps; - - color = []; - for (var i = 0; i < baseComps; ++i) - color.push(args[i]); - - color = base.getRgb(color); - } - var code = patternName.code; - return new TilingPattern(pattern, code, dict, color, xref, ctx); - case 2: - var shading = xref.fetchIfRef(dict.get('Shading')); - var matrix = dict.get('Matrix'); - return Pattern.parseShading(shading, matrix, xref, res, ctx); - default: - error('Unknown type of pattern: ' + typeNum); + case 1: + var base = cs.base; + var color; + if (base) { + var baseComps = base.numComps; + + color = []; + for (var i = 0; i < baseComps; ++i) + color.push(args[i]); + + color = base.getRgb(color); + } + var code = patternName.code; + return new TilingPattern(pattern, code, dict, color, xref, ctx); + case 2: + var shading = xref.fetchIfRef(dict.get('Shading')); + var matrix = dict.get('Matrix'); + return Pattern.parseShading(shading, matrix, xref, res, ctx); + default: + error('Unknown type of pattern: ' + typeNum); } return null; }; @@ -5773,12 +5773,12 @@ var Pattern = (function() { var type = dict.get('ShadingType'); switch (type) { - case 2: - case 3: - // both radial and axial shadings are handled by RadialAxial shading - return new RadialAxialShading(dict, matrix, xref, res, ctx); - default: - return new DummyShading(); + case 2: + case 3: + // both radial and axial shadings are handled by RadialAxial shading + return new RadialAxialShading(dict, matrix, xref, res, ctx); + default: + return new DummyShading(); } }; return constructor; @@ -5959,17 +5959,17 @@ var TilingPattern = (function() { var paintType = dict.get('PaintType'); switch (paintType) { - case PAINT_TYPE_COLORED: - tmpCtx.fillStyle = ctx.fillStyle; - tmpCtx.strokeStyle = ctx.strokeStyle; - break; - case PAINT_TYPE_UNCOLORED: - color = Util.makeCssRgb.apply(this, color); - tmpCtx.fillStyle = color; - tmpCtx.strokeStyle = color; - break; - default: - error('Unsupported paint type: ' + paintType); + case PAINT_TYPE_COLORED: + tmpCtx.fillStyle = ctx.fillStyle; + tmpCtx.strokeStyle = ctx.strokeStyle; + break; + case PAINT_TYPE_UNCOLORED: + color = Util.makeCssRgb.apply(this, color); + tmpCtx.fillStyle = color; + tmpCtx.strokeStyle = color; + break; + default: + error('Unsupported paint type: ' + paintType); } var scale = [width / xstep, height / ystep]; @@ -6062,8 +6062,8 @@ var PDFImage = (function() { this.decode = dict.get('Decode', 'D'); - var mask = xref.fetchIfRef(image.dict.get('Mask')); - var smask = xref.fetchIfRef(image.dict.get('SMask')); + var mask = xref.fetchIfRef(dict.get('Mask')); + var smask = xref.fetchIfRef(dict.get('SMask')); if (mask) { TODO('masked images'); diff --git a/test/driver.js b/test/driver.js index f3e45a53d..7d6c54509 100644 --- a/test/driver.js +++ b/test/driver.js @@ -8,6 +8,7 @@ 'use strict'; var appPath, browser, canvas, currentTaskIdx, manifest, stdout; +var inFlightRequests = 0; function queryParams() { var qs = window.location.search.substring(1); @@ -42,12 +43,12 @@ function load() { if (r.readyState == 4) { log('done\n'); manifest = JSON.parse(r.responseText); - currentTaskIdx = 0, nextTask(); + currentTaskIdx = 0; + nextTask(); } }; r.send(null); } -window.onload = load; function nextTask() { if (currentTaskIdx == manifest.length) { @@ -73,7 +74,8 @@ function nextTask() { failure = 'load PDF doc : ' + e.toString(); } - task.pageNum = 1, nextPage(task, failure); + task.pageNum = 1; + nextPage(task, failure); } }; r.send(null); @@ -89,7 +91,8 @@ function nextPage(task, loadError) { if (!task.pdfDoc) { sendTaskResult(canvas.toDataURL('image/png'), task, failure); log('done' + (failure ? ' (failed !: ' + failure + ')' : '') + '\n'); - ++currentTaskIdx, nextTask(); + ++currentTaskIdx; + nextTask(); return; } @@ -98,7 +101,8 @@ function nextPage(task, loadError) { log(' Round ' + (1 + task.round) + '\n'); task.pageNum = 1; } else { - ++currentTaskIdx, nextTask(); + ++currentTaskIdx; + nextTask(); return; } } @@ -123,7 +127,7 @@ function nextPage(task, loadError) { page.startRendering( ctx, function(e) { - snapshotCurrentPage(page, task, (!failure && e) ? + snapshotCurrentPage(task, (!failure && e) ? ('render : ' + e) : failure); } ); @@ -135,11 +139,11 @@ function nextPage(task, loadError) { if (failure) { // Skip right to snapshotting if there was a failure, since the // fonts might be in an inconsistent state. - snapshotCurrentPage(page, task, failure); + snapshotCurrentPage(task, failure); } } -function snapshotCurrentPage(page, task, failure) { +function snapshotCurrentPage(task, failure) { log('done, snapshotting... '); sendTaskResult(canvas.toDataURL('image/png'), task, failure); @@ -149,7 +153,8 @@ function snapshotCurrentPage(page, task, failure) { var backoff = (inFlightRequests > 0) ? inFlightRequests * 10 : 0; setTimeout( function() { - ++task.pageNum, nextPage(task); + ++task.pageNum; + nextPage(task); }, backoff ); @@ -182,7 +187,6 @@ function done() { } } -var inFlightRequests = 0; function sendTaskResult(snapshot, task, failure) { var result = { browser: browser, id: task.id, @@ -201,7 +205,7 @@ function sendTaskResult(snapshot, task, failure) { if (r.readyState == 4) { inFlightRequests--; } - } + }; document.getElementById('inFlightCount').innerHTML = inFlightRequests++; r.send(JSON.stringify(result)); } diff --git a/test/pdfs/.gitignore b/test/pdfs/.gitignore index 95de9fb8e..77c89ece6 100644 --- a/test/pdfs/.gitignore +++ b/test/pdfs/.gitignore @@ -1,3 +1,13 @@ -pdf.pdf +DiwanProfile.pdf +artofwar.pdf +cable.pdf +ecma262.pdf +hmm.pdf +i9.pdf intelisa.pdf openweb_tm-PRINT.pdf +pdf.pdf +pdkids.pdf +shavian.pdf +jai.pdf + diff --git a/test/pdfs/fips197.pdf.link b/test/pdfs/fips197.pdf.link new file mode 100644 index 000000000..815eb1d48 --- /dev/null +++ b/test/pdfs/fips197.pdf.link @@ -0,0 +1 @@ +http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf diff --git a/test/pdfs/wdsg_fitc.pdf.link b/test/pdfs/wdsg_fitc.pdf.link new file mode 100644 index 000000000..77d3b590d --- /dev/null +++ b/test/pdfs/wdsg_fitc.pdf.link @@ -0,0 +1 @@ +http://www.airgid.com/book/wdsg_fitc.pdf diff --git a/test/pdfs/wnv_chinese.pdf.link b/test/pdfs/wnv_chinese.pdf.link new file mode 100644 index 000000000..fbbc81760 --- /dev/null +++ b/test/pdfs/wnv_chinese.pdf.link @@ -0,0 +1 @@ +http://www.cdc.gov/ncidod/dvbid/westnile/languages/chinese.pdf diff --git a/test/resources/browser_manifests/.gitignore b/test/resources/browser_manifests/.gitignore new file mode 100644 index 000000000..ca57ac505 --- /dev/null +++ b/test/resources/browser_manifests/.gitignore @@ -0,0 +1,2 @@ +browser_manifest.json + diff --git a/test/test_manifest.json b/test/test_manifest.json index 3734ee9e4..44955eefa 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -69,6 +69,12 @@ "rounds": 1, "type": "load" }, + { "id": "wnv_chinese-pdf", + "file": "pdfs/wnv_chinese.pdf", + "link": true, + "rounds": 1, + "type": "eq" + }, { "id": "i9-pdf", "file": "pdfs/i9.pdf", "link": true, @@ -115,5 +121,17 @@ "link": true, "rounds": 1, "type": "eq" + }, + { "id": "wdsg_fitc", + "file": "pdfs/wdsg_fitc.pdf", + "link": true, + "rounds": 1, + "type": "eq" + }, + { "id": "fips197", + "file": "pdfs/fips197.pdf", + "link": true, + "rounds": 1, + "type": "load" } ] diff --git a/utils/fonts_utils.js b/utils/fonts_utils.js index e17eefa45..ff5e686b5 100644 --- a/utils/fonts_utils.js +++ b/utils/fonts_utils.js @@ -82,7 +82,7 @@ function readCharstringEncoding(aString) { } else if (value <= 31) { token = CFFEncodingMap[value]; } else if (value < 247) { - token = parseInt(value) - 139; + token = parseInt(value, 10) - 139; } else if (value < 251) { token = (value - 247) * 256 + aString[i++] + 108; } else if (value < 255) { @@ -126,7 +126,7 @@ function readFontDictData(aString, aMap) { while (!parsed) { var byte = aString[i++]; - var nibbles = [parseInt(byte / 16), parseInt(byte % 16)]; + var nibbles = [parseInt(byte / 16, 10), parseInt(byte % 16, 10)]; for (var j = 0; j < nibbles.length; j++) { var nibble = nibbles[j]; switch (nibble) { @@ -157,7 +157,7 @@ function readFontDictData(aString, aMap) { } else if (value <= 31) { token = aMap[value]; } else if (value <= 246) { - token = parseInt(value) - 139; + token = parseInt(value, 10) - 139; } else if (value <= 250) { token = (value - 247) * 256 + aString[i++] + 108; } else if (value <= 254) { @@ -206,7 +206,7 @@ function readFontIndexData(aStream, aIsByte) { } error(offsize + ' is not a valid offset size'); return null; - }; + } var offsets = []; for (var i = 0; i < count + 1; i++) @@ -249,7 +249,7 @@ var Type2Parser = function(aFilePath) { function dump(aStr) { if (debug) log(aStr); - }; + } function parseAsToken(aString, aMap) { var decoded = readFontDictData(aString, aMap); @@ -290,7 +290,7 @@ var Type2Parser = function(aFilePath) { } } } - }; + } this.parse = function(aStream) { font.set('major', aStream.getByte()); @@ -363,7 +363,7 @@ var Type2Parser = function(aFilePath) { aStream.pos = charsetEntry; var charset = readCharset(aStream, charStrings); } - } + }; }; /* diff --git a/web/compatibility.js b/web/compatibility.js old mode 100755 new mode 100644 index 63ebecb63..2301678d5 --- a/web/compatibility.js +++ b/web/compatibility.js @@ -1,6 +1,8 @@ /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ +'use strict'; + // Checking if the typed arrays are supported (function() { if (typeof Uint8Array !== 'undefined') @@ -10,8 +12,9 @@ return this.slice(start, end); } - function set_(array, offset) { - if (arguments.length < 2) offset = 0; + function set_function(array, offset) { + if (arguments.length < 2) + offset = 0; for (var i = 0, n = array.length; i < n; ++i, ++offset) this[offset] = array[i] & 0xFF; } @@ -19,15 +22,17 @@ function TypedArray(arg1) { var result; if (typeof arg1 === 'number') { - result = new Array(arg1); - for (var i = 0; i < arg1; ++i) - result[i] = 0; + result = []; + for (var i = 0; i < arg1; ++i) + result[i] = 0; } else - result = arg1.slice(0); + result = arg1.slice(0); + result.subarray = subarray; result.buffer = result; result.byteLength = result.length; - result.set = set_; + result.set = set_function; + if (typeof arg1 === 'object' && arg1.buffer) result.buffer = arg1.buffer; diff --git a/web/viewer.css b/web/viewer.css old mode 100755 new mode 100644 diff --git a/web/viewer.js b/web/viewer.js index d7c9d6b66..520cf4efa 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -98,7 +98,7 @@ var PDFView = { }, get page() { - return parseInt(document.location.hash.substring(1)) || 1; + return parseInt(document.location.hash.substring(1), 10) || 1; }, open: function(url, scale) { @@ -170,7 +170,7 @@ var PDFView = { } this.setScale(scale || kDefaultScale, true); - this.page = parseInt(document.location.hash.substring(1)) || 1; + this.page = parseInt(document.location.hash.substring(1), 10) || 1; this.pagesRefMap = pagesRefMap; this.destinations = pdf.catalog.destinations; if (pdf.catalog.documentOutline) { @@ -209,7 +209,7 @@ var PDFView = { var currentHeight = kBottomMargin; var windowTop = window.pageYOffset; - for (var i = 1; i <= pages.length; i++) { + for (var i = 1; i <= pages.length; ++i) { var page = pages[i - 1]; var pageHeight = page.height * page.scale + kBottomMargin; if (currentHeight + pageHeight > windowTop) @@ -219,10 +219,11 @@ var PDFView = { } var windowBottom = window.pageYOffset + window.innerHeight; - for (; i <= pages.length && currentHeight < windowBottom; i++) { - var page = pages[i - 1]; - visiblePages.push({ id: page.id, y: currentHeight, view: page }); - currentHeight += page.height * page.scale + kBottomMargin; + for (; i <= pages.length && currentHeight < windowBottom; ++i) { + var singlePage = pages[i - 1]; + visiblePages.push({ id: singlePage.id, y: currentHeight, + view: singlePage }); + currentHeight += singlePage.height * singlePage.scale + kBottomMargin; } return visiblePages; @@ -256,13 +257,13 @@ var PageView = function(container, content, id, width, height, div.removeAttribute('data-loaded'); }; - function setupLinks(canvas, content, scale) { + function setupLinks(content, scale) { function bindLink(link, dest) { link.onclick = function() { if (dest) PDFView.navigateTo(dest); return false; - } + }; } var links = content.getLinks(); for (var i = 0; i < links.length; i++) { @@ -283,8 +284,6 @@ var PageView = function(container, content, id, width, height, var width = 0, height = 0, widthScale, heightScale; var scale = 0; switch (dest[1].name) { - default: - return; case 'XYZ': x = dest[2]; y = dest[3]; @@ -315,6 +314,8 @@ var PageView = function(container, content, id, width, height, height / kCssUnits; scale = Math.min(widthScale, heightScale); break; + default: + return; } var boundingRect = [ @@ -369,7 +370,7 @@ var PageView = function(container, content, id, width, height, stats.begin = Date.now(); this.content.startRendering(ctx, this.updateStats); - setupLinks(canvas, this.content, this.scale); + setupLinks(this.content, this.scale); div.setAttribute('data-loaded', true); return true; diff --git a/worker/canvas.js b/worker/canvas.js index 5a9237d9a..d8b0dd338 100644 --- a/worker/canvas.js +++ b/worker/canvas.js @@ -39,7 +39,7 @@ function GradientProxy(cmdQueue, x0, y0, x1, y1) { cmdQueue.push(['$createLinearGradient', [x0, y0, x1, y1]]); this.addColorStop = function(i, rgba) { cmdQueue.push(['$addColorStop', [i, rgba]]); - } + }; } // Really simple PatternProxy. @@ -72,7 +72,7 @@ function CanvasProxy(width, height) { throw 'CanvasProxy can only provide a 2d context.'; } return ctx; - } + }; // Expose only the minimum of the canvas object - there is no dom to do // more here. @@ -127,7 +127,7 @@ function CanvasProxy(width, height) { return function() { // console.log("funcCall", name) cmdQueue.push([name, Array.prototype.slice.call(arguments)]); - } + }; } var name; for (var i = 0; i < ctxFunc.length; i++) { @@ -139,11 +139,11 @@ function CanvasProxy(width, height) { ctx.createPattern = function(object, kind) { return new PatternProxy(cmdQueue, object, kind); - } + }; ctx.createLinearGradient = function(x0, y0, x1, y1) { return new GradientProxy(cmdQueue, x0, y0, x1, y1); - } + }; ctx.getImageData = function(x, y, w, h) { return { @@ -151,11 +151,11 @@ function CanvasProxy(width, height) { height: h, data: Uint8ClampedArray(w * h * 4) }; - } + }; ctx.putImageData = function(data, x, y, width, height) { cmdQueue.push(['$putImageData', [data, x, y, width, height]]); - } + }; ctx.drawImage = function(image, x, y, width, height, sx, sy, swidth, sheight) { @@ -168,7 +168,7 @@ function CanvasProxy(width, height) { } else { throw 'unkown type to drawImage'; } - } + }; // Setup property access to `ctx`. var ctxProp = { @@ -195,14 +195,14 @@ function CanvasProxy(width, height) { function buildGetter(name) { return function() { return ctx['$' + name]; - } + }; } function buildSetter(name) { return function(value) { cmdQueue.push(['$', name, value]); - return ctx['$' + name] = value; - } + return (ctx['$' + name] = value); + }; } // Setting the value to `stroke|fillStyle` needs special handling, as it @@ -215,9 +215,9 @@ function CanvasProxy(width, height) { cmdQueue.push(['$' + name + 'Pattern', [value.id]]); } else { cmdQueue.push(['$', name, value]); - return ctx['$' + name] = value; + return (ctx['$' + name] = value); } - } + }; } for (var name in ctxProp) { diff --git a/worker/client.js b/worker/client.js index a20a4179f..a51484c64 100644 --- a/worker/client.js +++ b/worker/client.js @@ -3,23 +3,26 @@ 'use strict'; -if (typeof console.time == 'undefined') { +var consoleUtils = (function() { var consoleTimer = {}; - console.time = function(name) { + + var obj = {}; + obj.time = function(name) { consoleTimer[name] = Date.now(); }; - - console.timeEnd = function(name) { + obj.timeEnd = function(name) { var time = consoleTimer[name]; if (time == null) { throw 'Unkown timer name ' + name; } - this.log('Timer:', name, Date.now() - time); + console.log('Timer:', name, Date.now() - time); }; -} + + return obj; +})(); function FontWorker() { - this.worker = new Worker('worker/font.js'); + this.worker = new Worker('../worker/font.js'); this.fontsWaiting = 0; this.fontsWaitingCallbacks = []; @@ -58,7 +61,7 @@ FontWorker.prototype = { 'fonts': function(data) { // console.log("got processed fonts from worker", Object.keys(data)); - for (name in data) { + for (var name in data) { // Update the encoding property. var font = Fonts.lookup(name); font.properties = { @@ -96,7 +99,7 @@ FontWorker.prototype = { this.fontsWaiting++; } - console.time('ensureFonts'); + consoleUtils.time('ensureFonts'); // If there are fonts, that need to get loaded, tell the FontWorker to get // started and push the callback on the waiting-callback-stack. if (notLoaded.length != 0) { @@ -124,7 +127,7 @@ function WorkerPDFDoc(canvas) { this.ctx = canvas.getContext('2d'); this.canvas = canvas; - this.worker = new Worker('worker/pdf.js'); + this.worker = new Worker('../worker/pdf.js'); this.fontWorker = new FontWorker(); this.waitingForFonts = false; this.waitingForFontsCallback = []; @@ -167,7 +170,8 @@ function WorkerPDFDoc(canvas) { }, '$putImageData': function(imageData, x, y) { - var imgData = this.getImageData(0, 0, imageData.width, imageData.height); + var imgData = this.getImageData(0, 0, + imageData.width, imageData.height); // Store the .data property to avaid property lookups. var imageRealData = imageData.data; @@ -176,7 +180,7 @@ function WorkerPDFDoc(canvas) { // Copy over the imageData. var len = imageRealData.length; while (len--) - imgRealData[len] = imageRealData[len]; + imgRealData[len] = imageRealData[len]; this.putImageData(imgData, x, y); }, @@ -273,7 +277,7 @@ function WorkerPDFDoc(canvas) { }, 'pdf_num_pages': function(data) { - this.numPages = parseInt(data); + this.numPages = parseInt(data, 10); if (this.loadCallback) { this.loadCallback(); } @@ -302,8 +306,8 @@ function WorkerPDFDoc(canvas) { 'setup_page': function(data) { var size = data.split(','); var canvas = this.canvas, ctx = this.ctx; - canvas.width = parseInt(size[0]); - canvas.height = parseInt(size[1]); + canvas.width = parseInt(size[0], 10); + canvas.height = parseInt(size[1], 10); }, 'fonts': function(data) { @@ -339,7 +343,7 @@ function WorkerPDFDoc(canvas) { var renderData = function() { if (id == 0) { - console.time('main canvas rendering'); + consoleUtils.time('main canvas rendering'); var ctx = this.ctx; ctx.save(); ctx.fillStyle = 'rgb(255, 255, 255)'; @@ -348,8 +352,8 @@ function WorkerPDFDoc(canvas) { } renderProxyCanvas(canvasList[id], cmdQueue); if (id == 0) { - console.timeEnd('main canvas rendering'); - console.timeEnd('>>> total page display time:'); + consoleUtils.timeEnd('main canvas rendering'); + consoleUtils.timeEnd('>>> total page display time:'); } }.bind(this); @@ -368,50 +372,52 @@ function WorkerPDFDoc(canvas) { }; // Listen to the WebWorker for data and call actionHandler on it. - this.worker.onmessage = function(event) { + this.worker.addEventListener('message', function(event) { var data = event.data; if (data.action in actionHandler) { actionHandler[data.action].call(this, data.data); } else { throw 'Unkown action from worker: ' + data.action; } - }.bind(this); + }.bind(this)); } -WorkerPDFDoc.prototype.open = function(url, callback) { - var req = new XMLHttpRequest(); - req.open('GET', url); - req.mozResponseType = req.responseType = 'arraybuffer'; - req.expected = (document.URL.indexOf('file:') == 0) ? 0 : 200; - req.onreadystatechange = function() { - if (req.readyState == 4 && req.status == req.expected) { - var data = req.mozResponseArrayBuffer || req.mozResponse || - req.responseArrayBuffer || req.response; - - this.loadCallback = callback; - this.worker.postMessage(data); - this.showPage(this.numPage); - } - }.bind(this); - req.send(null); -}; +WorkerPDFDoc.prototype = { + open: function(url, callback) { + var req = new XMLHttpRequest(); + req.open('GET', url); + req.mozResponseType = req.responseType = 'arraybuffer'; + req.expected = (document.URL.indexOf('file:') == 0) ? 0 : 200; + req.onreadystatechange = function() { + if (req.readyState == 4 && req.status == req.expected) { + var data = req.mozResponseArrayBuffer || req.mozResponse || + req.responseArrayBuffer || req.response; + + this.loadCallback = callback; + this.worker.postMessage(data); + this.showPage(this.numPage); + } + }.bind(this); + req.send(null); + }, -WorkerPDFDoc.prototype.showPage = function(numPage) { - this.numPage = parseInt(numPage); - console.log('=== start rendering page ' + numPage + ' ==='); - console.time('>>> total page display time:'); - this.worker.postMessage(numPage); - if (this.onChangePage) { - this.onChangePage(numPage); - } -}; + showPage: function(numPage) { + this.numPage = parseInt(numPage, 10); + console.log('=== start rendering page ' + numPage + ' ==='); + consoleUtils.time('>>> total page display time:'); + this.worker.postMessage(numPage); + if (this.onChangePage) { + this.onChangePage(numPage); + } + }, -WorkerPDFDoc.prototype.nextPage = function() { - if (this.numPage == this.numPages) return; - this.showPage(++this.numPage); -}; + nextPage: function() { + if (this.numPage != this.numPages) + this.showPage(++this.numPage); + }, -WorkerPDFDoc.prototype.prevPage = function() { - if (this.numPage == 1) return; - this.showPage(--this.numPage); + prevPage: function() { + if (this.numPage != 1) + this.showPage(--this.numPage); + } }; diff --git a/worker/console.js b/worker/console.js index fc49583a6..43ec1af88 100644 --- a/worker/console.js +++ b/worker/console.js @@ -25,3 +25,4 @@ var console = { this.log('Timer:', name, Date.now() - time); } }; + diff --git a/worker/font.js b/worker/font.js index 549b73101..4319de7b9 100644 --- a/worker/font.js +++ b/worker/font.js @@ -56,11 +56,12 @@ var actionHandler = { }; // Listen to the MainThread for data and call actionHandler on it. -this.onmessage = function(event) { +addEventListener('message', function(event) { var data = event.data; if (data.action in actionHandler) { actionHandler[data.action].call(this, data.data); } else { throw 'Unkown action from worker: ' + data.action; } -}; +}); + diff --git a/worker/pdf.js b/worker/pdf.js index 8cb6342db..f205547ab 100644 --- a/worker/pdf.js +++ b/worker/pdf.js @@ -11,8 +11,10 @@ var console = { action: 'log', data: args }); - }, + } +}; +var consoleUtils = { time: function(name) { consoleTimer[name] = Date.now(); }, @@ -22,7 +24,7 @@ var console = { if (time == null) { throw 'Unkown timer name ' + name; } - this.log('Timer:', name, Date.now() - time); + console.log('Timer:', name, Date.now() - time); } }; @@ -42,7 +44,7 @@ var canvas = new CanvasProxy(1224, 1584); // Listen for messages from the main thread. var pdfDocument = null; -onmessage = function(event) { +addEventListener('message', function(event) { var data = event.data; // If there is no pdfDocument yet, then the sent data is the PDFDocument. if (!pdfDocument) { @@ -55,10 +57,10 @@ onmessage = function(event) { } // User requested to render a certain page. else { - console.time('compile'); + consoleUtils.time('compile'); // Let's try to render the first page... - var page = pdfDocument.getPage(parseInt(data)); + var page = pdfDocument.getPage(parseInt(data, 10)); var pdfToCssUnitsCoef = 96.0 / 72.0; var pageWidth = (page.mediaBox[2] - page.mediaBox[0]) * pdfToCssUnitsCoef; @@ -77,19 +79,19 @@ onmessage = function(event) { var fonts = []; var gfx = new CanvasGraphics(canvas.getContext('2d'), CanvasProxy); page.compile(gfx, fonts); - console.timeEnd('compile'); + consoleUtils.timeEnd('compile'); // Send fonts to the main thread. - console.time('fonts'); + consoleUtils.time('fonts'); postMessage({ action: 'fonts', data: fonts }); - console.timeEnd('fonts'); + consoleUtils.timeEnd('fonts'); - console.time('display'); + consoleUtils.time('display'); page.display(gfx); canvas.flush(); - console.timeEnd('display'); + consoleUtils.timeEnd('display'); } -}; +});