From faf8b8ac0bdec7b22755ba139e7f82d98a583ca9 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Sat, 10 Sep 2011 20:21:20 -0500 Subject: [PATCH 01/28] Refactoring charsToUnicode into charsToGlyphs --- fonts.js | 24 +++++++----------------- pdf.js | 29 ++++++++++++++++------------- 2 files changed, 23 insertions(+), 30 deletions(-) diff --git a/fonts.js b/fonts.js index cc353c03b..ac070ed18 100755 --- a/fonts.js +++ b/fonts.js @@ -1296,7 +1296,7 @@ var Font = (function Font() { return rule; }, - charsToUnicode: function fonts_chars2Unicode(chars) { + charsToGlyphs: function fonts_chars2Glyphs(chars) { var charsCache = this.charsCache; var str; @@ -1315,7 +1315,7 @@ var Font = (function Font() { var encoding = this.encoding; if (!encoding) return chars; - str = ''; + var glyphs = []; if (this.composite) { // composite fonts have multi-byte strings convert the string from @@ -1329,11 +1329,9 @@ var Font = (function Font() { var unicode = encoding[charcode]; if ('undefined' == typeof(unicode)) { warn('Unencoded charcode ' + charcode); - unicode = charcode; - } else { - unicode = unicode.unicode; + unicode = { unicode: charcode }; } - str += String.fromCharCode(unicode); + glyphs.push(unicode); } } else { @@ -1342,22 +1340,14 @@ var Font = (function Font() { var unicode = encoding[charcode]; if ('undefined' == typeof(unicode)) { warn('Unencoded charcode ' + charcode); - unicode = charcode; - } else { - unicode = unicode.unicode; + unicode = { unicode: charcode }; } - - // Handle surrogate pairs - if (unicode > 0xFFFF) { - str += String.fromCharCode(unicode & 0xFFFF); - unicode >>= 16; - } - str += String.fromCharCode(unicode); + glyphs.push(unicode); } } // Enter the translated string into the cache - return charsCache[chars] = str; + return charsCache[chars] = glyphs; } }; diff --git a/pdf.js b/pdf.js index 3e8249524..17ef1f3ca 100644 --- a/pdf.js +++ b/pdf.js @@ -4954,7 +4954,6 @@ var CanvasGraphics = (function() { showText: function(text) { var ctx = this.ctx; var current = this.current; - var originalText = text; ctx.save(); ctx.transform.apply(ctx, current.textMatrix); @@ -4963,9 +4962,15 @@ var CanvasGraphics = (function() { ctx.translate(current.x, -1 * current.y); var font = current.font; + var glyphs = []; if (font) { ctx.transform.apply(ctx, font.textMatrix || IDENTITY_MATRIX); - text = font.charsToUnicode(text); + glyphs = font.charsToGlyphs(text); + } else { + // fallback to simple glyphs + glyphs = []; + for (var i = 0; i < text.length; ++i) + glyphs.push({unicode: text.charCodeAt(i)}); } var composite = font.composite; @@ -4977,21 +4982,19 @@ 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]; + var unicode = glyph.unicode; + var char = unicode >= 0x10000 ? + String.fromCharCode(0xD800 | ((unicode - 0x10000) >> 10), + 0xDC00 | (unicode & 0x3FF)) : String.fromCharCode(unicode); - var charWidth = font.encoding[charcode].width * fontSize * 0.001; + var charWidth = glyph.width * fontSize * 0.001; charWidth += charSpacing; - if (charcode == 32) + if (unicode == 32) charWidth += wordSpacing; - ctx.fillText(text.charAt(i), 0, 0); + ctx.fillText(char, 0, 0); ctx.translate(charWidth, 0); width += charWidth; } From eaa5b8dab38f28de738bd8d8b8165d92bd6d5a44 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Sun, 11 Sep 2011 13:32:08 -0500 Subject: [PATCH 02/28] Pruning unreachable code; misc font fixes --- pdf.js | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/pdf.js b/pdf.js index 17ef1f3ca..193f8e13e 100644 --- a/pdf.js +++ b/pdf.js @@ -4339,11 +4339,12 @@ var PartialEvaluator = (function() { // 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 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 @@ -4501,7 +4502,8 @@ var PartialEvaluator = (function() { } } - var defaultWidth = 0; + // TODO implement default widths for standard fonts metrics + var defaultWidth = 1000; var widths = Metrics[stdFontMap[baseFontName] || baseFontName]; if (IsNum(widths)) { defaultWidth = widths; @@ -4902,7 +4904,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; @@ -4954,27 +4956,16 @@ var CanvasGraphics = (function() { showText: function(text) { var ctx = this.ctx; var current = this.current; + 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; - var glyphs = []; - if (font) { - ctx.transform.apply(ctx, font.textMatrix || IDENTITY_MATRIX); - glyphs = font.charsToGlyphs(text); - } else { - // fallback to simple glyphs - glyphs = []; - for (var i = 0; i < text.length; ++i) - glyphs.push({unicode: text.charCodeAt(i)}); - } - - var composite = font.composite; - var encoding = font.encoding; + var glyphs = font.charsToGlyphs(text); + var defaultCharWidth = font.defaultWidth; var fontSize = current.fontSize; var charSpacing = current.charSpacing; var wordSpacing = current.wordSpacing; @@ -4989,7 +4980,7 @@ var CanvasGraphics = (function() { String.fromCharCode(0xD800 | ((unicode - 0x10000) >> 10), 0xDC00 | (unicode & 0x3FF)) : String.fromCharCode(unicode); - var charWidth = glyph.width * fontSize * 0.001; + var charWidth = (glyph.width || defaultCharWidth) * fontSize * 0.001; charWidth += charSpacing; if (unicode == 32) charWidth += wordSpacing; From b5b0357dca98233981c00523943f95388084d5b2 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Sun, 11 Sep 2011 13:37:13 -0500 Subject: [PATCH 03/28] Remove extra space --- pdf.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pdf.js b/pdf.js index 193f8e13e..ed1c09ab3 100644 --- a/pdf.js +++ b/pdf.js @@ -4502,7 +4502,7 @@ var PartialEvaluator = (function() { } } - // TODO implement default widths for standard fonts metrics + // TODO implement default widths for standard fonts metrics var defaultWidth = 1000; var widths = Metrics[stdFontMap[baseFontName] || baseFontName]; if (IsNum(widths)) { From e512000877759a429c36bd7159ca895e6fca03bb Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Wed, 14 Sep 2011 21:29:32 -0500 Subject: [PATCH 04/28] Fixing properties.widths is null issue; fips197 test file --- pdf.js | 2 +- test/pdfs/fips197.pdf.link | 1 + test/test_manifest.json | 6 ++++++ 3 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 test/pdfs/fips197.pdf.link diff --git a/pdf.js b/pdf.js index 7514f4d96..640cd637d 100644 --- a/pdf.js +++ b/pdf.js @@ -4506,7 +4506,7 @@ var PartialEvaluator = (function() { type: type.name, encoding: map, differences: [], - widths: widths, + widths: widths || {}, defaultWidth: defaultWidth, firstChar: 0, lastChar: 256 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/test_manifest.json b/test/test_manifest.json index 1155195cb..7893faa18 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -109,5 +109,11 @@ "link": true, "rounds": 1, "type": "eq" + }, + { "id": "fips197", + "file": "pdfs/fips197.pdf", + "link": true, + "rounds": 1, + "type": "load" } ] From 1a79f3fb43a8ba8a072df546993bf3f1733dfa3b Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Thu, 15 Sep 2011 09:33:19 -0700 Subject: [PATCH 05/28] Changed exec flags of non-exec files (chmod -x) --- fonts.js | 0 web/compatibility.js | 0 web/viewer.css | 0 3 files changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 fonts.js mode change 100755 => 100644 web/compatibility.js mode change 100755 => 100644 web/viewer.css diff --git a/fonts.js b/fonts.js old mode 100755 new mode 100644 diff --git a/web/compatibility.js b/web/compatibility.js old mode 100755 new mode 100644 diff --git a/web/viewer.css b/web/viewer.css old mode 100755 new mode 100644 From 07254bb0a5459df1a9f7d451bbb4be73d00083f0 Mon Sep 17 00:00:00 2001 From: Kalervo Kujala Date: Thu, 15 Sep 2011 23:32:44 +0300 Subject: [PATCH 06/28] Fix small lint warnings. --- pdf.js | 4 ++-- test/driver.js | 25 +++++++++++++++---------- utils/cffStandardStrings.js | 2 +- utils/fonts_utils.js | 14 +++++++------- web/compatibility.js | 19 ++++++++++++------- web/viewer.js | 25 +++++++++++++------------ worker/canvas.js | 26 +++++++++++++------------- worker/client.js | 21 +++++++++++---------- worker/console.js | 1 + worker/font.js | 1 + worker/pdf.js | 2 +- 11 files changed, 77 insertions(+), 63 deletions(-) diff --git a/pdf.js b/pdf.js index 37afdc376..599e50af8 100644 --- a/pdf.js +++ b/pdf.js @@ -6061,8 +6061,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..14f1280a6 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,7 +43,8 @@ function load() { if (r.readyState == 4) { log('done\n'); manifest = JSON.parse(r.responseText); - currentTaskIdx = 0, nextTask(); + currentTaskIdx = 0; + nextTask(); } }; r.send(null); @@ -73,7 +75,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 +92,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 +102,8 @@ function nextPage(task, loadError) { log(' Round ' + (1 + task.round) + '\n'); task.pageNum = 1; } else { - ++currentTaskIdx, nextTask(); + ++currentTaskIdx; + nextTask(); return; } } @@ -123,7 +128,7 @@ function nextPage(task, loadError) { page.startRendering( ctx, function(e) { - snapshotCurrentPage(page, task, (!failure && e) ? + snapshotCurrentPage(task, (!failure && e) ? ('render : ' + e) : failure); } ); @@ -135,11 +140,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 +154,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 +188,6 @@ function done() { } } -var inFlightRequests = 0; function sendTaskResult(snapshot, task, failure) { var result = { browser: browser, id: task.id, @@ -201,7 +206,7 @@ function sendTaskResult(snapshot, task, failure) { if (r.readyState == 4) { inFlightRequests--; } - } + }; document.getElementById('inFlightCount').innerHTML = inFlightRequests++; r.send(JSON.stringify(result)); } diff --git a/utils/cffStandardStrings.js b/utils/cffStandardStrings.js index 326f298b0..8a6570551 100644 --- a/utils/cffStandardStrings.js +++ b/utils/cffStandardStrings.js @@ -560,7 +560,7 @@ var CFFDictDataMap = { '18': { name: 'ExpansionFactor' }, - '9': { + '19': { name: 'initialRandomSeed' }, '20': { diff --git a/utils/fonts_utils.js b/utils/fonts_utils.js index 4826a09a1..a51469c53 100644 --- a/utils/fonts_utils.js +++ b/utils/fonts_utils.js @@ -69,7 +69,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) { @@ -113,7 +113,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) { @@ -144,7 +144,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) { @@ -193,7 +193,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++) @@ -236,7 +236,7 @@ var Type2Parser = function(aFilePath) { function dump(aStr) { if (debug) log(aStr); - }; + } function parseAsToken(aString, aMap) { var decoded = readFontDictData(aString, aMap); @@ -277,7 +277,7 @@ var Type2Parser = function(aFilePath) { } } } - }; + } this.parse = function(aStream) { font.set('major', aStream.getByte()); @@ -353,7 +353,7 @@ var Type2Parser = function(aFilePath) { aStream.pos = charsetEntry; var charset = readCharset(aStream, charStrings); } - } + }; }; /* diff --git a/web/compatibility.js b/web/compatibility.js index 63ebecb63..2301678d5 100755 --- 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.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..36a0ae03a 100644 --- a/worker/client.js +++ b/worker/client.js @@ -58,7 +58,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 = { @@ -176,7 +176,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 +273,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 +302,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) { @@ -397,7 +397,7 @@ WorkerPDFDoc.prototype.open = function(url, callback) { }; WorkerPDFDoc.prototype.showPage = function(numPage) { - this.numPage = parseInt(numPage); + this.numPage = parseInt(numPage, 10); console.log('=== start rendering page ' + numPage + ' ==='); console.time('>>> total page display time:'); this.worker.postMessage(numPage); @@ -407,11 +407,12 @@ WorkerPDFDoc.prototype.showPage = function(numPage) { }; WorkerPDFDoc.prototype.nextPage = function() { - if (this.numPage == this.numPages) return; - this.showPage(++this.numPage); + if (this.numPage != this.numPages) + this.showPage(++this.numPage); }; WorkerPDFDoc.prototype.prevPage = function() { - if (this.numPage == 1) return; - this.showPage(--this.numPage); + 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..44a4782f0 100644 --- a/worker/font.js +++ b/worker/font.js @@ -64,3 +64,4 @@ this.onmessage = function(event) { throw 'Unkown action from worker: ' + data.action; } }; + diff --git a/worker/pdf.js b/worker/pdf.js index 8cb6342db..7a72ac824 100644 --- a/worker/pdf.js +++ b/worker/pdf.js @@ -58,7 +58,7 @@ onmessage = function(event) { console.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; From 10a2fa66c2ae6e75680e6624721401e50f82c727 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Thu, 15 Sep 2011 19:26:32 -0500 Subject: [PATCH 07/28] Fixing defaultWidth; variables names --- fonts.js | 33 ++++++++++++++++++++------------- pdf.js | 15 ++++++++------- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/fonts.js b/fonts.js index 404c3f853..2b40c1f49 100755 --- a/fonts.js +++ b/fonts.js @@ -472,6 +472,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; @@ -1298,13 +1299,13 @@ var Font = (function Font() { 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 @@ -1315,7 +1316,8 @@ var Font = (function Font() { var encoding = this.encoding; if (!encoding) return chars; - var glyphs = []; + + glyphs = []; if (this.composite) { // composite fonts have multi-byte strings convert the string from @@ -1326,23 +1328,28 @@ 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 = { unicode: charcode }; + glyph = { unicode: charcode }; } - glyphs.push(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 = { unicode: charcode }; + glyph = { unicode: charcode }; } - glyphs.push(unicode); + glyphs.push(glyph); + if (charcode == 0x20) + glyphs.push(null); } } diff --git a/pdf.js b/pdf.js index b4fd9c7a2..a8ae55234 100644 --- a/pdf.js +++ b/pdf.js @@ -4294,7 +4294,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') { @@ -4521,8 +4520,7 @@ var PartialEvaluator = (function() { } } - // TODO implement default widths for standard fonts metrics - var defaultWidth = 1000; + var defaultWidth = 0; var widths = Metrics[stdFontMap[baseFontName] || baseFontName]; if (IsNum(widths)) { defaultWidth = widths; @@ -4994,6 +4992,12 @@ var CanvasGraphics = (function() { var width = 0; for (var i = 0; i < glyphs.length; i++) { var glyph = glyphs[i]; + if (glyph === null) { + // word break + width += wordSpacing; + continue; + } + var unicode = glyph.unicode; var char = unicode >= 0x10000 ? String.fromCharCode(0xD800 | ((unicode - 0x10000) >> 10), @@ -5001,11 +5005,8 @@ var CanvasGraphics = (function() { var charWidth = (glyph.width || defaultCharWidth) * fontSize * 0.001; charWidth += charSpacing; - if (unicode == 32) - charWidth += wordSpacing; - ctx.fillText(char, 0, 0); - ctx.translate(charWidth, 0); + ctx.fillText(char, width, 0); width += charWidth; } current.x += width; From a0ef97fb6044c6028b7ca4b63a4cd884d9181933 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Thu, 15 Sep 2011 19:32:36 -0500 Subject: [PATCH 08/28] Transfer defaultWidth for files without the file --- fonts.js | 1 + 1 file changed, 1 insertion(+) diff --git a/fonts.js b/fonts.js index 2b40c1f49..e8d629084 100755 --- a/fonts.js +++ b/fonts.js @@ -436,6 +436,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; From 6e290349120160b39cfaf565fc061d00958cdd13 Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Wed, 14 Sep 2011 17:34:29 -0700 Subject: [PATCH 09/28] Hello World example Perhaps the simplest use of pdf.js. See README.md in the new folder --- helloworld/README.md | 13 ++++++++ helloworld/hello.js | 49 ++++++++++++++++++++++++++++ helloworld/helloworld.pdf | 68 +++++++++++++++++++++++++++++++++++++++ helloworld/index.html | 18 +++++++++++ 4 files changed, 148 insertions(+) create mode 100644 helloworld/README.md create mode 100644 helloworld/hello.js create mode 100644 helloworld/helloworld.pdf create mode 100644 helloworld/index.html diff --git a/helloworld/README.md b/helloworld/README.md new file mode 100644 index 000000000..70d5e760b --- /dev/null +++ b/helloworld/README.md @@ -0,0 +1,13 @@ +## "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/helloworld/hello.js b/helloworld/hello.js new file mode 100644 index 000000000..0bf46311a --- /dev/null +++ b/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 binaryGet = 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 +// +binaryGet('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/helloworld/helloworld.pdf b/helloworld/helloworld.pdf new file mode 100644 index 000000000..d98b4e1db --- /dev/null +++ b/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/helloworld/index.html b/helloworld/index.html new file mode 100644 index 000000000..68a465f52 --- /dev/null +++ b/helloworld/index.html @@ -0,0 +1,18 @@ + + + + + + + + + + + + + + + + + + From 3476364886a922180006b1b2ac5abf7d4afa3d00 Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Thu, 15 Sep 2011 09:28:58 -0700 Subject: [PATCH 10/28] Moved helloworld/ to examples/helloworld/ --- {helloworld => examples/helloworld}/README.md | 0 {helloworld => examples/helloworld}/hello.js | 0 .../helloworld}/helloworld.pdf | 0 examples/helloworld/index.html | 18 ++++++++++++++++++ helloworld/index.html | 18 ------------------ 5 files changed, 18 insertions(+), 18 deletions(-) rename {helloworld => examples/helloworld}/README.md (100%) rename {helloworld => examples/helloworld}/hello.js (100%) rename {helloworld => examples/helloworld}/helloworld.pdf (100%) create mode 100644 examples/helloworld/index.html delete mode 100644 helloworld/index.html diff --git a/helloworld/README.md b/examples/helloworld/README.md similarity index 100% rename from helloworld/README.md rename to examples/helloworld/README.md diff --git a/helloworld/hello.js b/examples/helloworld/hello.js similarity index 100% rename from helloworld/hello.js rename to examples/helloworld/hello.js diff --git a/helloworld/helloworld.pdf b/examples/helloworld/helloworld.pdf similarity index 100% rename from helloworld/helloworld.pdf rename to examples/helloworld/helloworld.pdf 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/helloworld/index.html b/helloworld/index.html deleted file mode 100644 index 68a465f52..000000000 --- a/helloworld/index.html +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - From bc91b98fc8f4d17a5d7c2224d2e93b91fee50385 Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Thu, 15 Sep 2011 11:14:55 -0700 Subject: [PATCH 11/28] Addressing comments by Vivien --- examples/helloworld/README.md | 7 +++++-- examples/helloworld/hello.js | 6 +++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/examples/helloworld/README.md b/examples/helloworld/README.md index 70d5e760b..8395733f3 100644 --- a/examples/helloworld/README.md +++ b/examples/helloworld/README.md @@ -1,11 +1,14 @@ ## "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. +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`. +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 diff --git a/examples/helloworld/hello.js b/examples/helloworld/hello.js index 0bf46311a..21799c33a 100644 --- a/examples/helloworld/hello.js +++ b/examples/helloworld/hello.js @@ -4,10 +4,10 @@ // -// Ajax GET request for binary files +// Ajax GET request, for binary files // (like jQuery's $.get(), but supports the binary type ArrayBuffer) // -var binaryGet = function(url, callback){ +var ajaxGet = function(url, callback){ var xhr = new XMLHttpRequest(); xhr.open('GET', url); xhr.mozResponseType = xhr.responseType = 'arraybuffer'; @@ -26,7 +26,7 @@ var binaryGet = function(url, callback){ // // This is where the fun happens // -binaryGet('helloworld.pdf', function(data){ +ajaxGet('helloworld.pdf', function(data){ // // Instantiate PDFDoc with PDF data // From f3745f7c139d4d97deccbd9c5262a1b686d4c947 Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Thu, 15 Sep 2011 16:28:38 -0700 Subject: [PATCH 12/28] New README, .gitignores; Makefile Makefile: commented out unnecessary closure download and shell testing (at least until someone decides to work on this). --- Makefile | 72 ++++++++--------- README.md | 85 ++++++++++++++++++--- test/pdfs/.gitignore | 12 ++- test/resources/browser_manifests/.gitignore | 2 + 4 files changed, 123 insertions(+), 48 deletions(-) create mode 100644 test/resources/browser_manifests/.gitignore 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..fe6d296a9 100644 --- a/README.md +++ b/README.md @@ -1,17 +1,80 @@ # 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 + +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. + +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 +82,14 @@ 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 -and talk to us on IRC: +Talk to us on IRC: #pdfjs on irc.mozilla.org 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/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 + From e2b1cf1baf5c17a957b4591787036889b48ec824 Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Thu, 15 Sep 2011 16:39:18 -0700 Subject: [PATCH 13/28] Minor --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index fe6d296a9..252934cb7 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,8 @@ successful. ## Getting started +**Online demo** + For an online demo, visit: http://andreasgal.github.com/pdf.js/web/viewer.html @@ -25,6 +27,8 @@ For an online demo, visit: 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/ From cac74d7cb3d1c04bd43bd4e60a24e85e46357db9 Mon Sep 17 00:00:00 2001 From: Adil Allawi Date: Thu, 15 Sep 2011 23:23:22 +0100 Subject: [PATCH 14/28] Fix OS/2 table to match font yMax and yMin from head table --- fonts.js | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/fonts.js b/fonts.js index 7d51e2c4b..064702de2 100644 --- a/fonts.js +++ b/fonts.js @@ -833,9 +833,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, @@ -1008,7 +1010,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; var tables = []; for (var i = 0; i < numTables; i++) { var table = readTableEntry(font); @@ -1022,6 +1024,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 { @@ -1048,6 +1052,13 @@ var Font = (function Font() { createOpenTypeHeader(header.version, ttf, numTables); if (requiredTables.indexOf('OS/2') != -1) { + if (typeof(head) != 'undefined') { + var ymax = int16([head.data[42],head.data[43]]); + var ymin = int16([head.data[38],head.data[39]]) - 0x10000; //always negative + properties.ascent = ymax; + properties.descent = ymin; + } + tables.push({ tag: 'OS/2', data: stringToArray(createOS2Table(properties)) From 870de2f7f3ed286603bbf1f19213d996839d2280 Mon Sep 17 00:00:00 2001 From: Adil Allawi Date: Fri, 16 Sep 2011 13:50:11 +0100 Subject: [PATCH 15/28] Add opentype ascender, descender and units-per-em to font properties Prefer font ascender, descender when creating OS/2 table scale PDF ascender and descender in OS/2 table by font units per em if it exists Fix the truetype font header to prevent Windows rejecting a Mac truetype font --- fonts.js | 48 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/fonts.js b/fonts.js index 064702de2..fd7a23bd3 100644 --- a/fonts.js +++ b/fonts.js @@ -12,6 +12,9 @@ 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; @@ -534,6 +537,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; @@ -694,6 +701,24 @@ var Font = (function Font() { } } + var openTypeUnitsPerEm = (typeof (properties.openTypeUnitsPerEm) == 'undefined') ? kPDFGlyphSpaceUnits : properties.openTypeUnitsPerEm; + var typoAscent = properties.ascent; + var typoDescent = properties.descent; + var winAscent = typoAscent; + var winDescent = -typoDescent; + + // if the font already has ascent and descent information then use these values + if (typeof (properties.openTypeAscent) != 'undefined') { + typoAscent = properties.openTypeAscent; + typoDescent = properties.openTypeDescent; + winAscent = properties.openTypeYMax; + winDescent = -properties.openTypeYMin; + } else if (openTypeUnitsPerEm != kPDFGlyphSpaceUnits) { + // if the font units differ to the PDF glyph space units then scale up the values + typoAscent = Math.round(typoAscent * openTypeUnitsPerEm / kPDFGlyphSpaceUnits); + typoDescent = Math.round(typoDescent * openTypeUnitsPerEm / kPDFGlyphSpaceUnits); + } + return '\x00\x03' + // version '\x02\x24' + // xAvgCharWidth '\x01\xF4' + // usWeightClass @@ -722,11 +747,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 @@ -1047,18 +1072,19 @@ var Font = (function Font() { virtualOffset: numTables * (4 * 4) }; + //extract some more font properties from the OpenType head and hhea tables + properties.openTypeUnitsPerEm = int16([head.data[18], head.data[19]]); + properties.openTypeYMax = int16([head.data[42], head.data[43]]); + properties.openTypeYMin = int16([head.data[38], head.data[39]]) - 0x10000; //always negative + properties.openTypeAscent = int16([hhea.data[4], hhea.data[5]]); + properties.openTypeDescent = int16([hhea.data[6], hhea.data[7]]) - 0x10000; //always negative + + // The new numbers of tables will be the last one plus the num // of missing tables createOpenTypeHeader(header.version, ttf, numTables); if (requiredTables.indexOf('OS/2') != -1) { - if (typeof(head) != 'undefined') { - var ymax = int16([head.data[42],head.data[43]]); - var ymin = int16([head.data[38],head.data[39]]) - 0x10000; //always negative - properties.ascent = ymax; - properties.descent = ymin; - } - tables.push({ tag: 'OS/2', data: stringToArray(createOS2Table(properties)) From 530d78e0f854505b814d3dcfe10fca82c3ba659b Mon Sep 17 00:00:00 2001 From: Adil Allawi Date: Fri, 16 Sep 2011 23:55:06 +0100 Subject: [PATCH 16/28] Added myself to the license (yay!) Tweaked according to comments in pull request #482 --- LICENSE | 1 + fonts.js | 48 ++++++++++++++++++++++++------------------------ 2 files changed, 25 insertions(+), 24 deletions(-) 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/fonts.js b/fonts.js index fd7a23bd3..4f95d16f0 100644 --- a/fonts.js +++ b/fonts.js @@ -670,7 +670,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; @@ -701,22 +703,19 @@ var Font = (function Font() { } } - var openTypeUnitsPerEm = (typeof (properties.openTypeUnitsPerEm) == 'undefined') ? kPDFGlyphSpaceUnits : properties.openTypeUnitsPerEm; - var typoAscent = properties.ascent; - var typoDescent = properties.descent; - var winAscent = typoAscent; - var winDescent = -typoDescent; + 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 the font already has ascent and descent information then use these values - if (typeof (properties.openTypeAscent) != 'undefined') { - typoAscent = properties.openTypeAscent; - typoDescent = properties.openTypeDescent; - winAscent = properties.openTypeYMax; - winDescent = -properties.openTypeYMin; - } else if (openTypeUnitsPerEm != kPDFGlyphSpaceUnits) { + // 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 * openTypeUnitsPerEm / kPDFGlyphSpaceUnits); - typoDescent = Math.round(typoDescent * openTypeUnitsPerEm / kPDFGlyphSpaceUnits); + typoAscent = Math.round(typoAscent * unitsPerEm / kPDFGlyphSpaceUnits); + typoDescent = Math.round(typoDescent * unitsPerEm / kPDFGlyphSpaceUnits); + winAscent = typoAscent; + winDescent = -typoDescent; } return '\x00\x03' + // version @@ -1072,22 +1071,23 @@ var Font = (function Font() { virtualOffset: numTables * (4 * 4) }; - //extract some more font properties from the OpenType head and hhea tables - properties.openTypeUnitsPerEm = int16([head.data[18], head.data[19]]); - properties.openTypeYMax = int16([head.data[42], head.data[43]]); - properties.openTypeYMin = int16([head.data[38], head.data[39]]) - 0x10000; //always negative - properties.openTypeAscent = int16([hhea.data[4], hhea.data[5]]); - properties.openTypeDescent = int16([hhea.data[6], hhea.data[7]]) - 0x10000; //always negative - - // The new numbers of tables will be the last one plus the num // of missing tables createOpenTypeHeader(header.version, ttf, numTables); if (requiredTables.indexOf('OS/2') != -1) { + //extract some more font properties from the OpenType head and hhea tables + 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, //always negative + ascent: int16([hhea.data[4], hhea.data[5]]), + descent: int16([hhea.data[6], hhea.data[7]]) - 0x10000 //always negative + } + tables.push({ tag: 'OS/2', - data: stringToArray(createOS2Table(properties)) + data: stringToArray(createOS2Table(properties, override)) }); } From 559bd070bf8b8644b4cfe0860fc2622b6908602a Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Fri, 16 Sep 2011 19:53:52 -0500 Subject: [PATCH 17/28] Fixing lint long lines warnings --- fonts.js | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/fonts.js b/fonts.js index 4f95d16f0..52fc2fb0a 100644 --- a/fonts.js +++ b/fonts.js @@ -12,7 +12,8 @@ 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 +// 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 @@ -709,9 +710,12 @@ var Font = (function Font() { 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 + // 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; @@ -1076,14 +1080,15 @@ 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 + // 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, //always negative + 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 //always negative - } + descent: int16([hhea.data[6], hhea.data[7]]) - 0x10000 + }; tables.push({ tag: 'OS/2', @@ -1329,7 +1334,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); From fc574f646a5b11627626732aabf1514806dad1af Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Fri, 16 Sep 2011 22:49:43 -0500 Subject: [PATCH 18/28] Fixing double metrics issue for glyphs (#484) --- pdf.js | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/pdf.js b/pdf.js index 599e50af8..0a1fae18b 100644 --- a/pdf.js +++ b/pdf.js @@ -4357,7 +4357,13 @@ var PartialEvaluator = (function() { var lastChar = properties.lastChar; 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]; map[i] = { From 1d00b89285e44d55c1bedcf0fcc5df941e463b81 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Sat, 17 Sep 2011 10:32:42 -0500 Subject: [PATCH 19/28] Modifying how the artofwar encryption handled; add wdsg_fitc as reftest --- pdf.js | 11 +++++++++-- test/pdfs/wdsg_fitc.pdf.link | 1 + test/test_manifest.json | 6 ++++++ 3 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 test/pdfs/wdsg_fitc.pdf.link diff --git a/pdf.js b/pdf.js index 599e50af8..6a79cdf21 100644 --- a/pdf.js +++ b/pdf.js @@ -3224,7 +3224,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 to 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(); } @@ -4381,7 +4388,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'); 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/test_manifest.json b/test/test_manifest.json index 3734ee9e4..926e37c18 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -115,5 +115,11 @@ "link": true, "rounds": 1, "type": "eq" + }, + { "id": "wdsg_fitc", + "file": "pdfs/wdsg_fitc.pdf", + "link": true, + "rounds": 1, + "type": "eq" } ] From 1f047495b1650f1317c54d54e05ae88a0a13630d Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Sat, 17 Sep 2011 17:13:22 -0500 Subject: [PATCH 20/28] Fixing large cmap-s; reduce changes in the indent encoding --- fonts.js | 77 +++++++++++++++++++++++----------- test/pdfs/wnv_chinese.pdf.link | 1 + test/test_manifest.json | 6 +++ 3 files changed, 60 insertions(+), 24 deletions(-) create mode 100644 test/pdfs/wnv_chinese.pdf.link diff --git a/fonts.js b/fonts.js index 4f95d16f0..f9462ccbb 100644 --- a/fonts.js +++ b/fonts.js @@ -594,19 +594,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; @@ -635,22 +640,39 @@ 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); + 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); + } } - for (var i = 0; i < glyphs.length; i++) - glyphsIds += string16(deltas ? deltas[i] : i + 1); - endCount += '\xFF\xFF'; startCount += '\xFF\xFF'; idDeltas += '\x00\x01'; @@ -1124,7 +1146,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 @@ -1132,18 +1154,25 @@ 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 = 0; i <= 0x1f; i++) + encoding[i].unicode += kCmapGlyphOffset; + for (i = 127; i <= 255; i++) + encoding[i].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); 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/test_manifest.json b/test/test_manifest.json index 926e37c18..66d26c4e9 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, From 391ec1f99a02f37af7e7a2777e22e27e5303f84f Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Sun, 18 Sep 2011 09:11:31 -0500 Subject: [PATCH 21/28] Moving defaultWidth into the charsToGlyphs function --- fonts.js | 10 ++++++++-- pdf.js | 3 +-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/fonts.js b/fonts.js index 3beac011a..ce4215663 100644 --- a/fonts.js +++ b/fonts.js @@ -1373,7 +1373,10 @@ var Font = (function Font() { var glyph = encoding[charcode]; if ('undefined' == typeof(glyph)) { warn('Unencoded charcode ' + charcode); - glyph = { unicode: charcode }; + glyph = { + unicode: charcode, + width: this.defaultWidth + }; } glyphs.push(glyph); // placing null after each word break charcode (ASCII SPACE) @@ -1387,7 +1390,10 @@ var Font = (function Font() { var glyph = encoding[charcode]; if ('undefined' == typeof(glyph)) { warn('Unencoded charcode ' + charcode); - glyph = { unicode: charcode }; + glyph = { + unicode: charcode, + width: this.defaultWidth + }; } glyphs.push(glyph); if (charcode == 0x20) diff --git a/pdf.js b/pdf.js index 975792141..5c3102b85 100644 --- a/pdf.js +++ b/pdf.js @@ -4989,7 +4989,6 @@ var CanvasGraphics = (function() { ctx.transform.apply(ctx, font.textMatrix || IDENTITY_MATRIX); var glyphs = font.charsToGlyphs(text); - var defaultCharWidth = font.defaultWidth; var fontSize = current.fontSize; var charSpacing = current.charSpacing; var wordSpacing = current.wordSpacing; @@ -5010,7 +5009,7 @@ var CanvasGraphics = (function() { String.fromCharCode(0xD800 | ((unicode - 0x10000) >> 10), 0xDC00 | (unicode & 0x3FF)) : String.fromCharCode(unicode); - var charWidth = (glyph.width || defaultCharWidth) * fontSize * 0.001; + var charWidth = glyph.width * fontSize * 0.001; charWidth += charSpacing; ctx.fillText(char, width, 0); From 1c5b1cbc3449c6f10a37d888c7f2de29779680ee Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Sun, 18 Sep 2011 12:08:52 -0500 Subject: [PATCH 22/28] Fixing the CID to GID mapping for cid fonts (regression of arial_unicode_XX_cidfont.pdf) --- fonts.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/fonts.js b/fonts.js index 874048532..6c70f36be 100644 --- a/fonts.js +++ b/fonts.js @@ -1165,10 +1165,13 @@ var Font = (function Font() { }; } } else { - for (i = 0; i <= 0x1f; i++) - encoding[i].unicode += kCmapGlyphOffset; - for (i = 127; i <= 255; i++) - encoding[i].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 = []; From 7036bcd4c74025080ee09f22d4db18ace33bab61 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Sun, 18 Sep 2011 14:44:57 -0500 Subject: [PATCH 23/28] Zero gjslint warnings mark --- pdf.js | 4 +- test/driver.js | 2 +- worker/client.js | 101 +++++++++++++++++++++++++---------------------- worker/font.js | 4 +- worker/pdf.js | 22 ++++++----- 5 files changed, 70 insertions(+), 63 deletions(-) diff --git a/pdf.js b/pdf.js index 1ee56e7ed..6ce70215f 100644 --- a/pdf.js +++ b/pdf.js @@ -3226,8 +3226,8 @@ var XRef = (function() { if (this.encrypt && !suppressEncryption) { try { e = parser.getObj(this.encrypt.createCipherTransform(num, gen)); - } catch(ex) { - // almost all streams must to encrypted, but sometimes + } 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); diff --git a/test/driver.js b/test/driver.js index 14f1280a6..92beaf78f 100644 --- a/test/driver.js +++ b/test/driver.js @@ -49,7 +49,7 @@ function load() { }; r.send(null); } -window.onload = load; +documet.addEventListener('DOMContentLoaded', load); function nextTask() { if (currentTaskIdx == manifest.length) { diff --git a/worker/client.js b/worker/client.js index 36a0ae03a..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 = []; @@ -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; @@ -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,51 +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; +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); + this.loadCallback = callback; + this.worker.postMessage(data); + this.showPage(this.numPage); + } + }.bind(this); + req.send(null); + }, + + 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); } - }.bind(this); - req.send(null); -}; + }, -WorkerPDFDoc.prototype.showPage = function(numPage) { - this.numPage = parseInt(numPage, 10); - console.log('=== start rendering page ' + numPage + ' ==='); - console.time('>>> total page display time:'); - this.worker.postMessage(numPage); - if (this.onChangePage) { - this.onChangePage(numPage); + nextPage: function() { + if (this.numPage != this.numPages) + this.showPage(++this.numPage); + }, + + prevPage: function() { + if (this.numPage != 1) + this.showPage(--this.numPage); } }; - -WorkerPDFDoc.prototype.nextPage = function() { - if (this.numPage != this.numPages) - this.showPage(++this.numPage); -}; - -WorkerPDFDoc.prototype.prevPage = function() { - if (this.numPage != 1) - this.showPage(--this.numPage); -}; - diff --git a/worker/font.js b/worker/font.js index 44a4782f0..4319de7b9 100644 --- a/worker/font.js +++ b/worker/font.js @@ -56,12 +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 7a72ac824..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,7 +57,7 @@ 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, 10)); @@ -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'); } -}; +}); From f401a64eb2ef0485bec074b67d74d3a60fdcfc9c Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Sun, 18 Sep 2011 21:25:05 -0500 Subject: [PATCH 24/28] Sanitize 'loca' font table: some fonts have first glyph with length 10 --- fonts.js | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 53 insertions(+), 1 deletion(-) diff --git a/fonts.js b/fonts.js index 874048532..b17549091 100644 --- a/fonts.js +++ b/fonts.js @@ -1053,6 +1053,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] << 8) | data[offset + 1]; + }; + itemEncode = function(data, offset, value) { + data[offset] = (value >> 8) & 0xFF; + data[offset + 1] = value & 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']; @@ -1060,7 +1103,7 @@ var Font = (function Font() { var header = readOpenTypeHeader(font); var numTables = header.numTables; - var cmap, maxp, hhea, hmtx, vhea, vmtx, head; + var cmap, maxp, hhea, hmtx, vhea, vmtx, head, loca, glyf; var tables = []; for (var i = 0; i < numTables; i++) { var table = readTableEntry(font); @@ -1083,6 +1126,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); } @@ -1127,6 +1174,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) { From ebb9d7dd2c18bc059fb6331e236581da42b3c785 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Sun, 18 Sep 2011 22:44:25 -0500 Subject: [PATCH 25/28] Fix for 16-bit 'loca' table --- fonts.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fonts.js b/fonts.js index b17549091..b1c164df7 100644 --- a/fonts.js +++ b/fonts.js @@ -1071,11 +1071,11 @@ var Font = (function Font() { } else { itemSize = 2; itemDecode = function(data, offset) { - return (data[offset] << 8) | data[offset + 1]; + return (data[offset] << 9) | (data[offset + 1] << 1); }; itemEncode = function(data, offset, value) { - data[offset] = (value >> 8) & 0xFF; - data[offset + 1] = value & 0xFF; + data[offset] = (value >> 9) & 0xFF; + data[offset + 1] = (value >> 1) & 0xFF; }; } var locaData = loca.data; From 999487444d9c511ed7322591870d720b1e459b15 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Mon, 19 Sep 2011 07:06:20 -0500 Subject: [PATCH 26/28] removing onload from driver.js --- test/driver.js | 1 - 1 file changed, 1 deletion(-) diff --git a/test/driver.js b/test/driver.js index 92beaf78f..7d6c54509 100644 --- a/test/driver.js +++ b/test/driver.js @@ -49,7 +49,6 @@ function load() { }; r.send(null); } -documet.addEventListener('DOMContentLoaded', load); function nextTask() { if (currentTaskIdx == manifest.length) { From 230fc48ccd75b2144cb4561dadc315ef67c44871 Mon Sep 17 00:00:00 2001 From: Julian Viereck Date: Mon, 19 Sep 2011 21:11:14 +0300 Subject: [PATCH 27/28] Add links to subscribe to mailing list. --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 252934cb7..c6cf92ede 100644 --- a/README.md +++ b/README.md @@ -90,9 +90,14 @@ 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 Talk to us on IRC: From 74cddad4338b3c7499e5b3ed71218a9816529a7b Mon Sep 17 00:00:00 2001 From: Kalervo Kujala Date: Mon, 19 Sep 2011 23:17:58 +0300 Subject: [PATCH 28/28] Align switch cases properly. --- pdf.js | 726 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 360 insertions(+), 366 deletions(-) diff --git a/pdf.js b/pdf.js index e3e5a9799..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; + 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 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; + 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 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; - break; + 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; } + 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 ')': + 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': + 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; @@ -3483,7 +3478,6 @@ var Page = (function() { break; default: TODO('other link types'); - break; } } else if (annotation.has('Dest')) { // simple destination link @@ -3713,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; @@ -4353,8 +4349,7 @@ var PartialEvaluator = (function() { baseEncoding = Encodings.StandardEncoding.slice(); break; default: - warn('Unknown type of font: ' + type); - break; + warn('Unknown type of font: ' + type); } } @@ -4455,7 +4450,6 @@ var PartialEvaluator = (function() { token = parseInt(token, 10); // a number tokens.push(token); token = ''; - break; } switch (byte) { case 0x5B: @@ -4466,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) { @@ -5405,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 + '"'); @@ -5748,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; + 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 = []; + 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); + 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; }; @@ -5779,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; @@ -5965,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];