diff --git a/src/api.js b/src/api.js index 4e7ce4e76..6c862a91b 100644 --- a/src/api.js +++ b/src/api.js @@ -311,13 +311,19 @@ var PDFPageProxy = (function PDFPageProxyClosure() { ensureFonts: function PDFPageProxy_ensureFonts(fonts, callback) { this.stats.time('Font Loading'); // Convert the font names to the corresponding font obj. + var fontObjs = []; for (var i = 0, ii = fonts.length; i < ii; i++) { - fonts[i] = this.objs.objs[fonts[i]].data; + var obj = this.objs.objs[fonts[i]].data; + if (obj.error) { + warn('Error during font loading: ' + obj.error); + continue; + } + fontObjs.push(obj); } // Load all the fonts FontLoader.bind( - fonts, + fontObjs, function pageEnsureFontsFontObjs(fontObjs) { this.stats.timeEnd('Font Loading'); @@ -565,7 +571,12 @@ var WorkerTransport = (function WorkerTransportClosure() { // At this point, only the font object is created but the font is // not yet attached to the DOM. This is done in `FontLoader.bind`. - var font = new Font(name, file, properties); + var font; + try { + font = new Font(name, file, properties); + } catch (e) { + font = new ErrorFont(e); + } this.objs.resolve(id, font); break; default: diff --git a/src/fonts.js b/src/fonts.js index 4aef0962a..4af42265d 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -1577,13 +1577,19 @@ var Font = (function FontClosure() { return; } + // Some fonts might use wrong font types for Type1C or CIDFontType0C + var subtype = properties.subtype; + if (subtype == 'Type1C' && (type != 'Type1' && type != 'MMType1')) + type = 'Type1'; + if (subtype == 'CIDFontType0C' && type != 'CIDFontType0') + type = 'CIDFontType0'; + var data; switch (type) { case 'Type1': case 'CIDFontType0': this.mimetype = 'font/opentype'; - var subtype = properties.subtype; var cff = (subtype == 'Type1C' || subtype == 'CIDFontType0C') ? new CFFFont(file, properties) : new Type1Font(name, file, properties); @@ -2938,6 +2944,7 @@ var Font = (function FontClosure() { } this.toFontChar = toFontChar; } + var unitsPerEm = properties.unitsPerEm || 1000; // defaulting to 1000 var fields = { // PostScript Font Program @@ -2958,7 +2965,7 @@ var Font = (function FontClosure() { '\x00\x00\x00\x00' + // checksumAdjustement '\x5F\x0F\x3C\xF5' + // magicNumber '\x00\x00' + // Flags - '\x03\xE8' + // unitsPerEM (defaulting to 1000) + safeString16(unitsPerEm) + // unitsPerEM '\x00\x00\x00\x00\x9e\x0b\x7e\x27' + // creation date '\x00\x00\x00\x00\x9e\x0b\x7e\x27' + // modifification date '\x00\x00' + // xMin @@ -3309,6 +3316,20 @@ var Font = (function FontClosure() { return Font; })(); +var ErrorFont = (function ErrorFontClosure() { + function ErrorFont(error) { + this.error = error; + } + + ErrorFont.prototype = { + charsToGlyphs: function ErrorFont_charsToGlyphs() { + return []; + } + }; + + return ErrorFont; +})(); + var CallothersubrCmd = (function CallothersubrCmdClosure() { function CallothersubrCmd(index) { this.index = index; @@ -4413,6 +4434,19 @@ var CFFParser = (function CFFParserClosure() { var charStringOffset = topDict.getByName('CharStrings'); cff.charStrings = this.parseCharStrings(charStringOffset); + var fontMatrix = topDict.getByName('FontMatrix'); + if (fontMatrix) { + // estimating unitsPerEM for the font + properties.unitsPerEm = 1 / fontMatrix[0]; + } + + var fontBBox = topDict.getByName('FontBBox'); + if (fontBBox) { + // adjusting ascent/descent + properties.ascent = fontBBox[3]; + properties.descent = fontBBox[1]; + } + var charset, encoding; if (cff.isCIDFont) { var fdArrayIndex = this.parseIndex(topDict.getByName('FDArray')).obj; @@ -4486,7 +4520,7 @@ var CFFParser = (function CFFParserClosure() { return parseFloatOperand(pos); } else if (value === 28) { value = dict[pos++]; - value = (value << 8) | dict[pos++]; + value = ((value << 24) | (dict[pos++] << 16)) >> 16; return value; } else if (value === 29) { value = dict[pos++]; diff --git a/src/image.js b/src/image.js index 59f70f99a..19c66ade2 100644 --- a/src/image.js +++ b/src/image.js @@ -97,7 +97,12 @@ var PDFImage = (function PDFImageClosure() { if (smask) { this.smask = new PDFImage(xref, res, smask, false); } else if (mask) { - this.mask = new PDFImage(xref, res, mask, false); + if (isStream(mask)) { + this.mask = new PDFImage(xref, res, mask, false); + } else { + // Color key mask (just an array). + this.mask = mask; + } } } /** @@ -129,11 +134,15 @@ var PDFImage = (function PDFImageClosure() { maskPromise.resolve(null); } else { smaskPromise.resolve(null); - if (mask && isStream(mask)) { - handleImageData(handler, xref, res, mask, maskPromise); - } else if (mask) { - TODO('handle color key masking'); - maskPromise.resolve(null); + if (mask) { + if (isStream(mask)) { + handleImageData(handler, xref, res, mask, maskPromise); + } else if (isArray(mask)) { + maskPromise.resolve(mask); + } else { + warn('Unsupported mask format.'); + maskPromise.resolve(null); + } } else { maskPromise.resolve(null); } @@ -279,7 +288,7 @@ var PDFImage = (function PDFImageClosure() { } return output; }, - getOpacity: function PDFImage_getOpacity(width, height) { + getOpacity: function PDFImage_getOpacity(width, height, image) { var smask = this.smask; var mask = this.mask; var originalWidth = this.width; @@ -294,18 +303,40 @@ var PDFImage = (function PDFImageClosure() { if (sw != width || sh != height) buf = PDFImage.resize(buf, smask.bpc, 1, sw, sh, width, height); } else if (mask) { - var sw = mask.width; - var sh = mask.height; - buf = new Uint8Array(sw * sh); - mask.numComps = 1; - mask.fillGrayBuffer(buf); + if (mask instanceof PDFImage) { + var sw = mask.width; + var sh = mask.height; + buf = new Uint8Array(sw * sh); + mask.numComps = 1; + mask.fillGrayBuffer(buf); - // Need to invert values in buffer - for (var i = 0, ii = sw * sh; i < ii; ++i) - buf[i] = 255 - buf[i]; + // Need to invert values in buffer + for (var i = 0, ii = sw * sh; i < ii; ++i) + buf[i] = 255 - buf[i]; - if (sw != width || sh != height) - buf = PDFImage.resize(buf, mask.bpc, 1, sw, sh, width, height); + if (sw != width || sh != height) + buf = PDFImage.resize(buf, mask.bpc, 1, sw, sh, width, height); + } else if (isArray(mask)) { + // Color key mask: if any of the compontents are outside the range + // then they should be painted. + buf = new Uint8Array(width * height); + var numComps = this.numComps; + for (var i = 0, ii = width * height; i < ii; ++i) { + var opacity = 0; + var imageOffset = i * numComps; + for (var j = 0; j < numComps; ++j) { + var color = image[imageOffset + j]; + var maskOffset = j * 2; + if (color < mask[maskOffset] || color > mask[maskOffset + 1]) { + opacity = 255; + break; + } + } + buf[i] = opacity; + } + } else { + error('Unknown mask format.'); + } } else { buf = new Uint8Array(width * height); for (var i = 0, ii = width * height; i < ii; ++i) @@ -357,7 +388,7 @@ var PDFImage = (function PDFImageClosure() { comps = PDFImage.resize(comps, this.bpc, 3, originalWidth, originalHeight, width, height); var compsPos = 0; - var opacity = this.getOpacity(width, height); + var opacity = this.getOpacity(width, height, imgArray); var opacityPos = 0; var length = width * actualHeight * 4; diff --git a/test/pdfs/colorkeymask.pdf b/test/pdfs/colorkeymask.pdf new file mode 100644 index 000000000..a056d9cbc Binary files /dev/null and b/test/pdfs/colorkeymask.pdf differ diff --git a/test/pdfs/issue1810.pdf.link b/test/pdfs/issue1810.pdf.link index b8689c298..8c84c7505 100644 --- a/test/pdfs/issue1810.pdf.link +++ b/test/pdfs/issue1810.pdf.link @@ -1 +1 @@ -http://www.freepatentsonline.com/pdf/documents/uspt/D661/USD661296/USD661296S1.pdf +http://assets.sbnation.com/assets/1167519/USD661296S1.pdf diff --git a/test/test_manifest.json b/test/test_manifest.json index a80c7bb4f..d19633947 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -673,5 +673,11 @@ "md5": "2ac7c68e26a8ef797aead15e4875cc6d", "rounds": 1, "type": "load" + }, + { "id": "colorkeymask", + "file": "pdfs/colorkeymask.pdf", + "md5": "9f11e815b485f7f0e1fa5c116c636cf9", + "rounds": 1, + "type": "eq" } ] diff --git a/test/unit/font_spec.js b/test/unit/font_spec.js index 9f0969324..b2436778d 100644 --- a/test/unit/font_spec.js +++ b/test/unit/font_spec.js @@ -42,7 +42,7 @@ describe('font', function() { } describe('CFFParser', function() { - var parser = new CFFParser(fontData); + var parser = new CFFParser(fontData, {}); var cff = parser.parse(); it('parses header', function() {