diff --git a/examples/svgviewer/index.html b/examples/svgviewer/index.html
index 931b595a3..70bf357ed 100644
--- a/examples/svgviewer/index.html
+++ b/examples/svgviewer/index.html
@@ -2,21 +2,24 @@
+
+
+ PDF.js SVG viewer example
+
+
-
- SVG Viewer Example
+
diff --git a/examples/svgviewer/viewer.js b/examples/svgviewer/viewer.js
index 51f5ad2dd..7d330419e 100644
--- a/examples/svgviewer/viewer.js
+++ b/examples/svgviewer/viewer.js
@@ -1,40 +1,29 @@
'use strict';
+var DEFAULT_SCALE = 1.5;
+
// Parse query string to extract some parameters (it can fail for some input)
var query = document.location.href.replace(/^[^?]*(\?([^#]*))?(#.*)?/, '$2');
var queryParams = query ? JSON.parse('{' + query.split('&').map(function (a) {
return a.split('=').map(decodeURIComponent).map(JSON.stringify).join(': ');
}).join(',') + '}') : {};
-var url = queryParams.file || '../../test/pdfs/liveprogramming.pdf';
-var scale = +queryParams.scale || 1.5;
+var url = queryParams.file || '../../web/compressed.tracemonkey-pldi-09.pdf';
function renderDocument(pdf, svgLib) {
- var numPages = pdf.numPages;
- // Using promise to fetch the page
-
- // For testing only.
- var MAX_NUM_PAGES = 50;
- var ii = Math.min(MAX_NUM_PAGES, numPages);
-
var promise = Promise.resolve();
- for (var i = 1; i <= ii; i++) {
- var anchor = document.createElement('a');
- anchor.setAttribute('name', 'page=' + i);
- anchor.setAttribute('title', 'Page ' + i);
- document.body.appendChild(anchor);
-
+ for (var i = 1; i <= pdf.numPages; i++) {
// Using promise to fetch and render the next page
- promise = promise.then(function (pageNum, anchor) {
+ promise = promise.then(function (pageNum) {
return pdf.getPage(pageNum).then(function (page) {
- var viewport = page.getViewport(scale);
+ var viewport = page.getViewport(DEFAULT_SCALE);
var container = document.createElement('div');
container.id = 'pageContainer' + pageNum;
container.className = 'pageContainer';
container.style.width = viewport.width + 'px';
container.style.height = viewport.height + 'px';
- anchor.appendChild(container);
+ document.body.appendChild(container);
return page.getOperatorList().then(function (opList) {
var svgGfx = new svgLib.SVGGraphics(page.commonObjs, page.objs);
@@ -43,7 +32,7 @@ function renderDocument(pdf, svgLib) {
});
});
});
- }.bind(null, i, anchor));
+ }.bind(null, i));
}
}
diff --git a/src/display/svg.js b/src/display/svg.js
index 8d9f5e7f5..41fbc25f6 100644
--- a/src/display/svg.js
+++ b/src/display/svg.js
@@ -294,16 +294,6 @@ var SVGExtraState = (function SVGExtraStateClosure() {
})();
var SVGGraphics = (function SVGGraphicsClosure() {
- function createScratchSVG(width, height) {
- var NS = 'http://www.w3.org/2000/svg';
- var svg = document.createElementNS(NS, 'svg:svg');
- svg.setAttributeNS(null, 'version', '1.1');
- svg.setAttributeNS(null, 'width', width + 'px');
- svg.setAttributeNS(null, 'height', height + 'px');
- svg.setAttributeNS(null, 'viewBox', '0 0 ' + width + ' ' + height);
- return svg;
- }
-
function opListToTree(opList) {
var opTree = [];
var tmp = [];
@@ -410,9 +400,7 @@ var SVGGraphics = (function SVGGraphicsClosure() {
this.transformMatrix = this.transformStack.pop();
this.current = this.extraStack.pop();
- this.tgrp = document.createElementNS(NS, 'svg:g');
- this.tgrp.setAttributeNS(null, 'transform', pm(this.transformMatrix));
- this.pgrp.appendChild(this.tgrp);
+ this.tgrp = null;
},
group: function SVGGraphics_group(items) {
@@ -454,28 +442,18 @@ var SVGGraphics = (function SVGGraphicsClosure() {
var transformMatrix = [a, b, c, d, e, f];
this.transformMatrix = Util.transform(this.transformMatrix,
transformMatrix);
-
- this.tgrp = document.createElementNS(NS, 'svg:g');
- this.tgrp.setAttributeNS(null, 'transform', pm(this.transformMatrix));
+ this.tgrp = null;
},
getSVG: function SVGGraphics_getSVG(operatorList, viewport) {
- this.svg = createScratchSVG(viewport.width, viewport.height);
this.viewport = viewport;
+ var svgElement = this._initialize(viewport);
return this.loadDependencies(operatorList).then(function () {
this.transformMatrix = IDENTITY_MATRIX;
- this.pgrp = document.createElementNS(NS, 'svg:g'); // Parent group
- this.pgrp.setAttributeNS(null, 'transform', pm(viewport.transform));
- this.tgrp = document.createElementNS(NS, 'svg:g'); // Transform group
- this.tgrp.setAttributeNS(null, 'transform', pm(this.transformMatrix));
- this.defs = document.createElementNS(NS, 'svg:defs');
- this.pgrp.appendChild(this.defs);
- this.pgrp.appendChild(this.tgrp);
- this.svg.appendChild(this.pgrp);
var opTree = this.convertOpList(operatorList);
this.executeOpTree(opTree);
- return this.svg;
+ return svgElement;
}.bind(this));
},
@@ -634,7 +612,7 @@ var SVGGraphics = (function SVGGraphicsClosure() {
this.group(opTree[x].items);
break;
default:
- warn('Unimplemented method '+ fn);
+ warn('Unimplemented operator ' + fn);
break;
}
}
@@ -760,8 +738,7 @@ var SVGGraphics = (function SVGGraphicsClosure() {
current.txtElement.appendChild(current.tspan);
current.txtgrp.appendChild(current.txtElement);
- this.tgrp.appendChild(current.txtElement);
-
+ this._ensureTransformGroup().appendChild(current.txtElement);
},
setLeadingMoveText: function SVGGraphics_setLeadingMoveText(x, y) {
@@ -818,16 +795,7 @@ var SVGGraphics = (function SVGGraphicsClosure() {
current.xcoords = [];
},
- endText: function SVGGraphics_endText() {
- if (this.current.pendingClip) {
- this.cgrp.appendChild(this.tgrp);
- this.pgrp.appendChild(this.cgrp);
- } else {
- this.pgrp.appendChild(this.tgrp);
- }
- this.tgrp = document.createElementNS(NS, 'svg:g');
- this.tgrp.setAttributeNS(null, 'transform', pm(this.transformMatrix));
- },
+ endText: function SVGGraphics_endText() {},
// Path properties
setLineWidth: function SVGGraphics_setLineWidth(width) {
@@ -925,30 +893,15 @@ var SVGGraphics = (function SVGGraphicsClosure() {
pf(current.dashPhase) + 'px');
current.path.setAttributeNS(null, 'fill', 'none');
- this.tgrp.appendChild(current.path);
- if (current.pendingClip) {
- this.cgrp.appendChild(this.tgrp);
- this.pgrp.appendChild(this.cgrp);
- } else {
- this.pgrp.appendChild(this.tgrp);
- }
+ this._ensureTransformGroup().appendChild(current.path);
+
// Saving a reference in current.element so that it can be addressed
// in 'fill' and 'stroke'
current.element = current.path;
current.setCurrentPoint(x, y);
},
- endPath: function SVGGraphics_endPath() {
- var current = this.current;
- if (current.pendingClip) {
- this.cgrp.appendChild(this.tgrp);
- this.pgrp.appendChild(this.cgrp);
- } else {
- this.pgrp.appendChild(this.tgrp);
- }
- this.tgrp = document.createElementNS(NS, 'svg:g');
- this.tgrp.setAttributeNS(null, 'transform', pm(this.transformMatrix));
- },
+ endPath: function SVGGraphics_endPath() {},
clip: function SVGGraphics_clip(type) {
var current = this.current;
@@ -967,12 +920,12 @@ var SVGGraphics = (function SVGGraphicsClosure() {
this.clippath.appendChild(clipElement);
this.defs.appendChild(this.clippath);
- // Create a new group with that attribute
+ // Create a clipping group that references the clipping path.
current.pendingClip = true;
this.cgrp = document.createElementNS(NS, 'svg:g');
this.cgrp.setAttributeNS(null, 'clip-path',
'url(#' + current.clipId + ')');
- this.pgrp.appendChild(this.cgrp);
+ this.svg.appendChild(this.cgrp);
},
closePath: function SVGGraphics_closePath() {
@@ -1016,20 +969,11 @@ var SVGGraphics = (function SVGGraphicsClosure() {
case 'D':
this.setDash(value[0], value[1]);
break;
- case 'RI':
- break;
- case 'FL':
- break;
case 'Font':
this.setFont(value);
break;
- case 'CA':
- break;
- case 'ca':
- break;
- case 'BM':
- break;
- case 'SMask':
+ default:
+ warn('Unimplemented graphic state ' + key);
break;
}
}
@@ -1083,11 +1027,11 @@ var SVGGraphics = (function SVGGraphicsClosure() {
rect.setAttributeNS(null, 'width', '1px');
rect.setAttributeNS(null, 'height', '1px');
rect.setAttributeNS(null, 'fill', current.fillColor);
- this.tgrp.appendChild(rect);
+
+ this._ensureTransformGroup().appendChild(rect);
},
paintJpegXObject: function SVGGraphics_paintJpegXObject(objId, w, h) {
- var current = this.current;
var imgObj = this.objs.get(objId);
var imgEl = document.createElementNS(NS, 'svg:image');
imgEl.setAttributeNS(XLINK_NS, 'xlink:href', imgObj.src);
@@ -1098,13 +1042,7 @@ var SVGGraphics = (function SVGGraphicsClosure() {
imgEl.setAttributeNS(null, 'transform',
'scale(' + pf(1 / w) + ' ' + pf(-1 / h) + ')');
- this.tgrp.appendChild(imgEl);
- if (current.pendingClip) {
- this.cgrp.appendChild(this.tgrp);
- this.pgrp.appendChild(this.cgrp);
- } else {
- this.pgrp.appendChild(this.tgrp);
- }
+ this._ensureTransformGroup().appendChild(imgEl);
},
paintImageXObject: function SVGGraphics_paintImageXObject(objId) {
@@ -1118,7 +1056,6 @@ var SVGGraphics = (function SVGGraphicsClosure() {
paintInlineImageXObject:
function SVGGraphics_paintInlineImageXObject(imgData, mask) {
- var current = this.current;
var width = imgData.width;
var height = imgData.height;
@@ -1128,7 +1065,7 @@ var SVGGraphics = (function SVGGraphicsClosure() {
cliprect.setAttributeNS(null, 'y', '0');
cliprect.setAttributeNS(null, 'width', pf(width));
cliprect.setAttributeNS(null, 'height', pf(height));
- current.element = cliprect;
+ this.current.element = cliprect;
this.clip('nonzero');
var imgEl = document.createElementNS(NS, 'svg:image');
imgEl.setAttributeNS(XLINK_NS, 'xlink:href', imgSrc);
@@ -1142,13 +1079,7 @@ var SVGGraphics = (function SVGGraphicsClosure() {
if (mask) {
mask.appendChild(imgEl);
} else {
- this.tgrp.appendChild(imgEl);
- }
- if (current.pendingClip) {
- this.cgrp.appendChild(this.tgrp);
- this.pgrp.appendChild(this.cgrp);
- } else {
- this.pgrp.appendChild(this.tgrp);
+ this._ensureTransformGroup().appendChild(imgEl);
}
},
@@ -1171,15 +1102,14 @@ var SVGGraphics = (function SVGGraphicsClosure() {
rect.setAttributeNS(null, 'fill', fillColor);
rect.setAttributeNS(null, 'mask', 'url(#' + current.maskId +')');
this.defs.appendChild(mask);
- this.tgrp.appendChild(rect);
+
+ this._ensureTransformGroup().appendChild(rect);
this.paintInlineImageXObject(imgData, mask);
},
paintFormXObjectBegin:
function SVGGraphics_paintFormXObjectBegin(matrix, bbox) {
- this.save();
-
if (isArray(matrix) && matrix.length === 6) {
this.transform(matrix[0], matrix[1], matrix[2],
matrix[3], matrix[4], matrix[5]);
@@ -1201,8 +1131,54 @@ var SVGGraphics = (function SVGGraphicsClosure() {
},
paintFormXObjectEnd:
- function SVGGraphics_paintFormXObjectEnd() {
- this.restore();
+ function SVGGraphics_paintFormXObjectEnd() {},
+
+ /**
+ * @private
+ */
+ _initialize: function SVGGraphics_initialize(viewport) {
+ // Create the SVG element.
+ var svg = document.createElementNS(NS, 'svg:svg');
+ svg.setAttributeNS(null, 'version', '1.1');
+ svg.setAttributeNS(null, 'width', viewport.width + 'px');
+ svg.setAttributeNS(null, 'height', viewport.height + 'px');
+ svg.setAttributeNS(null, 'viewBox', '0 0 ' + viewport.width +
+ ' ' + viewport.height);
+
+ // Create the definitions element.
+ var definitions = document.createElementNS(NS, 'svg:defs');
+ svg.appendChild(definitions);
+ this.defs = definitions;
+
+ // Create the root group element, which acts a container for all other
+ // groups and applies the viewport transform.
+ var rootGroup = document.createElementNS(NS, 'svg:g');
+ rootGroup.setAttributeNS(null, 'transform', pm(viewport.transform));
+ svg.appendChild(rootGroup);
+
+ // For the construction of the SVG image we are only interested in the
+ // root group, so we expose it as the entry point of the SVG image for
+ // the other code in this class.
+ this.svg = rootGroup;
+
+ return svg;
+ },
+
+ /**
+ * @private
+ */
+ _ensureTransformGroup: function SVGGraphics_ensureTransformGroup() {
+ if (!this.tgrp) {
+ this.tgrp = document.createElementNS(NS, 'svg:g');
+ this.tgrp.setAttributeNS(null, 'transform', pm(this.transformMatrix));
+
+ if (this.current.pendingClip) {
+ this.cgrp.appendChild(this.tgrp);
+ } else {
+ this.svg.appendChild(this.tgrp);
+ }
+ }
+ return this.tgrp;
}
};
return SVGGraphics;