From 8d5d97264e1d00788b6dfe2030e79ec8c7f1495e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E5=B7=B4=E9=87=8C=E5=88=87=E7=BD=97?=
 <ydfzgyj@baliqieluo.com>
Date: Mon, 8 May 2017 11:32:44 +0800
Subject: [PATCH] fix(svg) adjust strategy for decoding JPEG images

---
 examples/node/domstubs.js | 18 ++++++++++++++++++
 examples/node/pdf2svg.js  |  5 +++--
 src/core/evaluator.js     |  9 +++++----
 src/core/worker.js        |  2 +-
 src/display/api.js        | 34 ++++++++++++++++++++++++++++------
 src/display/svg.js        |  4 ++--
 src/main_loader.js        |  1 +
 src/pdf.js                |  1 +
 src/shared/util.js        |  7 +++++++
 9 files changed, 66 insertions(+), 15 deletions(-)

diff --git a/examples/node/domstubs.js b/examples/node/domstubs.js
index b654d6070..ce4dc7890 100644
--- a/examples/node/domstubs.js
+++ b/examples/node/domstubs.js
@@ -147,3 +147,21 @@ global.document = {
     return [];
   }
 };
+
+function Image () {
+  this._src = null;
+  this.onload = null;
+}
+Image.prototype = {
+  get src () {
+    return this._src;
+  },
+  set src (value) {
+    this._src = value;
+    if (this.onload) {
+      this.onload();
+    }
+  }
+}
+
+global.Image = Image;
diff --git a/examples/node/pdf2svg.js b/examples/node/pdf2svg.js
index 5f09209d7..b9ce96b6f 100644
--- a/examples/node/pdf2svg.js
+++ b/examples/node/pdf2svg.js
@@ -39,14 +39,15 @@ function writeToFile(svgdump, pageNum) {
 function getFileNameFromPath(path) {
   var index = path.lastIndexOf('/');
   var extIndex = path.lastIndexOf('.');
-  return path.substring(index , extIndex);
+  return path.substring(index, extIndex);
 }
 
 // Will be using promises to load document, pages and misc data instead of
 // callback.
 pdfjsLib.getDocument({
   data: data,
-  disableNativeImageDecoder: true,
+  // Try to export JPEG images directly if they don't need any further processing.
+  nativeImageDecoderSupport: pdfjsLib.NativeImageDecoding.DISPLAY
 }).then(function (doc) {
   var numPages = doc.numPages;
   console.log('# Document Loaded');
diff --git a/src/core/evaluator.js b/src/core/evaluator.js
index f7014e509..262122f8a 100644
--- a/src/core/evaluator.js
+++ b/src/core/evaluator.js
@@ -52,6 +52,7 @@ var IDENTITY_MATRIX = sharedUtil.IDENTITY_MATRIX;
 var UNSUPPORTED_FEATURES = sharedUtil.UNSUPPORTED_FEATURES;
 var ImageKind = sharedUtil.ImageKind;
 var OPS = sharedUtil.OPS;
+var NativeImageDecoding = sharedUtil.NativeImageDecoding;
 var TextRenderingMode = sharedUtil.TextRenderingMode;
 var CMapCompressionType = sharedUtil.CMapCompressionType;
 var Util = sharedUtil.Util;
@@ -113,7 +114,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
     forceDataSchema: false,
     maxImageSize: -1,
     disableFontFace: false,
-    disableNativeImageDecoder: false,
+    nativeImageDecoderSupport: NativeImageDecoding.DECODE,
     ignoreErrors: false,
   };
 
@@ -461,14 +462,14 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
         return;
       }
 
-      var useNativeImageDecoder = !this.options.disableNativeImageDecoder;
+      var nativeImageDecoderSupport = this.options.nativeImageDecoderSupport;
       // If there is no imageMask, create the PDFImage and a lot
       // of image processing can be done here.
       var objId = 'img_' + this.idFactory.createObjId();
       operatorList.addDependency(objId);
       args = [objId, w, h];
 
-      if (useNativeImageDecoder &&
+      if (nativeImageDecoderSupport !== NativeImageDecoding.NONE &&
           !softMask && !mask && image instanceof JpegStream &&
           NativeImageDecoder.isSupported(image, this.xref, resources)) {
         // These JPEGs don't need any more processing so we can just send it.
@@ -481,7 +482,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
 
       // Creates native image decoder only if a JPEG image or mask is present.
       var nativeImageDecoder = null;
-      if (useNativeImageDecoder &&
+      if (nativeImageDecoderSupport === NativeImageDecoding.DECODE &&
           (image instanceof JpegStream || mask instanceof JpegStream ||
            softMask instanceof JpegStream)) {
         nativeImageDecoder = new NativeImageDecoder(this.xref, resources,
diff --git a/src/core/worker.js b/src/core/worker.js
index f52cdefbd..5d7f3c583 100644
--- a/src/core/worker.js
+++ b/src/core/worker.js
@@ -731,7 +731,7 @@ var WorkerMessageHandler = {
         forceDataSchema: data.disableCreateObjectURL,
         maxImageSize: data.maxImageSize === undefined ? -1 : data.maxImageSize,
         disableFontFace: data.disableFontFace,
-        disableNativeImageDecoder: data.disableNativeImageDecoder,
+        nativeImageDecoderSupport: data.nativeImageDecoderSupport,
         ignoreErrors: data.ignoreErrors,
       };
 
diff --git a/src/display/api.js b/src/display/api.js
index 16d670d5b..e8f8d99cf 100644
--- a/src/display/api.js
+++ b/src/display/api.js
@@ -17,9 +17,9 @@
 import {
   createPromiseCapability, deprecated, error, getVerbosityLevel, globalScope,
   info, InvalidPDFException, isArray, isArrayBuffer, isInt, isSameOrigin,
-  loadJpegStream, MessageHandler, MissingPDFException, PageViewport,
-  PasswordException, StatTimer, stringToBytes, UnexpectedResponseException,
-  UnknownErrorException, Util, warn
+  loadJpegStream, MessageHandler, MissingPDFException, NativeImageDecoding,
+  PageViewport, PasswordException, StatTimer, stringToBytes,
+  UnexpectedResponseException, UnknownErrorException, Util, warn
 } from '../shared/util';
 import {
   DOMCanvasFactory, DOMCMapReaderFactory, getDefaultSetting,
@@ -104,10 +104,18 @@ if (typeof PDFJSDev !== 'undefined' &&
  * @property {string} docBaseUrl - (optional) The base URL of the document,
  *   used when attempting to recover valid absolute URLs for annotations, and
  *   outline items, that (incorrectly) only specify relative URLs.
- * @property {boolean} disableNativeImageDecoder - (optional) Disable decoding
+ * @property {boolean} disableNativeImageDecoder - (deprecated) Disable decoding
  *   of certain (simple) JPEG images in the browser. This is useful for
  *   environments without DOM image support, such as e.g. Node.js.
  *   The default value is `false`.
+ * @property {string} nativeImageDecoderSupport - (optional) Strategy for
+ *   decoding certain (simple) JPEG images in the browser. This is useful for
+ *   environments without DOM image and canvas support, such as e.g. Node.js.
+ *   Valid values are 'decode', 'display' or 'none'; where 'decode' is intended
+ *   for browsers with full image/canvas support, 'display' for environments
+ *   with limited image support through stubs (useful for SVG conversion),
+ *   and 'none' where JPEG images will be decoded entirely by PDF.js.
+ *   The default value is 'decode'.
  * @property {Object} CMapReaderFactory - (optional) The factory that will be
  *   used when reading built-in CMap files. Providing a custom factory is useful
  *   for environments without `XMLHttpRequest` support, such as e.g. Node.js.
@@ -229,10 +237,24 @@ function getDocument(src, pdfDataRangeTransport,
   }
 
   params.rangeChunkSize = params.rangeChunkSize || DEFAULT_RANGE_CHUNK_SIZE;
-  params.disableNativeImageDecoder = params.disableNativeImageDecoder === true;
   params.ignoreErrors = params.stopAtErrors !== true;
   var CMapReaderFactory = params.CMapReaderFactory || DOMCMapReaderFactory;
 
+  if (params.disableNativeImageDecoder !== undefined) {
+    deprecated('parameter disableNativeImageDecoder, ' +
+      'use nativeImageDecoderSupport instead');
+  }
+  params.nativeImageDecoderSupport = params.nativeImageDecoderSupport ||
+    (params.disableNativeImageDecoder === true ? NativeImageDecoding.NONE :
+      NativeImageDecoding.DECODE);
+  if (params.nativeImageDecoderSupport !== NativeImageDecoding.DECODE &&
+      params.nativeImageDecoderSupport !== NativeImageDecoding.NONE &&
+      params.nativeImageDecoderSupport !== NativeImageDecoding.DISPLAY) {
+    warn('Invalid parameter nativeImageDecoderSupport: ' +
+      'need a state of enum {NativeImageDecoding}');
+    params.nativeImageDecoderSupport = NativeImageDecoding.DECODE;
+  }
+
   if (!worker) {
     // Worker was not provided -- creating and owning our own. If message port
     // is specified in global settings, using it.
@@ -293,7 +315,7 @@ function _fetchDocument(worker, source, pdfDataRangeTransport, docId) {
     postMessageTransfers: getDefaultSetting('postMessageTransfers') &&
                           !isPostMessageTransfersDisabled,
     docBaseUrl: source.docBaseUrl,
-    disableNativeImageDecoder: source.disableNativeImageDecoder,
+    nativeImageDecoderSupport: source.nativeImageDecoderSupport,
     ignoreErrors: source.ignoreErrors,
   }).then(function (workerId) {
     if (worker.destroyed) {
diff --git a/src/display/svg.js b/src/display/svg.js
index c772a5eb3..46d037588 100644
--- a/src/display/svg.js
+++ b/src/display/svg.js
@@ -1029,8 +1029,8 @@ SVGGraphics = (function SVGGraphicsClosure() {
       var imgObj = this.objs.get(objId);
       var imgEl = document.createElementNS(NS, 'svg:image');
       imgEl.setAttributeNS(XLINK_NS, 'xlink:href', imgObj.src);
-      imgEl.setAttributeNS(null, 'width', imgObj.width + 'px');
-      imgEl.setAttributeNS(null, 'height', imgObj.height + 'px');
+      imgEl.setAttributeNS(null, 'width', pf(w));
+      imgEl.setAttributeNS(null, 'height', pf(h));
       imgEl.setAttributeNS(null, 'x', '0');
       imgEl.setAttributeNS(null, 'y', pf(-h));
       imgEl.setAttributeNS(null, 'transform',
diff --git a/src/main_loader.js b/src/main_loader.js
index aef74011a..9529bc61a 100644
--- a/src/main_loader.js
+++ b/src/main_loader.js
@@ -54,6 +54,7 @@
   exports.InvalidPDFException = sharedUtil.InvalidPDFException;
   exports.MissingPDFException = sharedUtil.MissingPDFException;
   exports.SVGGraphics = displaySVG.SVGGraphics;
+  exports.NativeImageDecoding = sharedUtil.NativeImageDecoding;
   exports.UnexpectedResponseException = sharedUtil.UnexpectedResponseException;
   exports.OPS = sharedUtil.OPS;
   exports.UNSUPPORTED_FEATURES = sharedUtil.UNSUPPORTED_FEATURES;
diff --git a/src/pdf.js b/src/pdf.js
index d9c4f2f33..8389f8b81 100644
--- a/src/pdf.js
+++ b/src/pdf.js
@@ -42,6 +42,7 @@ exports.PasswordResponses = pdfjsSharedUtil.PasswordResponses;
 exports.InvalidPDFException = pdfjsSharedUtil.InvalidPDFException;
 exports.MissingPDFException = pdfjsSharedUtil.MissingPDFException;
 exports.SVGGraphics = pdfjsDisplaySVG.SVGGraphics;
+exports.NativeImageDecoding = pdfjsSharedUtil.NativeImageDecoding;
 exports.UnexpectedResponseException =
   pdfjsSharedUtil.UnexpectedResponseException;
 exports.OPS = pdfjsSharedUtil.OPS;
diff --git a/src/shared/util.js b/src/shared/util.js
index 867575d2c..46cc9da00 100644
--- a/src/shared/util.js
+++ b/src/shared/util.js
@@ -22,6 +22,12 @@ var globalScope = (typeof window !== 'undefined') ? window :
 
 var FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0];
 
+const NativeImageDecoding = {
+  NONE: 'none',
+  DECODE: 'decode',
+  DISPLAY: 'display'
+};
+
 var TextRenderingMode = {
   FILL: 0,
   STROKE: 1,
@@ -1369,6 +1375,7 @@ export {
   MessageHandler,
   MissingDataException,
   MissingPDFException,
+  NativeImageDecoding,
   NotImplementedException,
   PageViewport,
   PasswordException,