Merge remote-tracking branch 'upstream/master' into dev
Conflicts: Makefile test/unit/unit_test.html
This commit is contained in:
commit
09eed8d971
1
LICENSE
1
LICENSE
@ -9,6 +9,7 @@
|
||||
Yury Delendik
|
||||
Kalervo Kujala
|
||||
Adil Allawi <@ironymark>
|
||||
Jakob Miland <saebekassebil@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
|
7
Makefile
7
Makefile
@ -68,7 +68,8 @@ bundle: | $(BUILD_DIR)
|
||||
@cd src; \
|
||||
cat $(PDF_JS_FILES) > all_files.tmp; \
|
||||
sed '/PDFJSSCRIPT_INCLUDE_ALL/ r all_files.tmp' pdf.js > ../$(BUILD_TARGET); \
|
||||
sed -i '' "s/PDFJSSCRIPT_BUNDLE_VER/`git log --format="%H" -n 1`/" ../$(BUILD_TARGET); \
|
||||
sed -i.bak "s/PDFJSSCRIPT_BUNDLE_VER/`git log --format="%h" -n 1`/" ../$(BUILD_TARGET); \
|
||||
rm -f ../$(BUILD_TARGET).bak
|
||||
rm -f *.tmp; \
|
||||
cd ..
|
||||
|
||||
@ -138,8 +139,8 @@ browser-test:
|
||||
# To install gjslint, see:
|
||||
#
|
||||
# <http://code.google.com/closure/utilities/docs/linter_howto.html>
|
||||
SRC_DIRS := . src utils web test test/unit examples/helloworld \
|
||||
extensions/firefox extensions/firefox/components extensions/chrome
|
||||
SRC_DIRS := . src utils web test examples/helloworld extensions/firefox \
|
||||
extensions/firefox/components extensions/chrome test/unit
|
||||
GJSLINT_FILES = $(foreach DIR,$(SRC_DIRS),$(wildcard $(DIR)/*.js))
|
||||
lint:
|
||||
gjslint --nojsdoc $(GJSLINT_FILES)
|
||||
|
@ -205,3 +205,4 @@ a "PDF Reference" from Adobe:
|
||||
|
||||
Recommended chapters to read: "2. Overview", "3.4 File Structure",
|
||||
"4.1 Graphics Objects" that lists the PDF commands.
|
||||
|
||||
|
141
examples/acroforms/forms.js
Normal file
141
examples/acroforms/forms.js
Normal file
@ -0,0 +1,141 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
//
|
||||
// Basic AcroForms input controls rendering
|
||||
//
|
||||
|
||||
'use strict';
|
||||
|
||||
var formFields = {};
|
||||
|
||||
function setupForm(div, content, scale) {
|
||||
function bindInputItem(input, item) {
|
||||
if (input.name in formFields) {
|
||||
var value = formFields[input.name];
|
||||
if (input.type == 'checkbox')
|
||||
input.checked = value;
|
||||
else if (!input.type || input.type == 'text')
|
||||
input.value = value;
|
||||
}
|
||||
input.onchange = function pageViewSetupInputOnBlur() {
|
||||
if (input.type == 'checkbox')
|
||||
formFields[input.name] = input.checked;
|
||||
else if (!input.type || input.type == 'text')
|
||||
formFields[input.name] = input.value;
|
||||
};
|
||||
}
|
||||
function createElementWithStyle(tagName, item) {
|
||||
var element = document.createElement(tagName);
|
||||
element.style.left = (item.x * scale) + 'px';
|
||||
element.style.top = (item.y * scale) + 'px';
|
||||
element.style.width = Math.ceil(item.width * scale) + 'px';
|
||||
element.style.height = Math.ceil(item.height * scale) + 'px';
|
||||
return element;
|
||||
}
|
||||
function assignFontStyle(element, item) {
|
||||
var fontStyles = '';
|
||||
if ('fontSize' in item)
|
||||
fontStyles += 'font-size: ' + Math.round(item.fontSize * scale) + 'px;';
|
||||
switch (item.textAlignment) {
|
||||
case 0:
|
||||
fontStyles += 'text-align: left;';
|
||||
break;
|
||||
case 1:
|
||||
fontStyles += 'text-align: center;';
|
||||
break;
|
||||
case 2:
|
||||
fontStyles += 'text-align: right;';
|
||||
break;
|
||||
}
|
||||
element.setAttribute('style', element.getAttribute('style') + fontStyles);
|
||||
}
|
||||
|
||||
var items = content.getAnnotations();
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
var item = items[i];
|
||||
switch (item.type) {
|
||||
case 'Widget':
|
||||
if (item.fieldType != 'Tx' && item.fieldType != 'Btn' &&
|
||||
item.fieldType != 'Ch')
|
||||
break;
|
||||
var inputDiv = createElementWithStyle('div', item);
|
||||
inputDiv.className = 'inputHint';
|
||||
div.appendChild(inputDiv);
|
||||
var input;
|
||||
if (item.fieldType == 'Tx') {
|
||||
input = createElementWithStyle('input', item);
|
||||
}
|
||||
if (item.fieldType == 'Btn') {
|
||||
input = createElementWithStyle('input', item);
|
||||
if (item.flags & 32768) {
|
||||
input.type = 'radio';
|
||||
// radio button is not supported
|
||||
} else if (item.flags & 65536) {
|
||||
input.type = 'button';
|
||||
// pushbutton is not supported
|
||||
} else {
|
||||
input.type = 'checkbox';
|
||||
}
|
||||
}
|
||||
if (item.fieldType == 'Ch') {
|
||||
input = createElementWithStyle('select', item);
|
||||
// select box is not supported
|
||||
}
|
||||
input.className = 'inputControl';
|
||||
input.name = item.fullName;
|
||||
input.title = item.alternativeText;
|
||||
assignFontStyle(input, item);
|
||||
bindInputItem(input, item);
|
||||
div.appendChild(input);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function renderPage(div, pdf, pageNumber, callback) {
|
||||
var page = pdf.getPage(pageNumber);
|
||||
var scale = 1.5;
|
||||
|
||||
var pageDisplayWidth = page.width * scale;
|
||||
var pageDisplayHeight = page.height * scale;
|
||||
|
||||
var pageDivHolder = document.createElement('div');
|
||||
pageDivHolder.className = 'pdfpage';
|
||||
pageDivHolder.style.width = pageDisplayWidth + 'px';
|
||||
pageDivHolder.style.height = pageDisplayHeight + 'px';
|
||||
div.appendChild(pageDivHolder);
|
||||
|
||||
// Prepare canvas using PDF page dimensions
|
||||
var canvas = document.createElement('canvas');
|
||||
var context = canvas.getContext('2d');
|
||||
canvas.width = pageDisplayWidth;
|
||||
canvas.height = pageDisplayHeight;
|
||||
pageDivHolder.appendChild(canvas);
|
||||
|
||||
|
||||
// Render PDF page into canvas context
|
||||
page.startRendering(context, callback);
|
||||
|
||||
// Prepare and populate form elements layer
|
||||
var formDiv = document.createElement('div');
|
||||
pageDivHolder.appendChild(formDiv);
|
||||
|
||||
setupForm(formDiv, page, scale);
|
||||
}
|
||||
|
||||
PDFJS.getPdf(pdfWithFormsPath, function getPdfForm(data) {
|
||||
// Instantiate PDFDoc with PDF data
|
||||
var pdf = new PDFJS.PDFDoc(data);
|
||||
|
||||
// Rendering all pages starting from first
|
||||
var viewer = document.getElementById('viewer');
|
||||
var pageNumber = 1;
|
||||
renderPage(viewer, pdf, pageNumber++, function pageRenderingComplete() {
|
||||
if (pageNumber > pdf.numPages)
|
||||
return; // All pages rendered
|
||||
// Continue rendering of the next page
|
||||
renderPage(viewer, pdf, pageNumber++, pageRenderingComplete);
|
||||
});
|
||||
});
|
||||
|
52
examples/acroforms/index.html
Normal file
52
examples/acroforms/index.html
Normal file
@ -0,0 +1,52 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<!-- In production, only one script (pdf.js) is necessary -->
|
||||
<!-- In production, change the content of PDFJS.workerSrc below -->
|
||||
<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/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">
|
||||
// Specify the main script used to create a new PDF.JS web worker.
|
||||
// In production, change this to point to the combined `pdf.js` file.
|
||||
PDFJS.workerSrc = '../../src/worker_loader.js';
|
||||
|
||||
// Specify the PDF with AcroForm here
|
||||
var pdfWithFormsPath = '../../test/pdfs/f1040.pdf';
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.pdfpage { position:relative; top: 0; left: 0; border: solid 1px black; margin: 10px; }
|
||||
.pdfpage > canvas { position: absolute; top: 0; left: 0; }
|
||||
.pdfpage > div { position: absolute; top: 0; left: 0; }
|
||||
.inputControl { background: transparent; border: 0px none; position: absolute; margin: auto; }
|
||||
.inputControl[type='checkbox'] { margin: 0px; }
|
||||
.inputHint { opacity: 0.2; background: #ccc; position: absolute; }
|
||||
</style>
|
||||
|
||||
<script type="text/javascript" src="forms.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="viewer"></div>
|
||||
</body>
|
||||
|
||||
</html>
|
@ -6,13 +6,13 @@
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>uriloader@pdf.js</em:id>
|
||||
<em:name>pdf.js</em:name>
|
||||
<em:version>0.1</em:version>
|
||||
<em:version>0.1.0</em:version>
|
||||
<em:iconURL>chrome://pdf.js/skin/logo.png</em:iconURL>
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
||||
<em:minVersion>6.0</em:minVersion>
|
||||
<em:maxVersion>11.0.*</em:maxVersion>
|
||||
<em:maxVersion>11.0a1</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
<em:bootstrap>true</em:bootstrap>
|
||||
@ -20,5 +20,6 @@
|
||||
<em:creator>Vivien Nicolas</em:creator>
|
||||
<em:description>pdf.js uri loader</em:description>
|
||||
<em:homepageURL>https://github.com/mozilla/pdf.js/</em:homepageURL>
|
||||
<em:type>2</em:type>
|
||||
</Description>
|
||||
</RDF>
|
||||
|
463
src/canvas.js
463
src/canvas.js
@ -6,8 +6,19 @@
|
||||
// <canvas> contexts store most of the state we need natively.
|
||||
// However, PDF needs a bit more state, which we store here.
|
||||
|
||||
var CanvasExtraState = (function canvasExtraState() {
|
||||
function constructor(old) {
|
||||
var TextRenderingMode = {
|
||||
FILL: 0,
|
||||
STROKE: 1,
|
||||
FILL_STROKE: 2,
|
||||
INVISIBLE: 3,
|
||||
FILL_ADD_TO_PATH: 4,
|
||||
STROKE_ADD_TO_PATH: 5,
|
||||
FILL_STROKE_ADD_TO_PATH: 6,
|
||||
ADD_TO_PATH: 7
|
||||
};
|
||||
|
||||
var CanvasExtraState = (function CanvasExtraStateClosure() {
|
||||
function CanvasExtraState(old) {
|
||||
// Are soft masks and alpha values shapes or opacities?
|
||||
this.alphaIsShape = false;
|
||||
this.fontSize = 0;
|
||||
@ -23,6 +34,7 @@ var CanvasExtraState = (function canvasExtraState() {
|
||||
this.charSpacing = 0;
|
||||
this.wordSpacing = 0;
|
||||
this.textHScale = 1;
|
||||
this.textRenderingMode = TextRenderingMode.FILL;
|
||||
// Color spaces
|
||||
this.fillColorSpace = new DeviceGrayCS();
|
||||
this.fillColorSpaceObj = null;
|
||||
@ -40,7 +52,7 @@ var CanvasExtraState = (function canvasExtraState() {
|
||||
this.old = old;
|
||||
}
|
||||
|
||||
constructor.prototype = {
|
||||
CanvasExtraState.prototype = {
|
||||
clone: function canvasextra_clone() {
|
||||
return Object.create(this);
|
||||
},
|
||||
@ -49,7 +61,7 @@ var CanvasExtraState = (function canvasExtraState() {
|
||||
this.y = y;
|
||||
}
|
||||
};
|
||||
return constructor;
|
||||
return CanvasExtraState;
|
||||
})();
|
||||
|
||||
function ScratchCanvas(width, height) {
|
||||
@ -59,16 +71,122 @@ function ScratchCanvas(width, height) {
|
||||
return canvas;
|
||||
}
|
||||
|
||||
var CanvasGraphics = (function canvasGraphics() {
|
||||
function addContextCurrentTransform(ctx) {
|
||||
// If the context doesn't expose a `mozCurrentTransform`, add a JS based on.
|
||||
if (!ctx.mozCurrentTransform) {
|
||||
// Store the original context
|
||||
ctx._originalSave = ctx.save;
|
||||
ctx._originalRestore = ctx.restore;
|
||||
ctx._originalRotate = ctx.rotate;
|
||||
ctx._originalScale = ctx.scale;
|
||||
ctx._originalTranslate = ctx.translate;
|
||||
ctx._originalTransform = ctx.transform;
|
||||
|
||||
ctx._transformMatrix = [1, 0, 0, 1, 0, 0];
|
||||
ctx._transformStack = [];
|
||||
|
||||
Object.defineProperty(ctx, 'mozCurrentTransform', {
|
||||
get: function getCurrentTransform() {
|
||||
return this._transformMatrix;
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(ctx, 'mozCurrentTransformInverse', {
|
||||
get: function getCurrentTransformInverse() {
|
||||
// Calculation done using WolframAlpha:
|
||||
// http://www.wolframalpha.com/input/?
|
||||
// i=Inverse+{{a%2C+c%2C+e}%2C+{b%2C+d%2C+f}%2C+{0%2C+0%2C+1}}
|
||||
|
||||
var m = this._transformMatrix;
|
||||
var a = m[0], b = m[1], c = m[2], d = m[3], e = m[4], f = m[5];
|
||||
|
||||
var ad_bc = a * d - b * c;
|
||||
var bc_ad = b * c - a * d;
|
||||
|
||||
return [
|
||||
d / ad_bc,
|
||||
b / bc_ad,
|
||||
c / bc_ad,
|
||||
a / ad_bc,
|
||||
(d * e - c * f) / bc_ad,
|
||||
(b * e - a * f) / ad_bc
|
||||
];
|
||||
}
|
||||
});
|
||||
|
||||
ctx.save = function ctxSave() {
|
||||
var old = this._transformMatrix;
|
||||
this._transformStack.push(old);
|
||||
this._transformMatrix = old.slice(0, 6);
|
||||
|
||||
this._originalSave();
|
||||
};
|
||||
|
||||
ctx.restore = function ctxRestore() {
|
||||
var prev = this._transformStack.pop();
|
||||
if (prev) {
|
||||
this._transformMatrix = prev;
|
||||
this._originalRestore();
|
||||
}
|
||||
};
|
||||
|
||||
ctx.translate = function ctxTranslate(x, y) {
|
||||
var m = this._transformMatrix;
|
||||
m[4] = m[0] * x + m[2] * y + m[4];
|
||||
m[5] = m[1] * x + m[3] * y + m[5];
|
||||
|
||||
this._originalTranslate(x, y);
|
||||
};
|
||||
|
||||
ctx.scale = function ctxScale(x, y) {
|
||||
var m = this._transformMatrix;
|
||||
m[0] = m[0] * x;
|
||||
m[1] = m[1] * x;
|
||||
m[2] = m[2] * y;
|
||||
m[3] = m[3] * y;
|
||||
|
||||
this._originalScale(x, y);
|
||||
};
|
||||
|
||||
ctx.transform = function ctxTransform(a, b, c, d, e, f) {
|
||||
var m = this._transformMatrix;
|
||||
this._transformMatrix = [
|
||||
m[0] * a + m[2] * b,
|
||||
m[1] * a + m[3] * b,
|
||||
m[0] * c + m[2] * d,
|
||||
m[1] * c + m[3] * d,
|
||||
m[0] * e + m[2] * f + m[4],
|
||||
m[1] * e + m[3] * f + m[5]
|
||||
];
|
||||
|
||||
ctx._originalTransform(a, b, c, d, e, f);
|
||||
};
|
||||
|
||||
ctx.rotate = function ctxRotate(angle) {
|
||||
var cosValue = Math.cos(angle);
|
||||
var sinValue = Math.sin(angle);
|
||||
|
||||
var m = this._transformMatrix;
|
||||
this._transformMatrix = [
|
||||
m[0] * cosValue + m[2] * sinValue,
|
||||
m[1] * cosValue + m[3] * sinValue,
|
||||
m[0] * (-sinValue) + m[2] * cosValue,
|
||||
m[1] * (-sinValue) + m[3] * cosValue,
|
||||
m[4],
|
||||
m[5]
|
||||
];
|
||||
|
||||
this._originalRotate(angle);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
var CanvasGraphics = (function CanvasGraphicsClosure() {
|
||||
// Defines the time the executeIRQueue is going to be executing
|
||||
// before it stops and shedules a continue of execution.
|
||||
var kExecutionTime = 50;
|
||||
|
||||
// Number of IR commands to execute before checking
|
||||
// if we execute longer then `kExecutionTime`.
|
||||
var kExecutionTimeCheck = 500;
|
||||
|
||||
function constructor(canvasCtx, objs) {
|
||||
function CanvasGraphics(canvasCtx, objs, textLayer) {
|
||||
this.ctx = canvasCtx;
|
||||
this.current = new CanvasExtraState();
|
||||
this.stateStack = [];
|
||||
@ -77,6 +195,10 @@ var CanvasGraphics = (function canvasGraphics() {
|
||||
this.xobjs = null;
|
||||
this.ScratchCanvas = ScratchCanvas;
|
||||
this.objs = objs;
|
||||
this.textLayer = textLayer;
|
||||
if (canvasCtx) {
|
||||
addContextCurrentTransform(canvasCtx);
|
||||
}
|
||||
}
|
||||
|
||||
var LINE_CAP_STYLES = ['butt', 'round', 'square'];
|
||||
@ -84,7 +206,36 @@ var CanvasGraphics = (function canvasGraphics() {
|
||||
var NORMAL_CLIP = {};
|
||||
var EO_CLIP = {};
|
||||
|
||||
constructor.prototype = {
|
||||
CanvasGraphics.prototype = {
|
||||
slowCommands: {
|
||||
'stroke': true,
|
||||
'closeStroke': true,
|
||||
'fill': true,
|
||||
'eoFill': true,
|
||||
'fillStroke': true,
|
||||
'eoFillStroke': true,
|
||||
'closeFillStroke': true,
|
||||
'closeEOFillStroke': true,
|
||||
'showText': true,
|
||||
'showSpacedText': true,
|
||||
'setStrokeColorSpace': true,
|
||||
'setFillColorSpace': true,
|
||||
'setStrokeColor': true,
|
||||
'setStrokeColorN': true,
|
||||
'setFillColor': true,
|
||||
'setFillColorN_IR': true,
|
||||
'setStrokeGray': true,
|
||||
'setFillGray': true,
|
||||
'setStrokeRGBColor': true,
|
||||
'setFillRGBColor': true,
|
||||
'setStrokeCMYKColor': true,
|
||||
'setFillCMYKColor': true,
|
||||
'paintJpegXObject': true,
|
||||
'paintImageXObject': true,
|
||||
'paintImageMaskXObject': true,
|
||||
'shadingFill': true
|
||||
},
|
||||
|
||||
beginDrawing: function canvasGraphicsBeginDrawing(mediaBox) {
|
||||
var cw = this.ctx.canvas.width, ch = this.ctx.canvas.height;
|
||||
this.ctx.save();
|
||||
@ -102,7 +253,13 @@ var CanvasGraphics = (function canvasGraphics() {
|
||||
this.ctx.transform(0, -1, -1, 0, cw, ch);
|
||||
break;
|
||||
}
|
||||
// Scale so that canvas units are the same as PDF user space units
|
||||
this.ctx.scale(cw / mediaBox.width, ch / mediaBox.height);
|
||||
// Move the media left-top corner to the (0,0) canvas position
|
||||
this.ctx.translate(-mediaBox.x, -mediaBox.y);
|
||||
|
||||
if (this.textLayer)
|
||||
this.textLayer.beginLayout();
|
||||
},
|
||||
|
||||
executeIRQueue: function canvasGraphicsExecuteIRQueue(codeIR,
|
||||
@ -112,32 +269,39 @@ var CanvasGraphics = (function canvasGraphics() {
|
||||
var i = executionStartIdx || 0;
|
||||
var argsArrayLen = argsArray.length;
|
||||
|
||||
// Sometimes the IRQueue to execute is empty.
|
||||
if (argsArrayLen == i) {
|
||||
return i;
|
||||
}
|
||||
|
||||
var executionEndIdx;
|
||||
var startTime = Date.now();
|
||||
var endTime = Date.now() + kExecutionTime;
|
||||
|
||||
var objs = this.objs;
|
||||
var fnName;
|
||||
var slowCommands = this.slowCommands;
|
||||
|
||||
do {
|
||||
executionEndIdx = Math.min(argsArrayLen, i + kExecutionTimeCheck);
|
||||
while (true) {
|
||||
fnName = fnArray[i];
|
||||
|
||||
for (i; i < executionEndIdx; i++) {
|
||||
if (fnArray[i] !== 'dependency') {
|
||||
this[fnArray[i]].apply(this, argsArray[i]);
|
||||
} else {
|
||||
var deps = argsArray[i];
|
||||
for (var n = 0, nn = deps.length; n < nn; n++) {
|
||||
var depObjId = deps[n];
|
||||
if (fnName !== 'dependency') {
|
||||
this[fnName].apply(this, argsArray[i]);
|
||||
} else {
|
||||
var deps = argsArray[i];
|
||||
for (var n = 0, nn = deps.length; n < nn; n++) {
|
||||
var depObjId = deps[n];
|
||||
|
||||
// If the promise isn't resolved yet, add the continueCallback
|
||||
// to the promise and bail out.
|
||||
if (!objs.isResolved(depObjId)) {
|
||||
objs.get(depObjId, continueCallback);
|
||||
return i;
|
||||
}
|
||||
// If the promise isn't resolved yet, add the continueCallback
|
||||
// to the promise and bail out.
|
||||
if (!objs.isResolved(depObjId)) {
|
||||
objs.get(depObjId, continueCallback);
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
i++;
|
||||
|
||||
// If the entire IRQueue was executed, stop as were done.
|
||||
if (i == argsArrayLen) {
|
||||
return i;
|
||||
@ -146,18 +310,21 @@ var CanvasGraphics = (function canvasGraphics() {
|
||||
// If the execution took longer then a certain amount of time, shedule
|
||||
// to continue exeution after a short delay.
|
||||
// However, this is only possible if a 'continueCallback' is passed in.
|
||||
if (continueCallback && (Date.now() - startTime) > kExecutionTime) {
|
||||
if (continueCallback && slowCommands[fnName] && Date.now() > endTime) {
|
||||
setTimeout(continueCallback, 0);
|
||||
return i;
|
||||
}
|
||||
|
||||
// If the IRQueue isn't executed completly yet OR the execution time
|
||||
// was short enough, do another execution round.
|
||||
} while (true);
|
||||
}
|
||||
},
|
||||
|
||||
endDrawing: function canvasGraphicsEndDrawing() {
|
||||
this.ctx.restore();
|
||||
|
||||
if (this.textLayer)
|
||||
this.textLayer.endLayout();
|
||||
},
|
||||
|
||||
// Graphics state
|
||||
@ -176,6 +343,8 @@ var CanvasGraphics = (function canvasGraphics() {
|
||||
setDash: function canvasGraphicsSetDash(dashArray, dashPhase) {
|
||||
this.ctx.mozDash = dashArray;
|
||||
this.ctx.mozDashOffset = dashPhase;
|
||||
this.ctx.webkitLineDash = dashArray;
|
||||
this.ctx.webkitLineDashOffset = dashPhase;
|
||||
},
|
||||
setRenderingIntent: function canvasGraphicsSetRenderingIntent(intent) {
|
||||
TODO('set rendering intent: ' + intent);
|
||||
@ -394,7 +563,9 @@ var CanvasGraphics = (function canvasGraphics() {
|
||||
this.ctx.font = rule;
|
||||
},
|
||||
setTextRenderingMode: function canvasGraphicsSetTextRenderingMode(mode) {
|
||||
TODO('text rendering mode: ' + mode);
|
||||
if (mode >= TextRenderingMode.FILL_ADD_TO_PATH)
|
||||
TODO('unsupported text rendering mode: ' + mode);
|
||||
this.current.textRenderingMode = mode;
|
||||
},
|
||||
setTextRise: function canvasGraphicsSetTextRise(rise) {
|
||||
TODO('text rise: ' + rise);
|
||||
@ -416,23 +587,67 @@ var CanvasGraphics = (function canvasGraphics() {
|
||||
nextLine: function canvasGraphicsNextLine() {
|
||||
this.moveText(0, this.current.leading);
|
||||
},
|
||||
showText: function canvasGraphicsShowText(text) {
|
||||
applyTextTransforms: function canvasApplyTransforms() {
|
||||
var ctx = this.ctx;
|
||||
var current = this.current;
|
||||
var textHScale = current.textHScale;
|
||||
var fontMatrix = current.font.fontMatrix || IDENTITY_MATRIX;
|
||||
|
||||
ctx.transform.apply(ctx, current.textMatrix);
|
||||
ctx.scale(1, -1);
|
||||
ctx.translate(current.x, -1 * current.y);
|
||||
ctx.transform.apply(ctx, fontMatrix);
|
||||
ctx.scale(textHScale, 1);
|
||||
},
|
||||
getTextGeometry: function canvasGetTextGeometry() {
|
||||
var geometry = {};
|
||||
var ctx = this.ctx;
|
||||
var font = this.current.font;
|
||||
var ctxMatrix = ctx.mozCurrentTransform;
|
||||
if (ctxMatrix) {
|
||||
var bl = Util.applyTransform([0, 0], ctxMatrix);
|
||||
var tr = Util.applyTransform([1, 1], ctxMatrix);
|
||||
geometry.x = bl[0];
|
||||
geometry.y = bl[1];
|
||||
geometry.hScale = tr[0] - bl[0];
|
||||
geometry.vScale = tr[1] - bl[1];
|
||||
}
|
||||
geometry.spaceWidth = font.spaceWidth;
|
||||
return geometry;
|
||||
},
|
||||
|
||||
showText: function canvasGraphicsShowText(str, skipTextSelection) {
|
||||
var ctx = this.ctx;
|
||||
var current = this.current;
|
||||
var font = current.font;
|
||||
var glyphs = font.charsToGlyphs(text);
|
||||
var glyphs = font.charsToGlyphs(str);
|
||||
var fontSize = current.fontSize;
|
||||
var charSpacing = current.charSpacing;
|
||||
var wordSpacing = current.wordSpacing;
|
||||
var textHScale = current.textHScale;
|
||||
var fontMatrix = font.fontMatrix || IDENTITY_MATRIX;
|
||||
var textHScale2 = textHScale * fontMatrix[0];
|
||||
var glyphsLength = glyphs.length;
|
||||
var textLayer = this.textLayer;
|
||||
var text = {str: '', length: 0, canvasWidth: 0, geom: {}};
|
||||
var textSelection = textLayer && !skipTextSelection ? true : false;
|
||||
var textRenderingMode = current.textRenderingMode;
|
||||
|
||||
// Type3 fonts - each glyph is a "mini-PDF"
|
||||
if (font.coded) {
|
||||
ctx.save();
|
||||
ctx.transform.apply(ctx, current.textMatrix);
|
||||
ctx.translate(current.x, current.y);
|
||||
|
||||
var fontMatrix = font.fontMatrix || IDENTITY_MATRIX;
|
||||
ctx.scale(1 / textHScale, 1);
|
||||
ctx.scale(textHScale, 1);
|
||||
ctx.lineWidth /= current.textMatrix[0];
|
||||
|
||||
if (textSelection) {
|
||||
this.save();
|
||||
ctx.scale(1, -1);
|
||||
text.geom = this.getTextGeometry();
|
||||
this.restore();
|
||||
}
|
||||
for (var i = 0; i < glyphsLength; ++i) {
|
||||
|
||||
var glyph = glyphs[i];
|
||||
@ -452,18 +667,20 @@ var CanvasGraphics = (function canvasGraphics() {
|
||||
var width = transformed[0] * fontSize + charSpacing;
|
||||
|
||||
ctx.translate(width, 0);
|
||||
current.x += width;
|
||||
current.x += width * textHScale;
|
||||
|
||||
text.str += glyph.unicode;
|
||||
text.length++;
|
||||
text.canvasWidth += width;
|
||||
}
|
||||
ctx.restore();
|
||||
} else {
|
||||
ctx.save();
|
||||
ctx.transform.apply(ctx, current.textMatrix);
|
||||
ctx.scale(1, -1);
|
||||
ctx.translate(current.x, -1 * current.y);
|
||||
ctx.transform.apply(ctx, font.fontMatrix || IDENTITY_MATRIX);
|
||||
this.applyTextTransforms();
|
||||
ctx.lineWidth /= current.textMatrix[0] * fontMatrix[0];
|
||||
|
||||
ctx.scale(1 / textHScale, 1);
|
||||
if (textSelection)
|
||||
text.geom = this.getTextGeometry();
|
||||
|
||||
var width = 0;
|
||||
for (var i = 0; i < glyphsLength; ++i) {
|
||||
@ -474,36 +691,106 @@ var CanvasGraphics = (function canvasGraphics() {
|
||||
continue;
|
||||
}
|
||||
|
||||
var unicode = glyph.unicode;
|
||||
var char = (unicode >= 0x10000) ?
|
||||
String.fromCharCode(0xD800 | ((unicode - 0x10000) >> 10),
|
||||
0xDC00 | (unicode & 0x3FF)) : String.fromCharCode(unicode);
|
||||
var char = glyph.fontChar;
|
||||
var charWidth = glyph.width * fontSize * 0.001 + charSpacing;
|
||||
|
||||
ctx.fillText(char, width, 0);
|
||||
width += glyph.width * fontSize * 0.001 + charSpacing;
|
||||
switch (textRenderingMode) {
|
||||
default: // other unsupported rendering modes
|
||||
case TextRenderingMode.FILL:
|
||||
case TextRenderingMode.FILL_ADD_TO_PATH:
|
||||
ctx.fillText(char, width, 0);
|
||||
break;
|
||||
case TextRenderingMode.STROKE:
|
||||
case TextRenderingMode.STROKE_ADD_TO_PATH:
|
||||
ctx.strokeText(char, width, 0);
|
||||
break;
|
||||
case TextRenderingMode.FILL_STROKE:
|
||||
case TextRenderingMode.FILL_STROKE_ADD_TO_PATH:
|
||||
ctx.fillText(char, width, 0);
|
||||
ctx.strokeText(char, width, 0);
|
||||
break;
|
||||
case TextRenderingMode.INVISIBLE:
|
||||
break;
|
||||
}
|
||||
|
||||
width += charWidth;
|
||||
|
||||
text.str += glyph.unicode === ' ' ? '\u00A0' : glyph.unicode;
|
||||
text.length++;
|
||||
text.canvasWidth += charWidth;
|
||||
}
|
||||
current.x += width;
|
||||
|
||||
current.x += width * textHScale2;
|
||||
ctx.restore();
|
||||
}
|
||||
},
|
||||
|
||||
if (textSelection)
|
||||
this.textLayer.appendText(text, font.loadedName, fontSize);
|
||||
|
||||
return text;
|
||||
},
|
||||
showSpacedText: function canvasGraphicsShowSpacedText(arr) {
|
||||
var ctx = this.ctx;
|
||||
var current = this.current;
|
||||
var font = current.font;
|
||||
var fontSize = current.fontSize;
|
||||
var textHScale = current.textHScale;
|
||||
if (!font.coded)
|
||||
textHScale *= (font.fontMatrix || IDENTITY_MATRIX)[0];
|
||||
var arrLength = arr.length;
|
||||
var textLayer = this.textLayer;
|
||||
var text = {str: '', length: 0, canvasWidth: 0, geom: {}};
|
||||
var textSelection = textLayer ? true : false;
|
||||
|
||||
if (textSelection) {
|
||||
ctx.save();
|
||||
// Type3 fonts - each glyph is a "mini-PDF" (see also showText)
|
||||
if (font.coded) {
|
||||
ctx.transform.apply(ctx, current.textMatrix);
|
||||
ctx.scale(1, -1);
|
||||
ctx.translate(current.x, -1 * current.y);
|
||||
ctx.scale(textHScale, 1);
|
||||
} else
|
||||
this.applyTextTransforms();
|
||||
text.geom = this.getTextGeometry();
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
for (var i = 0; i < arrLength; ++i) {
|
||||
var e = arr[i];
|
||||
if (isNum(e)) {
|
||||
current.x -= e * 0.001 * fontSize * textHScale;
|
||||
var spacingLength = -e * 0.001 * fontSize * textHScale;
|
||||
current.x += spacingLength;
|
||||
|
||||
if (textSelection) {
|
||||
// Emulate precise spacing via HTML spaces
|
||||
text.canvasWidth += spacingLength;
|
||||
if (e < 0 && text.geom.spaceWidth > 0) { // avoid div by zero
|
||||
var numFakeSpaces = Math.round(-e / text.geom.spaceWidth);
|
||||
if (numFakeSpaces > 0) {
|
||||
text.str += '\u00A0';
|
||||
text.length++;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (isString(e)) {
|
||||
this.showText(e);
|
||||
var shownText = this.showText(e, true);
|
||||
|
||||
if (textSelection) {
|
||||
if (shownText.str === ' ') {
|
||||
text.str += '\u00A0';
|
||||
} else {
|
||||
text.str += shownText.str;
|
||||
}
|
||||
text.canvasWidth += shownText.canvasWidth;
|
||||
text.length += e.length;
|
||||
}
|
||||
} else {
|
||||
malformed('TJ array element ' + e + ' is not string or num');
|
||||
}
|
||||
}
|
||||
|
||||
if (textSelection)
|
||||
this.textLayer.appendText(text, font.loadedName, fontSize);
|
||||
},
|
||||
nextLineShowText: function canvasGraphicsNextLineShowText(text) {
|
||||
this.nextLine();
|
||||
@ -658,9 +945,9 @@ var CanvasGraphics = (function canvasGraphics() {
|
||||
var height = canvas.height;
|
||||
|
||||
var bl = Util.applyTransform([0, 0], inv);
|
||||
var br = Util.applyTransform([0, width], inv);
|
||||
var ul = Util.applyTransform([height, 0], inv);
|
||||
var ur = Util.applyTransform([height, width], inv);
|
||||
var br = Util.applyTransform([0, height], inv);
|
||||
var ul = Util.applyTransform([width, 0], inv);
|
||||
var ur = Util.applyTransform([width, height], inv);
|
||||
|
||||
var x0 = Math.min(bl[0], br[0], ul[0], ur[0]);
|
||||
var y0 = Math.min(bl[1], br[1], ul[1], ur[1]);
|
||||
@ -710,8 +997,8 @@ var CanvasGraphics = (function canvasGraphics() {
|
||||
},
|
||||
|
||||
paintJpegXObject: function canvasGraphicsPaintJpegXObject(objId, w, h) {
|
||||
var image = this.objs.get(objId);
|
||||
if (!image) {
|
||||
var domImage = this.objs.get(objId);
|
||||
if (!domImage) {
|
||||
error('Dependent image isn\'t ready yet');
|
||||
}
|
||||
|
||||
@ -721,7 +1008,6 @@ var CanvasGraphics = (function canvasGraphics() {
|
||||
// scale the image to the unit square
|
||||
ctx.scale(1 / w, -1 / h);
|
||||
|
||||
var domImage = image.getImage();
|
||||
ctx.drawImage(domImage, 0, 0, domImage.width, domImage.height,
|
||||
0, -h, w, h);
|
||||
|
||||
@ -777,7 +1063,11 @@ var CanvasGraphics = (function canvasGraphics() {
|
||||
this.restore();
|
||||
},
|
||||
|
||||
paintImageXObject: function canvasGraphicsPaintImageXObject(imgData) {
|
||||
paintImageXObject: function canvasGraphicsPaintImageXObject(objId) {
|
||||
var imgData = this.objs.get(objId);
|
||||
if (!imgData)
|
||||
error('Dependent image isn\'t ready yet');
|
||||
|
||||
this.save();
|
||||
var ctx = this.ctx;
|
||||
var w = imgData.width;
|
||||
@ -787,26 +1077,16 @@ var CanvasGraphics = (function canvasGraphics() {
|
||||
|
||||
var tmpCanvas = new this.ScratchCanvas(w, h);
|
||||
var tmpCtx = tmpCanvas.getContext('2d');
|
||||
var tmpImgData;
|
||||
this.putBinaryImageData(tmpCtx, imgData, w, h);
|
||||
|
||||
// Some browsers can set an UInt8Array directly as imageData, some
|
||||
// can't. As long as we don't have proper feature detection, just
|
||||
// copy over each pixel and set the imageData that way.
|
||||
tmpImgData = tmpCtx.getImageData(0, 0, w, h);
|
||||
|
||||
// Copy over the imageData.
|
||||
var tmpImgDataPixels = tmpImgData.data;
|
||||
var len = tmpImgDataPixels.length;
|
||||
|
||||
while (len--) {
|
||||
tmpImgDataPixels[len] = imgData.data[len];
|
||||
}
|
||||
|
||||
tmpCtx.putImageData(tmpImgData, 0, 0);
|
||||
ctx.drawImage(tmpCanvas, 0, -h);
|
||||
this.restore();
|
||||
},
|
||||
|
||||
putBinaryImageData: function canvasPutBinaryImageData() {
|
||||
//
|
||||
},
|
||||
|
||||
// Marked content
|
||||
|
||||
markPoint: function canvasGraphicsMarkPoint(tag) {
|
||||
@ -864,6 +1144,41 @@ var CanvasGraphics = (function canvasGraphics() {
|
||||
}
|
||||
};
|
||||
|
||||
return constructor;
|
||||
return CanvasGraphics;
|
||||
})();
|
||||
|
||||
if (!isWorker) {
|
||||
// Feature detection if the browser can use an Uint8Array directly as imgData.
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.width = 1;
|
||||
canvas.height = 1;
|
||||
var ctx = canvas.getContext('2d');
|
||||
|
||||
try {
|
||||
ctx.putImageData({
|
||||
width: 1,
|
||||
height: 1,
|
||||
data: new Uint8Array(4)
|
||||
}, 0, 0);
|
||||
|
||||
CanvasGraphics.prototype.putBinaryImageData =
|
||||
function CanvasGraphicsPutBinaryImageDataNative(ctx, imgData) {
|
||||
ctx.putImageData(imgData, 0, 0);
|
||||
};
|
||||
} catch (e) {
|
||||
CanvasGraphics.prototype.putBinaryImageData =
|
||||
function CanvasGraphicsPutBinaryImageDataShim(ctx, imgData, w, h) {
|
||||
var tmpImgData = ctx.getImageData(0, 0, w, h);
|
||||
|
||||
// Copy over the imageData pixel by pixel.
|
||||
var tmpImgDataPixels = tmpImgData.data;
|
||||
var len = tmpImgDataPixels.length;
|
||||
|
||||
while (len--) {
|
||||
tmpImgDataPixels[len] = imgData.data[len];
|
||||
}
|
||||
|
||||
ctx.putImageData(tmpImgData, 0, 0);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -3,13 +3,13 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var ColorSpace = (function colorSpaceColorSpace() {
|
||||
var ColorSpace = (function ColorSpaceClosure() {
|
||||
// Constructor should define this.numComps, this.defaultColor, this.name
|
||||
function constructor() {
|
||||
function ColorSpace() {
|
||||
error('should not call ColorSpace constructor');
|
||||
}
|
||||
|
||||
constructor.prototype = {
|
||||
ColorSpace.prototype = {
|
||||
// Input: array of size numComps representing color component values
|
||||
// Output: array of rgb values, each value ranging from [0.1]
|
||||
getRgb: function colorSpaceGetRgb(color) {
|
||||
@ -22,15 +22,15 @@ var ColorSpace = (function colorSpaceColorSpace() {
|
||||
}
|
||||
};
|
||||
|
||||
constructor.parse = function colorSpaceParse(cs, xref, res) {
|
||||
var IR = constructor.parseToIR(cs, xref, res);
|
||||
ColorSpace.parse = function colorSpaceParse(cs, xref, res) {
|
||||
var IR = ColorSpace.parseToIR(cs, xref, res);
|
||||
if (IR instanceof AlternateCS)
|
||||
return IR;
|
||||
|
||||
return constructor.fromIR(IR);
|
||||
return ColorSpace.fromIR(IR);
|
||||
};
|
||||
|
||||
constructor.fromIR = function colorSpaceFromIR(IR) {
|
||||
ColorSpace.fromIR = function colorSpaceFromIR(IR) {
|
||||
var name = isArray(IR) ? IR[0] : IR;
|
||||
|
||||
switch (name) {
|
||||
@ -63,7 +63,7 @@ var ColorSpace = (function colorSpaceColorSpace() {
|
||||
return null;
|
||||
};
|
||||
|
||||
constructor.parseToIR = function colorSpaceParseToIR(cs, xref, res) {
|
||||
ColorSpace.parseToIR = function colorSpaceParseToIR(cs, xref, res) {
|
||||
if (isName(cs)) {
|
||||
var colorSpaces = xref.fetchIfRef(res.get('ColorSpace'));
|
||||
if (isDict(colorSpaces)) {
|
||||
@ -154,8 +154,31 @@ var ColorSpace = (function colorSpaceColorSpace() {
|
||||
}
|
||||
return null;
|
||||
};
|
||||
/**
|
||||
* Checks if a decode map matches the default decode map for a color space.
|
||||
* This handles the general decode maps where there are two values per
|
||||
* component. e.g. [0, 1, 0, 1, 0, 1] for a RGB color.
|
||||
* This does not handle Lab, Indexed, or Pattern decode maps since they are
|
||||
* slightly different.
|
||||
* @param {Array} decode Decode map (usually from an image).
|
||||
* @param {Number} n Number of components the color space has.
|
||||
*/
|
||||
ColorSpace.isDefaultDecode = function colorSpaceIsDefaultDecode(decode, n) {
|
||||
if (!decode)
|
||||
return true;
|
||||
|
||||
return constructor;
|
||||
if (n * 2 !== decode.length) {
|
||||
warning('The decode map is not the correct length');
|
||||
return true;
|
||||
}
|
||||
for (var i = 0, ii = decode.length; i < ii; i += 2) {
|
||||
if (decode[i] != 0 || decode[i + 1] != 1)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
return ColorSpace;
|
||||
})();
|
||||
|
||||
/**
|
||||
@ -164,8 +187,8 @@ var ColorSpace = (function colorSpaceColorSpace() {
|
||||
* Both color spaces use a tinting function to convert colors to a base color
|
||||
* space.
|
||||
*/
|
||||
var AlternateCS = (function alternateCS() {
|
||||
function constructor(numComps, base, tintFn) {
|
||||
var AlternateCS = (function AlternateCSClosure() {
|
||||
function AlternateCS(numComps, base, tintFn) {
|
||||
this.name = 'Alternate';
|
||||
this.numComps = numComps;
|
||||
this.defaultColor = [];
|
||||
@ -175,7 +198,7 @@ var AlternateCS = (function alternateCS() {
|
||||
this.tintFn = tintFn;
|
||||
}
|
||||
|
||||
constructor.prototype = {
|
||||
AlternateCS.prototype = {
|
||||
getRgb: function altcs_getRgb(color) {
|
||||
var tinted = this.tintFn(color);
|
||||
return this.base.getRgb(tinted);
|
||||
@ -200,24 +223,27 @@ var AlternateCS = (function alternateCS() {
|
||||
baseBuf[pos++] = 255 * tinted[j];
|
||||
}
|
||||
return base.getRgbBuffer(baseBuf, 8);
|
||||
},
|
||||
isDefaultDecode: function altcs_isDefaultDecode(decodeMap) {
|
||||
return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
|
||||
}
|
||||
};
|
||||
|
||||
return constructor;
|
||||
return AlternateCS;
|
||||
})();
|
||||
|
||||
var PatternCS = (function patternCS() {
|
||||
function constructor(baseCS) {
|
||||
var PatternCS = (function PatternCSClosure() {
|
||||
function PatternCS(baseCS) {
|
||||
this.name = 'Pattern';
|
||||
this.base = baseCS;
|
||||
}
|
||||
constructor.prototype = {};
|
||||
PatternCS.prototype = {};
|
||||
|
||||
return constructor;
|
||||
return PatternCS;
|
||||
})();
|
||||
|
||||
var IndexedCS = (function indexedCS() {
|
||||
function constructor(base, highVal, lookup) {
|
||||
var IndexedCS = (function IndexedCSClosure() {
|
||||
function IndexedCS(base, highVal, lookup) {
|
||||
this.name = 'Indexed';
|
||||
this.numComps = 1;
|
||||
this.defaultColor = [0];
|
||||
@ -240,7 +266,7 @@ var IndexedCS = (function indexedCS() {
|
||||
this.lookup = lookupArray;
|
||||
}
|
||||
|
||||
constructor.prototype = {
|
||||
IndexedCS.prototype = {
|
||||
getRgb: function indexcs_getRgb(color) {
|
||||
var numComps = this.base.numComps;
|
||||
var start = color[0] * numComps;
|
||||
@ -267,19 +293,23 @@ var IndexedCS = (function indexedCS() {
|
||||
}
|
||||
|
||||
return base.getRgbBuffer(baseBuf, 8);
|
||||
},
|
||||
isDefaultDecode: function indexcs_isDefaultDecode(decodeMap) {
|
||||
// indexed color maps shouldn't be changed
|
||||
return true;
|
||||
}
|
||||
};
|
||||
return constructor;
|
||||
return IndexedCS;
|
||||
})();
|
||||
|
||||
var DeviceGrayCS = (function deviceGrayCS() {
|
||||
function constructor() {
|
||||
var DeviceGrayCS = (function DeviceGrayCSClosure() {
|
||||
function DeviceGrayCS() {
|
||||
this.name = 'DeviceGray';
|
||||
this.numComps = 1;
|
||||
this.defaultColor = [0];
|
||||
}
|
||||
|
||||
constructor.prototype = {
|
||||
DeviceGrayCS.prototype = {
|
||||
getRgb: function graycs_getRgb(color) {
|
||||
var c = color[0];
|
||||
return [c, c, c];
|
||||
@ -295,18 +325,21 @@ var DeviceGrayCS = (function deviceGrayCS() {
|
||||
rgbBuf[j++] = c;
|
||||
}
|
||||
return rgbBuf;
|
||||
},
|
||||
isDefaultDecode: function graycs_isDefaultDecode(decodeMap) {
|
||||
return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
|
||||
}
|
||||
};
|
||||
return constructor;
|
||||
return DeviceGrayCS;
|
||||
})();
|
||||
|
||||
var DeviceRgbCS = (function deviceRgbCS() {
|
||||
function constructor() {
|
||||
var DeviceRgbCS = (function DeviceRgbCSClosure() {
|
||||
function DeviceRgbCS() {
|
||||
this.name = 'DeviceRGB';
|
||||
this.numComps = 3;
|
||||
this.defaultColor = [0, 0, 0];
|
||||
}
|
||||
constructor.prototype = {
|
||||
DeviceRgbCS.prototype = {
|
||||
getRgb: function rgbcs_getRgb(color) {
|
||||
return color;
|
||||
},
|
||||
@ -319,18 +352,21 @@ var DeviceRgbCS = (function deviceRgbCS() {
|
||||
for (i = 0; i < length; ++i)
|
||||
rgbBuf[i] = (scale * input[i]) | 0;
|
||||
return rgbBuf;
|
||||
},
|
||||
isDefaultDecode: function rgbcs_isDefaultDecode(decodeMap) {
|
||||
return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
|
||||
}
|
||||
};
|
||||
return constructor;
|
||||
return DeviceRgbCS;
|
||||
})();
|
||||
|
||||
var DeviceCmykCS = (function deviceCmykCS() {
|
||||
function constructor() {
|
||||
var DeviceCmykCS = (function DeviceCmykCSClosure() {
|
||||
function DeviceCmykCS() {
|
||||
this.name = 'DeviceCMYK';
|
||||
this.numComps = 4;
|
||||
this.defaultColor = [0, 0, 0, 1];
|
||||
}
|
||||
constructor.prototype = {
|
||||
DeviceCmykCS.prototype = {
|
||||
getRgb: function cmykcs_getRgb(color) {
|
||||
var c = color[0], m = color[1], y = color[2], k = color[3];
|
||||
var c1 = 1 - c, m1 = 1 - m, y1 = 1 - y, k1 = 1 - k;
|
||||
@ -403,9 +439,12 @@ var DeviceCmykCS = (function deviceCmykCS() {
|
||||
}
|
||||
|
||||
return rgbBuf;
|
||||
},
|
||||
isDefaultDecode: function cmykcs_isDefaultDecode(decodeMap) {
|
||||
return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
|
||||
}
|
||||
};
|
||||
|
||||
return constructor;
|
||||
return DeviceCmykCS;
|
||||
})();
|
||||
|
||||
|
390
src/core.js
390
src/core.js
@ -5,6 +5,8 @@
|
||||
|
||||
var globalScope = (typeof window === 'undefined') ? this : window;
|
||||
|
||||
var isWorker = (typeof window == 'undefined');
|
||||
|
||||
var ERRORS = 0, WARNINGS = 1, TODOS = 5;
|
||||
var verbosity = WARNINGS;
|
||||
|
||||
@ -31,7 +33,7 @@ function getPdf(arg, callback) {
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', params.url);
|
||||
xhr.mozResponseType = xhr.responseType = 'arraybuffer';
|
||||
xhr.expected = (document.URL.indexOf('file:') === 0) ? 0 : 200;
|
||||
xhr.expected = (params.url.indexOf('file:') === 0) ? 0 : 200;
|
||||
|
||||
if ('progress' in params)
|
||||
xhr.onprogress = params.progress || undefined;
|
||||
@ -39,19 +41,23 @@ function getPdf(arg, callback) {
|
||||
if ('error' in params)
|
||||
xhr.onerror = params.error || undefined;
|
||||
|
||||
xhr.onreadystatechange = function getPdfOnreadystatechange() {
|
||||
if (xhr.readyState === 4 && xhr.status === xhr.expected) {
|
||||
var data = (xhr.mozResponseArrayBuffer || xhr.mozResponse ||
|
||||
xhr.responseArrayBuffer || xhr.response);
|
||||
callback(data);
|
||||
xhr.onreadystatechange = function getPdfOnreadystatechange(e) {
|
||||
if (xhr.readyState === 4) {
|
||||
if (xhr.status === xhr.expected) {
|
||||
var data = (xhr.mozResponseArrayBuffer || xhr.mozResponse ||
|
||||
xhr.responseArrayBuffer || xhr.response);
|
||||
callback(data);
|
||||
} else if (params.error) {
|
||||
params.error(e);
|
||||
}
|
||||
}
|
||||
};
|
||||
xhr.send(null);
|
||||
}
|
||||
globalScope.PDFJS.getPdf = getPdf;
|
||||
|
||||
var Page = (function pagePage() {
|
||||
function constructor(xref, pageNumber, pageDict, ref) {
|
||||
var Page = (function PageClosure() {
|
||||
function Page(xref, pageNumber, pageDict, ref) {
|
||||
this.pageNumber = pageNumber;
|
||||
this.pageDict = pageDict;
|
||||
this.stats = {
|
||||
@ -63,9 +69,11 @@ var Page = (function pagePage() {
|
||||
};
|
||||
this.xref = xref;
|
||||
this.ref = ref;
|
||||
|
||||
this.displayReadyPromise = null;
|
||||
}
|
||||
|
||||
constructor.prototype = {
|
||||
Page.prototype = {
|
||||
getPageProp: function pageGetPageProp(key) {
|
||||
return this.xref.fetchIfRef(this.pageDict.get(key));
|
||||
},
|
||||
@ -101,9 +109,11 @@ var Page = (function pagePage() {
|
||||
width: this.width,
|
||||
height: this.height
|
||||
};
|
||||
var mediaBox = this.mediaBox;
|
||||
var offsetX = mediaBox[0], offsetY = mediaBox[1];
|
||||
if (isArray(obj) && obj.length == 4) {
|
||||
var tl = this.rotatePoint(obj[0], obj[1]);
|
||||
var br = this.rotatePoint(obj[2], obj[3]);
|
||||
var tl = this.rotatePoint(obj[0] - offsetX, obj[1] - offsetY);
|
||||
var br = this.rotatePoint(obj[2] - offsetX, obj[3] - offsetY);
|
||||
view.x = Math.min(tl.x, br.x);
|
||||
view.y = Math.min(tl.y, br.y);
|
||||
view.width = Math.abs(tl.x - br.x);
|
||||
@ -156,18 +166,12 @@ var Page = (function pagePage() {
|
||||
IRQueue, fonts) {
|
||||
var self = this;
|
||||
this.IRQueue = IRQueue;
|
||||
var gfx = new CanvasGraphics(this.ctx, this.objs);
|
||||
|
||||
var displayContinuation = function pageDisplayContinuation() {
|
||||
// Always defer call to display() to work around bug in
|
||||
// Firefox error reporting from XHR callbacks.
|
||||
setTimeout(function pageSetTimeout() {
|
||||
try {
|
||||
self.display(gfx, self.callback);
|
||||
} catch (e) {
|
||||
if (self.callback) self.callback(e.toString());
|
||||
throw e;
|
||||
}
|
||||
self.displayReadyPromise.resolve();
|
||||
});
|
||||
};
|
||||
|
||||
@ -241,6 +245,7 @@ var Page = (function pagePage() {
|
||||
startIdx = gfx.executeIRQueue(IRQueue, startIdx, next);
|
||||
if (startIdx == length) {
|
||||
self.stats.render = Date.now();
|
||||
gfx.endDrawing();
|
||||
if (callback) callback();
|
||||
}
|
||||
}
|
||||
@ -262,57 +267,160 @@ var Page = (function pagePage() {
|
||||
}
|
||||
},
|
||||
getLinks: function pageGetLinks() {
|
||||
var links = [];
|
||||
var annotations = pageGetAnnotations();
|
||||
var i, n = annotations.length;
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (annotations[i].type != 'Link')
|
||||
continue;
|
||||
links.push(annotations[i]);
|
||||
}
|
||||
return links;
|
||||
},
|
||||
getAnnotations: function pageGetAnnotations() {
|
||||
var xref = this.xref;
|
||||
function getInheritableProperty(annotation, name) {
|
||||
var item = annotation;
|
||||
while (item && !item.has(name)) {
|
||||
item = xref.fetchIfRef(item.get('Parent'));
|
||||
}
|
||||
if (!item)
|
||||
return null;
|
||||
return item.get(name);
|
||||
}
|
||||
|
||||
var annotations = xref.fetchIfRef(this.annotations) || [];
|
||||
var i, n = annotations.length;
|
||||
var links = [];
|
||||
var items = [];
|
||||
for (i = 0; i < n; ++i) {
|
||||
var annotation = xref.fetch(annotations[i]);
|
||||
var annotationRef = annotations[i];
|
||||
var annotation = xref.fetch(annotationRef);
|
||||
if (!isDict(annotation))
|
||||
continue;
|
||||
var subtype = annotation.get('Subtype');
|
||||
if (!isName(subtype) || subtype.name != 'Link')
|
||||
if (!isName(subtype))
|
||||
continue;
|
||||
var rect = annotation.get('Rect');
|
||||
var topLeftCorner = this.rotatePoint(rect[0], rect[1]);
|
||||
var bottomRightCorner = this.rotatePoint(rect[2], rect[3]);
|
||||
|
||||
var link = {};
|
||||
link.x = Math.min(topLeftCorner.x, bottomRightCorner.x);
|
||||
link.y = Math.min(topLeftCorner.y, bottomRightCorner.y);
|
||||
link.width = Math.abs(topLeftCorner.x - bottomRightCorner.x);
|
||||
link.height = Math.abs(topLeftCorner.y - bottomRightCorner.y);
|
||||
var a = this.xref.fetchIfRef(annotation.get('A'));
|
||||
if (a) {
|
||||
switch (a.get('S').name) {
|
||||
case 'URI':
|
||||
link.url = a.get('URI');
|
||||
var item = {};
|
||||
item.type = subtype.name;
|
||||
item.x = Math.min(topLeftCorner.x, bottomRightCorner.x);
|
||||
item.y = Math.min(topLeftCorner.y, bottomRightCorner.y);
|
||||
item.width = Math.abs(topLeftCorner.x - bottomRightCorner.x);
|
||||
item.height = Math.abs(topLeftCorner.y - bottomRightCorner.y);
|
||||
switch (subtype.name) {
|
||||
case 'Link':
|
||||
var a = this.xref.fetchIfRef(annotation.get('A'));
|
||||
if (a) {
|
||||
switch (a.get('S').name) {
|
||||
case 'URI':
|
||||
item.url = a.get('URI');
|
||||
break;
|
||||
case 'GoTo':
|
||||
item.dest = a.get('D');
|
||||
break;
|
||||
default:
|
||||
TODO('other link types');
|
||||
}
|
||||
} else if (annotation.has('Dest')) {
|
||||
// simple destination link
|
||||
var dest = annotation.get('Dest');
|
||||
item.dest = isName(dest) ? dest.name : dest;
|
||||
}
|
||||
break;
|
||||
case 'Widget':
|
||||
var fieldType = getInheritableProperty(annotation, 'FT');
|
||||
if (!isName(fieldType))
|
||||
break;
|
||||
case 'GoTo':
|
||||
link.dest = a.get('D');
|
||||
break;
|
||||
default:
|
||||
TODO('other link types');
|
||||
}
|
||||
} else if (annotation.has('Dest')) {
|
||||
// simple destination link
|
||||
var dest = annotation.get('Dest');
|
||||
link.dest = isName(dest) ? dest.name : dest;
|
||||
item.fieldType = fieldType.name;
|
||||
// Building the full field name by collecting the field and
|
||||
// its ancestors 'T' properties and joining them using '.'.
|
||||
var fieldName = [];
|
||||
var namedItem = annotation, ref = annotationRef;
|
||||
while (namedItem) {
|
||||
var parentRef = namedItem.get('Parent');
|
||||
var parent = xref.fetchIfRef(parentRef);
|
||||
var name = namedItem.get('T');
|
||||
if (name)
|
||||
fieldName.unshift(stringToPDFString(name));
|
||||
else {
|
||||
// The field name is absent, that means more than one field
|
||||
// with the same name may exist. Replacing the empty name
|
||||
// with the '`' plus index in the parent's 'Kids' array.
|
||||
// This is not in the PDF spec but necessary to id the
|
||||
// the input controls.
|
||||
var kids = xref.fetchIfRef(parent.get('Kids'));
|
||||
var j, jj;
|
||||
for (j = 0, jj = kids.length; j < jj; j++) {
|
||||
if (kids[j].num == ref.num && kids[j].gen == ref.gen)
|
||||
break;
|
||||
}
|
||||
fieldName.unshift('`' + j);
|
||||
}
|
||||
namedItem = parent;
|
||||
ref = parentRef;
|
||||
}
|
||||
item.fullName = fieldName.join('.');
|
||||
var alternativeText = stringToPDFString(annotation.get('TU') || '');
|
||||
item.alternativeText = alternativeText;
|
||||
var da = getInheritableProperty(annotation, 'DA') || '';
|
||||
var m = /([\d\.]+)\sTf/.exec(da);
|
||||
if (m)
|
||||
item.fontSize = parseFloat(m[1]);
|
||||
item.textAlignment = getInheritableProperty(annotation, 'Q');
|
||||
item.flags = getInheritableProperty(annotation, 'Ff') || 0;
|
||||
break;
|
||||
case 'Text':
|
||||
var content = annotation.get('Contents');
|
||||
var title = annotation.get('T');
|
||||
item.content = stringToPDFString(content || '');
|
||||
item.title = stringToPDFString(title || '');
|
||||
item.name = annotation.get('Name').name;
|
||||
break;
|
||||
default:
|
||||
TODO('unimplemented annotation type: ' + subtype.name);
|
||||
break;
|
||||
}
|
||||
links.push(link);
|
||||
items.push(item);
|
||||
}
|
||||
return links;
|
||||
return items;
|
||||
},
|
||||
startRendering: function pageStartRendering(ctx, callback) {
|
||||
this.ctx = ctx;
|
||||
this.callback = callback;
|
||||
|
||||
startRendering: function pageStartRendering(ctx, callback, textLayer) {
|
||||
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;
|
||||
}
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
return constructor;
|
||||
return Page;
|
||||
})();
|
||||
|
||||
/**
|
||||
@ -325,8 +433,8 @@ var Page = (function pagePage() {
|
||||
* need for the `PDFDocModel` anymore and there is only one object on the
|
||||
* main thread and not one entire copy on each worker instance.
|
||||
*/
|
||||
var PDFDocModel = (function pdfDoc() {
|
||||
function constructor(arg, callback) {
|
||||
var PDFDocModel = (function PDFDocModelClosure() {
|
||||
function PDFDocModel(arg, callback) {
|
||||
if (isStream(arg))
|
||||
init.call(this, arg);
|
||||
else if (isArrayBuffer(arg))
|
||||
@ -339,6 +447,7 @@ var PDFDocModel = (function pdfDoc() {
|
||||
assertWellFormed(stream.length > 0, 'stream must have data');
|
||||
this.stream = stream;
|
||||
this.setup();
|
||||
this.acroForm = this.xref.fetchIfRef(this.catalog.catDict.get('AcroForm'));
|
||||
}
|
||||
|
||||
function find(stream, needle, limit, backwards) {
|
||||
@ -357,7 +466,7 @@ var PDFDocModel = (function pdfDoc() {
|
||||
return true; /* found */
|
||||
}
|
||||
|
||||
constructor.prototype = {
|
||||
PDFDocModel.prototype = {
|
||||
get linearization() {
|
||||
var length = this.stream.length;
|
||||
var linearization = false;
|
||||
@ -379,12 +488,17 @@ var PDFDocModel = (function pdfDoc() {
|
||||
if (find(stream, 'endobj', 1024))
|
||||
startXRef = stream.pos + 6;
|
||||
} else {
|
||||
// Find startxref at the end of the file.
|
||||
var start = stream.end - 1024;
|
||||
if (start < 0)
|
||||
start = 0;
|
||||
stream.pos = start;
|
||||
if (find(stream, 'startxref', 1024, true)) {
|
||||
// Find startxref by jumping backward from the end of the file.
|
||||
var step = 1024;
|
||||
var found = false, pos = stream.end;
|
||||
while (!found && pos > 0) {
|
||||
pos -= step - 'startxref'.length;
|
||||
if (pos < 0)
|
||||
pos = 0;
|
||||
stream.pos = pos;
|
||||
found = find(stream, 'startxref', step, true);
|
||||
}
|
||||
if (found) {
|
||||
stream.skip(9);
|
||||
var ch;
|
||||
do {
|
||||
@ -425,10 +539,19 @@ var PDFDocModel = (function pdfDoc() {
|
||||
},
|
||||
setup: function pdfDocSetup(ownerPassword, userPassword) {
|
||||
this.checkHeader();
|
||||
this.xref = new XRef(this.stream,
|
||||
this.startXRef,
|
||||
this.mainXRefEntriesOffset);
|
||||
this.catalog = new Catalog(this.xref);
|
||||
var xref = new XRef(this.stream,
|
||||
this.startXRef,
|
||||
this.mainXRefEntriesOffset);
|
||||
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() {
|
||||
var linearization = this.linearization;
|
||||
@ -436,16 +559,32 @@ var PDFDocModel = (function pdfDoc() {
|
||||
// shadow the prototype getter
|
||||
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) {
|
||||
return this.catalog.getPage(n);
|
||||
}
|
||||
};
|
||||
|
||||
return constructor;
|
||||
return PDFDocModel;
|
||||
})();
|
||||
|
||||
var PDFDoc = (function pdfDoc() {
|
||||
function constructor(arg, callback) {
|
||||
var PDFDoc = (function PDFDocClosure() {
|
||||
function PDFDoc(arg, callback) {
|
||||
var stream = null;
|
||||
var data = null;
|
||||
|
||||
@ -462,7 +601,7 @@ var PDFDoc = (function pdfDoc() {
|
||||
this.data = data;
|
||||
this.stream = stream;
|
||||
this.pdf = new PDFDocModel(stream);
|
||||
|
||||
this.fingerprint = this.pdf.getFingerprint();
|
||||
this.catalog = this.pdf.catalog;
|
||||
this.objs = new PDFObjects();
|
||||
|
||||
@ -481,39 +620,38 @@ var PDFDoc = (function pdfDoc() {
|
||||
throw 'No PDFJS.workerSrc specified';
|
||||
}
|
||||
|
||||
var worker;
|
||||
try {
|
||||
worker = new Worker(workerSrc);
|
||||
} catch (e) {
|
||||
// Some versions of FF can't create a worker on localhost, see:
|
||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=683280
|
||||
globalScope.PDFJS.disableWorker = true;
|
||||
this.setupFakeWorker();
|
||||
var worker = new Worker(workerSrc);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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();
|
||||
} catch (e) {}
|
||||
}
|
||||
// Either workers are disabled, not supported or have thrown an exception.
|
||||
// Thus, we fallback to a faked worker.
|
||||
globalScope.PDFJS.disableWorker = true;
|
||||
this.setupFakeWorker();
|
||||
}
|
||||
|
||||
constructor.prototype = {
|
||||
PDFDoc.prototype = {
|
||||
setupFakeWorker: function() {
|
||||
// If we don't use a worker, just post/sendMessage to the main thread.
|
||||
var fakeWorker = {
|
||||
@ -549,8 +687,12 @@ var PDFDoc = (function pdfDoc() {
|
||||
|
||||
switch (type) {
|
||||
case 'JpegStream':
|
||||
var IR = data[2];
|
||||
new JpegImageLoader(id, IR, this.objs);
|
||||
var imageData = data[2];
|
||||
loadJpegStream(id, imageData, this.objs);
|
||||
break;
|
||||
case 'Image':
|
||||
var imageData = data[2];
|
||||
this.objs.resolve(id, imageData);
|
||||
break;
|
||||
case 'Font':
|
||||
var name = data[2];
|
||||
@ -558,20 +700,9 @@ var PDFDoc = (function pdfDoc() {
|
||||
var properties = data[4];
|
||||
|
||||
if (file) {
|
||||
// Rewrap the ArrayBuffer in a stream.
|
||||
var fontFileDict = new Dict();
|
||||
fontFileDict.map = file.dict.map;
|
||||
|
||||
var fontFile = new Stream(file.bytes, file.start,
|
||||
file.end - file.start, fontFileDict);
|
||||
|
||||
// Check if this is a FlateStream. Otherwise just use the created
|
||||
// Stream one. This makes complex_ttf_font.pdf work.
|
||||
var cmf = file.bytes[0];
|
||||
if ((cmf & 0x0f) == 0x08) {
|
||||
file = new FlateStream(fontFile);
|
||||
} else {
|
||||
file = fontFile;
|
||||
}
|
||||
file = new Stream(file, 0, file.length, fontFileDict);
|
||||
}
|
||||
|
||||
// For now, resolve the font object here direclty. The real font
|
||||
@ -599,6 +730,49 @@ var PDFDoc = (function pdfDoc() {
|
||||
}
|
||||
}.bind(this));
|
||||
|
||||
messageHandler.on('page_error', function pdfDocError(data) {
|
||||
var page = this.pageCache[data.pageNum];
|
||||
if (page.displayReadyPromise)
|
||||
page.displayReadyPromise.reject(data.error);
|
||||
else
|
||||
throw data.error;
|
||||
}, this);
|
||||
|
||||
messageHandler.on('jpeg_decode', function(data, promise) {
|
||||
var imageData = data[0];
|
||||
var components = data[1];
|
||||
if (components != 3 && components != 1)
|
||||
error('Only 3 component or 1 component can be returned');
|
||||
|
||||
var img = new Image();
|
||||
img.onload = (function jpegImageLoaderOnload() {
|
||||
var width = img.width;
|
||||
var height = img.height;
|
||||
var size = width * height;
|
||||
var rgbaLength = size * 4;
|
||||
var buf = new Uint8Array(size * components);
|
||||
var tmpCanvas = new ScratchCanvas(width, height);
|
||||
var tmpCtx = tmpCanvas.getContext('2d');
|
||||
tmpCtx.drawImage(img, 0, 0);
|
||||
var data = tmpCtx.getImageData(0, 0, width, height).data;
|
||||
|
||||
if (components == 3) {
|
||||
for (var i = 0, j = 0; i < rgbaLength; i += 4, j += 3) {
|
||||
buf[j] = data[i];
|
||||
buf[j + 1] = data[i + 1];
|
||||
buf[j + 2] = data[i + 2];
|
||||
}
|
||||
} else if (components == 1) {
|
||||
for (var i = 0, j = 0; i < rgbaLength; i += 4, j++) {
|
||||
buf[j] = data[i];
|
||||
}
|
||||
}
|
||||
promise.resolve({ data: buf, width: width, height: height});
|
||||
}).bind(this);
|
||||
var src = 'data:image/jpeg;base64,' + window.btoa(imageData);
|
||||
img.src = src;
|
||||
});
|
||||
|
||||
setTimeout(function pdfDocFontReadySetTimeout() {
|
||||
messageHandler.send('doc', this.data);
|
||||
this.workerReadyPromise.resolve(true);
|
||||
@ -645,7 +819,7 @@ var PDFDoc = (function pdfDoc() {
|
||||
}
|
||||
};
|
||||
|
||||
return constructor;
|
||||
return PDFDoc;
|
||||
})();
|
||||
|
||||
globalScope.PDFJS.PDFDoc = PDFDoc;
|
||||
|
@ -3,8 +3,8 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var ARCFourCipher = (function arcFourCipher() {
|
||||
function constructor(key) {
|
||||
var ARCFourCipher = (function ARCFourCipherClosure() {
|
||||
function ARCFourCipher(key) {
|
||||
this.a = 0;
|
||||
this.b = 0;
|
||||
var s = new Uint8Array(256);
|
||||
@ -20,7 +20,7 @@ var ARCFourCipher = (function arcFourCipher() {
|
||||
this.s = s;
|
||||
}
|
||||
|
||||
constructor.prototype = {
|
||||
ARCFourCipher.prototype = {
|
||||
encryptBlock: function arcFourCipherEncryptBlock(data) {
|
||||
var i, n = data.length, tmp, tmp2;
|
||||
var a = this.a, b = this.b, s = this.s;
|
||||
@ -39,12 +39,12 @@ var ARCFourCipher = (function arcFourCipher() {
|
||||
return output;
|
||||
}
|
||||
};
|
||||
constructor.prototype.decryptBlock = constructor.prototype.encryptBlock;
|
||||
ARCFourCipher.prototype.decryptBlock = ARCFourCipher.prototype.encryptBlock;
|
||||
|
||||
return constructor;
|
||||
return ARCFourCipher;
|
||||
})();
|
||||
|
||||
var calculateMD5 = (function calculateMD5() {
|
||||
var calculateMD5 = (function calculateMD5Closure() {
|
||||
var r = new Uint8Array([
|
||||
7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
|
||||
5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20,
|
||||
@ -128,20 +128,20 @@ var calculateMD5 = (function calculateMD5() {
|
||||
return hash;
|
||||
})();
|
||||
|
||||
var NullCipher = (function nullCipher() {
|
||||
function constructor() {
|
||||
var NullCipher = (function NullCipherClosure() {
|
||||
function NullCipher() {
|
||||
}
|
||||
|
||||
constructor.prototype = {
|
||||
NullCipher.prototype = {
|
||||
decryptBlock: function nullCipherDecryptBlock(data) {
|
||||
return data;
|
||||
}
|
||||
};
|
||||
|
||||
return constructor;
|
||||
return NullCipher;
|
||||
})();
|
||||
|
||||
var AES128Cipher = (function aes128Cipher() {
|
||||
var AES128Cipher = (function AES128CipherClosure() {
|
||||
var rcon = new Uint8Array([
|
||||
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c,
|
||||
0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a,
|
||||
@ -330,7 +330,7 @@ var AES128Cipher = (function aes128Cipher() {
|
||||
return state;
|
||||
}
|
||||
|
||||
function constructor(key) {
|
||||
function AES128Cipher(key) {
|
||||
this.key = expandKey128(key);
|
||||
this.buffer = new Uint8Array(16);
|
||||
this.bufferPosition = 0;
|
||||
@ -370,7 +370,7 @@ var AES128Cipher = (function aes128Cipher() {
|
||||
return output;
|
||||
}
|
||||
|
||||
constructor.prototype = {
|
||||
AES128Cipher.prototype = {
|
||||
decryptBlock: function aes128CipherDecryptBlock(data) {
|
||||
var i, sourceLength = data.length;
|
||||
var buffer = this.buffer, bufferLength = this.bufferPosition;
|
||||
@ -391,15 +391,15 @@ var AES128Cipher = (function aes128Cipher() {
|
||||
}
|
||||
};
|
||||
|
||||
return constructor;
|
||||
return AES128Cipher;
|
||||
})();
|
||||
|
||||
var CipherTransform = (function cipherTransform() {
|
||||
function constructor(stringCipherConstructor, streamCipherConstructor) {
|
||||
var CipherTransform = (function CipherTransformClosure() {
|
||||
function CipherTransform(stringCipherConstructor, streamCipherConstructor) {
|
||||
this.stringCipherConstructor = stringCipherConstructor;
|
||||
this.streamCipherConstructor = streamCipherConstructor;
|
||||
}
|
||||
constructor.prototype = {
|
||||
CipherTransform.prototype = {
|
||||
createStream: function cipherTransformCreateStream(stream) {
|
||||
var cipher = new this.streamCipherConstructor();
|
||||
return new DecryptStream(stream,
|
||||
@ -415,10 +415,10 @@ var CipherTransform = (function cipherTransform() {
|
||||
return bytesToString(data);
|
||||
}
|
||||
};
|
||||
return constructor;
|
||||
return CipherTransform;
|
||||
})();
|
||||
|
||||
var CipherTransformFactory = (function cipherTransformFactory() {
|
||||
var CipherTransformFactory = (function CipherTransformFactoryClosure() {
|
||||
function prepareKeyData(fileId, password, ownerPassword, userPassword,
|
||||
flags, revision, keyLength, encryptMetadata) {
|
||||
var defaultPasswordBytes = new Uint8Array([
|
||||
@ -490,7 +490,7 @@ var CipherTransformFactory = (function cipherTransformFactory() {
|
||||
|
||||
var identityName = new Name('Identity');
|
||||
|
||||
function constructor(dict, fileId, password) {
|
||||
function CipherTransformFactory(dict, fileId, password) {
|
||||
var filter = dict.get('Filter');
|
||||
if (!isName(filter) || filter.name != 'Standard')
|
||||
error('unknown encryption method');
|
||||
@ -573,7 +573,7 @@ var CipherTransformFactory = (function cipherTransformFactory() {
|
||||
return null;
|
||||
}
|
||||
|
||||
constructor.prototype = {
|
||||
CipherTransformFactory.prototype = {
|
||||
createCipherTransform: function buildCipherCreateCipherTransform(num,
|
||||
gen) {
|
||||
if (this.algorithm == 4) {
|
||||
@ -592,6 +592,6 @@ var CipherTransformFactory = (function cipherTransformFactory() {
|
||||
}
|
||||
};
|
||||
|
||||
return constructor;
|
||||
return CipherTransformFactory;
|
||||
})();
|
||||
|
||||
|
137
src/evaluator.js
137
src/evaluator.js
@ -3,8 +3,8 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var PartialEvaluator = (function partialEvaluator() {
|
||||
function constructor(xref, handler, uniquePrefix) {
|
||||
var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||
function PartialEvaluator(xref, handler, uniquePrefix) {
|
||||
this.state = new EvalState();
|
||||
this.stateStack = [];
|
||||
|
||||
@ -111,7 +111,7 @@ var PartialEvaluator = (function partialEvaluator() {
|
||||
EX: 'endCompat'
|
||||
};
|
||||
|
||||
constructor.prototype = {
|
||||
PartialEvaluator.prototype = {
|
||||
getIRQueue: function partialEvaluatorGetIRQueue(stream, resources,
|
||||
queue, dependency) {
|
||||
|
||||
@ -155,6 +155,11 @@ var PartialEvaluator = (function partialEvaluator() {
|
||||
font.loadedName = loadedName;
|
||||
|
||||
var translated = font.translated;
|
||||
// Convert the file to an ArrayBuffer which will be turned back into
|
||||
// a Stream in the main thread.
|
||||
if (translated.file)
|
||||
translated.file = translated.file.getBytes();
|
||||
|
||||
handler.send('obj', [
|
||||
loadedName,
|
||||
'Font',
|
||||
@ -179,62 +184,54 @@ var PartialEvaluator = (function partialEvaluator() {
|
||||
var w = dict.get('Width', 'W');
|
||||
var h = dict.get('Height', 'H');
|
||||
|
||||
if (image instanceof JpegStream && image.isNative) {
|
||||
var objId = 'img_' + uniquePrefix + (++self.objIdCounter);
|
||||
handler.send('obj', [objId, 'JpegStream', image.getIR()]);
|
||||
var imageMask = dict.get('ImageMask', 'IM') || false;
|
||||
if (imageMask) {
|
||||
// This depends on a tmpCanvas beeing filled with the
|
||||
// current fillStyle, such that processing the pixel
|
||||
// data can't be done here. Instead of creating a
|
||||
// complete PDFImage, only read the information needed
|
||||
// for later.
|
||||
|
||||
// Add the dependency on the image object.
|
||||
insertDependency([objId]);
|
||||
|
||||
// The normal fn.
|
||||
fn = 'paintJpegXObject';
|
||||
args = [objId, w, h];
|
||||
var width = dict.get('Width', 'W');
|
||||
var height = dict.get('Height', 'H');
|
||||
var bitStrideLength = (width + 7) >> 3;
|
||||
var imgArray = image.getBytes(bitStrideLength * height);
|
||||
var decode = dict.get('Decode', 'D');
|
||||
var inverseDecode = !!decode && decode[0] > 0;
|
||||
|
||||
fn = 'paintImageMaskXObject';
|
||||
args = [imgArray, inverseDecode, width, height];
|
||||
return;
|
||||
}
|
||||
|
||||
// Needs to be rendered ourself.
|
||||
|
||||
// Figure out if the image has an imageMask.
|
||||
var imageMask = dict.get('ImageMask', 'IM') || false;
|
||||
|
||||
// If there is no imageMask, create the PDFImage and a lot
|
||||
// of image processing can be done here.
|
||||
if (!imageMask) {
|
||||
var imageObj = new PDFImage(xref, resources, image, inline);
|
||||
var objId = 'img_' + uniquePrefix + (++self.objIdCounter);
|
||||
insertDependency([objId]);
|
||||
args = [objId, w, h];
|
||||
|
||||
if (imageObj.imageMask) {
|
||||
throw 'Can\'t handle this in the web worker :/';
|
||||
}
|
||||
|
||||
var imgData = {
|
||||
width: w,
|
||||
height: h,
|
||||
data: new Uint8Array(w * h * 4)
|
||||
};
|
||||
var pixels = imgData.data;
|
||||
imageObj.fillRgbaBuffer(pixels, imageObj.decode);
|
||||
|
||||
fn = 'paintImageXObject';
|
||||
args = [imgData];
|
||||
var softMask = dict.get('SMask', 'IM') || false;
|
||||
if (!softMask && image instanceof JpegStream && image.isNative) {
|
||||
// These JPEGs don't need any more processing so we can just send it.
|
||||
fn = 'paintJpegXObject';
|
||||
handler.send('obj', [objId, 'JpegStream', image.getIR()]);
|
||||
return;
|
||||
}
|
||||
|
||||
// This depends on a tmpCanvas beeing filled with the
|
||||
// current fillStyle, such that processing the pixel
|
||||
// data can't be done here. Instead of creating a
|
||||
// complete PDFImage, only read the information needed
|
||||
// for later.
|
||||
fn = 'paintImageMaskXObject';
|
||||
fn = 'paintImageXObject';
|
||||
|
||||
var width = dict.get('Width', 'W');
|
||||
var height = dict.get('Height', 'H');
|
||||
var bitStrideLength = (width + 7) >> 3;
|
||||
var imgArray = image.getBytes(bitStrideLength * height);
|
||||
var decode = dict.get('Decode', 'D');
|
||||
var inverseDecode = !!decode && decode[0] > 0;
|
||||
|
||||
args = [imgArray, inverseDecode, width, height];
|
||||
PDFImage.buildImage(function(imageObj) {
|
||||
var drawWidth = imageObj.drawWidth;
|
||||
var drawHeight = imageObj.drawHeight;
|
||||
var imgData = {
|
||||
width: drawWidth,
|
||||
height: drawHeight,
|
||||
data: new Uint8Array(drawWidth * drawHeight * 4)
|
||||
};
|
||||
var pixels = imgData.data;
|
||||
imageObj.fillRgbaBuffer(pixels, drawWidth, drawHeight);
|
||||
handler.send('obj', [objId, 'Image', imgData]);
|
||||
}, handler, xref, resources, image, inline);
|
||||
}
|
||||
|
||||
uniquePrefix = uniquePrefix || '';
|
||||
@ -493,6 +490,8 @@ var PartialEvaluator = (function partialEvaluator() {
|
||||
var baseName = encoding.get('BaseEncoding');
|
||||
if (baseName)
|
||||
baseEncoding = Encodings[baseName.name];
|
||||
else
|
||||
hasEncoding = false; // base encoding was not provided
|
||||
|
||||
// Load the differences between the base and original
|
||||
if (encoding.has('Differences')) {
|
||||
@ -512,6 +511,7 @@ var PartialEvaluator = (function partialEvaluator() {
|
||||
error('Encoding is not a Name nor a Dict');
|
||||
}
|
||||
}
|
||||
|
||||
properties.differences = differences;
|
||||
properties.baseEncoding = baseEncoding;
|
||||
properties.hasEncoding = hasEncoding;
|
||||
@ -554,9 +554,21 @@ var PartialEvaluator = (function partialEvaluator() {
|
||||
var startRange = tokens[j];
|
||||
var endRange = tokens[j + 1];
|
||||
var code = tokens[j + 2];
|
||||
while (startRange <= endRange) {
|
||||
charToUnicode[startRange] = code++;
|
||||
++startRange;
|
||||
if (code == 0xFFFF) {
|
||||
// CMap is broken, assuming code == startRange
|
||||
code = startRange;
|
||||
}
|
||||
if (isArray(code)) {
|
||||
var codeindex = 0;
|
||||
while (startRange <= endRange) {
|
||||
charToUnicode[startRange] = code[codeindex++];
|
||||
++startRange;
|
||||
}
|
||||
} else {
|
||||
while (startRange <= endRange) {
|
||||
charToUnicode[startRange] = code++;
|
||||
++startRange;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -595,9 +607,18 @@ var PartialEvaluator = (function partialEvaluator() {
|
||||
}
|
||||
} else if (byte == 0x3E) {
|
||||
if (token.length) {
|
||||
// parsing hex number
|
||||
tokens.push(parseInt(token, 16));
|
||||
token = '';
|
||||
if (token.length <= 4) {
|
||||
// parsing hex number
|
||||
tokens.push(parseInt(token, 16));
|
||||
token = '';
|
||||
} else {
|
||||
// parsing hex UTF-16BE numbers
|
||||
var str = [];
|
||||
for (var i = 0, ii = token.length; i < ii; i += 4)
|
||||
str.push(parseInt(token.substr(i, 4), 16));
|
||||
tokens.push(String.fromCharCode.apply(String, str));
|
||||
token = '';
|
||||
}
|
||||
}
|
||||
} else {
|
||||
token += String.fromCharCode(byte);
|
||||
@ -829,11 +850,11 @@ var PartialEvaluator = (function partialEvaluator() {
|
||||
}
|
||||
};
|
||||
|
||||
return constructor;
|
||||
return PartialEvaluator;
|
||||
})();
|
||||
|
||||
var EvalState = (function evalState() {
|
||||
function constructor() {
|
||||
var EvalState = (function EvalStateClosure() {
|
||||
function EvalState() {
|
||||
// Are soft masks and alpha values shapes or opacities?
|
||||
this.alphaIsShape = false;
|
||||
this.fontSize = 0;
|
||||
@ -850,8 +871,8 @@ var EvalState = (function evalState() {
|
||||
this.fillColorSpace = null;
|
||||
this.strokeColorSpace = null;
|
||||
}
|
||||
constructor.prototype = {
|
||||
EvalState.prototype = {
|
||||
};
|
||||
return constructor;
|
||||
return EvalState;
|
||||
})();
|
||||
|
||||
|
435
src/fonts.js
435
src/fonts.js
@ -3,8 +3,6 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var isWorker = (typeof window == 'undefined');
|
||||
|
||||
/**
|
||||
* Maximum time to wait for a font to be loaded by font-face rules.
|
||||
*/
|
||||
@ -719,20 +717,10 @@ function getUnicodeRangeFor(value) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
function adaptUnicode(unicode) {
|
||||
return (unicode <= 0x1F || (unicode >= 127 && unicode < kSizeOfGlyphArea)) ?
|
||||
unicode + kCmapGlyphOffset : unicode;
|
||||
}
|
||||
|
||||
function isAdaptedUnicode(unicode) {
|
||||
return unicode >= kCmapGlyphOffset &&
|
||||
unicode < kCmapGlyphOffset + kSizeOfGlyphArea;
|
||||
}
|
||||
|
||||
function isSpecialUnicode(unicode) {
|
||||
return (unicode <= 0x1F || (unicode >= 127 && unicode < kSizeOfGlyphArea)) ||
|
||||
unicode >= kCmapGlyphOffset &&
|
||||
unicode < kCmapGlyphOffset + kSizeOfGlyphArea;
|
||||
(unicode >= kCmapGlyphOffset &&
|
||||
unicode < kCmapGlyphOffset + kSizeOfGlyphArea);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -743,8 +731,8 @@ function isSpecialUnicode(unicode) {
|
||||
* var type1Font = new Font("MyFontName", binaryFile, propertiesObject);
|
||||
* type1Font.bind();
|
||||
*/
|
||||
var Font = (function Font() {
|
||||
var constructor = function font_constructor(name, file, properties) {
|
||||
var Font = (function FontClosure() {
|
||||
function Font(name, file, properties) {
|
||||
this.name = name;
|
||||
this.coded = properties.coded;
|
||||
this.charProcIRQueues = properties.charProcIRQueues;
|
||||
@ -771,16 +759,23 @@ var Font = (function Font() {
|
||||
this.widths = properties.widths;
|
||||
this.defaultWidth = properties.defaultWidth;
|
||||
this.composite = properties.composite;
|
||||
this.toUnicode = properties.toUnicode;
|
||||
this.hasEncoding = properties.hasEncoding;
|
||||
|
||||
this.fontMatrix = properties.fontMatrix;
|
||||
if (properties.type == 'Type3')
|
||||
this.widthMultiplier = 1.0;
|
||||
if (properties.type == 'Type3') {
|
||||
this.encoding = properties.baseEncoding;
|
||||
return;
|
||||
}
|
||||
|
||||
// Trying to fix encoding using glyph CIDSystemInfo.
|
||||
this.loadCidToUnicode(properties);
|
||||
|
||||
if (properties.toUnicode)
|
||||
this.toUnicode = properties.toUnicode;
|
||||
else
|
||||
this.rebuildToUnicode(properties);
|
||||
|
||||
if (!file) {
|
||||
// The file data is not specified. Trying to fix the font name
|
||||
// to be used with the canvas.font.
|
||||
@ -832,6 +827,8 @@ var Font = (function Font() {
|
||||
|
||||
this.data = data;
|
||||
this.fontMatrix = properties.fontMatrix;
|
||||
this.widthMultiplier = !properties.fontMatrix ? 1.0 :
|
||||
1.0 / properties.fontMatrix[0];
|
||||
this.encoding = properties.baseEncoding;
|
||||
this.hasShortCmap = properties.hasShortCmap;
|
||||
this.loadedName = getUniqueName();
|
||||
@ -887,6 +884,13 @@ var Font = (function Font() {
|
||||
String.fromCharCode(value & 0xff);
|
||||
};
|
||||
|
||||
function safeString16(value) {
|
||||
// clamp value to the 16-bit int range
|
||||
value = value > 0x7FFF ? 0x7FFF : value < -0x8000 ? -0x8000 : value;
|
||||
return String.fromCharCode((value >> 8) & 0xff) +
|
||||
String.fromCharCode(value & 0xff);
|
||||
};
|
||||
|
||||
function string32(value) {
|
||||
return String.fromCharCode((value >> 24) & 0xff) +
|
||||
String.fromCharCode((value >> 16) & 0xff) +
|
||||
@ -961,15 +965,15 @@ var Font = (function Font() {
|
||||
var ranges = [];
|
||||
for (var n = 0; n < length; ) {
|
||||
var start = codes[n].unicode;
|
||||
var startCode = codes[n].code;
|
||||
var codeIndices = [codes[n].code];
|
||||
++n;
|
||||
var end = start;
|
||||
while (n < length && end + 1 == codes[n].unicode) {
|
||||
codeIndices.push(codes[n].code);
|
||||
++end;
|
||||
++n;
|
||||
}
|
||||
var endCode = codes[n - 1].code;
|
||||
ranges.push([start, end, startCode, endCode]);
|
||||
ranges.push([start, end, codeIndices]);
|
||||
}
|
||||
|
||||
return ranges;
|
||||
@ -1012,17 +1016,16 @@ var Font = (function Font() {
|
||||
idDeltas += string16(0);
|
||||
idRangeOffsets += string16(offset);
|
||||
|
||||
var startCode = range[2];
|
||||
var endCode = range[3];
|
||||
for (var j = startCode; j <= endCode; ++j)
|
||||
glyphsIds += string16(deltas[j]);
|
||||
var codes = range[2];
|
||||
for (var j = 0, jj = codes.length; j < jj; ++j)
|
||||
glyphsIds += string16(deltas[codes[j]]);
|
||||
}
|
||||
} else {
|
||||
for (var i = 0; i < segCount - 1; i++) {
|
||||
var range = ranges[i];
|
||||
var start = range[0];
|
||||
var end = range[1];
|
||||
var startCode = range[2];
|
||||
var startCode = range[2][0];
|
||||
|
||||
startCount += string16(start);
|
||||
endCount += string16(end);
|
||||
@ -1226,7 +1229,7 @@ var Font = (function Font() {
|
||||
return nameTable;
|
||||
}
|
||||
|
||||
constructor.prototype = {
|
||||
Font.prototype = {
|
||||
name: null,
|
||||
font: null,
|
||||
mimetype: null,
|
||||
@ -1299,7 +1302,7 @@ var Font = (function Font() {
|
||||
properties.baseEncoding = encoding;
|
||||
}
|
||||
|
||||
function replaceCMapTable(cmap, font, properties) {
|
||||
function readCMapTable(cmap, font) {
|
||||
var start = (font.start ? font.start : 0) + cmap.offset;
|
||||
font.pos = start;
|
||||
|
||||
@ -1316,7 +1319,7 @@ var Font = (function Font() {
|
||||
}
|
||||
|
||||
// Check that table are sorted by platformID then encodingID,
|
||||
records.sort(function fontReplaceCMapTableSort(a, b) {
|
||||
records.sort(function fontReadCMapTableSort(a, b) {
|
||||
return ((a.platformID << 16) + a.encodingID) -
|
||||
((b.platformID << 16) + b.encodingID);
|
||||
});
|
||||
@ -1371,16 +1374,15 @@ var Font = (function Font() {
|
||||
for (var j = 0; j < 256; j++) {
|
||||
var index = font.getByte();
|
||||
if (index) {
|
||||
var unicode = adaptUnicode(j);
|
||||
glyphs.push({ unicode: unicode, code: j });
|
||||
glyphs.push({ unicode: j, code: j });
|
||||
ids.push(index);
|
||||
}
|
||||
}
|
||||
|
||||
properties.hasShortCmap = true;
|
||||
|
||||
createGlyphNameMap(glyphs, ids, properties);
|
||||
return cmap.data = createCMapTable(glyphs, ids);
|
||||
return {
|
||||
glyphs: glyphs,
|
||||
ids: ids,
|
||||
hasShortCmap: true
|
||||
};
|
||||
} else if (format == 4) {
|
||||
// re-creating the table in format 4 since the encoding
|
||||
// might be changed
|
||||
@ -1432,17 +1434,18 @@ var Font = (function Font() {
|
||||
var glyphCode = offsetIndex < 0 ? j :
|
||||
offsets[offsetIndex + j - start];
|
||||
glyphCode = (glyphCode + delta) & 0xFFFF;
|
||||
if (glyphCode == 0 || isAdaptedUnicode(j))
|
||||
if (glyphCode == 0)
|
||||
continue;
|
||||
|
||||
var unicode = adaptUnicode(j);
|
||||
glyphs.push({ unicode: unicode, code: j });
|
||||
glyphs.push({ unicode: j, code: j });
|
||||
ids.push(glyphCode);
|
||||
}
|
||||
}
|
||||
|
||||
createGlyphNameMap(glyphs, ids, properties);
|
||||
return cmap.data = createCMapTable(glyphs, ids);
|
||||
return {
|
||||
glyphs: glyphs,
|
||||
ids: ids
|
||||
};
|
||||
} else if (format == 6) {
|
||||
// Format 6 is a 2-bytes dense mapping, which means the font data
|
||||
// lives glue together even if they are pretty far in the unicode
|
||||
@ -1457,19 +1460,18 @@ var Font = (function Font() {
|
||||
for (var j = 0; j < entryCount; j++) {
|
||||
var glyphCode = int16(font.getBytes(2));
|
||||
var code = firstCode + j;
|
||||
if (isAdaptedUnicode(glyphCode))
|
||||
continue;
|
||||
|
||||
var unicode = adaptUnicode(code);
|
||||
glyphs.push({ unicode: unicode, code: code });
|
||||
glyphs.push({ unicode: code, code: code });
|
||||
ids.push(glyphCode);
|
||||
}
|
||||
|
||||
createGlyphNameMap(glyphs, ids, properties);
|
||||
return cmap.data = createCMapTable(glyphs, ids);
|
||||
return {
|
||||
glyphs: glyphs,
|
||||
ids: ids
|
||||
};
|
||||
}
|
||||
}
|
||||
return cmap.data;
|
||||
error('Unsupported cmap table format');
|
||||
};
|
||||
|
||||
function sanitizeMetrics(font, header, metrics, numGlyphs) {
|
||||
@ -1708,17 +1710,108 @@ var Font = (function Font() {
|
||||
tables.push(cmap);
|
||||
}
|
||||
|
||||
var glyphs = [];
|
||||
for (i = 1; i < numGlyphs; i++) {
|
||||
if (isAdaptedUnicode(i))
|
||||
continue;
|
||||
|
||||
glyphs.push({ unicode: adaptUnicode(i) });
|
||||
var cidToGidMap = properties.cidToGidMap || [];
|
||||
var gidToCidMap = [0];
|
||||
if (cidToGidMap.length > 0) {
|
||||
for (var j = cidToGidMap.length - 1; j >= 0; j--) {
|
||||
var gid = cidToGidMap[j];
|
||||
if (gid)
|
||||
gidToCidMap[gid] = j;
|
||||
}
|
||||
// filling the gaps using CID above the CIDs currently used in font
|
||||
var nextCid = cidToGidMap.length;
|
||||
for (var i = 1; i < numGlyphs; i++) {
|
||||
if (!gidToCidMap[i])
|
||||
gidToCidMap[i] = nextCid++;
|
||||
}
|
||||
}
|
||||
cmap.data = createCMapTable(glyphs);
|
||||
|
||||
var glyphs = [], ids = [];
|
||||
var usedUnicodes = [];
|
||||
var unassignedUnicodeItems = [];
|
||||
for (var i = 1; i < numGlyphs; i++) {
|
||||
var cid = gidToCidMap[i] || i;
|
||||
var unicode = this.toUnicode[cid];
|
||||
if (!unicode || isSpecialUnicode(unicode) ||
|
||||
unicode in usedUnicodes) {
|
||||
unassignedUnicodeItems.push(i);
|
||||
continue;
|
||||
}
|
||||
usedUnicodes[unicode] = true;
|
||||
glyphs.push({ unicode: unicode, code: cid });
|
||||
ids.push(i);
|
||||
}
|
||||
// trying to fit as many unassigned symbols as we can
|
||||
// in the range allocated for the user defined symbols
|
||||
var unusedUnicode = kCmapGlyphOffset;
|
||||
for (var j = 0, jj = unassignedUnicodeItems.length; j < jj; j++) {
|
||||
var i = unassignedUnicodeItems[j];
|
||||
var cid = gidToCidMap[i] || i;
|
||||
while (unusedUnicode in usedUnicodes)
|
||||
unusedUnicode++;
|
||||
if (unusedUnicode >= kCmapGlyphOffset + kSizeOfGlyphArea)
|
||||
break;
|
||||
var unicode = unusedUnicode++;
|
||||
this.toUnicode[cid] = unicode;
|
||||
usedUnicodes[unicode] = true;
|
||||
glyphs.push({ unicode: unicode, code: cid });
|
||||
ids.push(i);
|
||||
}
|
||||
cmap.data = createCMapTable(glyphs, ids);
|
||||
} else {
|
||||
replaceCMapTable(cmap, font, properties);
|
||||
var cmapTable = readCMapTable(cmap, font);
|
||||
var glyphs = cmapTable.glyphs;
|
||||
var ids = cmapTable.ids;
|
||||
var hasShortCmap = !!cmapTable.hasShortCmap;
|
||||
var toUnicode = this.toUnicode;
|
||||
|
||||
if (toUnicode && toUnicode.length > 0) {
|
||||
// checking if cmap is just identity map
|
||||
var isIdentity = true;
|
||||
for (var i = 0, ii = glyphs.length; i < ii; i++) {
|
||||
if (glyphs[i].unicode != i + 1) {
|
||||
isIdentity = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// if it is, replacing with meaningful toUnicode values
|
||||
if (isIdentity) {
|
||||
var usedUnicodes = [], unassignedUnicodeItems = [];
|
||||
for (var i = 0, ii = glyphs.length; i < ii; i++) {
|
||||
var unicode = toUnicode[i + 1];
|
||||
if (!unicode || unicode in usedUnicodes) {
|
||||
unassignedUnicodeItems.push(i);
|
||||
continue;
|
||||
}
|
||||
glyphs[i].unicode = unicode;
|
||||
usedUnicodes[unicode] = true;
|
||||
}
|
||||
var unusedUnicode = kCmapGlyphOffset;
|
||||
for (var j = 0, jj = unassignedUnicodeItems.length; j < jj; j++) {
|
||||
var i = unassignedUnicodeItems[j];
|
||||
while (unusedUnicode in usedUnicodes)
|
||||
unusedUnicode++;
|
||||
var cid = i + 1;
|
||||
// override only if unicode mapping is not specified
|
||||
if (!(cid in toUnicode))
|
||||
toUnicode[cid] = unusedUnicode;
|
||||
glyphs[i].unicode = unusedUnicode++;
|
||||
}
|
||||
this.useToUnicode = true;
|
||||
}
|
||||
}
|
||||
properties.hasShortCmap = hasShortCmap;
|
||||
|
||||
// remove glyph references outside range of avaialable glyphs
|
||||
for (var i = 0, ii = ids.length; i < ii; i++) {
|
||||
if (ids[i] >= numGlyphs)
|
||||
ids[i] = 0;
|
||||
}
|
||||
|
||||
createGlyphNameMap(glyphs, ids, properties);
|
||||
this.glyphNameMap = properties.glyphNameMap;
|
||||
|
||||
cmap.data = createCMapTable(glyphs, ids);
|
||||
}
|
||||
|
||||
// Rewrite the 'post' table if needed
|
||||
@ -1808,6 +1901,14 @@ var Font = (function Font() {
|
||||
}
|
||||
properties.baseEncoding = encoding;
|
||||
}
|
||||
if (properties.subtype == 'CIDFontType0C') {
|
||||
var toUnicode = [];
|
||||
for (var i = 0; i < charstrings.length; ++i) {
|
||||
var charstring = charstrings[i];
|
||||
toUnicode[charstring.code] = charstring.unicode;
|
||||
}
|
||||
this.toUnicode = toUnicode;
|
||||
}
|
||||
|
||||
var fields = {
|
||||
// PostScript Font Program
|
||||
@ -1832,9 +1933,9 @@ var Font = (function Font() {
|
||||
'\x00\x00\x00\x00\x9e\x0b\x7e\x27' + // creation date
|
||||
'\x00\x00\x00\x00\x9e\x0b\x7e\x27' + // modifification date
|
||||
'\x00\x00' + // xMin
|
||||
string16(properties.descent) + // yMin
|
||||
safeString16(properties.descent) + // yMin
|
||||
'\x0F\xFF' + // xMax
|
||||
string16(properties.ascent) + // yMax
|
||||
safeString16(properties.ascent) + // yMax
|
||||
string16(properties.italicAngle ? 2 : 0) + // macStyle
|
||||
'\x00\x11' + // lowestRecPPEM
|
||||
'\x00\x00' + // fontDirectionHint
|
||||
@ -1846,15 +1947,15 @@ var Font = (function Font() {
|
||||
'hhea': (function fontFieldsHhea() {
|
||||
return stringToArray(
|
||||
'\x00\x01\x00\x00' + // Version number
|
||||
string16(properties.ascent) + // Typographic Ascent
|
||||
string16(properties.descent) + // Typographic Descent
|
||||
safeString16(properties.ascent) + // Typographic Ascent
|
||||
safeString16(properties.descent) + // Typographic Descent
|
||||
'\x00\x00' + // Line Gap
|
||||
'\xFF\xFF' + // advanceWidthMax
|
||||
'\x00\x00' + // minLeftSidebearing
|
||||
'\x00\x00' + // minRightSidebearing
|
||||
'\x00\x00' + // xMaxExtent
|
||||
string16(properties.capHeight) + // caretSlopeRise
|
||||
string16(Math.tan(properties.italicAngle) *
|
||||
safeString16(properties.capHeight) + // caretSlopeRise
|
||||
safeString16(Math.tan(properties.italicAngle) *
|
||||
properties.xHeight) + // caretSlopeRun
|
||||
'\x00\x00' + // caretOffset
|
||||
'\x00\x00' + // -reserved-
|
||||
@ -1868,8 +1969,11 @@ var Font = (function Font() {
|
||||
// Horizontal metrics
|
||||
'hmtx': (function fontFieldsHmtx() {
|
||||
var hmtx = '\x00\x00\x00\x00'; // Fake .notdef
|
||||
for (var i = 0, ii = charstrings.length; i < ii; i++)
|
||||
hmtx += string16(charstrings[i].width) + string16(0);
|
||||
for (var i = 0, ii = charstrings.length; i < ii; i++) {
|
||||
var charstring = charstrings[i];
|
||||
var width = 'width' in charstring ? charstring.width : 0;
|
||||
hmtx += string16(width) + string16(0);
|
||||
}
|
||||
return stringToArray(hmtx);
|
||||
})(),
|
||||
|
||||
@ -1898,17 +2002,35 @@ var Font = (function Font() {
|
||||
return stringToArray(otf.file);
|
||||
},
|
||||
|
||||
loadCidToUnicode: function font_loadCidToUnicode(properties) {
|
||||
if (properties.cidToGidMap) {
|
||||
this.cidToUnicode = properties.cidToGidMap;
|
||||
return;
|
||||
rebuildToUnicode: function font_rebuildToUnicode(properties) {
|
||||
var firstChar = properties.firstChar, lastChar = properties.lastChar;
|
||||
var map = [];
|
||||
if (properties.composite) {
|
||||
var isIdentityMap = this.cidToUnicode.length == 0;
|
||||
for (var i = firstChar, ii = lastChar; i <= ii; i++) {
|
||||
// TODO missing map the character according font's CMap
|
||||
var cid = i;
|
||||
map[i] = isIdentityMap ? cid : this.cidToUnicode[cid];
|
||||
}
|
||||
} else {
|
||||
for (var i = firstChar, ii = lastChar; i <= ii; i++) {
|
||||
var glyph = properties.differences[i];
|
||||
if (!glyph)
|
||||
glyph = properties.baseEncoding[i];
|
||||
if (!!glyph && (glyph in GlyphsUnicode))
|
||||
map[i] = GlyphsUnicode[glyph];
|
||||
}
|
||||
}
|
||||
this.toUnicode = map;
|
||||
},
|
||||
|
||||
loadCidToUnicode: function font_loadCidToUnicode(properties) {
|
||||
if (!properties.cidSystemInfo)
|
||||
return;
|
||||
|
||||
var cidToUnicodeMap = [];
|
||||
var cidToUnicodeMap = [], unicodeToCIDMap = [];
|
||||
this.cidToUnicode = cidToUnicodeMap;
|
||||
this.unicodeToCID = unicodeToCIDMap;
|
||||
|
||||
var cidSystemInfo = properties.cidSystemInfo;
|
||||
var cidToUnicode;
|
||||
@ -1920,28 +2042,34 @@ var Font = (function Font() {
|
||||
if (!cidToUnicode)
|
||||
return; // identity encoding
|
||||
|
||||
var glyph = 1, i, j, k, ii;
|
||||
var cid = 1, i, j, k, ii;
|
||||
for (i = 0, ii = cidToUnicode.length; i < ii; ++i) {
|
||||
var unicode = cidToUnicode[i];
|
||||
if (isArray(unicode)) {
|
||||
var length = unicode.length;
|
||||
for (j = 0; j < length; j++)
|
||||
cidToUnicodeMap[unicode[j]] = glyph;
|
||||
glyph++;
|
||||
for (j = 0; j < length; j++) {
|
||||
cidToUnicodeMap[cid] = unicode[j];
|
||||
unicodeToCIDMap[unicode[j]] = cid;
|
||||
}
|
||||
cid++;
|
||||
} else if (typeof unicode === 'object') {
|
||||
var fillLength = unicode.f;
|
||||
if (fillLength) {
|
||||
k = unicode.c;
|
||||
for (j = 0; j < fillLength; ++j) {
|
||||
cidToUnicodeMap[k] = glyph++;
|
||||
cidToUnicodeMap[cid] = k;
|
||||
unicodeToCIDMap[k] = cid;
|
||||
cid++;
|
||||
k++;
|
||||
}
|
||||
} else
|
||||
glyph += unicode.s;
|
||||
cid += unicode.s;
|
||||
} else if (unicode) {
|
||||
cidToUnicodeMap[unicode] = glyph++;
|
||||
cidToUnicodeMap[cid] = unicode;
|
||||
unicodeToCIDMap[unicode] = cid;
|
||||
cid++;
|
||||
} else
|
||||
glyph++;
|
||||
cid++;
|
||||
}
|
||||
},
|
||||
|
||||
@ -1964,7 +2092,7 @@ var Font = (function Font() {
|
||||
window.btoa(data) + ');');
|
||||
var rule = "@font-face { font-family:'" + fontName + "';src:" + url + '}';
|
||||
|
||||
document.documentElement.firstChild.appendChild(
|
||||
document.documentElement.getElementsByTagName('head')[0].appendChild(
|
||||
document.createElement('style'));
|
||||
|
||||
var styleSheet = document.styleSheets[document.styleSheets.length - 1];
|
||||
@ -1973,6 +2101,37 @@ var Font = (function Font() {
|
||||
return rule;
|
||||
},
|
||||
|
||||
get spaceWidth() {
|
||||
// trying to estimate space character width
|
||||
var possibleSpaceReplacements = ['space', 'minus', 'one', 'i'];
|
||||
var width;
|
||||
for (var i = 0, ii = possibleSpaceReplacements.length; i < ii; i++) {
|
||||
var glyphName = possibleSpaceReplacements[i];
|
||||
// if possible, getting width by glyph name
|
||||
if (glyphName in this.widths) {
|
||||
width = this.widths[glyphName];
|
||||
break;
|
||||
}
|
||||
var glyphUnicode = GlyphsUnicode[glyphName];
|
||||
// finding the charcode via unicodeToCID map
|
||||
var charcode = 0;
|
||||
if (this.composite)
|
||||
charcode = this.unicodeToCID[glyphUnicode];
|
||||
// ... via toUnicode map
|
||||
if (!charcode && 'toUnicode' in this)
|
||||
charcode = this.toUnicode.indexOf(glyphUnicode);
|
||||
// setting it to unicode if negative or undefined
|
||||
if (!(charcode > 0))
|
||||
charcode = glyphUnicode;
|
||||
// trying to get width via charcode
|
||||
width = this.widths[charcode];
|
||||
if (width)
|
||||
break; // the non-zero width found
|
||||
}
|
||||
width = (width || this.defaultWidth) * this.widthMultiplier;
|
||||
return shadow(this, 'spaceWidth', width);
|
||||
},
|
||||
|
||||
charToGlyph: function fonts_charToGlyph(charcode) {
|
||||
var unicode, width, codeIRQueue;
|
||||
|
||||
@ -1981,30 +2140,30 @@ var Font = (function Font() {
|
||||
switch (this.type) {
|
||||
case 'CIDFontType0':
|
||||
if (this.noUnicodeAdaptation) {
|
||||
width = this.widths[this.cidToUnicode[charcode]];
|
||||
width = this.widths[this.unicodeToCID[charcode] || charcode];
|
||||
unicode = charcode;
|
||||
break;
|
||||
}
|
||||
unicode = adaptUnicode(this.cidToUnicode[charcode] || charcode);
|
||||
unicode = this.toUnicode[charcode] || charcode;
|
||||
break;
|
||||
case 'CIDFontType2':
|
||||
if (this.noUnicodeAdaptation) {
|
||||
width = this.widths[this.cidToUnicode[charcode]];
|
||||
width = this.widths[this.unicodeToCID[charcode] || charcode];
|
||||
unicode = charcode;
|
||||
break;
|
||||
}
|
||||
unicode = adaptUnicode(this.cidToUnicode[charcode] || charcode);
|
||||
unicode = this.toUnicode[charcode] || charcode;
|
||||
break;
|
||||
case 'Type1':
|
||||
var glyphName = this.differences[charcode] || this.encoding[charcode];
|
||||
if (!isNum(width))
|
||||
width = this.widths[glyphName];
|
||||
if (this.noUnicodeAdaptation) {
|
||||
if (!isNum(width))
|
||||
width = this.widths[glyphName];
|
||||
unicode = GlyphsUnicode[glyphName] || charcode;
|
||||
break;
|
||||
}
|
||||
unicode = this.glyphNameMap[glyphName] ||
|
||||
adaptUnicode(GlyphsUnicode[glyphName] || charcode);
|
||||
GlyphsUnicode[glyphName] || charcode;
|
||||
break;
|
||||
case 'Type3':
|
||||
var glyphName = this.differences[charcode] || this.encoding[charcode];
|
||||
@ -2012,6 +2171,10 @@ var Font = (function Font() {
|
||||
unicode = charcode;
|
||||
break;
|
||||
case 'TrueType':
|
||||
if (this.useToUnicode) {
|
||||
unicode = this.toUnicode[charcode] || charcode;
|
||||
break;
|
||||
}
|
||||
var glyphName = this.differences[charcode] || this.encoding[charcode];
|
||||
if (!glyphName)
|
||||
glyphName = Encodings.StandardEncoding[charcode];
|
||||
@ -2022,16 +2185,16 @@ var Font = (function Font() {
|
||||
break;
|
||||
}
|
||||
if (!this.hasEncoding) {
|
||||
unicode = adaptUnicode(charcode);
|
||||
unicode = this.useToUnicode ? this.toUnicode[charcode] : charcode;
|
||||
break;
|
||||
}
|
||||
if (this.hasShortCmap) {
|
||||
if (this.hasShortCmap && false) {
|
||||
var j = Encodings.MacRomanEncoding.indexOf(glyphName);
|
||||
unicode = j >= 0 && !isSpecialUnicode(j) ? j :
|
||||
unicode = j >= 0 ? j :
|
||||
this.glyphNameMap[glyphName];
|
||||
} else {
|
||||
unicode = glyphName in GlyphsUnicode ?
|
||||
adaptUnicode(GlyphsUnicode[glyphName]) :
|
||||
GlyphsUnicode[glyphName] :
|
||||
this.glyphNameMap[glyphName];
|
||||
}
|
||||
break;
|
||||
@ -2039,14 +2202,23 @@ var Font = (function Font() {
|
||||
warn('Unsupported font type: ' + this.type);
|
||||
break;
|
||||
}
|
||||
|
||||
var unicodeChars = !('toUnicode' in this) ? charcode :
|
||||
this.toUnicode[charcode] || charcode;
|
||||
if (typeof unicodeChars === 'number')
|
||||
unicodeChars = String.fromCharCode(unicodeChars);
|
||||
|
||||
width = (isNum(width) ? width : this.defaultWidth) * this.widthMultiplier;
|
||||
|
||||
return {
|
||||
unicode: unicode,
|
||||
width: isNum(width) ? width : this.defaultWidth,
|
||||
fontChar: String.fromCharCode(unicode),
|
||||
unicode: unicodeChars,
|
||||
width: width,
|
||||
codeIRQueue: codeIRQueue
|
||||
};
|
||||
},
|
||||
|
||||
charsToGlyphs: function fonts_chars2Glyphs(chars) {
|
||||
charsToGlyphs: function fonts_charsToGlyphs(chars) {
|
||||
var charsCache = this.charsCache;
|
||||
var glyphs;
|
||||
|
||||
@ -2094,7 +2266,7 @@ var Font = (function Font() {
|
||||
}
|
||||
};
|
||||
|
||||
return constructor;
|
||||
return Font;
|
||||
})();
|
||||
|
||||
/*
|
||||
@ -2753,22 +2925,13 @@ CFF.prototype = {
|
||||
getOrderedCharStrings: function cff_getOrderedCharStrings(glyphs,
|
||||
properties) {
|
||||
var charstrings = [];
|
||||
var reverseMapping = {};
|
||||
var encoding = properties.baseEncoding;
|
||||
var i, length, glyphName;
|
||||
for (i = 0, length = encoding.length; i < length; ++i) {
|
||||
glyphName = encoding[i];
|
||||
if (!glyphName || isSpecialUnicode(i))
|
||||
continue;
|
||||
reverseMapping[glyphName] = i;
|
||||
}
|
||||
reverseMapping['.notdef'] = 0;
|
||||
var unusedUnicode = kCmapGlyphOffset;
|
||||
for (i = 0, length = glyphs.length; i < length; i++) {
|
||||
var item = glyphs[i];
|
||||
var glyphName = item.glyph;
|
||||
var unicode = glyphName in reverseMapping ?
|
||||
reverseMapping[glyphName] : unusedUnicode++;
|
||||
var unicode = glyphName in GlyphsUnicode ?
|
||||
GlyphsUnicode[glyphName] : unusedUnicode++;
|
||||
charstrings.push({
|
||||
glyph: glyphName,
|
||||
unicode: unicode,
|
||||
@ -3013,9 +3176,9 @@ CFF.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
var Type2CFF = (function type2CFF() {
|
||||
var Type2CFF = (function Type2CFFClosure() {
|
||||
// TODO: replace parsing code with the Type2Parser in font_utils.js
|
||||
function constructor(file, properties) {
|
||||
function Type2CFF(file, properties) {
|
||||
var bytes = file.getBytes();
|
||||
this.bytes = bytes;
|
||||
this.properties = properties;
|
||||
@ -3023,7 +3186,7 @@ var Type2CFF = (function type2CFF() {
|
||||
this.data = this.parse();
|
||||
}
|
||||
|
||||
constructor.prototype = {
|
||||
Type2CFF.prototype = {
|
||||
parse: function cff_parse() {
|
||||
var header = this.parseHeader();
|
||||
var properties = this.properties;
|
||||
@ -3055,16 +3218,14 @@ var Type2CFF = (function type2CFF() {
|
||||
}
|
||||
|
||||
var charStrings = this.parseIndex(topDict.CharStrings);
|
||||
var charset = this.parseCharsets(topDict.charset,
|
||||
charStrings.length, strings);
|
||||
var encoding = this.parseEncoding(topDict.Encoding, properties,
|
||||
strings, charset);
|
||||
|
||||
var charset, encoding;
|
||||
var isCIDFont = properties.subtype == 'CIDFontType0C';
|
||||
if (isCIDFont) {
|
||||
charset = [];
|
||||
charset.length = charStrings.length;
|
||||
charset = ['.notdef'];
|
||||
for (var i = 1, ii = charStrings.length; i < ii; ++i)
|
||||
charset.push('glyph' + i);
|
||||
|
||||
encoding = this.parseCidMap(topDict.charset,
|
||||
charStrings.length);
|
||||
} else {
|
||||
@ -3133,38 +3294,44 @@ var Type2CFF = (function type2CFF() {
|
||||
var charstrings = [];
|
||||
var unicodeUsed = [];
|
||||
var unassignedUnicodeItems = [];
|
||||
var inverseEncoding = [];
|
||||
for (var charcode in encoding)
|
||||
inverseEncoding[encoding[charcode]] = charcode | 0;
|
||||
for (var i = 0, ii = charsets.length; i < ii; i++) {
|
||||
var glyph = charsets[i];
|
||||
var encodingFound = false;
|
||||
for (var charcode in encoding) {
|
||||
if (encoding[charcode] == i) {
|
||||
var code = charcode | 0;
|
||||
charstrings.push({
|
||||
unicode: adaptUnicode(code),
|
||||
code: code,
|
||||
gid: i,
|
||||
glyph: glyph
|
||||
});
|
||||
unicodeUsed[code] = true;
|
||||
encodingFound = true;
|
||||
break;
|
||||
}
|
||||
if (glyph == '.notdef') {
|
||||
charstrings.push({
|
||||
unicode: 0,
|
||||
code: 0,
|
||||
gid: i,
|
||||
glyph: glyph
|
||||
});
|
||||
continue;
|
||||
}
|
||||
if (!encodingFound) {
|
||||
var code = inverseEncoding[i];
|
||||
if (!code || isSpecialUnicode(code)) {
|
||||
unassignedUnicodeItems.push(i);
|
||||
continue;
|
||||
}
|
||||
charstrings.push({
|
||||
unicode: code,
|
||||
code: code,
|
||||
gid: i,
|
||||
glyph: glyph
|
||||
});
|
||||
unicodeUsed[code] = true;
|
||||
}
|
||||
|
||||
var nextUnusedUnicode = 0x21;
|
||||
var nextUnusedUnicode = kCmapGlyphOffset;
|
||||
for (var j = 0, jj = unassignedUnicodeItems.length; j < jj; ++j) {
|
||||
var i = unassignedUnicodeItems[j];
|
||||
// giving unicode value anyway
|
||||
while (unicodeUsed[nextUnusedUnicode])
|
||||
while (nextUnusedUnicode in unicodeUsed)
|
||||
nextUnusedUnicode++;
|
||||
var code = nextUnusedUnicode++;
|
||||
var unicode = nextUnusedUnicode++;
|
||||
charstrings.push({
|
||||
unicode: adaptUnicode(code),
|
||||
code: code,
|
||||
unicode: unicode,
|
||||
code: inverseEncoding[i] || 0,
|
||||
gid: i,
|
||||
glyph: charsets[i]
|
||||
});
|
||||
@ -3563,6 +3730,6 @@ var Type2CFF = (function type2CFF() {
|
||||
}
|
||||
};
|
||||
|
||||
return constructor;
|
||||
return Type2CFF;
|
||||
})();
|
||||
|
||||
|
555
src/function.js
555
src/function.js
@ -3,7 +3,7 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var PDFFunction = (function pdfFunction() {
|
||||
var PDFFunction = (function PDFFunctionClosure() {
|
||||
var CONSTRUCT_SAMPLED = 0;
|
||||
var CONSTRUCT_INTERPOLATED = 2;
|
||||
var CONSTRUCT_STICHED = 3;
|
||||
@ -270,7 +270,6 @@ var PDFFunction = (function pdfFunction() {
|
||||
|
||||
constructStiched: function pdfFunctionConstructStiched(fn, dict, xref) {
|
||||
var domain = dict.get('Domain');
|
||||
var range = dict.get('Range');
|
||||
|
||||
if (!domain)
|
||||
error('No domain');
|
||||
@ -279,13 +278,13 @@ var PDFFunction = (function pdfFunction() {
|
||||
if (inputSize != 1)
|
||||
error('Bad domain for stiched function');
|
||||
|
||||
var fnRefs = dict.get('Functions');
|
||||
var fnRefs = xref.fetchIfRef(dict.get('Functions'));
|
||||
var fns = [];
|
||||
for (var i = 0, ii = fnRefs.length; i < ii; ++i)
|
||||
fns.push(PDFFunction.getIR(xref, xref.fetchIfRef(fnRefs[i])));
|
||||
|
||||
var bounds = dict.get('Bounds');
|
||||
var encode = dict.get('Encode');
|
||||
var bounds = xref.fetchIfRef(dict.get('Bounds'));
|
||||
var encode = xref.fetchIfRef(dict.get('Encode'));
|
||||
|
||||
return [CONSTRUCT_STICHED, domain, bounds, encode, fns];
|
||||
},
|
||||
@ -336,16 +335,550 @@ var PDFFunction = (function pdfFunction() {
|
||||
};
|
||||
},
|
||||
|
||||
constructPostScript: function pdfFunctionConstructPostScript() {
|
||||
return [CONSTRUCT_POSTSCRIPT];
|
||||
constructPostScript: function pdfFunctionConstructPostScript(fn, dict,
|
||||
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() {
|
||||
TODO('unhandled type of function');
|
||||
return function constructPostScriptFromIRResult() {
|
||||
return [255, 105, 180];
|
||||
constructPostScriptFromIR:
|
||||
function pdfFunctionConstructPostScriptFromIR(IR) {
|
||||
var domain = IR[1];
|
||||
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;
|
||||
})();
|
||||
|
||||
|
@ -4287,6 +4287,7 @@ var GlyphsUnicode = {
|
||||
zretroflexhook: 0x0290,
|
||||
zstroke: 0x01B6,
|
||||
zuhiragana: 0x305A,
|
||||
zukatakana: 0x30BA
|
||||
zukatakana: 0x30BA,
|
||||
'.notdef': 0x0000
|
||||
};
|
||||
|
||||
|
262
src/image.js
262
src/image.js
@ -3,8 +3,37 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var PDFImage = (function pdfImage() {
|
||||
function constructor(xref, res, image, inline) {
|
||||
var PDFImage = (function PDFImageClosure() {
|
||||
/**
|
||||
* Decode the image in the main thread if it supported. Resovles the promise
|
||||
* when the image data is ready.
|
||||
*/
|
||||
function handleImageData(handler, xref, res, image, promise) {
|
||||
if (image instanceof JpegStream && image.isNative) {
|
||||
// For natively supported jpegs send them to the main thread for decoding.
|
||||
var dict = image.dict;
|
||||
var colorSpace = dict.get('ColorSpace', 'CS');
|
||||
colorSpace = ColorSpace.parse(colorSpace, xref, res);
|
||||
var numComps = colorSpace.numComps;
|
||||
handler.send('jpeg_decode', [image.getIR(), numComps], function(message) {
|
||||
var data = message.data;
|
||||
var stream = new Stream(data, 0, data.length, image.dict);
|
||||
promise.resolve(stream);
|
||||
});
|
||||
} else {
|
||||
promise.resolve(image);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Decode and clamp a value. The formula is different from the spec because we
|
||||
* don't decode to float range [0,1], we decode it in the [0,max] range.
|
||||
*/
|
||||
function decodeAndClamp(value, addend, coefficient, max) {
|
||||
value = addend + value * coefficient;
|
||||
// Clamp the value to the range
|
||||
return value < 0 ? 0 : value > max ? max : value;
|
||||
}
|
||||
function PDFImage(xref, res, image, inline, smask) {
|
||||
this.image = image;
|
||||
if (image.getParams) {
|
||||
// JPX/JPEG2000 streams directly contain bits per component
|
||||
@ -49,34 +78,142 @@ var PDFImage = (function pdfImage() {
|
||||
}
|
||||
|
||||
this.decode = dict.get('Decode', 'D');
|
||||
this.needsDecode = false;
|
||||
if (this.decode && this.colorSpace &&
|
||||
!this.colorSpace.isDefaultDecode(this.decode)) {
|
||||
this.needsDecode = true;
|
||||
// Do some preprocessing to avoid more math.
|
||||
var max = (1 << bitsPerComponent) - 1;
|
||||
this.decodeCoefficients = [];
|
||||
this.decodeAddends = [];
|
||||
for (var i = 0, j = 0; i < this.decode.length; i += 2, ++j) {
|
||||
var dmin = this.decode[i];
|
||||
var dmax = this.decode[i + 1];
|
||||
this.decodeCoefficients[j] = dmax - dmin;
|
||||
this.decodeAddends[j] = max * dmin;
|
||||
}
|
||||
}
|
||||
|
||||
var mask = xref.fetchIfRef(dict.get('Mask'));
|
||||
var smask = xref.fetchIfRef(dict.get('SMask'));
|
||||
|
||||
if (mask) {
|
||||
TODO('masked images');
|
||||
} else if (smask) {
|
||||
this.smask = new PDFImage(xref, res, smask);
|
||||
this.smask = new PDFImage(xref, res, smask, false);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Handles processing of image data and calls the callback with an argument
|
||||
* of a PDFImage when the image is ready to be used.
|
||||
*/
|
||||
PDFImage.buildImage = function buildImage(callback, handler, xref, res,
|
||||
image, inline) {
|
||||
var imageDataPromise = new Promise();
|
||||
var smaskPromise = new Promise();
|
||||
// The image data and smask data may not be ready yet, wait till both are
|
||||
// resolved.
|
||||
Promise.all([imageDataPromise, smaskPromise]).then(function(results) {
|
||||
var imageData = results[0], smaskData = results[1];
|
||||
var image = new PDFImage(xref, res, imageData, inline, smaskData);
|
||||
callback(image);
|
||||
});
|
||||
|
||||
constructor.prototype = {
|
||||
getComponents: function getComponents(buffer, decodeMap) {
|
||||
handleImageData(handler, xref, res, image, imageDataPromise);
|
||||
|
||||
var smask = xref.fetchIfRef(image.dict.get('SMask'));
|
||||
if (smask)
|
||||
handleImageData(handler, xref, res, smask, smaskPromise);
|
||||
else
|
||||
smaskPromise.resolve(null);
|
||||
};
|
||||
|
||||
/**
|
||||
* Resize an image using the nearest neighbor algorithm. Currently only
|
||||
* supports one and three component images.
|
||||
* @param {TypedArray} pixels The original image with one component.
|
||||
* @param {Number} bpc Number of bits per component.
|
||||
* @param {Number} components Number of color components, 1 or 3 is supported.
|
||||
* @param {Number} w1 Original width.
|
||||
* @param {Number} h1 Original height.
|
||||
* @param {Number} w2 New width.
|
||||
* @param {Number} h2 New height.
|
||||
* @return {TypedArray} Resized image data.
|
||||
*/
|
||||
PDFImage.resize = function resize(pixels, bpc, components, w1, h1, w2, h2) {
|
||||
var length = w2 * h2 * components;
|
||||
var temp = bpc <= 8 ? new Uint8Array(length) :
|
||||
bpc <= 16 ? new Uint16Array(length) : new Uint32Array(length);
|
||||
var xRatio = w1 / w2;
|
||||
var yRatio = h1 / h2;
|
||||
var px, py, newIndex, oldIndex;
|
||||
for (var i = 0; i < h2; i++) {
|
||||
for (var j = 0; j < w2; j++) {
|
||||
px = Math.floor(j * xRatio);
|
||||
py = Math.floor(i * yRatio);
|
||||
newIndex = (i * w2) + j;
|
||||
oldIndex = ((py * w1) + px);
|
||||
if (components === 1) {
|
||||
temp[newIndex] = pixels[oldIndex];
|
||||
} else if (components === 3) {
|
||||
newIndex *= 3;
|
||||
oldIndex *= 3;
|
||||
temp[newIndex] = pixels[oldIndex];
|
||||
temp[newIndex + 1] = pixels[oldIndex + 1];
|
||||
temp[newIndex + 2] = pixels[oldIndex + 2];
|
||||
}
|
||||
}
|
||||
}
|
||||
return temp;
|
||||
};
|
||||
|
||||
PDFImage.prototype = {
|
||||
get drawWidth() {
|
||||
if (!this.smask)
|
||||
return this.width;
|
||||
return Math.max(this.width, this.smask.width);
|
||||
},
|
||||
get drawHeight() {
|
||||
if (!this.smask)
|
||||
return this.height;
|
||||
return Math.max(this.height, this.smask.height);
|
||||
},
|
||||
getComponents: function getComponents(buffer) {
|
||||
var bpc = this.bpc;
|
||||
if (bpc == 8)
|
||||
var needsDecode = this.needsDecode;
|
||||
var decodeMap = this.decode;
|
||||
|
||||
// This image doesn't require any extra work.
|
||||
if (bpc == 8 && !needsDecode)
|
||||
return buffer;
|
||||
|
||||
var bufferLength = buffer.length;
|
||||
var width = this.width;
|
||||
var height = this.height;
|
||||
var numComps = this.numComps;
|
||||
|
||||
var length = width * height;
|
||||
var length = width * height * numComps;
|
||||
var bufferPos = 0;
|
||||
var output = bpc <= 8 ? new Uint8Array(length) :
|
||||
bpc <= 16 ? new Uint16Array(length) : new Uint32Array(length);
|
||||
var rowComps = width * numComps;
|
||||
var decodeAddends, decodeCoefficients;
|
||||
if (needsDecode) {
|
||||
decodeAddends = this.decodeAddends;
|
||||
decodeCoefficients = this.decodeCoefficients;
|
||||
}
|
||||
var max = (1 << bpc) - 1;
|
||||
|
||||
if (bpc == 1) {
|
||||
if (bpc == 8) {
|
||||
// Optimization for reading 8 bpc images that have a decode.
|
||||
for (var i = 0, ii = length; i < ii; ++i) {
|
||||
var compIndex = i % numComps;
|
||||
var value = buffer[i];
|
||||
value = decodeAndClamp(value, decodeAddends[compIndex],
|
||||
decodeCoefficients[compIndex], max);
|
||||
output[i] = value;
|
||||
}
|
||||
} else if (bpc == 1) {
|
||||
// Optimization for reading 1 bpc images.
|
||||
var valueZero = 0, valueOne = 1;
|
||||
if (decodeMap) {
|
||||
valueZero = decodeMap[0] ? 1 : 0;
|
||||
@ -101,8 +238,7 @@ var PDFImage = (function pdfImage() {
|
||||
output[i] = !(buf & mask) ? valueZero : valueOne;
|
||||
}
|
||||
} else {
|
||||
if (decodeMap != null)
|
||||
TODO('interpolate component values');
|
||||
// The general case that handles all other bpc values.
|
||||
var bits = 0, buf = 0;
|
||||
for (var i = 0, ii = length; i < ii; ++i) {
|
||||
if (i % rowComps == 0) {
|
||||
@ -116,41 +252,34 @@ var PDFImage = (function pdfImage() {
|
||||
}
|
||||
|
||||
var remainingBits = bits - bpc;
|
||||
output[i] = buf >> remainingBits;
|
||||
var value = buf >> remainingBits;
|
||||
if (needsDecode) {
|
||||
var compIndex = i % numComps;
|
||||
value = decodeAndClamp(value, decodeAddends[compIndex],
|
||||
decodeCoefficients[compIndex], max);
|
||||
}
|
||||
output[i] = value;
|
||||
buf = buf & ((1 << remainingBits) - 1);
|
||||
bits = remainingBits;
|
||||
}
|
||||
}
|
||||
return output;
|
||||
},
|
||||
getOpacity: function getOpacity() {
|
||||
getOpacity: function getOpacity(width, height) {
|
||||
var smask = this.smask;
|
||||
var width = this.width;
|
||||
var height = this.height;
|
||||
var buf = new Uint8Array(width * height);
|
||||
var originalWidth = this.width;
|
||||
var originalHeight = this.height;
|
||||
var buf;
|
||||
|
||||
if (smask) {
|
||||
if (smask.image.getImage) {
|
||||
// smask is a DOM image
|
||||
var tempCanvas = new ScratchCanvas(width, height);
|
||||
var tempCtx = tempCanvas.getContext('2d');
|
||||
var domImage = smask.image.getImage();
|
||||
tempCtx.drawImage(domImage, 0, 0, domImage.width, domImage.height,
|
||||
0, 0, width, height);
|
||||
var data = tempCtx.getImageData(0, 0, width, height).data;
|
||||
for (var i = 0, j = 0, ii = width * height; i < ii; ++i, j += 4)
|
||||
buf[i] = data[j]; // getting first component value
|
||||
return buf;
|
||||
}
|
||||
var sw = smask.width;
|
||||
var sh = smask.height;
|
||||
if (sw != this.width || sh != this.height)
|
||||
error('smask dimensions do not match image dimensions: ' + sw +
|
||||
' != ' + this.width + ', ' + sh + ' != ' + this.height);
|
||||
|
||||
buf = new Uint8Array(sw * sh);
|
||||
smask.fillGrayBuffer(buf);
|
||||
return buf;
|
||||
if (sw != width || sh != height)
|
||||
buf = PDFImage.resize(buf, smask.bps, 1, sw, sh, width, height);
|
||||
} else {
|
||||
buf = new Uint8Array(width * height);
|
||||
for (var i = 0, ii = width * height; i < ii; ++i)
|
||||
buf[i] = 255;
|
||||
}
|
||||
@ -159,8 +288,7 @@ var PDFImage = (function pdfImage() {
|
||||
applyStencilMask: function applyStencilMask(buffer, inverseDecode) {
|
||||
var width = this.width, height = this.height;
|
||||
var bitStrideLength = (width + 7) >> 3;
|
||||
this.image.reset();
|
||||
var imgArray = this.image.getBytes(bitStrideLength * height);
|
||||
var imgArray = this.getImageBytes(bitStrideLength * height);
|
||||
var imgArrayPos = 0;
|
||||
var i, j, mask, buf;
|
||||
// removing making non-masked pixels transparent
|
||||
@ -180,21 +308,23 @@ var PDFImage = (function pdfImage() {
|
||||
}
|
||||
}
|
||||
},
|
||||
fillRgbaBuffer: function fillRgbaBuffer(buffer, decodeMap) {
|
||||
fillRgbaBuffer: function fillRgbaBuffer(buffer, width, height) {
|
||||
var numComps = this.numComps;
|
||||
var width = this.width;
|
||||
var height = this.height;
|
||||
var originalWidth = this.width;
|
||||
var originalHeight = this.height;
|
||||
var bpc = this.bpc;
|
||||
|
||||
// rows start at byte boundary;
|
||||
var rowBytes = (width * numComps * bpc + 7) >> 3;
|
||||
this.image.reset();
|
||||
var imgArray = this.image.getBytes(height * rowBytes);
|
||||
var rowBytes = (originalWidth * numComps * bpc + 7) >> 3;
|
||||
var imgArray = this.getImageBytes(originalHeight * rowBytes);
|
||||
|
||||
var comps = this.colorSpace.getRgbBuffer(
|
||||
this.getComponents(imgArray, decodeMap), bpc);
|
||||
this.getComponents(imgArray), bpc);
|
||||
if (originalWidth != width || originalHeight != height)
|
||||
comps = PDFImage.resize(comps, this.bpc, 3, originalWidth,
|
||||
originalHeight, width, height);
|
||||
var compsPos = 0;
|
||||
var opacity = this.getOpacity();
|
||||
var opacity = this.getOpacity(width, height);
|
||||
var opacityPos = 0;
|
||||
var length = width * height * 4;
|
||||
|
||||
@ -216,42 +346,28 @@ var PDFImage = (function pdfImage() {
|
||||
|
||||
// rows start at byte boundary;
|
||||
var rowBytes = (width * numComps * bpc + 7) >> 3;
|
||||
this.image.reset();
|
||||
var imgArray = this.image.getBytes(height * rowBytes);
|
||||
var imgArray = this.getImageBytes(height * rowBytes);
|
||||
|
||||
var comps = this.getComponents(imgArray);
|
||||
var length = width * height;
|
||||
|
||||
// we aren't using a colorspace so we need to scale the value
|
||||
var scale = 255 / ((1 << bpc) - 1);
|
||||
for (var i = 0; i < length; ++i)
|
||||
buffer[i] = comps[i];
|
||||
buffer[i] = (scale * comps[i]) | 0;
|
||||
},
|
||||
getImageBytes: function getImageBytes(length) {
|
||||
this.image.reset();
|
||||
return this.image.getBytes(length);
|
||||
}
|
||||
};
|
||||
return constructor;
|
||||
return PDFImage;
|
||||
})();
|
||||
|
||||
var JpegImageLoader = (function jpegImage() {
|
||||
function JpegImageLoader(objId, imageData, objs) {
|
||||
var src = 'data:image/jpeg;base64,' + window.btoa(imageData);
|
||||
|
||||
var img = new Image();
|
||||
img.onload = (function jpegImageLoaderOnload() {
|
||||
this.loaded = true;
|
||||
|
||||
objs.resolve(objId, this);
|
||||
|
||||
if (this.onLoad)
|
||||
this.onLoad();
|
||||
}).bind(this);
|
||||
img.src = src;
|
||||
this.domImage = img;
|
||||
}
|
||||
|
||||
JpegImageLoader.prototype = {
|
||||
getImage: function jpegImageLoaderGetImage() {
|
||||
return this.domImage;
|
||||
}
|
||||
};
|
||||
|
||||
return JpegImageLoader;
|
||||
})();
|
||||
function loadJpegStream(id, imageData, objs) {
|
||||
var img = new Image();
|
||||
img.onload = (function jpegImageLoaderOnload() {
|
||||
objs.resolve(id, img);
|
||||
});
|
||||
img.src = 'data:image/jpeg;base64,' + window.btoa(imageData);
|
||||
}
|
||||
|
||||
|
@ -3,6 +3,9 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
// The Metrics object contains glyph widths (in glyph space units).
|
||||
// As per PDF spec, for most fonts (Type 3 being an exception) a glyph
|
||||
// space unit corresponds to 1/1000th of text space unit.
|
||||
var Metrics = {
|
||||
'Courier': 600,
|
||||
'Courier-Bold': 600,
|
||||
|
106
src/obj.js
106
src/obj.js
@ -3,34 +3,42 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var Name = (function nameName() {
|
||||
function constructor(name) {
|
||||
var Name = (function NameClosure() {
|
||||
function Name(name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
constructor.prototype = {
|
||||
};
|
||||
Name.prototype = {};
|
||||
|
||||
return constructor;
|
||||
return Name;
|
||||
})();
|
||||
|
||||
var Cmd = (function cmdCmd() {
|
||||
function constructor(cmd) {
|
||||
var Cmd = (function CmdClosure() {
|
||||
function Cmd(cmd) {
|
||||
this.cmd = cmd;
|
||||
}
|
||||
|
||||
constructor.prototype = {
|
||||
Cmd.prototype = {};
|
||||
|
||||
var cmdCache = {};
|
||||
|
||||
Cmd.get = function cmdGet(cmd) {
|
||||
var cmdValue = cmdCache[cmd];
|
||||
if (cmdValue)
|
||||
return cmdValue;
|
||||
|
||||
return cmdCache[cmd] = new Cmd(cmd);
|
||||
};
|
||||
|
||||
return constructor;
|
||||
return Cmd;
|
||||
})();
|
||||
|
||||
var Dict = (function dictDict() {
|
||||
function constructor() {
|
||||
var Dict = (function DictClosure() {
|
||||
function Dict() {
|
||||
this.map = Object.create(null);
|
||||
}
|
||||
|
||||
constructor.prototype = {
|
||||
Dict.prototype = {
|
||||
get: function dictGet(key1, key2, key3) {
|
||||
var value;
|
||||
if (typeof (value = this.map[key1]) != 'undefined' || key1 in this.map ||
|
||||
@ -60,29 +68,28 @@ var Dict = (function dictDict() {
|
||||
}
|
||||
};
|
||||
|
||||
return constructor;
|
||||
return Dict;
|
||||
})();
|
||||
|
||||
var Ref = (function refRef() {
|
||||
function constructor(num, gen) {
|
||||
var Ref = (function RefClosure() {
|
||||
function Ref(num, gen) {
|
||||
this.num = num;
|
||||
this.gen = gen;
|
||||
}
|
||||
|
||||
constructor.prototype = {
|
||||
};
|
||||
Ref.prototype = {};
|
||||
|
||||
return constructor;
|
||||
return Ref;
|
||||
})();
|
||||
|
||||
// The reference is identified by number and generation,
|
||||
// this structure stores only one instance of the reference.
|
||||
var RefSet = (function refSet() {
|
||||
function constructor() {
|
||||
var RefSet = (function RefSetClosure() {
|
||||
function RefSet() {
|
||||
this.dict = {};
|
||||
}
|
||||
|
||||
constructor.prototype = {
|
||||
RefSet.prototype = {
|
||||
has: function refSetHas(ref) {
|
||||
return !!this.dict['R' + ref.num + '.' + ref.gen];
|
||||
},
|
||||
@ -92,18 +99,18 @@ var RefSet = (function refSet() {
|
||||
}
|
||||
};
|
||||
|
||||
return constructor;
|
||||
return RefSet;
|
||||
})();
|
||||
|
||||
var Catalog = (function catalogCatalog() {
|
||||
function constructor(xref) {
|
||||
var Catalog = (function CatalogClosure() {
|
||||
function Catalog(xref) {
|
||||
this.xref = xref;
|
||||
var obj = xref.getCatalogObj();
|
||||
assertWellFormed(isDict(obj), 'catalog object is not a dictionary');
|
||||
this.catDict = obj;
|
||||
}
|
||||
|
||||
constructor.prototype = {
|
||||
Catalog.prototype = {
|
||||
get toplevelPagesDict() {
|
||||
var pagesObj = this.catDict.get('Pages');
|
||||
assertWellFormed(isRef(pagesObj), 'invalid top-level pages reference');
|
||||
@ -253,16 +260,16 @@ var Catalog = (function catalogCatalog() {
|
||||
}
|
||||
};
|
||||
|
||||
return constructor;
|
||||
return Catalog;
|
||||
})();
|
||||
|
||||
var XRef = (function xRefXRef() {
|
||||
function constructor(stream, startXRef, mainXRefEntriesOffset) {
|
||||
var XRef = (function XRefClosure() {
|
||||
function XRef(stream, startXRef, mainXRefEntriesOffset) {
|
||||
this.stream = stream;
|
||||
this.entries = [];
|
||||
this.xrefstms = {};
|
||||
var trailerDict = this.readXRef(startXRef);
|
||||
|
||||
this.trailer = trailerDict;
|
||||
// prepare the XRef cache
|
||||
this.cache = [];
|
||||
|
||||
@ -278,7 +285,7 @@ var XRef = (function xRefXRef() {
|
||||
error('Invalid root reference');
|
||||
}
|
||||
|
||||
constructor.prototype = {
|
||||
XRef.prototype = {
|
||||
readXRefTable: function readXRefTable(parser) {
|
||||
var obj;
|
||||
while (true) {
|
||||
@ -518,20 +525,29 @@ var XRef = (function xRefXRef() {
|
||||
readXRef: function readXref(startXRef) {
|
||||
var stream = this.stream;
|
||||
stream.pos = startXRef;
|
||||
var parser = new Parser(new Lexer(stream), true);
|
||||
var obj = parser.getObj();
|
||||
// parse an old-style xref table
|
||||
if (isCmd(obj, 'xref'))
|
||||
return this.readXRefTable(parser);
|
||||
// parse an xref stream
|
||||
if (isInt(obj)) {
|
||||
if (!isInt(parser.getObj()) ||
|
||||
!isCmd(parser.getObj(), 'obj') ||
|
||||
!isStream(obj = parser.getObj())) {
|
||||
error('Invalid XRef stream');
|
||||
|
||||
try {
|
||||
var parser = new Parser(new Lexer(stream), true);
|
||||
var obj = parser.getObj();
|
||||
|
||||
// parse an old-style xref table
|
||||
if (isCmd(obj, 'xref'))
|
||||
return this.readXRefTable(parser);
|
||||
|
||||
// parse an xref stream
|
||||
if (isInt(obj)) {
|
||||
if (!isInt(parser.getObj()) ||
|
||||
!isCmd(parser.getObj(), 'obj') ||
|
||||
!isStream(obj = parser.getObj())) {
|
||||
error('Invalid XRef stream');
|
||||
}
|
||||
return this.readXRefStream(obj);
|
||||
}
|
||||
return this.readXRefStream(obj);
|
||||
} catch (e) {
|
||||
log('Reading of the xref table/stream failed: ' + e);
|
||||
}
|
||||
|
||||
warn('Indexing all PDF objects');
|
||||
return this.indexObjects();
|
||||
},
|
||||
getEntry: function xRefGetEntry(i) {
|
||||
@ -589,7 +605,7 @@ var XRef = (function xRefXRef() {
|
||||
e = parser.getObj();
|
||||
}
|
||||
// Don't cache streams since they are mutable (except images).
|
||||
if (!isStream(e) || e.getImage)
|
||||
if (!isStream(e) || e instanceof JpegStream)
|
||||
this.cache[num] = e;
|
||||
return e;
|
||||
}
|
||||
@ -633,7 +649,7 @@ var XRef = (function xRefXRef() {
|
||||
}
|
||||
};
|
||||
|
||||
return constructor;
|
||||
return XRef;
|
||||
})();
|
||||
|
||||
/**
|
||||
@ -642,7 +658,7 @@ var XRef = (function xRefXRef() {
|
||||
* inside of a worker. The `PDFObjects` implements some basic functions to
|
||||
* manage these objects.
|
||||
*/
|
||||
var PDFObjects = (function pdfObjects() {
|
||||
var PDFObjects = (function PDFObjectsClosure() {
|
||||
function PDFObjects() {
|
||||
this.objs = {};
|
||||
}
|
||||
|
@ -9,8 +9,8 @@ function isEOF(v) {
|
||||
return v == EOF;
|
||||
}
|
||||
|
||||
var Parser = (function parserParser() {
|
||||
function constructor(lexer, allowStreams, xref) {
|
||||
var Parser = (function ParserClosure() {
|
||||
function Parser(lexer, allowStreams, xref) {
|
||||
this.lexer = lexer;
|
||||
this.allowStreams = allowStreams;
|
||||
this.xref = xref;
|
||||
@ -18,7 +18,7 @@ var Parser = (function parserParser() {
|
||||
this.refill();
|
||||
}
|
||||
|
||||
constructor.prototype = {
|
||||
Parser.prototype = {
|
||||
refill: function parserRefill() {
|
||||
this.buf1 = this.lexer.getObj();
|
||||
this.buf2 = this.lexer.getObj();
|
||||
@ -157,7 +157,7 @@ var Parser = (function parserParser() {
|
||||
imageStream = this.filter(imageStream, dict, length);
|
||||
imageStream.parameters = dict;
|
||||
|
||||
this.buf2 = new Cmd('EI');
|
||||
this.buf2 = Cmd.get('EI');
|
||||
this.shift();
|
||||
|
||||
return imageStream;
|
||||
@ -225,7 +225,8 @@ var Parser = (function parserParser() {
|
||||
return new PredictorStream(new FlateStream(stream), params);
|
||||
}
|
||||
return new FlateStream(stream);
|
||||
} else if (name == 'LZWDecode' || name == 'LZW') {
|
||||
}
|
||||
if (name == 'LZWDecode' || name == 'LZW') {
|
||||
var earlyChange = 1;
|
||||
if (params) {
|
||||
if (params.has('EarlyChange'))
|
||||
@ -234,31 +235,34 @@ var Parser = (function parserParser() {
|
||||
new LZWStream(stream, earlyChange), params);
|
||||
}
|
||||
return new LZWStream(stream, earlyChange);
|
||||
} else if (name == 'DCTDecode' || name == 'DCT') {
|
||||
}
|
||||
if (name == 'DCTDecode' || name == 'DCT') {
|
||||
var bytes = stream.getBytes(length);
|
||||
return new JpegStream(bytes, stream.dict, this.xref);
|
||||
} else if (name == 'ASCII85Decode' || name == 'A85') {
|
||||
return new Ascii85Stream(stream);
|
||||
} else if (name == 'ASCIIHexDecode' || name == 'AHx') {
|
||||
return new AsciiHexStream(stream);
|
||||
} else if (name == 'CCITTFaxDecode' || name == 'CCF') {
|
||||
return new CCITTFaxStream(stream, params);
|
||||
} else {
|
||||
TODO('filter "' + name + '" not supported yet');
|
||||
}
|
||||
if (name == 'ASCII85Decode' || name == 'A85') {
|
||||
return new Ascii85Stream(stream);
|
||||
}
|
||||
if (name == 'ASCIIHexDecode' || name == 'AHx') {
|
||||
return new AsciiHexStream(stream);
|
||||
}
|
||||
if (name == 'CCITTFaxDecode' || name == 'CCF') {
|
||||
return new CCITTFaxStream(stream, params);
|
||||
}
|
||||
warn('filter "' + name + '" not supported yet');
|
||||
return stream;
|
||||
}
|
||||
};
|
||||
|
||||
return constructor;
|
||||
return Parser;
|
||||
})();
|
||||
|
||||
var Lexer = (function lexer() {
|
||||
function constructor(stream) {
|
||||
var Lexer = (function LexerClosure() {
|
||||
function Lexer(stream) {
|
||||
this.stream = stream;
|
||||
}
|
||||
|
||||
constructor.isSpace = function lexerIsSpace(ch) {
|
||||
Lexer.isSpace = function lexerIsSpace(ch) {
|
||||
return ch == ' ' || ch == '\t' || ch == '\x0d' || ch == '\x0a';
|
||||
};
|
||||
|
||||
@ -292,7 +296,7 @@ var Lexer = (function lexer() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
constructor.prototype = {
|
||||
Lexer.prototype = {
|
||||
getNumber: function lexerGetNumber(ch) {
|
||||
var floating = false;
|
||||
var str = ch;
|
||||
@ -492,14 +496,14 @@ var Lexer = (function lexer() {
|
||||
// array punctuation
|
||||
case '[':
|
||||
case ']':
|
||||
return new Cmd(ch);
|
||||
return Cmd.get(ch);
|
||||
// hex string or dict punctuation
|
||||
case '<':
|
||||
ch = stream.lookChar();
|
||||
if (ch == '<') {
|
||||
// dict punctuation
|
||||
stream.skip();
|
||||
return new Cmd('<<');
|
||||
return Cmd.get('<<');
|
||||
}
|
||||
return this.getHexString(ch);
|
||||
// dict punctuation
|
||||
@ -507,11 +511,11 @@ var Lexer = (function lexer() {
|
||||
ch = stream.lookChar();
|
||||
if (ch == '>') {
|
||||
stream.skip();
|
||||
return new Cmd('>>');
|
||||
return Cmd.get('>>');
|
||||
}
|
||||
case '{':
|
||||
case '}':
|
||||
return new Cmd(ch);
|
||||
return Cmd.get(ch);
|
||||
// fall through
|
||||
case ')':
|
||||
error('Illegal character: ' + ch);
|
||||
@ -534,7 +538,7 @@ var Lexer = (function lexer() {
|
||||
return false;
|
||||
if (str == 'null')
|
||||
return null;
|
||||
return new Cmd(str);
|
||||
return Cmd.get(str);
|
||||
},
|
||||
skipToNextLine: function lexerSkipToNextLine() {
|
||||
var stream = this.stream;
|
||||
@ -554,11 +558,11 @@ var Lexer = (function lexer() {
|
||||
}
|
||||
};
|
||||
|
||||
return constructor;
|
||||
return Lexer;
|
||||
})();
|
||||
|
||||
var Linearization = (function linearizationLinearization() {
|
||||
function constructor(stream) {
|
||||
var Linearization = (function LinearizationClosure() {
|
||||
function Linearization(stream) {
|
||||
this.parser = new Parser(new Lexer(stream), false);
|
||||
var obj1 = this.parser.getObj();
|
||||
var obj2 = this.parser.getObj();
|
||||
@ -572,7 +576,7 @@ var Linearization = (function linearizationLinearization() {
|
||||
}
|
||||
}
|
||||
|
||||
constructor.prototype = {
|
||||
Linearization.prototype = {
|
||||
getInt: function linearizationGetInt(name) {
|
||||
var linDict = this.linDict;
|
||||
var obj;
|
||||
@ -631,6 +635,6 @@ var Linearization = (function linearizationLinearization() {
|
||||
}
|
||||
};
|
||||
|
||||
return constructor;
|
||||
return Linearization;
|
||||
})();
|
||||
|
||||
|
@ -3,13 +3,18 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var Pattern = (function patternPattern() {
|
||||
var PatternType = {
|
||||
AXIAL: 2,
|
||||
RADIAL: 3
|
||||
};
|
||||
|
||||
var Pattern = (function PatternClosure() {
|
||||
// Constructor should define this.getPattern
|
||||
function constructor() {
|
||||
function Pattern() {
|
||||
error('should not call Pattern constructor');
|
||||
}
|
||||
|
||||
constructor.prototype = {
|
||||
Pattern.prototype = {
|
||||
// Input: current Canvas context
|
||||
// Output: the appropriate fillStyle or strokeStyle
|
||||
getPattern: function pattern_getStyle(ctx) {
|
||||
@ -17,34 +22,34 @@ var Pattern = (function patternPattern() {
|
||||
}
|
||||
};
|
||||
|
||||
constructor.shadingFromIR = function pattern_shadingFromIR(ctx, raw) {
|
||||
Pattern.shadingFromIR = function pattern_shadingFromIR(ctx, raw) {
|
||||
return Shadings[raw[0]].fromIR(ctx, raw);
|
||||
};
|
||||
|
||||
constructor.parseShading = function pattern_shading(shading, matrix, xref,
|
||||
Pattern.parseShading = function pattern_shading(shading, matrix, xref,
|
||||
res, ctx) {
|
||||
|
||||
var dict = isStream(shading) ? shading.dict : shading;
|
||||
var type = dict.get('ShadingType');
|
||||
|
||||
switch (type) {
|
||||
case 2:
|
||||
case 3:
|
||||
// both radial and axial shadings are handled by RadialAxial shading
|
||||
case PatternType.AXIAL:
|
||||
case PatternType.RADIAL:
|
||||
// Both radial and axial shadings are handled by RadialAxial shading.
|
||||
return new Shadings.RadialAxial(dict, matrix, xref, res, ctx);
|
||||
default:
|
||||
return new Shadings.Dummy();
|
||||
}
|
||||
};
|
||||
return constructor;
|
||||
return Pattern;
|
||||
})();
|
||||
|
||||
var Shadings = {};
|
||||
|
||||
// Radial and axial shading have very similar implementations
|
||||
// If needed, the implementations can be broken into two classes
|
||||
Shadings.RadialAxial = (function radialAxialShading() {
|
||||
function constructor(dict, matrix, xref, res, ctx) {
|
||||
Shadings.RadialAxial = (function RadialAxialClosure() {
|
||||
function RadialAxial(dict, matrix, xref, res, ctx) {
|
||||
this.matrix = matrix;
|
||||
this.coordsArr = dict.get('Coords');
|
||||
this.shadingType = dict.get('ShadingType');
|
||||
@ -97,7 +102,7 @@ Shadings.RadialAxial = (function radialAxialShading() {
|
||||
this.colorStops = colorStops;
|
||||
}
|
||||
|
||||
constructor.fromIR = function radialAxialShadingGetIR(ctx, raw) {
|
||||
RadialAxial.fromIR = function radialAxialShadingGetIR(ctx, raw) {
|
||||
var type = raw[1];
|
||||
var colorStops = raw[2];
|
||||
var p0 = raw[3];
|
||||
@ -117,9 +122,9 @@ Shadings.RadialAxial = (function radialAxialShading() {
|
||||
}
|
||||
|
||||
var grad;
|
||||
if (type == 2)
|
||||
if (type == PatternType.AXIAL)
|
||||
grad = ctx.createLinearGradient(p0[0], p0[1], p1[0], p1[1]);
|
||||
else if (type == 3)
|
||||
else if (type == PatternType.RADIAL)
|
||||
grad = ctx.createRadialGradient(p0[0], p0[1], r0, p1[0], p1[1], r1);
|
||||
|
||||
for (var i = 0, ii = colorStops.length; i < ii; ++i) {
|
||||
@ -129,16 +134,16 @@ Shadings.RadialAxial = (function radialAxialShading() {
|
||||
return grad;
|
||||
};
|
||||
|
||||
constructor.prototype = {
|
||||
RadialAxial.prototype = {
|
||||
getIR: function radialAxialShadingGetIR() {
|
||||
var coordsArr = this.coordsArr;
|
||||
var type = this.shadingType;
|
||||
if (type == 2) {
|
||||
if (type == PatternType.AXIAL) {
|
||||
var p0 = [coordsArr[0], coordsArr[1]];
|
||||
var p1 = [coordsArr[2], coordsArr[3]];
|
||||
var r0 = null;
|
||||
var r1 = null;
|
||||
} else if (type == 3) {
|
||||
} else if (type == PatternType.RADIAL) {
|
||||
var p0 = [coordsArr[0], coordsArr[1]];
|
||||
var p1 = [coordsArr[3], coordsArr[4]];
|
||||
var r0 = coordsArr[2];
|
||||
@ -157,28 +162,32 @@ Shadings.RadialAxial = (function radialAxialShading() {
|
||||
}
|
||||
};
|
||||
|
||||
return constructor;
|
||||
return RadialAxial;
|
||||
})();
|
||||
|
||||
Shadings.Dummy = (function dummyShading() {
|
||||
function constructor() {
|
||||
Shadings.Dummy = (function DummyClosure() {
|
||||
function Dummy() {
|
||||
this.type = 'Pattern';
|
||||
}
|
||||
|
||||
constructor.fromIR = function dummyShadingFromIR() {
|
||||
Dummy.fromIR = function dummyShadingFromIR() {
|
||||
return 'hotpink';
|
||||
};
|
||||
|
||||
constructor.prototype = {
|
||||
Dummy.prototype = {
|
||||
getIR: function dummyShadingGetIR() {
|
||||
return ['Dummy'];
|
||||
}
|
||||
};
|
||||
return constructor;
|
||||
return Dummy;
|
||||
})();
|
||||
|
||||
var TilingPattern = (function tilingPattern() {
|
||||
var PAINT_TYPE_COLORED = 1, PAINT_TYPE_UNCOLORED = 2;
|
||||
var TilingPattern = (function TilingPatternClosure() {
|
||||
var PaintType = {
|
||||
COLORED: 1,
|
||||
UNCOLORED: 2
|
||||
};
|
||||
var MAX_PATTERN_SIZE = 512;
|
||||
|
||||
function TilingPattern(IR, color, ctx, objs) {
|
||||
var IRQueue = IR[2];
|
||||
@ -204,13 +213,13 @@ var TilingPattern = (function tilingPattern() {
|
||||
var width = botRight[0] - topLeft[0];
|
||||
var height = botRight[1] - topLeft[1];
|
||||
|
||||
// TODO: hack to avoid OOM, we would idealy compute the tiling
|
||||
// TODO: hack to avoid OOM, we would ideally compute the tiling
|
||||
// pattern to be only as large as the acual size in device space
|
||||
// This could be computed with .mozCurrentTransform, but still
|
||||
// needs to be implemented
|
||||
while (Math.abs(width) > 512 || Math.abs(height) > 512) {
|
||||
width = 512;
|
||||
height = 512;
|
||||
while (Math.abs(width) > MAX_PATTERN_SIZE ||
|
||||
Math.abs(height) > MAX_PATTERN_SIZE) {
|
||||
width = height = MAX_PATTERN_SIZE;
|
||||
}
|
||||
|
||||
var tmpCanvas = new ScratchCanvas(width, height);
|
||||
@ -220,11 +229,11 @@ var TilingPattern = (function tilingPattern() {
|
||||
var graphics = new CanvasGraphics(tmpCtx, objs);
|
||||
|
||||
switch (paintType) {
|
||||
case PAINT_TYPE_COLORED:
|
||||
case PaintType.COLORED:
|
||||
tmpCtx.fillStyle = ctx.fillStyle;
|
||||
tmpCtx.strokeStyle = ctx.strokeStyle;
|
||||
break;
|
||||
case PAINT_TYPE_UNCOLORED:
|
||||
case PaintType.UNCOLORED:
|
||||
color = Util.makeCssRgb.apply(this, color);
|
||||
tmpCtx.fillStyle = color;
|
||||
tmpCtx.strokeStyle = color;
|
||||
|
184
src/stream.js
184
src/stream.js
@ -3,8 +3,8 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var Stream = (function streamStream() {
|
||||
function constructor(arrayBuffer, start, length, dict) {
|
||||
var Stream = (function StreamClosure() {
|
||||
function Stream(arrayBuffer, start, length, dict) {
|
||||
this.bytes = new Uint8Array(arrayBuffer);
|
||||
this.start = start || 0;
|
||||
this.pos = this.start;
|
||||
@ -14,7 +14,7 @@ var Stream = (function streamStream() {
|
||||
|
||||
// required methods for a stream. if a particular stream does not
|
||||
// implement these, an error should be thrown
|
||||
constructor.prototype = {
|
||||
Stream.prototype = {
|
||||
get length() {
|
||||
return this.end - this.start;
|
||||
},
|
||||
@ -67,11 +67,11 @@ var Stream = (function streamStream() {
|
||||
isStream: true
|
||||
};
|
||||
|
||||
return constructor;
|
||||
return Stream;
|
||||
})();
|
||||
|
||||
var StringStream = (function stringStream() {
|
||||
function constructor(str) {
|
||||
var StringStream = (function StringStreamClosure() {
|
||||
function StringStream(str) {
|
||||
var length = str.length;
|
||||
var bytes = new Uint8Array(length);
|
||||
for (var n = 0; n < length; ++n)
|
||||
@ -79,21 +79,21 @@ var StringStream = (function stringStream() {
|
||||
Stream.call(this, bytes);
|
||||
}
|
||||
|
||||
constructor.prototype = Stream.prototype;
|
||||
StringStream.prototype = Stream.prototype;
|
||||
|
||||
return constructor;
|
||||
return StringStream;
|
||||
})();
|
||||
|
||||
// super class for the decoding streams
|
||||
var DecodeStream = (function decodeStream() {
|
||||
function constructor() {
|
||||
var DecodeStream = (function DecodeStreamClosure() {
|
||||
function DecodeStream() {
|
||||
this.pos = 0;
|
||||
this.bufferLength = 0;
|
||||
this.eof = false;
|
||||
this.buffer = null;
|
||||
}
|
||||
|
||||
constructor.prototype = {
|
||||
DecodeStream.prototype = {
|
||||
ensureBuffer: function decodestream_ensureBuffer(requested) {
|
||||
var buffer = this.buffer;
|
||||
var current = buffer ? buffer.byteLength : 0;
|
||||
@ -178,24 +178,24 @@ var DecodeStream = (function decodeStream() {
|
||||
}
|
||||
};
|
||||
|
||||
return constructor;
|
||||
return DecodeStream;
|
||||
})();
|
||||
|
||||
var FakeStream = (function fakeStream() {
|
||||
function constructor(stream) {
|
||||
var FakeStream = (function FakeStreamClosure() {
|
||||
function FakeStream(stream) {
|
||||
this.dict = stream.dict;
|
||||
DecodeStream.call(this);
|
||||
}
|
||||
|
||||
constructor.prototype = Object.create(DecodeStream.prototype);
|
||||
constructor.prototype.readBlock = function fakeStreamReadBlock() {
|
||||
FakeStream.prototype = Object.create(DecodeStream.prototype);
|
||||
FakeStream.prototype.readBlock = function fakeStreamReadBlock() {
|
||||
var bufferLength = this.bufferLength;
|
||||
bufferLength += 1024;
|
||||
var buffer = this.ensureBuffer(bufferLength);
|
||||
this.bufferLength = bufferLength;
|
||||
};
|
||||
|
||||
constructor.prototype.getBytes = function fakeStreamGetBytes(length) {
|
||||
FakeStream.prototype.getBytes = function fakeStreamGetBytes(length) {
|
||||
var end, pos = this.pos;
|
||||
|
||||
if (length) {
|
||||
@ -217,18 +217,20 @@ var FakeStream = (function fakeStream() {
|
||||
return this.buffer.subarray(pos, end);
|
||||
};
|
||||
|
||||
return constructor;
|
||||
return FakeStream;
|
||||
})();
|
||||
|
||||
var StreamsSequenceStream = (function streamSequenceStream() {
|
||||
function constructor(streams) {
|
||||
var StreamsSequenceStream = (function StreamsSequenceStreamClosure() {
|
||||
function StreamsSequenceStream(streams) {
|
||||
this.streams = streams;
|
||||
DecodeStream.call(this);
|
||||
}
|
||||
|
||||
constructor.prototype = Object.create(DecodeStream.prototype);
|
||||
StreamsSequenceStream.prototype = Object.create(DecodeStream.prototype);
|
||||
|
||||
StreamsSequenceStream.prototype.readBlock =
|
||||
function streamSequenceStreamReadBlock() {
|
||||
|
||||
constructor.prototype.readBlock = function streamSequenceStreamReadBlock() {
|
||||
var streams = this.streams;
|
||||
if (streams.length == 0) {
|
||||
this.eof = true;
|
||||
@ -243,10 +245,10 @@ var StreamsSequenceStream = (function streamSequenceStream() {
|
||||
this.bufferLength = newLength;
|
||||
};
|
||||
|
||||
return constructor;
|
||||
return StreamsSequenceStream;
|
||||
})();
|
||||
|
||||
var FlateStream = (function flateStream() {
|
||||
var FlateStream = (function FlateStreamClosure() {
|
||||
var codeLenCodeMap = new Uint32Array([
|
||||
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
|
||||
]);
|
||||
@ -339,7 +341,7 @@ var FlateStream = (function flateStream() {
|
||||
0x50003, 0x50013, 0x5000b, 0x5001b, 0x50007, 0x50017, 0x5000f, 0x00000
|
||||
]), 5];
|
||||
|
||||
function constructor(stream) {
|
||||
function FlateStream(stream) {
|
||||
var bytes = stream.getBytes();
|
||||
var bytesPos = 0;
|
||||
|
||||
@ -364,9 +366,9 @@ var FlateStream = (function flateStream() {
|
||||
DecodeStream.call(this);
|
||||
}
|
||||
|
||||
constructor.prototype = Object.create(DecodeStream.prototype);
|
||||
FlateStream.prototype = Object.create(DecodeStream.prototype);
|
||||
|
||||
constructor.prototype.getBits = function flateStreamGetBits(bits) {
|
||||
FlateStream.prototype.getBits = function flateStreamGetBits(bits) {
|
||||
var codeSize = this.codeSize;
|
||||
var codeBuf = this.codeBuf;
|
||||
var bytes = this.bytes;
|
||||
@ -386,7 +388,7 @@ var FlateStream = (function flateStream() {
|
||||
return b;
|
||||
};
|
||||
|
||||
constructor.prototype.getCode = function flateStreamGetCode(table) {
|
||||
FlateStream.prototype.getCode = function flateStreamGetCode(table) {
|
||||
var codes = table[0];
|
||||
var maxLen = table[1];
|
||||
var codeSize = this.codeSize;
|
||||
@ -412,7 +414,7 @@ var FlateStream = (function flateStream() {
|
||||
return codeVal;
|
||||
};
|
||||
|
||||
constructor.prototype.generateHuffmanTable =
|
||||
FlateStream.prototype.generateHuffmanTable =
|
||||
function flateStreamGenerateHuffmanTable(lengths) {
|
||||
var n = lengths.length;
|
||||
|
||||
@ -451,7 +453,7 @@ var FlateStream = (function flateStream() {
|
||||
return [codes, maxLen];
|
||||
};
|
||||
|
||||
constructor.prototype.readBlock = function flateStreamReadBlock() {
|
||||
FlateStream.prototype.readBlock = function flateStreamReadBlock() {
|
||||
// read block header
|
||||
var hdr = this.getBits(3);
|
||||
if (hdr & 1)
|
||||
@ -582,11 +584,11 @@ var FlateStream = (function flateStream() {
|
||||
}
|
||||
};
|
||||
|
||||
return constructor;
|
||||
return FlateStream;
|
||||
})();
|
||||
|
||||
var PredictorStream = (function predictorStream() {
|
||||
function constructor(stream, params) {
|
||||
var PredictorStream = (function PredictorStreamClosure() {
|
||||
function PredictorStream(stream, params) {
|
||||
var predictor = this.predictor = params.get('Predictor') || 1;
|
||||
|
||||
if (predictor <= 1)
|
||||
@ -613,9 +615,9 @@ var PredictorStream = (function predictorStream() {
|
||||
return this;
|
||||
}
|
||||
|
||||
constructor.prototype = Object.create(DecodeStream.prototype);
|
||||
PredictorStream.prototype = Object.create(DecodeStream.prototype);
|
||||
|
||||
constructor.prototype.readBlockTiff =
|
||||
PredictorStream.prototype.readBlockTiff =
|
||||
function predictorStreamReadBlockTiff() {
|
||||
var rowBytes = this.rowBytes;
|
||||
|
||||
@ -676,7 +678,9 @@ var PredictorStream = (function predictorStream() {
|
||||
this.bufferLength += rowBytes;
|
||||
};
|
||||
|
||||
constructor.prototype.readBlockPng = function predictorStreamReadBlockPng() {
|
||||
PredictorStream.prototype.readBlockPng =
|
||||
function predictorStreamReadBlockPng() {
|
||||
|
||||
var rowBytes = this.rowBytes;
|
||||
var pixBytes = this.pixBytes;
|
||||
|
||||
@ -753,7 +757,7 @@ var PredictorStream = (function predictorStream() {
|
||||
this.bufferLength += rowBytes;
|
||||
};
|
||||
|
||||
return constructor;
|
||||
return PredictorStream;
|
||||
})();
|
||||
|
||||
/**
|
||||
@ -763,7 +767,7 @@ var PredictorStream = (function predictorStream() {
|
||||
* a library to decode these images and the stream behaves like all the other
|
||||
* DecodeStreams.
|
||||
*/
|
||||
var JpegStream = (function jpegStream() {
|
||||
var JpegStream = (function JpegStreamClosure() {
|
||||
function isAdobeImage(bytes) {
|
||||
var maxBytesScanned = Math.max(bytes.length - 16, 1024);
|
||||
// Looking for APP14, 'Adobe'
|
||||
@ -794,7 +798,7 @@ var JpegStream = (function jpegStream() {
|
||||
return newBytes;
|
||||
}
|
||||
|
||||
function constructor(bytes, dict, xref) {
|
||||
function JpegStream(bytes, dict, xref) {
|
||||
// TODO: per poppler, some images may have 'junk' before that
|
||||
// need to be removed
|
||||
this.dict = dict;
|
||||
@ -825,9 +829,9 @@ var JpegStream = (function jpegStream() {
|
||||
DecodeStream.call(this);
|
||||
}
|
||||
|
||||
constructor.prototype = Object.create(DecodeStream.prototype);
|
||||
JpegStream.prototype = Object.create(DecodeStream.prototype);
|
||||
|
||||
constructor.prototype.ensureBuffer = function jpegStreamEnsureBuffer(req) {
|
||||
JpegStream.prototype.ensureBuffer = function jpegStreamEnsureBuffer(req) {
|
||||
if (this.bufferLength)
|
||||
return;
|
||||
var jpegImage = new JpegImage();
|
||||
@ -839,18 +843,18 @@ var JpegStream = (function jpegStream() {
|
||||
this.buffer = data;
|
||||
this.bufferLength = data.length;
|
||||
};
|
||||
constructor.prototype.getIR = function jpegStreamGetIR() {
|
||||
JpegStream.prototype.getIR = function jpegStreamGetIR() {
|
||||
return this.src;
|
||||
};
|
||||
constructor.prototype.getChar = function jpegStreamGetChar() {
|
||||
JpegStream.prototype.getChar = function jpegStreamGetChar() {
|
||||
error('internal error: getChar is not valid on JpegStream');
|
||||
};
|
||||
|
||||
return constructor;
|
||||
return JpegStream;
|
||||
})();
|
||||
|
||||
var DecryptStream = (function decryptStream() {
|
||||
function constructor(str, decrypt) {
|
||||
var DecryptStream = (function DecryptStreamClosure() {
|
||||
function DecryptStream(str, decrypt) {
|
||||
this.str = str;
|
||||
this.dict = str.dict;
|
||||
this.decrypt = decrypt;
|
||||
@ -860,9 +864,9 @@ var DecryptStream = (function decryptStream() {
|
||||
|
||||
var chunkSize = 512;
|
||||
|
||||
constructor.prototype = Object.create(DecodeStream.prototype);
|
||||
DecryptStream.prototype = Object.create(DecodeStream.prototype);
|
||||
|
||||
constructor.prototype.readBlock = function decryptStreamReadBlock() {
|
||||
DecryptStream.prototype.readBlock = function decryptStreamReadBlock() {
|
||||
var chunk = this.str.getBytes(chunkSize);
|
||||
if (!chunk || chunk.length == 0) {
|
||||
this.eof = true;
|
||||
@ -879,11 +883,11 @@ var DecryptStream = (function decryptStream() {
|
||||
this.bufferLength = bufferLength;
|
||||
};
|
||||
|
||||
return constructor;
|
||||
return DecryptStream;
|
||||
})();
|
||||
|
||||
var Ascii85Stream = (function ascii85Stream() {
|
||||
function constructor(str) {
|
||||
var Ascii85Stream = (function Ascii85StreamClosure() {
|
||||
function Ascii85Stream(str) {
|
||||
this.str = str;
|
||||
this.dict = str.dict;
|
||||
this.input = new Uint8Array(5);
|
||||
@ -891,9 +895,9 @@ var Ascii85Stream = (function ascii85Stream() {
|
||||
DecodeStream.call(this);
|
||||
}
|
||||
|
||||
constructor.prototype = Object.create(DecodeStream.prototype);
|
||||
Ascii85Stream.prototype = Object.create(DecodeStream.prototype);
|
||||
|
||||
constructor.prototype.readBlock = function ascii85StreamReadBlock() {
|
||||
Ascii85Stream.prototype.readBlock = function ascii85StreamReadBlock() {
|
||||
var tildaCode = '~'.charCodeAt(0);
|
||||
var zCode = 'z'.charCodeAt(0);
|
||||
var str = this.str;
|
||||
@ -948,11 +952,11 @@ var Ascii85Stream = (function ascii85Stream() {
|
||||
}
|
||||
};
|
||||
|
||||
return constructor;
|
||||
return Ascii85Stream;
|
||||
})();
|
||||
|
||||
var AsciiHexStream = (function asciiHexStream() {
|
||||
function constructor(str) {
|
||||
var AsciiHexStream = (function AsciiHexStreamClosure() {
|
||||
function AsciiHexStream(str) {
|
||||
this.str = str;
|
||||
this.dict = str.dict;
|
||||
|
||||
@ -986,9 +990,9 @@ var AsciiHexStream = (function asciiHexStream() {
|
||||
102: 15
|
||||
};
|
||||
|
||||
constructor.prototype = Object.create(DecodeStream.prototype);
|
||||
AsciiHexStream.prototype = Object.create(DecodeStream.prototype);
|
||||
|
||||
constructor.prototype.readBlock = function asciiHexStreamReadBlock() {
|
||||
AsciiHexStream.prototype.readBlock = function asciiHexStreamReadBlock() {
|
||||
var gtCode = '>'.charCodeAt(0), bytes = this.str.getBytes(), c, n,
|
||||
decodeLength, buffer, bufferLength, i, length;
|
||||
|
||||
@ -1018,10 +1022,10 @@ var AsciiHexStream = (function asciiHexStream() {
|
||||
this.eof = true;
|
||||
};
|
||||
|
||||
return constructor;
|
||||
return AsciiHexStream;
|
||||
})();
|
||||
|
||||
var CCITTFaxStream = (function ccittFaxStream() {
|
||||
var CCITTFaxStream = (function CCITTFaxStreamClosure() {
|
||||
|
||||
var ccittEOL = -2;
|
||||
var twoDimPass = 0;
|
||||
@ -1449,7 +1453,7 @@ var CCITTFaxStream = (function ccittFaxStream() {
|
||||
[2, 2], [2, 2], [2, 2], [2, 2]
|
||||
];
|
||||
|
||||
function constructor(str, params) {
|
||||
function CCITTFaxStream(str, params) {
|
||||
this.str = str;
|
||||
this.dict = str.dict;
|
||||
|
||||
@ -1494,9 +1498,9 @@ var CCITTFaxStream = (function ccittFaxStream() {
|
||||
DecodeStream.call(this);
|
||||
}
|
||||
|
||||
constructor.prototype = Object.create(DecodeStream.prototype);
|
||||
CCITTFaxStream.prototype = Object.create(DecodeStream.prototype);
|
||||
|
||||
constructor.prototype.readBlock = function ccittFaxStreamReadBlock() {
|
||||
CCITTFaxStream.prototype.readBlock = function ccittFaxStreamReadBlock() {
|
||||
while (!this.eof) {
|
||||
var c = this.lookChar();
|
||||
this.buf = EOF;
|
||||
@ -1505,7 +1509,7 @@ var CCITTFaxStream = (function ccittFaxStream() {
|
||||
}
|
||||
};
|
||||
|
||||
constructor.prototype.addPixels =
|
||||
CCITTFaxStream.prototype.addPixels =
|
||||
function ccittFaxStreamAddPixels(a1, blackPixels) {
|
||||
var codingLine = this.codingLine;
|
||||
var codingPos = this.codingPos;
|
||||
@ -1525,7 +1529,7 @@ var CCITTFaxStream = (function ccittFaxStream() {
|
||||
this.codingPos = codingPos;
|
||||
};
|
||||
|
||||
constructor.prototype.addPixelsNeg =
|
||||
CCITTFaxStream.prototype.addPixelsNeg =
|
||||
function ccittFaxStreamAddPixelsNeg(a1, blackPixels) {
|
||||
var codingLine = this.codingLine;
|
||||
var codingPos = this.codingPos;
|
||||
@ -1554,7 +1558,7 @@ var CCITTFaxStream = (function ccittFaxStream() {
|
||||
this.codingPos = codingPos;
|
||||
};
|
||||
|
||||
constructor.prototype.lookChar = function ccittFaxStreamLookChar() {
|
||||
CCITTFaxStream.prototype.lookChar = function ccittFaxStreamLookChar() {
|
||||
if (this.buf != EOF)
|
||||
return this.buf;
|
||||
|
||||
@ -1852,10 +1856,10 @@ var CCITTFaxStream = (function ccittFaxStream() {
|
||||
// values. The first array element indicates whether a valid code is being
|
||||
// returned. The second array element is the actual code. The third array
|
||||
// element indicates whether EOF was reached.
|
||||
var findTableCode = function ccittFaxStreamFindTableCode(start, end, table,
|
||||
limit) {
|
||||
var limitValue = limit || 0;
|
||||
CCITTFaxStream.prototype.findTableCode =
|
||||
function ccittFaxStreamFindTableCode(start, end, table, limit) {
|
||||
|
||||
var limitValue = limit || 0;
|
||||
for (var i = start; i <= end; ++i) {
|
||||
var code = this.lookBits(i);
|
||||
if (code == EOF)
|
||||
@ -1873,7 +1877,9 @@ var CCITTFaxStream = (function ccittFaxStream() {
|
||||
return [false, 0, false];
|
||||
};
|
||||
|
||||
constructor.prototype.getTwoDimCode = function ccittFaxStreamGetTwoDimCode() {
|
||||
CCITTFaxStream.prototype.getTwoDimCode =
|
||||
function ccittFaxStreamGetTwoDimCode() {
|
||||
|
||||
var code = 0;
|
||||
var p;
|
||||
if (this.eoblock) {
|
||||
@ -1884,7 +1890,7 @@ var CCITTFaxStream = (function ccittFaxStream() {
|
||||
return p[1];
|
||||
}
|
||||
} else {
|
||||
var result = findTableCode(1, 7, twoDimTable);
|
||||
var result = this.findTableCode(1, 7, twoDimTable);
|
||||
if (result[0] && result[2])
|
||||
return result[1];
|
||||
}
|
||||
@ -1892,7 +1898,9 @@ var CCITTFaxStream = (function ccittFaxStream() {
|
||||
return EOF;
|
||||
};
|
||||
|
||||
constructor.prototype.getWhiteCode = function ccittFaxStreamGetWhiteCode() {
|
||||
CCITTFaxStream.prototype.getWhiteCode =
|
||||
function ccittFaxStreamGetWhiteCode() {
|
||||
|
||||
var code = 0;
|
||||
var p;
|
||||
var n;
|
||||
@ -1911,11 +1919,11 @@ var CCITTFaxStream = (function ccittFaxStream() {
|
||||
return p[1];
|
||||
}
|
||||
} else {
|
||||
var result = findTableCode(1, 9, whiteTable2);
|
||||
var result = this.findTableCode(1, 9, whiteTable2);
|
||||
if (result[0])
|
||||
return result[1];
|
||||
|
||||
result = findTableCode(11, 12, whiteTable1);
|
||||
result = this.findTableCode(11, 12, whiteTable1);
|
||||
if (result[0])
|
||||
return result[1];
|
||||
}
|
||||
@ -1924,7 +1932,9 @@ var CCITTFaxStream = (function ccittFaxStream() {
|
||||
return 1;
|
||||
};
|
||||
|
||||
constructor.prototype.getBlackCode = function ccittFaxStreamGetBlackCode() {
|
||||
CCITTFaxStream.prototype.getBlackCode =
|
||||
function ccittFaxStreamGetBlackCode() {
|
||||
|
||||
var code, p;
|
||||
if (this.eoblock) {
|
||||
code = this.lookBits(13);
|
||||
@ -1942,15 +1952,15 @@ var CCITTFaxStream = (function ccittFaxStream() {
|
||||
return p[1];
|
||||
}
|
||||
} else {
|
||||
var result = findTableCode(2, 6, blackTable3);
|
||||
var result = this.findTableCode(2, 6, blackTable3);
|
||||
if (result[0])
|
||||
return result[1];
|
||||
|
||||
result = findTableCode(7, 12, blackTable2, 64);
|
||||
result = this.findTableCode(7, 12, blackTable2, 64);
|
||||
if (result[0])
|
||||
return result[1];
|
||||
|
||||
result = findTableCode(10, 13, blackTable1);
|
||||
result = this.findTableCode(10, 13, blackTable1);
|
||||
if (result[0])
|
||||
return result[1];
|
||||
}
|
||||
@ -1959,7 +1969,7 @@ var CCITTFaxStream = (function ccittFaxStream() {
|
||||
return 1;
|
||||
};
|
||||
|
||||
constructor.prototype.lookBits = function ccittFaxStreamLookBits(n) {
|
||||
CCITTFaxStream.prototype.lookBits = function ccittFaxStreamLookBits(n) {
|
||||
var c;
|
||||
while (this.inputBits < n) {
|
||||
if ((c = this.str.getByte()) == null) {
|
||||
@ -1974,16 +1984,16 @@ var CCITTFaxStream = (function ccittFaxStream() {
|
||||
return (this.inputBuf >> (this.inputBits - n)) & (0xFFFF >> (16 - n));
|
||||
};
|
||||
|
||||
constructor.prototype.eatBits = function ccittFaxStreamEatBits(n) {
|
||||
CCITTFaxStream.prototype.eatBits = function ccittFaxStreamEatBits(n) {
|
||||
if ((this.inputBits -= n) < 0)
|
||||
this.inputBits = 0;
|
||||
};
|
||||
|
||||
return constructor;
|
||||
return CCITTFaxStream;
|
||||
})();
|
||||
|
||||
var LZWStream = (function lzwStream() {
|
||||
function constructor(str, earlyChange) {
|
||||
var LZWStream = (function LZWStreamClosure() {
|
||||
function LZWStream(str, earlyChange) {
|
||||
this.str = str;
|
||||
this.dict = str.dict;
|
||||
this.cachedData = 0;
|
||||
@ -2009,9 +2019,9 @@ var LZWStream = (function lzwStream() {
|
||||
DecodeStream.call(this);
|
||||
}
|
||||
|
||||
constructor.prototype = Object.create(DecodeStream.prototype);
|
||||
LZWStream.prototype = Object.create(DecodeStream.prototype);
|
||||
|
||||
constructor.prototype.readBits = function lzwStreamReadBits(n) {
|
||||
LZWStream.prototype.readBits = function lzwStreamReadBits(n) {
|
||||
var bitsCached = this.bitsCached;
|
||||
var cachedData = this.cachedData;
|
||||
while (bitsCached < n) {
|
||||
@ -2029,7 +2039,7 @@ var LZWStream = (function lzwStream() {
|
||||
return (cachedData >>> bitsCached) & ((1 << n) - 1);
|
||||
};
|
||||
|
||||
constructor.prototype.readBlock = function lzwStreamReadBlock() {
|
||||
LZWStream.prototype.readBlock = function lzwStreamReadBlock() {
|
||||
var blockSize = 512;
|
||||
var estimatedDecodedSize = blockSize * 2, decodedSizeDelta = blockSize;
|
||||
var i, j, q;
|
||||
@ -2108,6 +2118,6 @@ var LZWStream = (function lzwStream() {
|
||||
this.bufferLength = currentBufferLength;
|
||||
};
|
||||
|
||||
return constructor;
|
||||
return LZWStream;
|
||||
})();
|
||||
|
||||
|
74
src/util.js
74
src/util.js
@ -76,24 +76,24 @@ function stringToBytes(str) {
|
||||
|
||||
var IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0];
|
||||
|
||||
var Util = (function utilUtil() {
|
||||
function constructor() {}
|
||||
constructor.makeCssRgb = function makergb(r, g, b) {
|
||||
var Util = (function UtilClosure() {
|
||||
function Util() {}
|
||||
Util.makeCssRgb = function makergb(r, g, b) {
|
||||
var ri = (255 * r) | 0, gi = (255 * g) | 0, bi = (255 * b) | 0;
|
||||
return 'rgb(' + ri + ',' + gi + ',' + bi + ')';
|
||||
};
|
||||
constructor.makeCssCmyk = function makecmyk(c, m, y, k) {
|
||||
Util.makeCssCmyk = function makecmyk(c, m, y, k) {
|
||||
c = (new DeviceCmykCS()).getRgb([c, m, y, k]);
|
||||
var ri = (255 * c[0]) | 0, gi = (255 * c[1]) | 0, bi = (255 * c[2]) | 0;
|
||||
return 'rgb(' + ri + ',' + gi + ',' + bi + ')';
|
||||
};
|
||||
constructor.applyTransform = function apply(p, m) {
|
||||
Util.applyTransform = function apply(p, m) {
|
||||
var xt = p[0] * m[0] + p[1] * m[2] + m[4];
|
||||
var yt = p[0] * m[1] + p[1] * m[3] + m[5];
|
||||
return [xt, yt];
|
||||
};
|
||||
|
||||
return constructor;
|
||||
return Util;
|
||||
})();
|
||||
|
||||
var PDFStringTranslateTable = [
|
||||
@ -197,7 +197,7 @@ function isPDFFunction(v) {
|
||||
* can be set. If any of these happens twice or the data is required before
|
||||
* it was set, an exception is throw.
|
||||
*/
|
||||
var Promise = (function promise() {
|
||||
var Promise = (function PromiseClosure() {
|
||||
var EMPTY_PROMISE = {};
|
||||
|
||||
/**
|
||||
@ -206,6 +206,8 @@ var Promise = (function promise() {
|
||||
*/
|
||||
function Promise(name, data) {
|
||||
this.name = name;
|
||||
this.isRejected = false;
|
||||
this.error = null;
|
||||
// If you build a promise and pass in some data it's already resolved.
|
||||
if (data != null) {
|
||||
this.isResolved = true;
|
||||
@ -216,8 +218,35 @@ var Promise = (function promise() {
|
||||
this._data = EMPTY_PROMISE;
|
||||
}
|
||||
this.callbacks = [];
|
||||
this.errbacks = [];
|
||||
};
|
||||
/**
|
||||
* Builds a promise that is resolved when all the passed in promises are
|
||||
* resolved.
|
||||
* @param {Promise[]} promises Array of promises to wait for.
|
||||
* @return {Promise} New dependant promise.
|
||||
*/
|
||||
Promise.all = function(promises) {
|
||||
var deferred = new Promise();
|
||||
var unresolved = promises.length;
|
||||
var results = [];
|
||||
if (unresolved === 0) {
|
||||
deferred.resolve(results);
|
||||
return deferred;
|
||||
}
|
||||
for (var i = 0; i < unresolved; ++i) {
|
||||
var promise = promises[i];
|
||||
promise.then((function(i) {
|
||||
return function(value) {
|
||||
results[i] = value;
|
||||
unresolved--;
|
||||
if (unresolved === 0)
|
||||
deferred.resolve(results);
|
||||
};
|
||||
})(i));
|
||||
}
|
||||
return deferred;
|
||||
};
|
||||
|
||||
Promise.prototype = {
|
||||
hasData: false,
|
||||
|
||||
@ -256,9 +285,12 @@ var Promise = (function promise() {
|
||||
if (this.isResolved) {
|
||||
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.data = data;
|
||||
this.data = data || null;
|
||||
var callbacks = this.callbacks;
|
||||
|
||||
for (var i = 0, ii = callbacks.length; i < ii; i++) {
|
||||
@ -266,7 +298,24 @@ var Promise = (function promise() {
|
||||
}
|
||||
},
|
||||
|
||||
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) {
|
||||
throw 'Requiring callback' + this.name;
|
||||
}
|
||||
@ -275,8 +324,13 @@ var Promise = (function promise() {
|
||||
if (this.isResolved) {
|
||||
var data = this.data;
|
||||
callback.call(null, data);
|
||||
} else if (this.isRejected && errorback) {
|
||||
var error = this.error;
|
||||
errback.call(null, error);
|
||||
} else {
|
||||
this.callbacks.push(callback);
|
||||
if (errback)
|
||||
this.errbacks.push(errback);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -6,6 +6,8 @@
|
||||
function MessageHandler(name, comObj) {
|
||||
this.name = name;
|
||||
this.comObj = comObj;
|
||||
this.callbackIndex = 1;
|
||||
var callbacks = this.callbacks = {};
|
||||
var ah = this.actionHandler = {};
|
||||
|
||||
ah['console_log'] = [function ahConsoleLog(data) {
|
||||
@ -17,9 +19,30 @@ function MessageHandler(name, comObj) {
|
||||
|
||||
comObj.onmessage = function messageHandlerComObjOnMessage(event) {
|
||||
var data = event.data;
|
||||
if (data.action in ah) {
|
||||
if (data.isReply) {
|
||||
var callbackId = data.callbackId;
|
||||
if (data.callbackId in callbacks) {
|
||||
var callback = callbacks[callbackId];
|
||||
delete callbacks[callbackId];
|
||||
callback(data.data);
|
||||
} else {
|
||||
throw 'Cannot resolve callback ' + callbackId;
|
||||
}
|
||||
} else if (data.action in ah) {
|
||||
var action = ah[data.action];
|
||||
action[0].call(action[1], data.data);
|
||||
if (data.callbackId) {
|
||||
var promise = new Promise();
|
||||
promise.then(function(resolvedData) {
|
||||
comObj.postMessage({
|
||||
isReply: true,
|
||||
callbackId: data.callbackId,
|
||||
data: resolvedData
|
||||
});
|
||||
});
|
||||
action[0].call(action[1], data.data, promise);
|
||||
} else {
|
||||
action[0].call(action[1], data.data);
|
||||
}
|
||||
} else {
|
||||
throw 'Unkown action from worker: ' + data.action;
|
||||
}
|
||||
@ -34,12 +57,23 @@ MessageHandler.prototype = {
|
||||
}
|
||||
ah[actionName] = [handler, scope];
|
||||
},
|
||||
|
||||
send: function messageHandlerSend(actionName, data) {
|
||||
this.comObj.postMessage({
|
||||
/**
|
||||
* Sends a message to the comObj to invoke the action with the supplied data.
|
||||
* @param {String} actionName Action to call.
|
||||
* @param {JSON} data JSON data to send.
|
||||
* @param {function} [callback] Optional callback that will handle a reply.
|
||||
*/
|
||||
send: function messageHandlerSend(actionName, data, callback) {
|
||||
var message = {
|
||||
action: actionName,
|
||||
data: data
|
||||
});
|
||||
};
|
||||
if (callback) {
|
||||
var callbackId = this.callbackIndex++;
|
||||
this.callbacks[callbackId] = callback;
|
||||
message.callbackId = callbackId;
|
||||
}
|
||||
this.comObj.postMessage(message);
|
||||
}
|
||||
};
|
||||
|
||||
@ -67,7 +101,6 @@ var WorkerMessageHandler = {
|
||||
handler.on('page_request', function wphSetupPageRequest(pageNum) {
|
||||
pageNum = parseInt(pageNum);
|
||||
|
||||
var page = pdfDoc.getPage(pageNum);
|
||||
|
||||
// The following code does quite the same as
|
||||
// Page.prototype.startRendering, but stops at one point and sends the
|
||||
@ -77,9 +110,23 @@ var WorkerMessageHandler = {
|
||||
var start = Date.now();
|
||||
|
||||
var dependency = [];
|
||||
|
||||
// Pre compile the pdf page and fetch the fonts/images.
|
||||
var IRQueue = page.getIRQueue(handler, dependency);
|
||||
var IRQueue = null;
|
||||
try {
|
||||
var page = pdfDoc.getPage(pageNum);
|
||||
// Pre compile the pdf page and fetch the fonts/images.
|
||||
IRQueue = page.getIRQueue(handler, dependency);
|
||||
} catch (e) {
|
||||
// Turn the error into an obj that can be serialized
|
||||
e = {
|
||||
message: typeof e === 'object' ? e.message : e,
|
||||
stack: typeof e === 'object' ? e.stack : null
|
||||
};
|
||||
handler.send('page_error', {
|
||||
pageNum: pageNum,
|
||||
error: e
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('page=%d - getIRQueue: time=%dms, len=%d', pageNum,
|
||||
Date.now() - start, IRQueue.fnArray.length);
|
||||
|
@ -139,6 +139,11 @@ function nextPage(task, loadError) {
|
||||
if (task.skipPages && task.skipPages.indexOf(task.pageNum) >= 0) {
|
||||
log(' skipping page ' + task.pageNum + '/' + task.pdfDoc.numPages +
|
||||
'... ');
|
||||
// empty the canvas
|
||||
canvas.width = 1;
|
||||
canvas.height = 1;
|
||||
clear(canvas.getContext('2d'));
|
||||
|
||||
snapshotCurrentPage(task, '');
|
||||
return;
|
||||
}
|
||||
@ -160,12 +165,24 @@ function nextPage(task, loadError) {
|
||||
canvas.height = pageHeight * pdfToCssUnitsCoef;
|
||||
clear(ctx);
|
||||
|
||||
// using the text layer builder that does nothing to test
|
||||
// text layer creation operations
|
||||
var textLayerBuilder = {
|
||||
beginLayout: function nullTextLayerBuilderBeginLayout() {},
|
||||
endLayout: function nullTextLayerBuilderEndLayout() {},
|
||||
appendText: function nullTextLayerBuilderAppendText(text, fontName,
|
||||
fontSize) {}
|
||||
};
|
||||
|
||||
page.startRendering(
|
||||
ctx,
|
||||
function nextPageStartRendering(e) {
|
||||
snapshotCurrentPage(task, (!failure && e) ?
|
||||
('render : ' + e) : failure);
|
||||
}
|
||||
function nextPageStartRendering(error) {
|
||||
var failureMessage = false;
|
||||
if (error)
|
||||
failureMessage = 'render : ' + error.message;
|
||||
snapshotCurrentPage(task, failureMessage);
|
||||
},
|
||||
textLayerBuilder
|
||||
);
|
||||
} catch (e) {
|
||||
failure = 'page setup : ' + e.toString();
|
||||
|
6
test/pdfs/.gitignore
vendored
6
test/pdfs/.gitignore
vendored
@ -16,3 +16,9 @@
|
||||
!alphatrans.pdf
|
||||
!devicen.pdf
|
||||
!cmykjpeg.pdf
|
||||
!issue840.pdf
|
||||
!scan-bad.pdf
|
||||
!freeculture.pdf
|
||||
!issue918.pdf
|
||||
!smaskdim.pdf
|
||||
!type4psfunc.pdf
|
||||
|
1
test/pdfs/aboutstacks.pdf.link
Normal file
1
test/pdfs/aboutstacks.pdf.link
Normal file
@ -0,0 +1 @@
|
||||
http://greenhousechallenge.org/media/item/313/38/About-Stacks.pdf
|
1
test/pdfs/bpl13210.pdf.link
Normal file
1
test/pdfs/bpl13210.pdf.link
Normal file
@ -0,0 +1 @@
|
||||
http://h20000.www2.hp.com/bc/docs/support/SupportManual/bpl13210/bpl13210.pdf
|
BIN
test/pdfs/freeculture.pdf
Normal file
BIN
test/pdfs/freeculture.pdf
Normal file
Binary file not shown.
1
test/pdfs/geothermal.pdf.link
Normal file
1
test/pdfs/geothermal.pdf.link
Normal file
@ -0,0 +1 @@
|
||||
http://geothermal.inel.gov/publications/future_of_geothermal_energy.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
|
BIN
test/pdfs/issue840.pdf
Normal file
BIN
test/pdfs/issue840.pdf
Normal file
Binary file not shown.
BIN
test/pdfs/issue918.pdf
Normal file
BIN
test/pdfs/issue918.pdf
Normal file
Binary file not shown.
1
test/pdfs/issue919.pdf.link
Normal file
1
test/pdfs/issue919.pdf.link
Normal file
@ -0,0 +1 @@
|
||||
http://agb.traviangames.com/Travian_AR_Terms.pdf
|
1
test/pdfs/lista_preliminar.pdf.link
Normal file
1
test/pdfs/lista_preliminar.pdf.link
Normal file
@ -0,0 +1 @@
|
||||
http://www.lfg.com.br/concursodebolsas/lista_preliminar_classificao.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
|
1
test/pdfs/piperine.pdf.link
Normal file
1
test/pdfs/piperine.pdf.link
Normal file
@ -0,0 +1 @@
|
||||
http://www.erowid.org/archive/rhodium/chemistry/3base/piperonal.pepper/piperine.pepper/465e03piperine.pdf
|
1
test/pdfs/protectip.pdf.link
Normal file
1
test/pdfs/protectip.pdf.link
Normal file
@ -0,0 +1 @@
|
||||
http://leahy.senate.gov/imo/media/doc/BillText-PROTECTIPAct.pdf
|
BIN
test/pdfs/scan-bad.pdf
Executable file
BIN
test/pdfs/scan-bad.pdf
Executable file
Binary file not shown.
BIN
test/pdfs/smaskdim.pdf
Executable file
BIN
test/pdfs/smaskdim.pdf
Executable file
Binary file not shown.
1
test/pdfs/tutorial.pdf.link
Normal file
1
test/pdfs/tutorial.pdf.link
Normal file
@ -0,0 +1 @@
|
||||
http://cplusplus.com/files/tutorial.pdf
|
BIN
test/pdfs/type4psfunc.pdf
Executable file
BIN
test/pdfs/type4psfunc.pdf
Executable file
Binary file not shown.
@ -12,6 +12,7 @@ DOC_ROOT = os.path.abspath(os.path.join(os.path.dirname(__file__),".."))
|
||||
ANAL = True
|
||||
DEFAULT_MANIFEST_FILE = 'test_manifest.json'
|
||||
EQLOG_FILE = 'eq.log'
|
||||
BROWSERLOG_FILE = 'browser.log'
|
||||
REFDIR = 'ref'
|
||||
TMPDIR = 'tmp'
|
||||
VERBOSE = False
|
||||
@ -229,6 +230,7 @@ class BaseBrowserCommand(object):
|
||||
def setup(self):
|
||||
self.tempDir = tempfile.mkdtemp()
|
||||
self.profileDir = os.path.join(self.tempDir, "profile")
|
||||
self.browserLog = open(BROWSERLOG_FILE, "w")
|
||||
|
||||
def teardown(self):
|
||||
# If the browser is still running, wait up to ten seconds for it to quit
|
||||
@ -245,6 +247,8 @@ class BaseBrowserCommand(object):
|
||||
if self.tempDir is not None and os.path.exists(self.tempDir):
|
||||
shutil.rmtree(self.tempDir)
|
||||
|
||||
self.browserLog.close()
|
||||
|
||||
def start(self, url):
|
||||
raise Exception("Can't start BaseBrowserCommand")
|
||||
|
||||
@ -262,7 +266,7 @@ class FirefoxBrowserCommand(BaseBrowserCommand):
|
||||
if platform.system() == "Darwin":
|
||||
cmds.append("-foreground")
|
||||
cmds.extend(["-no-remote", "-profile", self.profileDir, url])
|
||||
self.process = subprocess.Popen(cmds)
|
||||
self.process = subprocess.Popen(cmds, stdout = self.browserLog, stderr = self.browserLog)
|
||||
|
||||
class ChromeBrowserCommand(BaseBrowserCommand):
|
||||
def _fixupMacPath(self):
|
||||
@ -272,7 +276,7 @@ class ChromeBrowserCommand(BaseBrowserCommand):
|
||||
cmds = [self.path]
|
||||
cmds.extend(["--user-data-dir=%s" % self.profileDir,
|
||||
"--no-first-run", "--disable-sync", url])
|
||||
self.process = subprocess.Popen(cmds)
|
||||
self.process = subprocess.Popen(cmds, stdout = self.browserLog, stderr = self.browserLog)
|
||||
|
||||
def makeBrowserCommand(browser):
|
||||
path = browser["path"].lower()
|
||||
|
@ -17,13 +17,13 @@
|
||||
"rounds": 1,
|
||||
"type": "load"
|
||||
},
|
||||
{ "id": "intelisa-load",
|
||||
{ "id": "intelisa-eq",
|
||||
"file": "pdfs/intelisa.pdf",
|
||||
"md5": "f5712097d29287a97f1278839814f682",
|
||||
"md5": "f3ed5487d1afa34d8b77c0c734a95c79",
|
||||
"link": true,
|
||||
"pageLimit": 100,
|
||||
"rounds": 1,
|
||||
"type": "load"
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "pdfspec-load",
|
||||
"file": "pdfs/pdf.pdf",
|
||||
@ -88,6 +88,13 @@
|
||||
"rounds": 1,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "freeculture",
|
||||
"file": "pdfs/freeculture.pdf",
|
||||
"md5": "dcdf3a8268e6a18938a42d5149efcfca",
|
||||
"rounds": 1,
|
||||
"pageLimit": 5,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "wnv_chinese-pdf",
|
||||
"file": "pdfs/wnv_chinese.pdf",
|
||||
"md5": "db682638e68391125e8982d3c984841e",
|
||||
@ -221,6 +228,12 @@
|
||||
"rounds": 1,
|
||||
"type": "load"
|
||||
},
|
||||
{ "id": "scan-bad",
|
||||
"file": "pdfs/scan-bad.pdf",
|
||||
"md5": "4cf988f01ab83f61aca57f406dfd6584",
|
||||
"rounds": 1,
|
||||
"type": "load"
|
||||
},
|
||||
{ "id": "ibwa-bad",
|
||||
"file": "pdfs/ibwa-bad.pdf",
|
||||
"md5": "6ca059d32b74ac2688ae06f727fee755",
|
||||
@ -276,5 +289,111 @@
|
||||
"link": false,
|
||||
"rounds": 1,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "protectip",
|
||||
"file": "pdfs/protectip.pdf",
|
||||
"md5": "676e7a7b8f96d04825361832b1838a93",
|
||||
"link": true,
|
||||
"rounds": 1,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "piperine",
|
||||
"file": "pdfs/piperine.pdf",
|
||||
"md5": "603ca43dc5732dbba1579f122958c0c2",
|
||||
"link": true,
|
||||
"rounds": 1,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "issue840",
|
||||
"file": "pdfs/issue840.pdf",
|
||||
"md5": "20d88011dd7e3c4fb5274979094dab93",
|
||||
"rounds": 1,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "bpl13210",
|
||||
"file": "pdfs/bpl13210.pdf",
|
||||
"md5": "8a08512baa9fa95378d9ad4b995947c7",
|
||||
"link": true,
|
||||
"pageLimit": 5,
|
||||
"rounds": 1,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "tutorial",
|
||||
"file": "pdfs/tutorial.pdf",
|
||||
"md5": "6e122f618c27f3aa9a689423e3be6b8d",
|
||||
"link": true,
|
||||
"rounds": 1,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "geothermal.pdf",
|
||||
"file": "pdfs/geothermal.pdf",
|
||||
"md5": "ecffc0ce38ffdf1e90dc952f186e9a91",
|
||||
"rounds": 1,
|
||||
"link": true,
|
||||
"pageLimit": 5,
|
||||
"skipPages": [1],
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "lista_preliminar",
|
||||
"file": "pdfs/lista_preliminar.pdf",
|
||||
"md5": "4eff251319eeb660ba8a7a5cfac7787d",
|
||||
"rounds": 1,
|
||||
"link": true,
|
||||
"pageLimit": 3,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "issue919",
|
||||
"file": "pdfs/issue919.pdf",
|
||||
"md5": "3a1716a512aca4d7a8d6106bd4885d14",
|
||||
"rounds": 1,
|
||||
"link": true,
|
||||
"pageLimit": 3,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "issue918",
|
||||
"file": "pdfs/issue918.pdf",
|
||||
"md5": "d582cc0f2592ae82936589ced2a47e55",
|
||||
"rounds": 1,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "issue1001",
|
||||
"file": "pdfs/issue1001.pdf",
|
||||
"md5": "0f1496e80a82a923e91d9e74c55ad94e",
|
||||
"rounds": 1,
|
||||
"link": true,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "aboutstacks",
|
||||
"file": "pdfs/aboutstacks.pdf",
|
||||
"md5": "6e7c8416a293ba2d83bc8dd20c6ccf51",
|
||||
"rounds": 1,
|
||||
"link": true,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "smaskdim",
|
||||
"file": "pdfs/smaskdim.pdf",
|
||||
"md5": "de80aeca7cbf79940189fd34d59671ee",
|
||||
"rounds": 1,
|
||||
"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
|
||||
});
|
||||
});
|
||||
|
@ -12,5 +12,120 @@ describe('obj', function() {
|
||||
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);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -5,11 +5,16 @@
|
||||
|
||||
// Checking if the typed arrays are supported
|
||||
(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;
|
||||
}
|
||||
|
||||
function subarray(start, end) {
|
||||
return this.slice(start, end);
|
||||
return new TypedArray(this.slice(start, end));
|
||||
}
|
||||
|
||||
function setArrayOffset(array, offset) {
|
||||
@ -46,6 +51,8 @@
|
||||
window.Uint32Array = TypedArray;
|
||||
window.Int32Array = TypedArray;
|
||||
window.Uint16Array = TypedArray;
|
||||
window.Float32Array = TypedArray;
|
||||
window.Float64Array = TypedArray;
|
||||
})();
|
||||
|
||||
// Object.create() ?
|
||||
@ -205,3 +212,15 @@
|
||||
});
|
||||
})();
|
||||
|
||||
// HTMLElement dataset property
|
||||
(function checkDatasetProperty() {
|
||||
var div = document.createElement('div');
|
||||
if ('dataset' in div)
|
||||
return; // dataset property exists
|
||||
Object.defineProperty(HTMLElement.prototype, 'dataset', {
|
||||
get: function htmlElementDatasetGetter() {
|
||||
// adding dataset field to the actual object
|
||||
return (this.dataset = {});
|
||||
}
|
||||
});
|
||||
})();
|
||||
|
3
web/images/check.svg
Normal file
3
web/images/check.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg height="40" width="40" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2.379,14.729 5.208,11.899 12.958,19.648 25.877,6.733 28.707,9.561 12.958,25.308z" fill="#333333"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 188 B |
3
web/images/comment.svg
Normal file
3
web/images/comment.svg
Normal file
@ -0,0 +1,3 @@
|
||||
<svg height="40" width="40" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M16,5.333c-7.732,0-14,4.701-14,10.5c0,1.982,0.741,3.833,2.016,5.414L2,25.667l5.613-1.441c2.339,1.317,5.237,2.107,8.387,2.107c7.732,0,14-4.701,14-10.5C30,10.034,23.732,5.333,16,5.333z" fill="#333333"></path>
|
||||
</svg>
|
After Width: | Height: | Size: 289 B |
@ -15,6 +15,7 @@ body {
|
||||
/* === Toolbar === */
|
||||
#controls {
|
||||
background-color: #eee;
|
||||
background: -o-linear-gradient(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));
|
||||
border-bottom: 1px solid #666;
|
||||
@ -82,6 +83,7 @@ span#info {
|
||||
bottom: 18px;
|
||||
left: -290px;
|
||||
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;
|
||||
-webkit-transition: left 0.25s ease-in-out 1s;
|
||||
z-index: 1;
|
||||
@ -90,6 +92,7 @@ span#info {
|
||||
#sidebar:hover {
|
||||
left: 0px;
|
||||
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;
|
||||
-webkit-transition: left 0.25s ease-in-out 0s;
|
||||
}
|
||||
@ -232,6 +235,56 @@ canvas {
|
||||
-webkit-box-shadow: 0px 2px 10px #ff0;
|
||||
}
|
||||
|
||||
.textLayer {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.textLayer > div {
|
||||
color: transparent;
|
||||
position: absolute;
|
||||
line-height:1.3;
|
||||
}
|
||||
|
||||
.annotComment > div {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.annotComment > img {
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
.annotComment > img:hover {
|
||||
cursor: pointer;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.annotComment > div {
|
||||
padding: 0.2em;
|
||||
max-width: 20em;
|
||||
background-color: #F1E47B;
|
||||
box-shadow: 0px 2px 10px #333;
|
||||
-moz-box-shadow: 0px 2px 10px #333;
|
||||
-webkit-box-shadow: 0px 2px 10px #333;
|
||||
}
|
||||
|
||||
.annotComment > div > h1 {
|
||||
font-weight: normal;
|
||||
font-size: 1.2em;
|
||||
border-bottom: 1px solid #000000;
|
||||
margin: 0px;
|
||||
}
|
||||
|
||||
/* TODO: file FF bug to support ::-moz-selection:window-inactive
|
||||
so we can override the opaque grey background when the window is inactive;
|
||||
see https://bugzilla.mozilla.org/show_bug.cgi?id=706209 */
|
||||
::selection { background:rgba(0,0,255,0.3); }
|
||||
::-moz-selection { background:rgba(0,0,255,0.3); }
|
||||
|
||||
#viewer {
|
||||
margin: 44px 0px 0px;
|
||||
padding: 8px 0px;
|
||||
@ -252,6 +305,38 @@ canvas {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#errorWrapper {
|
||||
background: none repeat scroll 0 0 #FF5555;
|
||||
color: white;
|
||||
left: 0;
|
||||
position: fixed;
|
||||
right: 0;
|
||||
top: 30px;
|
||||
z-index: 1000;
|
||||
padding: 3px;
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
#errorMessageLeft {
|
||||
float: left;
|
||||
}
|
||||
|
||||
#errorMessageRight {
|
||||
float: right;
|
||||
}
|
||||
|
||||
#errorMoreInfo {
|
||||
background-color: #FFFFFF;
|
||||
color: black;
|
||||
padding: 3px;
|
||||
margin: 3px;
|
||||
width: 98%;
|
||||
}
|
||||
|
||||
.clearBoth {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
/* === Printed media overrides === */
|
||||
@media print {
|
||||
#sidebar {
|
||||
|
@ -27,9 +27,9 @@
|
||||
<script type="text/javascript" src="../src/worker.js"></script> <!-- PDFJSSCRIPT_REMOVE -->
|
||||
<script type="text/javascript" src="../external/jpgjs/jpg.js"></script> <!-- PDFJSSCRIPT_REMOVE -->
|
||||
<script type="text/javascript">PDFJS.workerSrc = '../src/worker_loader.js';</script> <!-- PDFJSSCRIPT_REMOVE -->
|
||||
|
||||
<script type="text/javascript" src="viewer.js"></script>
|
||||
</head>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="controls">
|
||||
@ -67,10 +67,11 @@
|
||||
<option value="0.75">75%</option>
|
||||
<option value="1">100%</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 id="pageWidthOption" value="page-width">Page Width</option>
|
||||
<option id="pageFitOption" value="page-fit">Page Fit</option>
|
||||
<option id="pageAutoOption" value="auto" selected="selected">Auto</option>
|
||||
</select>
|
||||
|
||||
<div class="separator"></div>
|
||||
@ -97,6 +98,24 @@
|
||||
|
||||
<span id="info">--</span>
|
||||
</div>
|
||||
<div id="errorWrapper" hidden='true'>
|
||||
<div id="errorMessageLeft">
|
||||
<span id="errorMessage"></span>
|
||||
<button id="errorShowMore" onclick="" oncontextmenu="return false;">
|
||||
More Information
|
||||
</button>
|
||||
<button id="errorShowLess" onclick="" oncontextmenu="return false;" hidden='true'>
|
||||
Less Information
|
||||
</button>
|
||||
</div>
|
||||
<div id="errorMessageRight">
|
||||
<button id="errorClose" oncontextmenu="return false;">
|
||||
Close
|
||||
</button>
|
||||
</div>
|
||||
<div class="clearBoth"></div>
|
||||
<textarea id="errorMoreInfo" hidden='true' readonly="readonly"></textarea>
|
||||
</div>
|
||||
|
||||
<div id="sidebar">
|
||||
<div id="sidebarBox">
|
||||
@ -121,4 +140,3 @@
|
||||
<div id="viewer"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
649
web/viewer.js
649
web/viewer.js
@ -4,14 +4,15 @@
|
||||
'use strict';
|
||||
|
||||
var kDefaultURL = 'compressed.tracemonkey-pldi-09.pdf';
|
||||
var kDefaultScale = 1.5;
|
||||
var kDefaultScale = 'auto';
|
||||
var kDefaultScaleDelta = 1.1;
|
||||
var kCacheSize = 20;
|
||||
var kCssUnits = 96.0 / 72.0;
|
||||
var kScrollbarPadding = 40;
|
||||
var kMinScale = 0.25;
|
||||
var kMaxScale = 4.0;
|
||||
|
||||
var kImageDirectory = './images/';
|
||||
var kSettingsMemory = 20;
|
||||
|
||||
var Cache = function cacheCache(size) {
|
||||
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 renderingQueue = new RenderingQueue();
|
||||
var currentPageNumber = 1;
|
||||
|
||||
var PDFView = {
|
||||
pages: [],
|
||||
thumbnails: [],
|
||||
currentScale: kDefaultScale,
|
||||
currentScale: 0,
|
||||
currentScaleValue: null,
|
||||
initialBookmark: document.location.hash.substring(1),
|
||||
|
||||
setScale: function pdfViewSetScale(val, resetAutoSettings) {
|
||||
if (val == this.currentScale)
|
||||
return;
|
||||
|
||||
var pages = this.pages;
|
||||
for (var i = 0; i < pages.length; i++)
|
||||
pages[i].update(val * kCssUnits);
|
||||
@ -55,6 +176,7 @@ var PDFView = {
|
||||
return;
|
||||
|
||||
var scale = parseFloat(value);
|
||||
this.currentScaleValue = value;
|
||||
if (scale) {
|
||||
this.setScale(scale, true);
|
||||
return;
|
||||
@ -73,6 +195,10 @@ var PDFView = {
|
||||
this.setScale(
|
||||
Math.min(pageWidthScale, pageHeightScale), resetAutoSettings);
|
||||
}
|
||||
if ('auto' == value)
|
||||
this.setScale(Math.min(1.0, pageWidthScale), resetAutoSettings);
|
||||
|
||||
selectScaleOption(value);
|
||||
},
|
||||
|
||||
zoomIn: function pdfViewZoomIn() {
|
||||
@ -129,7 +255,14 @@ var PDFView = {
|
||||
if (evt.lengthComputable)
|
||||
self.progress(evt.loaded / evt.total);
|
||||
},
|
||||
error: self.error
|
||||
error: function getPdfError(e) {
|
||||
var loadingIndicator = document.getElementById('loading');
|
||||
loadingIndicator.innerHTML = 'Error';
|
||||
var moreInfo = {
|
||||
message: 'Unexpected server response of ' + e.target.status + '.'
|
||||
};
|
||||
self.error('An error occurred while loading the PDF.', moreInfo);
|
||||
}
|
||||
},
|
||||
function getPdfLoad(data) {
|
||||
self.loading = true;
|
||||
@ -168,7 +301,8 @@ var PDFView = {
|
||||
(destRef + 1);
|
||||
if (pageNumber) {
|
||||
var pdfOpenParams = '#page=' + pageNumber;
|
||||
if (isName(dest[1], 'XYZ')) {
|
||||
var destKind = dest[1];
|
||||
if ('name' in destKind && destKind.name == 'XYZ') {
|
||||
var scale = (dest[4] || this.currentScale);
|
||||
pdfOpenParams += '&zoom=' + (scale * 100);
|
||||
if (dest[2] || dest[3]) {
|
||||
@ -181,9 +315,48 @@ var PDFView = {
|
||||
return '';
|
||||
},
|
||||
|
||||
error: function pdfViewError() {
|
||||
var loadingIndicator = document.getElementById('loading');
|
||||
loadingIndicator.innerHTML = 'Error';
|
||||
/**
|
||||
* Show the error box.
|
||||
* @param {String} message A message that is human readable.
|
||||
* @param {Object} moreInfo (optional) Further information about the error
|
||||
* that is more technical. Should have a 'message'
|
||||
* and optionally a 'stack' property.
|
||||
*/
|
||||
error: function pdfViewError(message, moreInfo) {
|
||||
var errorWrapper = document.getElementById('errorWrapper');
|
||||
errorWrapper.removeAttribute('hidden');
|
||||
|
||||
var errorMessage = document.getElementById('errorMessage');
|
||||
errorMessage.innerHTML = message;
|
||||
|
||||
var closeButton = document.getElementById('errorClose');
|
||||
closeButton.onclick = function() {
|
||||
errorWrapper.setAttribute('hidden', 'true');
|
||||
};
|
||||
|
||||
var errorMoreInfo = document.getElementById('errorMoreInfo');
|
||||
var moreInfoButton = document.getElementById('errorShowMore');
|
||||
var lessInfoButton = document.getElementById('errorShowLess');
|
||||
moreInfoButton.onclick = function() {
|
||||
errorMoreInfo.removeAttribute('hidden');
|
||||
moreInfoButton.setAttribute('hidden', 'true');
|
||||
lessInfoButton.removeAttribute('hidden');
|
||||
};
|
||||
lessInfoButton.onclick = function() {
|
||||
errorMoreInfo.setAttribute('hidden', 'true');
|
||||
moreInfoButton.removeAttribute('hidden');
|
||||
lessInfoButton.setAttribute('hidden', 'true');
|
||||
};
|
||||
moreInfoButton.removeAttribute('hidden');
|
||||
lessInfoButton.setAttribute('hidden', 'true');
|
||||
errorMoreInfo.value = 'PDF.JS Build: ' + PDFJS.build + '\n';
|
||||
|
||||
if (moreInfo) {
|
||||
errorMoreInfo.value += 'Message: ' + moreInfo.message;
|
||||
if (moreInfo.stack)
|
||||
errorMoreInfo.value += '\n' + 'Stack: ' + moreInfo.stack;
|
||||
}
|
||||
errorMoreInfo.rows = errorMoreInfo.value.split('\n').length - 1;
|
||||
},
|
||||
|
||||
progress: function pdfViewProgress(level) {
|
||||
@ -193,6 +366,17 @@ var PDFView = {
|
||||
},
|
||||
|
||||
load: function pdfViewLoad(data, scale) {
|
||||
function bindOnAfterDraw(pageView, thumbnailView) {
|
||||
// when page is painted, using the image as thumbnail base
|
||||
pageView.onAfterDraw = function pdfViewLoadOnAfterDraw() {
|
||||
thumbnailView.setImage(pageView.canvas);
|
||||
preDraw();
|
||||
};
|
||||
}
|
||||
|
||||
var errorWrapper = document.getElementById('errorWrapper');
|
||||
errorWrapper.setAttribute('hidden', 'true');
|
||||
|
||||
var loadingIndicator = document.getElementById('loading');
|
||||
loadingIndicator.setAttribute('hidden', 'true');
|
||||
|
||||
@ -209,28 +393,48 @@ var PDFView = {
|
||||
while (container.hasChildNodes())
|
||||
container.removeChild(container.lastChild);
|
||||
|
||||
var pdf = new PDFJS.PDFDoc(data);
|
||||
var pdf;
|
||||
try {
|
||||
pdf = new PDFJS.PDFDoc(data);
|
||||
} catch (e) {
|
||||
this.error('An error occurred while reading the PDF.', e);
|
||||
}
|
||||
var pagesCount = pdf.numPages;
|
||||
var id = pdf.fingerprint;
|
||||
var storedHash = null;
|
||||
document.getElementById('numPages').innerHTML = 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 pagesRefMap = {};
|
||||
var thumbnails = this.thumbnails = [];
|
||||
for (var i = 1; i <= pagesCount; i++) {
|
||||
var page = pdf.getPage(i);
|
||||
pages.push(new PageView(container, page, i, page.width, page.height,
|
||||
page.stats, this.navigateTo.bind(this)));
|
||||
thumbnails.push(new ThumbnailView(sidebar, page, i,
|
||||
page.width / page.height));
|
||||
var pageView = new PageView(container, page, i, page.width, page.height,
|
||||
page.stats, this.navigateTo.bind(this));
|
||||
var thumbnailView = new ThumbnailView(sidebar, page, i,
|
||||
page.width / page.height);
|
||||
bindOnAfterDraw(pageView, thumbnailView);
|
||||
|
||||
pages.push(pageView);
|
||||
thumbnails.push(thumbnailView);
|
||||
var pageRef = page.ref;
|
||||
pagesRefMap[pageRef.num + ' ' + pageRef.gen + ' R'] = i;
|
||||
}
|
||||
|
||||
this.setScale(scale || kDefaultScale, true);
|
||||
|
||||
this.pagesRefMap = pagesRefMap;
|
||||
this.destinations = pdf.catalog.destinations;
|
||||
|
||||
if (pdf.catalog.documentOutline) {
|
||||
this.outline = new DocumentOutlineView(pdf.catalog.documentOutline);
|
||||
var outlineSwitchButton = document.getElementById('outlineSwitch');
|
||||
@ -238,12 +442,20 @@ var PDFView = {
|
||||
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) {
|
||||
this.setHash(this.initialBookmark);
|
||||
this.initialBookmark = null;
|
||||
}
|
||||
else
|
||||
else if (storedHash)
|
||||
this.setHash(storedHash);
|
||||
else {
|
||||
this.parseScale(scale || kDefaultScale, true);
|
||||
this.page = 1;
|
||||
}
|
||||
},
|
||||
|
||||
setHash: function pdfViewSetHash(hash) {
|
||||
@ -269,8 +481,16 @@ var PDFView = {
|
||||
if ('zoom' in params) {
|
||||
var zoomArgs = params.zoom.split(','); // scale,left,top
|
||||
// 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];
|
||||
currentPage.scrollIntoView(dest);
|
||||
} else
|
||||
@ -294,6 +514,7 @@ var PDFView = {
|
||||
outlineScrollView.setAttribute('hidden', 'true');
|
||||
thumbsSwitchButton.setAttribute('data-selected', true);
|
||||
outlineSwitchButton.removeAttribute('data-selected');
|
||||
updateThumbViewArea();
|
||||
break;
|
||||
case 'outline':
|
||||
thumbsScrollView.setAttribute('hidden', 'true');
|
||||
@ -328,6 +549,34 @@ var PDFView = {
|
||||
currentHeight += singlePage.height * singlePage.scale + kBottomMargin;
|
||||
}
|
||||
return visiblePages;
|
||||
},
|
||||
|
||||
getVisibleThumbs: function pdfViewGetVisibleThumbs() {
|
||||
var thumbs = this.thumbnails;
|
||||
var kBottomMargin = 5;
|
||||
var visibleThumbs = [];
|
||||
|
||||
var view = document.getElementById('sidebarScrollView');
|
||||
var currentHeight = kBottomMargin;
|
||||
var top = view.scrollTop;
|
||||
for (var i = 1; i <= thumbs.length; ++i) {
|
||||
var thumb = thumbs[i - 1];
|
||||
var thumbHeight = thumb.height * thumb.scaleY + kBottomMargin;
|
||||
if (currentHeight + thumbHeight > top)
|
||||
break;
|
||||
|
||||
currentHeight += thumbHeight;
|
||||
}
|
||||
|
||||
var bottom = top + view.clientHeight;
|
||||
for (; i <= thumbs.length && currentHeight < bottom; ++i) {
|
||||
var singleThumb = thumbs[i - 1];
|
||||
visibleThumbs.push({ id: singleThumb.id, y: currentHeight,
|
||||
view: singleThumb });
|
||||
currentHeight += singleThumb.height * singleThumb.scaleY + kBottomMargin;
|
||||
}
|
||||
|
||||
return visibleThumbs;
|
||||
}
|
||||
};
|
||||
|
||||
@ -360,9 +609,11 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
|
||||
while (div.hasChildNodes())
|
||||
div.removeChild(div.lastChild);
|
||||
div.removeAttribute('data-loaded');
|
||||
|
||||
delete this.canvas;
|
||||
};
|
||||
|
||||
function setupLinks(content, scale) {
|
||||
function setupAnnotations(content, scale) {
|
||||
function bindLink(link, dest) {
|
||||
link.href = PDFView.getDestinationHash(dest);
|
||||
link.onclick = function pageViewSetupLinksOnclick() {
|
||||
@ -371,18 +622,67 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
|
||||
return false;
|
||||
};
|
||||
}
|
||||
function createElementWithStyle(tagName, item) {
|
||||
var element = document.createElement(tagName);
|
||||
element.style.left = (Math.floor(item.x - view.x) * scale) + 'px';
|
||||
element.style.top = (Math.floor(item.y - view.y) * scale) + 'px';
|
||||
element.style.width = Math.ceil(item.width * scale) + 'px';
|
||||
element.style.height = Math.ceil(item.height * scale) + 'px';
|
||||
return element;
|
||||
}
|
||||
function createCommentAnnotation(type, item) {
|
||||
var container = document.createElement('section');
|
||||
container.className = 'annotComment';
|
||||
|
||||
var links = content.getLinks();
|
||||
for (var i = 0; i < links.length; i++) {
|
||||
var link = document.createElement('a');
|
||||
link.style.left = (Math.floor(links[i].x - view.x) * scale) + 'px';
|
||||
link.style.top = (Math.floor(links[i].y - view.y) * scale) + 'px';
|
||||
link.style.width = Math.ceil(links[i].width * scale) + 'px';
|
||||
link.style.height = Math.ceil(links[i].height * scale) + 'px';
|
||||
link.href = links[i].url || '';
|
||||
if (!links[i].url)
|
||||
bindLink(link, ('dest' in links[i]) ? links[i].dest : null);
|
||||
div.appendChild(link);
|
||||
var image = createElementWithStyle('img', item);
|
||||
image.src = kImageDirectory + type.toLowerCase() + '.svg';
|
||||
var content = document.createElement('div');
|
||||
content.setAttribute('hidden', true);
|
||||
var title = document.createElement('h1');
|
||||
var text = document.createElement('p');
|
||||
var offsetPos = Math.floor(item.x - view.x + item.width);
|
||||
content.style.left = (offsetPos * scale) + 'px';
|
||||
content.style.top = (Math.floor(item.y - view.y) * scale) + 'px';
|
||||
title.textContent = item.title;
|
||||
|
||||
if (!item.content) {
|
||||
content.setAttribute('hidden', true);
|
||||
} else {
|
||||
text.innerHTML = item.content.replace('\n', '<br />');
|
||||
image.addEventListener('mouseover', function annotationImageOver() {
|
||||
this.nextSibling.removeAttribute('hidden');
|
||||
}, false);
|
||||
|
||||
image.addEventListener('mouseout', function annotationImageOut() {
|
||||
this.nextSibling.setAttribute('hidden', true);
|
||||
}, false);
|
||||
}
|
||||
|
||||
content.appendChild(title);
|
||||
content.appendChild(text);
|
||||
container.appendChild(image);
|
||||
container.appendChild(content);
|
||||
|
||||
return container;
|
||||
}
|
||||
|
||||
var items = content.getAnnotations();
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
var item = items[i];
|
||||
switch (item.type) {
|
||||
case 'Link':
|
||||
var link = createElementWithStyle('a', item);
|
||||
link.href = item.url || '';
|
||||
if (!item.url)
|
||||
bindLink(link, ('dest' in item) ? item.dest : null);
|
||||
div.appendChild(link);
|
||||
break;
|
||||
case 'Text':
|
||||
var comment = createCommentAnnotation(item.name, item);
|
||||
if (comment)
|
||||
div.appendChild(comment);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -441,7 +741,7 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
|
||||
];
|
||||
|
||||
if (scale && scale !== PDFView.currentScale)
|
||||
PDFView.setScale(scale, true);
|
||||
PDFView.parseScale(scale, true);
|
||||
|
||||
setTimeout(function pageViewScrollIntoViewRelayout() {
|
||||
// letting page to re-layout before scrolling
|
||||
@ -464,16 +764,26 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
|
||||
}, 0);
|
||||
};
|
||||
|
||||
this.draw = function pageviewDraw() {
|
||||
if (div.hasChildNodes()) {
|
||||
this.drawingRequired = function() {
|
||||
return !div.hasChildNodes();
|
||||
};
|
||||
|
||||
this.draw = function pageviewDraw(callback) {
|
||||
if (!this.drawingRequired()) {
|
||||
this.updateStats();
|
||||
return false;
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.id = 'page' + this.id;
|
||||
canvas.mozOpaque = true;
|
||||
div.appendChild(canvas);
|
||||
this.canvas = canvas;
|
||||
|
||||
var textLayer = document.createElement('div');
|
||||
textLayer.className = 'textLayer';
|
||||
div.appendChild(textLayer);
|
||||
|
||||
var scale = this.scale;
|
||||
canvas.width = pageWidth * scale;
|
||||
@ -487,12 +797,21 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
|
||||
ctx.translate(-this.x * scale, -this.y * scale);
|
||||
|
||||
stats.begin = Date.now();
|
||||
this.content.startRendering(ctx, this.updateStats);
|
||||
this.content.startRendering(ctx,
|
||||
(function pageViewDrawCallback(error) {
|
||||
if (error)
|
||||
PDFView.error('An error occurred while rendering the page.', error);
|
||||
this.updateStats();
|
||||
if (this.onAfterDraw)
|
||||
this.onAfterDraw();
|
||||
|
||||
setupLinks(this.content, this.scale);
|
||||
cache.push(this);
|
||||
callback();
|
||||
}).bind(this), new TextLayerBuilder(textLayer)
|
||||
);
|
||||
|
||||
setupAnnotations(this.content, this.scale);
|
||||
div.setAttribute('data-loaded', true);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
this.updateStats = function pageViewUpdateStats() {
|
||||
@ -511,6 +830,19 @@ var ThumbnailView = function thumbnailView(container, page, id, pageRatio) {
|
||||
return false;
|
||||
};
|
||||
|
||||
var view = page.view;
|
||||
this.width = view.width;
|
||||
this.height = view.height;
|
||||
this.id = id;
|
||||
|
||||
var maxThumbSize = 134;
|
||||
var canvasWidth = pageRatio >= 1 ? maxThumbSize :
|
||||
maxThumbSize * pageRatio;
|
||||
var canvasHeight = pageRatio <= 1 ? maxThumbSize :
|
||||
maxThumbSize / pageRatio;
|
||||
var scaleX = this.scaleX = (canvasWidth / this.width);
|
||||
var scaleY = this.scaleY = (canvasHeight / this.height);
|
||||
|
||||
var div = document.createElement('div');
|
||||
div.id = 'thumbnailContainer' + id;
|
||||
div.className = 'thumbnail';
|
||||
@ -518,19 +850,15 @@ var ThumbnailView = function thumbnailView(container, page, id, pageRatio) {
|
||||
anchor.appendChild(div);
|
||||
container.appendChild(anchor);
|
||||
|
||||
this.draw = function thumbnailViewDraw() {
|
||||
if (div.hasChildNodes())
|
||||
return;
|
||||
this.hasImage = false;
|
||||
|
||||
function getPageDrawContext() {
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.id = 'thumbnail' + id;
|
||||
canvas.mozOpaque = true;
|
||||
|
||||
var maxThumbSize = 134;
|
||||
canvas.width = pageRatio >= 1 ? maxThumbSize :
|
||||
maxThumbSize * pageRatio;
|
||||
canvas.height = pageRatio <= 1 ? maxThumbSize :
|
||||
maxThumbSize / pageRatio;
|
||||
canvas.width = canvasWidth;
|
||||
canvas.height = canvasHeight;
|
||||
|
||||
div.setAttribute('data-loaded', true);
|
||||
div.appendChild(canvas);
|
||||
@ -542,14 +870,37 @@ var ThumbnailView = function thumbnailView(container, page, id, pageRatio) {
|
||||
ctx.restore();
|
||||
|
||||
var view = page.view;
|
||||
var scaleX = (canvas.width / page.width);
|
||||
var scaleY = (canvas.height / page.height);
|
||||
ctx.translate(-view.x * scaleX, -view.y * scaleY);
|
||||
div.style.width = (view.width * scaleX) + 'px';
|
||||
div.style.height = (view.height * scaleY) + 'px';
|
||||
div.style.lineHeight = (view.height * scaleY) + 'px';
|
||||
|
||||
page.startRendering(ctx, function thumbnailViewDrawStartRendering() {});
|
||||
return ctx;
|
||||
}
|
||||
|
||||
this.draw = function thumbnailViewDraw(callback) {
|
||||
if (this.hasImage) {
|
||||
callback();
|
||||
return;
|
||||
}
|
||||
|
||||
var ctx = getPageDrawContext();
|
||||
page.startRendering(ctx, function thumbnailViewDrawStartRendering() {
|
||||
callback();
|
||||
});
|
||||
|
||||
this.hasImage = true;
|
||||
};
|
||||
|
||||
this.setImage = function thumbnailViewSetImage(img) {
|
||||
if (this.hasImage)
|
||||
return;
|
||||
|
||||
var ctx = getPageDrawContext();
|
||||
ctx.drawImage(img, 0, 0, img.width, img.height,
|
||||
0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||
|
||||
this.hasImage = true;
|
||||
};
|
||||
};
|
||||
|
||||
@ -589,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) {
|
||||
var params = document.location.search.substring(1).split('&');
|
||||
for (var i = 0; i < params.length; i++) {
|
||||
@ -596,44 +994,95 @@ window.addEventListener('load', function webViewerLoad(evt) {
|
||||
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));
|
||||
|
||||
if (!window.File || !window.FileReader || !window.FileList || !window.Blob)
|
||||
document.getElementById('fileInput').setAttribute('hidden', 'true');
|
||||
else
|
||||
document.getElementById('fileInput').value = null;
|
||||
|
||||
if ('disableWorker' in params)
|
||||
PDFJS.disableWorker = params['disableWorker'] === 'true' ? true : false;
|
||||
|
||||
var sidebarScrollView = document.getElementById('sidebarScrollView');
|
||||
sidebarScrollView.addEventListener('scroll', updateThumbViewArea, true);
|
||||
}, true);
|
||||
|
||||
window.addEventListener('unload', function webViewerUnload(evt) {
|
||||
window.scrollTo(0, 0);
|
||||
}, 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() {
|
||||
var visiblePages = PDFView.getVisiblePages();
|
||||
var pageToDraw;
|
||||
for (var i = 0; i < visiblePages.length; i++) {
|
||||
var page = visiblePages[i];
|
||||
if (PDFView.pages[page.id - 1].draw())
|
||||
cache.push(page.view);
|
||||
var pageObj = PDFView.pages[page.id - 1];
|
||||
|
||||
pageToDraw |= pageObj.drawingRequired();
|
||||
renderingQueue.enqueueDraw(pageObj);
|
||||
}
|
||||
|
||||
if (!visiblePages.length)
|
||||
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"
|
||||
var currentId = PDFView.page;
|
||||
var firstPage = visiblePages[0];
|
||||
PDFView.page = firstPage.id;
|
||||
updateViewarea.inProgress = false;
|
||||
|
||||
var currentScale = PDFView.currentScale;
|
||||
var currentScaleValue = PDFView.currentScaleValue;
|
||||
var normalizedScaleValue = currentScaleValue == currentScale ?
|
||||
currentScale * 100 : currentScaleValue;
|
||||
|
||||
var kViewerTopMargin = 52;
|
||||
var pageNumber = firstPage.id;
|
||||
var pdfOpenParams = '#page=' + pageNumber;
|
||||
pdfOpenParams += '&zoom=' + Math.round(PDFView.currentScale * 100);
|
||||
pdfOpenParams += '&zoom=' + normalizedScaleValue;
|
||||
var currentPage = PDFView.pages[pageNumber - 1];
|
||||
var topLeft = currentPage.getPagePoint(window.pageXOffset,
|
||||
window.pageYOffset - firstPage.y - kViewerTopMargin);
|
||||
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;
|
||||
}
|
||||
|
||||
@ -641,9 +1090,33 @@ window.addEventListener('scroll', function webViewerScroll(evt) {
|
||||
updateViewarea();
|
||||
}, true);
|
||||
|
||||
|
||||
var thumbnailTimer;
|
||||
|
||||
function updateThumbViewArea() {
|
||||
// Only render thumbs after pausing scrolling for this amount of time
|
||||
// (makes UI more responsive)
|
||||
var delay = 50; // in ms
|
||||
|
||||
if (thumbnailTimer)
|
||||
clearTimeout(thumbnailTimer);
|
||||
|
||||
thumbnailTimer = setTimeout(function() {
|
||||
var visibleThumbs = PDFView.getVisibleThumbs();
|
||||
for (var i = 0; i < visibleThumbs.length; i++) {
|
||||
var thumb = visibleThumbs[i];
|
||||
renderingQueue.enqueueDraw(PDFView.thumbnails[thumb.id - 1]);
|
||||
}
|
||||
}, delay);
|
||||
}
|
||||
|
||||
window.addEventListener('transitionend', updateThumbViewArea, true);
|
||||
window.addEventListener('webkitTransitionEnd', updateThumbViewArea, true);
|
||||
|
||||
window.addEventListener('resize', function webViewerResize(evt) {
|
||||
if (document.getElementById('pageWidthOption').selected ||
|
||||
document.getElementById('pageFitOption').selected)
|
||||
document.getElementById('pageFitOption').selected ||
|
||||
document.getElementById('pageAutoOption').selected)
|
||||
PDFView.parseScale(document.getElementById('scaleSelect').value);
|
||||
updateViewarea();
|
||||
});
|
||||
@ -673,7 +1146,6 @@ window.addEventListener('change', function webViewerChange(evt) {
|
||||
// implemented in Firefox.
|
||||
var file = files[0];
|
||||
fileReader.readAsBinaryString(file);
|
||||
|
||||
document.title = file.name;
|
||||
|
||||
// URL does not reflect proper document location - hiding some icons.
|
||||
@ -681,35 +1153,9 @@ window.addEventListener('change', function webViewerChange(evt) {
|
||||
document.getElementById('download').setAttribute('hidden', 'true');
|
||||
}, true);
|
||||
|
||||
window.addEventListener('transitionend', function webViewerTransitionend(evt) {
|
||||
var pageIndex = 0;
|
||||
var pagesCount = PDFView.pages.length;
|
||||
|
||||
var container = document.getElementById('sidebarView');
|
||||
container._interval = window.setInterval(function interval() {
|
||||
if (pageIndex >= pagesCount) {
|
||||
window.clearInterval(container._interval);
|
||||
return;
|
||||
}
|
||||
|
||||
PDFView.thumbnails[pageIndex++].draw();
|
||||
}, 500);
|
||||
}, true);
|
||||
|
||||
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)) {
|
||||
updateViewarea();
|
||||
return;
|
||||
}
|
||||
|
||||
function selectScaleOption(value) {
|
||||
var options = document.getElementById('scaleSelect').options;
|
||||
var predefinedValueFound = false;
|
||||
var value = '' + evt.scale;
|
||||
for (var i = 0; i < options.length; i++) {
|
||||
var option = options[i];
|
||||
if (option.value != value) {
|
||||
@ -719,7 +1165,22 @@ window.addEventListener('scalechange', function scalechange(evt) {
|
||||
option.selected = 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) {
|
||||
customScaleOption.textContent = Math.round(evt.scale * 10000) / 100 + '%';
|
||||
customScaleOption.selected = true;
|
||||
@ -737,25 +1198,49 @@ window.addEventListener('pagechange', function pagechange(evt) {
|
||||
}, true);
|
||||
|
||||
window.addEventListener('keydown', function keydown(evt) {
|
||||
if (evt.ctrlKey || evt.altKey || evt.shiftKey || evt.metaKey)
|
||||
return;
|
||||
var curElement = document.activeElement;
|
||||
if (curElement && curElement.tagName == 'INPUT')
|
||||
return;
|
||||
var controlsElement = document.getElementById('controls');
|
||||
while (curElement) {
|
||||
if (curElement === controlsElement)
|
||||
return; // ignoring if the 'controls' element is focused
|
||||
curElement = curElement.parentNode;
|
||||
}
|
||||
var handled = false;
|
||||
switch (evt.keyCode) {
|
||||
case 61: // FF/Mac '='
|
||||
case 107: // FF '+' and '='
|
||||
case 187: // Chrome '+'
|
||||
PDFView.zoomIn();
|
||||
handled = true;
|
||||
break;
|
||||
case 109: // FF '-'
|
||||
case 189: // Chrome '-'
|
||||
PDFView.zoomOut();
|
||||
handled = true;
|
||||
break;
|
||||
case 48: // '0'
|
||||
PDFView.setScale(kDefaultScale, true);
|
||||
handled = true;
|
||||
break;
|
||||
case 37: // left arrow
|
||||
case 75: // 'k'
|
||||
case 80: // 'p'
|
||||
PDFView.page--;
|
||||
handled = true;
|
||||
break;
|
||||
case 39: // right arrow
|
||||
case 74: // 'j'
|
||||
case 78: // 'n'
|
||||
PDFView.page++;
|
||||
handled = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (handled) {
|
||||
evt.preventDefault();
|
||||
}
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user