diff --git a/src/display/api.js b/src/display/api.js index dc256148c857d2..3bbb21ba09f89b 100644 --- a/src/display/api.js +++ b/src/display/api.js @@ -717,6 +717,8 @@ var PDFDocumentProxy = (function PDFDocumentProxyClosure() { * @property {Object} canvasFactory - (optional) The factory that will be used * when creating canvases. The default value is * {DOMCanvasFactory}. + * @property {string} backgroundColor - (optional) Background color to use for + * the canvas. The default value is 'rgb(255,255,255)'. */ /** @@ -2128,7 +2130,12 @@ var InternalRenderTask = (function InternalRenderTaskClosure() { this.objs, this.canvasFactory, params.imageLayer); - this.gfx.beginDrawing(params.transform, params.viewport, transparency); + this.gfx.beginDrawing({ + transform: params.transform, + viewport: params.viewport, + transparency, + backgroundColor: params.backgroundColor, + }); this.operatorListIdx = 0; this.graphicsReady = true; if (this.graphicsReadyCallback) { diff --git a/src/display/canvas.js b/src/display/canvas.js index 800df9eaae6a1a..904d36688f6921 100644 --- a/src/display/canvas.js +++ b/src/display/canvas.js @@ -705,8 +705,9 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { CanvasGraphics.prototype = { - beginDrawing: function CanvasGraphics_beginDrawing(transform, viewport, - transparency) { + beginDrawing: function CanvasGraphics_beginDrawing({ + transform, viewport, transparency, backgroundColor = null, + }) { // For pdfs that use blend modes we have to clear the canvas else certain // blend modes can look wrong since we'd be blending with a white // backdrop. The problem with a transparent backdrop though is we then @@ -716,7 +717,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { var height = this.ctx.canvas.height; this.ctx.save(); - this.ctx.fillStyle = 'rgb(255, 255, 255)'; + this.ctx.fillStyle = backgroundColor || 'rgb(255, 255, 255)'; this.ctx.fillRect(0, 0, width, height); this.ctx.restore(); diff --git a/test/pdfs/.gitignore b/test/pdfs/.gitignore index 60a986a9e03902..64926f486fb06c 100644 --- a/test/pdfs/.gitignore +++ b/test/pdfs/.gitignore @@ -280,3 +280,4 @@ !issue7878.pdf !font_ascent_descent.pdf !issue8097_reduced.pdf +!transparent.pdf diff --git a/test/pdfs/transparent.pdf b/test/pdfs/transparent.pdf new file mode 100644 index 00000000000000..f8e5d7aeb4e0de Binary files /dev/null and b/test/pdfs/transparent.pdf differ diff --git a/test/unit/api_spec.js b/test/unit/api_spec.js index 9cbfe02d0e1c9b..bc7d6adcd4fd80 100644 --- a/test/unit/api_spec.js +++ b/test/unit/api_spec.js @@ -13,6 +13,9 @@ * limitations under the License. */ +import { + buildGetDocumentParams, NodeFileReaderFactory, TEST_PDFS_PATH +} from './test_utils'; import { createPromiseCapability, FontType, InvalidPDFException, isNodeJS, MissingPDFException, PasswordException, PasswordResponses, StreamType, @@ -24,29 +27,8 @@ import { import { getDocument, PDFDocumentProxy, PDFPageProxy } from '../../src/display/api'; -import { NodeFileReaderFactory } from './test_utils'; import { PDFJS } from '../../src/display/global'; -const TEST_PDFS_PATH = { - dom: '../pdfs/', - node: './test/pdfs/', -}; - -function buildGetDocumentParams(filename, options) { - let params = Object.create(null); - if (isNodeJS()) { - params.data = NodeFileReaderFactory.fetch({ - path: TEST_PDFS_PATH.node + filename, - }); - } else { - params.url = new URL(TEST_PDFS_PATH.dom + filename, window.location).href; - } - for (let option in options) { - params[option] = options[option]; - } - return params; -} - describe('api', function() { let basicApiFileName = 'basicapi.pdf'; let basicApiFileLength = 105779; // bytes diff --git a/test/unit/custom_spec.js b/test/unit/custom_spec.js new file mode 100644 index 00000000000000..d6eaa231a77678 --- /dev/null +++ b/test/unit/custom_spec.js @@ -0,0 +1,111 @@ +/* Copyright 2017 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { buildGetDocumentParams } from './test_utils'; +import { DOMCanvasFactory } from '../../src/display/dom_utils'; +import { getDocument } from '../../src/display/api'; +import { isNodeJS } from '../../src/shared/util'; +import { PDFJS } from '../../src/display/global'; + +function getTopLeftPixel(canvasContext) { + let imgData = canvasContext.getImageData(0, 0, 1, 1); + return { + r: imgData.data[0], + g: imgData.data[1], + b: imgData.data[2], + a: imgData.data[3], + }; +} + +describe('custom canvas rendering', function() { + let transparentGetDocumentParams = buildGetDocumentParams('transparent.pdf'); + + let CanvasFactory; + let loadingTask; + let page; + + beforeAll(function(done) { + if (isNodeJS()) { + PDFJS.pdfjsNext = true; + // NOTE: To support running the canvas-related tests in Node.js, + // a `NodeCanvasFactory` would need to be added (in test_utils.js). + } else { + CanvasFactory = new DOMCanvasFactory(); + } + loadingTask = getDocument(transparentGetDocumentParams); + loadingTask.promise.then(function(doc) { + return doc.getPage(1); + }).then(function(data) { + page = data; + done(); + }).catch(function (reason) { + done.fail(reason); + }); + }); + + afterAll(function(done) { + CanvasFactory = null; + page = null; + loadingTask.destroy().then(done); + done(); + }); + + it('renders to canvas with a default white background', function(done) { + if (isNodeJS()) { + pending('TODO: Support Canvas testing in Node.js.'); + } + var viewport = page.getViewport(1); + var canvasAndCtx = CanvasFactory.create(viewport.width, viewport.height); + + page.render({ + canvasContext: canvasAndCtx.context, + viewport, + }).then(function() { + var { r, g, b, a } = getTopLeftPixel(canvasAndCtx.context); + CanvasFactory.destroy(canvasAndCtx); + expect(r).toEqual(255); + expect(g).toEqual(255); + expect(b).toEqual(255); + expect(a).toEqual(255); + done(); + }).catch(function (reason) { + done(reason); + }); + }); + + it('renders to canvas with a custom background', function(done) { + if (isNodeJS()) { + pending('TODO: Support Canvas testing in Node.js.'); + } + var viewport = page.getViewport(1); + var canvasAndCtx = CanvasFactory.create(viewport.width, viewport.height); + + page.render({ + canvasContext: canvasAndCtx.context, + viewport, + backgroundColor: 'rgba(255,0,0,1.0)' + }).then(function() { + var { r, g, b, a } = getTopLeftPixel(canvasAndCtx.context); + CanvasFactory.destroy(canvasAndCtx); + expect(r).toEqual(255); + expect(g).toEqual(0); + expect(b).toEqual(0); + expect(a).toEqual(255); + done(); + }).catch(function (reason) { + done(reason); + }); + }); +}); diff --git a/test/unit/jasmine-boot.js b/test/unit/jasmine-boot.js index 83e4b51a70b0c6..d96521b2eb91f5 100644 --- a/test/unit/jasmine-boot.js +++ b/test/unit/jasmine-boot.js @@ -52,7 +52,8 @@ function initializePDFJS(callback) { 'pdfjs-test/unit/network_spec', 'pdfjs-test/unit/parser_spec', 'pdfjs-test/unit/primitives_spec', 'pdfjs-test/unit/stream_spec', 'pdfjs-test/unit/type1_parser_spec', 'pdfjs-test/unit/ui_utils_spec', - 'pdfjs-test/unit/unicode_spec', 'pdfjs-test/unit/util_spec' + 'pdfjs-test/unit/unicode_spec', 'pdfjs-test/unit/util_spec', + 'pdfjs-test/unit/custom_spec' ].map(function (moduleName) { return SystemJS.import(moduleName); })).then(function (modules) { diff --git a/test/unit/test_utils.js b/test/unit/test_utils.js index 5754a45726eaf9..6104dc7892d156 100644 --- a/test/unit/test_utils.js +++ b/test/unit/test_utils.js @@ -13,7 +13,7 @@ * limitations under the License. */ -import { CMapCompressionType } from '../../src/shared/util'; +import { CMapCompressionType, isNodeJS } from '../../src/shared/util'; class NodeFileReaderFactory { static fetch(params) { @@ -23,6 +23,26 @@ class NodeFileReaderFactory { } } +const TEST_PDFS_PATH = { + dom: '../pdfs/', + node: './test/pdfs/', +}; + +function buildGetDocumentParams(filename, options) { + let params = Object.create(null); + if (isNodeJS()) { + params.data = NodeFileReaderFactory.fetch({ + path: TEST_PDFS_PATH.node + filename, + }); + } else { + params.url = new URL(TEST_PDFS_PATH.dom + filename, window.location).href; + } + for (let option in options) { + params[option] = options[option]; + } + return params; +} + class NodeCMapReaderFactory { constructor({ baseUrl = null, isCompressed = false, }) { this.baseUrl = baseUrl; @@ -57,4 +77,6 @@ class NodeCMapReaderFactory { export { NodeFileReaderFactory, NodeCMapReaderFactory, + buildGetDocumentParams, + TEST_PDFS_PATH, };