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
|
Yury Delendik
|
||||||
Kalervo Kujala
|
Kalervo Kujala
|
||||||
Adil Allawi <@ironymark>
|
Adil Allawi <@ironymark>
|
||||||
|
Jakob Miland <saebekassebil@gmail.com>
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a
|
Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
copy of this software and associated documentation files (the "Software"),
|
copy of this software and associated documentation files (the "Software"),
|
||||||
|
7
Makefile
7
Makefile
@ -68,7 +68,8 @@ bundle: | $(BUILD_DIR)
|
|||||||
@cd src; \
|
@cd src; \
|
||||||
cat $(PDF_JS_FILES) > all_files.tmp; \
|
cat $(PDF_JS_FILES) > all_files.tmp; \
|
||||||
sed '/PDFJSSCRIPT_INCLUDE_ALL/ r all_files.tmp' pdf.js > ../$(BUILD_TARGET); \
|
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; \
|
rm -f *.tmp; \
|
||||||
cd ..
|
cd ..
|
||||||
|
|
||||||
@ -138,8 +139,8 @@ browser-test:
|
|||||||
# To install gjslint, see:
|
# To install gjslint, see:
|
||||||
#
|
#
|
||||||
# <http://code.google.com/closure/utilities/docs/linter_howto.html>
|
# <http://code.google.com/closure/utilities/docs/linter_howto.html>
|
||||||
SRC_DIRS := . src utils web test test/unit examples/helloworld \
|
SRC_DIRS := . src utils web test examples/helloworld extensions/firefox \
|
||||||
extensions/firefox extensions/firefox/components extensions/chrome
|
extensions/firefox/components extensions/chrome test/unit
|
||||||
GJSLINT_FILES = $(foreach DIR,$(SRC_DIRS),$(wildcard $(DIR)/*.js))
|
GJSLINT_FILES = $(foreach DIR,$(SRC_DIRS),$(wildcard $(DIR)/*.js))
|
||||||
lint:
|
lint:
|
||||||
gjslint --nojsdoc $(GJSLINT_FILES)
|
gjslint --nojsdoc $(GJSLINT_FILES)
|
||||||
|
@ -205,3 +205,4 @@ a "PDF Reference" from Adobe:
|
|||||||
|
|
||||||
Recommended chapters to read: "2. Overview", "3.4 File Structure",
|
Recommended chapters to read: "2. Overview", "3.4 File Structure",
|
||||||
"4.1 Graphics Objects" that lists the PDF commands.
|
"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">
|
<Description about="urn:mozilla:install-manifest">
|
||||||
<em:id>uriloader@pdf.js</em:id>
|
<em:id>uriloader@pdf.js</em:id>
|
||||||
<em:name>pdf.js</em:name>
|
<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:iconURL>chrome://pdf.js/skin/logo.png</em:iconURL>
|
||||||
<em:targetApplication>
|
<em:targetApplication>
|
||||||
<Description>
|
<Description>
|
||||||
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
<em:id>{ec8030f7-c20a-464f-9b0e-13a3a9e97384}</em:id>
|
||||||
<em:minVersion>6.0</em:minVersion>
|
<em:minVersion>6.0</em:minVersion>
|
||||||
<em:maxVersion>11.0.*</em:maxVersion>
|
<em:maxVersion>11.0a1</em:maxVersion>
|
||||||
</Description>
|
</Description>
|
||||||
</em:targetApplication>
|
</em:targetApplication>
|
||||||
<em:bootstrap>true</em:bootstrap>
|
<em:bootstrap>true</em:bootstrap>
|
||||||
@ -20,5 +20,6 @@
|
|||||||
<em:creator>Vivien Nicolas</em:creator>
|
<em:creator>Vivien Nicolas</em:creator>
|
||||||
<em:description>pdf.js uri loader</em:description>
|
<em:description>pdf.js uri loader</em:description>
|
||||||
<em:homepageURL>https://github.com/mozilla/pdf.js/</em:homepageURL>
|
<em:homepageURL>https://github.com/mozilla/pdf.js/</em:homepageURL>
|
||||||
|
<em:type>2</em:type>
|
||||||
</Description>
|
</Description>
|
||||||
</RDF>
|
</RDF>
|
||||||
|
463
src/canvas.js
463
src/canvas.js
@ -6,8 +6,19 @@
|
|||||||
// <canvas> contexts store most of the state we need natively.
|
// <canvas> contexts store most of the state we need natively.
|
||||||
// However, PDF needs a bit more state, which we store here.
|
// However, PDF needs a bit more state, which we store here.
|
||||||
|
|
||||||
var CanvasExtraState = (function canvasExtraState() {
|
var TextRenderingMode = {
|
||||||
function constructor(old) {
|
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?
|
// Are soft masks and alpha values shapes or opacities?
|
||||||
this.alphaIsShape = false;
|
this.alphaIsShape = false;
|
||||||
this.fontSize = 0;
|
this.fontSize = 0;
|
||||||
@ -23,6 +34,7 @@ var CanvasExtraState = (function canvasExtraState() {
|
|||||||
this.charSpacing = 0;
|
this.charSpacing = 0;
|
||||||
this.wordSpacing = 0;
|
this.wordSpacing = 0;
|
||||||
this.textHScale = 1;
|
this.textHScale = 1;
|
||||||
|
this.textRenderingMode = TextRenderingMode.FILL;
|
||||||
// Color spaces
|
// Color spaces
|
||||||
this.fillColorSpace = new DeviceGrayCS();
|
this.fillColorSpace = new DeviceGrayCS();
|
||||||
this.fillColorSpaceObj = null;
|
this.fillColorSpaceObj = null;
|
||||||
@ -40,7 +52,7 @@ var CanvasExtraState = (function canvasExtraState() {
|
|||||||
this.old = old;
|
this.old = old;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor.prototype = {
|
CanvasExtraState.prototype = {
|
||||||
clone: function canvasextra_clone() {
|
clone: function canvasextra_clone() {
|
||||||
return Object.create(this);
|
return Object.create(this);
|
||||||
},
|
},
|
||||||
@ -49,7 +61,7 @@ var CanvasExtraState = (function canvasExtraState() {
|
|||||||
this.y = y;
|
this.y = y;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return constructor;
|
return CanvasExtraState;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
function ScratchCanvas(width, height) {
|
function ScratchCanvas(width, height) {
|
||||||
@ -59,16 +71,122 @@ function ScratchCanvas(width, height) {
|
|||||||
return canvas;
|
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
|
// Defines the time the executeIRQueue is going to be executing
|
||||||
// before it stops and shedules a continue of execution.
|
// before it stops and shedules a continue of execution.
|
||||||
var kExecutionTime = 50;
|
var kExecutionTime = 50;
|
||||||
|
|
||||||
// Number of IR commands to execute before checking
|
function CanvasGraphics(canvasCtx, objs, textLayer) {
|
||||||
// if we execute longer then `kExecutionTime`.
|
|
||||||
var kExecutionTimeCheck = 500;
|
|
||||||
|
|
||||||
function constructor(canvasCtx, objs) {
|
|
||||||
this.ctx = canvasCtx;
|
this.ctx = canvasCtx;
|
||||||
this.current = new CanvasExtraState();
|
this.current = new CanvasExtraState();
|
||||||
this.stateStack = [];
|
this.stateStack = [];
|
||||||
@ -77,6 +195,10 @@ var CanvasGraphics = (function canvasGraphics() {
|
|||||||
this.xobjs = null;
|
this.xobjs = null;
|
||||||
this.ScratchCanvas = ScratchCanvas;
|
this.ScratchCanvas = ScratchCanvas;
|
||||||
this.objs = objs;
|
this.objs = objs;
|
||||||
|
this.textLayer = textLayer;
|
||||||
|
if (canvasCtx) {
|
||||||
|
addContextCurrentTransform(canvasCtx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var LINE_CAP_STYLES = ['butt', 'round', 'square'];
|
var LINE_CAP_STYLES = ['butt', 'round', 'square'];
|
||||||
@ -84,7 +206,36 @@ var CanvasGraphics = (function canvasGraphics() {
|
|||||||
var NORMAL_CLIP = {};
|
var NORMAL_CLIP = {};
|
||||||
var EO_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) {
|
beginDrawing: function canvasGraphicsBeginDrawing(mediaBox) {
|
||||||
var cw = this.ctx.canvas.width, ch = this.ctx.canvas.height;
|
var cw = this.ctx.canvas.width, ch = this.ctx.canvas.height;
|
||||||
this.ctx.save();
|
this.ctx.save();
|
||||||
@ -102,7 +253,13 @@ var CanvasGraphics = (function canvasGraphics() {
|
|||||||
this.ctx.transform(0, -1, -1, 0, cw, ch);
|
this.ctx.transform(0, -1, -1, 0, cw, ch);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// Scale so that canvas units are the same as PDF user space units
|
||||||
this.ctx.scale(cw / mediaBox.width, ch / mediaBox.height);
|
this.ctx.scale(cw / mediaBox.width, ch / mediaBox.height);
|
||||||
|
// 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,
|
executeIRQueue: function canvasGraphicsExecuteIRQueue(codeIR,
|
||||||
@ -112,32 +269,39 @@ var CanvasGraphics = (function canvasGraphics() {
|
|||||||
var i = executionStartIdx || 0;
|
var i = executionStartIdx || 0;
|
||||||
var argsArrayLen = argsArray.length;
|
var argsArrayLen = argsArray.length;
|
||||||
|
|
||||||
|
// Sometimes the IRQueue to execute is empty.
|
||||||
|
if (argsArrayLen == i) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
var executionEndIdx;
|
var executionEndIdx;
|
||||||
var startTime = Date.now();
|
var endTime = Date.now() + kExecutionTime;
|
||||||
|
|
||||||
var objs = this.objs;
|
var objs = this.objs;
|
||||||
|
var fnName;
|
||||||
|
var slowCommands = this.slowCommands;
|
||||||
|
|
||||||
do {
|
while (true) {
|
||||||
executionEndIdx = Math.min(argsArrayLen, i + kExecutionTimeCheck);
|
fnName = fnArray[i];
|
||||||
|
|
||||||
for (i; i < executionEndIdx; i++) {
|
if (fnName !== 'dependency') {
|
||||||
if (fnArray[i] !== 'dependency') {
|
this[fnName].apply(this, argsArray[i]);
|
||||||
this[fnArray[i]].apply(this, argsArray[i]);
|
} else {
|
||||||
} else {
|
var deps = argsArray[i];
|
||||||
var deps = argsArray[i];
|
for (var n = 0, nn = deps.length; n < nn; n++) {
|
||||||
for (var n = 0, nn = deps.length; n < nn; n++) {
|
var depObjId = deps[n];
|
||||||
var depObjId = deps[n];
|
|
||||||
|
|
||||||
// If the promise isn't resolved yet, add the continueCallback
|
// If the promise isn't resolved yet, add the continueCallback
|
||||||
// to the promise and bail out.
|
// to the promise and bail out.
|
||||||
if (!objs.isResolved(depObjId)) {
|
if (!objs.isResolved(depObjId)) {
|
||||||
objs.get(depObjId, continueCallback);
|
objs.get(depObjId, continueCallback);
|
||||||
return i;
|
return i;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
i++;
|
||||||
|
|
||||||
// If the entire IRQueue was executed, stop as were done.
|
// If the entire IRQueue was executed, stop as were done.
|
||||||
if (i == argsArrayLen) {
|
if (i == argsArrayLen) {
|
||||||
return i;
|
return i;
|
||||||
@ -146,18 +310,21 @@ var CanvasGraphics = (function canvasGraphics() {
|
|||||||
// If the execution took longer then a certain amount of time, shedule
|
// If the execution took longer then a certain amount of time, shedule
|
||||||
// to continue exeution after a short delay.
|
// to continue exeution after a short delay.
|
||||||
// However, this is only possible if a 'continueCallback' is passed in.
|
// 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);
|
setTimeout(continueCallback, 0);
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the IRQueue isn't executed completly yet OR the execution time
|
// If the IRQueue isn't executed completly yet OR the execution time
|
||||||
// was short enough, do another execution round.
|
// was short enough, do another execution round.
|
||||||
} while (true);
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
endDrawing: function canvasGraphicsEndDrawing() {
|
endDrawing: function canvasGraphicsEndDrawing() {
|
||||||
this.ctx.restore();
|
this.ctx.restore();
|
||||||
|
|
||||||
|
if (this.textLayer)
|
||||||
|
this.textLayer.endLayout();
|
||||||
},
|
},
|
||||||
|
|
||||||
// Graphics state
|
// Graphics state
|
||||||
@ -176,6 +343,8 @@ var CanvasGraphics = (function canvasGraphics() {
|
|||||||
setDash: function canvasGraphicsSetDash(dashArray, dashPhase) {
|
setDash: function canvasGraphicsSetDash(dashArray, dashPhase) {
|
||||||
this.ctx.mozDash = dashArray;
|
this.ctx.mozDash = dashArray;
|
||||||
this.ctx.mozDashOffset = dashPhase;
|
this.ctx.mozDashOffset = dashPhase;
|
||||||
|
this.ctx.webkitLineDash = dashArray;
|
||||||
|
this.ctx.webkitLineDashOffset = dashPhase;
|
||||||
},
|
},
|
||||||
setRenderingIntent: function canvasGraphicsSetRenderingIntent(intent) {
|
setRenderingIntent: function canvasGraphicsSetRenderingIntent(intent) {
|
||||||
TODO('set rendering intent: ' + intent);
|
TODO('set rendering intent: ' + intent);
|
||||||
@ -394,7 +563,9 @@ var CanvasGraphics = (function canvasGraphics() {
|
|||||||
this.ctx.font = rule;
|
this.ctx.font = rule;
|
||||||
},
|
},
|
||||||
setTextRenderingMode: function canvasGraphicsSetTextRenderingMode(mode) {
|
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) {
|
setTextRise: function canvasGraphicsSetTextRise(rise) {
|
||||||
TODO('text rise: ' + rise);
|
TODO('text rise: ' + rise);
|
||||||
@ -416,23 +587,67 @@ var CanvasGraphics = (function canvasGraphics() {
|
|||||||
nextLine: function canvasGraphicsNextLine() {
|
nextLine: function canvasGraphicsNextLine() {
|
||||||
this.moveText(0, this.current.leading);
|
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 ctx = this.ctx;
|
||||||
var current = this.current;
|
var current = this.current;
|
||||||
var font = current.font;
|
var font = current.font;
|
||||||
var glyphs = font.charsToGlyphs(text);
|
var glyphs = font.charsToGlyphs(str);
|
||||||
var fontSize = current.fontSize;
|
var fontSize = current.fontSize;
|
||||||
var charSpacing = current.charSpacing;
|
var charSpacing = current.charSpacing;
|
||||||
var wordSpacing = current.wordSpacing;
|
var wordSpacing = current.wordSpacing;
|
||||||
var textHScale = current.textHScale;
|
var textHScale = current.textHScale;
|
||||||
|
var fontMatrix = font.fontMatrix || IDENTITY_MATRIX;
|
||||||
|
var textHScale2 = textHScale * fontMatrix[0];
|
||||||
var glyphsLength = glyphs.length;
|
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) {
|
if (font.coded) {
|
||||||
ctx.save();
|
ctx.save();
|
||||||
ctx.transform.apply(ctx, current.textMatrix);
|
ctx.transform.apply(ctx, current.textMatrix);
|
||||||
ctx.translate(current.x, current.y);
|
ctx.translate(current.x, current.y);
|
||||||
|
|
||||||
var fontMatrix = font.fontMatrix || IDENTITY_MATRIX;
|
ctx.scale(textHScale, 1);
|
||||||
ctx.scale(1 / 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) {
|
for (var i = 0; i < glyphsLength; ++i) {
|
||||||
|
|
||||||
var glyph = glyphs[i];
|
var glyph = glyphs[i];
|
||||||
@ -452,18 +667,20 @@ var CanvasGraphics = (function canvasGraphics() {
|
|||||||
var width = transformed[0] * fontSize + charSpacing;
|
var width = transformed[0] * fontSize + charSpacing;
|
||||||
|
|
||||||
ctx.translate(width, 0);
|
ctx.translate(width, 0);
|
||||||
current.x += width;
|
current.x += width * textHScale;
|
||||||
|
|
||||||
|
text.str += glyph.unicode;
|
||||||
|
text.length++;
|
||||||
|
text.canvasWidth += width;
|
||||||
}
|
}
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
} else {
|
} else {
|
||||||
ctx.save();
|
ctx.save();
|
||||||
ctx.transform.apply(ctx, current.textMatrix);
|
this.applyTextTransforms();
|
||||||
ctx.scale(1, -1);
|
ctx.lineWidth /= current.textMatrix[0] * fontMatrix[0];
|
||||||
ctx.translate(current.x, -1 * current.y);
|
|
||||||
ctx.transform.apply(ctx, font.fontMatrix || IDENTITY_MATRIX);
|
|
||||||
|
|
||||||
ctx.scale(1 / textHScale, 1);
|
if (textSelection)
|
||||||
|
text.geom = this.getTextGeometry();
|
||||||
|
|
||||||
var width = 0;
|
var width = 0;
|
||||||
for (var i = 0; i < glyphsLength; ++i) {
|
for (var i = 0; i < glyphsLength; ++i) {
|
||||||
@ -474,36 +691,106 @@ var CanvasGraphics = (function canvasGraphics() {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
var unicode = glyph.unicode;
|
var char = glyph.fontChar;
|
||||||
var char = (unicode >= 0x10000) ?
|
var charWidth = glyph.width * fontSize * 0.001 + charSpacing;
|
||||||
String.fromCharCode(0xD800 | ((unicode - 0x10000) >> 10),
|
|
||||||
0xDC00 | (unicode & 0x3FF)) : String.fromCharCode(unicode);
|
|
||||||
|
|
||||||
ctx.fillText(char, width, 0);
|
switch (textRenderingMode) {
|
||||||
width += glyph.width * fontSize * 0.001 + charSpacing;
|
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();
|
ctx.restore();
|
||||||
}
|
}
|
||||||
},
|
|
||||||
|
|
||||||
|
if (textSelection)
|
||||||
|
this.textLayer.appendText(text, font.loadedName, fontSize);
|
||||||
|
|
||||||
|
return text;
|
||||||
|
},
|
||||||
showSpacedText: function canvasGraphicsShowSpacedText(arr) {
|
showSpacedText: function canvasGraphicsShowSpacedText(arr) {
|
||||||
var ctx = this.ctx;
|
var ctx = this.ctx;
|
||||||
var current = this.current;
|
var current = this.current;
|
||||||
|
var font = current.font;
|
||||||
var fontSize = current.fontSize;
|
var fontSize = current.fontSize;
|
||||||
var textHScale = current.textHScale;
|
var textHScale = current.textHScale;
|
||||||
|
if (!font.coded)
|
||||||
|
textHScale *= (font.fontMatrix || IDENTITY_MATRIX)[0];
|
||||||
var arrLength = arr.length;
|
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) {
|
for (var i = 0; i < arrLength; ++i) {
|
||||||
var e = arr[i];
|
var e = arr[i];
|
||||||
if (isNum(e)) {
|
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)) {
|
} 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 {
|
} else {
|
||||||
malformed('TJ array element ' + e + ' is not string or num');
|
malformed('TJ array element ' + e + ' is not string or num');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (textSelection)
|
||||||
|
this.textLayer.appendText(text, font.loadedName, fontSize);
|
||||||
},
|
},
|
||||||
nextLineShowText: function canvasGraphicsNextLineShowText(text) {
|
nextLineShowText: function canvasGraphicsNextLineShowText(text) {
|
||||||
this.nextLine();
|
this.nextLine();
|
||||||
@ -658,9 +945,9 @@ var CanvasGraphics = (function canvasGraphics() {
|
|||||||
var height = canvas.height;
|
var height = canvas.height;
|
||||||
|
|
||||||
var bl = Util.applyTransform([0, 0], inv);
|
var bl = Util.applyTransform([0, 0], inv);
|
||||||
var br = Util.applyTransform([0, width], inv);
|
var br = Util.applyTransform([0, height], inv);
|
||||||
var ul = Util.applyTransform([height, 0], inv);
|
var ul = Util.applyTransform([width, 0], inv);
|
||||||
var ur = Util.applyTransform([height, width], inv);
|
var ur = Util.applyTransform([width, height], inv);
|
||||||
|
|
||||||
var x0 = Math.min(bl[0], br[0], ul[0], ur[0]);
|
var x0 = Math.min(bl[0], br[0], ul[0], ur[0]);
|
||||||
var y0 = Math.min(bl[1], br[1], ul[1], ur[1]);
|
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) {
|
paintJpegXObject: function canvasGraphicsPaintJpegXObject(objId, w, h) {
|
||||||
var image = this.objs.get(objId);
|
var domImage = this.objs.get(objId);
|
||||||
if (!image) {
|
if (!domImage) {
|
||||||
error('Dependent image isn\'t ready yet');
|
error('Dependent image isn\'t ready yet');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -721,7 +1008,6 @@ var CanvasGraphics = (function canvasGraphics() {
|
|||||||
// scale the image to the unit square
|
// scale the image to the unit square
|
||||||
ctx.scale(1 / w, -1 / h);
|
ctx.scale(1 / w, -1 / h);
|
||||||
|
|
||||||
var domImage = image.getImage();
|
|
||||||
ctx.drawImage(domImage, 0, 0, domImage.width, domImage.height,
|
ctx.drawImage(domImage, 0, 0, domImage.width, domImage.height,
|
||||||
0, -h, w, h);
|
0, -h, w, h);
|
||||||
|
|
||||||
@ -777,7 +1063,11 @@ var CanvasGraphics = (function canvasGraphics() {
|
|||||||
this.restore();
|
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();
|
this.save();
|
||||||
var ctx = this.ctx;
|
var ctx = this.ctx;
|
||||||
var w = imgData.width;
|
var w = imgData.width;
|
||||||
@ -787,26 +1077,16 @@ var CanvasGraphics = (function canvasGraphics() {
|
|||||||
|
|
||||||
var tmpCanvas = new this.ScratchCanvas(w, h);
|
var tmpCanvas = new this.ScratchCanvas(w, h);
|
||||||
var tmpCtx = tmpCanvas.getContext('2d');
|
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);
|
ctx.drawImage(tmpCanvas, 0, -h);
|
||||||
this.restore();
|
this.restore();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
putBinaryImageData: function canvasPutBinaryImageData() {
|
||||||
|
//
|
||||||
|
},
|
||||||
|
|
||||||
// Marked content
|
// Marked content
|
||||||
|
|
||||||
markPoint: function canvasGraphicsMarkPoint(tag) {
|
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';
|
'use strict';
|
||||||
|
|
||||||
var ColorSpace = (function colorSpaceColorSpace() {
|
var ColorSpace = (function ColorSpaceClosure() {
|
||||||
// Constructor should define this.numComps, this.defaultColor, this.name
|
// Constructor should define this.numComps, this.defaultColor, this.name
|
||||||
function constructor() {
|
function ColorSpace() {
|
||||||
error('should not call ColorSpace constructor');
|
error('should not call ColorSpace constructor');
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor.prototype = {
|
ColorSpace.prototype = {
|
||||||
// Input: array of size numComps representing color component values
|
// Input: array of size numComps representing color component values
|
||||||
// Output: array of rgb values, each value ranging from [0.1]
|
// Output: array of rgb values, each value ranging from [0.1]
|
||||||
getRgb: function colorSpaceGetRgb(color) {
|
getRgb: function colorSpaceGetRgb(color) {
|
||||||
@ -22,15 +22,15 @@ var ColorSpace = (function colorSpaceColorSpace() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor.parse = function colorSpaceParse(cs, xref, res) {
|
ColorSpace.parse = function colorSpaceParse(cs, xref, res) {
|
||||||
var IR = constructor.parseToIR(cs, xref, res);
|
var IR = ColorSpace.parseToIR(cs, xref, res);
|
||||||
if (IR instanceof AlternateCS)
|
if (IR instanceof AlternateCS)
|
||||||
return IR;
|
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;
|
var name = isArray(IR) ? IR[0] : IR;
|
||||||
|
|
||||||
switch (name) {
|
switch (name) {
|
||||||
@ -63,7 +63,7 @@ var ColorSpace = (function colorSpaceColorSpace() {
|
|||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor.parseToIR = function colorSpaceParseToIR(cs, xref, res) {
|
ColorSpace.parseToIR = function colorSpaceParseToIR(cs, xref, res) {
|
||||||
if (isName(cs)) {
|
if (isName(cs)) {
|
||||||
var colorSpaces = xref.fetchIfRef(res.get('ColorSpace'));
|
var colorSpaces = xref.fetchIfRef(res.get('ColorSpace'));
|
||||||
if (isDict(colorSpaces)) {
|
if (isDict(colorSpaces)) {
|
||||||
@ -154,8 +154,31 @@ var ColorSpace = (function colorSpaceColorSpace() {
|
|||||||
}
|
}
|
||||||
return null;
|
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
|
* Both color spaces use a tinting function to convert colors to a base color
|
||||||
* space.
|
* space.
|
||||||
*/
|
*/
|
||||||
var AlternateCS = (function alternateCS() {
|
var AlternateCS = (function AlternateCSClosure() {
|
||||||
function constructor(numComps, base, tintFn) {
|
function AlternateCS(numComps, base, tintFn) {
|
||||||
this.name = 'Alternate';
|
this.name = 'Alternate';
|
||||||
this.numComps = numComps;
|
this.numComps = numComps;
|
||||||
this.defaultColor = [];
|
this.defaultColor = [];
|
||||||
@ -175,7 +198,7 @@ var AlternateCS = (function alternateCS() {
|
|||||||
this.tintFn = tintFn;
|
this.tintFn = tintFn;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor.prototype = {
|
AlternateCS.prototype = {
|
||||||
getRgb: function altcs_getRgb(color) {
|
getRgb: function altcs_getRgb(color) {
|
||||||
var tinted = this.tintFn(color);
|
var tinted = this.tintFn(color);
|
||||||
return this.base.getRgb(tinted);
|
return this.base.getRgb(tinted);
|
||||||
@ -200,24 +223,27 @@ var AlternateCS = (function alternateCS() {
|
|||||||
baseBuf[pos++] = 255 * tinted[j];
|
baseBuf[pos++] = 255 * tinted[j];
|
||||||
}
|
}
|
||||||
return base.getRgbBuffer(baseBuf, 8);
|
return base.getRgbBuffer(baseBuf, 8);
|
||||||
|
},
|
||||||
|
isDefaultDecode: function altcs_isDefaultDecode(decodeMap) {
|
||||||
|
return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return constructor;
|
return AlternateCS;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
var PatternCS = (function patternCS() {
|
var PatternCS = (function PatternCSClosure() {
|
||||||
function constructor(baseCS) {
|
function PatternCS(baseCS) {
|
||||||
this.name = 'Pattern';
|
this.name = 'Pattern';
|
||||||
this.base = baseCS;
|
this.base = baseCS;
|
||||||
}
|
}
|
||||||
constructor.prototype = {};
|
PatternCS.prototype = {};
|
||||||
|
|
||||||
return constructor;
|
return PatternCS;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
var IndexedCS = (function indexedCS() {
|
var IndexedCS = (function IndexedCSClosure() {
|
||||||
function constructor(base, highVal, lookup) {
|
function IndexedCS(base, highVal, lookup) {
|
||||||
this.name = 'Indexed';
|
this.name = 'Indexed';
|
||||||
this.numComps = 1;
|
this.numComps = 1;
|
||||||
this.defaultColor = [0];
|
this.defaultColor = [0];
|
||||||
@ -240,7 +266,7 @@ var IndexedCS = (function indexedCS() {
|
|||||||
this.lookup = lookupArray;
|
this.lookup = lookupArray;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor.prototype = {
|
IndexedCS.prototype = {
|
||||||
getRgb: function indexcs_getRgb(color) {
|
getRgb: function indexcs_getRgb(color) {
|
||||||
var numComps = this.base.numComps;
|
var numComps = this.base.numComps;
|
||||||
var start = color[0] * numComps;
|
var start = color[0] * numComps;
|
||||||
@ -267,19 +293,23 @@ var IndexedCS = (function indexedCS() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return base.getRgbBuffer(baseBuf, 8);
|
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() {
|
var DeviceGrayCS = (function DeviceGrayCSClosure() {
|
||||||
function constructor() {
|
function DeviceGrayCS() {
|
||||||
this.name = 'DeviceGray';
|
this.name = 'DeviceGray';
|
||||||
this.numComps = 1;
|
this.numComps = 1;
|
||||||
this.defaultColor = [0];
|
this.defaultColor = [0];
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor.prototype = {
|
DeviceGrayCS.prototype = {
|
||||||
getRgb: function graycs_getRgb(color) {
|
getRgb: function graycs_getRgb(color) {
|
||||||
var c = color[0];
|
var c = color[0];
|
||||||
return [c, c, c];
|
return [c, c, c];
|
||||||
@ -295,18 +325,21 @@ var DeviceGrayCS = (function deviceGrayCS() {
|
|||||||
rgbBuf[j++] = c;
|
rgbBuf[j++] = c;
|
||||||
}
|
}
|
||||||
return rgbBuf;
|
return rgbBuf;
|
||||||
|
},
|
||||||
|
isDefaultDecode: function graycs_isDefaultDecode(decodeMap) {
|
||||||
|
return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return constructor;
|
return DeviceGrayCS;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
var DeviceRgbCS = (function deviceRgbCS() {
|
var DeviceRgbCS = (function DeviceRgbCSClosure() {
|
||||||
function constructor() {
|
function DeviceRgbCS() {
|
||||||
this.name = 'DeviceRGB';
|
this.name = 'DeviceRGB';
|
||||||
this.numComps = 3;
|
this.numComps = 3;
|
||||||
this.defaultColor = [0, 0, 0];
|
this.defaultColor = [0, 0, 0];
|
||||||
}
|
}
|
||||||
constructor.prototype = {
|
DeviceRgbCS.prototype = {
|
||||||
getRgb: function rgbcs_getRgb(color) {
|
getRgb: function rgbcs_getRgb(color) {
|
||||||
return color;
|
return color;
|
||||||
},
|
},
|
||||||
@ -319,18 +352,21 @@ var DeviceRgbCS = (function deviceRgbCS() {
|
|||||||
for (i = 0; i < length; ++i)
|
for (i = 0; i < length; ++i)
|
||||||
rgbBuf[i] = (scale * input[i]) | 0;
|
rgbBuf[i] = (scale * input[i]) | 0;
|
||||||
return rgbBuf;
|
return rgbBuf;
|
||||||
|
},
|
||||||
|
isDefaultDecode: function rgbcs_isDefaultDecode(decodeMap) {
|
||||||
|
return ColorSpace.isDefaultDecode(decodeMap, this.numComps);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return constructor;
|
return DeviceRgbCS;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
var DeviceCmykCS = (function deviceCmykCS() {
|
var DeviceCmykCS = (function DeviceCmykCSClosure() {
|
||||||
function constructor() {
|
function DeviceCmykCS() {
|
||||||
this.name = 'DeviceCMYK';
|
this.name = 'DeviceCMYK';
|
||||||
this.numComps = 4;
|
this.numComps = 4;
|
||||||
this.defaultColor = [0, 0, 0, 1];
|
this.defaultColor = [0, 0, 0, 1];
|
||||||
}
|
}
|
||||||
constructor.prototype = {
|
DeviceCmykCS.prototype = {
|
||||||
getRgb: function cmykcs_getRgb(color) {
|
getRgb: function cmykcs_getRgb(color) {
|
||||||
var c = color[0], m = color[1], y = color[2], k = color[3];
|
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;
|
var c1 = 1 - c, m1 = 1 - m, y1 = 1 - y, k1 = 1 - k;
|
||||||
@ -403,9 +439,12 @@ var DeviceCmykCS = (function deviceCmykCS() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return rgbBuf;
|
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 globalScope = (typeof window === 'undefined') ? this : window;
|
||||||
|
|
||||||
|
var isWorker = (typeof window == 'undefined');
|
||||||
|
|
||||||
var ERRORS = 0, WARNINGS = 1, TODOS = 5;
|
var ERRORS = 0, WARNINGS = 1, TODOS = 5;
|
||||||
var verbosity = WARNINGS;
|
var verbosity = WARNINGS;
|
||||||
|
|
||||||
@ -31,7 +33,7 @@ function getPdf(arg, callback) {
|
|||||||
var xhr = new XMLHttpRequest();
|
var xhr = new XMLHttpRequest();
|
||||||
xhr.open('GET', params.url);
|
xhr.open('GET', params.url);
|
||||||
xhr.mozResponseType = xhr.responseType = 'arraybuffer';
|
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)
|
if ('progress' in params)
|
||||||
xhr.onprogress = params.progress || undefined;
|
xhr.onprogress = params.progress || undefined;
|
||||||
@ -39,19 +41,23 @@ function getPdf(arg, callback) {
|
|||||||
if ('error' in params)
|
if ('error' in params)
|
||||||
xhr.onerror = params.error || undefined;
|
xhr.onerror = params.error || undefined;
|
||||||
|
|
||||||
xhr.onreadystatechange = function getPdfOnreadystatechange() {
|
xhr.onreadystatechange = function getPdfOnreadystatechange(e) {
|
||||||
if (xhr.readyState === 4 && xhr.status === xhr.expected) {
|
if (xhr.readyState === 4) {
|
||||||
var data = (xhr.mozResponseArrayBuffer || xhr.mozResponse ||
|
if (xhr.status === xhr.expected) {
|
||||||
xhr.responseArrayBuffer || xhr.response);
|
var data = (xhr.mozResponseArrayBuffer || xhr.mozResponse ||
|
||||||
callback(data);
|
xhr.responseArrayBuffer || xhr.response);
|
||||||
|
callback(data);
|
||||||
|
} else if (params.error) {
|
||||||
|
params.error(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
xhr.send(null);
|
xhr.send(null);
|
||||||
}
|
}
|
||||||
globalScope.PDFJS.getPdf = getPdf;
|
globalScope.PDFJS.getPdf = getPdf;
|
||||||
|
|
||||||
var Page = (function pagePage() {
|
var Page = (function PageClosure() {
|
||||||
function constructor(xref, pageNumber, pageDict, ref) {
|
function Page(xref, pageNumber, pageDict, ref) {
|
||||||
this.pageNumber = pageNumber;
|
this.pageNumber = pageNumber;
|
||||||
this.pageDict = pageDict;
|
this.pageDict = pageDict;
|
||||||
this.stats = {
|
this.stats = {
|
||||||
@ -63,9 +69,11 @@ var Page = (function pagePage() {
|
|||||||
};
|
};
|
||||||
this.xref = xref;
|
this.xref = xref;
|
||||||
this.ref = ref;
|
this.ref = ref;
|
||||||
|
|
||||||
|
this.displayReadyPromise = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor.prototype = {
|
Page.prototype = {
|
||||||
getPageProp: function pageGetPageProp(key) {
|
getPageProp: function pageGetPageProp(key) {
|
||||||
return this.xref.fetchIfRef(this.pageDict.get(key));
|
return this.xref.fetchIfRef(this.pageDict.get(key));
|
||||||
},
|
},
|
||||||
@ -101,9 +109,11 @@ var Page = (function pagePage() {
|
|||||||
width: this.width,
|
width: this.width,
|
||||||
height: this.height
|
height: this.height
|
||||||
};
|
};
|
||||||
|
var mediaBox = this.mediaBox;
|
||||||
|
var offsetX = mediaBox[0], offsetY = mediaBox[1];
|
||||||
if (isArray(obj) && obj.length == 4) {
|
if (isArray(obj) && obj.length == 4) {
|
||||||
var tl = this.rotatePoint(obj[0], obj[1]);
|
var tl = this.rotatePoint(obj[0] - offsetX, obj[1] - offsetY);
|
||||||
var br = this.rotatePoint(obj[2], obj[3]);
|
var br = this.rotatePoint(obj[2] - offsetX, obj[3] - offsetY);
|
||||||
view.x = Math.min(tl.x, br.x);
|
view.x = Math.min(tl.x, br.x);
|
||||||
view.y = Math.min(tl.y, br.y);
|
view.y = Math.min(tl.y, br.y);
|
||||||
view.width = Math.abs(tl.x - br.x);
|
view.width = Math.abs(tl.x - br.x);
|
||||||
@ -156,18 +166,12 @@ var Page = (function pagePage() {
|
|||||||
IRQueue, fonts) {
|
IRQueue, fonts) {
|
||||||
var self = this;
|
var self = this;
|
||||||
this.IRQueue = IRQueue;
|
this.IRQueue = IRQueue;
|
||||||
var gfx = new CanvasGraphics(this.ctx, this.objs);
|
|
||||||
|
|
||||||
var displayContinuation = function pageDisplayContinuation() {
|
var displayContinuation = function pageDisplayContinuation() {
|
||||||
// Always defer call to display() to work around bug in
|
// Always defer call to display() to work around bug in
|
||||||
// Firefox error reporting from XHR callbacks.
|
// Firefox error reporting from XHR callbacks.
|
||||||
setTimeout(function pageSetTimeout() {
|
setTimeout(function pageSetTimeout() {
|
||||||
try {
|
self.displayReadyPromise.resolve();
|
||||||
self.display(gfx, self.callback);
|
|
||||||
} catch (e) {
|
|
||||||
if (self.callback) self.callback(e.toString());
|
|
||||||
throw e;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -241,6 +245,7 @@ var Page = (function pagePage() {
|
|||||||
startIdx = gfx.executeIRQueue(IRQueue, startIdx, next);
|
startIdx = gfx.executeIRQueue(IRQueue, startIdx, next);
|
||||||
if (startIdx == length) {
|
if (startIdx == length) {
|
||||||
self.stats.render = Date.now();
|
self.stats.render = Date.now();
|
||||||
|
gfx.endDrawing();
|
||||||
if (callback) callback();
|
if (callback) callback();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -262,57 +267,160 @@ var Page = (function pagePage() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
getLinks: function pageGetLinks() {
|
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;
|
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 annotations = xref.fetchIfRef(this.annotations) || [];
|
||||||
var i, n = annotations.length;
|
var i, n = annotations.length;
|
||||||
var links = [];
|
var items = [];
|
||||||
for (i = 0; i < n; ++i) {
|
for (i = 0; i < n; ++i) {
|
||||||
var annotation = xref.fetch(annotations[i]);
|
var annotationRef = annotations[i];
|
||||||
|
var annotation = xref.fetch(annotationRef);
|
||||||
if (!isDict(annotation))
|
if (!isDict(annotation))
|
||||||
continue;
|
continue;
|
||||||
var subtype = annotation.get('Subtype');
|
var subtype = annotation.get('Subtype');
|
||||||
if (!isName(subtype) || subtype.name != 'Link')
|
if (!isName(subtype))
|
||||||
continue;
|
continue;
|
||||||
var rect = annotation.get('Rect');
|
var rect = annotation.get('Rect');
|
||||||
var topLeftCorner = this.rotatePoint(rect[0], rect[1]);
|
var topLeftCorner = this.rotatePoint(rect[0], rect[1]);
|
||||||
var bottomRightCorner = this.rotatePoint(rect[2], rect[3]);
|
var bottomRightCorner = this.rotatePoint(rect[2], rect[3]);
|
||||||
|
|
||||||
var link = {};
|
var item = {};
|
||||||
link.x = Math.min(topLeftCorner.x, bottomRightCorner.x);
|
item.type = subtype.name;
|
||||||
link.y = Math.min(topLeftCorner.y, bottomRightCorner.y);
|
item.x = Math.min(topLeftCorner.x, bottomRightCorner.x);
|
||||||
link.width = Math.abs(topLeftCorner.x - bottomRightCorner.x);
|
item.y = Math.min(topLeftCorner.y, bottomRightCorner.y);
|
||||||
link.height = Math.abs(topLeftCorner.y - bottomRightCorner.y);
|
item.width = Math.abs(topLeftCorner.x - bottomRightCorner.x);
|
||||||
var a = this.xref.fetchIfRef(annotation.get('A'));
|
item.height = Math.abs(topLeftCorner.y - bottomRightCorner.y);
|
||||||
if (a) {
|
switch (subtype.name) {
|
||||||
switch (a.get('S').name) {
|
case 'Link':
|
||||||
case 'URI':
|
var a = this.xref.fetchIfRef(annotation.get('A'));
|
||||||
link.url = a.get('URI');
|
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;
|
break;
|
||||||
case 'GoTo':
|
item.fieldType = fieldType.name;
|
||||||
link.dest = a.get('D');
|
// Building the full field name by collecting the field and
|
||||||
break;
|
// its ancestors 'T' properties and joining them using '.'.
|
||||||
default:
|
var fieldName = [];
|
||||||
TODO('other link types');
|
var namedItem = annotation, ref = annotationRef;
|
||||||
}
|
while (namedItem) {
|
||||||
} else if (annotation.has('Dest')) {
|
var parentRef = namedItem.get('Parent');
|
||||||
// simple destination link
|
var parent = xref.fetchIfRef(parentRef);
|
||||||
var dest = annotation.get('Dest');
|
var name = namedItem.get('T');
|
||||||
link.dest = isName(dest) ? dest.name : dest;
|
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) {
|
startRendering: function pageStartRendering(ctx, callback, textLayer) {
|
||||||
this.ctx = ctx;
|
|
||||||
this.callback = callback;
|
|
||||||
|
|
||||||
this.startRenderingTime = Date.now();
|
this.startRenderingTime = Date.now();
|
||||||
this.pdf.startRendering(this);
|
|
||||||
|
// If there is no displayReadyPromise yet, then the IRQueue was never
|
||||||
|
// requested before. Make the request and create the promise.
|
||||||
|
if (!this.displayReadyPromise) {
|
||||||
|
this.pdf.startRendering(this);
|
||||||
|
this.displayReadyPromise = new Promise();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Once the IRQueue and fonts are loaded, perform the actual rendering.
|
||||||
|
this.displayReadyPromise.then(
|
||||||
|
function pageDisplayReadyPromise() {
|
||||||
|
var gfx = new CanvasGraphics(ctx, this.objs, textLayer);
|
||||||
|
try {
|
||||||
|
this.display(gfx, callback);
|
||||||
|
} catch (e) {
|
||||||
|
if (callback)
|
||||||
|
callback(e);
|
||||||
|
else
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}.bind(this),
|
||||||
|
function pageDisplayReadPromiseError(reason) {
|
||||||
|
if (callback)
|
||||||
|
callback(reason);
|
||||||
|
else
|
||||||
|
throw reason;
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
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
|
* need for the `PDFDocModel` anymore and there is only one object on the
|
||||||
* main thread and not one entire copy on each worker instance.
|
* main thread and not one entire copy on each worker instance.
|
||||||
*/
|
*/
|
||||||
var PDFDocModel = (function pdfDoc() {
|
var PDFDocModel = (function PDFDocModelClosure() {
|
||||||
function constructor(arg, callback) {
|
function PDFDocModel(arg, callback) {
|
||||||
if (isStream(arg))
|
if (isStream(arg))
|
||||||
init.call(this, arg);
|
init.call(this, arg);
|
||||||
else if (isArrayBuffer(arg))
|
else if (isArrayBuffer(arg))
|
||||||
@ -339,6 +447,7 @@ var PDFDocModel = (function pdfDoc() {
|
|||||||
assertWellFormed(stream.length > 0, 'stream must have data');
|
assertWellFormed(stream.length > 0, 'stream must have data');
|
||||||
this.stream = stream;
|
this.stream = stream;
|
||||||
this.setup();
|
this.setup();
|
||||||
|
this.acroForm = this.xref.fetchIfRef(this.catalog.catDict.get('AcroForm'));
|
||||||
}
|
}
|
||||||
|
|
||||||
function find(stream, needle, limit, backwards) {
|
function find(stream, needle, limit, backwards) {
|
||||||
@ -357,7 +466,7 @@ var PDFDocModel = (function pdfDoc() {
|
|||||||
return true; /* found */
|
return true; /* found */
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor.prototype = {
|
PDFDocModel.prototype = {
|
||||||
get linearization() {
|
get linearization() {
|
||||||
var length = this.stream.length;
|
var length = this.stream.length;
|
||||||
var linearization = false;
|
var linearization = false;
|
||||||
@ -379,12 +488,17 @@ var PDFDocModel = (function pdfDoc() {
|
|||||||
if (find(stream, 'endobj', 1024))
|
if (find(stream, 'endobj', 1024))
|
||||||
startXRef = stream.pos + 6;
|
startXRef = stream.pos + 6;
|
||||||
} else {
|
} else {
|
||||||
// Find startxref at the end of the file.
|
// Find startxref by jumping backward from the end of the file.
|
||||||
var start = stream.end - 1024;
|
var step = 1024;
|
||||||
if (start < 0)
|
var found = false, pos = stream.end;
|
||||||
start = 0;
|
while (!found && pos > 0) {
|
||||||
stream.pos = start;
|
pos -= step - 'startxref'.length;
|
||||||
if (find(stream, 'startxref', 1024, true)) {
|
if (pos < 0)
|
||||||
|
pos = 0;
|
||||||
|
stream.pos = pos;
|
||||||
|
found = find(stream, 'startxref', step, true);
|
||||||
|
}
|
||||||
|
if (found) {
|
||||||
stream.skip(9);
|
stream.skip(9);
|
||||||
var ch;
|
var ch;
|
||||||
do {
|
do {
|
||||||
@ -425,10 +539,19 @@ var PDFDocModel = (function pdfDoc() {
|
|||||||
},
|
},
|
||||||
setup: function pdfDocSetup(ownerPassword, userPassword) {
|
setup: function pdfDocSetup(ownerPassword, userPassword) {
|
||||||
this.checkHeader();
|
this.checkHeader();
|
||||||
this.xref = new XRef(this.stream,
|
var xref = new XRef(this.stream,
|
||||||
this.startXRef,
|
this.startXRef,
|
||||||
this.mainXRefEntriesOffset);
|
this.mainXRefEntriesOffset);
|
||||||
this.catalog = new Catalog(this.xref);
|
this.xref = xref;
|
||||||
|
this.catalog = new Catalog(xref);
|
||||||
|
if (xref.trailer && xref.trailer.has('ID')) {
|
||||||
|
var fileID = '';
|
||||||
|
var id = xref.fetchIfRef(xref.trailer.get('ID'))[0];
|
||||||
|
id.split('').forEach(function(el) {
|
||||||
|
fileID += Number(el.charCodeAt(0)).toString(16);
|
||||||
|
});
|
||||||
|
this.fileID = fileID;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
get numPages() {
|
get numPages() {
|
||||||
var linearization = this.linearization;
|
var linearization = this.linearization;
|
||||||
@ -436,16 +559,32 @@ var PDFDocModel = (function pdfDoc() {
|
|||||||
// shadow the prototype getter
|
// shadow the prototype getter
|
||||||
return shadow(this, 'numPages', num);
|
return shadow(this, 'numPages', num);
|
||||||
},
|
},
|
||||||
|
getFingerprint: function pdfDocGetFingerprint() {
|
||||||
|
if (this.fileID) {
|
||||||
|
return this.fileID;
|
||||||
|
} else {
|
||||||
|
// If we got no fileID, then we generate one,
|
||||||
|
// from the first 100 bytes of PDF
|
||||||
|
var data = this.stream.bytes.subarray(0, 100);
|
||||||
|
var hash = calculateMD5(data, 0, data.length);
|
||||||
|
var strHash = '';
|
||||||
|
for (var i = 0, length = hash.length; i < length; i++) {
|
||||||
|
strHash += Number(hash[i]).toString(16);
|
||||||
|
}
|
||||||
|
|
||||||
|
return strHash;
|
||||||
|
}
|
||||||
|
},
|
||||||
getPage: function pdfDocGetPage(n) {
|
getPage: function pdfDocGetPage(n) {
|
||||||
return this.catalog.getPage(n);
|
return this.catalog.getPage(n);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return constructor;
|
return PDFDocModel;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
var PDFDoc = (function pdfDoc() {
|
var PDFDoc = (function PDFDocClosure() {
|
||||||
function constructor(arg, callback) {
|
function PDFDoc(arg, callback) {
|
||||||
var stream = null;
|
var stream = null;
|
||||||
var data = null;
|
var data = null;
|
||||||
|
|
||||||
@ -462,7 +601,7 @@ var PDFDoc = (function pdfDoc() {
|
|||||||
this.data = data;
|
this.data = data;
|
||||||
this.stream = stream;
|
this.stream = stream;
|
||||||
this.pdf = new PDFDocModel(stream);
|
this.pdf = new PDFDocModel(stream);
|
||||||
|
this.fingerprint = this.pdf.getFingerprint();
|
||||||
this.catalog = this.pdf.catalog;
|
this.catalog = this.pdf.catalog;
|
||||||
this.objs = new PDFObjects();
|
this.objs = new PDFObjects();
|
||||||
|
|
||||||
@ -481,39 +620,38 @@ var PDFDoc = (function pdfDoc() {
|
|||||||
throw 'No PDFJS.workerSrc specified';
|
throw 'No PDFJS.workerSrc specified';
|
||||||
}
|
}
|
||||||
|
|
||||||
var worker;
|
|
||||||
try {
|
try {
|
||||||
worker = new Worker(workerSrc);
|
|
||||||
} catch (e) {
|
|
||||||
// Some versions of FF can't create a worker on localhost, see:
|
// Some versions of FF can't create a worker on localhost, see:
|
||||||
// https://bugzilla.mozilla.org/show_bug.cgi?id=683280
|
// https://bugzilla.mozilla.org/show_bug.cgi?id=683280
|
||||||
globalScope.PDFJS.disableWorker = true;
|
var worker = new Worker(workerSrc);
|
||||||
this.setupFakeWorker();
|
|
||||||
|
var messageHandler = new MessageHandler('main', worker);
|
||||||
|
// Tell the worker the file it was created from.
|
||||||
|
messageHandler.send('workerSrc', workerSrc);
|
||||||
|
messageHandler.on('test', function pdfDocTest(supportTypedArray) {
|
||||||
|
if (supportTypedArray) {
|
||||||
|
this.worker = worker;
|
||||||
|
this.setupMessageHandler(messageHandler);
|
||||||
|
} else {
|
||||||
|
globalScope.PDFJS.disableWorker = true;
|
||||||
|
this.setupFakeWorker();
|
||||||
|
}
|
||||||
|
}.bind(this));
|
||||||
|
|
||||||
|
var testObj = new Uint8Array(1);
|
||||||
|
// Some versions of Opera throw a DATA_CLONE_ERR on
|
||||||
|
// serializing the typed array.
|
||||||
|
messageHandler.send('test', testObj);
|
||||||
return;
|
return;
|
||||||
}
|
} catch (e) {}
|
||||||
|
|
||||||
var messageHandler = new MessageHandler('main', worker);
|
|
||||||
|
|
||||||
// Tell the worker the file it was created from.
|
|
||||||
messageHandler.send('workerSrc', workerSrc);
|
|
||||||
|
|
||||||
messageHandler.on('test', function pdfDocTest(supportTypedArray) {
|
|
||||||
if (supportTypedArray) {
|
|
||||||
this.worker = worker;
|
|
||||||
this.setupMessageHandler(messageHandler);
|
|
||||||
} else {
|
|
||||||
this.setupFakeWorker();
|
|
||||||
}
|
|
||||||
}.bind(this));
|
|
||||||
|
|
||||||
var testObj = new Uint8Array(1);
|
|
||||||
messageHandler.send('test', testObj);
|
|
||||||
} else {
|
|
||||||
this.setupFakeWorker();
|
|
||||||
}
|
}
|
||||||
|
// Either workers are disabled, not supported or have thrown an exception.
|
||||||
|
// Thus, we fallback to a faked worker.
|
||||||
|
globalScope.PDFJS.disableWorker = true;
|
||||||
|
this.setupFakeWorker();
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor.prototype = {
|
PDFDoc.prototype = {
|
||||||
setupFakeWorker: function() {
|
setupFakeWorker: function() {
|
||||||
// If we don't use a worker, just post/sendMessage to the main thread.
|
// If we don't use a worker, just post/sendMessage to the main thread.
|
||||||
var fakeWorker = {
|
var fakeWorker = {
|
||||||
@ -549,8 +687,12 @@ var PDFDoc = (function pdfDoc() {
|
|||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'JpegStream':
|
case 'JpegStream':
|
||||||
var IR = data[2];
|
var imageData = data[2];
|
||||||
new JpegImageLoader(id, IR, this.objs);
|
loadJpegStream(id, imageData, this.objs);
|
||||||
|
break;
|
||||||
|
case 'Image':
|
||||||
|
var imageData = data[2];
|
||||||
|
this.objs.resolve(id, imageData);
|
||||||
break;
|
break;
|
||||||
case 'Font':
|
case 'Font':
|
||||||
var name = data[2];
|
var name = data[2];
|
||||||
@ -558,20 +700,9 @@ var PDFDoc = (function pdfDoc() {
|
|||||||
var properties = data[4];
|
var properties = data[4];
|
||||||
|
|
||||||
if (file) {
|
if (file) {
|
||||||
|
// Rewrap the ArrayBuffer in a stream.
|
||||||
var fontFileDict = new Dict();
|
var fontFileDict = new Dict();
|
||||||
fontFileDict.map = file.dict.map;
|
file = new Stream(file, 0, file.length, fontFileDict);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// For now, resolve the font object here direclty. The real font
|
// For now, resolve the font object here direclty. The real font
|
||||||
@ -599,6 +730,49 @@ var PDFDoc = (function pdfDoc() {
|
|||||||
}
|
}
|
||||||
}.bind(this));
|
}.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() {
|
setTimeout(function pdfDocFontReadySetTimeout() {
|
||||||
messageHandler.send('doc', this.data);
|
messageHandler.send('doc', this.data);
|
||||||
this.workerReadyPromise.resolve(true);
|
this.workerReadyPromise.resolve(true);
|
||||||
@ -645,7 +819,7 @@ var PDFDoc = (function pdfDoc() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return constructor;
|
return PDFDoc;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
globalScope.PDFJS.PDFDoc = PDFDoc;
|
globalScope.PDFJS.PDFDoc = PDFDoc;
|
||||||
|
@ -3,8 +3,8 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var ARCFourCipher = (function arcFourCipher() {
|
var ARCFourCipher = (function ARCFourCipherClosure() {
|
||||||
function constructor(key) {
|
function ARCFourCipher(key) {
|
||||||
this.a = 0;
|
this.a = 0;
|
||||||
this.b = 0;
|
this.b = 0;
|
||||||
var s = new Uint8Array(256);
|
var s = new Uint8Array(256);
|
||||||
@ -20,7 +20,7 @@ var ARCFourCipher = (function arcFourCipher() {
|
|||||||
this.s = s;
|
this.s = s;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor.prototype = {
|
ARCFourCipher.prototype = {
|
||||||
encryptBlock: function arcFourCipherEncryptBlock(data) {
|
encryptBlock: function arcFourCipherEncryptBlock(data) {
|
||||||
var i, n = data.length, tmp, tmp2;
|
var i, n = data.length, tmp, tmp2;
|
||||||
var a = this.a, b = this.b, s = this.s;
|
var a = this.a, b = this.b, s = this.s;
|
||||||
@ -39,12 +39,12 @@ var ARCFourCipher = (function arcFourCipher() {
|
|||||||
return output;
|
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([
|
var r = new Uint8Array([
|
||||||
7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,
|
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,
|
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;
|
return hash;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
var NullCipher = (function nullCipher() {
|
var NullCipher = (function NullCipherClosure() {
|
||||||
function constructor() {
|
function NullCipher() {
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor.prototype = {
|
NullCipher.prototype = {
|
||||||
decryptBlock: function nullCipherDecryptBlock(data) {
|
decryptBlock: function nullCipherDecryptBlock(data) {
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return constructor;
|
return NullCipher;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
var AES128Cipher = (function aes128Cipher() {
|
var AES128Cipher = (function AES128CipherClosure() {
|
||||||
var rcon = new Uint8Array([
|
var rcon = new Uint8Array([
|
||||||
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c,
|
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36, 0x6c,
|
||||||
0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a,
|
0xd8, 0xab, 0x4d, 0x9a, 0x2f, 0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a,
|
||||||
@ -330,7 +330,7 @@ var AES128Cipher = (function aes128Cipher() {
|
|||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
||||||
function constructor(key) {
|
function AES128Cipher(key) {
|
||||||
this.key = expandKey128(key);
|
this.key = expandKey128(key);
|
||||||
this.buffer = new Uint8Array(16);
|
this.buffer = new Uint8Array(16);
|
||||||
this.bufferPosition = 0;
|
this.bufferPosition = 0;
|
||||||
@ -370,7 +370,7 @@ var AES128Cipher = (function aes128Cipher() {
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor.prototype = {
|
AES128Cipher.prototype = {
|
||||||
decryptBlock: function aes128CipherDecryptBlock(data) {
|
decryptBlock: function aes128CipherDecryptBlock(data) {
|
||||||
var i, sourceLength = data.length;
|
var i, sourceLength = data.length;
|
||||||
var buffer = this.buffer, bufferLength = this.bufferPosition;
|
var buffer = this.buffer, bufferLength = this.bufferPosition;
|
||||||
@ -391,15 +391,15 @@ var AES128Cipher = (function aes128Cipher() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return constructor;
|
return AES128Cipher;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
var CipherTransform = (function cipherTransform() {
|
var CipherTransform = (function CipherTransformClosure() {
|
||||||
function constructor(stringCipherConstructor, streamCipherConstructor) {
|
function CipherTransform(stringCipherConstructor, streamCipherConstructor) {
|
||||||
this.stringCipherConstructor = stringCipherConstructor;
|
this.stringCipherConstructor = stringCipherConstructor;
|
||||||
this.streamCipherConstructor = streamCipherConstructor;
|
this.streamCipherConstructor = streamCipherConstructor;
|
||||||
}
|
}
|
||||||
constructor.prototype = {
|
CipherTransform.prototype = {
|
||||||
createStream: function cipherTransformCreateStream(stream) {
|
createStream: function cipherTransformCreateStream(stream) {
|
||||||
var cipher = new this.streamCipherConstructor();
|
var cipher = new this.streamCipherConstructor();
|
||||||
return new DecryptStream(stream,
|
return new DecryptStream(stream,
|
||||||
@ -415,10 +415,10 @@ var CipherTransform = (function cipherTransform() {
|
|||||||
return bytesToString(data);
|
return bytesToString(data);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return constructor;
|
return CipherTransform;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
var CipherTransformFactory = (function cipherTransformFactory() {
|
var CipherTransformFactory = (function CipherTransformFactoryClosure() {
|
||||||
function prepareKeyData(fileId, password, ownerPassword, userPassword,
|
function prepareKeyData(fileId, password, ownerPassword, userPassword,
|
||||||
flags, revision, keyLength, encryptMetadata) {
|
flags, revision, keyLength, encryptMetadata) {
|
||||||
var defaultPasswordBytes = new Uint8Array([
|
var defaultPasswordBytes = new Uint8Array([
|
||||||
@ -490,7 +490,7 @@ var CipherTransformFactory = (function cipherTransformFactory() {
|
|||||||
|
|
||||||
var identityName = new Name('Identity');
|
var identityName = new Name('Identity');
|
||||||
|
|
||||||
function constructor(dict, fileId, password) {
|
function CipherTransformFactory(dict, fileId, password) {
|
||||||
var filter = dict.get('Filter');
|
var filter = dict.get('Filter');
|
||||||
if (!isName(filter) || filter.name != 'Standard')
|
if (!isName(filter) || filter.name != 'Standard')
|
||||||
error('unknown encryption method');
|
error('unknown encryption method');
|
||||||
@ -573,7 +573,7 @@ var CipherTransformFactory = (function cipherTransformFactory() {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor.prototype = {
|
CipherTransformFactory.prototype = {
|
||||||
createCipherTransform: function buildCipherCreateCipherTransform(num,
|
createCipherTransform: function buildCipherCreateCipherTransform(num,
|
||||||
gen) {
|
gen) {
|
||||||
if (this.algorithm == 4) {
|
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';
|
'use strict';
|
||||||
|
|
||||||
var PartialEvaluator = (function partialEvaluator() {
|
var PartialEvaluator = (function PartialEvaluatorClosure() {
|
||||||
function constructor(xref, handler, uniquePrefix) {
|
function PartialEvaluator(xref, handler, uniquePrefix) {
|
||||||
this.state = new EvalState();
|
this.state = new EvalState();
|
||||||
this.stateStack = [];
|
this.stateStack = [];
|
||||||
|
|
||||||
@ -111,7 +111,7 @@ var PartialEvaluator = (function partialEvaluator() {
|
|||||||
EX: 'endCompat'
|
EX: 'endCompat'
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor.prototype = {
|
PartialEvaluator.prototype = {
|
||||||
getIRQueue: function partialEvaluatorGetIRQueue(stream, resources,
|
getIRQueue: function partialEvaluatorGetIRQueue(stream, resources,
|
||||||
queue, dependency) {
|
queue, dependency) {
|
||||||
|
|
||||||
@ -155,6 +155,11 @@ var PartialEvaluator = (function partialEvaluator() {
|
|||||||
font.loadedName = loadedName;
|
font.loadedName = loadedName;
|
||||||
|
|
||||||
var translated = font.translated;
|
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', [
|
handler.send('obj', [
|
||||||
loadedName,
|
loadedName,
|
||||||
'Font',
|
'Font',
|
||||||
@ -179,62 +184,54 @@ var PartialEvaluator = (function partialEvaluator() {
|
|||||||
var w = dict.get('Width', 'W');
|
var w = dict.get('Width', 'W');
|
||||||
var h = dict.get('Height', 'H');
|
var h = dict.get('Height', 'H');
|
||||||
|
|
||||||
if (image instanceof JpegStream && image.isNative) {
|
var imageMask = dict.get('ImageMask', 'IM') || false;
|
||||||
var objId = 'img_' + uniquePrefix + (++self.objIdCounter);
|
if (imageMask) {
|
||||||
handler.send('obj', [objId, 'JpegStream', image.getIR()]);
|
// 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.
|
var width = dict.get('Width', 'W');
|
||||||
insertDependency([objId]);
|
var height = dict.get('Height', 'H');
|
||||||
|
var bitStrideLength = (width + 7) >> 3;
|
||||||
// The normal fn.
|
var imgArray = image.getBytes(bitStrideLength * height);
|
||||||
fn = 'paintJpegXObject';
|
var decode = dict.get('Decode', 'D');
|
||||||
args = [objId, w, h];
|
var inverseDecode = !!decode && decode[0] > 0;
|
||||||
|
|
||||||
|
fn = 'paintImageMaskXObject';
|
||||||
|
args = [imgArray, inverseDecode, width, height];
|
||||||
return;
|
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
|
// If there is no imageMask, create the PDFImage and a lot
|
||||||
// of image processing can be done here.
|
// of image processing can be done here.
|
||||||
if (!imageMask) {
|
var objId = 'img_' + uniquePrefix + (++self.objIdCounter);
|
||||||
var imageObj = new PDFImage(xref, resources, image, inline);
|
insertDependency([objId]);
|
||||||
|
args = [objId, w, h];
|
||||||
|
|
||||||
if (imageObj.imageMask) {
|
var softMask = dict.get('SMask', 'IM') || false;
|
||||||
throw 'Can\'t handle this in the web worker :/';
|
if (!softMask && image instanceof JpegStream && image.isNative) {
|
||||||
}
|
// These JPEGs don't need any more processing so we can just send it.
|
||||||
|
fn = 'paintJpegXObject';
|
||||||
var imgData = {
|
handler.send('obj', [objId, 'JpegStream', image.getIR()]);
|
||||||
width: w,
|
|
||||||
height: h,
|
|
||||||
data: new Uint8Array(w * h * 4)
|
|
||||||
};
|
|
||||||
var pixels = imgData.data;
|
|
||||||
imageObj.fillRgbaBuffer(pixels, imageObj.decode);
|
|
||||||
|
|
||||||
fn = 'paintImageXObject';
|
|
||||||
args = [imgData];
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// This depends on a tmpCanvas beeing filled with the
|
fn = 'paintImageXObject';
|
||||||
// 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';
|
|
||||||
|
|
||||||
var width = dict.get('Width', 'W');
|
PDFImage.buildImage(function(imageObj) {
|
||||||
var height = dict.get('Height', 'H');
|
var drawWidth = imageObj.drawWidth;
|
||||||
var bitStrideLength = (width + 7) >> 3;
|
var drawHeight = imageObj.drawHeight;
|
||||||
var imgArray = image.getBytes(bitStrideLength * height);
|
var imgData = {
|
||||||
var decode = dict.get('Decode', 'D');
|
width: drawWidth,
|
||||||
var inverseDecode = !!decode && decode[0] > 0;
|
height: drawHeight,
|
||||||
|
data: new Uint8Array(drawWidth * drawHeight * 4)
|
||||||
args = [imgArray, inverseDecode, width, height];
|
};
|
||||||
|
var pixels = imgData.data;
|
||||||
|
imageObj.fillRgbaBuffer(pixels, drawWidth, drawHeight);
|
||||||
|
handler.send('obj', [objId, 'Image', imgData]);
|
||||||
|
}, handler, xref, resources, image, inline);
|
||||||
}
|
}
|
||||||
|
|
||||||
uniquePrefix = uniquePrefix || '';
|
uniquePrefix = uniquePrefix || '';
|
||||||
@ -493,6 +490,8 @@ var PartialEvaluator = (function partialEvaluator() {
|
|||||||
var baseName = encoding.get('BaseEncoding');
|
var baseName = encoding.get('BaseEncoding');
|
||||||
if (baseName)
|
if (baseName)
|
||||||
baseEncoding = Encodings[baseName.name];
|
baseEncoding = Encodings[baseName.name];
|
||||||
|
else
|
||||||
|
hasEncoding = false; // base encoding was not provided
|
||||||
|
|
||||||
// Load the differences between the base and original
|
// Load the differences between the base and original
|
||||||
if (encoding.has('Differences')) {
|
if (encoding.has('Differences')) {
|
||||||
@ -512,6 +511,7 @@ var PartialEvaluator = (function partialEvaluator() {
|
|||||||
error('Encoding is not a Name nor a Dict');
|
error('Encoding is not a Name nor a Dict');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
properties.differences = differences;
|
properties.differences = differences;
|
||||||
properties.baseEncoding = baseEncoding;
|
properties.baseEncoding = baseEncoding;
|
||||||
properties.hasEncoding = hasEncoding;
|
properties.hasEncoding = hasEncoding;
|
||||||
@ -554,9 +554,21 @@ var PartialEvaluator = (function partialEvaluator() {
|
|||||||
var startRange = tokens[j];
|
var startRange = tokens[j];
|
||||||
var endRange = tokens[j + 1];
|
var endRange = tokens[j + 1];
|
||||||
var code = tokens[j + 2];
|
var code = tokens[j + 2];
|
||||||
while (startRange <= endRange) {
|
if (code == 0xFFFF) {
|
||||||
charToUnicode[startRange] = code++;
|
// CMap is broken, assuming code == startRange
|
||||||
++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;
|
break;
|
||||||
@ -595,9 +607,18 @@ var PartialEvaluator = (function partialEvaluator() {
|
|||||||
}
|
}
|
||||||
} else if (byte == 0x3E) {
|
} else if (byte == 0x3E) {
|
||||||
if (token.length) {
|
if (token.length) {
|
||||||
// parsing hex number
|
if (token.length <= 4) {
|
||||||
tokens.push(parseInt(token, 16));
|
// parsing hex number
|
||||||
token = '';
|
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 {
|
} else {
|
||||||
token += String.fromCharCode(byte);
|
token += String.fromCharCode(byte);
|
||||||
@ -829,11 +850,11 @@ var PartialEvaluator = (function partialEvaluator() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return constructor;
|
return PartialEvaluator;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
var EvalState = (function evalState() {
|
var EvalState = (function EvalStateClosure() {
|
||||||
function constructor() {
|
function EvalState() {
|
||||||
// Are soft masks and alpha values shapes or opacities?
|
// Are soft masks and alpha values shapes or opacities?
|
||||||
this.alphaIsShape = false;
|
this.alphaIsShape = false;
|
||||||
this.fontSize = 0;
|
this.fontSize = 0;
|
||||||
@ -850,8 +871,8 @@ var EvalState = (function evalState() {
|
|||||||
this.fillColorSpace = null;
|
this.fillColorSpace = null;
|
||||||
this.strokeColorSpace = 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';
|
'use strict';
|
||||||
|
|
||||||
var isWorker = (typeof window == 'undefined');
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Maximum time to wait for a font to be loaded by font-face rules.
|
* Maximum time to wait for a font to be loaded by font-face rules.
|
||||||
*/
|
*/
|
||||||
@ -719,20 +717,10 @@ function getUnicodeRangeFor(value) {
|
|||||||
return -1;
|
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) {
|
function isSpecialUnicode(unicode) {
|
||||||
return (unicode <= 0x1F || (unicode >= 127 && unicode < kSizeOfGlyphArea)) ||
|
return (unicode <= 0x1F || (unicode >= 127 && unicode < kSizeOfGlyphArea)) ||
|
||||||
unicode >= kCmapGlyphOffset &&
|
(unicode >= kCmapGlyphOffset &&
|
||||||
unicode < kCmapGlyphOffset + kSizeOfGlyphArea;
|
unicode < kCmapGlyphOffset + kSizeOfGlyphArea);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -743,8 +731,8 @@ function isSpecialUnicode(unicode) {
|
|||||||
* var type1Font = new Font("MyFontName", binaryFile, propertiesObject);
|
* var type1Font = new Font("MyFontName", binaryFile, propertiesObject);
|
||||||
* type1Font.bind();
|
* type1Font.bind();
|
||||||
*/
|
*/
|
||||||
var Font = (function Font() {
|
var Font = (function FontClosure() {
|
||||||
var constructor = function font_constructor(name, file, properties) {
|
function Font(name, file, properties) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.coded = properties.coded;
|
this.coded = properties.coded;
|
||||||
this.charProcIRQueues = properties.charProcIRQueues;
|
this.charProcIRQueues = properties.charProcIRQueues;
|
||||||
@ -771,16 +759,23 @@ var Font = (function Font() {
|
|||||||
this.widths = properties.widths;
|
this.widths = properties.widths;
|
||||||
this.defaultWidth = properties.defaultWidth;
|
this.defaultWidth = properties.defaultWidth;
|
||||||
this.composite = properties.composite;
|
this.composite = properties.composite;
|
||||||
this.toUnicode = properties.toUnicode;
|
|
||||||
this.hasEncoding = properties.hasEncoding;
|
this.hasEncoding = properties.hasEncoding;
|
||||||
|
|
||||||
this.fontMatrix = properties.fontMatrix;
|
this.fontMatrix = properties.fontMatrix;
|
||||||
if (properties.type == 'Type3')
|
this.widthMultiplier = 1.0;
|
||||||
|
if (properties.type == 'Type3') {
|
||||||
|
this.encoding = properties.baseEncoding;
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Trying to fix encoding using glyph CIDSystemInfo.
|
// Trying to fix encoding using glyph CIDSystemInfo.
|
||||||
this.loadCidToUnicode(properties);
|
this.loadCidToUnicode(properties);
|
||||||
|
|
||||||
|
if (properties.toUnicode)
|
||||||
|
this.toUnicode = properties.toUnicode;
|
||||||
|
else
|
||||||
|
this.rebuildToUnicode(properties);
|
||||||
|
|
||||||
if (!file) {
|
if (!file) {
|
||||||
// The file data is not specified. Trying to fix the font name
|
// The file data is not specified. Trying to fix the font name
|
||||||
// to be used with the canvas.font.
|
// to be used with the canvas.font.
|
||||||
@ -832,6 +827,8 @@ var Font = (function Font() {
|
|||||||
|
|
||||||
this.data = data;
|
this.data = data;
|
||||||
this.fontMatrix = properties.fontMatrix;
|
this.fontMatrix = properties.fontMatrix;
|
||||||
|
this.widthMultiplier = !properties.fontMatrix ? 1.0 :
|
||||||
|
1.0 / properties.fontMatrix[0];
|
||||||
this.encoding = properties.baseEncoding;
|
this.encoding = properties.baseEncoding;
|
||||||
this.hasShortCmap = properties.hasShortCmap;
|
this.hasShortCmap = properties.hasShortCmap;
|
||||||
this.loadedName = getUniqueName();
|
this.loadedName = getUniqueName();
|
||||||
@ -887,6 +884,13 @@ var Font = (function Font() {
|
|||||||
String.fromCharCode(value & 0xff);
|
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) {
|
function string32(value) {
|
||||||
return String.fromCharCode((value >> 24) & 0xff) +
|
return String.fromCharCode((value >> 24) & 0xff) +
|
||||||
String.fromCharCode((value >> 16) & 0xff) +
|
String.fromCharCode((value >> 16) & 0xff) +
|
||||||
@ -961,15 +965,15 @@ var Font = (function Font() {
|
|||||||
var ranges = [];
|
var ranges = [];
|
||||||
for (var n = 0; n < length; ) {
|
for (var n = 0; n < length; ) {
|
||||||
var start = codes[n].unicode;
|
var start = codes[n].unicode;
|
||||||
var startCode = codes[n].code;
|
var codeIndices = [codes[n].code];
|
||||||
++n;
|
++n;
|
||||||
var end = start;
|
var end = start;
|
||||||
while (n < length && end + 1 == codes[n].unicode) {
|
while (n < length && end + 1 == codes[n].unicode) {
|
||||||
|
codeIndices.push(codes[n].code);
|
||||||
++end;
|
++end;
|
||||||
++n;
|
++n;
|
||||||
}
|
}
|
||||||
var endCode = codes[n - 1].code;
|
ranges.push([start, end, codeIndices]);
|
||||||
ranges.push([start, end, startCode, endCode]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ranges;
|
return ranges;
|
||||||
@ -1012,17 +1016,16 @@ var Font = (function Font() {
|
|||||||
idDeltas += string16(0);
|
idDeltas += string16(0);
|
||||||
idRangeOffsets += string16(offset);
|
idRangeOffsets += string16(offset);
|
||||||
|
|
||||||
var startCode = range[2];
|
var codes = range[2];
|
||||||
var endCode = range[3];
|
for (var j = 0, jj = codes.length; j < jj; ++j)
|
||||||
for (var j = startCode; j <= endCode; ++j)
|
glyphsIds += string16(deltas[codes[j]]);
|
||||||
glyphsIds += string16(deltas[j]);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for (var i = 0; i < segCount - 1; i++) {
|
for (var i = 0; i < segCount - 1; i++) {
|
||||||
var range = ranges[i];
|
var range = ranges[i];
|
||||||
var start = range[0];
|
var start = range[0];
|
||||||
var end = range[1];
|
var end = range[1];
|
||||||
var startCode = range[2];
|
var startCode = range[2][0];
|
||||||
|
|
||||||
startCount += string16(start);
|
startCount += string16(start);
|
||||||
endCount += string16(end);
|
endCount += string16(end);
|
||||||
@ -1226,7 +1229,7 @@ var Font = (function Font() {
|
|||||||
return nameTable;
|
return nameTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor.prototype = {
|
Font.prototype = {
|
||||||
name: null,
|
name: null,
|
||||||
font: null,
|
font: null,
|
||||||
mimetype: null,
|
mimetype: null,
|
||||||
@ -1299,7 +1302,7 @@ var Font = (function Font() {
|
|||||||
properties.baseEncoding = encoding;
|
properties.baseEncoding = encoding;
|
||||||
}
|
}
|
||||||
|
|
||||||
function replaceCMapTable(cmap, font, properties) {
|
function readCMapTable(cmap, font) {
|
||||||
var start = (font.start ? font.start : 0) + cmap.offset;
|
var start = (font.start ? font.start : 0) + cmap.offset;
|
||||||
font.pos = start;
|
font.pos = start;
|
||||||
|
|
||||||
@ -1316,7 +1319,7 @@ var Font = (function Font() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check that table are sorted by platformID then encodingID,
|
// 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) -
|
return ((a.platformID << 16) + a.encodingID) -
|
||||||
((b.platformID << 16) + b.encodingID);
|
((b.platformID << 16) + b.encodingID);
|
||||||
});
|
});
|
||||||
@ -1371,16 +1374,15 @@ var Font = (function Font() {
|
|||||||
for (var j = 0; j < 256; j++) {
|
for (var j = 0; j < 256; j++) {
|
||||||
var index = font.getByte();
|
var index = font.getByte();
|
||||||
if (index) {
|
if (index) {
|
||||||
var unicode = adaptUnicode(j);
|
glyphs.push({ unicode: j, code: j });
|
||||||
glyphs.push({ unicode: unicode, code: j });
|
|
||||||
ids.push(index);
|
ids.push(index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return {
|
||||||
properties.hasShortCmap = true;
|
glyphs: glyphs,
|
||||||
|
ids: ids,
|
||||||
createGlyphNameMap(glyphs, ids, properties);
|
hasShortCmap: true
|
||||||
return cmap.data = createCMapTable(glyphs, ids);
|
};
|
||||||
} else if (format == 4) {
|
} else if (format == 4) {
|
||||||
// re-creating the table in format 4 since the encoding
|
// re-creating the table in format 4 since the encoding
|
||||||
// might be changed
|
// might be changed
|
||||||
@ -1432,17 +1434,18 @@ var Font = (function Font() {
|
|||||||
var glyphCode = offsetIndex < 0 ? j :
|
var glyphCode = offsetIndex < 0 ? j :
|
||||||
offsets[offsetIndex + j - start];
|
offsets[offsetIndex + j - start];
|
||||||
glyphCode = (glyphCode + delta) & 0xFFFF;
|
glyphCode = (glyphCode + delta) & 0xFFFF;
|
||||||
if (glyphCode == 0 || isAdaptedUnicode(j))
|
if (glyphCode == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var unicode = adaptUnicode(j);
|
glyphs.push({ unicode: j, code: j });
|
||||||
glyphs.push({ unicode: unicode, code: j });
|
|
||||||
ids.push(glyphCode);
|
ids.push(glyphCode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createGlyphNameMap(glyphs, ids, properties);
|
return {
|
||||||
return cmap.data = createCMapTable(glyphs, ids);
|
glyphs: glyphs,
|
||||||
|
ids: ids
|
||||||
|
};
|
||||||
} else if (format == 6) {
|
} else if (format == 6) {
|
||||||
// Format 6 is a 2-bytes dense mapping, which means the font data
|
// 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
|
// 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++) {
|
for (var j = 0; j < entryCount; j++) {
|
||||||
var glyphCode = int16(font.getBytes(2));
|
var glyphCode = int16(font.getBytes(2));
|
||||||
var code = firstCode + j;
|
var code = firstCode + j;
|
||||||
if (isAdaptedUnicode(glyphCode))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var unicode = adaptUnicode(code);
|
glyphs.push({ unicode: code, code: code });
|
||||||
glyphs.push({ unicode: unicode, code: code });
|
|
||||||
ids.push(glyphCode);
|
ids.push(glyphCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
createGlyphNameMap(glyphs, ids, properties);
|
return {
|
||||||
return cmap.data = createCMapTable(glyphs, ids);
|
glyphs: glyphs,
|
||||||
|
ids: ids
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return cmap.data;
|
error('Unsupported cmap table format');
|
||||||
};
|
};
|
||||||
|
|
||||||
function sanitizeMetrics(font, header, metrics, numGlyphs) {
|
function sanitizeMetrics(font, header, metrics, numGlyphs) {
|
||||||
@ -1708,17 +1710,108 @@ var Font = (function Font() {
|
|||||||
tables.push(cmap);
|
tables.push(cmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
var glyphs = [];
|
var cidToGidMap = properties.cidToGidMap || [];
|
||||||
for (i = 1; i < numGlyphs; i++) {
|
var gidToCidMap = [0];
|
||||||
if (isAdaptedUnicode(i))
|
if (cidToGidMap.length > 0) {
|
||||||
continue;
|
for (var j = cidToGidMap.length - 1; j >= 0; j--) {
|
||||||
|
var gid = cidToGidMap[j];
|
||||||
glyphs.push({ unicode: adaptUnicode(i) });
|
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 {
|
} 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;
|
this.glyphNameMap = properties.glyphNameMap;
|
||||||
|
|
||||||
|
cmap.data = createCMapTable(glyphs, ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rewrite the 'post' table if needed
|
// Rewrite the 'post' table if needed
|
||||||
@ -1808,6 +1901,14 @@ var Font = (function Font() {
|
|||||||
}
|
}
|
||||||
properties.baseEncoding = encoding;
|
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 = {
|
var fields = {
|
||||||
// PostScript Font Program
|
// 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' + // creation date
|
||||||
'\x00\x00\x00\x00\x9e\x0b\x7e\x27' + // modifification date
|
'\x00\x00\x00\x00\x9e\x0b\x7e\x27' + // modifification date
|
||||||
'\x00\x00' + // xMin
|
'\x00\x00' + // xMin
|
||||||
string16(properties.descent) + // yMin
|
safeString16(properties.descent) + // yMin
|
||||||
'\x0F\xFF' + // xMax
|
'\x0F\xFF' + // xMax
|
||||||
string16(properties.ascent) + // yMax
|
safeString16(properties.ascent) + // yMax
|
||||||
string16(properties.italicAngle ? 2 : 0) + // macStyle
|
string16(properties.italicAngle ? 2 : 0) + // macStyle
|
||||||
'\x00\x11' + // lowestRecPPEM
|
'\x00\x11' + // lowestRecPPEM
|
||||||
'\x00\x00' + // fontDirectionHint
|
'\x00\x00' + // fontDirectionHint
|
||||||
@ -1846,15 +1947,15 @@ var Font = (function Font() {
|
|||||||
'hhea': (function fontFieldsHhea() {
|
'hhea': (function fontFieldsHhea() {
|
||||||
return stringToArray(
|
return stringToArray(
|
||||||
'\x00\x01\x00\x00' + // Version number
|
'\x00\x01\x00\x00' + // Version number
|
||||||
string16(properties.ascent) + // Typographic Ascent
|
safeString16(properties.ascent) + // Typographic Ascent
|
||||||
string16(properties.descent) + // Typographic Descent
|
safeString16(properties.descent) + // Typographic Descent
|
||||||
'\x00\x00' + // Line Gap
|
'\x00\x00' + // Line Gap
|
||||||
'\xFF\xFF' + // advanceWidthMax
|
'\xFF\xFF' + // advanceWidthMax
|
||||||
'\x00\x00' + // minLeftSidebearing
|
'\x00\x00' + // minLeftSidebearing
|
||||||
'\x00\x00' + // minRightSidebearing
|
'\x00\x00' + // minRightSidebearing
|
||||||
'\x00\x00' + // xMaxExtent
|
'\x00\x00' + // xMaxExtent
|
||||||
string16(properties.capHeight) + // caretSlopeRise
|
safeString16(properties.capHeight) + // caretSlopeRise
|
||||||
string16(Math.tan(properties.italicAngle) *
|
safeString16(Math.tan(properties.italicAngle) *
|
||||||
properties.xHeight) + // caretSlopeRun
|
properties.xHeight) + // caretSlopeRun
|
||||||
'\x00\x00' + // caretOffset
|
'\x00\x00' + // caretOffset
|
||||||
'\x00\x00' + // -reserved-
|
'\x00\x00' + // -reserved-
|
||||||
@ -1868,8 +1969,11 @@ var Font = (function Font() {
|
|||||||
// Horizontal metrics
|
// Horizontal metrics
|
||||||
'hmtx': (function fontFieldsHmtx() {
|
'hmtx': (function fontFieldsHmtx() {
|
||||||
var hmtx = '\x00\x00\x00\x00'; // Fake .notdef
|
var hmtx = '\x00\x00\x00\x00'; // Fake .notdef
|
||||||
for (var i = 0, ii = charstrings.length; i < ii; i++)
|
for (var i = 0, ii = charstrings.length; i < ii; i++) {
|
||||||
hmtx += string16(charstrings[i].width) + string16(0);
|
var charstring = charstrings[i];
|
||||||
|
var width = 'width' in charstring ? charstring.width : 0;
|
||||||
|
hmtx += string16(width) + string16(0);
|
||||||
|
}
|
||||||
return stringToArray(hmtx);
|
return stringToArray(hmtx);
|
||||||
})(),
|
})(),
|
||||||
|
|
||||||
@ -1898,17 +2002,35 @@ var Font = (function Font() {
|
|||||||
return stringToArray(otf.file);
|
return stringToArray(otf.file);
|
||||||
},
|
},
|
||||||
|
|
||||||
loadCidToUnicode: function font_loadCidToUnicode(properties) {
|
rebuildToUnicode: function font_rebuildToUnicode(properties) {
|
||||||
if (properties.cidToGidMap) {
|
var firstChar = properties.firstChar, lastChar = properties.lastChar;
|
||||||
this.cidToUnicode = properties.cidToGidMap;
|
var map = [];
|
||||||
return;
|
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)
|
if (!properties.cidSystemInfo)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var cidToUnicodeMap = [];
|
var cidToUnicodeMap = [], unicodeToCIDMap = [];
|
||||||
this.cidToUnicode = cidToUnicodeMap;
|
this.cidToUnicode = cidToUnicodeMap;
|
||||||
|
this.unicodeToCID = unicodeToCIDMap;
|
||||||
|
|
||||||
var cidSystemInfo = properties.cidSystemInfo;
|
var cidSystemInfo = properties.cidSystemInfo;
|
||||||
var cidToUnicode;
|
var cidToUnicode;
|
||||||
@ -1920,28 +2042,34 @@ var Font = (function Font() {
|
|||||||
if (!cidToUnicode)
|
if (!cidToUnicode)
|
||||||
return; // identity encoding
|
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) {
|
for (i = 0, ii = cidToUnicode.length; i < ii; ++i) {
|
||||||
var unicode = cidToUnicode[i];
|
var unicode = cidToUnicode[i];
|
||||||
if (isArray(unicode)) {
|
if (isArray(unicode)) {
|
||||||
var length = unicode.length;
|
var length = unicode.length;
|
||||||
for (j = 0; j < length; j++)
|
for (j = 0; j < length; j++) {
|
||||||
cidToUnicodeMap[unicode[j]] = glyph;
|
cidToUnicodeMap[cid] = unicode[j];
|
||||||
glyph++;
|
unicodeToCIDMap[unicode[j]] = cid;
|
||||||
|
}
|
||||||
|
cid++;
|
||||||
} else if (typeof unicode === 'object') {
|
} else if (typeof unicode === 'object') {
|
||||||
var fillLength = unicode.f;
|
var fillLength = unicode.f;
|
||||||
if (fillLength) {
|
if (fillLength) {
|
||||||
k = unicode.c;
|
k = unicode.c;
|
||||||
for (j = 0; j < fillLength; ++j) {
|
for (j = 0; j < fillLength; ++j) {
|
||||||
cidToUnicodeMap[k] = glyph++;
|
cidToUnicodeMap[cid] = k;
|
||||||
|
unicodeToCIDMap[k] = cid;
|
||||||
|
cid++;
|
||||||
k++;
|
k++;
|
||||||
}
|
}
|
||||||
} else
|
} else
|
||||||
glyph += unicode.s;
|
cid += unicode.s;
|
||||||
} else if (unicode) {
|
} else if (unicode) {
|
||||||
cidToUnicodeMap[unicode] = glyph++;
|
cidToUnicodeMap[cid] = unicode;
|
||||||
|
unicodeToCIDMap[unicode] = cid;
|
||||||
|
cid++;
|
||||||
} else
|
} else
|
||||||
glyph++;
|
cid++;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -1964,7 +2092,7 @@ var Font = (function Font() {
|
|||||||
window.btoa(data) + ');');
|
window.btoa(data) + ');');
|
||||||
var rule = "@font-face { font-family:'" + fontName + "';src:" + url + '}';
|
var rule = "@font-face { font-family:'" + fontName + "';src:" + url + '}';
|
||||||
|
|
||||||
document.documentElement.firstChild.appendChild(
|
document.documentElement.getElementsByTagName('head')[0].appendChild(
|
||||||
document.createElement('style'));
|
document.createElement('style'));
|
||||||
|
|
||||||
var styleSheet = document.styleSheets[document.styleSheets.length - 1];
|
var styleSheet = document.styleSheets[document.styleSheets.length - 1];
|
||||||
@ -1973,6 +2101,37 @@ var Font = (function Font() {
|
|||||||
return rule;
|
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) {
|
charToGlyph: function fonts_charToGlyph(charcode) {
|
||||||
var unicode, width, codeIRQueue;
|
var unicode, width, codeIRQueue;
|
||||||
|
|
||||||
@ -1981,30 +2140,30 @@ var Font = (function Font() {
|
|||||||
switch (this.type) {
|
switch (this.type) {
|
||||||
case 'CIDFontType0':
|
case 'CIDFontType0':
|
||||||
if (this.noUnicodeAdaptation) {
|
if (this.noUnicodeAdaptation) {
|
||||||
width = this.widths[this.cidToUnicode[charcode]];
|
width = this.widths[this.unicodeToCID[charcode] || charcode];
|
||||||
unicode = charcode;
|
unicode = charcode;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
unicode = adaptUnicode(this.cidToUnicode[charcode] || charcode);
|
unicode = this.toUnicode[charcode] || charcode;
|
||||||
break;
|
break;
|
||||||
case 'CIDFontType2':
|
case 'CIDFontType2':
|
||||||
if (this.noUnicodeAdaptation) {
|
if (this.noUnicodeAdaptation) {
|
||||||
width = this.widths[this.cidToUnicode[charcode]];
|
width = this.widths[this.unicodeToCID[charcode] || charcode];
|
||||||
unicode = charcode;
|
unicode = charcode;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
unicode = adaptUnicode(this.cidToUnicode[charcode] || charcode);
|
unicode = this.toUnicode[charcode] || charcode;
|
||||||
break;
|
break;
|
||||||
case 'Type1':
|
case 'Type1':
|
||||||
var glyphName = this.differences[charcode] || this.encoding[charcode];
|
var glyphName = this.differences[charcode] || this.encoding[charcode];
|
||||||
|
if (!isNum(width))
|
||||||
|
width = this.widths[glyphName];
|
||||||
if (this.noUnicodeAdaptation) {
|
if (this.noUnicodeAdaptation) {
|
||||||
if (!isNum(width))
|
|
||||||
width = this.widths[glyphName];
|
|
||||||
unicode = GlyphsUnicode[glyphName] || charcode;
|
unicode = GlyphsUnicode[glyphName] || charcode;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
unicode = this.glyphNameMap[glyphName] ||
|
unicode = this.glyphNameMap[glyphName] ||
|
||||||
adaptUnicode(GlyphsUnicode[glyphName] || charcode);
|
GlyphsUnicode[glyphName] || charcode;
|
||||||
break;
|
break;
|
||||||
case 'Type3':
|
case 'Type3':
|
||||||
var glyphName = this.differences[charcode] || this.encoding[charcode];
|
var glyphName = this.differences[charcode] || this.encoding[charcode];
|
||||||
@ -2012,6 +2171,10 @@ var Font = (function Font() {
|
|||||||
unicode = charcode;
|
unicode = charcode;
|
||||||
break;
|
break;
|
||||||
case 'TrueType':
|
case 'TrueType':
|
||||||
|
if (this.useToUnicode) {
|
||||||
|
unicode = this.toUnicode[charcode] || charcode;
|
||||||
|
break;
|
||||||
|
}
|
||||||
var glyphName = this.differences[charcode] || this.encoding[charcode];
|
var glyphName = this.differences[charcode] || this.encoding[charcode];
|
||||||
if (!glyphName)
|
if (!glyphName)
|
||||||
glyphName = Encodings.StandardEncoding[charcode];
|
glyphName = Encodings.StandardEncoding[charcode];
|
||||||
@ -2022,16 +2185,16 @@ var Font = (function Font() {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (!this.hasEncoding) {
|
if (!this.hasEncoding) {
|
||||||
unicode = adaptUnicode(charcode);
|
unicode = this.useToUnicode ? this.toUnicode[charcode] : charcode;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (this.hasShortCmap) {
|
if (this.hasShortCmap && false) {
|
||||||
var j = Encodings.MacRomanEncoding.indexOf(glyphName);
|
var j = Encodings.MacRomanEncoding.indexOf(glyphName);
|
||||||
unicode = j >= 0 && !isSpecialUnicode(j) ? j :
|
unicode = j >= 0 ? j :
|
||||||
this.glyphNameMap[glyphName];
|
this.glyphNameMap[glyphName];
|
||||||
} else {
|
} else {
|
||||||
unicode = glyphName in GlyphsUnicode ?
|
unicode = glyphName in GlyphsUnicode ?
|
||||||
adaptUnicode(GlyphsUnicode[glyphName]) :
|
GlyphsUnicode[glyphName] :
|
||||||
this.glyphNameMap[glyphName];
|
this.glyphNameMap[glyphName];
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@ -2039,14 +2202,23 @@ var Font = (function Font() {
|
|||||||
warn('Unsupported font type: ' + this.type);
|
warn('Unsupported font type: ' + this.type);
|
||||||
break;
|
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 {
|
return {
|
||||||
unicode: unicode,
|
fontChar: String.fromCharCode(unicode),
|
||||||
width: isNum(width) ? width : this.defaultWidth,
|
unicode: unicodeChars,
|
||||||
|
width: width,
|
||||||
codeIRQueue: codeIRQueue
|
codeIRQueue: codeIRQueue
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
charsToGlyphs: function fonts_chars2Glyphs(chars) {
|
charsToGlyphs: function fonts_charsToGlyphs(chars) {
|
||||||
var charsCache = this.charsCache;
|
var charsCache = this.charsCache;
|
||||||
var glyphs;
|
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,
|
getOrderedCharStrings: function cff_getOrderedCharStrings(glyphs,
|
||||||
properties) {
|
properties) {
|
||||||
var charstrings = [];
|
var charstrings = [];
|
||||||
var reverseMapping = {};
|
|
||||||
var encoding = properties.baseEncoding;
|
|
||||||
var i, length, glyphName;
|
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;
|
var unusedUnicode = kCmapGlyphOffset;
|
||||||
for (i = 0, length = glyphs.length; i < length; i++) {
|
for (i = 0, length = glyphs.length; i < length; i++) {
|
||||||
var item = glyphs[i];
|
var item = glyphs[i];
|
||||||
var glyphName = item.glyph;
|
var glyphName = item.glyph;
|
||||||
var unicode = glyphName in reverseMapping ?
|
var unicode = glyphName in GlyphsUnicode ?
|
||||||
reverseMapping[glyphName] : unusedUnicode++;
|
GlyphsUnicode[glyphName] : unusedUnicode++;
|
||||||
charstrings.push({
|
charstrings.push({
|
||||||
glyph: glyphName,
|
glyph: glyphName,
|
||||||
unicode: unicode,
|
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
|
// TODO: replace parsing code with the Type2Parser in font_utils.js
|
||||||
function constructor(file, properties) {
|
function Type2CFF(file, properties) {
|
||||||
var bytes = file.getBytes();
|
var bytes = file.getBytes();
|
||||||
this.bytes = bytes;
|
this.bytes = bytes;
|
||||||
this.properties = properties;
|
this.properties = properties;
|
||||||
@ -3023,7 +3186,7 @@ var Type2CFF = (function type2CFF() {
|
|||||||
this.data = this.parse();
|
this.data = this.parse();
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor.prototype = {
|
Type2CFF.prototype = {
|
||||||
parse: function cff_parse() {
|
parse: function cff_parse() {
|
||||||
var header = this.parseHeader();
|
var header = this.parseHeader();
|
||||||
var properties = this.properties;
|
var properties = this.properties;
|
||||||
@ -3055,16 +3218,14 @@ var Type2CFF = (function type2CFF() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var charStrings = this.parseIndex(topDict.CharStrings);
|
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 charset, encoding;
|
||||||
var isCIDFont = properties.subtype == 'CIDFontType0C';
|
var isCIDFont = properties.subtype == 'CIDFontType0C';
|
||||||
if (isCIDFont) {
|
if (isCIDFont) {
|
||||||
charset = [];
|
charset = ['.notdef'];
|
||||||
charset.length = charStrings.length;
|
for (var i = 1, ii = charStrings.length; i < ii; ++i)
|
||||||
|
charset.push('glyph' + i);
|
||||||
|
|
||||||
encoding = this.parseCidMap(topDict.charset,
|
encoding = this.parseCidMap(topDict.charset,
|
||||||
charStrings.length);
|
charStrings.length);
|
||||||
} else {
|
} else {
|
||||||
@ -3133,38 +3294,44 @@ var Type2CFF = (function type2CFF() {
|
|||||||
var charstrings = [];
|
var charstrings = [];
|
||||||
var unicodeUsed = [];
|
var unicodeUsed = [];
|
||||||
var unassignedUnicodeItems = [];
|
var unassignedUnicodeItems = [];
|
||||||
|
var inverseEncoding = [];
|
||||||
|
for (var charcode in encoding)
|
||||||
|
inverseEncoding[encoding[charcode]] = charcode | 0;
|
||||||
for (var i = 0, ii = charsets.length; i < ii; i++) {
|
for (var i = 0, ii = charsets.length; i < ii; i++) {
|
||||||
var glyph = charsets[i];
|
var glyph = charsets[i];
|
||||||
var encodingFound = false;
|
if (glyph == '.notdef') {
|
||||||
for (var charcode in encoding) {
|
charstrings.push({
|
||||||
if (encoding[charcode] == i) {
|
unicode: 0,
|
||||||
var code = charcode | 0;
|
code: 0,
|
||||||
charstrings.push({
|
gid: i,
|
||||||
unicode: adaptUnicode(code),
|
glyph: glyph
|
||||||
code: code,
|
});
|
||||||
gid: i,
|
continue;
|
||||||
glyph: glyph
|
|
||||||
});
|
|
||||||
unicodeUsed[code] = true;
|
|
||||||
encodingFound = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!encodingFound) {
|
var code = inverseEncoding[i];
|
||||||
|
if (!code || isSpecialUnicode(code)) {
|
||||||
unassignedUnicodeItems.push(i);
|
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) {
|
for (var j = 0, jj = unassignedUnicodeItems.length; j < jj; ++j) {
|
||||||
var i = unassignedUnicodeItems[j];
|
var i = unassignedUnicodeItems[j];
|
||||||
// giving unicode value anyway
|
// giving unicode value anyway
|
||||||
while (unicodeUsed[nextUnusedUnicode])
|
while (nextUnusedUnicode in unicodeUsed)
|
||||||
nextUnusedUnicode++;
|
nextUnusedUnicode++;
|
||||||
var code = nextUnusedUnicode++;
|
var unicode = nextUnusedUnicode++;
|
||||||
charstrings.push({
|
charstrings.push({
|
||||||
unicode: adaptUnicode(code),
|
unicode: unicode,
|
||||||
code: code,
|
code: inverseEncoding[i] || 0,
|
||||||
gid: i,
|
gid: i,
|
||||||
glyph: charsets[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';
|
'use strict';
|
||||||
|
|
||||||
var PDFFunction = (function pdfFunction() {
|
var PDFFunction = (function PDFFunctionClosure() {
|
||||||
var CONSTRUCT_SAMPLED = 0;
|
var CONSTRUCT_SAMPLED = 0;
|
||||||
var CONSTRUCT_INTERPOLATED = 2;
|
var CONSTRUCT_INTERPOLATED = 2;
|
||||||
var CONSTRUCT_STICHED = 3;
|
var CONSTRUCT_STICHED = 3;
|
||||||
@ -270,7 +270,6 @@ var PDFFunction = (function pdfFunction() {
|
|||||||
|
|
||||||
constructStiched: function pdfFunctionConstructStiched(fn, dict, xref) {
|
constructStiched: function pdfFunctionConstructStiched(fn, dict, xref) {
|
||||||
var domain = dict.get('Domain');
|
var domain = dict.get('Domain');
|
||||||
var range = dict.get('Range');
|
|
||||||
|
|
||||||
if (!domain)
|
if (!domain)
|
||||||
error('No domain');
|
error('No domain');
|
||||||
@ -279,13 +278,13 @@ var PDFFunction = (function pdfFunction() {
|
|||||||
if (inputSize != 1)
|
if (inputSize != 1)
|
||||||
error('Bad domain for stiched function');
|
error('Bad domain for stiched function');
|
||||||
|
|
||||||
var fnRefs = dict.get('Functions');
|
var fnRefs = xref.fetchIfRef(dict.get('Functions'));
|
||||||
var fns = [];
|
var fns = [];
|
||||||
for (var i = 0, ii = fnRefs.length; i < ii; ++i)
|
for (var i = 0, ii = fnRefs.length; i < ii; ++i)
|
||||||
fns.push(PDFFunction.getIR(xref, xref.fetchIfRef(fnRefs[i])));
|
fns.push(PDFFunction.getIR(xref, xref.fetchIfRef(fnRefs[i])));
|
||||||
|
|
||||||
var bounds = dict.get('Bounds');
|
var bounds = xref.fetchIfRef(dict.get('Bounds'));
|
||||||
var encode = dict.get('Encode');
|
var encode = xref.fetchIfRef(dict.get('Encode'));
|
||||||
|
|
||||||
return [CONSTRUCT_STICHED, domain, bounds, encode, fns];
|
return [CONSTRUCT_STICHED, domain, bounds, encode, fns];
|
||||||
},
|
},
|
||||||
@ -336,16 +335,550 @@ var PDFFunction = (function pdfFunction() {
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
constructPostScript: function pdfFunctionConstructPostScript() {
|
constructPostScript: function pdfFunctionConstructPostScript(fn, dict,
|
||||||
return [CONSTRUCT_POSTSCRIPT];
|
xref) {
|
||||||
|
var domain = dict.get('Domain');
|
||||||
|
var range = dict.get('Range');
|
||||||
|
|
||||||
|
if (!domain)
|
||||||
|
error('No domain.');
|
||||||
|
|
||||||
|
if (!range)
|
||||||
|
error('No range.');
|
||||||
|
|
||||||
|
var lexer = new PostScriptLexer(fn);
|
||||||
|
var parser = new PostScriptParser(lexer);
|
||||||
|
var code = parser.parse();
|
||||||
|
|
||||||
|
return [CONSTRUCT_POSTSCRIPT, domain, range, code];
|
||||||
},
|
},
|
||||||
|
|
||||||
constructPostScriptFromIR: function pdfFunctionConstructPostScriptFromIR() {
|
constructPostScriptFromIR:
|
||||||
TODO('unhandled type of function');
|
function pdfFunctionConstructPostScriptFromIR(IR) {
|
||||||
return function constructPostScriptFromIRResult() {
|
var domain = IR[1];
|
||||||
return [255, 105, 180];
|
var range = IR[2];
|
||||||
|
var code = IR[3];
|
||||||
|
var numOutputs = range.length / 2;
|
||||||
|
var evaluator = new PostScriptEvaluator(code);
|
||||||
|
// Cache the values for a big speed up, the cache size is limited though
|
||||||
|
// since the number of possible values can be huge from a PS function.
|
||||||
|
var cache = new FunctionCache();
|
||||||
|
return function constructPostScriptFromIRResult(args) {
|
||||||
|
var initialStack = [];
|
||||||
|
for (var i = 0, ii = (domain.length / 2); i < ii; ++i) {
|
||||||
|
initialStack.push(args[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
var key = initialStack.join('_');
|
||||||
|
if (cache.has(key))
|
||||||
|
return cache.get(key);
|
||||||
|
|
||||||
|
var stack = evaluator.execute(initialStack);
|
||||||
|
var transformed = new Array(numOutputs);
|
||||||
|
for (i = numOutputs - 1; i >= 0; --i) {
|
||||||
|
var out = stack.pop();
|
||||||
|
var rangeIndex = 2 * i;
|
||||||
|
if (out < range[rangeIndex])
|
||||||
|
out = range[rangeIndex];
|
||||||
|
else if (out > range[rangeIndex + 1])
|
||||||
|
out = range[rangeIndex + 1];
|
||||||
|
transformed[i] = out;
|
||||||
|
}
|
||||||
|
cache.set(key, transformed);
|
||||||
|
return transformed;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
var FunctionCache = (function FunctionCacheClosure() {
|
||||||
|
// Of 10 PDF's with type4 functions the maxium number of distinct values seen
|
||||||
|
// was 256. This still may need some tweaking in the future though.
|
||||||
|
var MAX_CACHE_SIZE = 1024;
|
||||||
|
function FunctionCache() {
|
||||||
|
this.cache = {};
|
||||||
|
this.total = 0;
|
||||||
|
}
|
||||||
|
FunctionCache.prototype = {
|
||||||
|
has: function has(key) {
|
||||||
|
return key in this.cache;
|
||||||
|
},
|
||||||
|
get: function get(key) {
|
||||||
|
return this.cache[key];
|
||||||
|
},
|
||||||
|
set: function set(key, value) {
|
||||||
|
if (this.total < MAX_CACHE_SIZE) {
|
||||||
|
this.cache[key] = value;
|
||||||
|
this.total++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return FunctionCache;
|
||||||
|
})();
|
||||||
|
|
||||||
|
var PostScriptStack = (function PostScriptStackClosure() {
|
||||||
|
var MAX_STACK_SIZE = 100;
|
||||||
|
function PostScriptStack(initialStack) {
|
||||||
|
this.stack = initialStack || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
PostScriptStack.prototype = {
|
||||||
|
push: function push(value) {
|
||||||
|
if (this.stack.length >= MAX_STACK_SIZE)
|
||||||
|
error('PostScript function stack overflow.');
|
||||||
|
this.stack.push(value);
|
||||||
|
},
|
||||||
|
pop: function pop() {
|
||||||
|
if (this.stack.length <= 0)
|
||||||
|
error('PostScript function stack underflow.');
|
||||||
|
return this.stack.pop();
|
||||||
|
},
|
||||||
|
copy: function copy(n) {
|
||||||
|
if (this.stack.length + n >= MAX_STACK_SIZE)
|
||||||
|
error('PostScript function stack overflow.');
|
||||||
|
var stack = this.stack;
|
||||||
|
for (var i = stack.length - n, j = n - 1; j >= 0; j--, i++)
|
||||||
|
stack.push(stack[i]);
|
||||||
|
},
|
||||||
|
index: function index(n) {
|
||||||
|
this.push(this.stack[this.stack.length - n - 1]);
|
||||||
|
},
|
||||||
|
// rotate the last n stack elements p times
|
||||||
|
roll: function roll(n, p) {
|
||||||
|
var stack = this.stack;
|
||||||
|
var l = stack.length - n;
|
||||||
|
var r = stack.length - 1, c = l + (p - Math.floor(p / n) * n), i, j, t;
|
||||||
|
for (i = l, j = r; i < j; i++, j--) {
|
||||||
|
t = stack[i]; stack[i] = stack[j]; stack[j] = t;
|
||||||
|
}
|
||||||
|
for (i = l, j = c - 1; i < j; i++, j--) {
|
||||||
|
t = stack[i]; stack[i] = stack[j]; stack[j] = t;
|
||||||
|
}
|
||||||
|
for (i = c, j = r; i < j; i++, j--) {
|
||||||
|
t = stack[i]; stack[i] = stack[j]; stack[j] = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return PostScriptStack;
|
||||||
|
})();
|
||||||
|
var PostScriptEvaluator = (function PostScriptEvaluatorClosure() {
|
||||||
|
function PostScriptEvaluator(operators, operands) {
|
||||||
|
this.operators = operators;
|
||||||
|
this.operands = operands;
|
||||||
|
}
|
||||||
|
PostScriptEvaluator.prototype = {
|
||||||
|
execute: function execute(initialStack) {
|
||||||
|
var stack = new PostScriptStack(initialStack);
|
||||||
|
var counter = 0;
|
||||||
|
var operators = this.operators;
|
||||||
|
var length = operators.length;
|
||||||
|
var operator, a, b;
|
||||||
|
while (counter < length) {
|
||||||
|
operator = operators[counter++];
|
||||||
|
if (typeof operator == 'number') {
|
||||||
|
// Operator is really an operand and should be pushed to the stack.
|
||||||
|
stack.push(operator);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
switch (operator) {
|
||||||
|
// non standard ps operators
|
||||||
|
case 'jz': // jump if false
|
||||||
|
b = stack.pop();
|
||||||
|
a = stack.pop();
|
||||||
|
if (!a)
|
||||||
|
counter = b;
|
||||||
|
break;
|
||||||
|
case 'j': // jump
|
||||||
|
a = stack.pop();
|
||||||
|
counter = a;
|
||||||
|
break;
|
||||||
|
|
||||||
|
// all ps operators in alphabetical order (excluding if/ifelse)
|
||||||
|
case 'abs':
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(Math.abs(a));
|
||||||
|
break;
|
||||||
|
case 'add':
|
||||||
|
b = stack.pop();
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(a + b);
|
||||||
|
break;
|
||||||
|
case 'and':
|
||||||
|
b = stack.pop();
|
||||||
|
a = stack.pop();
|
||||||
|
if (isBool(a) && isBool(b))
|
||||||
|
stack.push(a && b);
|
||||||
|
else
|
||||||
|
stack.push(a & b);
|
||||||
|
break;
|
||||||
|
case 'atan':
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(Math.atan(a));
|
||||||
|
break;
|
||||||
|
case 'bitshift':
|
||||||
|
b = stack.pop();
|
||||||
|
a = stack.pop();
|
||||||
|
if (a > 0)
|
||||||
|
stack.push(a << b);
|
||||||
|
else
|
||||||
|
stack.push(a >> b);
|
||||||
|
break;
|
||||||
|
case 'ceiling':
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(Math.ceil(a));
|
||||||
|
break;
|
||||||
|
case 'copy':
|
||||||
|
a = stack.pop();
|
||||||
|
stack.copy(a);
|
||||||
|
break;
|
||||||
|
case 'cos':
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(Math.cos(a));
|
||||||
|
break;
|
||||||
|
case 'cvi':
|
||||||
|
a = stack.pop() | 0;
|
||||||
|
stack.push(a);
|
||||||
|
break;
|
||||||
|
case 'cvr':
|
||||||
|
// noop
|
||||||
|
break;
|
||||||
|
case 'div':
|
||||||
|
b = stack.pop();
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(a / b);
|
||||||
|
break;
|
||||||
|
case 'dup':
|
||||||
|
stack.copy(1);
|
||||||
|
break;
|
||||||
|
case 'eq':
|
||||||
|
b = stack.pop();
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(a == b);
|
||||||
|
break;
|
||||||
|
case 'exch':
|
||||||
|
stack.roll(2, 1);
|
||||||
|
break;
|
||||||
|
case 'exp':
|
||||||
|
b = stack.pop();
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(Math.pow(a, b));
|
||||||
|
break;
|
||||||
|
case 'false':
|
||||||
|
stack.push(false);
|
||||||
|
break;
|
||||||
|
case 'floor':
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(Math.floor(a));
|
||||||
|
break;
|
||||||
|
case 'ge':
|
||||||
|
b = stack.pop();
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(a >= b);
|
||||||
|
break;
|
||||||
|
case 'gt':
|
||||||
|
b = stack.pop();
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(a > b);
|
||||||
|
break;
|
||||||
|
case 'idiv':
|
||||||
|
b = stack.pop();
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push((a / b) | 0);
|
||||||
|
break;
|
||||||
|
case 'index':
|
||||||
|
a = stack.pop();
|
||||||
|
stack.index(a);
|
||||||
|
break;
|
||||||
|
case 'le':
|
||||||
|
b = stack.pop();
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(a <= b);
|
||||||
|
break;
|
||||||
|
case 'ln':
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(Math.log(a));
|
||||||
|
break;
|
||||||
|
case 'log':
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(Math.log(a) / Math.LN10);
|
||||||
|
break;
|
||||||
|
case 'lt':
|
||||||
|
b = stack.pop();
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(a < b);
|
||||||
|
break;
|
||||||
|
case 'mod':
|
||||||
|
b = stack.pop();
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(a % b);
|
||||||
|
break;
|
||||||
|
case 'mul':
|
||||||
|
b = stack.pop();
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(a * b);
|
||||||
|
break;
|
||||||
|
case 'ne':
|
||||||
|
b = stack.pop();
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(a != b);
|
||||||
|
break;
|
||||||
|
case 'neg':
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(-b);
|
||||||
|
break;
|
||||||
|
case 'not':
|
||||||
|
a = stack.pop();
|
||||||
|
if (isBool(a) && isBool(b))
|
||||||
|
stack.push(a && b);
|
||||||
|
else
|
||||||
|
stack.push(a & b);
|
||||||
|
break;
|
||||||
|
case 'or':
|
||||||
|
b = stack.pop();
|
||||||
|
a = stack.pop();
|
||||||
|
if (isBool(a) && isBool(b))
|
||||||
|
stack.push(a || b);
|
||||||
|
else
|
||||||
|
stack.push(a | b);
|
||||||
|
break;
|
||||||
|
case 'pop':
|
||||||
|
stack.pop();
|
||||||
|
break;
|
||||||
|
case 'roll':
|
||||||
|
b = stack.pop();
|
||||||
|
a = stack.pop();
|
||||||
|
stack.roll(a, b);
|
||||||
|
break;
|
||||||
|
case 'round':
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(Math.round(a));
|
||||||
|
break;
|
||||||
|
case 'sin':
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(Math.sin(a));
|
||||||
|
break;
|
||||||
|
case 'sqrt':
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(Math.sqrt(a));
|
||||||
|
break;
|
||||||
|
case 'sub':
|
||||||
|
b = stack.pop();
|
||||||
|
a = stack.pop();
|
||||||
|
stack.push(a - b);
|
||||||
|
break;
|
||||||
|
case 'true':
|
||||||
|
stack.push(true);
|
||||||
|
break;
|
||||||
|
case 'truncate':
|
||||||
|
a = stack.pop();
|
||||||
|
a = a < 0 ? Math.ceil(a) : Math.floor(a);
|
||||||
|
stack.push(a);
|
||||||
|
break;
|
||||||
|
case 'xor':
|
||||||
|
b = stack.pop();
|
||||||
|
a = stack.pop();
|
||||||
|
if (isBool(a) && isBool(b))
|
||||||
|
stack.push(a != b);
|
||||||
|
else
|
||||||
|
stack.push(a ^ b);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
error('Unknown operator ' + operator);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return stack.stack;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return PostScriptEvaluator;
|
||||||
|
})();
|
||||||
|
|
||||||
|
var PostScriptParser = (function PostScriptParserClosure() {
|
||||||
|
function PostScriptParser(lexer) {
|
||||||
|
this.lexer = lexer;
|
||||||
|
this.operators = [];
|
||||||
|
this.token;
|
||||||
|
this.prev;
|
||||||
|
}
|
||||||
|
PostScriptParser.prototype = {
|
||||||
|
nextToken: function nextToken() {
|
||||||
|
this.prev = this.token;
|
||||||
|
this.token = this.lexer.getToken();
|
||||||
|
},
|
||||||
|
accept: function accept(type) {
|
||||||
|
if (this.token.type == type) {
|
||||||
|
this.nextToken();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
expect: function expect(type) {
|
||||||
|
if (this.accept(type))
|
||||||
|
return true;
|
||||||
|
error('Unexpected symbol: found ' + this.token.type + ' expected ' +
|
||||||
|
type + '.');
|
||||||
|
},
|
||||||
|
parse: function parse() {
|
||||||
|
this.nextToken();
|
||||||
|
this.expect(PostScriptTokenTypes.LBRACE);
|
||||||
|
this.parseBlock();
|
||||||
|
this.expect(PostScriptTokenTypes.RBRACE);
|
||||||
|
return this.operators;
|
||||||
|
},
|
||||||
|
parseBlock: function parseBlock() {
|
||||||
|
while (true) {
|
||||||
|
if (this.accept(PostScriptTokenTypes.NUMBER)) {
|
||||||
|
this.operators.push(this.prev.value);
|
||||||
|
} else if (this.accept(PostScriptTokenTypes.OPERATOR)) {
|
||||||
|
this.operators.push(this.prev.value);
|
||||||
|
} else if (this.accept(PostScriptTokenTypes.LBRACE)) {
|
||||||
|
this.parseCondition();
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
parseCondition: function parseCondition() {
|
||||||
|
// Add two place holders that will be updated later
|
||||||
|
var conditionLocation = this.operators.length;
|
||||||
|
this.operators.push(null, null);
|
||||||
|
|
||||||
|
this.parseBlock();
|
||||||
|
this.expect(PostScriptTokenTypes.RBRACE);
|
||||||
|
if (this.accept(PostScriptTokenTypes.IF)) {
|
||||||
|
// The true block is right after the 'if' so it just falls through on
|
||||||
|
// true else it jumps and skips the true block.
|
||||||
|
this.operators[conditionLocation] = this.operators.length;
|
||||||
|
this.operators[conditionLocation + 1] = 'jz';
|
||||||
|
} else if (this.accept(PostScriptTokenTypes.LBRACE)) {
|
||||||
|
var jumpLocation = this.operators.length;
|
||||||
|
this.operators.push(null, null);
|
||||||
|
var endOfTrue = this.operators.length;
|
||||||
|
this.parseBlock();
|
||||||
|
this.expect(PostScriptTokenTypes.RBRACE);
|
||||||
|
this.expect(PostScriptTokenTypes.IFELSE);
|
||||||
|
// The jump is added at the end of the true block to skip the false
|
||||||
|
// block.
|
||||||
|
this.operators[jumpLocation] = this.operators.length;
|
||||||
|
this.operators[jumpLocation + 1] = 'j';
|
||||||
|
|
||||||
|
this.operators[conditionLocation] = endOfTrue;
|
||||||
|
this.operators[conditionLocation + 1] = 'jz';
|
||||||
|
} else {
|
||||||
|
error('PS Function: error parsing conditional.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return PostScriptParser;
|
||||||
|
})();
|
||||||
|
|
||||||
|
var PostScriptTokenTypes = {
|
||||||
|
LBRACE: 0,
|
||||||
|
RBRACE: 1,
|
||||||
|
NUMBER: 2,
|
||||||
|
OPERATOR: 3,
|
||||||
|
IF: 4,
|
||||||
|
IFELSE: 5
|
||||||
|
};
|
||||||
|
|
||||||
|
var PostScriptToken = (function PostScriptTokenClosure() {
|
||||||
|
function PostScriptToken(type, value) {
|
||||||
|
this.type = type;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
var opCache = {};
|
||||||
|
|
||||||
|
PostScriptToken.getOperator = function getOperator(op) {
|
||||||
|
var opValue = opCache[op];
|
||||||
|
if (opValue)
|
||||||
|
return opValue;
|
||||||
|
|
||||||
|
return opCache[op] = new PostScriptToken(PostScriptTokenTypes.OPERATOR, op);
|
||||||
|
};
|
||||||
|
|
||||||
|
PostScriptToken.LBRACE = new PostScriptToken(PostScriptTokenTypes.LBRACE,
|
||||||
|
'{');
|
||||||
|
PostScriptToken.RBRACE = new PostScriptToken(PostScriptTokenTypes.RBRACE,
|
||||||
|
'}');
|
||||||
|
PostScriptToken.IF = new PostScriptToken(PostScriptTokenTypes.IF, 'IF');
|
||||||
|
PostScriptToken.IFELSE = new PostScriptToken(PostScriptTokenTypes.IFELSE,
|
||||||
|
'IFELSE');
|
||||||
|
return PostScriptToken;
|
||||||
|
})();
|
||||||
|
|
||||||
|
var PostScriptLexer = (function PostScriptLexerClosure() {
|
||||||
|
function PostScriptLexer(stream) {
|
||||||
|
this.stream = stream;
|
||||||
|
}
|
||||||
|
PostScriptLexer.prototype = {
|
||||||
|
getToken: function getToken() {
|
||||||
|
var s = '';
|
||||||
|
var ch;
|
||||||
|
var comment = false;
|
||||||
|
var stream = this.stream;
|
||||||
|
|
||||||
|
// skip comments
|
||||||
|
while (true) {
|
||||||
|
if (!(ch = stream.getChar()))
|
||||||
|
return EOF;
|
||||||
|
|
||||||
|
if (comment) {
|
||||||
|
if (ch == '\x0a' || ch == '\x0d')
|
||||||
|
comment = false;
|
||||||
|
} else if (ch == '%') {
|
||||||
|
comment = true;
|
||||||
|
} else if (!Lexer.isSpace(ch)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch (ch) {
|
||||||
|
case '0': case '1': case '2': case '3': case '4':
|
||||||
|
case '5': case '6': case '7': case '8': case '9':
|
||||||
|
case '+': case '-': case '.':
|
||||||
|
return new PostScriptToken(PostScriptTokenTypes.NUMBER,
|
||||||
|
this.getNumber(ch));
|
||||||
|
case '{':
|
||||||
|
return PostScriptToken.LBRACE;
|
||||||
|
case '}':
|
||||||
|
return PostScriptToken.RBRACE;
|
||||||
|
}
|
||||||
|
// operator
|
||||||
|
var str = ch.toLowerCase();
|
||||||
|
while (true) {
|
||||||
|
ch = stream.lookChar().toLowerCase();
|
||||||
|
if (ch >= 'a' && ch <= 'z')
|
||||||
|
str += ch;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
stream.skip();
|
||||||
|
}
|
||||||
|
switch (str) {
|
||||||
|
case 'if':
|
||||||
|
return PostScriptToken.IF;
|
||||||
|
case 'ifelse':
|
||||||
|
return PostScriptToken.IFELSE;
|
||||||
|
default:
|
||||||
|
return PostScriptToken.getOperator(str);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getNumber: function getNumber(ch) {
|
||||||
|
var str = ch;
|
||||||
|
var stream = this.stream;
|
||||||
|
while (true) {
|
||||||
|
ch = stream.lookChar();
|
||||||
|
if ((ch >= '0' && ch <= '9') || ch == '-' || ch == '.')
|
||||||
|
str += ch;
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
stream.skip();
|
||||||
|
}
|
||||||
|
var value = parseFloat(str);
|
||||||
|
if (isNaN(value))
|
||||||
|
error('Invalid floating point number: ' + value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return PostScriptLexer;
|
||||||
|
})();
|
||||||
|
|
||||||
|
@ -4287,6 +4287,7 @@ var GlyphsUnicode = {
|
|||||||
zretroflexhook: 0x0290,
|
zretroflexhook: 0x0290,
|
||||||
zstroke: 0x01B6,
|
zstroke: 0x01B6,
|
||||||
zuhiragana: 0x305A,
|
zuhiragana: 0x305A,
|
||||||
zukatakana: 0x30BA
|
zukatakana: 0x30BA,
|
||||||
|
'.notdef': 0x0000
|
||||||
};
|
};
|
||||||
|
|
||||||
|
262
src/image.js
262
src/image.js
@ -3,8 +3,37 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var PDFImage = (function pdfImage() {
|
var PDFImage = (function PDFImageClosure() {
|
||||||
function constructor(xref, res, image, inline) {
|
/**
|
||||||
|
* 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;
|
this.image = image;
|
||||||
if (image.getParams) {
|
if (image.getParams) {
|
||||||
// JPX/JPEG2000 streams directly contain bits per component
|
// JPX/JPEG2000 streams directly contain bits per component
|
||||||
@ -49,34 +78,142 @@ var PDFImage = (function pdfImage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this.decode = dict.get('Decode', 'D');
|
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 mask = xref.fetchIfRef(dict.get('Mask'));
|
||||||
var smask = xref.fetchIfRef(dict.get('SMask'));
|
|
||||||
|
|
||||||
if (mask) {
|
if (mask) {
|
||||||
TODO('masked images');
|
TODO('masked images');
|
||||||
} else if (smask) {
|
} 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 = {
|
handleImageData(handler, xref, res, image, imageDataPromise);
|
||||||
getComponents: function getComponents(buffer, decodeMap) {
|
|
||||||
|
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;
|
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;
|
return buffer;
|
||||||
|
|
||||||
|
var bufferLength = buffer.length;
|
||||||
var width = this.width;
|
var width = this.width;
|
||||||
var height = this.height;
|
var height = this.height;
|
||||||
var numComps = this.numComps;
|
var numComps = this.numComps;
|
||||||
|
|
||||||
var length = width * height;
|
var length = width * height * numComps;
|
||||||
var bufferPos = 0;
|
var bufferPos = 0;
|
||||||
var output = bpc <= 8 ? new Uint8Array(length) :
|
var output = bpc <= 8 ? new Uint8Array(length) :
|
||||||
bpc <= 16 ? new Uint16Array(length) : new Uint32Array(length);
|
bpc <= 16 ? new Uint16Array(length) : new Uint32Array(length);
|
||||||
var rowComps = width * numComps;
|
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;
|
var valueZero = 0, valueOne = 1;
|
||||||
if (decodeMap) {
|
if (decodeMap) {
|
||||||
valueZero = decodeMap[0] ? 1 : 0;
|
valueZero = decodeMap[0] ? 1 : 0;
|
||||||
@ -101,8 +238,7 @@ var PDFImage = (function pdfImage() {
|
|||||||
output[i] = !(buf & mask) ? valueZero : valueOne;
|
output[i] = !(buf & mask) ? valueZero : valueOne;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (decodeMap != null)
|
// The general case that handles all other bpc values.
|
||||||
TODO('interpolate component values');
|
|
||||||
var bits = 0, buf = 0;
|
var bits = 0, buf = 0;
|
||||||
for (var i = 0, ii = length; i < ii; ++i) {
|
for (var i = 0, ii = length; i < ii; ++i) {
|
||||||
if (i % rowComps == 0) {
|
if (i % rowComps == 0) {
|
||||||
@ -116,41 +252,34 @@ var PDFImage = (function pdfImage() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var remainingBits = bits - bpc;
|
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);
|
buf = buf & ((1 << remainingBits) - 1);
|
||||||
bits = remainingBits;
|
bits = remainingBits;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return output;
|
return output;
|
||||||
},
|
},
|
||||||
getOpacity: function getOpacity() {
|
getOpacity: function getOpacity(width, height) {
|
||||||
var smask = this.smask;
|
var smask = this.smask;
|
||||||
var width = this.width;
|
var originalWidth = this.width;
|
||||||
var height = this.height;
|
var originalHeight = this.height;
|
||||||
var buf = new Uint8Array(width * height);
|
var buf;
|
||||||
|
|
||||||
if (smask) {
|
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 sw = smask.width;
|
||||||
var sh = smask.height;
|
var sh = smask.height;
|
||||||
if (sw != this.width || sh != this.height)
|
buf = new Uint8Array(sw * sh);
|
||||||
error('smask dimensions do not match image dimensions: ' + sw +
|
|
||||||
' != ' + this.width + ', ' + sh + ' != ' + this.height);
|
|
||||||
|
|
||||||
smask.fillGrayBuffer(buf);
|
smask.fillGrayBuffer(buf);
|
||||||
return buf;
|
if (sw != width || sh != height)
|
||||||
|
buf = PDFImage.resize(buf, smask.bps, 1, sw, sh, width, height);
|
||||||
} else {
|
} else {
|
||||||
|
buf = new Uint8Array(width * height);
|
||||||
for (var i = 0, ii = width * height; i < ii; ++i)
|
for (var i = 0, ii = width * height; i < ii; ++i)
|
||||||
buf[i] = 255;
|
buf[i] = 255;
|
||||||
}
|
}
|
||||||
@ -159,8 +288,7 @@ var PDFImage = (function pdfImage() {
|
|||||||
applyStencilMask: function applyStencilMask(buffer, inverseDecode) {
|
applyStencilMask: function applyStencilMask(buffer, inverseDecode) {
|
||||||
var width = this.width, height = this.height;
|
var width = this.width, height = this.height;
|
||||||
var bitStrideLength = (width + 7) >> 3;
|
var bitStrideLength = (width + 7) >> 3;
|
||||||
this.image.reset();
|
var imgArray = this.getImageBytes(bitStrideLength * height);
|
||||||
var imgArray = this.image.getBytes(bitStrideLength * height);
|
|
||||||
var imgArrayPos = 0;
|
var imgArrayPos = 0;
|
||||||
var i, j, mask, buf;
|
var i, j, mask, buf;
|
||||||
// removing making non-masked pixels transparent
|
// 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 numComps = this.numComps;
|
||||||
var width = this.width;
|
var originalWidth = this.width;
|
||||||
var height = this.height;
|
var originalHeight = this.height;
|
||||||
var bpc = this.bpc;
|
var bpc = this.bpc;
|
||||||
|
|
||||||
// rows start at byte boundary;
|
// rows start at byte boundary;
|
||||||
var rowBytes = (width * numComps * bpc + 7) >> 3;
|
var rowBytes = (originalWidth * numComps * bpc + 7) >> 3;
|
||||||
this.image.reset();
|
var imgArray = this.getImageBytes(originalHeight * rowBytes);
|
||||||
var imgArray = this.image.getBytes(height * rowBytes);
|
|
||||||
|
|
||||||
var comps = this.colorSpace.getRgbBuffer(
|
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 compsPos = 0;
|
||||||
var opacity = this.getOpacity();
|
var opacity = this.getOpacity(width, height);
|
||||||
var opacityPos = 0;
|
var opacityPos = 0;
|
||||||
var length = width * height * 4;
|
var length = width * height * 4;
|
||||||
|
|
||||||
@ -216,42 +346,28 @@ var PDFImage = (function pdfImage() {
|
|||||||
|
|
||||||
// rows start at byte boundary;
|
// rows start at byte boundary;
|
||||||
var rowBytes = (width * numComps * bpc + 7) >> 3;
|
var rowBytes = (width * numComps * bpc + 7) >> 3;
|
||||||
this.image.reset();
|
var imgArray = this.getImageBytes(height * rowBytes);
|
||||||
var imgArray = this.image.getBytes(height * rowBytes);
|
|
||||||
|
|
||||||
var comps = this.getComponents(imgArray);
|
var comps = this.getComponents(imgArray);
|
||||||
var length = width * height;
|
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)
|
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 loadJpegStream(id, imageData, objs) {
|
||||||
function JpegImageLoader(objId, imageData, objs) {
|
var img = new Image();
|
||||||
var src = 'data:image/jpeg;base64,' + window.btoa(imageData);
|
img.onload = (function jpegImageLoaderOnload() {
|
||||||
|
objs.resolve(id, img);
|
||||||
var img = new Image();
|
});
|
||||||
img.onload = (function jpegImageLoaderOnload() {
|
img.src = 'data:image/jpeg;base64,' + window.btoa(imageData);
|
||||||
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;
|
|
||||||
})();
|
|
||||||
|
|
||||||
|
@ -3,6 +3,9 @@
|
|||||||
|
|
||||||
'use strict';
|
'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 = {
|
var Metrics = {
|
||||||
'Courier': 600,
|
'Courier': 600,
|
||||||
'Courier-Bold': 600,
|
'Courier-Bold': 600,
|
||||||
|
106
src/obj.js
106
src/obj.js
@ -3,34 +3,42 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var Name = (function nameName() {
|
var Name = (function NameClosure() {
|
||||||
function constructor(name) {
|
function Name(name) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor.prototype = {
|
Name.prototype = {};
|
||||||
};
|
|
||||||
|
|
||||||
return constructor;
|
return Name;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
var Cmd = (function cmdCmd() {
|
var Cmd = (function CmdClosure() {
|
||||||
function constructor(cmd) {
|
function Cmd(cmd) {
|
||||||
this.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() {
|
var Dict = (function DictClosure() {
|
||||||
function constructor() {
|
function Dict() {
|
||||||
this.map = Object.create(null);
|
this.map = Object.create(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor.prototype = {
|
Dict.prototype = {
|
||||||
get: function dictGet(key1, key2, key3) {
|
get: function dictGet(key1, key2, key3) {
|
||||||
var value;
|
var value;
|
||||||
if (typeof (value = this.map[key1]) != 'undefined' || key1 in this.map ||
|
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() {
|
var Ref = (function RefClosure() {
|
||||||
function constructor(num, gen) {
|
function Ref(num, gen) {
|
||||||
this.num = num;
|
this.num = num;
|
||||||
this.gen = gen;
|
this.gen = gen;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor.prototype = {
|
Ref.prototype = {};
|
||||||
};
|
|
||||||
|
|
||||||
return constructor;
|
return Ref;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// The reference is identified by number and generation,
|
// The reference is identified by number and generation,
|
||||||
// this structure stores only one instance of the reference.
|
// this structure stores only one instance of the reference.
|
||||||
var RefSet = (function refSet() {
|
var RefSet = (function RefSetClosure() {
|
||||||
function constructor() {
|
function RefSet() {
|
||||||
this.dict = {};
|
this.dict = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor.prototype = {
|
RefSet.prototype = {
|
||||||
has: function refSetHas(ref) {
|
has: function refSetHas(ref) {
|
||||||
return !!this.dict['R' + ref.num + '.' + ref.gen];
|
return !!this.dict['R' + ref.num + '.' + ref.gen];
|
||||||
},
|
},
|
||||||
@ -92,18 +99,18 @@ var RefSet = (function refSet() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return constructor;
|
return RefSet;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
var Catalog = (function catalogCatalog() {
|
var Catalog = (function CatalogClosure() {
|
||||||
function constructor(xref) {
|
function Catalog(xref) {
|
||||||
this.xref = xref;
|
this.xref = xref;
|
||||||
var obj = xref.getCatalogObj();
|
var obj = xref.getCatalogObj();
|
||||||
assertWellFormed(isDict(obj), 'catalog object is not a dictionary');
|
assertWellFormed(isDict(obj), 'catalog object is not a dictionary');
|
||||||
this.catDict = obj;
|
this.catDict = obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor.prototype = {
|
Catalog.prototype = {
|
||||||
get toplevelPagesDict() {
|
get toplevelPagesDict() {
|
||||||
var pagesObj = this.catDict.get('Pages');
|
var pagesObj = this.catDict.get('Pages');
|
||||||
assertWellFormed(isRef(pagesObj), 'invalid top-level pages reference');
|
assertWellFormed(isRef(pagesObj), 'invalid top-level pages reference');
|
||||||
@ -253,16 +260,16 @@ var Catalog = (function catalogCatalog() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return constructor;
|
return Catalog;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
var XRef = (function xRefXRef() {
|
var XRef = (function XRefClosure() {
|
||||||
function constructor(stream, startXRef, mainXRefEntriesOffset) {
|
function XRef(stream, startXRef, mainXRefEntriesOffset) {
|
||||||
this.stream = stream;
|
this.stream = stream;
|
||||||
this.entries = [];
|
this.entries = [];
|
||||||
this.xrefstms = {};
|
this.xrefstms = {};
|
||||||
var trailerDict = this.readXRef(startXRef);
|
var trailerDict = this.readXRef(startXRef);
|
||||||
|
this.trailer = trailerDict;
|
||||||
// prepare the XRef cache
|
// prepare the XRef cache
|
||||||
this.cache = [];
|
this.cache = [];
|
||||||
|
|
||||||
@ -278,7 +285,7 @@ var XRef = (function xRefXRef() {
|
|||||||
error('Invalid root reference');
|
error('Invalid root reference');
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor.prototype = {
|
XRef.prototype = {
|
||||||
readXRefTable: function readXRefTable(parser) {
|
readXRefTable: function readXRefTable(parser) {
|
||||||
var obj;
|
var obj;
|
||||||
while (true) {
|
while (true) {
|
||||||
@ -518,20 +525,29 @@ var XRef = (function xRefXRef() {
|
|||||||
readXRef: function readXref(startXRef) {
|
readXRef: function readXref(startXRef) {
|
||||||
var stream = this.stream;
|
var stream = this.stream;
|
||||||
stream.pos = startXRef;
|
stream.pos = startXRef;
|
||||||
var parser = new Parser(new Lexer(stream), true);
|
|
||||||
var obj = parser.getObj();
|
try {
|
||||||
// parse an old-style xref table
|
var parser = new Parser(new Lexer(stream), true);
|
||||||
if (isCmd(obj, 'xref'))
|
var obj = parser.getObj();
|
||||||
return this.readXRefTable(parser);
|
|
||||||
// parse an xref stream
|
// parse an old-style xref table
|
||||||
if (isInt(obj)) {
|
if (isCmd(obj, 'xref'))
|
||||||
if (!isInt(parser.getObj()) ||
|
return this.readXRefTable(parser);
|
||||||
!isCmd(parser.getObj(), 'obj') ||
|
|
||||||
!isStream(obj = parser.getObj())) {
|
// parse an xref stream
|
||||||
error('Invalid 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();
|
return this.indexObjects();
|
||||||
},
|
},
|
||||||
getEntry: function xRefGetEntry(i) {
|
getEntry: function xRefGetEntry(i) {
|
||||||
@ -589,7 +605,7 @@ var XRef = (function xRefXRef() {
|
|||||||
e = parser.getObj();
|
e = parser.getObj();
|
||||||
}
|
}
|
||||||
// Don't cache streams since they are mutable (except images).
|
// 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;
|
this.cache[num] = e;
|
||||||
return 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
|
* inside of a worker. The `PDFObjects` implements some basic functions to
|
||||||
* manage these objects.
|
* manage these objects.
|
||||||
*/
|
*/
|
||||||
var PDFObjects = (function pdfObjects() {
|
var PDFObjects = (function PDFObjectsClosure() {
|
||||||
function PDFObjects() {
|
function PDFObjects() {
|
||||||
this.objs = {};
|
this.objs = {};
|
||||||
}
|
}
|
||||||
|
@ -9,8 +9,8 @@ function isEOF(v) {
|
|||||||
return v == EOF;
|
return v == EOF;
|
||||||
}
|
}
|
||||||
|
|
||||||
var Parser = (function parserParser() {
|
var Parser = (function ParserClosure() {
|
||||||
function constructor(lexer, allowStreams, xref) {
|
function Parser(lexer, allowStreams, xref) {
|
||||||
this.lexer = lexer;
|
this.lexer = lexer;
|
||||||
this.allowStreams = allowStreams;
|
this.allowStreams = allowStreams;
|
||||||
this.xref = xref;
|
this.xref = xref;
|
||||||
@ -18,7 +18,7 @@ var Parser = (function parserParser() {
|
|||||||
this.refill();
|
this.refill();
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor.prototype = {
|
Parser.prototype = {
|
||||||
refill: function parserRefill() {
|
refill: function parserRefill() {
|
||||||
this.buf1 = this.lexer.getObj();
|
this.buf1 = this.lexer.getObj();
|
||||||
this.buf2 = this.lexer.getObj();
|
this.buf2 = this.lexer.getObj();
|
||||||
@ -157,7 +157,7 @@ var Parser = (function parserParser() {
|
|||||||
imageStream = this.filter(imageStream, dict, length);
|
imageStream = this.filter(imageStream, dict, length);
|
||||||
imageStream.parameters = dict;
|
imageStream.parameters = dict;
|
||||||
|
|
||||||
this.buf2 = new Cmd('EI');
|
this.buf2 = Cmd.get('EI');
|
||||||
this.shift();
|
this.shift();
|
||||||
|
|
||||||
return imageStream;
|
return imageStream;
|
||||||
@ -225,7 +225,8 @@ var Parser = (function parserParser() {
|
|||||||
return new PredictorStream(new FlateStream(stream), params);
|
return new PredictorStream(new FlateStream(stream), params);
|
||||||
}
|
}
|
||||||
return new FlateStream(stream);
|
return new FlateStream(stream);
|
||||||
} else if (name == 'LZWDecode' || name == 'LZW') {
|
}
|
||||||
|
if (name == 'LZWDecode' || name == 'LZW') {
|
||||||
var earlyChange = 1;
|
var earlyChange = 1;
|
||||||
if (params) {
|
if (params) {
|
||||||
if (params.has('EarlyChange'))
|
if (params.has('EarlyChange'))
|
||||||
@ -234,31 +235,34 @@ var Parser = (function parserParser() {
|
|||||||
new LZWStream(stream, earlyChange), params);
|
new LZWStream(stream, earlyChange), params);
|
||||||
}
|
}
|
||||||
return new LZWStream(stream, earlyChange);
|
return new LZWStream(stream, earlyChange);
|
||||||
} else if (name == 'DCTDecode' || name == 'DCT') {
|
}
|
||||||
|
if (name == 'DCTDecode' || name == 'DCT') {
|
||||||
var bytes = stream.getBytes(length);
|
var bytes = stream.getBytes(length);
|
||||||
return new JpegStream(bytes, stream.dict, this.xref);
|
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 stream;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return constructor;
|
return Parser;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
var Lexer = (function lexer() {
|
var Lexer = (function LexerClosure() {
|
||||||
function constructor(stream) {
|
function Lexer(stream) {
|
||||||
this.stream = stream;
|
this.stream = stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor.isSpace = function lexerIsSpace(ch) {
|
Lexer.isSpace = function lexerIsSpace(ch) {
|
||||||
return ch == ' ' || ch == '\t' || ch == '\x0d' || ch == '\x0a';
|
return ch == ' ' || ch == '\t' || ch == '\x0d' || ch == '\x0a';
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -292,7 +296,7 @@ var Lexer = (function lexer() {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor.prototype = {
|
Lexer.prototype = {
|
||||||
getNumber: function lexerGetNumber(ch) {
|
getNumber: function lexerGetNumber(ch) {
|
||||||
var floating = false;
|
var floating = false;
|
||||||
var str = ch;
|
var str = ch;
|
||||||
@ -492,14 +496,14 @@ var Lexer = (function lexer() {
|
|||||||
// array punctuation
|
// array punctuation
|
||||||
case '[':
|
case '[':
|
||||||
case ']':
|
case ']':
|
||||||
return new Cmd(ch);
|
return Cmd.get(ch);
|
||||||
// hex string or dict punctuation
|
// hex string or dict punctuation
|
||||||
case '<':
|
case '<':
|
||||||
ch = stream.lookChar();
|
ch = stream.lookChar();
|
||||||
if (ch == '<') {
|
if (ch == '<') {
|
||||||
// dict punctuation
|
// dict punctuation
|
||||||
stream.skip();
|
stream.skip();
|
||||||
return new Cmd('<<');
|
return Cmd.get('<<');
|
||||||
}
|
}
|
||||||
return this.getHexString(ch);
|
return this.getHexString(ch);
|
||||||
// dict punctuation
|
// dict punctuation
|
||||||
@ -507,11 +511,11 @@ var Lexer = (function lexer() {
|
|||||||
ch = stream.lookChar();
|
ch = stream.lookChar();
|
||||||
if (ch == '>') {
|
if (ch == '>') {
|
||||||
stream.skip();
|
stream.skip();
|
||||||
return new Cmd('>>');
|
return Cmd.get('>>');
|
||||||
}
|
}
|
||||||
case '{':
|
case '{':
|
||||||
case '}':
|
case '}':
|
||||||
return new Cmd(ch);
|
return Cmd.get(ch);
|
||||||
// fall through
|
// fall through
|
||||||
case ')':
|
case ')':
|
||||||
error('Illegal character: ' + ch);
|
error('Illegal character: ' + ch);
|
||||||
@ -534,7 +538,7 @@ var Lexer = (function lexer() {
|
|||||||
return false;
|
return false;
|
||||||
if (str == 'null')
|
if (str == 'null')
|
||||||
return null;
|
return null;
|
||||||
return new Cmd(str);
|
return Cmd.get(str);
|
||||||
},
|
},
|
||||||
skipToNextLine: function lexerSkipToNextLine() {
|
skipToNextLine: function lexerSkipToNextLine() {
|
||||||
var stream = this.stream;
|
var stream = this.stream;
|
||||||
@ -554,11 +558,11 @@ var Lexer = (function lexer() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return constructor;
|
return Lexer;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
var Linearization = (function linearizationLinearization() {
|
var Linearization = (function LinearizationClosure() {
|
||||||
function constructor(stream) {
|
function Linearization(stream) {
|
||||||
this.parser = new Parser(new Lexer(stream), false);
|
this.parser = new Parser(new Lexer(stream), false);
|
||||||
var obj1 = this.parser.getObj();
|
var obj1 = this.parser.getObj();
|
||||||
var obj2 = this.parser.getObj();
|
var obj2 = this.parser.getObj();
|
||||||
@ -572,7 +576,7 @@ var Linearization = (function linearizationLinearization() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor.prototype = {
|
Linearization.prototype = {
|
||||||
getInt: function linearizationGetInt(name) {
|
getInt: function linearizationGetInt(name) {
|
||||||
var linDict = this.linDict;
|
var linDict = this.linDict;
|
||||||
var obj;
|
var obj;
|
||||||
@ -631,6 +635,6 @@ var Linearization = (function linearizationLinearization() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return constructor;
|
return Linearization;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
@ -3,13 +3,18 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var Pattern = (function patternPattern() {
|
var PatternType = {
|
||||||
|
AXIAL: 2,
|
||||||
|
RADIAL: 3
|
||||||
|
};
|
||||||
|
|
||||||
|
var Pattern = (function PatternClosure() {
|
||||||
// Constructor should define this.getPattern
|
// Constructor should define this.getPattern
|
||||||
function constructor() {
|
function Pattern() {
|
||||||
error('should not call Pattern constructor');
|
error('should not call Pattern constructor');
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor.prototype = {
|
Pattern.prototype = {
|
||||||
// Input: current Canvas context
|
// Input: current Canvas context
|
||||||
// Output: the appropriate fillStyle or strokeStyle
|
// Output: the appropriate fillStyle or strokeStyle
|
||||||
getPattern: function pattern_getStyle(ctx) {
|
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);
|
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) {
|
res, ctx) {
|
||||||
|
|
||||||
var dict = isStream(shading) ? shading.dict : shading;
|
var dict = isStream(shading) ? shading.dict : shading;
|
||||||
var type = dict.get('ShadingType');
|
var type = dict.get('ShadingType');
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 2:
|
case PatternType.AXIAL:
|
||||||
case 3:
|
case PatternType.RADIAL:
|
||||||
// both radial and axial shadings are handled by RadialAxial shading
|
// Both radial and axial shadings are handled by RadialAxial shading.
|
||||||
return new Shadings.RadialAxial(dict, matrix, xref, res, ctx);
|
return new Shadings.RadialAxial(dict, matrix, xref, res, ctx);
|
||||||
default:
|
default:
|
||||||
return new Shadings.Dummy();
|
return new Shadings.Dummy();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return constructor;
|
return Pattern;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
var Shadings = {};
|
var Shadings = {};
|
||||||
|
|
||||||
// Radial and axial shading have very similar implementations
|
// Radial and axial shading have very similar implementations
|
||||||
// If needed, the implementations can be broken into two classes
|
// If needed, the implementations can be broken into two classes
|
||||||
Shadings.RadialAxial = (function radialAxialShading() {
|
Shadings.RadialAxial = (function RadialAxialClosure() {
|
||||||
function constructor(dict, matrix, xref, res, ctx) {
|
function RadialAxial(dict, matrix, xref, res, ctx) {
|
||||||
this.matrix = matrix;
|
this.matrix = matrix;
|
||||||
this.coordsArr = dict.get('Coords');
|
this.coordsArr = dict.get('Coords');
|
||||||
this.shadingType = dict.get('ShadingType');
|
this.shadingType = dict.get('ShadingType');
|
||||||
@ -97,7 +102,7 @@ Shadings.RadialAxial = (function radialAxialShading() {
|
|||||||
this.colorStops = colorStops;
|
this.colorStops = colorStops;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor.fromIR = function radialAxialShadingGetIR(ctx, raw) {
|
RadialAxial.fromIR = function radialAxialShadingGetIR(ctx, raw) {
|
||||||
var type = raw[1];
|
var type = raw[1];
|
||||||
var colorStops = raw[2];
|
var colorStops = raw[2];
|
||||||
var p0 = raw[3];
|
var p0 = raw[3];
|
||||||
@ -117,9 +122,9 @@ Shadings.RadialAxial = (function radialAxialShading() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var grad;
|
var grad;
|
||||||
if (type == 2)
|
if (type == PatternType.AXIAL)
|
||||||
grad = ctx.createLinearGradient(p0[0], p0[1], p1[0], p1[1]);
|
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);
|
grad = ctx.createRadialGradient(p0[0], p0[1], r0, p1[0], p1[1], r1);
|
||||||
|
|
||||||
for (var i = 0, ii = colorStops.length; i < ii; ++i) {
|
for (var i = 0, ii = colorStops.length; i < ii; ++i) {
|
||||||
@ -129,16 +134,16 @@ Shadings.RadialAxial = (function radialAxialShading() {
|
|||||||
return grad;
|
return grad;
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor.prototype = {
|
RadialAxial.prototype = {
|
||||||
getIR: function radialAxialShadingGetIR() {
|
getIR: function radialAxialShadingGetIR() {
|
||||||
var coordsArr = this.coordsArr;
|
var coordsArr = this.coordsArr;
|
||||||
var type = this.shadingType;
|
var type = this.shadingType;
|
||||||
if (type == 2) {
|
if (type == PatternType.AXIAL) {
|
||||||
var p0 = [coordsArr[0], coordsArr[1]];
|
var p0 = [coordsArr[0], coordsArr[1]];
|
||||||
var p1 = [coordsArr[2], coordsArr[3]];
|
var p1 = [coordsArr[2], coordsArr[3]];
|
||||||
var r0 = null;
|
var r0 = null;
|
||||||
var r1 = null;
|
var r1 = null;
|
||||||
} else if (type == 3) {
|
} else if (type == PatternType.RADIAL) {
|
||||||
var p0 = [coordsArr[0], coordsArr[1]];
|
var p0 = [coordsArr[0], coordsArr[1]];
|
||||||
var p1 = [coordsArr[3], coordsArr[4]];
|
var p1 = [coordsArr[3], coordsArr[4]];
|
||||||
var r0 = coordsArr[2];
|
var r0 = coordsArr[2];
|
||||||
@ -157,28 +162,32 @@ Shadings.RadialAxial = (function radialAxialShading() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return constructor;
|
return RadialAxial;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
Shadings.Dummy = (function dummyShading() {
|
Shadings.Dummy = (function DummyClosure() {
|
||||||
function constructor() {
|
function Dummy() {
|
||||||
this.type = 'Pattern';
|
this.type = 'Pattern';
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor.fromIR = function dummyShadingFromIR() {
|
Dummy.fromIR = function dummyShadingFromIR() {
|
||||||
return 'hotpink';
|
return 'hotpink';
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor.prototype = {
|
Dummy.prototype = {
|
||||||
getIR: function dummyShadingGetIR() {
|
getIR: function dummyShadingGetIR() {
|
||||||
return ['Dummy'];
|
return ['Dummy'];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
return constructor;
|
return Dummy;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
var TilingPattern = (function tilingPattern() {
|
var TilingPattern = (function TilingPatternClosure() {
|
||||||
var PAINT_TYPE_COLORED = 1, PAINT_TYPE_UNCOLORED = 2;
|
var PaintType = {
|
||||||
|
COLORED: 1,
|
||||||
|
UNCOLORED: 2
|
||||||
|
};
|
||||||
|
var MAX_PATTERN_SIZE = 512;
|
||||||
|
|
||||||
function TilingPattern(IR, color, ctx, objs) {
|
function TilingPattern(IR, color, ctx, objs) {
|
||||||
var IRQueue = IR[2];
|
var IRQueue = IR[2];
|
||||||
@ -204,13 +213,13 @@ var TilingPattern = (function tilingPattern() {
|
|||||||
var width = botRight[0] - topLeft[0];
|
var width = botRight[0] - topLeft[0];
|
||||||
var height = botRight[1] - topLeft[1];
|
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
|
// pattern to be only as large as the acual size in device space
|
||||||
// This could be computed with .mozCurrentTransform, but still
|
// This could be computed with .mozCurrentTransform, but still
|
||||||
// needs to be implemented
|
// needs to be implemented
|
||||||
while (Math.abs(width) > 512 || Math.abs(height) > 512) {
|
while (Math.abs(width) > MAX_PATTERN_SIZE ||
|
||||||
width = 512;
|
Math.abs(height) > MAX_PATTERN_SIZE) {
|
||||||
height = 512;
|
width = height = MAX_PATTERN_SIZE;
|
||||||
}
|
}
|
||||||
|
|
||||||
var tmpCanvas = new ScratchCanvas(width, height);
|
var tmpCanvas = new ScratchCanvas(width, height);
|
||||||
@ -220,11 +229,11 @@ var TilingPattern = (function tilingPattern() {
|
|||||||
var graphics = new CanvasGraphics(tmpCtx, objs);
|
var graphics = new CanvasGraphics(tmpCtx, objs);
|
||||||
|
|
||||||
switch (paintType) {
|
switch (paintType) {
|
||||||
case PAINT_TYPE_COLORED:
|
case PaintType.COLORED:
|
||||||
tmpCtx.fillStyle = ctx.fillStyle;
|
tmpCtx.fillStyle = ctx.fillStyle;
|
||||||
tmpCtx.strokeStyle = ctx.strokeStyle;
|
tmpCtx.strokeStyle = ctx.strokeStyle;
|
||||||
break;
|
break;
|
||||||
case PAINT_TYPE_UNCOLORED:
|
case PaintType.UNCOLORED:
|
||||||
color = Util.makeCssRgb.apply(this, color);
|
color = Util.makeCssRgb.apply(this, color);
|
||||||
tmpCtx.fillStyle = color;
|
tmpCtx.fillStyle = color;
|
||||||
tmpCtx.strokeStyle = color;
|
tmpCtx.strokeStyle = color;
|
||||||
|
184
src/stream.js
184
src/stream.js
@ -3,8 +3,8 @@
|
|||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var Stream = (function streamStream() {
|
var Stream = (function StreamClosure() {
|
||||||
function constructor(arrayBuffer, start, length, dict) {
|
function Stream(arrayBuffer, start, length, dict) {
|
||||||
this.bytes = new Uint8Array(arrayBuffer);
|
this.bytes = new Uint8Array(arrayBuffer);
|
||||||
this.start = start || 0;
|
this.start = start || 0;
|
||||||
this.pos = this.start;
|
this.pos = this.start;
|
||||||
@ -14,7 +14,7 @@ var Stream = (function streamStream() {
|
|||||||
|
|
||||||
// required methods for a stream. if a particular stream does not
|
// required methods for a stream. if a particular stream does not
|
||||||
// implement these, an error should be thrown
|
// implement these, an error should be thrown
|
||||||
constructor.prototype = {
|
Stream.prototype = {
|
||||||
get length() {
|
get length() {
|
||||||
return this.end - this.start;
|
return this.end - this.start;
|
||||||
},
|
},
|
||||||
@ -67,11 +67,11 @@ var Stream = (function streamStream() {
|
|||||||
isStream: true
|
isStream: true
|
||||||
};
|
};
|
||||||
|
|
||||||
return constructor;
|
return Stream;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
var StringStream = (function stringStream() {
|
var StringStream = (function StringStreamClosure() {
|
||||||
function constructor(str) {
|
function StringStream(str) {
|
||||||
var length = str.length;
|
var length = str.length;
|
||||||
var bytes = new Uint8Array(length);
|
var bytes = new Uint8Array(length);
|
||||||
for (var n = 0; n < length; ++n)
|
for (var n = 0; n < length; ++n)
|
||||||
@ -79,21 +79,21 @@ var StringStream = (function stringStream() {
|
|||||||
Stream.call(this, bytes);
|
Stream.call(this, bytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor.prototype = Stream.prototype;
|
StringStream.prototype = Stream.prototype;
|
||||||
|
|
||||||
return constructor;
|
return StringStream;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// super class for the decoding streams
|
// super class for the decoding streams
|
||||||
var DecodeStream = (function decodeStream() {
|
var DecodeStream = (function DecodeStreamClosure() {
|
||||||
function constructor() {
|
function DecodeStream() {
|
||||||
this.pos = 0;
|
this.pos = 0;
|
||||||
this.bufferLength = 0;
|
this.bufferLength = 0;
|
||||||
this.eof = false;
|
this.eof = false;
|
||||||
this.buffer = null;
|
this.buffer = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor.prototype = {
|
DecodeStream.prototype = {
|
||||||
ensureBuffer: function decodestream_ensureBuffer(requested) {
|
ensureBuffer: function decodestream_ensureBuffer(requested) {
|
||||||
var buffer = this.buffer;
|
var buffer = this.buffer;
|
||||||
var current = buffer ? buffer.byteLength : 0;
|
var current = buffer ? buffer.byteLength : 0;
|
||||||
@ -178,24 +178,24 @@ var DecodeStream = (function decodeStream() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return constructor;
|
return DecodeStream;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
var FakeStream = (function fakeStream() {
|
var FakeStream = (function FakeStreamClosure() {
|
||||||
function constructor(stream) {
|
function FakeStream(stream) {
|
||||||
this.dict = stream.dict;
|
this.dict = stream.dict;
|
||||||
DecodeStream.call(this);
|
DecodeStream.call(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor.prototype = Object.create(DecodeStream.prototype);
|
FakeStream.prototype = Object.create(DecodeStream.prototype);
|
||||||
constructor.prototype.readBlock = function fakeStreamReadBlock() {
|
FakeStream.prototype.readBlock = function fakeStreamReadBlock() {
|
||||||
var bufferLength = this.bufferLength;
|
var bufferLength = this.bufferLength;
|
||||||
bufferLength += 1024;
|
bufferLength += 1024;
|
||||||
var buffer = this.ensureBuffer(bufferLength);
|
var buffer = this.ensureBuffer(bufferLength);
|
||||||
this.bufferLength = bufferLength;
|
this.bufferLength = bufferLength;
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor.prototype.getBytes = function fakeStreamGetBytes(length) {
|
FakeStream.prototype.getBytes = function fakeStreamGetBytes(length) {
|
||||||
var end, pos = this.pos;
|
var end, pos = this.pos;
|
||||||
|
|
||||||
if (length) {
|
if (length) {
|
||||||
@ -217,18 +217,20 @@ var FakeStream = (function fakeStream() {
|
|||||||
return this.buffer.subarray(pos, end);
|
return this.buffer.subarray(pos, end);
|
||||||
};
|
};
|
||||||
|
|
||||||
return constructor;
|
return FakeStream;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
var StreamsSequenceStream = (function streamSequenceStream() {
|
var StreamsSequenceStream = (function StreamsSequenceStreamClosure() {
|
||||||
function constructor(streams) {
|
function StreamsSequenceStream(streams) {
|
||||||
this.streams = streams;
|
this.streams = streams;
|
||||||
DecodeStream.call(this);
|
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;
|
var streams = this.streams;
|
||||||
if (streams.length == 0) {
|
if (streams.length == 0) {
|
||||||
this.eof = true;
|
this.eof = true;
|
||||||
@ -243,10 +245,10 @@ var StreamsSequenceStream = (function streamSequenceStream() {
|
|||||||
this.bufferLength = newLength;
|
this.bufferLength = newLength;
|
||||||
};
|
};
|
||||||
|
|
||||||
return constructor;
|
return StreamsSequenceStream;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
var FlateStream = (function flateStream() {
|
var FlateStream = (function FlateStreamClosure() {
|
||||||
var codeLenCodeMap = new Uint32Array([
|
var codeLenCodeMap = new Uint32Array([
|
||||||
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15
|
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
|
0x50003, 0x50013, 0x5000b, 0x5001b, 0x50007, 0x50017, 0x5000f, 0x00000
|
||||||
]), 5];
|
]), 5];
|
||||||
|
|
||||||
function constructor(stream) {
|
function FlateStream(stream) {
|
||||||
var bytes = stream.getBytes();
|
var bytes = stream.getBytes();
|
||||||
var bytesPos = 0;
|
var bytesPos = 0;
|
||||||
|
|
||||||
@ -364,9 +366,9 @@ var FlateStream = (function flateStream() {
|
|||||||
DecodeStream.call(this);
|
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 codeSize = this.codeSize;
|
||||||
var codeBuf = this.codeBuf;
|
var codeBuf = this.codeBuf;
|
||||||
var bytes = this.bytes;
|
var bytes = this.bytes;
|
||||||
@ -386,7 +388,7 @@ var FlateStream = (function flateStream() {
|
|||||||
return b;
|
return b;
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor.prototype.getCode = function flateStreamGetCode(table) {
|
FlateStream.prototype.getCode = function flateStreamGetCode(table) {
|
||||||
var codes = table[0];
|
var codes = table[0];
|
||||||
var maxLen = table[1];
|
var maxLen = table[1];
|
||||||
var codeSize = this.codeSize;
|
var codeSize = this.codeSize;
|
||||||
@ -412,7 +414,7 @@ var FlateStream = (function flateStream() {
|
|||||||
return codeVal;
|
return codeVal;
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor.prototype.generateHuffmanTable =
|
FlateStream.prototype.generateHuffmanTable =
|
||||||
function flateStreamGenerateHuffmanTable(lengths) {
|
function flateStreamGenerateHuffmanTable(lengths) {
|
||||||
var n = lengths.length;
|
var n = lengths.length;
|
||||||
|
|
||||||
@ -451,7 +453,7 @@ var FlateStream = (function flateStream() {
|
|||||||
return [codes, maxLen];
|
return [codes, maxLen];
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor.prototype.readBlock = function flateStreamReadBlock() {
|
FlateStream.prototype.readBlock = function flateStreamReadBlock() {
|
||||||
// read block header
|
// read block header
|
||||||
var hdr = this.getBits(3);
|
var hdr = this.getBits(3);
|
||||||
if (hdr & 1)
|
if (hdr & 1)
|
||||||
@ -582,11 +584,11 @@ var FlateStream = (function flateStream() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return constructor;
|
return FlateStream;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
var PredictorStream = (function predictorStream() {
|
var PredictorStream = (function PredictorStreamClosure() {
|
||||||
function constructor(stream, params) {
|
function PredictorStream(stream, params) {
|
||||||
var predictor = this.predictor = params.get('Predictor') || 1;
|
var predictor = this.predictor = params.get('Predictor') || 1;
|
||||||
|
|
||||||
if (predictor <= 1)
|
if (predictor <= 1)
|
||||||
@ -613,9 +615,9 @@ var PredictorStream = (function predictorStream() {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor.prototype = Object.create(DecodeStream.prototype);
|
PredictorStream.prototype = Object.create(DecodeStream.prototype);
|
||||||
|
|
||||||
constructor.prototype.readBlockTiff =
|
PredictorStream.prototype.readBlockTiff =
|
||||||
function predictorStreamReadBlockTiff() {
|
function predictorStreamReadBlockTiff() {
|
||||||
var rowBytes = this.rowBytes;
|
var rowBytes = this.rowBytes;
|
||||||
|
|
||||||
@ -676,7 +678,9 @@ var PredictorStream = (function predictorStream() {
|
|||||||
this.bufferLength += rowBytes;
|
this.bufferLength += rowBytes;
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor.prototype.readBlockPng = function predictorStreamReadBlockPng() {
|
PredictorStream.prototype.readBlockPng =
|
||||||
|
function predictorStreamReadBlockPng() {
|
||||||
|
|
||||||
var rowBytes = this.rowBytes;
|
var rowBytes = this.rowBytes;
|
||||||
var pixBytes = this.pixBytes;
|
var pixBytes = this.pixBytes;
|
||||||
|
|
||||||
@ -753,7 +757,7 @@ var PredictorStream = (function predictorStream() {
|
|||||||
this.bufferLength += rowBytes;
|
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
|
* a library to decode these images and the stream behaves like all the other
|
||||||
* DecodeStreams.
|
* DecodeStreams.
|
||||||
*/
|
*/
|
||||||
var JpegStream = (function jpegStream() {
|
var JpegStream = (function JpegStreamClosure() {
|
||||||
function isAdobeImage(bytes) {
|
function isAdobeImage(bytes) {
|
||||||
var maxBytesScanned = Math.max(bytes.length - 16, 1024);
|
var maxBytesScanned = Math.max(bytes.length - 16, 1024);
|
||||||
// Looking for APP14, 'Adobe'
|
// Looking for APP14, 'Adobe'
|
||||||
@ -794,7 +798,7 @@ var JpegStream = (function jpegStream() {
|
|||||||
return newBytes;
|
return newBytes;
|
||||||
}
|
}
|
||||||
|
|
||||||
function constructor(bytes, dict, xref) {
|
function JpegStream(bytes, dict, xref) {
|
||||||
// TODO: per poppler, some images may have 'junk' before that
|
// TODO: per poppler, some images may have 'junk' before that
|
||||||
// need to be removed
|
// need to be removed
|
||||||
this.dict = dict;
|
this.dict = dict;
|
||||||
@ -825,9 +829,9 @@ var JpegStream = (function jpegStream() {
|
|||||||
DecodeStream.call(this);
|
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)
|
if (this.bufferLength)
|
||||||
return;
|
return;
|
||||||
var jpegImage = new JpegImage();
|
var jpegImage = new JpegImage();
|
||||||
@ -839,18 +843,18 @@ var JpegStream = (function jpegStream() {
|
|||||||
this.buffer = data;
|
this.buffer = data;
|
||||||
this.bufferLength = data.length;
|
this.bufferLength = data.length;
|
||||||
};
|
};
|
||||||
constructor.prototype.getIR = function jpegStreamGetIR() {
|
JpegStream.prototype.getIR = function jpegStreamGetIR() {
|
||||||
return this.src;
|
return this.src;
|
||||||
};
|
};
|
||||||
constructor.prototype.getChar = function jpegStreamGetChar() {
|
JpegStream.prototype.getChar = function jpegStreamGetChar() {
|
||||||
error('internal error: getChar is not valid on JpegStream');
|
error('internal error: getChar is not valid on JpegStream');
|
||||||
};
|
};
|
||||||
|
|
||||||
return constructor;
|
return JpegStream;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
var DecryptStream = (function decryptStream() {
|
var DecryptStream = (function DecryptStreamClosure() {
|
||||||
function constructor(str, decrypt) {
|
function DecryptStream(str, decrypt) {
|
||||||
this.str = str;
|
this.str = str;
|
||||||
this.dict = str.dict;
|
this.dict = str.dict;
|
||||||
this.decrypt = decrypt;
|
this.decrypt = decrypt;
|
||||||
@ -860,9 +864,9 @@ var DecryptStream = (function decryptStream() {
|
|||||||
|
|
||||||
var chunkSize = 512;
|
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);
|
var chunk = this.str.getBytes(chunkSize);
|
||||||
if (!chunk || chunk.length == 0) {
|
if (!chunk || chunk.length == 0) {
|
||||||
this.eof = true;
|
this.eof = true;
|
||||||
@ -879,11 +883,11 @@ var DecryptStream = (function decryptStream() {
|
|||||||
this.bufferLength = bufferLength;
|
this.bufferLength = bufferLength;
|
||||||
};
|
};
|
||||||
|
|
||||||
return constructor;
|
return DecryptStream;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
var Ascii85Stream = (function ascii85Stream() {
|
var Ascii85Stream = (function Ascii85StreamClosure() {
|
||||||
function constructor(str) {
|
function Ascii85Stream(str) {
|
||||||
this.str = str;
|
this.str = str;
|
||||||
this.dict = str.dict;
|
this.dict = str.dict;
|
||||||
this.input = new Uint8Array(5);
|
this.input = new Uint8Array(5);
|
||||||
@ -891,9 +895,9 @@ var Ascii85Stream = (function ascii85Stream() {
|
|||||||
DecodeStream.call(this);
|
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 tildaCode = '~'.charCodeAt(0);
|
||||||
var zCode = 'z'.charCodeAt(0);
|
var zCode = 'z'.charCodeAt(0);
|
||||||
var str = this.str;
|
var str = this.str;
|
||||||
@ -948,11 +952,11 @@ var Ascii85Stream = (function ascii85Stream() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return constructor;
|
return Ascii85Stream;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
var AsciiHexStream = (function asciiHexStream() {
|
var AsciiHexStream = (function AsciiHexStreamClosure() {
|
||||||
function constructor(str) {
|
function AsciiHexStream(str) {
|
||||||
this.str = str;
|
this.str = str;
|
||||||
this.dict = str.dict;
|
this.dict = str.dict;
|
||||||
|
|
||||||
@ -986,9 +990,9 @@ var AsciiHexStream = (function asciiHexStream() {
|
|||||||
102: 15
|
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,
|
var gtCode = '>'.charCodeAt(0), bytes = this.str.getBytes(), c, n,
|
||||||
decodeLength, buffer, bufferLength, i, length;
|
decodeLength, buffer, bufferLength, i, length;
|
||||||
|
|
||||||
@ -1018,10 +1022,10 @@ var AsciiHexStream = (function asciiHexStream() {
|
|||||||
this.eof = true;
|
this.eof = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
return constructor;
|
return AsciiHexStream;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
var CCITTFaxStream = (function ccittFaxStream() {
|
var CCITTFaxStream = (function CCITTFaxStreamClosure() {
|
||||||
|
|
||||||
var ccittEOL = -2;
|
var ccittEOL = -2;
|
||||||
var twoDimPass = 0;
|
var twoDimPass = 0;
|
||||||
@ -1449,7 +1453,7 @@ var CCITTFaxStream = (function ccittFaxStream() {
|
|||||||
[2, 2], [2, 2], [2, 2], [2, 2]
|
[2, 2], [2, 2], [2, 2], [2, 2]
|
||||||
];
|
];
|
||||||
|
|
||||||
function constructor(str, params) {
|
function CCITTFaxStream(str, params) {
|
||||||
this.str = str;
|
this.str = str;
|
||||||
this.dict = str.dict;
|
this.dict = str.dict;
|
||||||
|
|
||||||
@ -1494,9 +1498,9 @@ var CCITTFaxStream = (function ccittFaxStream() {
|
|||||||
DecodeStream.call(this);
|
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) {
|
while (!this.eof) {
|
||||||
var c = this.lookChar();
|
var c = this.lookChar();
|
||||||
this.buf = EOF;
|
this.buf = EOF;
|
||||||
@ -1505,7 +1509,7 @@ var CCITTFaxStream = (function ccittFaxStream() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor.prototype.addPixels =
|
CCITTFaxStream.prototype.addPixels =
|
||||||
function ccittFaxStreamAddPixels(a1, blackPixels) {
|
function ccittFaxStreamAddPixels(a1, blackPixels) {
|
||||||
var codingLine = this.codingLine;
|
var codingLine = this.codingLine;
|
||||||
var codingPos = this.codingPos;
|
var codingPos = this.codingPos;
|
||||||
@ -1525,7 +1529,7 @@ var CCITTFaxStream = (function ccittFaxStream() {
|
|||||||
this.codingPos = codingPos;
|
this.codingPos = codingPos;
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor.prototype.addPixelsNeg =
|
CCITTFaxStream.prototype.addPixelsNeg =
|
||||||
function ccittFaxStreamAddPixelsNeg(a1, blackPixels) {
|
function ccittFaxStreamAddPixelsNeg(a1, blackPixels) {
|
||||||
var codingLine = this.codingLine;
|
var codingLine = this.codingLine;
|
||||||
var codingPos = this.codingPos;
|
var codingPos = this.codingPos;
|
||||||
@ -1554,7 +1558,7 @@ var CCITTFaxStream = (function ccittFaxStream() {
|
|||||||
this.codingPos = codingPos;
|
this.codingPos = codingPos;
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor.prototype.lookChar = function ccittFaxStreamLookChar() {
|
CCITTFaxStream.prototype.lookChar = function ccittFaxStreamLookChar() {
|
||||||
if (this.buf != EOF)
|
if (this.buf != EOF)
|
||||||
return this.buf;
|
return this.buf;
|
||||||
|
|
||||||
@ -1852,10 +1856,10 @@ var CCITTFaxStream = (function ccittFaxStream() {
|
|||||||
// values. The first array element indicates whether a valid code is being
|
// values. The first array element indicates whether a valid code is being
|
||||||
// returned. The second array element is the actual code. The third array
|
// returned. The second array element is the actual code. The third array
|
||||||
// element indicates whether EOF was reached.
|
// element indicates whether EOF was reached.
|
||||||
var findTableCode = function ccittFaxStreamFindTableCode(start, end, table,
|
CCITTFaxStream.prototype.findTableCode =
|
||||||
limit) {
|
function ccittFaxStreamFindTableCode(start, end, table, limit) {
|
||||||
var limitValue = limit || 0;
|
|
||||||
|
|
||||||
|
var limitValue = limit || 0;
|
||||||
for (var i = start; i <= end; ++i) {
|
for (var i = start; i <= end; ++i) {
|
||||||
var code = this.lookBits(i);
|
var code = this.lookBits(i);
|
||||||
if (code == EOF)
|
if (code == EOF)
|
||||||
@ -1873,7 +1877,9 @@ var CCITTFaxStream = (function ccittFaxStream() {
|
|||||||
return [false, 0, false];
|
return [false, 0, false];
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor.prototype.getTwoDimCode = function ccittFaxStreamGetTwoDimCode() {
|
CCITTFaxStream.prototype.getTwoDimCode =
|
||||||
|
function ccittFaxStreamGetTwoDimCode() {
|
||||||
|
|
||||||
var code = 0;
|
var code = 0;
|
||||||
var p;
|
var p;
|
||||||
if (this.eoblock) {
|
if (this.eoblock) {
|
||||||
@ -1884,7 +1890,7 @@ var CCITTFaxStream = (function ccittFaxStream() {
|
|||||||
return p[1];
|
return p[1];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var result = findTableCode(1, 7, twoDimTable);
|
var result = this.findTableCode(1, 7, twoDimTable);
|
||||||
if (result[0] && result[2])
|
if (result[0] && result[2])
|
||||||
return result[1];
|
return result[1];
|
||||||
}
|
}
|
||||||
@ -1892,7 +1898,9 @@ var CCITTFaxStream = (function ccittFaxStream() {
|
|||||||
return EOF;
|
return EOF;
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor.prototype.getWhiteCode = function ccittFaxStreamGetWhiteCode() {
|
CCITTFaxStream.prototype.getWhiteCode =
|
||||||
|
function ccittFaxStreamGetWhiteCode() {
|
||||||
|
|
||||||
var code = 0;
|
var code = 0;
|
||||||
var p;
|
var p;
|
||||||
var n;
|
var n;
|
||||||
@ -1911,11 +1919,11 @@ var CCITTFaxStream = (function ccittFaxStream() {
|
|||||||
return p[1];
|
return p[1];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var result = findTableCode(1, 9, whiteTable2);
|
var result = this.findTableCode(1, 9, whiteTable2);
|
||||||
if (result[0])
|
if (result[0])
|
||||||
return result[1];
|
return result[1];
|
||||||
|
|
||||||
result = findTableCode(11, 12, whiteTable1);
|
result = this.findTableCode(11, 12, whiteTable1);
|
||||||
if (result[0])
|
if (result[0])
|
||||||
return result[1];
|
return result[1];
|
||||||
}
|
}
|
||||||
@ -1924,7 +1932,9 @@ var CCITTFaxStream = (function ccittFaxStream() {
|
|||||||
return 1;
|
return 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor.prototype.getBlackCode = function ccittFaxStreamGetBlackCode() {
|
CCITTFaxStream.prototype.getBlackCode =
|
||||||
|
function ccittFaxStreamGetBlackCode() {
|
||||||
|
|
||||||
var code, p;
|
var code, p;
|
||||||
if (this.eoblock) {
|
if (this.eoblock) {
|
||||||
code = this.lookBits(13);
|
code = this.lookBits(13);
|
||||||
@ -1942,15 +1952,15 @@ var CCITTFaxStream = (function ccittFaxStream() {
|
|||||||
return p[1];
|
return p[1];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
var result = findTableCode(2, 6, blackTable3);
|
var result = this.findTableCode(2, 6, blackTable3);
|
||||||
if (result[0])
|
if (result[0])
|
||||||
return result[1];
|
return result[1];
|
||||||
|
|
||||||
result = findTableCode(7, 12, blackTable2, 64);
|
result = this.findTableCode(7, 12, blackTable2, 64);
|
||||||
if (result[0])
|
if (result[0])
|
||||||
return result[1];
|
return result[1];
|
||||||
|
|
||||||
result = findTableCode(10, 13, blackTable1);
|
result = this.findTableCode(10, 13, blackTable1);
|
||||||
if (result[0])
|
if (result[0])
|
||||||
return result[1];
|
return result[1];
|
||||||
}
|
}
|
||||||
@ -1959,7 +1969,7 @@ var CCITTFaxStream = (function ccittFaxStream() {
|
|||||||
return 1;
|
return 1;
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor.prototype.lookBits = function ccittFaxStreamLookBits(n) {
|
CCITTFaxStream.prototype.lookBits = function ccittFaxStreamLookBits(n) {
|
||||||
var c;
|
var c;
|
||||||
while (this.inputBits < n) {
|
while (this.inputBits < n) {
|
||||||
if ((c = this.str.getByte()) == null) {
|
if ((c = this.str.getByte()) == null) {
|
||||||
@ -1974,16 +1984,16 @@ var CCITTFaxStream = (function ccittFaxStream() {
|
|||||||
return (this.inputBuf >> (this.inputBits - n)) & (0xFFFF >> (16 - n));
|
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)
|
if ((this.inputBits -= n) < 0)
|
||||||
this.inputBits = 0;
|
this.inputBits = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
return constructor;
|
return CCITTFaxStream;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
var LZWStream = (function lzwStream() {
|
var LZWStream = (function LZWStreamClosure() {
|
||||||
function constructor(str, earlyChange) {
|
function LZWStream(str, earlyChange) {
|
||||||
this.str = str;
|
this.str = str;
|
||||||
this.dict = str.dict;
|
this.dict = str.dict;
|
||||||
this.cachedData = 0;
|
this.cachedData = 0;
|
||||||
@ -2009,9 +2019,9 @@ var LZWStream = (function lzwStream() {
|
|||||||
DecodeStream.call(this);
|
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 bitsCached = this.bitsCached;
|
||||||
var cachedData = this.cachedData;
|
var cachedData = this.cachedData;
|
||||||
while (bitsCached < n) {
|
while (bitsCached < n) {
|
||||||
@ -2029,7 +2039,7 @@ var LZWStream = (function lzwStream() {
|
|||||||
return (cachedData >>> bitsCached) & ((1 << n) - 1);
|
return (cachedData >>> bitsCached) & ((1 << n) - 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor.prototype.readBlock = function lzwStreamReadBlock() {
|
LZWStream.prototype.readBlock = function lzwStreamReadBlock() {
|
||||||
var blockSize = 512;
|
var blockSize = 512;
|
||||||
var estimatedDecodedSize = blockSize * 2, decodedSizeDelta = blockSize;
|
var estimatedDecodedSize = blockSize * 2, decodedSizeDelta = blockSize;
|
||||||
var i, j, q;
|
var i, j, q;
|
||||||
@ -2108,6 +2118,6 @@ var LZWStream = (function lzwStream() {
|
|||||||
this.bufferLength = currentBufferLength;
|
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 IDENTITY_MATRIX = [1, 0, 0, 1, 0, 0];
|
||||||
|
|
||||||
var Util = (function utilUtil() {
|
var Util = (function UtilClosure() {
|
||||||
function constructor() {}
|
function Util() {}
|
||||||
constructor.makeCssRgb = function makergb(r, g, b) {
|
Util.makeCssRgb = function makergb(r, g, b) {
|
||||||
var ri = (255 * r) | 0, gi = (255 * g) | 0, bi = (255 * b) | 0;
|
var ri = (255 * r) | 0, gi = (255 * g) | 0, bi = (255 * b) | 0;
|
||||||
return 'rgb(' + ri + ',' + gi + ',' + bi + ')';
|
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]);
|
c = (new DeviceCmykCS()).getRgb([c, m, y, k]);
|
||||||
var ri = (255 * c[0]) | 0, gi = (255 * c[1]) | 0, bi = (255 * c[2]) | 0;
|
var ri = (255 * c[0]) | 0, gi = (255 * c[1]) | 0, bi = (255 * c[2]) | 0;
|
||||||
return 'rgb(' + ri + ',' + gi + ',' + bi + ')';
|
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 xt = p[0] * m[0] + p[1] * m[2] + m[4];
|
||||||
var yt = p[0] * m[1] + p[1] * m[3] + m[5];
|
var yt = p[0] * m[1] + p[1] * m[3] + m[5];
|
||||||
return [xt, yt];
|
return [xt, yt];
|
||||||
};
|
};
|
||||||
|
|
||||||
return constructor;
|
return Util;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
var PDFStringTranslateTable = [
|
var PDFStringTranslateTable = [
|
||||||
@ -197,7 +197,7 @@ function isPDFFunction(v) {
|
|||||||
* can be set. If any of these happens twice or the data is required before
|
* can be set. If any of these happens twice or the data is required before
|
||||||
* it was set, an exception is throw.
|
* it was set, an exception is throw.
|
||||||
*/
|
*/
|
||||||
var Promise = (function promise() {
|
var Promise = (function PromiseClosure() {
|
||||||
var EMPTY_PROMISE = {};
|
var EMPTY_PROMISE = {};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -206,6 +206,8 @@ var Promise = (function promise() {
|
|||||||
*/
|
*/
|
||||||
function Promise(name, data) {
|
function Promise(name, data) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
|
this.isRejected = false;
|
||||||
|
this.error = null;
|
||||||
// If you build a promise and pass in some data it's already resolved.
|
// If you build a promise and pass in some data it's already resolved.
|
||||||
if (data != null) {
|
if (data != null) {
|
||||||
this.isResolved = true;
|
this.isResolved = true;
|
||||||
@ -216,8 +218,35 @@ var Promise = (function promise() {
|
|||||||
this._data = EMPTY_PROMISE;
|
this._data = EMPTY_PROMISE;
|
||||||
}
|
}
|
||||||
this.callbacks = [];
|
this.callbacks = [];
|
||||||
|
this.errbacks = [];
|
||||||
|
};
|
||||||
|
/**
|
||||||
|
* Builds a promise that is resolved when all the passed in promises are
|
||||||
|
* 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 = {
|
Promise.prototype = {
|
||||||
hasData: false,
|
hasData: false,
|
||||||
|
|
||||||
@ -256,9 +285,12 @@ var Promise = (function promise() {
|
|||||||
if (this.isResolved) {
|
if (this.isResolved) {
|
||||||
throw 'A Promise can be resolved only once ' + this.name;
|
throw 'A Promise can be resolved only once ' + this.name;
|
||||||
}
|
}
|
||||||
|
if (this.isRejected) {
|
||||||
|
throw 'The Promise was already rejected ' + this.name;
|
||||||
|
}
|
||||||
|
|
||||||
this.isResolved = true;
|
this.isResolved = true;
|
||||||
this.data = data;
|
this.data = data || null;
|
||||||
var callbacks = this.callbacks;
|
var callbacks = this.callbacks;
|
||||||
|
|
||||||
for (var i = 0, ii = callbacks.length; i < ii; i++) {
|
for (var i = 0, ii = callbacks.length; i < ii; i++) {
|
||||||
@ -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) {
|
if (!callback) {
|
||||||
throw 'Requiring callback' + this.name;
|
throw 'Requiring callback' + this.name;
|
||||||
}
|
}
|
||||||
@ -275,8 +324,13 @@ var Promise = (function promise() {
|
|||||||
if (this.isResolved) {
|
if (this.isResolved) {
|
||||||
var data = this.data;
|
var data = this.data;
|
||||||
callback.call(null, data);
|
callback.call(null, data);
|
||||||
|
} else if (this.isRejected && errorback) {
|
||||||
|
var error = this.error;
|
||||||
|
errback.call(null, error);
|
||||||
} else {
|
} else {
|
||||||
this.callbacks.push(callback);
|
this.callbacks.push(callback);
|
||||||
|
if (errback)
|
||||||
|
this.errbacks.push(errback);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -6,6 +6,8 @@
|
|||||||
function MessageHandler(name, comObj) {
|
function MessageHandler(name, comObj) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.comObj = comObj;
|
this.comObj = comObj;
|
||||||
|
this.callbackIndex = 1;
|
||||||
|
var callbacks = this.callbacks = {};
|
||||||
var ah = this.actionHandler = {};
|
var ah = this.actionHandler = {};
|
||||||
|
|
||||||
ah['console_log'] = [function ahConsoleLog(data) {
|
ah['console_log'] = [function ahConsoleLog(data) {
|
||||||
@ -17,9 +19,30 @@ function MessageHandler(name, comObj) {
|
|||||||
|
|
||||||
comObj.onmessage = function messageHandlerComObjOnMessage(event) {
|
comObj.onmessage = function messageHandlerComObjOnMessage(event) {
|
||||||
var data = event.data;
|
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];
|
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 {
|
} else {
|
||||||
throw 'Unkown action from worker: ' + data.action;
|
throw 'Unkown action from worker: ' + data.action;
|
||||||
}
|
}
|
||||||
@ -34,12 +57,23 @@ MessageHandler.prototype = {
|
|||||||
}
|
}
|
||||||
ah[actionName] = [handler, scope];
|
ah[actionName] = [handler, scope];
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
send: function messageHandlerSend(actionName, data) {
|
* Sends a message to the comObj to invoke the action with the supplied data.
|
||||||
this.comObj.postMessage({
|
* @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,
|
action: actionName,
|
||||||
data: data
|
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) {
|
handler.on('page_request', function wphSetupPageRequest(pageNum) {
|
||||||
pageNum = parseInt(pageNum);
|
pageNum = parseInt(pageNum);
|
||||||
|
|
||||||
var page = pdfDoc.getPage(pageNum);
|
|
||||||
|
|
||||||
// The following code does quite the same as
|
// The following code does quite the same as
|
||||||
// Page.prototype.startRendering, but stops at one point and sends the
|
// Page.prototype.startRendering, but stops at one point and sends the
|
||||||
@ -77,9 +110,23 @@ var WorkerMessageHandler = {
|
|||||||
var start = Date.now();
|
var start = Date.now();
|
||||||
|
|
||||||
var dependency = [];
|
var dependency = [];
|
||||||
|
var IRQueue = null;
|
||||||
// Pre compile the pdf page and fetch the fonts/images.
|
try {
|
||||||
var IRQueue = page.getIRQueue(handler, dependency);
|
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,
|
console.log('page=%d - getIRQueue: time=%dms, len=%d', pageNum,
|
||||||
Date.now() - start, IRQueue.fnArray.length);
|
Date.now() - start, IRQueue.fnArray.length);
|
||||||
|
@ -139,6 +139,11 @@ function nextPage(task, loadError) {
|
|||||||
if (task.skipPages && task.skipPages.indexOf(task.pageNum) >= 0) {
|
if (task.skipPages && task.skipPages.indexOf(task.pageNum) >= 0) {
|
||||||
log(' skipping page ' + task.pageNum + '/' + task.pdfDoc.numPages +
|
log(' skipping page ' + task.pageNum + '/' + task.pdfDoc.numPages +
|
||||||
'... ');
|
'... ');
|
||||||
|
// empty the canvas
|
||||||
|
canvas.width = 1;
|
||||||
|
canvas.height = 1;
|
||||||
|
clear(canvas.getContext('2d'));
|
||||||
|
|
||||||
snapshotCurrentPage(task, '');
|
snapshotCurrentPage(task, '');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -160,12 +165,24 @@ function nextPage(task, loadError) {
|
|||||||
canvas.height = pageHeight * pdfToCssUnitsCoef;
|
canvas.height = pageHeight * pdfToCssUnitsCoef;
|
||||||
clear(ctx);
|
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(
|
page.startRendering(
|
||||||
ctx,
|
ctx,
|
||||||
function nextPageStartRendering(e) {
|
function nextPageStartRendering(error) {
|
||||||
snapshotCurrentPage(task, (!failure && e) ?
|
var failureMessage = false;
|
||||||
('render : ' + e) : failure);
|
if (error)
|
||||||
}
|
failureMessage = 'render : ' + error.message;
|
||||||
|
snapshotCurrentPage(task, failureMessage);
|
||||||
|
},
|
||||||
|
textLayerBuilder
|
||||||
);
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
failure = 'page setup : ' + e.toString();
|
failure = 'page setup : ' + e.toString();
|
||||||
|
6
test/pdfs/.gitignore
vendored
6
test/pdfs/.gitignore
vendored
@ -16,3 +16,9 @@
|
|||||||
!alphatrans.pdf
|
!alphatrans.pdf
|
||||||
!devicen.pdf
|
!devicen.pdf
|
||||||
!cmykjpeg.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
|
ANAL = True
|
||||||
DEFAULT_MANIFEST_FILE = 'test_manifest.json'
|
DEFAULT_MANIFEST_FILE = 'test_manifest.json'
|
||||||
EQLOG_FILE = 'eq.log'
|
EQLOG_FILE = 'eq.log'
|
||||||
|
BROWSERLOG_FILE = 'browser.log'
|
||||||
REFDIR = 'ref'
|
REFDIR = 'ref'
|
||||||
TMPDIR = 'tmp'
|
TMPDIR = 'tmp'
|
||||||
VERBOSE = False
|
VERBOSE = False
|
||||||
@ -229,6 +230,7 @@ class BaseBrowserCommand(object):
|
|||||||
def setup(self):
|
def setup(self):
|
||||||
self.tempDir = tempfile.mkdtemp()
|
self.tempDir = tempfile.mkdtemp()
|
||||||
self.profileDir = os.path.join(self.tempDir, "profile")
|
self.profileDir = os.path.join(self.tempDir, "profile")
|
||||||
|
self.browserLog = open(BROWSERLOG_FILE, "w")
|
||||||
|
|
||||||
def teardown(self):
|
def teardown(self):
|
||||||
# If the browser is still running, wait up to ten seconds for it to quit
|
# 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):
|
if self.tempDir is not None and os.path.exists(self.tempDir):
|
||||||
shutil.rmtree(self.tempDir)
|
shutil.rmtree(self.tempDir)
|
||||||
|
|
||||||
|
self.browserLog.close()
|
||||||
|
|
||||||
def start(self, url):
|
def start(self, url):
|
||||||
raise Exception("Can't start BaseBrowserCommand")
|
raise Exception("Can't start BaseBrowserCommand")
|
||||||
|
|
||||||
@ -262,7 +266,7 @@ class FirefoxBrowserCommand(BaseBrowserCommand):
|
|||||||
if platform.system() == "Darwin":
|
if platform.system() == "Darwin":
|
||||||
cmds.append("-foreground")
|
cmds.append("-foreground")
|
||||||
cmds.extend(["-no-remote", "-profile", self.profileDir, url])
|
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):
|
class ChromeBrowserCommand(BaseBrowserCommand):
|
||||||
def _fixupMacPath(self):
|
def _fixupMacPath(self):
|
||||||
@ -272,7 +276,7 @@ class ChromeBrowserCommand(BaseBrowserCommand):
|
|||||||
cmds = [self.path]
|
cmds = [self.path]
|
||||||
cmds.extend(["--user-data-dir=%s" % self.profileDir,
|
cmds.extend(["--user-data-dir=%s" % self.profileDir,
|
||||||
"--no-first-run", "--disable-sync", url])
|
"--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):
|
def makeBrowserCommand(browser):
|
||||||
path = browser["path"].lower()
|
path = browser["path"].lower()
|
||||||
|
@ -17,13 +17,13 @@
|
|||||||
"rounds": 1,
|
"rounds": 1,
|
||||||
"type": "load"
|
"type": "load"
|
||||||
},
|
},
|
||||||
{ "id": "intelisa-load",
|
{ "id": "intelisa-eq",
|
||||||
"file": "pdfs/intelisa.pdf",
|
"file": "pdfs/intelisa.pdf",
|
||||||
"md5": "f5712097d29287a97f1278839814f682",
|
"md5": "f5712097d29287a97f1278839814f682",
|
||||||
"md5": "f3ed5487d1afa34d8b77c0c734a95c79",
|
|
||||||
"link": true,
|
"link": true,
|
||||||
|
"pageLimit": 100,
|
||||||
"rounds": 1,
|
"rounds": 1,
|
||||||
"type": "load"
|
"type": "eq"
|
||||||
},
|
},
|
||||||
{ "id": "pdfspec-load",
|
{ "id": "pdfspec-load",
|
||||||
"file": "pdfs/pdf.pdf",
|
"file": "pdfs/pdf.pdf",
|
||||||
@ -88,6 +88,13 @@
|
|||||||
"rounds": 1,
|
"rounds": 1,
|
||||||
"type": "eq"
|
"type": "eq"
|
||||||
},
|
},
|
||||||
|
{ "id": "freeculture",
|
||||||
|
"file": "pdfs/freeculture.pdf",
|
||||||
|
"md5": "dcdf3a8268e6a18938a42d5149efcfca",
|
||||||
|
"rounds": 1,
|
||||||
|
"pageLimit": 5,
|
||||||
|
"type": "eq"
|
||||||
|
},
|
||||||
{ "id": "wnv_chinese-pdf",
|
{ "id": "wnv_chinese-pdf",
|
||||||
"file": "pdfs/wnv_chinese.pdf",
|
"file": "pdfs/wnv_chinese.pdf",
|
||||||
"md5": "db682638e68391125e8982d3c984841e",
|
"md5": "db682638e68391125e8982d3c984841e",
|
||||||
@ -221,6 +228,12 @@
|
|||||||
"rounds": 1,
|
"rounds": 1,
|
||||||
"type": "load"
|
"type": "load"
|
||||||
},
|
},
|
||||||
|
{ "id": "scan-bad",
|
||||||
|
"file": "pdfs/scan-bad.pdf",
|
||||||
|
"md5": "4cf988f01ab83f61aca57f406dfd6584",
|
||||||
|
"rounds": 1,
|
||||||
|
"type": "load"
|
||||||
|
},
|
||||||
{ "id": "ibwa-bad",
|
{ "id": "ibwa-bad",
|
||||||
"file": "pdfs/ibwa-bad.pdf",
|
"file": "pdfs/ibwa-bad.pdf",
|
||||||
"md5": "6ca059d32b74ac2688ae06f727fee755",
|
"md5": "6ca059d32b74ac2688ae06f727fee755",
|
||||||
@ -276,5 +289,111 @@
|
|||||||
"link": false,
|
"link": false,
|
||||||
"rounds": 1,
|
"rounds": 1,
|
||||||
"type": "eq"
|
"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);
|
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
|
// Checking if the typed arrays are supported
|
||||||
(function checkTypedArrayCompatibility() {
|
(function checkTypedArrayCompatibility() {
|
||||||
if (typeof Uint8Array !== 'undefined')
|
if (typeof Uint8Array !== 'undefined') {
|
||||||
|
// some mobile version might not support Float64Array
|
||||||
|
if (typeof Float64Array === 'undefined')
|
||||||
|
window.Float64Array = Float32Array;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
function subarray(start, end) {
|
function subarray(start, end) {
|
||||||
return this.slice(start, end);
|
return new TypedArray(this.slice(start, end));
|
||||||
}
|
}
|
||||||
|
|
||||||
function setArrayOffset(array, offset) {
|
function setArrayOffset(array, offset) {
|
||||||
@ -46,6 +51,8 @@
|
|||||||
window.Uint32Array = TypedArray;
|
window.Uint32Array = TypedArray;
|
||||||
window.Int32Array = TypedArray;
|
window.Int32Array = TypedArray;
|
||||||
window.Uint16Array = TypedArray;
|
window.Uint16Array = TypedArray;
|
||||||
|
window.Float32Array = TypedArray;
|
||||||
|
window.Float64Array = TypedArray;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
// Object.create() ?
|
// Object.create() ?
|
||||||
@ -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 === */
|
/* === Toolbar === */
|
||||||
#controls {
|
#controls {
|
||||||
background-color: #eee;
|
background-color: #eee;
|
||||||
|
background: -o-linear-gradient(bottom,#eee 0%,#fff 100%);
|
||||||
background: -moz-linear-gradient(center bottom, #eee 0%, #fff 100%);
|
background: -moz-linear-gradient(center bottom, #eee 0%, #fff 100%);
|
||||||
background: -webkit-gradient(linear, left bottom, left top, color-stop(0.0, #ddd), color-stop(1.0, #fff));
|
background: -webkit-gradient(linear, left bottom, left top, color-stop(0.0, #ddd), color-stop(1.0, #fff));
|
||||||
border-bottom: 1px solid #666;
|
border-bottom: 1px solid #666;
|
||||||
@ -82,6 +83,7 @@ span#info {
|
|||||||
bottom: 18px;
|
bottom: 18px;
|
||||||
left: -290px;
|
left: -290px;
|
||||||
transition: left 0.25s ease-in-out 1s;
|
transition: left 0.25s ease-in-out 1s;
|
||||||
|
-o-transition: left 0.25s ease-in-out 1s;
|
||||||
-moz-transition: left 0.25s ease-in-out 1s;
|
-moz-transition: left 0.25s ease-in-out 1s;
|
||||||
-webkit-transition: left 0.25s ease-in-out 1s;
|
-webkit-transition: left 0.25s ease-in-out 1s;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
@ -90,6 +92,7 @@ span#info {
|
|||||||
#sidebar:hover {
|
#sidebar:hover {
|
||||||
left: 0px;
|
left: 0px;
|
||||||
transition: left 0.25s ease-in-out 0s;
|
transition: left 0.25s ease-in-out 0s;
|
||||||
|
-o-transition: left 0.25s ease-in-out 0s;
|
||||||
-moz-transition: left 0.25s ease-in-out 0s;
|
-moz-transition: left 0.25s ease-in-out 0s;
|
||||||
-webkit-transition: left 0.25s ease-in-out 0s;
|
-webkit-transition: left 0.25s ease-in-out 0s;
|
||||||
}
|
}
|
||||||
@ -232,6 +235,56 @@ canvas {
|
|||||||
-webkit-box-shadow: 0px 2px 10px #ff0;
|
-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 {
|
#viewer {
|
||||||
margin: 44px 0px 0px;
|
margin: 44px 0px 0px;
|
||||||
padding: 8px 0px;
|
padding: 8px 0px;
|
||||||
@ -252,6 +305,38 @@ canvas {
|
|||||||
display: none;
|
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 === */
|
/* === Printed media overrides === */
|
||||||
@media print {
|
@media print {
|
||||||
#sidebar {
|
#sidebar {
|
||||||
|
@ -27,9 +27,9 @@
|
|||||||
<script type="text/javascript" src="../src/worker.js"></script> <!-- PDFJSSCRIPT_REMOVE -->
|
<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" 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">PDFJS.workerSrc = '../src/worker_loader.js';</script> <!-- PDFJSSCRIPT_REMOVE -->
|
||||||
|
|
||||||
<script type="text/javascript" src="viewer.js"></script>
|
<script type="text/javascript" src="viewer.js"></script>
|
||||||
</head>
|
|
||||||
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
<div id="controls">
|
<div id="controls">
|
||||||
@ -67,10 +67,11 @@
|
|||||||
<option value="0.75">75%</option>
|
<option value="0.75">75%</option>
|
||||||
<option value="1">100%</option>
|
<option value="1">100%</option>
|
||||||
<option value="1.25">125%</option>
|
<option value="1.25">125%</option>
|
||||||
<option value="1.5" selected="selected">150%</option>
|
<option value="1.5">150%</option>
|
||||||
<option value="2">200%</option>
|
<option value="2">200%</option>
|
||||||
<option id="pageWidthOption" value="page-width">Page Width</option>
|
<option id="pageWidthOption" value="page-width">Page Width</option>
|
||||||
<option id="pageFitOption" value="page-fit">Page Fit</option>
|
<option id="pageFitOption" value="page-fit">Page Fit</option>
|
||||||
|
<option id="pageAutoOption" value="auto" selected="selected">Auto</option>
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
<div class="separator"></div>
|
<div class="separator"></div>
|
||||||
@ -97,6 +98,24 @@
|
|||||||
|
|
||||||
<span id="info">--</span>
|
<span id="info">--</span>
|
||||||
</div>
|
</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="sidebar">
|
||||||
<div id="sidebarBox">
|
<div id="sidebarBox">
|
||||||
@ -121,4 +140,3 @@
|
|||||||
<div id="viewer"></div>
|
<div id="viewer"></div>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
649
web/viewer.js
649
web/viewer.js
@ -4,14 +4,15 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var kDefaultURL = 'compressed.tracemonkey-pldi-09.pdf';
|
var kDefaultURL = 'compressed.tracemonkey-pldi-09.pdf';
|
||||||
var kDefaultScale = 1.5;
|
var kDefaultScale = 'auto';
|
||||||
var kDefaultScaleDelta = 1.1;
|
var kDefaultScaleDelta = 1.1;
|
||||||
var kCacheSize = 20;
|
var kCacheSize = 20;
|
||||||
var kCssUnits = 96.0 / 72.0;
|
var kCssUnits = 96.0 / 72.0;
|
||||||
var kScrollbarPadding = 40;
|
var kScrollbarPadding = 40;
|
||||||
var kMinScale = 0.25;
|
var kMinScale = 0.25;
|
||||||
var kMaxScale = 4.0;
|
var kMaxScale = 4.0;
|
||||||
|
var kImageDirectory = './images/';
|
||||||
|
var kSettingsMemory = 20;
|
||||||
|
|
||||||
var Cache = function cacheCache(size) {
|
var Cache = function cacheCache(size) {
|
||||||
var data = [];
|
var data = [];
|
||||||
@ -25,16 +26,136 @@ var Cache = function cacheCache(size) {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var RenderingQueue = (function RenderingQueueClosure() {
|
||||||
|
function RenderingQueue() {
|
||||||
|
this.items = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
RenderingQueue.prototype = {
|
||||||
|
enqueueDraw: function RenderingQueueEnqueueDraw(item) {
|
||||||
|
if (!item.drawingRequired())
|
||||||
|
return; // as no redraw required, no need for queueing.
|
||||||
|
|
||||||
|
if ('rendering' in item)
|
||||||
|
return; // is already in the queue
|
||||||
|
|
||||||
|
item.rendering = true;
|
||||||
|
this.items.push(item);
|
||||||
|
if (this.items.length > 1)
|
||||||
|
return; // not first item
|
||||||
|
|
||||||
|
item.draw(this.continueExecution.bind(this));
|
||||||
|
},
|
||||||
|
continueExecution: function RenderingQueueContinueExecution() {
|
||||||
|
var item = this.items.shift();
|
||||||
|
delete item.rendering;
|
||||||
|
|
||||||
|
if (this.items.length == 0)
|
||||||
|
return; // queue is empty
|
||||||
|
|
||||||
|
item = this.items[0];
|
||||||
|
item.draw(this.continueExecution.bind(this));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return RenderingQueue;
|
||||||
|
})();
|
||||||
|
|
||||||
|
// Settings Manager - This is a utility for saving settings
|
||||||
|
// First we see if localStorage is available, FF bug #495747
|
||||||
|
// If not, we use FUEL in FF
|
||||||
|
var Settings = (function SettingsClosure() {
|
||||||
|
var isLocalStorageEnabled = (function localStorageEnabledTest() {
|
||||||
|
try {
|
||||||
|
localStorage;
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
})();
|
||||||
|
var extPrefix = 'extensions.uriloader@pdf.js';
|
||||||
|
var isExtension = location.protocol == 'chrome:' && !isLocalStorageEnabled;
|
||||||
|
var inPrivateBrowsing = false;
|
||||||
|
if (isExtension) {
|
||||||
|
var pbs = Components.classes['@mozilla.org/privatebrowsing;1']
|
||||||
|
.getService(Components.interfaces.nsIPrivateBrowsingService);
|
||||||
|
inPrivateBrowsing = pbs.privateBrowsingEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
function Settings(fingerprint) {
|
||||||
|
var database = null;
|
||||||
|
var index;
|
||||||
|
if (inPrivateBrowsing)
|
||||||
|
return false;
|
||||||
|
else if (isExtension)
|
||||||
|
database = Application.prefs.getValue(extPrefix + '.database', '{}');
|
||||||
|
else if (isLocalStorageEnabled)
|
||||||
|
database = localStorage.getItem('database') || '{}';
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
|
||||||
|
database = JSON.parse(database);
|
||||||
|
if (!('files' in database))
|
||||||
|
database.files = [];
|
||||||
|
if (database.files.length >= kSettingsMemory)
|
||||||
|
database.files.shift();
|
||||||
|
for (var i = 0, length = database.files.length; i < length; i++) {
|
||||||
|
var branch = database.files[i];
|
||||||
|
if (branch.fingerprint == fingerprint) {
|
||||||
|
index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (typeof index != 'number')
|
||||||
|
index = database.files.push({fingerprint: fingerprint}) - 1;
|
||||||
|
this.file = database.files[index];
|
||||||
|
this.database = database;
|
||||||
|
if (isExtension)
|
||||||
|
Application.prefs.setValue(extPrefix + '.database',
|
||||||
|
JSON.stringify(database));
|
||||||
|
else if (isLocalStorageEnabled)
|
||||||
|
localStorage.setItem('database', JSON.stringify(database));
|
||||||
|
}
|
||||||
|
|
||||||
|
Settings.prototype = {
|
||||||
|
set: function settingsSet(name, val) {
|
||||||
|
if (inPrivateBrowsing)
|
||||||
|
return false;
|
||||||
|
var file = this.file;
|
||||||
|
file[name] = val;
|
||||||
|
if (isExtension)
|
||||||
|
Application.prefs.setValue(extPrefix + '.database',
|
||||||
|
JSON.stringify(this.database));
|
||||||
|
else if (isLocalStorageEnabled)
|
||||||
|
localStorage.setItem('database', JSON.stringify(this.database));
|
||||||
|
},
|
||||||
|
|
||||||
|
get: function settingsGet(name, defaultValue) {
|
||||||
|
if (inPrivateBrowsing)
|
||||||
|
return defaultValue;
|
||||||
|
else
|
||||||
|
return this.file[name] || defaultValue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return Settings;
|
||||||
|
})();
|
||||||
|
|
||||||
var cache = new Cache(kCacheSize);
|
var cache = new Cache(kCacheSize);
|
||||||
|
var renderingQueue = new RenderingQueue();
|
||||||
var currentPageNumber = 1;
|
var currentPageNumber = 1;
|
||||||
|
|
||||||
var PDFView = {
|
var PDFView = {
|
||||||
pages: [],
|
pages: [],
|
||||||
thumbnails: [],
|
thumbnails: [],
|
||||||
currentScale: kDefaultScale,
|
currentScale: 0,
|
||||||
|
currentScaleValue: null,
|
||||||
initialBookmark: document.location.hash.substring(1),
|
initialBookmark: document.location.hash.substring(1),
|
||||||
|
|
||||||
setScale: function pdfViewSetScale(val, resetAutoSettings) {
|
setScale: function pdfViewSetScale(val, resetAutoSettings) {
|
||||||
|
if (val == this.currentScale)
|
||||||
|
return;
|
||||||
|
|
||||||
var pages = this.pages;
|
var pages = this.pages;
|
||||||
for (var i = 0; i < pages.length; i++)
|
for (var i = 0; i < pages.length; i++)
|
||||||
pages[i].update(val * kCssUnits);
|
pages[i].update(val * kCssUnits);
|
||||||
@ -55,6 +176,7 @@ var PDFView = {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
var scale = parseFloat(value);
|
var scale = parseFloat(value);
|
||||||
|
this.currentScaleValue = value;
|
||||||
if (scale) {
|
if (scale) {
|
||||||
this.setScale(scale, true);
|
this.setScale(scale, true);
|
||||||
return;
|
return;
|
||||||
@ -73,6 +195,10 @@ var PDFView = {
|
|||||||
this.setScale(
|
this.setScale(
|
||||||
Math.min(pageWidthScale, pageHeightScale), resetAutoSettings);
|
Math.min(pageWidthScale, pageHeightScale), resetAutoSettings);
|
||||||
}
|
}
|
||||||
|
if ('auto' == value)
|
||||||
|
this.setScale(Math.min(1.0, pageWidthScale), resetAutoSettings);
|
||||||
|
|
||||||
|
selectScaleOption(value);
|
||||||
},
|
},
|
||||||
|
|
||||||
zoomIn: function pdfViewZoomIn() {
|
zoomIn: function pdfViewZoomIn() {
|
||||||
@ -129,7 +255,14 @@ var PDFView = {
|
|||||||
if (evt.lengthComputable)
|
if (evt.lengthComputable)
|
||||||
self.progress(evt.loaded / evt.total);
|
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) {
|
function getPdfLoad(data) {
|
||||||
self.loading = true;
|
self.loading = true;
|
||||||
@ -168,7 +301,8 @@ var PDFView = {
|
|||||||
(destRef + 1);
|
(destRef + 1);
|
||||||
if (pageNumber) {
|
if (pageNumber) {
|
||||||
var pdfOpenParams = '#page=' + 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);
|
var scale = (dest[4] || this.currentScale);
|
||||||
pdfOpenParams += '&zoom=' + (scale * 100);
|
pdfOpenParams += '&zoom=' + (scale * 100);
|
||||||
if (dest[2] || dest[3]) {
|
if (dest[2] || dest[3]) {
|
||||||
@ -181,9 +315,48 @@ var PDFView = {
|
|||||||
return '';
|
return '';
|
||||||
},
|
},
|
||||||
|
|
||||||
error: function pdfViewError() {
|
/**
|
||||||
var loadingIndicator = document.getElementById('loading');
|
* Show the error box.
|
||||||
loadingIndicator.innerHTML = 'Error';
|
* @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) {
|
progress: function pdfViewProgress(level) {
|
||||||
@ -193,6 +366,17 @@ var PDFView = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
load: function pdfViewLoad(data, scale) {
|
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');
|
var loadingIndicator = document.getElementById('loading');
|
||||||
loadingIndicator.setAttribute('hidden', 'true');
|
loadingIndicator.setAttribute('hidden', 'true');
|
||||||
|
|
||||||
@ -209,28 +393,48 @@ var PDFView = {
|
|||||||
while (container.hasChildNodes())
|
while (container.hasChildNodes())
|
||||||
container.removeChild(container.lastChild);
|
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 pagesCount = pdf.numPages;
|
||||||
|
var id = pdf.fingerprint;
|
||||||
|
var storedHash = null;
|
||||||
document.getElementById('numPages').innerHTML = pagesCount;
|
document.getElementById('numPages').innerHTML = pagesCount;
|
||||||
document.getElementById('pageNumber').max = pagesCount;
|
document.getElementById('pageNumber').max = pagesCount;
|
||||||
|
PDFView.documentFingerprint = id;
|
||||||
|
var store = PDFView.store = new Settings(id);
|
||||||
|
if (store.get('exists', false)) {
|
||||||
|
var page = store.get('page', '1');
|
||||||
|
var zoom = store.get('zoom', PDFView.currentScale);
|
||||||
|
var left = store.get('scrollLeft', '0');
|
||||||
|
var top = store.get('scrollTop', '0');
|
||||||
|
|
||||||
|
storedHash = 'page=' + page + '&zoom=' + zoom + ',' + left + ',' + top;
|
||||||
|
}
|
||||||
|
|
||||||
var pages = this.pages = [];
|
var pages = this.pages = [];
|
||||||
var pagesRefMap = {};
|
var pagesRefMap = {};
|
||||||
var thumbnails = this.thumbnails = [];
|
var thumbnails = this.thumbnails = [];
|
||||||
for (var i = 1; i <= pagesCount; i++) {
|
for (var i = 1; i <= pagesCount; i++) {
|
||||||
var page = pdf.getPage(i);
|
var page = pdf.getPage(i);
|
||||||
pages.push(new PageView(container, page, i, page.width, page.height,
|
var pageView = new PageView(container, page, i, page.width, page.height,
|
||||||
page.stats, this.navigateTo.bind(this)));
|
page.stats, this.navigateTo.bind(this));
|
||||||
thumbnails.push(new ThumbnailView(sidebar, page, i,
|
var thumbnailView = new ThumbnailView(sidebar, page, i,
|
||||||
page.width / page.height));
|
page.width / page.height);
|
||||||
|
bindOnAfterDraw(pageView, thumbnailView);
|
||||||
|
|
||||||
|
pages.push(pageView);
|
||||||
|
thumbnails.push(thumbnailView);
|
||||||
var pageRef = page.ref;
|
var pageRef = page.ref;
|
||||||
pagesRefMap[pageRef.num + ' ' + pageRef.gen + ' R'] = i;
|
pagesRefMap[pageRef.num + ' ' + pageRef.gen + ' R'] = i;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setScale(scale || kDefaultScale, true);
|
|
||||||
|
|
||||||
this.pagesRefMap = pagesRefMap;
|
this.pagesRefMap = pagesRefMap;
|
||||||
this.destinations = pdf.catalog.destinations;
|
this.destinations = pdf.catalog.destinations;
|
||||||
|
|
||||||
if (pdf.catalog.documentOutline) {
|
if (pdf.catalog.documentOutline) {
|
||||||
this.outline = new DocumentOutlineView(pdf.catalog.documentOutline);
|
this.outline = new DocumentOutlineView(pdf.catalog.documentOutline);
|
||||||
var outlineSwitchButton = document.getElementById('outlineSwitch');
|
var outlineSwitchButton = document.getElementById('outlineSwitch');
|
||||||
@ -238,12 +442,20 @@ var PDFView = {
|
|||||||
this.switchSidebarView('outline');
|
this.switchSidebarView('outline');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Reset the current scale, as otherwise the page's scale might not get
|
||||||
|
// updated if the zoom level stayed the same.
|
||||||
|
this.currentScale = 0;
|
||||||
|
this.currentScaleValue = null;
|
||||||
if (this.initialBookmark) {
|
if (this.initialBookmark) {
|
||||||
this.setHash(this.initialBookmark);
|
this.setHash(this.initialBookmark);
|
||||||
this.initialBookmark = null;
|
this.initialBookmark = null;
|
||||||
}
|
}
|
||||||
else
|
else if (storedHash)
|
||||||
|
this.setHash(storedHash);
|
||||||
|
else {
|
||||||
|
this.parseScale(scale || kDefaultScale, true);
|
||||||
this.page = 1;
|
this.page = 1;
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
setHash: function pdfViewSetHash(hash) {
|
setHash: function pdfViewSetHash(hash) {
|
||||||
@ -269,8 +481,16 @@ var PDFView = {
|
|||||||
if ('zoom' in params) {
|
if ('zoom' in params) {
|
||||||
var zoomArgs = params.zoom.split(','); // scale,left,top
|
var zoomArgs = params.zoom.split(','); // scale,left,top
|
||||||
// building destination array
|
// building destination array
|
||||||
var dest = [null, new Name('XYZ'), (zoomArgs[1] | 0),
|
|
||||||
(zoomArgs[2] | 0), (zoomArgs[0] | 0) / 100];
|
// If the zoom value, it has to get divided by 100. If it is a string,
|
||||||
|
// it should stay as it is.
|
||||||
|
var zoomArg = zoomArgs[0];
|
||||||
|
var zoomArgNumber = parseFloat(zoomArg);
|
||||||
|
if (zoomArgNumber)
|
||||||
|
zoomArg = zoomArgNumber / 100;
|
||||||
|
|
||||||
|
var dest = [null, {name: 'XYZ'}, (zoomArgs[1] | 0),
|
||||||
|
(zoomArgs[2] | 0), zoomArg];
|
||||||
var currentPage = this.pages[pageNumber - 1];
|
var currentPage = this.pages[pageNumber - 1];
|
||||||
currentPage.scrollIntoView(dest);
|
currentPage.scrollIntoView(dest);
|
||||||
} else
|
} else
|
||||||
@ -294,6 +514,7 @@ var PDFView = {
|
|||||||
outlineScrollView.setAttribute('hidden', 'true');
|
outlineScrollView.setAttribute('hidden', 'true');
|
||||||
thumbsSwitchButton.setAttribute('data-selected', true);
|
thumbsSwitchButton.setAttribute('data-selected', true);
|
||||||
outlineSwitchButton.removeAttribute('data-selected');
|
outlineSwitchButton.removeAttribute('data-selected');
|
||||||
|
updateThumbViewArea();
|
||||||
break;
|
break;
|
||||||
case 'outline':
|
case 'outline':
|
||||||
thumbsScrollView.setAttribute('hidden', 'true');
|
thumbsScrollView.setAttribute('hidden', 'true');
|
||||||
@ -328,6 +549,34 @@ var PDFView = {
|
|||||||
currentHeight += singlePage.height * singlePage.scale + kBottomMargin;
|
currentHeight += singlePage.height * singlePage.scale + kBottomMargin;
|
||||||
}
|
}
|
||||||
return visiblePages;
|
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())
|
while (div.hasChildNodes())
|
||||||
div.removeChild(div.lastChild);
|
div.removeChild(div.lastChild);
|
||||||
div.removeAttribute('data-loaded');
|
div.removeAttribute('data-loaded');
|
||||||
|
|
||||||
|
delete this.canvas;
|
||||||
};
|
};
|
||||||
|
|
||||||
function setupLinks(content, scale) {
|
function setupAnnotations(content, scale) {
|
||||||
function bindLink(link, dest) {
|
function bindLink(link, dest) {
|
||||||
link.href = PDFView.getDestinationHash(dest);
|
link.href = PDFView.getDestinationHash(dest);
|
||||||
link.onclick = function pageViewSetupLinksOnclick() {
|
link.onclick = function pageViewSetupLinksOnclick() {
|
||||||
@ -371,18 +622,67 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
|
|||||||
return false;
|
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();
|
var image = createElementWithStyle('img', item);
|
||||||
for (var i = 0; i < links.length; i++) {
|
image.src = kImageDirectory + type.toLowerCase() + '.svg';
|
||||||
var link = document.createElement('a');
|
var content = document.createElement('div');
|
||||||
link.style.left = (Math.floor(links[i].x - view.x) * scale) + 'px';
|
content.setAttribute('hidden', true);
|
||||||
link.style.top = (Math.floor(links[i].y - view.y) * scale) + 'px';
|
var title = document.createElement('h1');
|
||||||
link.style.width = Math.ceil(links[i].width * scale) + 'px';
|
var text = document.createElement('p');
|
||||||
link.style.height = Math.ceil(links[i].height * scale) + 'px';
|
var offsetPos = Math.floor(item.x - view.x + item.width);
|
||||||
link.href = links[i].url || '';
|
content.style.left = (offsetPos * scale) + 'px';
|
||||||
if (!links[i].url)
|
content.style.top = (Math.floor(item.y - view.y) * scale) + 'px';
|
||||||
bindLink(link, ('dest' in links[i]) ? links[i].dest : null);
|
title.textContent = item.title;
|
||||||
div.appendChild(link);
|
|
||||||
|
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)
|
if (scale && scale !== PDFView.currentScale)
|
||||||
PDFView.setScale(scale, true);
|
PDFView.parseScale(scale, true);
|
||||||
|
|
||||||
setTimeout(function pageViewScrollIntoViewRelayout() {
|
setTimeout(function pageViewScrollIntoViewRelayout() {
|
||||||
// letting page to re-layout before scrolling
|
// letting page to re-layout before scrolling
|
||||||
@ -464,16 +764,26 @@ var PageView = function pageView(container, content, id, pageWidth, pageHeight,
|
|||||||
}, 0);
|
}, 0);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.draw = function pageviewDraw() {
|
this.drawingRequired = function() {
|
||||||
if (div.hasChildNodes()) {
|
return !div.hasChildNodes();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.draw = function pageviewDraw(callback) {
|
||||||
|
if (!this.drawingRequired()) {
|
||||||
this.updateStats();
|
this.updateStats();
|
||||||
return false;
|
callback();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var canvas = document.createElement('canvas');
|
var canvas = document.createElement('canvas');
|
||||||
canvas.id = 'page' + this.id;
|
canvas.id = 'page' + this.id;
|
||||||
canvas.mozOpaque = true;
|
canvas.mozOpaque = true;
|
||||||
div.appendChild(canvas);
|
div.appendChild(canvas);
|
||||||
|
this.canvas = canvas;
|
||||||
|
|
||||||
|
var textLayer = document.createElement('div');
|
||||||
|
textLayer.className = 'textLayer';
|
||||||
|
div.appendChild(textLayer);
|
||||||
|
|
||||||
var scale = this.scale;
|
var scale = this.scale;
|
||||||
canvas.width = pageWidth * 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);
|
ctx.translate(-this.x * scale, -this.y * scale);
|
||||||
|
|
||||||
stats.begin = Date.now();
|
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);
|
div.setAttribute('data-loaded', true);
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
this.updateStats = function pageViewUpdateStats() {
|
this.updateStats = function pageViewUpdateStats() {
|
||||||
@ -511,6 +830,19 @@ var ThumbnailView = function thumbnailView(container, page, id, pageRatio) {
|
|||||||
return false;
|
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');
|
var div = document.createElement('div');
|
||||||
div.id = 'thumbnailContainer' + id;
|
div.id = 'thumbnailContainer' + id;
|
||||||
div.className = 'thumbnail';
|
div.className = 'thumbnail';
|
||||||
@ -518,19 +850,15 @@ var ThumbnailView = function thumbnailView(container, page, id, pageRatio) {
|
|||||||
anchor.appendChild(div);
|
anchor.appendChild(div);
|
||||||
container.appendChild(anchor);
|
container.appendChild(anchor);
|
||||||
|
|
||||||
this.draw = function thumbnailViewDraw() {
|
this.hasImage = false;
|
||||||
if (div.hasChildNodes())
|
|
||||||
return;
|
|
||||||
|
|
||||||
|
function getPageDrawContext() {
|
||||||
var canvas = document.createElement('canvas');
|
var canvas = document.createElement('canvas');
|
||||||
canvas.id = 'thumbnail' + id;
|
canvas.id = 'thumbnail' + id;
|
||||||
canvas.mozOpaque = true;
|
canvas.mozOpaque = true;
|
||||||
|
|
||||||
var maxThumbSize = 134;
|
canvas.width = canvasWidth;
|
||||||
canvas.width = pageRatio >= 1 ? maxThumbSize :
|
canvas.height = canvasHeight;
|
||||||
maxThumbSize * pageRatio;
|
|
||||||
canvas.height = pageRatio <= 1 ? maxThumbSize :
|
|
||||||
maxThumbSize / pageRatio;
|
|
||||||
|
|
||||||
div.setAttribute('data-loaded', true);
|
div.setAttribute('data-loaded', true);
|
||||||
div.appendChild(canvas);
|
div.appendChild(canvas);
|
||||||
@ -542,14 +870,37 @@ var ThumbnailView = function thumbnailView(container, page, id, pageRatio) {
|
|||||||
ctx.restore();
|
ctx.restore();
|
||||||
|
|
||||||
var view = page.view;
|
var view = page.view;
|
||||||
var scaleX = (canvas.width / page.width);
|
|
||||||
var scaleY = (canvas.height / page.height);
|
|
||||||
ctx.translate(-view.x * scaleX, -view.y * scaleY);
|
ctx.translate(-view.x * scaleX, -view.y * scaleY);
|
||||||
div.style.width = (view.width * scaleX) + 'px';
|
div.style.width = (view.width * scaleX) + 'px';
|
||||||
div.style.height = (view.height * scaleY) + 'px';
|
div.style.height = (view.height * scaleY) + 'px';
|
||||||
div.style.lineHeight = (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) {
|
window.addEventListener('load', function webViewerLoad(evt) {
|
||||||
var params = document.location.search.substring(1).split('&');
|
var params = document.location.search.substring(1).split('&');
|
||||||
for (var i = 0; i < params.length; i++) {
|
for (var i = 0; i < params.length; i++) {
|
||||||
@ -596,44 +994,95 @@ window.addEventListener('load', function webViewerLoad(evt) {
|
|||||||
params[unescape(param[0])] = unescape(param[1]);
|
params[unescape(param[0])] = unescape(param[1]);
|
||||||
}
|
}
|
||||||
|
|
||||||
var scale = ('scale' in params) ? params.scale : kDefaultScale;
|
var scale = ('scale' in params) ? params.scale : 0;
|
||||||
PDFView.open(params.file || kDefaultURL, parseFloat(scale));
|
PDFView.open(params.file || kDefaultURL, parseFloat(scale));
|
||||||
|
|
||||||
if (!window.File || !window.FileReader || !window.FileList || !window.Blob)
|
if (!window.File || !window.FileReader || !window.FileList || !window.Blob)
|
||||||
document.getElementById('fileInput').setAttribute('hidden', 'true');
|
document.getElementById('fileInput').setAttribute('hidden', 'true');
|
||||||
else
|
else
|
||||||
document.getElementById('fileInput').value = null;
|
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);
|
}, true);
|
||||||
|
|
||||||
window.addEventListener('unload', function webViewerUnload(evt) {
|
window.addEventListener('unload', function webViewerUnload(evt) {
|
||||||
window.scrollTo(0, 0);
|
window.scrollTo(0, 0);
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Render the next not yet visible page already such that it is
|
||||||
|
* hopefully ready once the user scrolls to it.
|
||||||
|
*/
|
||||||
|
function preDraw() {
|
||||||
|
var pages = PDFView.pages;
|
||||||
|
var visible = PDFView.getVisiblePages();
|
||||||
|
var last = visible[visible.length - 1];
|
||||||
|
// PageView.id is the actual page number, which is + 1 compared
|
||||||
|
// to the index in `pages`. That means, pages[last.id] is the next
|
||||||
|
// PageView instance.
|
||||||
|
if (pages[last.id] && pages[last.id].drawingRequired()) {
|
||||||
|
renderingQueue.enqueueDraw(pages[last.id]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// If there is nothing to draw on the next page, maybe the user
|
||||||
|
// is scrolling up, so, let's try to render the next page *before*
|
||||||
|
// the first visible page
|
||||||
|
if (pages[visible[0].id - 2]) {
|
||||||
|
renderingQueue.enqueueDraw(pages[visible[0].id - 2]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function updateViewarea() {
|
function updateViewarea() {
|
||||||
var visiblePages = PDFView.getVisiblePages();
|
var visiblePages = PDFView.getVisiblePages();
|
||||||
|
var pageToDraw;
|
||||||
for (var i = 0; i < visiblePages.length; i++) {
|
for (var i = 0; i < visiblePages.length; i++) {
|
||||||
var page = visiblePages[i];
|
var page = visiblePages[i];
|
||||||
if (PDFView.pages[page.id - 1].draw())
|
var pageObj = PDFView.pages[page.id - 1];
|
||||||
cache.push(page.view);
|
|
||||||
|
pageToDraw |= pageObj.drawingRequired();
|
||||||
|
renderingQueue.enqueueDraw(pageObj);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!visiblePages.length)
|
if (!visiblePages.length)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// If there is no need to draw a page that is currenlty visible, preDraw the
|
||||||
|
// next page the user might scroll to.
|
||||||
|
if (!pageToDraw) {
|
||||||
|
preDraw();
|
||||||
|
}
|
||||||
|
|
||||||
updateViewarea.inProgress = true; // used in "set page"
|
updateViewarea.inProgress = true; // used in "set page"
|
||||||
var currentId = PDFView.page;
|
var currentId = PDFView.page;
|
||||||
var firstPage = visiblePages[0];
|
var firstPage = visiblePages[0];
|
||||||
PDFView.page = firstPage.id;
|
PDFView.page = firstPage.id;
|
||||||
updateViewarea.inProgress = false;
|
updateViewarea.inProgress = false;
|
||||||
|
|
||||||
|
var currentScale = PDFView.currentScale;
|
||||||
|
var currentScaleValue = PDFView.currentScaleValue;
|
||||||
|
var normalizedScaleValue = currentScaleValue == currentScale ?
|
||||||
|
currentScale * 100 : currentScaleValue;
|
||||||
|
|
||||||
var kViewerTopMargin = 52;
|
var kViewerTopMargin = 52;
|
||||||
var pageNumber = firstPage.id;
|
var pageNumber = firstPage.id;
|
||||||
var pdfOpenParams = '#page=' + pageNumber;
|
var pdfOpenParams = '#page=' + pageNumber;
|
||||||
pdfOpenParams += '&zoom=' + Math.round(PDFView.currentScale * 100);
|
pdfOpenParams += '&zoom=' + normalizedScaleValue;
|
||||||
var currentPage = PDFView.pages[pageNumber - 1];
|
var currentPage = PDFView.pages[pageNumber - 1];
|
||||||
var topLeft = currentPage.getPagePoint(window.pageXOffset,
|
var topLeft = currentPage.getPagePoint(window.pageXOffset,
|
||||||
window.pageYOffset - firstPage.y - kViewerTopMargin);
|
window.pageYOffset - firstPage.y - kViewerTopMargin);
|
||||||
pdfOpenParams += ',' + Math.round(topLeft.x) + ',' + Math.round(topLeft.y);
|
pdfOpenParams += ',' + Math.round(topLeft.x) + ',' + Math.round(topLeft.y);
|
||||||
|
|
||||||
|
var store = PDFView.store;
|
||||||
|
store.set('exists', true);
|
||||||
|
store.set('page', pageNumber);
|
||||||
|
store.set('zoom', normalizedScaleValue);
|
||||||
|
store.set('scrollLeft', Math.round(topLeft.x));
|
||||||
|
store.set('scrollTop', Math.round(topLeft.y));
|
||||||
|
|
||||||
document.getElementById('viewBookmark').href = pdfOpenParams;
|
document.getElementById('viewBookmark').href = pdfOpenParams;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -641,9 +1090,33 @@ window.addEventListener('scroll', function webViewerScroll(evt) {
|
|||||||
updateViewarea();
|
updateViewarea();
|
||||||
}, true);
|
}, 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) {
|
window.addEventListener('resize', function webViewerResize(evt) {
|
||||||
if (document.getElementById('pageWidthOption').selected ||
|
if (document.getElementById('pageWidthOption').selected ||
|
||||||
document.getElementById('pageFitOption').selected)
|
document.getElementById('pageFitOption').selected ||
|
||||||
|
document.getElementById('pageAutoOption').selected)
|
||||||
PDFView.parseScale(document.getElementById('scaleSelect').value);
|
PDFView.parseScale(document.getElementById('scaleSelect').value);
|
||||||
updateViewarea();
|
updateViewarea();
|
||||||
});
|
});
|
||||||
@ -673,7 +1146,6 @@ window.addEventListener('change', function webViewerChange(evt) {
|
|||||||
// implemented in Firefox.
|
// implemented in Firefox.
|
||||||
var file = files[0];
|
var file = files[0];
|
||||||
fileReader.readAsBinaryString(file);
|
fileReader.readAsBinaryString(file);
|
||||||
|
|
||||||
document.title = file.name;
|
document.title = file.name;
|
||||||
|
|
||||||
// URL does not reflect proper document location - hiding some icons.
|
// URL does not reflect proper document location - hiding some icons.
|
||||||
@ -681,35 +1153,9 @@ window.addEventListener('change', function webViewerChange(evt) {
|
|||||||
document.getElementById('download').setAttribute('hidden', 'true');
|
document.getElementById('download').setAttribute('hidden', 'true');
|
||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
window.addEventListener('transitionend', function webViewerTransitionend(evt) {
|
function selectScaleOption(value) {
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
var options = document.getElementById('scaleSelect').options;
|
var options = document.getElementById('scaleSelect').options;
|
||||||
var predefinedValueFound = false;
|
var predefinedValueFound = false;
|
||||||
var value = '' + evt.scale;
|
|
||||||
for (var i = 0; i < options.length; i++) {
|
for (var i = 0; i < options.length; i++) {
|
||||||
var option = options[i];
|
var option = options[i];
|
||||||
if (option.value != value) {
|
if (option.value != value) {
|
||||||
@ -719,7 +1165,22 @@ window.addEventListener('scalechange', function scalechange(evt) {
|
|||||||
option.selected = true;
|
option.selected = true;
|
||||||
predefinedValueFound = true;
|
predefinedValueFound = true;
|
||||||
}
|
}
|
||||||
|
return predefinedValueFound;
|
||||||
|
}
|
||||||
|
|
||||||
|
window.addEventListener('scalechange', function scalechange(evt) {
|
||||||
|
var customScaleOption = document.getElementById('customScaleOption');
|
||||||
|
customScaleOption.selected = false;
|
||||||
|
|
||||||
|
if (!evt.resetAutoSettings &&
|
||||||
|
(document.getElementById('pageWidthOption').selected ||
|
||||||
|
document.getElementById('pageFitOption').selected ||
|
||||||
|
document.getElementById('pageAutoOption').selected)) {
|
||||||
|
updateViewarea();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var predefinedValueFound = selectScaleOption('' + evt.scale);
|
||||||
if (!predefinedValueFound) {
|
if (!predefinedValueFound) {
|
||||||
customScaleOption.textContent = Math.round(evt.scale * 10000) / 100 + '%';
|
customScaleOption.textContent = Math.round(evt.scale * 10000) / 100 + '%';
|
||||||
customScaleOption.selected = true;
|
customScaleOption.selected = true;
|
||||||
@ -737,25 +1198,49 @@ window.addEventListener('pagechange', function pagechange(evt) {
|
|||||||
}, true);
|
}, true);
|
||||||
|
|
||||||
window.addEventListener('keydown', function keydown(evt) {
|
window.addEventListener('keydown', function keydown(evt) {
|
||||||
|
if (evt.ctrlKey || evt.altKey || evt.shiftKey || evt.metaKey)
|
||||||
|
return;
|
||||||
var curElement = document.activeElement;
|
var curElement = document.activeElement;
|
||||||
|
if (curElement && curElement.tagName == 'INPUT')
|
||||||
|
return;
|
||||||
var controlsElement = document.getElementById('controls');
|
var controlsElement = document.getElementById('controls');
|
||||||
while (curElement) {
|
while (curElement) {
|
||||||
if (curElement === controlsElement)
|
if (curElement === controlsElement)
|
||||||
return; // ignoring if the 'controls' element is focused
|
return; // ignoring if the 'controls' element is focused
|
||||||
curElement = curElement.parentNode;
|
curElement = curElement.parentNode;
|
||||||
}
|
}
|
||||||
|
var handled = false;
|
||||||
switch (evt.keyCode) {
|
switch (evt.keyCode) {
|
||||||
case 61: // FF/Mac '='
|
case 61: // FF/Mac '='
|
||||||
case 107: // FF '+' and '='
|
case 107: // FF '+' and '='
|
||||||
case 187: // Chrome '+'
|
case 187: // Chrome '+'
|
||||||
PDFView.zoomIn();
|
PDFView.zoomIn();
|
||||||
|
handled = true;
|
||||||
break;
|
break;
|
||||||
case 109: // FF '-'
|
case 109: // FF '-'
|
||||||
case 189: // Chrome '-'
|
case 189: // Chrome '-'
|
||||||
PDFView.zoomOut();
|
PDFView.zoomOut();
|
||||||
|
handled = true;
|
||||||
break;
|
break;
|
||||||
case 48: // '0'
|
case 48: // '0'
|
||||||
PDFView.setScale(kDefaultScale, true);
|
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;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (handled) {
|
||||||
|
evt.preventDefault();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
Loading…
Reference in New Issue
Block a user