Merge remote-tracking branch 'upstream/master' into refactor
This commit is contained in:
commit
bc3b8e0ff1
2
Makefile
2
Makefile
@ -129,7 +129,7 @@ browser-test:
|
|||||||
#
|
#
|
||||||
# <http://code.google.com/closure/utilities/docs/linter_howto.html>
|
# <http://code.google.com/closure/utilities/docs/linter_howto.html>
|
||||||
SRC_DIRS := . src utils web test examples/helloworld extensions/firefox \
|
SRC_DIRS := . src utils web test examples/helloworld extensions/firefox \
|
||||||
extensions/firefox/components extensions/chrome
|
extensions/firefox/components extensions/chrome test/unit
|
||||||
GJSLINT_FILES = $(foreach DIR,$(SRC_DIRS),$(wildcard $(DIR)/*.js))
|
GJSLINT_FILES = $(foreach DIR,$(SRC_DIRS),$(wildcard $(DIR)/*.js))
|
||||||
lint:
|
lint:
|
||||||
gjslint --nojsdoc $(GJSLINT_FILES)
|
gjslint --nojsdoc $(GJSLINT_FILES)
|
||||||
|
@ -255,8 +255,11 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
}
|
}
|
||||||
// Scale so that canvas units are the same as PDF user space units
|
// Scale so that canvas units are the same as PDF user space units
|
||||||
this.ctx.scale(cw / mediaBox.width, ch / mediaBox.height);
|
this.ctx.scale(cw / mediaBox.width, ch / mediaBox.height);
|
||||||
this.textDivs = [];
|
// Move the media left-top corner to the (0,0) canvas position
|
||||||
this.textLayerQueue = [];
|
this.ctx.translate(-mediaBox.x, -mediaBox.y);
|
||||||
|
|
||||||
|
if (this.textLayer)
|
||||||
|
this.textLayer.beginLayout();
|
||||||
},
|
},
|
||||||
|
|
||||||
executeIRQueue: function canvasGraphicsExecuteIRQueue(codeIR,
|
executeIRQueue: function canvasGraphicsExecuteIRQueue(codeIR,
|
||||||
@ -320,27 +323,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
endDrawing: function canvasGraphicsEndDrawing() {
|
endDrawing: function canvasGraphicsEndDrawing() {
|
||||||
this.ctx.restore();
|
this.ctx.restore();
|
||||||
|
|
||||||
var textLayer = this.textLayer;
|
if (this.textLayer)
|
||||||
if (!textLayer)
|
this.textLayer.endLayout();
|
||||||
return;
|
|
||||||
|
|
||||||
var self = this;
|
|
||||||
var textDivs = this.textDivs;
|
|
||||||
this.textLayerTimer = setInterval(function renderTextLayer() {
|
|
||||||
if (textDivs.length === 0) {
|
|
||||||
clearInterval(self.textLayerTimer);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var textDiv = textDivs.shift();
|
|
||||||
if (textDiv.dataset.textLength > 1) { // avoid div by zero
|
|
||||||
textLayer.appendChild(textDiv);
|
|
||||||
// Adjust div width (via letterSpacing) to match canvas text
|
|
||||||
// Due to the .offsetWidth calls, this is slow
|
|
||||||
textDiv.style.letterSpacing =
|
|
||||||
((textDiv.dataset.canvasWidth - textDiv.offsetWidth) /
|
|
||||||
(textDiv.dataset.textLength - 1)) + 'px';
|
|
||||||
}
|
|
||||||
}, 0);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
// Graphics state
|
// Graphics state
|
||||||
@ -359,6 +343,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
setDash: function canvasGraphicsSetDash(dashArray, dashPhase) {
|
setDash: function canvasGraphicsSetDash(dashArray, dashPhase) {
|
||||||
this.ctx.mozDash = dashArray;
|
this.ctx.mozDash = dashArray;
|
||||||
this.ctx.mozDashOffset = dashPhase;
|
this.ctx.mozDashOffset = dashPhase;
|
||||||
|
this.ctx.webkitLineDash = dashArray;
|
||||||
|
this.ctx.webkitLineDashOffset = dashPhase;
|
||||||
},
|
},
|
||||||
setRenderingIntent: function canvasGraphicsSetRenderingIntent(intent) {
|
setRenderingIntent: function canvasGraphicsSetRenderingIntent(intent) {
|
||||||
TODO('set rendering intent: ' + intent);
|
TODO('set rendering intent: ' + intent);
|
||||||
@ -630,24 +616,6 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
return geometry;
|
return geometry;
|
||||||
},
|
},
|
||||||
|
|
||||||
pushTextDivs: function canvasGraphicsPushTextDivs(text) {
|
|
||||||
var div = document.createElement('div');
|
|
||||||
var fontSize = this.current.fontSize;
|
|
||||||
|
|
||||||
// vScale and hScale already contain the scaling to pixel units
|
|
||||||
// as mozCurrentTransform reflects ctx.scale() changes
|
|
||||||
// (see beginDrawing())
|
|
||||||
var fontHeight = fontSize * text.geom.vScale;
|
|
||||||
div.dataset.canvasWidth = text.canvasWidth * text.geom.hScale;
|
|
||||||
|
|
||||||
div.style.fontSize = fontHeight + 'px';
|
|
||||||
div.style.fontFamily = this.current.font.loadedName || 'sans-serif';
|
|
||||||
div.style.left = text.geom.x + 'px';
|
|
||||||
div.style.top = (text.geom.y - fontHeight) + 'px';
|
|
||||||
div.innerHTML = text.str;
|
|
||||||
div.dataset.textLength = text.length;
|
|
||||||
this.textDivs.push(div);
|
|
||||||
},
|
|
||||||
showText: function canvasGraphicsShowText(str, skipTextSelection) {
|
showText: function canvasGraphicsShowText(str, skipTextSelection) {
|
||||||
var ctx = this.ctx;
|
var ctx = this.ctx;
|
||||||
var current = this.current;
|
var current = this.current;
|
||||||
@ -672,6 +640,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
ctx.translate(current.x, current.y);
|
ctx.translate(current.x, current.y);
|
||||||
|
|
||||||
ctx.scale(textHScale, 1);
|
ctx.scale(textHScale, 1);
|
||||||
|
ctx.lineWidth /= current.textMatrix[0];
|
||||||
|
|
||||||
if (textSelection) {
|
if (textSelection) {
|
||||||
this.save();
|
this.save();
|
||||||
@ -708,6 +677,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
} else {
|
} else {
|
||||||
ctx.save();
|
ctx.save();
|
||||||
this.applyTextTransforms();
|
this.applyTextTransforms();
|
||||||
|
ctx.lineWidth /= current.textMatrix[0] * fontMatrix[0];
|
||||||
|
|
||||||
if (textSelection)
|
if (textSelection)
|
||||||
text.geom = this.getTextGeometry();
|
text.geom = this.getTextGeometry();
|
||||||
|
|
||||||
@ -744,7 +715,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
|
|
||||||
width += charWidth;
|
width += charWidth;
|
||||||
|
|
||||||
text.str += glyph.unicode === ' ' ? ' ' : glyph.unicode;
|
text.str += glyph.unicode === ' ' ? '\u00A0' : glyph.unicode;
|
||||||
text.length++;
|
text.length++;
|
||||||
text.canvasWidth += charWidth;
|
text.canvasWidth += charWidth;
|
||||||
}
|
}
|
||||||
@ -753,7 +724,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (textSelection)
|
if (textSelection)
|
||||||
this.pushTextDivs(text);
|
this.textLayer.appendText(text, font.loadedName, fontSize);
|
||||||
|
|
||||||
return text;
|
return text;
|
||||||
},
|
},
|
||||||
@ -796,7 +767,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
if (e < 0 && text.geom.spaceWidth > 0) { // avoid div by zero
|
if (e < 0 && text.geom.spaceWidth > 0) { // avoid div by zero
|
||||||
var numFakeSpaces = Math.round(-e / text.geom.spaceWidth);
|
var numFakeSpaces = Math.round(-e / text.geom.spaceWidth);
|
||||||
if (numFakeSpaces > 0) {
|
if (numFakeSpaces > 0) {
|
||||||
text.str += ' ';
|
text.str += '\u00A0';
|
||||||
text.length++;
|
text.length++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -806,7 +777,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
|
|
||||||
if (textSelection) {
|
if (textSelection) {
|
||||||
if (shownText.str === ' ') {
|
if (shownText.str === ' ') {
|
||||||
text.str += ' ';
|
text.str += '\u00A0';
|
||||||
} else {
|
} else {
|
||||||
text.str += shownText.str;
|
text.str += shownText.str;
|
||||||
}
|
}
|
||||||
@ -819,7 +790,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (textSelection)
|
if (textSelection)
|
||||||
this.pushTextDivs(text);
|
this.textLayer.appendText(text, font.loadedName, fontSize);
|
||||||
},
|
},
|
||||||
nextLineShowText: function canvasGraphicsNextLineShowText(text) {
|
nextLineShowText: function canvasGraphicsNextLineShowText(text) {
|
||||||
this.nextLine();
|
this.nextLine();
|
||||||
|
140
src/core.js
140
src/core.js
@ -70,8 +70,7 @@ var Page = (function PageClosure() {
|
|||||||
this.xref = xref;
|
this.xref = xref;
|
||||||
this.ref = ref;
|
this.ref = ref;
|
||||||
|
|
||||||
this.ctx = null;
|
this.displayReadyPromise = null;
|
||||||
this.callback = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Page.prototype = {
|
Page.prototype = {
|
||||||
@ -110,9 +109,11 @@ var Page = (function PageClosure() {
|
|||||||
width: this.width,
|
width: this.width,
|
||||||
height: this.height
|
height: this.height
|
||||||
};
|
};
|
||||||
|
var mediaBox = this.mediaBox;
|
||||||
|
var offsetX = mediaBox[0], offsetY = mediaBox[1];
|
||||||
if (isArray(obj) && obj.length == 4) {
|
if (isArray(obj) && obj.length == 4) {
|
||||||
var tl = this.rotatePoint(obj[0], obj[1]);
|
var tl = this.rotatePoint(obj[0] - offsetX, obj[1] - offsetY);
|
||||||
var br = this.rotatePoint(obj[2], obj[3]);
|
var br = this.rotatePoint(obj[2] - offsetX, obj[3] - offsetY);
|
||||||
view.x = Math.min(tl.x, br.x);
|
view.x = Math.min(tl.x, br.x);
|
||||||
view.y = Math.min(tl.y, br.y);
|
view.y = Math.min(tl.y, br.y);
|
||||||
view.width = Math.abs(tl.x - br.x);
|
view.width = Math.abs(tl.x - br.x);
|
||||||
@ -165,20 +166,12 @@ var Page = (function PageClosure() {
|
|||||||
IRQueue, fonts) {
|
IRQueue, fonts) {
|
||||||
var self = this;
|
var self = this;
|
||||||
this.IRQueue = IRQueue;
|
this.IRQueue = IRQueue;
|
||||||
var gfx = new CanvasGraphics(this.ctx, this.objs, this.textLayer);
|
|
||||||
|
|
||||||
var displayContinuation = function pageDisplayContinuation() {
|
var displayContinuation = function pageDisplayContinuation() {
|
||||||
// Always defer call to display() to work around bug in
|
// Always defer call to display() to work around bug in
|
||||||
// Firefox error reporting from XHR callbacks.
|
// Firefox error reporting from XHR callbacks.
|
||||||
setTimeout(function pageSetTimeout() {
|
setTimeout(function pageSetTimeout() {
|
||||||
try {
|
self.displayReadyPromise.resolve();
|
||||||
self.display(gfx, self.callback);
|
|
||||||
} catch (e) {
|
|
||||||
if (self.callback)
|
|
||||||
self.callback(e);
|
|
||||||
else
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -395,12 +388,35 @@ var Page = (function PageClosure() {
|
|||||||
return items;
|
return items;
|
||||||
},
|
},
|
||||||
startRendering: function pageStartRendering(ctx, callback, textLayer) {
|
startRendering: function pageStartRendering(ctx, callback, textLayer) {
|
||||||
this.ctx = ctx;
|
|
||||||
this.callback = callback;
|
|
||||||
this.textLayer = textLayer;
|
|
||||||
|
|
||||||
this.startRenderingTime = Date.now();
|
this.startRenderingTime = Date.now();
|
||||||
this.pdf.startRendering(this);
|
|
||||||
|
// If there is no displayReadyPromise yet, then the IRQueue was never
|
||||||
|
// requested before. Make the request and create the promise.
|
||||||
|
if (!this.displayReadyPromise) {
|
||||||
|
this.pdf.startRendering(this);
|
||||||
|
this.displayReadyPromise = new Promise();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Once the IRQueue and fonts are loaded, perform the actual rendering.
|
||||||
|
this.displayReadyPromise.then(
|
||||||
|
function pageDisplayReadyPromise() {
|
||||||
|
var gfx = new CanvasGraphics(ctx, this.objs, textLayer);
|
||||||
|
try {
|
||||||
|
this.display(gfx, callback);
|
||||||
|
} catch (e) {
|
||||||
|
if (callback)
|
||||||
|
callback(e);
|
||||||
|
else
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}.bind(this),
|
||||||
|
function pageDisplayReadPromiseError(reason) {
|
||||||
|
if (callback)
|
||||||
|
callback(reason);
|
||||||
|
else
|
||||||
|
throw reason;
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -523,10 +539,19 @@ var PDFDocModel = (function PDFDocModelClosure() {
|
|||||||
},
|
},
|
||||||
setup: function pdfDocSetup(ownerPassword, userPassword) {
|
setup: function pdfDocSetup(ownerPassword, userPassword) {
|
||||||
this.checkHeader();
|
this.checkHeader();
|
||||||
this.xref = new XRef(this.stream,
|
var xref = new XRef(this.stream,
|
||||||
this.startXRef,
|
this.startXRef,
|
||||||
this.mainXRefEntriesOffset);
|
this.mainXRefEntriesOffset);
|
||||||
this.catalog = new Catalog(this.xref);
|
this.xref = xref;
|
||||||
|
this.catalog = new Catalog(xref);
|
||||||
|
if (xref.trailer && xref.trailer.has('ID')) {
|
||||||
|
var fileID = '';
|
||||||
|
var id = xref.fetchIfRef(xref.trailer.get('ID'))[0];
|
||||||
|
id.split('').forEach(function(el) {
|
||||||
|
fileID += Number(el.charCodeAt(0)).toString(16);
|
||||||
|
});
|
||||||
|
this.fileID = fileID;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
get numPages() {
|
get numPages() {
|
||||||
var linearization = this.linearization;
|
var linearization = this.linearization;
|
||||||
@ -534,6 +559,22 @@ var PDFDocModel = (function PDFDocModelClosure() {
|
|||||||
// shadow the prototype getter
|
// shadow the prototype getter
|
||||||
return shadow(this, 'numPages', num);
|
return shadow(this, 'numPages', num);
|
||||||
},
|
},
|
||||||
|
getFingerprint: function pdfDocGetFingerprint() {
|
||||||
|
if (this.fileID) {
|
||||||
|
return this.fileID;
|
||||||
|
} else {
|
||||||
|
// If we got no fileID, then we generate one,
|
||||||
|
// from the first 100 bytes of PDF
|
||||||
|
var data = this.stream.bytes.subarray(0, 100);
|
||||||
|
var hash = calculateMD5(data, 0, data.length);
|
||||||
|
var strHash = '';
|
||||||
|
for (var i = 0, length = hash.length; i < length; i++) {
|
||||||
|
strHash += Number(hash[i]).toString(16);
|
||||||
|
}
|
||||||
|
|
||||||
|
return strHash;
|
||||||
|
}
|
||||||
|
},
|
||||||
getPage: function pdfDocGetPage(n) {
|
getPage: function pdfDocGetPage(n) {
|
||||||
return this.catalog.getPage(n);
|
return this.catalog.getPage(n);
|
||||||
}
|
}
|
||||||
@ -560,7 +601,7 @@ var PDFDoc = (function PDFDocClosure() {
|
|||||||
this.data = data;
|
this.data = data;
|
||||||
this.stream = stream;
|
this.stream = stream;
|
||||||
this.pdf = new PDFDocModel(stream);
|
this.pdf = new PDFDocModel(stream);
|
||||||
|
this.fingerprint = this.pdf.getFingerprint();
|
||||||
this.catalog = this.pdf.catalog;
|
this.catalog = this.pdf.catalog;
|
||||||
this.objs = new PDFObjects();
|
this.objs = new PDFObjects();
|
||||||
|
|
||||||
@ -579,36 +620,35 @@ var PDFDoc = (function PDFDocClosure() {
|
|||||||
throw 'No PDFJS.workerSrc specified';
|
throw 'No PDFJS.workerSrc specified';
|
||||||
}
|
}
|
||||||
|
|
||||||
var worker;
|
|
||||||
try {
|
try {
|
||||||
worker = new Worker(workerSrc);
|
|
||||||
} catch (e) {
|
|
||||||
// Some versions of FF can't create a worker on localhost, see:
|
// Some versions of FF can't create a worker on localhost, see:
|
||||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=683280
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=683280
|
||||||
globalScope.PDFJS.disableWorker = true;
|
var worker = new Worker(workerSrc);
|
||||||
this.setupFakeWorker();
|
|
||||||
|
var messageHandler = new MessageHandler('main', worker);
|
||||||
|
// Tell the worker the file it was created from.
|
||||||
|
messageHandler.send('workerSrc', workerSrc);
|
||||||
|
messageHandler.on('test', function pdfDocTest(supportTypedArray) {
|
||||||
|
if (supportTypedArray) {
|
||||||
|
this.worker = worker;
|
||||||
|
this.setupMessageHandler(messageHandler);
|
||||||
|
} else {
|
||||||
|
globalScope.PDFJS.disableWorker = true;
|
||||||
|
this.setupFakeWorker();
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
var testObj = new Uint8Array(1);
|
||||||
|
// Some versions of Opera throw a DATA_CLONE_ERR on
|
||||||
|
// serializing the typed array.
|
||||||
|
messageHandler.send('test', testObj);
|
||||||
return;
|
return;
|
||||||
}
|
} catch (e) {}
|
||||||
|
|
||||||
var messageHandler = new MessageHandler('main', worker);
|
|
||||||
|
|
||||||
// Tell the worker the file it was created from.
|
|
||||||
messageHandler.send('workerSrc', workerSrc);
|
|
||||||
|
|
||||||
messageHandler.on('test', function pdfDocTest(supportTypedArray) {
|
|
||||||
if (supportTypedArray) {
|
|
||||||
this.worker = worker;
|
|
||||||
this.setupMessageHandler(messageHandler);
|
|
||||||
} else {
|
|
||||||
this.setupFakeWorker();
|
|
||||||
}
|
|
||||||
}.bind(this));
|
|
||||||
|
|
||||||
var testObj = new Uint8Array(1);
|
|
||||||
messageHandler.send('test', testObj);
|
|
||||||
} else {
|
|
||||||
this.setupFakeWorker();
|
|
||||||
}
|
}
|
||||||
|
// Either workers are disabled, not supported or have thrown an exception.
|
||||||
|
// Thus, we fallback to a faked worker.
|
||||||
|
globalScope.PDFJS.disableWorker = true;
|
||||||
|
this.setupFakeWorker();
|
||||||
}
|
}
|
||||||
|
|
||||||
PDFDoc.prototype = {
|
PDFDoc.prototype = {
|
||||||
@ -692,8 +732,8 @@ var PDFDoc = (function PDFDocClosure() {
|
|||||||
|
|
||||||
messageHandler.on('page_error', function pdfDocError(data) {
|
messageHandler.on('page_error', function pdfDocError(data) {
|
||||||
var page = this.pageCache[data.pageNum];
|
var page = this.pageCache[data.pageNum];
|
||||||
if (page.callback)
|
if (page.displayReadyPromise)
|
||||||
page.callback(data.error);
|
page.displayReadyPromise.reject(data.error);
|
||||||
else
|
else
|
||||||
throw data.error;
|
throw data.error;
|
||||||
}, this);
|
}, this);
|
||||||
|
@ -2092,7 +2092,7 @@ var Font = (function FontClosure() {
|
|||||||
window.btoa(data) + ');');
|
window.btoa(data) + ');');
|
||||||
var rule = "@font-face { font-family:'" + fontName + "';src:" + url + '}';
|
var rule = "@font-face { font-family:'" + fontName + "';src:" + url + '}';
|
||||||
|
|
||||||
document.documentElement.firstChild.appendChild(
|
document.documentElement.getElementsByTagName('head')[0].appendChild(
|
||||||
document.createElement('style'));
|
document.createElement('style'));
|
||||||
|
|
||||||
var styleSheet = document.styleSheets[document.styleSheets.length - 1];
|
var styleSheet = document.styleSheets[document.styleSheets.length - 1];
|
||||||
|
553
src/function.js
553
src/function.js
@ -270,7 +270,6 @@ var PDFFunction = (function PDFFunctionClosure() {
|
|||||||
|
|
||||||
constructStiched: function pdfFunctionConstructStiched(fn, dict, xref) {
|
constructStiched: function pdfFunctionConstructStiched(fn, dict, xref) {
|
||||||
var domain = dict.get('Domain');
|
var domain = dict.get('Domain');
|
||||||
var range = dict.get('Range');
|
|
||||||
|
|
||||||
if (!domain)
|
if (!domain)
|
||||||
error('No domain');
|
error('No domain');
|
||||||
@ -279,13 +278,13 @@ var PDFFunction = (function PDFFunctionClosure() {
|
|||||||
if (inputSize != 1)
|
if (inputSize != 1)
|
||||||
error('Bad domain for stiched function');
|
error('Bad domain for stiched function');
|
||||||
|
|
||||||
var fnRefs = dict.get('Functions');
|
var fnRefs = xref.fetchIfRef(dict.get('Functions'));
|
||||||
var fns = [];
|
var fns = [];
|
||||||
for (var i = 0, ii = fnRefs.length; i < ii; ++i)
|
for (var i = 0, ii = fnRefs.length; i < ii; ++i)
|
||||||
fns.push(PDFFunction.getIR(xref, xref.fetchIfRef(fnRefs[i])));
|
fns.push(PDFFunction.getIR(xref, xref.fetchIfRef(fnRefs[i])));
|
||||||
|
|
||||||
var bounds = dict.get('Bounds');
|
var bounds = xref.fetchIfRef(dict.get('Bounds'));
|
||||||
var encode = dict.get('Encode');
|
var encode = xref.fetchIfRef(dict.get('Encode'));
|
||||||
|
|
||||||
return [CONSTRUCT_STICHED, domain, bounds, encode, fns];
|
return [CONSTRUCT_STICHED, domain, bounds, encode, fns];
|
||||||
},
|
},
|
||||||
@ -336,16 +335,550 @@ var PDFFunction = (function PDFFunctionClosure() {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
constructPostScript: function pdfFunctionConstructPostScript() {
|
constructPostScript: function pdfFunctionConstructPostScript(fn, dict,
|
||||||
return [CONSTRUCT_POSTSCRIPT];
|
xref) {
|
||||||
|
var domain = dict.get('Domain');
|
||||||
|
var range = dict.get('Range');
|
||||||
|
|
||||||
|
if (!domain)
|
||||||
|
error('No domain.');
|
||||||
|
|
||||||
|
if (!range)
|
||||||
|
error('No range.');
|
||||||
|
|
||||||
|
var lexer = new PostScriptLexer(fn);
|
||||||
|
var parser = new PostScriptParser(lexer);
|
||||||
|
var code = parser.parse();
|
||||||
|
|
||||||
|
return [CONSTRUCT_POSTSCRIPT, domain, range, code];
|
||||||
},
|
},
|
||||||
|
|
||||||
constructPostScriptFromIR: function pdfFunctionConstructPostScriptFromIR() {
|
constructPostScriptFromIR:
|
||||||
TODO('unhandled type of function');
|
function pdfFunctionConstructPostScriptFromIR(IR) {
|
||||||
return function constructPostScriptFromIRResult() {
|
var domain = IR[1];
|
||||||
return [255, 105, 180];
|
var range = IR[2];
|
||||||
|
var code = IR[3];
|
||||||
|
var numOutputs = range.length / 2;
|
||||||
|
var evaluator = new PostScriptEvaluator(code);
|
||||||
|
// Cache the values for a big speed up, the cache size is limited though
|
||||||
|
// since the number of possible values can be huge from a PS function.
|
||||||
|
var cache = new FunctionCache();
|
||||||
|
return function constructPostScriptFromIRResult(args) {
|
||||||
|
var initialStack = [];
|
||||||
|
for (var i = 0, ii = (domain.length / 2); i < ii; ++i) {
|
||||||
|
initialStack.push(args[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
var key = initialStack.join('_');
|
||||||
|
if (cache.has(key))
|
||||||
|
return cache.get(key);
|
||||||
|
|
||||||
|
var stack = evaluator.execute(initialStack);
|
||||||
|
var transformed = new Array(numOutputs);
|
||||||
|
for (i = numOutputs - 1; i >= 0; --i) {
|
||||||
|
var out = stack.pop();
|
||||||
|
var rangeIndex = 2 * i;
|
||||||
|
if (out < range[rangeIndex])
|
||||||
|
out = range[rangeIndex];
|
||||||
|
else if (out > range[rangeIndex + 1])
|
||||||
|
out = range[rangeIndex + 1];
|
||||||
|
transformed[i] = out;
|
||||||
|
}
|
||||||
|
cache.set(key, transformed);
|
||||||
|
return transformed;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
var FunctionCache = (function FunctionCacheClosure() {
|
||||||
|
// Of 10 PDF's with type4 functions the maxium number of distinct values seen
|
||||||
|
// was 256. This still may need some tweaking in the future though.
|
||||||
|
var MAX_CACHE_SIZE = 1024;
|
||||||
|
function FunctionCache() {
|
||||||
|
this.cache = {};
|
||||||
|
this.total = 0;
|
||||||
|
}
|
||||||
|
FunctionCache.prototype = {
|
||||||
|
has: function has(key) {
|
||||||
|
return key in this.cache;
|
||||||
|
},
|
||||||
|
get: function get(key) {
|
||||||
|
return this.cache[key];
|
||||||
|
},
|
||||||
|
set: function set(key, value) {
|
||||||
|
if (this.total < MAX_CACHE_SIZE) {
|
||||||
|
this.cache[key] = value;
|
||||||
|
this.total++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return FunctionCache;
|
||||||
|
})();
|
||||||
|
|
||||||
|
var PostScriptStack = (function PostScriptStackClosure() {
|
||||||
|
var MAX_STACK_SIZE = 100;
|
||||||
|
function PostScriptStack(initialStack) {
|
||||||
|
this.stack = initialStack || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
PostScriptStack.prototype = {
|
||||||
|
push: function push(value) {
|
||||||
|
if (this.stack.length >= MAX_STACK_SIZE)
|
||||||
|
error('PostScript function stack overflow.');
|
||||||
|
this.stack.push(value);
|
||||||
|
},
|
||||||
|
pop: function pop() {
|
||||||
|
if (this.stack.length <= 0)
|
||||||
|
error('PostScript function stack underflow.');
|
||||||
|
return this.stack.pop();
|
||||||
|
},
|
||||||
|
copy: function copy(n) {
|
||||||
|
if (this.stack.length + n >= MAX_STACK_SIZE)
|
||||||
|
error('PostScript function stack overflow.');
|
||||||
|
var stack = this.stack;
|
||||||
|
for (var i = stack.length - n, j = n - 1; j >= 0; j--, i++)
|
||||||
|
stack.push(stack[i]);
|
||||||
|
},
|
||||||
|
index: function index(n) {
|
||||||
|
this.push(this.stack[this.stack.length - n - 1]);
|
||||||
|
},
|
||||||
|
// rotate the last n stack elements p times
|
||||||
|
roll: function roll(n, p) {
|
||||||
|
var stack = this.stack;
|
||||||
|
var l = stack.length - n;
|
||||||
|
var r = stack.length - 1, c = l + (p - Math.floor(p / n) * n), i, j, t;
|
||||||
|
for (i = l, j = r; i < j; i++, j--) {
|
||||||
|
t = stack[i]; stack[i] = stack[j]; stack[j] = t;
|
||||||
|
}
|
||||||
|
for (i = l, j = c - 1; i < j; i++, j--) {
|
||||||
|
t = stack[i]; stack[i] = stack[j]; stack[j] = t;
|
||||||
|
}
|
||||||
|
for (i = c, j = r; i < j; i++, j--) {
|
||||||
|
t = stack[i]; stack[i] = stack[j]; stack[j] = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return PostScriptStack;
|
||||||
|
})();
|
||||||
|
var PostScriptEvaluator = (function PostScriptEvaluatorClosure() {
|
||||||
|
function PostScriptEvaluator(operators, operands) {
|
||||||
|
this.operators = operators;
|
||||||
|
this.operands = operands;
|
||||||
|
}
|
||||||
|
PostScriptEvaluator.prototype = {
|
||||||
|
execute: function execute(initialStack) {
|
||||||
|
var stack = new PostScriptStack(initialStack);
|
||||||
|
var counter = 0;
|
||||||
|
var operators = this.operators;
|
||||||
|
var length = operators.length;
|
||||||
|
var operator, a, b;
|
||||||
|
while (counter < length) {
|
||||||
|
operator = operators[counter++];
|
||||||
|
if (typeof operator == 'number') {
|
||||||
|
// Operator is really an operand and should be pushed to the stack.
|
||||||
|
stack.push(operator);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
switch (operator) {
|
||||||
|
// non standard ps operators
|
||||||
|
case 'jz': // jump if false
|
||||||
|
b = stack.pop();
|
||||||
|
a = stack.pop();
|
||||||
|
if (!a)
|
||||||
|
counter = b;
|
||||||
|
break;
|
||||||
|
case 'j': // jump
|
||||||
|
a = stack.pop();
|
||||||
|
counter = a;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// all ps operators in alphabetical order (excluding if/ifelse)
|
||||||
|
case 'abs':
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(Math.abs(a));
|
||||||
|
break;
|
||||||
|
case 'add':
|
||||||
|
b = stack.pop();
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(a + b);
|
||||||
|
break;
|
||||||
|
case 'and':
|
||||||
|
b = stack.pop();
|
||||||
|
a = stack.pop();
|
||||||
|
if (isBool(a) && isBool(b))
|
||||||
|
stack.push(a && b);
|
||||||
|
else
|
||||||
|
stack.push(a & b);
|
||||||
|
break;
|
||||||
|
case 'atan':
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(Math.atan(a));
|
||||||
|
break;
|
||||||
|
case 'bitshift':
|
||||||
|
b = stack.pop();
|
||||||
|
a = stack.pop();
|
||||||
|
if (a > 0)
|
||||||
|
stack.push(a << b);
|
||||||
|
else
|
||||||
|
stack.push(a >> b);
|
||||||
|
break;
|
||||||
|
case 'ceiling':
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(Math.ceil(a));
|
||||||
|
break;
|
||||||
|
case 'copy':
|
||||||
|
a = stack.pop();
|
||||||
|
stack.copy(a);
|
||||||
|
break;
|
||||||
|
case 'cos':
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(Math.cos(a));
|
||||||
|
break;
|
||||||
|
case 'cvi':
|
||||||
|
a = stack.pop() | 0;
|
||||||
|
stack.push(a);
|
||||||
|
break;
|
||||||
|
case 'cvr':
|
||||||
|
// noop
|
||||||
|
break;
|
||||||
|
case 'div':
|
||||||
|
b = stack.pop();
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(a / b);
|
||||||
|
break;
|
||||||
|
case 'dup':
|
||||||
|
stack.copy(1);
|
||||||
|
break;
|
||||||
|
case 'eq':
|
||||||
|
b = stack.pop();
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(a == b);
|
||||||
|
break;
|
||||||
|
case 'exch':
|
||||||
|
stack.roll(2, 1);
|
||||||
|
break;
|
||||||
|
case 'exp':
|
||||||
|
b = stack.pop();
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(Math.pow(a, b));
|
||||||
|
break;
|
||||||
|
case 'false':
|
||||||
|
stack.push(false);
|
||||||
|
break;
|
||||||
|
case 'floor':
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(Math.floor(a));
|
||||||
|
break;
|
||||||
|
case 'ge':
|
||||||
|
b = stack.pop();
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(a >= b);
|
||||||
|
break;
|
||||||
|
case 'gt':
|
||||||
|
b = stack.pop();
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(a > b);
|
||||||
|
break;
|
||||||
|
case 'idiv':
|
||||||
|
b = stack.pop();
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push((a / b) | 0);
|
||||||
|
break;
|
||||||
|
case 'index':
|
||||||
|
a = stack.pop();
|
||||||
|
stack.index(a);
|
||||||
|
break;
|
||||||
|
case 'le':
|
||||||
|
b = stack.pop();
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(a <= b);
|
||||||
|
break;
|
||||||
|
case 'ln':
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(Math.log(a));
|
||||||
|
break;
|
||||||
|
case 'log':
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(Math.log(a) / Math.LN10);
|
||||||
|
break;
|
||||||
|
case 'lt':
|
||||||
|
b = stack.pop();
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(a < b);
|
||||||
|
break;
|
||||||
|
case 'mod':
|
||||||
|
b = stack.pop();
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(a % b);
|
||||||
|
break;
|
||||||
|
case 'mul':
|
||||||
|
b = stack.pop();
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(a * b);
|
||||||
|
break;
|
||||||
|
case 'ne':
|
||||||
|
b = stack.pop();
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(a != b);
|
||||||
|
break;
|
||||||
|
case 'neg':
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(-b);
|
||||||
|
break;
|
||||||
|
case 'not':
|
||||||
|
a = stack.pop();
|
||||||
|
if (isBool(a) && isBool(b))
|
||||||
|
stack.push(a && b);
|
||||||
|
else
|
||||||
|
stack.push(a & b);
|
||||||
|
break;
|
||||||
|
case 'or':
|
||||||
|
b = stack.pop();
|
||||||
|
a = stack.pop();
|
||||||
|
if (isBool(a) && isBool(b))
|
||||||
|
stack.push(a || b);
|
||||||
|
else
|
||||||
|
stack.push(a | b);
|
||||||
|
break;
|
||||||
|
case 'pop':
|
||||||
|
stack.pop();
|
||||||
|
break;
|
||||||
|
case 'roll':
|
||||||
|
b = stack.pop();
|
||||||
|
a = stack.pop();
|
||||||
|
stack.roll(a, b);
|
||||||
|
break;
|
||||||
|
case 'round':
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(Math.round(a));
|
||||||
|
break;
|
||||||
|
case 'sin':
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(Math.sin(a));
|
||||||
|
break;
|
||||||
|
case 'sqrt':
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(Math.sqrt(a));
|
||||||
|
break;
|
||||||
|
case 'sub':
|
||||||
|
b = stack.pop();
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(a - b);
|
||||||
|
break;
|
||||||
|
case 'true':
|
||||||
|
stack.push(true);
|
||||||
|
break;
|
||||||
|
case 'truncate':
|
||||||
|
a = stack.pop();
|
||||||
|
a = a < 0 ? Math.ceil(a) : Math.floor(a);
|
||||||
|
stack.push(a);
|
||||||
|
break;
|
||||||
|
case 'xor':
|
||||||
|
b = stack.pop();
|
||||||
|
a = stack.pop();
|
||||||
|
if (isBool(a) && isBool(b))
|
||||||
|
stack.push(a != b);
|
||||||
|
else
|
||||||
|
stack.push(a ^ b);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
error('Unknown operator ' + operator);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stack.stack;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return PostScriptEvaluator;
|
||||||
|
})();
|
||||||
|
|
||||||
|
var PostScriptParser = (function PostScriptParserClosure() {
|
||||||
|
function PostScriptParser(lexer) {
|
||||||
|
this.lexer = lexer;
|
||||||
|
this.operators = [];
|
||||||
|
this.token;
|
||||||
|
this.prev;
|
||||||
|
}
|
||||||
|
PostScriptParser.prototype = {
|
||||||
|
nextToken: function nextToken() {
|
||||||
|
this.prev = this.token;
|
||||||
|
this.token = this.lexer.getToken();
|
||||||
|
},
|
||||||
|
accept: function accept(type) {
|
||||||
|
if (this.token.type == type) {
|
||||||
|
this.nextToken();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
expect: function expect(type) {
|
||||||
|
if (this.accept(type))
|
||||||
|
return true;
|
||||||
|
error('Unexpected symbol: found ' + this.token.type + ' expected ' +
|
||||||
|
type + '.');
|
||||||
|
},
|
||||||
|
parse: function parse() {
|
||||||
|
this.nextToken();
|
||||||
|
this.expect(PostScriptTokenTypes.LBRACE);
|
||||||
|
this.parseBlock();
|
||||||
|
this.expect(PostScriptTokenTypes.RBRACE);
|
||||||
|
return this.operators;
|
||||||
|
},
|
||||||
|
parseBlock: function parseBlock() {
|
||||||
|
while (true) {
|
||||||
|
if (this.accept(PostScriptTokenTypes.NUMBER)) {
|
||||||
|
this.operators.push(this.prev.value);
|
||||||
|
} else if (this.accept(PostScriptTokenTypes.OPERATOR)) {
|
||||||
|
this.operators.push(this.prev.value);
|
||||||
|
} else if (this.accept(PostScriptTokenTypes.LBRACE)) {
|
||||||
|
this.parseCondition();
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
parseCondition: function parseCondition() {
|
||||||
|
// Add two place holders that will be updated later
|
||||||
|
var conditionLocation = this.operators.length;
|
||||||
|
this.operators.push(null, null);
|
||||||
|
|
||||||
|
this.parseBlock();
|
||||||
|
this.expect(PostScriptTokenTypes.RBRACE);
|
||||||
|
if (this.accept(PostScriptTokenTypes.IF)) {
|
||||||
|
// The true block is right after the 'if' so it just falls through on
|
||||||
|
// true else it jumps and skips the true block.
|
||||||
|
this.operators[conditionLocation] = this.operators.length;
|
||||||
|
this.operators[conditionLocation + 1] = 'jz';
|
||||||
|
} else if (this.accept(PostScriptTokenTypes.LBRACE)) {
|
||||||
|
var jumpLocation = this.operators.length;
|
||||||
|
this.operators.push(null, null);
|
||||||
|
var endOfTrue = this.operators.length;
|
||||||
|
this.parseBlock();
|
||||||
|
this.expect(PostScriptTokenTypes.RBRACE);
|
||||||
|
this.expect(PostScriptTokenTypes.IFELSE);
|
||||||
|
// The jump is added at the end of the true block to skip the false
|
||||||
|
// block.
|
||||||
|
this.operators[jumpLocation] = this.operators.length;
|
||||||
|
this.operators[jumpLocation + 1] = 'j';
|
||||||
|
|
||||||
|
this.operators[conditionLocation] = endOfTrue;
|
||||||
|
this.operators[conditionLocation + 1] = 'jz';
|
||||||
|
} else {
|
||||||
|
error('PS Function: error parsing conditional.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return PostScriptParser;
|
||||||
|
})();
|
||||||
|
|
||||||
|
var PostScriptTokenTypes = {
|
||||||
|
LBRACE: 0,
|
||||||
|
RBRACE: 1,
|
||||||
|
NUMBER: 2,
|
||||||
|
OPERATOR: 3,
|
||||||
|
IF: 4,
|
||||||
|
IFELSE: 5
|
||||||
|
};
|
||||||
|
|
||||||
|
var PostScriptToken = (function PostScriptTokenClosure() {
|
||||||
|
function PostScriptToken(type, value) {
|
||||||
|
this.type = type;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
var opCache = {};
|
||||||
|
|
||||||
|
PostScriptToken.getOperator = function getOperator(op) {
|
||||||
|
var opValue = opCache[op];
|
||||||
|
if (opValue)
|
||||||
|
return opValue;
|
||||||
|
|
||||||
|
return opCache[op] = new PostScriptToken(PostScriptTokenTypes.OPERATOR, op);
|
||||||
|
};
|
||||||
|
|
||||||
|
PostScriptToken.LBRACE = new PostScriptToken(PostScriptTokenTypes.LBRACE,
|
||||||
|
'{');
|
||||||
|
PostScriptToken.RBRACE = new PostScriptToken(PostScriptTokenTypes.RBRACE,
|
||||||
|
'}');
|
||||||
|
PostScriptToken.IF = new PostScriptToken(PostScriptTokenTypes.IF, 'IF');
|
||||||
|
PostScriptToken.IFELSE = new PostScriptToken(PostScriptTokenTypes.IFELSE,
|
||||||
|
'IFELSE');
|
||||||
|
return PostScriptToken;
|
||||||
|
})();
|
||||||
|
|
||||||
|
var PostScriptLexer = (function PostScriptLexerClosure() {
|
||||||
|
function PostScriptLexer(stream) {
|
||||||
|
this.stream = stream;
|
||||||
|
}
|
||||||
|
PostScriptLexer.prototype = {
|
||||||
|
getToken: function getToken() {
|
||||||
|
var s = '';
|
||||||
|
var ch;
|
||||||
|
var comment = false;
|
||||||
|
var stream = this.stream;
|
||||||
|
|
||||||
|
// skip comments
|
||||||
|
while (true) {
|
||||||
|
if (!(ch = stream.getChar()))
|
||||||
|
return EOF;
|
||||||
|
|
||||||
|
if (comment) {
|
||||||
|
if (ch == '\x0a' || ch == '\x0d')
|
||||||
|
comment = false;
|
||||||
|
} else if (ch == '%') {
|
||||||
|
comment = true;
|
||||||
|
} else if (!Lexer.isSpace(ch)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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 new PostScriptToken(PostScriptTokenTypes.NUMBER,
|
||||||
|
this.getNumber(ch));
|
||||||
|
case '{':
|
||||||
|
return PostScriptToken.LBRACE;
|
||||||
|
case '}':
|
||||||
|
return PostScriptToken.RBRACE;
|
||||||
|
}
|
||||||
|
// operator
|
||||||
|
var str = ch.toLowerCase();
|
||||||
|
while (true) {
|
||||||
|
ch = stream.lookChar().toLowerCase();
|
||||||
|
if (ch >= 'a' && ch <= 'z')
|
||||||
|
str += ch;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
stream.skip();
|
||||||
|
}
|
||||||
|
switch (str) {
|
||||||
|
case 'if':
|
||||||
|
return PostScriptToken.IF;
|
||||||
|
case 'ifelse':
|
||||||
|
return PostScriptToken.IFELSE;
|
||||||
|
default:
|
||||||
|
return PostScriptToken.getOperator(str);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getNumber: function getNumber(ch) {
|
||||||
|
var str = ch;
|
||||||
|
var stream = this.stream;
|
||||||
|
while (true) {
|
||||||
|
ch = stream.lookChar();
|
||||||
|
if ((ch >= '0' && ch <= '9') || ch == '-' || ch == '.')
|
||||||
|
str += ch;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
stream.skip();
|
||||||
|
}
|
||||||
|
var value = parseFloat(str);
|
||||||
|
if (isNaN(value))
|
||||||
|
error('Invalid floating point number: ' + value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return PostScriptLexer;
|
||||||
|
})();
|
||||||
|
|
||||||
|
12
src/obj.js
12
src/obj.js
@ -8,8 +8,7 @@ var Name = (function NameClosure() {
|
|||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
Name.prototype = {
|
Name.prototype = {};
|
||||||
};
|
|
||||||
|
|
||||||
return Name;
|
return Name;
|
||||||
})();
|
})();
|
||||||
@ -19,9 +18,7 @@ var Cmd = (function CmdClosure() {
|
|||||||
this.cmd = cmd;
|
this.cmd = cmd;
|
||||||
}
|
}
|
||||||
|
|
||||||
Cmd.prototype = {
|
Cmd.prototype = {};
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
var cmdCache = {};
|
var cmdCache = {};
|
||||||
|
|
||||||
@ -80,8 +77,7 @@ var Ref = (function RefClosure() {
|
|||||||
this.gen = gen;
|
this.gen = gen;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ref.prototype = {
|
Ref.prototype = {};
|
||||||
};
|
|
||||||
|
|
||||||
return Ref;
|
return Ref;
|
||||||
})();
|
})();
|
||||||
@ -273,7 +269,7 @@ var XRef = (function XRefClosure() {
|
|||||||
this.entries = [];
|
this.entries = [];
|
||||||
this.xrefstms = {};
|
this.xrefstms = {};
|
||||||
var trailerDict = this.readXRef(startXRef);
|
var trailerDict = this.readXRef(startXRef);
|
||||||
|
this.trailer = trailerDict;
|
||||||
// prepare the XRef cache
|
// prepare the XRef cache
|
||||||
this.cache = [];
|
this.cache = [];
|
||||||
|
|
||||||
|
@ -1856,10 +1856,10 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() {
|
|||||||
// values. The first array element indicates whether a valid code is being
|
// values. The first array element indicates whether a valid code is being
|
||||||
// returned. The second array element is the actual code. The third array
|
// returned. The second array element is the actual code. The third array
|
||||||
// element indicates whether EOF was reached.
|
// element indicates whether EOF was reached.
|
||||||
var findTableCode = function ccittFaxStreamFindTableCode(start, end, table,
|
CCITTFaxStream.prototype.findTableCode =
|
||||||
limit) {
|
function ccittFaxStreamFindTableCode(start, end, table, limit) {
|
||||||
var limitValue = limit || 0;
|
|
||||||
|
|
||||||
|
var limitValue = limit || 0;
|
||||||
for (var i = start; i <= end; ++i) {
|
for (var i = start; i <= end; ++i) {
|
||||||
var code = this.lookBits(i);
|
var code = this.lookBits(i);
|
||||||
if (code == EOF)
|
if (code == EOF)
|
||||||
@ -1890,7 +1890,7 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() {
|
|||||||
return p[1];
|
return p[1];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var result = findTableCode(1, 7, twoDimTable);
|
var result = this.findTableCode(1, 7, twoDimTable);
|
||||||
if (result[0] && result[2])
|
if (result[0] && result[2])
|
||||||
return result[1];
|
return result[1];
|
||||||
}
|
}
|
||||||
@ -1919,11 +1919,11 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() {
|
|||||||
return p[1];
|
return p[1];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var result = findTableCode(1, 9, whiteTable2);
|
var result = this.findTableCode(1, 9, whiteTable2);
|
||||||
if (result[0])
|
if (result[0])
|
||||||
return result[1];
|
return result[1];
|
||||||
|
|
||||||
result = findTableCode(11, 12, whiteTable1);
|
result = this.findTableCode(11, 12, whiteTable1);
|
||||||
if (result[0])
|
if (result[0])
|
||||||
return result[1];
|
return result[1];
|
||||||
}
|
}
|
||||||
@ -1952,15 +1952,15 @@ var CCITTFaxStream = (function CCITTFaxStreamClosure() {
|
|||||||
return p[1];
|
return p[1];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var result = findTableCode(2, 6, blackTable3);
|
var result = this.findTableCode(2, 6, blackTable3);
|
||||||
if (result[0])
|
if (result[0])
|
||||||
return result[1];
|
return result[1];
|
||||||
|
|
||||||
result = findTableCode(7, 12, blackTable2, 64);
|
result = this.findTableCode(7, 12, blackTable2, 64);
|
||||||
if (result[0])
|
if (result[0])
|
||||||
return result[1];
|
return result[1];
|
||||||
|
|
||||||
result = findTableCode(10, 13, blackTable1);
|
result = this.findTableCode(10, 13, blackTable1);
|
||||||
if (result[0])
|
if (result[0])
|
||||||
return result[1];
|
return result[1];
|
||||||
}
|
}
|
||||||
|
32
src/util.js
32
src/util.js
@ -206,6 +206,8 @@ var Promise = (function PromiseClosure() {
|
|||||||
*/
|
*/
|
||||||
function Promise(name, data) {
|
function Promise(name, data) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
this.isRejected = false;
|
||||||
|
this.error = null;
|
||||||
// If you build a promise and pass in some data it's already resolved.
|
// If you build a promise and pass in some data it's already resolved.
|
||||||
if (data != null) {
|
if (data != null) {
|
||||||
this.isResolved = true;
|
this.isResolved = true;
|
||||||
@ -216,6 +218,7 @@ var Promise = (function PromiseClosure() {
|
|||||||
this._data = EMPTY_PROMISE;
|
this._data = EMPTY_PROMISE;
|
||||||
}
|
}
|
||||||
this.callbacks = [];
|
this.callbacks = [];
|
||||||
|
this.errbacks = [];
|
||||||
};
|
};
|
||||||
/**
|
/**
|
||||||
* Builds a promise that is resolved when all the passed in promises are
|
* Builds a promise that is resolved when all the passed in promises are
|
||||||
@ -282,9 +285,12 @@ var Promise = (function PromiseClosure() {
|
|||||||
if (this.isResolved) {
|
if (this.isResolved) {
|
||||||
throw 'A Promise can be resolved only once ' + this.name;
|
throw 'A Promise can be resolved only once ' + this.name;
|
||||||
}
|
}
|
||||||
|
if (this.isRejected) {
|
||||||
|
throw 'The Promise was already rejected ' + this.name;
|
||||||
|
}
|
||||||
|
|
||||||
this.isResolved = true;
|
this.isResolved = true;
|
||||||
this.data = data;
|
this.data = data || null;
|
||||||
var callbacks = this.callbacks;
|
var callbacks = this.callbacks;
|
||||||
|
|
||||||
for (var i = 0, ii = callbacks.length; i < ii; i++) {
|
for (var i = 0, ii = callbacks.length; i < ii; i++) {
|
||||||
@ -292,7 +298,24 @@ var Promise = (function PromiseClosure() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
then: function promiseThen(callback) {
|
reject: function proimseReject(reason) {
|
||||||
|
if (this.isRejected) {
|
||||||
|
throw 'A Promise can be rejected only once ' + this.name;
|
||||||
|
}
|
||||||
|
if (this.isResolved) {
|
||||||
|
throw 'The Promise was already resolved ' + this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.isRejected = true;
|
||||||
|
this.error = reason || null;
|
||||||
|
var errbacks = this.errbacks;
|
||||||
|
|
||||||
|
for (var i = 0, ii = errbacks.length; i < ii; i++) {
|
||||||
|
errbacks[i].call(null, reason);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
then: function promiseThen(callback, errback) {
|
||||||
if (!callback) {
|
if (!callback) {
|
||||||
throw 'Requiring callback' + this.name;
|
throw 'Requiring callback' + this.name;
|
||||||
}
|
}
|
||||||
@ -301,8 +324,13 @@ var Promise = (function PromiseClosure() {
|
|||||||
if (this.isResolved) {
|
if (this.isResolved) {
|
||||||
var data = this.data;
|
var data = this.data;
|
||||||
callback.call(null, data);
|
callback.call(null, data);
|
||||||
|
} else if (this.isRejected && errorback) {
|
||||||
|
var error = this.error;
|
||||||
|
errback.call(null, error);
|
||||||
} else {
|
} else {
|
||||||
this.callbacks.push(callback);
|
this.callbacks.push(callback);
|
||||||
|
if (errback)
|
||||||
|
this.errbacks.push(errback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -165,9 +165,14 @@ function nextPage(task, loadError) {
|
|||||||
canvas.height = pageHeight * pdfToCssUnitsCoef;
|
canvas.height = pageHeight * pdfToCssUnitsCoef;
|
||||||
clear(ctx);
|
clear(ctx);
|
||||||
|
|
||||||
// using non-attached to the document div to test
|
// using the text layer builder that does nothing to test
|
||||||
// text layer creation operations
|
// text layer creation operations
|
||||||
var textLayer = document.createElement('div');
|
var textLayerBuilder = {
|
||||||
|
beginLayout: function nullTextLayerBuilderBeginLayout() {},
|
||||||
|
endLayout: function nullTextLayerBuilderEndLayout() {},
|
||||||
|
appendText: function nullTextLayerBuilderAppendText(text, fontName,
|
||||||
|
fontSize) {}
|
||||||
|
};
|
||||||
|
|
||||||
page.startRendering(
|
page.startRendering(
|
||||||
ctx,
|
ctx,
|
||||||
@ -177,7 +182,7 @@ function nextPage(task, loadError) {
|
|||||||
failureMessage = 'render : ' + error.message;
|
failureMessage = 'render : ' + error.message;
|
||||||
snapshotCurrentPage(task, failureMessage);
|
snapshotCurrentPage(task, failureMessage);
|
||||||
},
|
},
|
||||||
textLayer
|
textLayerBuilder
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
failure = 'page setup : ' + e.toString();
|
failure = 'page setup : ' + e.toString();
|
||||||
|
1
test/pdfs/.gitignore
vendored
1
test/pdfs/.gitignore
vendored
@ -21,3 +21,4 @@
|
|||||||
!freeculture.pdf
|
!freeculture.pdf
|
||||||
!issue918.pdf
|
!issue918.pdf
|
||||||
!smaskdim.pdf
|
!smaskdim.pdf
|
||||||
|
!type4psfunc.pdf
|
||||||
|
1
test/pdfs/issue1001.pdf.link
Normal file
1
test/pdfs/issue1001.pdf.link
Normal file
@ -0,0 +1 @@
|
|||||||
|
http://www.myhillsapartment.com/island_club/floorplans/images/links/Island_IC_brochure.pdf
|
1
test/pdfs/issue1015.pdf.link
Normal file
1
test/pdfs/issue1015.pdf.link
Normal file
@ -0,0 +1 @@
|
|||||||
|
http://faculty.washington.edu/fidelr/RayaPubs/TheCaseStudyMethod.pdf
|
1
test/pdfs/ocs.pdf.link
Normal file
1
test/pdfs/ocs.pdf.link
Normal file
@ -0,0 +1 @@
|
|||||||
|
http://www.unibuc.ro/uploads_en/29535/10/Cyrillic_Alphabets-Chars.pdf
|
BIN
test/pdfs/type4psfunc.pdf
Executable file
BIN
test/pdfs/type4psfunc.pdf
Executable file
Binary file not shown.
@ -17,12 +17,13 @@
|
|||||||
"rounds": 1,
|
"rounds": 1,
|
||||||
"type": "load"
|
"type": "load"
|
||||||
},
|
},
|
||||||
{ "id": "intelisa-load",
|
{ "id": "intelisa-eq",
|
||||||
"file": "pdfs/intelisa.pdf",
|
"file": "pdfs/intelisa.pdf",
|
||||||
"md5": "f5712097d29287a97f1278839814f682",
|
"md5": "f5712097d29287a97f1278839814f682",
|
||||||
"link": true,
|
"link": true,
|
||||||
|
"pageLimit": 100,
|
||||||
"rounds": 1,
|
"rounds": 1,
|
||||||
"type": "load"
|
"type": "eq"
|
||||||
},
|
},
|
||||||
{ "id": "pdfspec-load",
|
{ "id": "pdfspec-load",
|
||||||
"file": "pdfs/pdf.pdf",
|
"file": "pdfs/pdf.pdf",
|
||||||
@ -355,6 +356,13 @@
|
|||||||
"rounds": 1,
|
"rounds": 1,
|
||||||
"type": "eq"
|
"type": "eq"
|
||||||
},
|
},
|
||||||
|
{ "id": "issue1001",
|
||||||
|
"file": "pdfs/issue1001.pdf",
|
||||||
|
"md5": "0f1496e80a82a923e91d9e74c55ad94e",
|
||||||
|
"rounds": 1,
|
||||||
|
"link": true,
|
||||||
|
"type": "eq"
|
||||||
|
},
|
||||||
{ "id": "aboutstacks",
|
{ "id": "aboutstacks",
|
||||||
"file": "pdfs/aboutstacks.pdf",
|
"file": "pdfs/aboutstacks.pdf",
|
||||||
"md5": "6e7c8416a293ba2d83bc8dd20c6ccf51",
|
"md5": "6e7c8416a293ba2d83bc8dd20c6ccf51",
|
||||||
@ -367,5 +375,25 @@
|
|||||||
"md5": "de80aeca7cbf79940189fd34d59671ee",
|
"md5": "de80aeca7cbf79940189fd34d59671ee",
|
||||||
"rounds": 1,
|
"rounds": 1,
|
||||||
"type": "eq"
|
"type": "eq"
|
||||||
|
},
|
||||||
|
{ "id": "type4psfunc",
|
||||||
|
"file": "pdfs/type4psfunc.pdf",
|
||||||
|
"md5": "7e6027a02ff78577f74dccdf84e37189",
|
||||||
|
"rounds": 1,
|
||||||
|
"type": "eq"
|
||||||
|
},
|
||||||
|
{ "id": "ocs",
|
||||||
|
"file": "pdfs/ocs.pdf",
|
||||||
|
"md5": "2ade57e954ae7632749cf328daeaa7a8",
|
||||||
|
"rounds": 1,
|
||||||
|
"link": true,
|
||||||
|
"type": "load"
|
||||||
|
},
|
||||||
|
{ "id": "issue1015",
|
||||||
|
"file": "pdfs/issue1015.pdf",
|
||||||
|
"md5": "b61503d1b445742b665212866afb60e2",
|
||||||
|
"rounds": 1,
|
||||||
|
"link": true,
|
||||||
|
"type": "eq"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
225
test/unit/function_spec.js
Normal file
225
test/unit/function_spec.js
Normal file
@ -0,0 +1,225 @@
|
|||||||
|
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
describe('function', function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
this.addMatchers({
|
||||||
|
toMatchArray: function(expected) {
|
||||||
|
var actual = this.actual;
|
||||||
|
if (actual.length != expected.length)
|
||||||
|
return false;
|
||||||
|
for (var i = 0; i < expected.length; i++) {
|
||||||
|
var a = actual[i], b = expected[i];
|
||||||
|
if (isArray(b)) {
|
||||||
|
if (a.length != b.length)
|
||||||
|
return false;
|
||||||
|
for (var j = 0; j < a.length; j++) {
|
||||||
|
var suba = a[j], subb = b[j];
|
||||||
|
if (suba !== subb)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (a !== b)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('PostScriptParser', function() {
|
||||||
|
function parse(program) {
|
||||||
|
var stream = new StringStream(program);
|
||||||
|
var parser = new PostScriptParser(new PostScriptLexer(stream));
|
||||||
|
return parser.parse();
|
||||||
|
}
|
||||||
|
it('parses empty programs', function() {
|
||||||
|
var output = parse('{}');
|
||||||
|
expect(output.length).toEqual(0);
|
||||||
|
});
|
||||||
|
it('parses positive numbers', function() {
|
||||||
|
var number = 999;
|
||||||
|
var program = parse('{ ' + number + ' }');
|
||||||
|
var expectedProgram = [number];
|
||||||
|
expect(program).toMatchArray(expectedProgram);
|
||||||
|
});
|
||||||
|
it('parses negative numbers', function() {
|
||||||
|
var number = -999;
|
||||||
|
var program = parse('{ ' + number + ' }');
|
||||||
|
var expectedProgram = [number];
|
||||||
|
expect(program).toMatchArray(expectedProgram);
|
||||||
|
});
|
||||||
|
it('parses negative floats', function() {
|
||||||
|
var number = 3.3;
|
||||||
|
var program = parse('{ ' + number + ' }');
|
||||||
|
var expectedProgram = [number];
|
||||||
|
expect(program).toMatchArray(expectedProgram);
|
||||||
|
});
|
||||||
|
it('parses operators', function() {
|
||||||
|
var program = parse('{ sub }');
|
||||||
|
var expectedProgram = ['sub'];
|
||||||
|
expect(program).toMatchArray(expectedProgram);
|
||||||
|
});
|
||||||
|
it('parses if statements', function() {
|
||||||
|
var program = parse('{ { 99 } if }');
|
||||||
|
var expectedProgram = [3, 'jz', 99];
|
||||||
|
expect(program).toMatchArray(expectedProgram);
|
||||||
|
});
|
||||||
|
it('parses ifelse statements', function() {
|
||||||
|
var program = parse('{ { 99 } { 44 } ifelse }');
|
||||||
|
var expectedProgram = [5, 'jz', 99, 6, 'j', 44];
|
||||||
|
expect(program).toMatchArray(expectedProgram);
|
||||||
|
});
|
||||||
|
it('handles missing brackets', function() {
|
||||||
|
expect(function() { parse('{'); }).toThrow(
|
||||||
|
new Error('Unexpected symbol: found undefined expected 1.'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('PostScriptEvaluator', function() {
|
||||||
|
function evaluate(program) {
|
||||||
|
var stream = new StringStream(program);
|
||||||
|
var parser = new PostScriptParser(new PostScriptLexer(stream));
|
||||||
|
var code = parser.parse();
|
||||||
|
var evaluator = new PostScriptEvaluator(code);
|
||||||
|
var output = evaluator.execute();
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
it('pushes stack', function() {
|
||||||
|
var stack = evaluate('{ 99 }');
|
||||||
|
var expectedStack = [99];
|
||||||
|
expect(stack).toMatchArray(expectedStack);
|
||||||
|
});
|
||||||
|
it('handles if with true', function() {
|
||||||
|
var stack = evaluate('{ 1 {99} if }');
|
||||||
|
var expectedStack = [99];
|
||||||
|
expect(stack).toMatchArray(expectedStack);
|
||||||
|
});
|
||||||
|
it('handles if with false', function() {
|
||||||
|
var stack = evaluate('{ 0 {99} if }');
|
||||||
|
var expectedStack = [];
|
||||||
|
expect(stack).toMatchArray(expectedStack);
|
||||||
|
});
|
||||||
|
it('handles ifelse with true', function() {
|
||||||
|
var stack = evaluate('{ 1 {99} {77} ifelse }');
|
||||||
|
var expectedStack = [99];
|
||||||
|
expect(stack).toMatchArray(expectedStack);
|
||||||
|
});
|
||||||
|
it('handles ifelse with false', function() {
|
||||||
|
var stack = evaluate('{ 0 {99} {77} ifelse }');
|
||||||
|
var expectedStack = [77];
|
||||||
|
expect(stack).toMatchArray(expectedStack);
|
||||||
|
});
|
||||||
|
it('handles nested if', function() {
|
||||||
|
var stack = evaluate('{ 1 {1 {77} if} if }');
|
||||||
|
var expectedStack = [77];
|
||||||
|
expect(stack).toMatchArray(expectedStack);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('abs', function() {
|
||||||
|
var stack = evaluate('{ -2 abs }');
|
||||||
|
var expectedStack = [2];
|
||||||
|
expect(stack).toMatchArray(expectedStack);
|
||||||
|
});
|
||||||
|
it('adds', function() {
|
||||||
|
var stack = evaluate('{ 1 2 add }');
|
||||||
|
var expectedStack = [3];
|
||||||
|
expect(stack).toMatchArray(expectedStack);
|
||||||
|
});
|
||||||
|
it('boolean ands', function() {
|
||||||
|
var stack = evaluate('{ true false and }');
|
||||||
|
var expectedStack = [false];
|
||||||
|
expect(stack).toMatchArray(expectedStack);
|
||||||
|
});
|
||||||
|
it('bitwise ands', function() {
|
||||||
|
var stack = evaluate('{ 254 1 and }');
|
||||||
|
var expectedStack = [254 & 1];
|
||||||
|
expect(stack).toMatchArray(expectedStack);
|
||||||
|
});
|
||||||
|
// TODO atan
|
||||||
|
// TODO bitshift
|
||||||
|
// TODO ceiling
|
||||||
|
// TODO copy
|
||||||
|
// TODO cos
|
||||||
|
it('converts to int', function() {
|
||||||
|
var stack = evaluate('{ 9.9 cvi }');
|
||||||
|
var expectedStack = [9];
|
||||||
|
expect(stack).toMatchArray(expectedStack);
|
||||||
|
});
|
||||||
|
it('converts negatives to int', function() {
|
||||||
|
var stack = evaluate('{ -9.9 cvi }');
|
||||||
|
var expectedStack = [-9];
|
||||||
|
expect(stack).toMatchArray(expectedStack);
|
||||||
|
});
|
||||||
|
// TODO cvr
|
||||||
|
// TODO div
|
||||||
|
it('duplicates', function() {
|
||||||
|
var stack = evaluate('{ 99 dup }');
|
||||||
|
var expectedStack = [99, 99];
|
||||||
|
expect(stack).toMatchArray(expectedStack);
|
||||||
|
});
|
||||||
|
// TODO eq
|
||||||
|
it('exchanges', function() {
|
||||||
|
var stack = evaluate('{ 44 99 exch }');
|
||||||
|
var expectedStack = [99, 44];
|
||||||
|
expect(stack).toMatchArray(expectedStack);
|
||||||
|
});
|
||||||
|
// TODO exp
|
||||||
|
// TODO false
|
||||||
|
// TODO floor
|
||||||
|
// TODO ge
|
||||||
|
// TODO gt
|
||||||
|
it('divides to integer', function() {
|
||||||
|
var stack = evaluate('{ 2 3 idiv }');
|
||||||
|
var expectedStack = [0];
|
||||||
|
expect(stack).toMatchArray(expectedStack);
|
||||||
|
});
|
||||||
|
it('divides to negative integer', function() {
|
||||||
|
var stack = evaluate('{ -2 3 idiv }');
|
||||||
|
var expectedStack = [0];
|
||||||
|
expect(stack).toMatchArray(expectedStack);
|
||||||
|
});
|
||||||
|
it('duplicates index', function() {
|
||||||
|
var stack = evaluate('{ 4 3 2 1 2 index }');
|
||||||
|
var expectedStack = [4, 3, 2, 1, 3];
|
||||||
|
expect(stack).toMatchArray(expectedStack);
|
||||||
|
});
|
||||||
|
// TODO le
|
||||||
|
// TODO ln
|
||||||
|
// TODO log
|
||||||
|
// TODO lt
|
||||||
|
// TODO mod
|
||||||
|
// TODO mul
|
||||||
|
// TODO ne
|
||||||
|
// TODO neg
|
||||||
|
// TODO not
|
||||||
|
// TODO or
|
||||||
|
it('pops stack', function() {
|
||||||
|
var stack = evaluate('{ 1 2 pop }');
|
||||||
|
var expectedStack = [1];
|
||||||
|
expect(stack).toMatchArray(expectedStack);
|
||||||
|
});
|
||||||
|
it('rolls stack right', function() {
|
||||||
|
var stack = evaluate('{ 1 3 2 2 4 1 roll }');
|
||||||
|
var expectedStack = [2, 1, 3, 2];
|
||||||
|
expect(stack).toMatchArray(expectedStack);
|
||||||
|
});
|
||||||
|
it('rolls stack left', function() {
|
||||||
|
var stack = evaluate('{ 1 3 2 2 4 -1 roll }');
|
||||||
|
var expectedStack = [3, 2, 2, 1];
|
||||||
|
expect(stack).toMatchArray(expectedStack);
|
||||||
|
});
|
||||||
|
// TODO round
|
||||||
|
// TODO sin
|
||||||
|
// TODO sqrt
|
||||||
|
// TODO sub
|
||||||
|
// TODO true
|
||||||
|
// TODO truncate
|
||||||
|
// TODO xor
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -3,14 +3,129 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
describe("obj", function() {
|
describe('obj', function() {
|
||||||
|
|
||||||
describe("Name", function() {
|
describe('Name', function() {
|
||||||
it("should retain the given name", function() {
|
it('should retain the given name', function() {
|
||||||
var givenName = "Font";
|
var givenName = 'Font';
|
||||||
var name = new Name(givenName);
|
var name = new Name(givenName);
|
||||||
expect(name.name).toEqual(givenName);
|
expect(name.name).toEqual(givenName);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('Cmd', function() {
|
||||||
|
it('should retain the given cmd name', function() {
|
||||||
|
var givenCmd = 'BT';
|
||||||
|
var cmd = new Cmd(givenCmd);
|
||||||
|
expect(cmd.cmd).toEqual(givenCmd);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should create only one object for a command and cache it', function() {
|
||||||
|
var firstBT = Cmd.get('BT');
|
||||||
|
var secondBT = Cmd.get('BT');
|
||||||
|
var firstET = Cmd.get('ET');
|
||||||
|
var secondET = Cmd.get('ET');
|
||||||
|
expect(firstBT).toBe(secondBT);
|
||||||
|
expect(firstET).toBe(secondET);
|
||||||
|
expect(firstBT).not.toBe(firstET);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Dict', function() {
|
||||||
|
var checkInvalidHasValues = function(dict) {
|
||||||
|
expect(dict.has()).toBeFalsy();
|
||||||
|
expect(dict.has('Prev')).toBeFalsy();
|
||||||
|
};
|
||||||
|
|
||||||
|
var checkInvalidKeyValues = function(dict) {
|
||||||
|
expect(dict.get()).toBeUndefined();
|
||||||
|
expect(dict.get('Prev')).toBeUndefined();
|
||||||
|
expect(dict.get('Decode', 'D')).toBeUndefined();
|
||||||
|
|
||||||
|
// Note that the getter with three arguments breaks the pattern here.
|
||||||
|
expect(dict.get('FontFile', 'FontFile2', 'FontFile3')).toBeNull();
|
||||||
|
};
|
||||||
|
|
||||||
|
var emptyDict, dictWithSizeKey, dictWithManyKeys;
|
||||||
|
var storedSize = 42;
|
||||||
|
var testFontFile = 'file1';
|
||||||
|
var testFontFile2 = 'file2';
|
||||||
|
var testFontFile3 = 'file3';
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
emptyDict = new Dict();
|
||||||
|
|
||||||
|
dictWithSizeKey = new Dict();
|
||||||
|
dictWithSizeKey.set('Size', storedSize);
|
||||||
|
|
||||||
|
dictWithManyKeys = new Dict();
|
||||||
|
dictWithManyKeys.set('FontFile', testFontFile);
|
||||||
|
dictWithManyKeys.set('FontFile2', testFontFile2);
|
||||||
|
dictWithManyKeys.set('FontFile3', testFontFile3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return invalid values for unknown keys', function() {
|
||||||
|
checkInvalidHasValues(emptyDict);
|
||||||
|
checkInvalidKeyValues(emptyDict);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return correct value for stored Size key', function() {
|
||||||
|
expect(dictWithSizeKey.has('Size')).toBeTruthy();
|
||||||
|
|
||||||
|
expect(dictWithSizeKey.get('Size')).toEqual(storedSize);
|
||||||
|
expect(dictWithSizeKey.get('Prev', 'Size')).toEqual(storedSize);
|
||||||
|
expect(dictWithSizeKey.get('Prev', 'Root', 'Size')).toEqual(storedSize);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return invalid values for unknown keys when Size key is stored',
|
||||||
|
function() {
|
||||||
|
checkInvalidHasValues(dictWithSizeKey);
|
||||||
|
checkInvalidKeyValues(dictWithSizeKey);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return correct value for stored Size key with undefined value',
|
||||||
|
function() {
|
||||||
|
var dict = new Dict();
|
||||||
|
dict.set('Size');
|
||||||
|
|
||||||
|
expect(dict.has('Size')).toBeTruthy();
|
||||||
|
|
||||||
|
checkInvalidKeyValues(dict);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return correct values for multiple stored keys', function() {
|
||||||
|
expect(dictWithManyKeys.has('FontFile')).toBeTruthy();
|
||||||
|
expect(dictWithManyKeys.has('FontFile2')).toBeTruthy();
|
||||||
|
expect(dictWithManyKeys.has('FontFile3')).toBeTruthy();
|
||||||
|
|
||||||
|
expect(dictWithManyKeys.get('FontFile3')).toEqual(testFontFile3);
|
||||||
|
expect(dictWithManyKeys.get('FontFile2', 'FontFile3'))
|
||||||
|
.toEqual(testFontFile2);
|
||||||
|
expect(dictWithManyKeys.get('FontFile', 'FontFile2', 'FontFile3'))
|
||||||
|
.toEqual(testFontFile);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should callback for each stored key', function() {
|
||||||
|
var callbackSpy = jasmine.createSpy('spy on callback in dictionary');
|
||||||
|
|
||||||
|
dictWithManyKeys.forEach(callbackSpy);
|
||||||
|
|
||||||
|
expect(callbackSpy).wasCalled();
|
||||||
|
expect(callbackSpy.argsForCall[0]).toEqual(['FontFile', testFontFile]);
|
||||||
|
expect(callbackSpy.argsForCall[1]).toEqual(['FontFile2', testFontFile2]);
|
||||||
|
expect(callbackSpy.argsForCall[2]).toEqual(['FontFile3', testFontFile3]);
|
||||||
|
expect(callbackSpy.callCount).toEqual(3);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Ref', function() {
|
||||||
|
it('should retain the stored values', function() {
|
||||||
|
var storedNum = 4;
|
||||||
|
var storedGen = 2;
|
||||||
|
var ref = new Ref(storedNum, storedGen);
|
||||||
|
expect(ref.num).toEqual(storedNum);
|
||||||
|
expect(ref.gen).toEqual(storedGen);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -11,9 +11,28 @@
|
|||||||
|
|
||||||
<!-- include spec files here... -->
|
<!-- include spec files here... -->
|
||||||
<script type="text/javascript" src="obj_spec.js"></script>
|
<script type="text/javascript" src="obj_spec.js"></script>
|
||||||
|
<script type="text/javascript" src="function_spec.js"></script>
|
||||||
|
|
||||||
<!-- include source files here... -->
|
<!-- include source files here... -->
|
||||||
|
<script type="text/javascript" src="../../src/core.js"></script>
|
||||||
|
<script type="text/javascript" src="../../src/util.js"></script>
|
||||||
|
<script type="text/javascript" src="../../src/canvas.js"></script>
|
||||||
<script type="text/javascript" src="../../src/obj.js"></script>
|
<script type="text/javascript" src="../../src/obj.js"></script>
|
||||||
|
<script type="text/javascript" src="../../src/function.js"></script>
|
||||||
|
<script type="text/javascript" src="../../src/charsets.js"></script>
|
||||||
|
<script type="text/javascript" src="../../src/cidmaps.js"></script>
|
||||||
|
<script type="text/javascript" src="../../src/colorspace.js"></script>
|
||||||
|
<script type="text/javascript" src="../../src/crypto.js"></script>
|
||||||
|
<script type="text/javascript" src="../../src/evaluator.js"></script>
|
||||||
|
<script type="text/javascript" src="../../src/fonts.js"></script>
|
||||||
|
<script type="text/javascript" src="../../src/glyphlist.js"></script>
|
||||||
|
<script type="text/javascript" src="../../src/image.js"></script>
|
||||||
|
<script type="text/javascript" src="../../src/metrics.js"></script>
|
||||||
|
<script type="text/javascript" src="../../src/parser.js"></script>
|
||||||
|
<script type="text/javascript" src="../../src/pattern.js"></script>
|
||||||
|
<script type="text/javascript" src="../../src/stream.js"></script>
|
||||||
|
<script type="text/javascript" src="../../src/worker.js"></script>
|
||||||
|
<script type="text/javascript" src="../../external/jpgjs/jpg.js"></script>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
'use strict';
|
'use strict';
|
||||||
|
@ -5,11 +5,16 @@
|
|||||||
|
|
||||||
// Checking if the typed arrays are supported
|
// Checking if the typed arrays are supported
|
||||||
(function checkTypedArrayCompatibility() {
|
(function checkTypedArrayCompatibility() {
|
||||||
if (typeof Uint8Array !== 'undefined')
|
if (typeof Uint8Array !== 'undefined') {
|
||||||
|
// some mobile version might not support Float64Array
|
||||||
|
if (typeof Float64Array === 'undefined')
|
||||||
|
window.Float64Array = Float32Array;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
function subarray(start, end) {
|
function subarray(start, end) {
|
||||||
return this.slice(start, end);
|
return new TypedArray(this.slice(start, end));
|
||||||
}
|
}
|
||||||
|
|
||||||
function setArrayOffset(array, offset) {
|
function setArrayOffset(array, offset) {
|
||||||
@ -46,6 +51,8 @@
|
|||||||
window.Uint32Array = TypedArray;
|
window.Uint32Array = TypedArray;
|
||||||
window.Int32Array = TypedArray;
|
window.Int32Array = TypedArray;
|
||||||
window.Uint16Array = TypedArray;
|
window.Uint16Array = TypedArray;
|
||||||
|
window.Float32Array = TypedArray;
|
||||||
|
window.Float64Array = TypedArray;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// Object.create() ?
|
// Object.create() ?
|
||||||
|
@ -15,6 +15,7 @@ body {
|
|||||||
/* === Toolbar === */
|
/* === Toolbar === */
|
||||||
#controls {
|
#controls {
|
||||||
background-color: #eee;
|
background-color: #eee;
|
||||||
|
background: -o-linear-gradient(bottom,#eee 0%,#fff 100%);
|
||||||
background: -moz-linear-gradient(center bottom, #eee 0%, #fff 100%);
|
background: -moz-linear-gradient(center bottom, #eee 0%, #fff 100%);
|
||||||
background: -webkit-gradient(linear, left bottom, left top, color-stop(0.0, #ddd), color-stop(1.0, #fff));
|
background: -webkit-gradient(linear, left bottom, left top, color-stop(0.0, #ddd), color-stop(1.0, #fff));
|
||||||
border-bottom: 1px solid #666;
|
border-bottom: 1px solid #666;
|
||||||
@ -82,6 +83,7 @@ span#info {
|
|||||||
bottom: 18px;
|
bottom: 18px;
|
||||||
left: -290px;
|
left: -290px;
|
||||||
transition: left 0.25s ease-in-out 1s;
|
transition: left 0.25s ease-in-out 1s;
|
||||||
|
-o-transition: left 0.25s ease-in-out 1s;
|
||||||
-moz-transition: left 0.25s ease-in-out 1s;
|
-moz-transition: left 0.25s ease-in-out 1s;
|
||||||
-webkit-transition: left 0.25s ease-in-out 1s;
|
-webkit-transition: left 0.25s ease-in-out 1s;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
@ -90,6 +92,7 @@ span#info {
|
|||||||
#sidebar:hover {
|
#sidebar:hover {
|
||||||
left: 0px;
|
left: 0px;
|
||||||
transition: left 0.25s ease-in-out 0s;
|
transition: left 0.25s ease-in-out 0s;
|
||||||
|
-o-transition: left 0.25s ease-in-out 0s;
|
||||||
-moz-transition: left 0.25s ease-in-out 0s;
|
-moz-transition: left 0.25s ease-in-out 0s;
|
||||||
-webkit-transition: left 0.25s ease-in-out 0s;
|
-webkit-transition: left 0.25s ease-in-out 0s;
|
||||||
}
|
}
|
||||||
@ -327,7 +330,7 @@ canvas {
|
|||||||
color: black;
|
color: black;
|
||||||
padding: 3px;
|
padding: 3px;
|
||||||
margin: 3px;
|
margin: 3px;
|
||||||
white-space: pre;
|
width: 98%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.clearBoth {
|
.clearBoth {
|
||||||
|
@ -67,10 +67,11 @@
|
|||||||
<option value="0.75">75%</option>
|
<option value="0.75">75%</option>
|
||||||
<option value="1">100%</option>
|
<option value="1">100%</option>
|
||||||
<option value="1.25">125%</option>
|
<option value="1.25">125%</option>
|
||||||
<option value="1.5" selected="selected">150%</option>
|
<option value="1.5">150%</option>
|
||||||
<option value="2">200%</option>
|
<option value="2">200%</option>
|
||||||
<option id="pageWidthOption" value="page-width">Page Width</option>
|
<option id="pageWidthOption" value="page-width">Page Width</option>
|
||||||
<option id="pageFitOption" value="page-fit">Page Fit</option>
|
<option id="pageFitOption" value="page-fit">Page Fit</option>
|
||||||
|
<option id="pageAutoOption" value="auto" selected="selected">Auto</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<div class="separator"></div>
|
<div class="separator"></div>
|
||||||
@ -113,7 +114,7 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="clearBoth"></div>
|
<div class="clearBoth"></div>
|
||||||
<div id="errorMoreInfo" hidden='true'></div>
|
<textarea id="errorMoreInfo" hidden='true' readonly="readonly"></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="sidebar">
|
<div id="sidebar">
|
||||||
|
337
web/viewer.js
337
web/viewer.js
@ -4,7 +4,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var kDefaultURL = 'compressed.tracemonkey-pldi-09.pdf';
|
var kDefaultURL = 'compressed.tracemonkey-pldi-09.pdf';
|
||||||
var kDefaultScale = 1.5;
|
var kDefaultScale = 'auto';
|
||||||
var kDefaultScaleDelta = 1.1;
|
var kDefaultScaleDelta = 1.1;
|
||||||
var kCacheSize = 20;
|
var kCacheSize = 20;
|
||||||
var kCssUnits = 96.0 / 72.0;
|
var kCssUnits = 96.0 / 72.0;
|
||||||
@ -12,6 +12,7 @@ var kScrollbarPadding = 40;
|
|||||||
var kMinScale = 0.25;
|
var kMinScale = 0.25;
|
||||||
var kMaxScale = 4.0;
|
var kMaxScale = 4.0;
|
||||||
var kImageDirectory = './images/';
|
var kImageDirectory = './images/';
|
||||||
|
var kSettingsMemory = 20;
|
||||||
|
|
||||||
var Cache = function cacheCache(size) {
|
var Cache = function cacheCache(size) {
|
||||||
var data = [];
|
var data = [];
|
||||||
@ -25,16 +26,136 @@ var Cache = function cacheCache(size) {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var RenderingQueue = (function RenderingQueueClosure() {
|
||||||
|
function RenderingQueue() {
|
||||||
|
this.items = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderingQueue.prototype = {
|
||||||
|
enqueueDraw: function RenderingQueueEnqueueDraw(item) {
|
||||||
|
if (!item.drawingRequired())
|
||||||
|
return; // as no redraw required, no need for queueing.
|
||||||
|
|
||||||
|
if ('rendering' in item)
|
||||||
|
return; // is already in the queue
|
||||||
|
|
||||||
|
item.rendering = true;
|
||||||
|
this.items.push(item);
|
||||||
|
if (this.items.length > 1)
|
||||||
|
return; // not first item
|
||||||
|
|
||||||
|
item.draw(this.continueExecution.bind(this));
|
||||||
|
},
|
||||||
|
continueExecution: function RenderingQueueContinueExecution() {
|
||||||
|
var item = this.items.shift();
|
||||||
|
delete item.rendering;
|
||||||
|
|
||||||
|
if (this.items.length == 0)
|
||||||
|
return; // queue is empty
|
||||||
|
|
||||||
|
item = this.items[0];
|
||||||
|
item.draw(this.continueExecution.bind(this));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return RenderingQueue;
|
||||||
|
})();
|
||||||
|
|
||||||
|
// Settings Manager - This is a utility for saving settings
|
||||||
|
// First we see if localStorage is available, FF bug #495747
|
||||||
|
// If not, we use FUEL in FF
|
||||||
|
var Settings = (function SettingsClosure() {
|
||||||
|
var isLocalStorageEnabled = (function localStorageEnabledTest() {
|
||||||
|
try {
|
||||||
|
localStorage;
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
})();
|
||||||
|
var extPrefix = 'extensions.uriloader@pdf.js';
|
||||||
|
var isExtension = location.protocol == 'chrome:' && !isLocalStorageEnabled;
|
||||||
|
var inPrivateBrowsing = false;
|
||||||
|
if (isExtension) {
|
||||||
|
var pbs = Components.classes['@mozilla.org/privatebrowsing;1']
|
||||||
|
.getService(Components.interfaces.nsIPrivateBrowsingService);
|
||||||
|
inPrivateBrowsing = pbs.privateBrowsingEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Settings(fingerprint) {
|
||||||
|
var database = null;
|
||||||
|
var index;
|
||||||
|
if (inPrivateBrowsing)
|
||||||
|
return false;
|
||||||
|
else if (isExtension)
|
||||||
|
database = Application.prefs.getValue(extPrefix + '.database', '{}');
|
||||||
|
else if (isLocalStorageEnabled)
|
||||||
|
database = localStorage.getItem('database') || '{}';
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
|
||||||
|
database = JSON.parse(database);
|
||||||
|
if (!('files' in database))
|
||||||
|
database.files = [];
|
||||||
|
if (database.files.length >= kSettingsMemory)
|
||||||
|
database.files.shift();
|
||||||
|
for (var i = 0, length = database.files.length; i < length; i++) {
|
||||||
|
var branch = database.files[i];
|
||||||
|
if (branch.fingerprint == fingerprint) {
|
||||||
|
index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (typeof index != 'number')
|
||||||
|
index = database.files.push({fingerprint: fingerprint}) - 1;
|
||||||
|
this.file = database.files[index];
|
||||||
|
this.database = database;
|
||||||
|
if (isExtension)
|
||||||
|
Application.prefs.setValue(extPrefix + '.database',
|
||||||
|
JSON.stringify(database));
|
||||||
|
else if (isLocalStorageEnabled)
|
||||||
|
localStorage.setItem('database', JSON.stringify(database));
|
||||||
|
}
|
||||||
|
|
||||||
|
Settings.prototype = {
|
||||||
|
set: function settingsSet(name, val) {
|
||||||
|
if (inPrivateBrowsing)
|
||||||
|
return false;
|
||||||
|
var file = this.file;
|
||||||
|
file[name] = val;
|
||||||
|
if (isExtension)
|
||||||
|
Application.prefs.setValue(extPrefix + '.database',
|
||||||
|
JSON.stringify(this.database));
|
||||||
|
else if (isLocalStorageEnabled)
|
||||||
|
localStorage.setItem('database', JSON.stringify(this.database));
|
||||||
|
},
|
||||||
|
|
||||||
|
get: function settingsGet(name, defaultValue) {
|
||||||
|
if (inPrivateBrowsing)
|
||||||
|
return defaultValue;
|
||||||
|
else
|
||||||
|
return this.file[name] || defaultValue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return Settings;
|
||||||
|
})();
|
||||||
|
|
||||||
var cache = new Cache(kCacheSize);
|
var cache = new Cache(kCacheSize);
|
||||||
|
var renderingQueue = new RenderingQueue();
|
||||||
var currentPageNumber = 1;
|
var currentPageNumber = 1;
|
||||||
|
|
||||||
var PDFView = {
|
var PDFView = {
|
||||||
pages: [],
|
pages: [],
|
||||||
thumbnails: [],
|
thumbnails: [],
|
||||||
currentScale: kDefaultScale,
|
currentScale: 0,
|
||||||
|
currentScaleValue: null,
|
||||||
initialBookmark: document.location.hash.substring(1),
|
initialBookmark: document.location.hash.substring(1),
|
||||||
|
|
||||||
setScale: function pdfViewSetScale(val, resetAutoSettings) {
|
setScale: function pdfViewSetScale(val, resetAutoSettings) {
|
||||||
|
if (val == this.currentScale)
|
||||||
|
return;
|
||||||
|
|
||||||
var pages = this.pages;
|
var pages = this.pages;
|
||||||
for (var i = 0; i < pages.length; i++)
|
for (var i = 0; i < pages.length; i++)
|
||||||
pages[i].update(val * kCssUnits);
|
pages[i].update(val * kCssUnits);
|
||||||
@ -55,6 +176,7 @@ var PDFView = {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
var scale = parseFloat(value);
|
var scale = parseFloat(value);
|
||||||
|
this.currentScaleValue = value;
|
||||||
if (scale) {
|
if (scale) {
|
||||||
this.setScale(scale, true);
|
this.setScale(scale, true);
|
||||||
return;
|
return;
|
||||||
@ -73,6 +195,10 @@ var PDFView = {
|
|||||||
this.setScale(
|
this.setScale(
|
||||||
Math.min(pageWidthScale, pageHeightScale), resetAutoSettings);
|
Math.min(pageWidthScale, pageHeightScale), resetAutoSettings);
|
||||||
}
|
}
|
||||||
|
if ('auto' == value)
|
||||||
|
this.setScale(Math.min(1.0, pageWidthScale), resetAutoSettings);
|
||||||
|
|
||||||
|
selectScaleOption(value);
|
||||||
},
|
},
|
||||||
|
|
||||||
zoomIn: function pdfViewZoomIn() {
|
zoomIn: function pdfViewZoomIn() {
|
||||||
@ -223,13 +349,14 @@ var PDFView = {
|
|||||||
};
|
};
|
||||||
moreInfoButton.removeAttribute('hidden');
|
moreInfoButton.removeAttribute('hidden');
|
||||||
lessInfoButton.setAttribute('hidden', 'true');
|
lessInfoButton.setAttribute('hidden', 'true');
|
||||||
errorMoreInfo.innerHTML = 'PDF.JS Build: ' + PDFJS.build + '\n';
|
errorMoreInfo.value = 'PDF.JS Build: ' + PDFJS.build + '\n';
|
||||||
|
|
||||||
if (moreInfo) {
|
if (moreInfo) {
|
||||||
errorMoreInfo.innerHTML += 'Message: ' + moreInfo.message;
|
errorMoreInfo.value += 'Message: ' + moreInfo.message;
|
||||||
if (moreInfo.stack)
|
if (moreInfo.stack)
|
||||||
errorMoreInfo.innerHTML += '\n' + 'Stack: ' + moreInfo.stack;
|
errorMoreInfo.value += '\n' + 'Stack: ' + moreInfo.stack;
|
||||||
}
|
}
|
||||||
|
errorMoreInfo.rows = errorMoreInfo.value.split('\n').length - 1;
|
||||||
},
|
},
|
||||||
|
|
||||||
progress: function pdfViewProgress(level) {
|
progress: function pdfViewProgress(level) {
|
||||||
@ -243,6 +370,7 @@ var PDFView = {
|
|||||||
// when page is painted, using the image as thumbnail base
|
// when page is painted, using the image as thumbnail base
|
||||||
pageView.onAfterDraw = function pdfViewLoadOnAfterDraw() {
|
pageView.onAfterDraw = function pdfViewLoadOnAfterDraw() {
|
||||||
thumbnailView.setImage(pageView.canvas);
|
thumbnailView.setImage(pageView.canvas);
|
||||||
|
preDraw();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -272,8 +400,20 @@ var PDFView = {
|
|||||||
this.error('An error occurred while reading the PDF.', e);
|
this.error('An error occurred while reading the PDF.', e);
|
||||||
}
|
}
|
||||||
var pagesCount = pdf.numPages;
|
var pagesCount = pdf.numPages;
|
||||||
|
var id = pdf.fingerprint;
|
||||||
|
var storedHash = null;
|
||||||
document.getElementById('numPages').innerHTML = pagesCount;
|
document.getElementById('numPages').innerHTML = pagesCount;
|
||||||
document.getElementById('pageNumber').max = pagesCount;
|
document.getElementById('pageNumber').max = pagesCount;
|
||||||
|
PDFView.documentFingerprint = id;
|
||||||
|
var store = PDFView.store = new Settings(id);
|
||||||
|
if (store.get('exists', false)) {
|
||||||
|
var page = store.get('page', '1');
|
||||||
|
var zoom = store.get('zoom', PDFView.currentScale);
|
||||||
|
var left = store.get('scrollLeft', '0');
|
||||||
|
var top = store.get('scrollTop', '0');
|
||||||
|
|
||||||
|
storedHash = 'page=' + page + '&zoom=' + zoom + ',' + left + ',' + top;
|
||||||
|
}
|
||||||
|
|
||||||
var pages = this.pages = [];
|
var pages = this.pages = [];
|
||||||
var pagesRefMap = {};
|
var pagesRefMap = {};
|
||||||
@ -294,7 +434,6 @@ var PDFView = {
|
|||||||
|
|
||||||
this.pagesRefMap = pagesRefMap;
|
this.pagesRefMap = pagesRefMap;
|
||||||
this.destinations = pdf.catalog.destinations;
|
this.destinations = pdf.catalog.destinations;
|
||||||
this.setScale(scale || kDefaultScale, true);
|
|
||||||
|
|
||||||
if (pdf.catalog.documentOutline) {
|
if (pdf.catalog.documentOutline) {
|
||||||
this.outline = new DocumentOutlineView(pdf.catalog.documentOutline);
|
this.outline = new DocumentOutlineView(pdf.catalog.documentOutline);
|
||||||
@ -303,12 +442,20 @@ var PDFView = {
|
|||||||
this.switchSidebarView('outline');
|
this.switchSidebarView('outline');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset the current scale, as otherwise the page's scale might not get
|
||||||
|
// updated if the zoom level stayed the same.
|
||||||
|
this.currentScale = 0;
|
||||||
|
this.currentScaleValue = null;
|
||||||
if (this.initialBookmark) {
|
if (this.initialBookmark) {
|
||||||
this.setHash(this.initialBookmark);
|
this.setHash(this.initialBookmark);
|
||||||
this.initialBookmark = null;
|
this.initialBookmark = null;
|
||||||
}
|
}
|
||||||
else
|
else if (storedHash)
|
||||||
|
this.setHash(storedHash);
|
||||||
|
else {
|
||||||
|
this.parseScale(scale || kDefaultScale, true);
|
||||||
this.page = 1;
|
this.page = 1;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
setHash: function pdfViewSetHash(hash) {
|
setHash: function pdfViewSetHash(hash) {
|
||||||
@ -334,8 +481,16 @@ var PDFView = {
|
|||||||
if ('zoom' in params) {
|
if ('zoom' in params) {
|
||||||
var zoomArgs = params.zoom.split(','); // scale,left,top
|
var zoomArgs = params.zoom.split(','); // scale,left,top
|
||||||
// building destination array
|
// building destination array
|
||||||
var dest = [null, new Name('XYZ'), (zoomArgs[1] | 0),
|
|
||||||
(zoomArgs[2] | 0), (zoomArgs[0] | 0) / 100];
|
// If the zoom value, it has to get divided by 100. If it is a string,
|
||||||
|
// it should stay as it is.
|
||||||
|
var zoomArg = zoomArgs[0];
|
||||||
|
var zoomArgNumber = parseFloat(zoomArg);
|
||||||
|
if (zoomArgNumber)
|
||||||
|
zoomArg = zoomArgNumber / 100;
|
||||||
|
|
||||||
|
var dest = [null, {name: 'XYZ'}, (zoomArgs[1] | 0),
|
||||||
|
(zoomArgs[2] | 0), zoomArg];
|
||||||
var currentPage = this.pages[pageNumber - 1];
|
var currentPage = this.pages[pageNumber - 1];
|
||||||
currentPage.scrollIntoView(dest);
|
currentPage.scrollIntoView(dest);
|
||||||
} else
|
} else
|
||||||
@ -609,10 +764,15 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
|
|||||||
}, 0);
|
}, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.draw = function pageviewDraw() {
|
this.drawingRequired = function() {
|
||||||
if (div.hasChildNodes()) {
|
return !div.hasChildNodes();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.draw = function pageviewDraw(callback) {
|
||||||
|
if (!this.drawingRequired()) {
|
||||||
this.updateStats();
|
this.updateStats();
|
||||||
return false;
|
callback();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var canvas = document.createElement('canvas');
|
var canvas = document.createElement('canvas');
|
||||||
@ -644,13 +804,14 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
|
|||||||
this.updateStats();
|
this.updateStats();
|
||||||
if (this.onAfterDraw)
|
if (this.onAfterDraw)
|
||||||
this.onAfterDraw();
|
this.onAfterDraw();
|
||||||
}).bind(this), textLayer
|
|
||||||
|
cache.push(this);
|
||||||
|
callback();
|
||||||
|
}).bind(this), new TextLayerBuilder(textLayer)
|
||||||
);
|
);
|
||||||
|
|
||||||
setupAnnotations(this.content, this.scale);
|
setupAnnotations(this.content, this.scale);
|
||||||
div.setAttribute('data-loaded', true);
|
div.setAttribute('data-loaded', true);
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.updateStats = function pageViewUpdateStats() {
|
this.updateStats = function pageViewUpdateStats() {
|
||||||
@ -717,12 +878,16 @@ var ThumbnailView = function thumbnailView(container, page, id, pageRatio) {
|
|||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.draw = function thumbnailViewDraw() {
|
this.draw = function thumbnailViewDraw(callback) {
|
||||||
if (this.hasImage)
|
if (this.hasImage) {
|
||||||
|
callback();
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var ctx = getPageDrawContext();
|
var ctx = getPageDrawContext();
|
||||||
page.startRendering(ctx, function thumbnailViewDrawStartRendering() {});
|
page.startRendering(ctx, function thumbnailViewDrawStartRendering() {
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
|
||||||
this.hasImage = true;
|
this.hasImage = true;
|
||||||
};
|
};
|
||||||
@ -775,6 +940,53 @@ var DocumentOutlineView = function documentOutlineView(outline) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var TextLayerBuilder = function textLayerBuilder(textLayerDiv) {
|
||||||
|
this.textLayerDiv = textLayerDiv;
|
||||||
|
|
||||||
|
this.beginLayout = function textLayerBuilderBeginLayout() {
|
||||||
|
this.textDivs = [];
|
||||||
|
this.textLayerQueue = [];
|
||||||
|
};
|
||||||
|
|
||||||
|
this.endLayout = function textLayerBuilderEndLayout() {
|
||||||
|
var self = this;
|
||||||
|
var textDivs = this.textDivs;
|
||||||
|
var textLayerDiv = this.textLayerDiv;
|
||||||
|
this.textLayerTimer = setInterval(function renderTextLayer() {
|
||||||
|
if (textDivs.length === 0) {
|
||||||
|
clearInterval(self.textLayerTimer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var textDiv = textDivs.shift();
|
||||||
|
if (textDiv.dataset.textLength >= 1) { // avoid div by zero
|
||||||
|
textLayerDiv.appendChild(textDiv);
|
||||||
|
// Adjust div width (via letterSpacing) to match canvas text
|
||||||
|
// Due to the .offsetWidth calls, this is slow
|
||||||
|
textDiv.style.letterSpacing =
|
||||||
|
((textDiv.dataset.canvasWidth - textDiv.offsetWidth) /
|
||||||
|
(textDiv.dataset.textLength - 1)) + 'px';
|
||||||
|
}
|
||||||
|
}, 0);
|
||||||
|
};
|
||||||
|
|
||||||
|
this.appendText = function textLayerBuilderAppendText(text,
|
||||||
|
fontName, fontSize) {
|
||||||
|
var textDiv = document.createElement('div');
|
||||||
|
|
||||||
|
// vScale and hScale already contain the scaling to pixel units
|
||||||
|
var fontHeight = fontSize * text.geom.vScale;
|
||||||
|
textDiv.dataset.canvasWidth = text.canvasWidth * text.geom.hScale;
|
||||||
|
|
||||||
|
textDiv.style.fontSize = fontHeight + 'px';
|
||||||
|
textDiv.style.fontFamily = fontName || 'sans-serif';
|
||||||
|
textDiv.style.left = text.geom.x + 'px';
|
||||||
|
textDiv.style.top = (text.geom.y - fontHeight) + 'px';
|
||||||
|
textDiv.textContent = text.str;
|
||||||
|
textDiv.dataset.textLength = text.length;
|
||||||
|
this.textDivs.push(textDiv);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
window.addEventListener('load', function webViewerLoad(evt) {
|
window.addEventListener('load', function webViewerLoad(evt) {
|
||||||
var params = document.location.search.substring(1).split('&');
|
var params = document.location.search.substring(1).split('&');
|
||||||
for (var i = 0; i < params.length; i++) {
|
for (var i = 0; i < params.length; i++) {
|
||||||
@ -782,7 +994,7 @@ window.addEventListener('load', function webViewerLoad(evt) {
|
|||||||
params[unescape(param[0])] = unescape(param[1]);
|
params[unescape(param[0])] = unescape(param[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
var scale = ('scale' in params) ? params.scale : kDefaultScale;
|
var scale = ('scale' in params) ? params.scale : 0;
|
||||||
PDFView.open(params.file || kDefaultURL, parseFloat(scale));
|
PDFView.open(params.file || kDefaultURL, parseFloat(scale));
|
||||||
|
|
||||||
if (!window.File || !window.FileReader || !window.FileList || !window.Blob)
|
if (!window.File || !window.FileReader || !window.FileList || !window.Blob)
|
||||||
@ -801,31 +1013,76 @@ window.addEventListener('unload', function webViewerUnload(evt) {
|
|||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the next not yet visible page already such that it is
|
||||||
|
* hopefully ready once the user scrolls to it.
|
||||||
|
*/
|
||||||
|
function preDraw() {
|
||||||
|
var pages = PDFView.pages;
|
||||||
|
var visible = PDFView.getVisiblePages();
|
||||||
|
var last = visible[visible.length - 1];
|
||||||
|
// PageView.id is the actual page number, which is + 1 compared
|
||||||
|
// to the index in `pages`. That means, pages[last.id] is the next
|
||||||
|
// PageView instance.
|
||||||
|
if (pages[last.id] && pages[last.id].drawingRequired()) {
|
||||||
|
renderingQueue.enqueueDraw(pages[last.id]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// If there is nothing to draw on the next page, maybe the user
|
||||||
|
// is scrolling up, so, let's try to render the next page *before*
|
||||||
|
// the first visible page
|
||||||
|
if (pages[visible[0].id - 2]) {
|
||||||
|
renderingQueue.enqueueDraw(pages[visible[0].id - 2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function updateViewarea() {
|
function updateViewarea() {
|
||||||
var visiblePages = PDFView.getVisiblePages();
|
var visiblePages = PDFView.getVisiblePages();
|
||||||
|
var pageToDraw;
|
||||||
for (var i = 0; i < visiblePages.length; i++) {
|
for (var i = 0; i < visiblePages.length; i++) {
|
||||||
var page = visiblePages[i];
|
var page = visiblePages[i];
|
||||||
if (PDFView.pages[page.id - 1].draw())
|
var pageObj = PDFView.pages[page.id - 1];
|
||||||
cache.push(page.view);
|
|
||||||
|
pageToDraw |= pageObj.drawingRequired();
|
||||||
|
renderingQueue.enqueueDraw(pageObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!visiblePages.length)
|
if (!visiblePages.length)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// If there is no need to draw a page that is currenlty visible, preDraw the
|
||||||
|
// next page the user might scroll to.
|
||||||
|
if (!pageToDraw) {
|
||||||
|
preDraw();
|
||||||
|
}
|
||||||
|
|
||||||
updateViewarea.inProgress = true; // used in "set page"
|
updateViewarea.inProgress = true; // used in "set page"
|
||||||
var currentId = PDFView.page;
|
var currentId = PDFView.page;
|
||||||
var firstPage = visiblePages[0];
|
var firstPage = visiblePages[0];
|
||||||
PDFView.page = firstPage.id;
|
PDFView.page = firstPage.id;
|
||||||
updateViewarea.inProgress = false;
|
updateViewarea.inProgress = false;
|
||||||
|
|
||||||
|
var currentScale = PDFView.currentScale;
|
||||||
|
var currentScaleValue = PDFView.currentScaleValue;
|
||||||
|
var normalizedScaleValue = currentScaleValue == currentScale ?
|
||||||
|
currentScale * 100 : currentScaleValue;
|
||||||
|
|
||||||
var kViewerTopMargin = 52;
|
var kViewerTopMargin = 52;
|
||||||
var pageNumber = firstPage.id;
|
var pageNumber = firstPage.id;
|
||||||
var pdfOpenParams = '#page=' + pageNumber;
|
var pdfOpenParams = '#page=' + pageNumber;
|
||||||
pdfOpenParams += '&zoom=' + Math.round(PDFView.currentScale * 100);
|
pdfOpenParams += '&zoom=' + normalizedScaleValue;
|
||||||
var currentPage = PDFView.pages[pageNumber - 1];
|
var currentPage = PDFView.pages[pageNumber - 1];
|
||||||
var topLeft = currentPage.getPagePoint(window.pageXOffset,
|
var topLeft = currentPage.getPagePoint(window.pageXOffset,
|
||||||
window.pageYOffset - firstPage.y - kViewerTopMargin);
|
window.pageYOffset - firstPage.y - kViewerTopMargin);
|
||||||
pdfOpenParams += ',' + Math.round(topLeft.x) + ',' + Math.round(topLeft.y);
|
pdfOpenParams += ',' + Math.round(topLeft.x) + ',' + Math.round(topLeft.y);
|
||||||
|
|
||||||
|
var store = PDFView.store;
|
||||||
|
store.set('exists', true);
|
||||||
|
store.set('page', pageNumber);
|
||||||
|
store.set('zoom', normalizedScaleValue);
|
||||||
|
store.set('scrollLeft', Math.round(topLeft.x));
|
||||||
|
store.set('scrollTop', Math.round(topLeft.y));
|
||||||
|
|
||||||
document.getElementById('viewBookmark').href = pdfOpenParams;
|
document.getElementById('viewBookmark').href = pdfOpenParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -848,7 +1105,7 @@ function updateThumbViewArea() {
|
|||||||
var visibleThumbs = PDFView.getVisibleThumbs();
|
var visibleThumbs = PDFView.getVisibleThumbs();
|
||||||
for (var i = 0; i < visibleThumbs.length; i++) {
|
for (var i = 0; i < visibleThumbs.length; i++) {
|
||||||
var thumb = visibleThumbs[i];
|
var thumb = visibleThumbs[i];
|
||||||
PDFView.thumbnails[thumb.id - 1].draw();
|
renderingQueue.enqueueDraw(PDFView.thumbnails[thumb.id - 1]);
|
||||||
}
|
}
|
||||||
}, delay);
|
}, delay);
|
||||||
}
|
}
|
||||||
@ -858,7 +1115,8 @@ window.addEventListener('webkitTransitionEnd', updateThumbViewArea, true);
|
|||||||
|
|
||||||
window.addEventListener('resize', function webViewerResize(evt) {
|
window.addEventListener('resize', function webViewerResize(evt) {
|
||||||
if (document.getElementById('pageWidthOption').selected ||
|
if (document.getElementById('pageWidthOption').selected ||
|
||||||
document.getElementById('pageFitOption').selected)
|
document.getElementById('pageFitOption').selected ||
|
||||||
|
document.getElementById('pageAutoOption').selected)
|
||||||
PDFView.parseScale(document.getElementById('scaleSelect').value);
|
PDFView.parseScale(document.getElementById('scaleSelect').value);
|
||||||
updateViewarea();
|
updateViewarea();
|
||||||
});
|
});
|
||||||
@ -888,7 +1146,6 @@ window.addEventListener('change', function webViewerChange(evt) {
|
|||||||
// implemented in Firefox.
|
// implemented in Firefox.
|
||||||
var file = files[0];
|
var file = files[0];
|
||||||
fileReader.readAsBinaryString(file);
|
fileReader.readAsBinaryString(file);
|
||||||
|
|
||||||
document.title = file.name;
|
document.title = file.name;
|
||||||
|
|
||||||
// URL does not reflect proper document location - hiding some icons.
|
// URL does not reflect proper document location - hiding some icons.
|
||||||
@ -896,20 +1153,9 @@ window.addEventListener('change', function webViewerChange(evt) {
|
|||||||
document.getElementById('download').setAttribute('hidden', 'true');
|
document.getElementById('download').setAttribute('hidden', 'true');
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
window.addEventListener('scalechange', function scalechange(evt) {
|
function selectScaleOption(value) {
|
||||||
var customScaleOption = document.getElementById('customScaleOption');
|
|
||||||
customScaleOption.selected = false;
|
|
||||||
|
|
||||||
if (!evt.resetAutoSettings &&
|
|
||||||
(document.getElementById('pageWidthOption').selected ||
|
|
||||||
document.getElementById('pageFitOption').selected)) {
|
|
||||||
updateViewarea();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var options = document.getElementById('scaleSelect').options;
|
var options = document.getElementById('scaleSelect').options;
|
||||||
var predefinedValueFound = false;
|
var predefinedValueFound = false;
|
||||||
var value = '' + evt.scale;
|
|
||||||
for (var i = 0; i < options.length; i++) {
|
for (var i = 0; i < options.length; i++) {
|
||||||
var option = options[i];
|
var option = options[i];
|
||||||
if (option.value != value) {
|
if (option.value != value) {
|
||||||
@ -919,7 +1165,22 @@ window.addEventListener('scalechange', function scalechange(evt) {
|
|||||||
option.selected = true;
|
option.selected = true;
|
||||||
predefinedValueFound = true;
|
predefinedValueFound = true;
|
||||||
}
|
}
|
||||||
|
return predefinedValueFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('scalechange', function scalechange(evt) {
|
||||||
|
var customScaleOption = document.getElementById('customScaleOption');
|
||||||
|
customScaleOption.selected = false;
|
||||||
|
|
||||||
|
if (!evt.resetAutoSettings &&
|
||||||
|
(document.getElementById('pageWidthOption').selected ||
|
||||||
|
document.getElementById('pageFitOption').selected ||
|
||||||
|
document.getElementById('pageAutoOption').selected)) {
|
||||||
|
updateViewarea();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var predefinedValueFound = selectScaleOption('' + evt.scale);
|
||||||
if (!predefinedValueFound) {
|
if (!predefinedValueFound) {
|
||||||
customScaleOption.textContent = Math.round(evt.scale * 10000) / 100 + '%';
|
customScaleOption.textContent = Math.round(evt.scale * 10000) / 100 + '%';
|
||||||
customScaleOption.selected = true;
|
customScaleOption.selected = true;
|
||||||
@ -937,6 +1198,8 @@ window.addEventListener('pagechange', function pagechange(evt) {
|
|||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
window.addEventListener('keydown', function keydown(evt) {
|
window.addEventListener('keydown', function keydown(evt) {
|
||||||
|
if (evt.ctrlKey || evt.altKey || evt.shiftKey || evt.metaKey)
|
||||||
|
return;
|
||||||
var curElement = document.activeElement;
|
var curElement = document.activeElement;
|
||||||
if (curElement && curElement.tagName == 'INPUT')
|
if (curElement && curElement.tagName == 'INPUT')
|
||||||
return;
|
return;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user