Merge branch 'master' into tiling
This commit is contained in:
commit
07b6ee2d03
3
LICENSE
3
LICENSE
@ -4,6 +4,9 @@
|
||||
Contributors: Andreas Gal <gal@mozilla.com>
|
||||
Chris G Jones <cjones@mozilla.com>
|
||||
Shaon Barman <shaon.barman@gmail.com>
|
||||
Vivien Nicolas <21@vingtetun.org>
|
||||
Justin D'Arcangelo <justindarc@gmail.com>
|
||||
Yury Delendik
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
|
46
fonts.js
46
fonts.js
@ -1,6 +1,8 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* Maximum file size of the font.
|
||||
*/
|
||||
@ -30,6 +32,7 @@ var fontCount = 0;
|
||||
*/
|
||||
var Fonts = {
|
||||
_active: null,
|
||||
|
||||
get active() {
|
||||
return this._active;
|
||||
},
|
||||
@ -38,12 +41,34 @@ var Fonts = {
|
||||
this._active = this[aName];
|
||||
},
|
||||
|
||||
unicodeFromCode: function fonts_unicodeFromCode(aCode) {
|
||||
chars2Unicode: function(chars) {
|
||||
var active = this._active;
|
||||
if (!active || !active.properties.encoding)
|
||||
return aCode;
|
||||
if (!active)
|
||||
return chars;
|
||||
|
||||
return GlyphsUnicode[active.properties.encoding[aCode]];
|
||||
// if we translated this string before, just grab it from the cache
|
||||
var ret = active.cache[chars];
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
// translate the string using the font's encoding
|
||||
var encoding = active.properties.encoding;
|
||||
if (!encoding)
|
||||
return chars;
|
||||
|
||||
var ret = "";
|
||||
for (var i = 0; i < chars.length; ++i) {
|
||||
var ch = chars.charCodeAt(i);
|
||||
var uc = encoding[ch];
|
||||
if (uc instanceof Name) // we didn't convert the glyph yet
|
||||
uc = encoding[ch] = GlyphsUnicode[uc.name];
|
||||
ret += String.fromCharCode(uc);
|
||||
}
|
||||
|
||||
// enter the translated string into the cache
|
||||
active.cache[chars] = ret;
|
||||
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
|
||||
@ -83,7 +108,8 @@ var Font = function(aName, aFile, aProperties) {
|
||||
encoding: {},
|
||||
charset: null
|
||||
},
|
||||
loading: false
|
||||
loading: false,
|
||||
cache: Object.create(null)
|
||||
};
|
||||
|
||||
this.mimetype = "font/ttf";
|
||||
@ -99,7 +125,8 @@ var Font = function(aName, aFile, aProperties) {
|
||||
Fonts[aName] = {
|
||||
data: this.font,
|
||||
properties: aProperties,
|
||||
loading: true
|
||||
loading: true,
|
||||
cache: Object.create(null)
|
||||
}
|
||||
|
||||
// Attach the font to the document
|
||||
@ -178,7 +205,7 @@ Font.prototype = {
|
||||
}
|
||||
}
|
||||
ctx.font = "bold italic 20px " + fontName + ", Symbol, Arial";
|
||||
var textWidth = ctx.mozMeasureText(testString);
|
||||
var textWidth = ctx.measureText(testString).width;
|
||||
|
||||
if (debug)
|
||||
ctx.fillText(testString, 20, 20);
|
||||
@ -193,7 +220,7 @@ Font.prototype = {
|
||||
window.clearInterval(interval);
|
||||
Fonts[fontName].loading = false;
|
||||
warn("Is " + fontName + " for charset: " + charset + " loaded?");
|
||||
} else if (textWidth != ctx.mozMeasureText(testString)) {
|
||||
} else if (textWidth != ctx.measureText(testString).width) {
|
||||
window.clearInterval(interval);
|
||||
Fonts[fontName].loading = false;
|
||||
}
|
||||
@ -1017,7 +1044,8 @@ var Type1Parser = function() {
|
||||
this.extractFontProgram = function t1_extractFontProgram(aStream) {
|
||||
var eexecString = decrypt(aStream, kEexecEncryptionKey, 4);
|
||||
var subrs = [], glyphs = [];
|
||||
var inSubrs = inGlyphs = false;
|
||||
var inGlyphs = false;
|
||||
var inSubrs = false;
|
||||
var glyph = "";
|
||||
|
||||
var token = "";
|
||||
|
@ -1,3 +1,8 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
"use strict";
|
||||
|
||||
var GlyphsUnicode = {
|
||||
A: 0x0041,
|
||||
AE: 0x00C6,
|
||||
|
BIN
images/buttons.png
Normal file
BIN
images/buttons.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.7 KiB |
BIN
images/source/Buttons.psd.zip
Normal file
BIN
images/source/Buttons.psd.zip
Normal file
Binary file not shown.
123
multi-page-viewer.css
Normal file
123
multi-page-viewer.css
Normal file
@ -0,0 +1,123 @@
|
||||
/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- /
|
||||
/* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */
|
||||
|
||||
body {
|
||||
background-color: #929292;
|
||||
font-family: 'Lucida Grande', 'Lucida Sans Unicode', Helvetica, Arial, Verdana, sans-serif;
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
}
|
||||
|
||||
canvas {
|
||||
box-shadow: 0px 4px 10px #000;
|
||||
-moz-box-shadow: 0px 4px 10px #000;
|
||||
-webkit-box-shadow: 0px 4px 10px #000;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.control {
|
||||
display: inline-block;
|
||||
float: left;
|
||||
margin: 0px 20px 0px 0px;
|
||||
padding: 0px 4px 0px 0px;
|
||||
}
|
||||
|
||||
.control > input {
|
||||
float: left;
|
||||
margin: 0px 2px 0px 0px;
|
||||
}
|
||||
|
||||
.control > span {
|
||||
cursor: default;
|
||||
float: left;
|
||||
height: 18px;
|
||||
margin: 5px 2px 0px;
|
||||
padding: 0px;
|
||||
user-select: none;
|
||||
-moz-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
.control .label {
|
||||
clear: both;
|
||||
float: left;
|
||||
font-size: 0.65em;
|
||||
margin: 2px 0px 0px;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.page {
|
||||
width: 816px;
|
||||
height: 1056px;
|
||||
margin: 10px auto;
|
||||
}
|
||||
|
||||
#controls {
|
||||
background-color: #eee;
|
||||
border-bottom: 1px solid #666;
|
||||
padding: 4px 0px 0px 8px;
|
||||
position:fixed;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
height: 40px;
|
||||
width: 100%;
|
||||
box-shadow: 0px 2px 8px #000;
|
||||
-moz-box-shadow: 0px 2px 8px #000;
|
||||
-webkit-box-shadow: 0px 2px 8px #000;
|
||||
}
|
||||
|
||||
#controls input {
|
||||
user-select: text;
|
||||
-moz-user-select: text;
|
||||
-webkit-user-select: text;
|
||||
}
|
||||
|
||||
#previousPageButton {
|
||||
background: url('images/buttons.png') no-repeat 0px -23px;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
float: left;
|
||||
margin: 0px;
|
||||
width: 28px;
|
||||
height: 23px;
|
||||
}
|
||||
|
||||
#previousPageButton.down {
|
||||
background: url('images/buttons.png') no-repeat 0px -46px;
|
||||
}
|
||||
|
||||
#previousPageButton.disabled {
|
||||
background: url('images/buttons.png') no-repeat 0px 0px;
|
||||
}
|
||||
|
||||
#nextPageButton {
|
||||
background: url('images/buttons.png') no-repeat -28px -23px;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
float: left;
|
||||
margin: 0px;
|
||||
width: 28px;
|
||||
height: 23px;
|
||||
}
|
||||
|
||||
#nextPageButton.down {
|
||||
background: url('images/buttons.png') no-repeat -28px -46px;
|
||||
}
|
||||
|
||||
#nextPageButton.disabled {
|
||||
background: url('images/buttons.png') no-repeat -28px 0px;
|
||||
}
|
||||
|
||||
#pageNumber, #scale {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#viewer {
|
||||
margin: 44px 0px 0px;
|
||||
padding: 8px 0px;
|
||||
}
|
31
multi-page-viewer.html
Normal file
31
multi-page-viewer.html
Normal file
@ -0,0 +1,31 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>pdf.js Multi-Page Viewer</title>
|
||||
<link rel="stylesheet" href="multi-page-viewer.css" type="text/css" media="screen" charset="utf-8"/>
|
||||
<script type="text/javascript" src="pdf.js"></script>
|
||||
<script type="text/javascript" src="fonts.js"></script>
|
||||
<script type="text/javascript" src="glyphlist.js"></script>
|
||||
<script type="text/javascript" src="multi-page-viewer.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="controls">
|
||||
<span class="control">
|
||||
<span id="previousPageButton"></span><span id="nextPageButton"></span>
|
||||
<span class="label">Previous/Next</span>
|
||||
</span>
|
||||
<span class="control">
|
||||
<input type="text" id="pageNumber" value="1" size="2"/>
|
||||
<span>/</span>
|
||||
<span id="numPages">--</span>
|
||||
<span class="label">Page Number</span>
|
||||
</span>
|
||||
<span class="control">
|
||||
<input type="text" id="scale" value="100" size="2"/>
|
||||
<span>%</span>
|
||||
<span class="label">Zoom</span>
|
||||
</span>
|
||||
</div>
|
||||
<div id="viewer"></div>
|
||||
</body>
|
||||
</html>
|
370
multi-page-viewer.js
Normal file
370
multi-page-viewer.js
Normal file
@ -0,0 +1,370 @@
|
||||
/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- /
|
||||
/* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */
|
||||
|
||||
"use strict";
|
||||
|
||||
var PDFViewer = {
|
||||
queryParams: {},
|
||||
|
||||
element: null,
|
||||
|
||||
pageNumberInput: null,
|
||||
previousPageButton: null,
|
||||
nextPageButton: null,
|
||||
|
||||
willJumpToPage: false,
|
||||
|
||||
pdf: null,
|
||||
|
||||
url: 'compressed.tracemonkey-pldi-09.pdf',
|
||||
pageNumber: 1,
|
||||
numberOfPages: 1,
|
||||
|
||||
scale: 1.0,
|
||||
|
||||
pageWidth: function() {
|
||||
return 816 * PDFViewer.scale;
|
||||
},
|
||||
|
||||
pageHeight: function() {
|
||||
return 1056 * PDFViewer.scale;
|
||||
},
|
||||
|
||||
lastPagesDrawn: [],
|
||||
|
||||
visiblePages: function() {
|
||||
var pageHeight = PDFViewer.pageHeight() + 20; // Add 20 for the margins.
|
||||
var windowTop = window.pageYOffset;
|
||||
var windowBottom = window.pageYOffset + window.innerHeight;
|
||||
var pageStartIndex = Math.floor(windowTop / pageHeight);
|
||||
var pageStopIndex = Math.ceil(windowBottom / pageHeight);
|
||||
|
||||
var pages = [];
|
||||
|
||||
for (var i = pageStartIndex; i <= pageStopIndex; i++) {
|
||||
pages.push(i + 1);
|
||||
}
|
||||
|
||||
return pages;
|
||||
},
|
||||
|
||||
createPage: function(num) {
|
||||
var anchor = document.createElement('a');
|
||||
anchor.name = '' + num;
|
||||
|
||||
var div = document.createElement('div');
|
||||
div.id = 'pageContainer' + num;
|
||||
div.className = 'page';
|
||||
div.style.width = PDFViewer.pageWidth() + 'px';
|
||||
div.style.height = PDFViewer.pageHeight() + 'px';
|
||||
|
||||
PDFViewer.element.appendChild(anchor);
|
||||
PDFViewer.element.appendChild(div);
|
||||
},
|
||||
|
||||
removePage: function(num) {
|
||||
var div = document.getElementById('pageContainer' + num);
|
||||
|
||||
if (div && div.hasChildNodes()) {
|
||||
while (div.childNodes.length > 0) {
|
||||
div.removeChild(div.firstChild);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
drawPage: function(num) {
|
||||
if (PDFViewer.pdf) {
|
||||
var page = PDFViewer.pdf.getPage(num);
|
||||
var div = document.getElementById('pageContainer' + num);
|
||||
|
||||
if (div && !div.hasChildNodes()) {
|
||||
var canvas = document.createElement('canvas');
|
||||
canvas.id = 'page' + num;
|
||||
canvas.mozOpaque = true;
|
||||
|
||||
// Canvas dimensions must be specified in CSS pixels. CSS pixels
|
||||
// are always 96 dpi. These dimensions are 8.5in x 11in at 96dpi.
|
||||
canvas.width = PDFViewer.pageWidth();
|
||||
canvas.height = PDFViewer.pageHeight();
|
||||
|
||||
var ctx = canvas.getContext('2d');
|
||||
ctx.save();
|
||||
ctx.fillStyle = 'rgb(255, 255, 255)';
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.restore();
|
||||
|
||||
var gfx = new CanvasGraphics(ctx);
|
||||
var fonts = [];
|
||||
|
||||
// page.compile will collect all fonts for us, once we have loaded them
|
||||
// we can trigger the actual page rendering with page.display
|
||||
page.compile(gfx, fonts);
|
||||
|
||||
var fontsReady = true;
|
||||
|
||||
// Inspect fonts and translate the missing one
|
||||
var fontCount = fonts.length;
|
||||
|
||||
for (var i = 0; i < fontCount; i++) {
|
||||
var font = fonts[i];
|
||||
|
||||
if (Fonts[font.name]) {
|
||||
fontsReady = fontsReady && !Fonts[font.name].loading;
|
||||
continue;
|
||||
}
|
||||
|
||||
new Font(font.name, font.file, font.properties);
|
||||
|
||||
fontsReady = false;
|
||||
}
|
||||
|
||||
var pageInterval;
|
||||
var delayLoadFont = function() {
|
||||
for (var i = 0; i < fontCount; i++) {
|
||||
if (Fonts[font.name].loading) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
clearInterval(pageInterval);
|
||||
|
||||
PDFViewer.drawPage(num);
|
||||
}
|
||||
|
||||
if (!fontsReady) {
|
||||
pageInterval = setInterval(delayLoadFont, 10);
|
||||
return;
|
||||
}
|
||||
|
||||
page.display(gfx);
|
||||
div.appendChild(canvas);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
changeScale: function(num) {
|
||||
while (PDFViewer.element.childNodes.length > 0) {
|
||||
PDFViewer.element.removeChild(PDFViewer.element.firstChild);
|
||||
}
|
||||
|
||||
PDFViewer.scale = num / 100;
|
||||
|
||||
if (PDFViewer.pdf) {
|
||||
for (var i = 1; i <= PDFViewer.numberOfPages; i++) {
|
||||
PDFViewer.createPage(i);
|
||||
}
|
||||
|
||||
if (PDFViewer.numberOfPages > 0) {
|
||||
PDFViewer.drawPage(1);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
goToPage: function(num) {
|
||||
if (1 <= num && num <= PDFViewer.numberOfPages) {
|
||||
PDFViewer.pageNumber = num;
|
||||
PDFViewer.pageNumberInput.value = PDFViewer.pageNumber;
|
||||
PDFViewer.willJumpToPage = true;
|
||||
|
||||
document.location.hash = PDFViewer.pageNumber;
|
||||
|
||||
PDFViewer.previousPageButton.className = (PDFViewer.pageNumber === 1) ?
|
||||
'disabled' : '';
|
||||
PDFViewer.nextPageButton.className = (PDFViewer.pageNumber === PDFViewer.numberOfPages) ?
|
||||
'disabled' : '';
|
||||
}
|
||||
},
|
||||
|
||||
goToPreviousPage: function() {
|
||||
if (PDFViewer.pageNumber > 1) {
|
||||
PDFViewer.goToPage(--PDFViewer.pageNumber);
|
||||
}
|
||||
},
|
||||
|
||||
goToNextPage: function() {
|
||||
if (PDFViewer.pageNumber < PDFViewer.numberOfPages) {
|
||||
PDFViewer.goToPage(++PDFViewer.pageNumber);
|
||||
}
|
||||
},
|
||||
|
||||
open: function(url) {
|
||||
PDFViewer.url = url;
|
||||
document.title = url;
|
||||
|
||||
var req = new XMLHttpRequest();
|
||||
req.open('GET', url);
|
||||
req.mozResponseType = req.responseType = 'arraybuffer';
|
||||
req.expected = (document.URL.indexOf('file:') === 0) ? 0 : 200;
|
||||
|
||||
req.onreadystatechange = function() {
|
||||
if (req.readyState === 4 && req.status === req.expected) {
|
||||
var data = req.mozResponseArrayBuffer ||
|
||||
req.mozResponse ||
|
||||
req.responseArrayBuffer ||
|
||||
req.response;
|
||||
|
||||
PDFViewer.pdf = new PDFDoc(new Stream(data));
|
||||
PDFViewer.numberOfPages = PDFViewer.pdf.numPages;
|
||||
document.getElementById('numPages').innerHTML = PDFViewer.numberOfPages.toString();
|
||||
|
||||
for (var i = 1; i <= PDFViewer.numberOfPages; i++) {
|
||||
PDFViewer.createPage(i);
|
||||
}
|
||||
|
||||
if (PDFViewer.numberOfPages > 0) {
|
||||
PDFViewer.drawPage(1);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
req.send(null);
|
||||
}
|
||||
};
|
||||
|
||||
window.onload = function() {
|
||||
|
||||
// Parse the URL query parameters into a cached object.
|
||||
PDFViewer.queryParams = function() {
|
||||
var qs = window.location.search.substring(1);
|
||||
var kvs = qs.split('&');
|
||||
var params = {};
|
||||
for (var i = 0; i < kvs.length; ++i) {
|
||||
var kv = kvs[i].split('=');
|
||||
params[unescape(kv[0])] = unescape(kv[1]);
|
||||
}
|
||||
|
||||
return params;
|
||||
}();
|
||||
|
||||
PDFViewer.element = document.getElementById('viewer');
|
||||
|
||||
PDFViewer.pageNumberInput = document.getElementById('pageNumber');
|
||||
PDFViewer.pageNumberInput.onkeydown = function(evt) {
|
||||
var charCode = evt.charCode || evt.keyCode;
|
||||
|
||||
// Up arrow key.
|
||||
if (charCode === 38) {
|
||||
PDFViewer.goToNextPage();
|
||||
this.select();
|
||||
}
|
||||
|
||||
// Down arrow key.
|
||||
else if (charCode === 40) {
|
||||
PDFViewer.goToPreviousPage();
|
||||
this.select();
|
||||
}
|
||||
|
||||
// All other non-numeric keys (excluding Left arrow, Right arrow,
|
||||
// Backspace, and Delete keys).
|
||||
else if ((charCode < 48 || charCode > 57) &&
|
||||
charCode !== 8 && // Backspace
|
||||
charCode !== 46 && // Delete
|
||||
charCode !== 37 && // Left arrow
|
||||
charCode !== 39 // Right arrow
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
PDFViewer.pageNumberInput.onkeyup = function(evt) {
|
||||
var charCode = evt.charCode || evt.keyCode;
|
||||
|
||||
// All numeric keys, Backspace, and Delete.
|
||||
if ((charCode >= 48 && charCode <= 57) ||
|
||||
charCode === 8 || // Backspace
|
||||
charCode === 46 // Delete
|
||||
) {
|
||||
PDFViewer.goToPage(this.value);
|
||||
}
|
||||
|
||||
this.focus();
|
||||
};
|
||||
|
||||
PDFViewer.previousPageButton = document.getElementById('previousPageButton');
|
||||
PDFViewer.previousPageButton.onclick = function(evt) {
|
||||
if (this.className.indexOf('disabled') === -1) {
|
||||
PDFViewer.goToPreviousPage();
|
||||
}
|
||||
};
|
||||
PDFViewer.previousPageButton.onmousedown = function(evt) {
|
||||
if (this.className.indexOf('disabled') === -1) {
|
||||
this.className = 'down';
|
||||
}
|
||||
};
|
||||
PDFViewer.previousPageButton.onmouseup = function(evt) {
|
||||
this.className = (this.className.indexOf('disabled') !== -1) ? 'disabled' : '';
|
||||
};
|
||||
PDFViewer.previousPageButton.onmouseout = function(evt) {
|
||||
this.className = (this.className.indexOf('disabled') !== -1) ? 'disabled' : '';
|
||||
};
|
||||
|
||||
PDFViewer.nextPageButton = document.getElementById('nextPageButton');
|
||||
PDFViewer.nextPageButton.onclick = function(evt) {
|
||||
if (this.className.indexOf('disabled') === -1) {
|
||||
PDFViewer.goToNextPage();
|
||||
}
|
||||
};
|
||||
PDFViewer.nextPageButton.onmousedown = function(evt) {
|
||||
if (this.className.indexOf('disabled') === -1) {
|
||||
this.className = 'down';
|
||||
}
|
||||
};
|
||||
PDFViewer.nextPageButton.onmouseup = function(evt) {
|
||||
this.className = (this.className.indexOf('disabled') !== -1) ? 'disabled' : '';
|
||||
};
|
||||
PDFViewer.nextPageButton.onmouseout = function(evt) {
|
||||
this.className = (this.className.indexOf('disabled') !== -1) ? 'disabled' : '';
|
||||
};
|
||||
|
||||
var scaleInput = document.getElementById('scale');
|
||||
scaleInput.onchange = function(evt) {
|
||||
PDFViewer.changeScale(this.value);
|
||||
};
|
||||
|
||||
PDFViewer.pageNumber = parseInt(PDFViewer.queryParams.page) || PDFViewer.pageNumber;
|
||||
PDFViewer.scale = parseInt(scaleInput.value) / 100 || 1.0;
|
||||
PDFViewer.open(PDFViewer.queryParams.file || PDFViewer.url);
|
||||
|
||||
window.onscroll = function(evt) {
|
||||
var lastPagesDrawn = PDFViewer.lastPagesDrawn;
|
||||
var visiblePages = PDFViewer.visiblePages();
|
||||
|
||||
var pagesToDraw = [];
|
||||
var pagesToKeep = [];
|
||||
var pagesToRemove = [];
|
||||
|
||||
var i;
|
||||
|
||||
// Determine which visible pages were not previously drawn.
|
||||
for (i = 0; i < visiblePages.length; i++) {
|
||||
if (lastPagesDrawn.indexOf(visiblePages[i]) === -1) {
|
||||
pagesToDraw.push(visiblePages[i]);
|
||||
PDFViewer.drawPage(visiblePages[i]);
|
||||
} else {
|
||||
pagesToKeep.push(visiblePages[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Determine which previously drawn pages are no longer visible.
|
||||
for (i = 0; i < lastPagesDrawn.length; i++) {
|
||||
if (visiblePages.indexOf(lastPagesDrawn[i]) === -1) {
|
||||
pagesToRemove.push(lastPagesDrawn[i]);
|
||||
PDFViewer.removePage(lastPagesDrawn[i]);
|
||||
}
|
||||
}
|
||||
|
||||
PDFViewer.lastPagesDrawn = pagesToDraw.concat(pagesToKeep);
|
||||
|
||||
// Update the page number input with the current page number.
|
||||
if (!PDFViewer.willJumpToPage && visiblePages.length > 0) {
|
||||
PDFViewer.pageNumber = PDFViewer.pageNumberInput.value = visiblePages[0];
|
||||
PDFViewer.previousPageButton.className = (PDFViewer.pageNumber === 1) ?
|
||||
'disabled' : '';
|
||||
PDFViewer.nextPageButton.className = (PDFViewer.pageNumber === PDFViewer.numberOfPages) ?
|
||||
'disabled' : '';
|
||||
} else {
|
||||
PDFViewer.willJumpToPage = false;
|
||||
}
|
||||
};
|
||||
};
|
223
pdf.js
223
pdf.js
@ -1,11 +1,12 @@
|
||||
/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- /
|
||||
/* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */
|
||||
|
||||
"use strict";
|
||||
|
||||
var ERRORS = 0, WARNINGS = 1, TODOS = 5;
|
||||
var verbosity = WARNINGS;
|
||||
|
||||
function log(msg) {
|
||||
msg = msg.toString ? msg.toString() : msg;
|
||||
if (console && console.log)
|
||||
console.log(msg);
|
||||
else if (print)
|
||||
@ -390,6 +391,12 @@ var FlateStream = (function() {
|
||||
return [codes, maxLen];
|
||||
},
|
||||
readBlock: function() {
|
||||
function repeat(stream, array, len, offset, what) {
|
||||
var repeat = stream.getBits(len) + offset;
|
||||
while (repeat-- > 0)
|
||||
array[i++] = what;
|
||||
}
|
||||
|
||||
var stream = this.stream;
|
||||
|
||||
// read block header
|
||||
@ -450,11 +457,6 @@ var FlateStream = (function() {
|
||||
var codes = numLitCodes + numDistCodes;
|
||||
var codeLengths = new Array(codes);
|
||||
while (i < codes) {
|
||||
function repeat(stream, array, len, offset, what) {
|
||||
var repeat = stream.getBits(len) + offset;
|
||||
while (repeat-- > 0)
|
||||
array[i++] = what;
|
||||
}
|
||||
var code = this.getCode(codeLenCodeTab);
|
||||
if (code == 16) {
|
||||
repeat(this, codeLengths, 2, 3, len);
|
||||
@ -507,6 +509,94 @@ var FlateStream = (function() {
|
||||
return constructor;
|
||||
})();
|
||||
|
||||
var PredictorStream = (function() {
|
||||
function constructor(stream, params) {
|
||||
this.stream = stream;
|
||||
this.predictor = params.get("Predictor") || 1;
|
||||
if (this.predictor <= 1) {
|
||||
return stream; // no prediction
|
||||
}
|
||||
if (params.has("EarlyChange")) {
|
||||
error("EarlyChange predictor parameter is not supported");
|
||||
}
|
||||
this.colors = params.get("Colors") || 1;
|
||||
this.bitsPerComponent = params.get("BitsPerComponent") || 8;
|
||||
this.columns = params.get("Columns") || 1;
|
||||
if (this.colors !== 1 || this.bitsPerComponent !== 8) {
|
||||
error("Multi-color and multi-byte predictors are not supported");
|
||||
}
|
||||
if (this.predictor < 10 || this.predictor > 15) {
|
||||
error("Unsupported predictor");
|
||||
}
|
||||
this.currentRow = new Uint8Array(this.columns);
|
||||
this.pos = 0;
|
||||
this.bufferLength = 0;
|
||||
}
|
||||
|
||||
constructor.prototype = {
|
||||
readRow : function() {
|
||||
var lastRow = this.currentRow;
|
||||
var predictor = this.stream.getByte();
|
||||
var currentRow = this.stream.getBytes(this.columns), i;
|
||||
switch (predictor) {
|
||||
default:
|
||||
error("Unsupported predictor");
|
||||
break;
|
||||
case 0:
|
||||
break;
|
||||
case 2:
|
||||
for (i = 0; i < currentRow.length; ++i) {
|
||||
currentRow[i] = (lastRow[i] + currentRow[i]) & 0xFF;
|
||||
}
|
||||
break;
|
||||
}
|
||||
this.pos = 0;
|
||||
this.bufferLength = currentRow.length;
|
||||
this.currentRow = currentRow;
|
||||
},
|
||||
getByte : function() {
|
||||
if (this.pos >= this.bufferLength) {
|
||||
this.readRow();
|
||||
}
|
||||
return this.currentRow[this.pos++];
|
||||
},
|
||||
getBytes : function(n) {
|
||||
var i, bytes;
|
||||
bytes = new Uint8Array(n);
|
||||
for (i = 0; i < n; ++i) {
|
||||
if (this.pos >= this.bufferLength) {
|
||||
this.readRow();
|
||||
}
|
||||
bytes[i] = this.currentRow[this.pos++];
|
||||
}
|
||||
return bytes;
|
||||
},
|
||||
getChar : function() {
|
||||
return String.formCharCode(this.getByte());
|
||||
},
|
||||
lookChar : function() {
|
||||
if (this.pos >= this.bufferLength) {
|
||||
this.readRow();
|
||||
}
|
||||
return String.formCharCode(this.currentRow[this.pos]);
|
||||
},
|
||||
skip : function(n) {
|
||||
var i;
|
||||
if (!n) {
|
||||
n = 1;
|
||||
}
|
||||
while (n > this.bufferLength - this.pos) {
|
||||
n -= this.bufferLength - this.pos;
|
||||
this.readRow();
|
||||
if (this.bufferLength === 0) break;
|
||||
}
|
||||
this.pos += n;
|
||||
}
|
||||
};
|
||||
|
||||
return constructor;
|
||||
})();
|
||||
|
||||
var DecryptStream = (function() {
|
||||
function constructor(str, fileKey, encAlgorithm, keyLength) {
|
||||
// TODO
|
||||
@ -523,9 +613,6 @@ var Name = (function() {
|
||||
}
|
||||
|
||||
constructor.prototype = {
|
||||
toString: function() {
|
||||
return this.name;
|
||||
}
|
||||
};
|
||||
|
||||
return constructor;
|
||||
@ -537,9 +624,6 @@ var Cmd = (function() {
|
||||
}
|
||||
|
||||
constructor.prototype = {
|
||||
toString: function() {
|
||||
return this.cmd;
|
||||
}
|
||||
};
|
||||
|
||||
return constructor;
|
||||
@ -566,12 +650,6 @@ var Dict = (function() {
|
||||
forEach: function(aCallback) {
|
||||
for (var key in this.map)
|
||||
aCallback(key, this.map[key]);
|
||||
},
|
||||
toString: function() {
|
||||
var keys = [];
|
||||
for (var key in this.map)
|
||||
keys.push(key);
|
||||
return "Dict with " + keys.length + " keys: " + keys;
|
||||
}
|
||||
};
|
||||
|
||||
@ -738,6 +816,7 @@ var Lexer = (function() {
|
||||
var done = false;
|
||||
var str = "";
|
||||
var stream = this.stream;
|
||||
var ch;
|
||||
do {
|
||||
switch (ch = stream.getChar()) {
|
||||
case undefined:
|
||||
@ -1098,7 +1177,9 @@ var Parser = (function() {
|
||||
this.encAlgorithm,
|
||||
this.keyLength);
|
||||
}
|
||||
return this.filter(stream, dict);
|
||||
stream = this.filter(stream, dict);
|
||||
stream.parameters = dict;
|
||||
return stream;
|
||||
},
|
||||
filter: function(stream, dict) {
|
||||
var filter = dict.get2("Filter", "F");
|
||||
@ -1123,8 +1204,9 @@ var Parser = (function() {
|
||||
},
|
||||
makeFilter: function(stream, name, params) {
|
||||
if (name == "FlateDecode" || name == "Fl") {
|
||||
if (params)
|
||||
error("params not supported yet for FlateDecode");
|
||||
if (params) {
|
||||
return new PredictorStream(new FlateStream(stream), params);
|
||||
}
|
||||
return new FlateStream(stream);
|
||||
} else {
|
||||
error("filter '" + name + "' not supported yet");
|
||||
@ -1217,10 +1299,10 @@ var XRef = (function() {
|
||||
this.stream = stream;
|
||||
this.entries = [];
|
||||
this.xrefstms = {};
|
||||
this.readXRef(startXRef);
|
||||
var trailerDict = this.readXRef(startXRef);
|
||||
|
||||
// get the root dictionary (catalog) object
|
||||
if (!IsRef(this.root = this.trailerDict.get("Root")))
|
||||
if (!IsRef(this.root = trailerDict.get("Root")))
|
||||
error("Invalid root reference");
|
||||
|
||||
// prepare the XRef cache
|
||||
@ -1275,18 +1357,18 @@ var XRef = (function() {
|
||||
error("Invalid XRef table");
|
||||
|
||||
// get the 'Prev' pointer
|
||||
var more = false;
|
||||
var prev;
|
||||
obj = dict.get("Prev");
|
||||
if (IsInt(obj)) {
|
||||
this.prev = obj;
|
||||
more = true;
|
||||
prev = obj;
|
||||
} else if (IsRef(obj)) {
|
||||
// certain buggy PDF generators generate "/Prev NNN 0 R" instead
|
||||
// of "/Prev NNN"
|
||||
this.prev = obj.num;
|
||||
more = true;
|
||||
prev = obj.num;
|
||||
}
|
||||
if (prev) {
|
||||
this.readXRef(prev);
|
||||
}
|
||||
this.trailerDict = dict;
|
||||
|
||||
// check for 'XRefStm' key
|
||||
if (IsInt(obj = dict.get("XRefStm"))) {
|
||||
@ -1296,11 +1378,64 @@ var XRef = (function() {
|
||||
this.xrefstms[pos] = 1; // avoid infinite recursion
|
||||
this.readXRef(pos);
|
||||
}
|
||||
|
||||
return more;
|
||||
return dict;
|
||||
},
|
||||
readXRefStream: function(parser) {
|
||||
error("Invalid XRef stream");
|
||||
readXRefStream: function(stream) {
|
||||
var streamParameters = stream.parameters;
|
||||
var length = streamParameters.get("Length");
|
||||
var byteWidths = streamParameters.get("W");
|
||||
var range = streamParameters.get("Index");
|
||||
if (!range) {
|
||||
range = [0, streamParameters.get("Size")];
|
||||
}
|
||||
var i, j;
|
||||
while (range.length > 0) {
|
||||
var first = range[0], n = range[1];
|
||||
if (!IsInt(first) || !IsInt(n)) {
|
||||
error("Invalid XRef range fields");
|
||||
}
|
||||
var typeFieldWidth = byteWidths[0], offsetFieldWidth = byteWidths[1], generationFieldWidth = byteWidths[2];
|
||||
if (!IsInt(typeFieldWidth) || !IsInt(offsetFieldWidth) || !IsInt(generationFieldWidth)) {
|
||||
error("Invalid XRef entry fields length");
|
||||
}
|
||||
for (i = 0; i < n; ++i) {
|
||||
var type = 0, offset = 0, generation = 0;
|
||||
for (j = 0; j < typeFieldWidth; ++j) {
|
||||
type = (type << 8) | stream.getByte();
|
||||
}
|
||||
for (j = 0; j < offsetFieldWidth; ++j) {
|
||||
offset = (offset << 8) | stream.getByte();
|
||||
}
|
||||
for (j = 0; j < generationFieldWidth; ++j) {
|
||||
generation = (generation << 8) | stream.getByte();
|
||||
}
|
||||
var entry = { offset: offset, gen: generation };
|
||||
if (typeFieldWidth > 0) {
|
||||
switch (type) {
|
||||
case 0:
|
||||
entry.free = true;
|
||||
break;
|
||||
case 1:
|
||||
entry.uncompressed = true;
|
||||
break;
|
||||
case 2:
|
||||
break;
|
||||
default:
|
||||
error("Invalid XRef entry type");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!this.entries[first + i]) {
|
||||
this.entries[first + i] = entry;
|
||||
}
|
||||
}
|
||||
range.splice(0, 2);
|
||||
}
|
||||
var prev = streamParameters.get("Prev");
|
||||
if (IsInt(prev)) {
|
||||
this.readXRef(prev);
|
||||
}
|
||||
return streamParameters;
|
||||
},
|
||||
readXRef: function(startXRef) {
|
||||
var stream = this.stream;
|
||||
@ -1442,7 +1577,7 @@ var Catalog = (function() {
|
||||
return shadow(this, "toplevelPagesDict", obj);
|
||||
},
|
||||
get numPages() {
|
||||
obj = this.toplevelPagesDict.get("Count");
|
||||
var obj = this.toplevelPagesDict.get("Count");
|
||||
assertWellFormed(IsInt(obj),
|
||||
"page count in top level pages object is not an integer");
|
||||
// shadow the prototype getter
|
||||
@ -1584,7 +1719,7 @@ var PDFDoc = (function() {
|
||||
},
|
||||
getPage: function(n) {
|
||||
var linearization = this.linearization;
|
||||
assert(!linearization, "linearized page access not implemented");
|
||||
// assert(!linearization, "linearized page access not implemented");
|
||||
return this.catalog.getPage(n);
|
||||
}
|
||||
};
|
||||
@ -1925,9 +2060,11 @@ var CanvasGraphics = (function() {
|
||||
|
||||
// Get the font charset if any
|
||||
var charset = descriptor.get("CharSet");
|
||||
assertWellFormed(IsString(charset), "invalid charset");
|
||||
if (charset) {
|
||||
assertWellFormed(IsString(charset), "invalid charset");
|
||||
|
||||
charset = charset.split("/");
|
||||
charset = charset.split("/");
|
||||
}
|
||||
} else if (IsName(encoding)) {
|
||||
var encoding = Encodings[encoding.name];
|
||||
if (!encoding)
|
||||
@ -1935,7 +2072,7 @@ var CanvasGraphics = (function() {
|
||||
|
||||
var widths = xref.fetchIfRef(fontDict.get("Widths"));
|
||||
var firstChar = xref.fetchIfRef(fontDict.get("FirstChar"));
|
||||
assertWellFormed(IsArray(widths) && IsInteger(firstChar),
|
||||
assertWellFormed(IsArray(widths) && IsInt(firstChar),
|
||||
"invalid font Widths or FirstChar");
|
||||
var charset = [];
|
||||
for (var j = 0; j < widths.length; j++) {
|
||||
@ -2245,13 +2382,7 @@ var CanvasGraphics = (function() {
|
||||
this.ctx.translate(0, 2 * this.current.y);
|
||||
this.ctx.scale(1, -1);
|
||||
this.ctx.transform.apply(this.ctx, this.current.textMatrix);
|
||||
|
||||
// Replace characters code by glyphs code
|
||||
var glyphs = [];
|
||||
for (var i = 0; i < text.length; i++)
|
||||
glyphs[i] = String.fromCharCode(Fonts.unicodeFromCode(text[i].charCodeAt(0)));
|
||||
|
||||
this.ctx.fillText(glyphs.join(""), this.current.x, this.current.y);
|
||||
this.ctx.fillText(Fonts.chars2Unicode(text), this.current.x, this.current.y);
|
||||
this.current.x += this.ctx.measureText(text).width;
|
||||
|
||||
this.ctx.restore();
|
||||
@ -2514,7 +2645,7 @@ var CanvasGraphics = (function() {
|
||||
error("No support for array of functions");
|
||||
else if (!IsPDFFunction(fnObj))
|
||||
error("Invalid function");
|
||||
fn = new PDFFunction(this.xref, fnObj);
|
||||
var fn = new PDFFunction(this.xref, fnObj);
|
||||
|
||||
var gradient = this.ctx.createLinearGradient(x0, y0, x1, y1);
|
||||
|
||||
|
175
test.py
Normal file
175
test.py
Normal file
@ -0,0 +1,175 @@
|
||||
import json, os, sys, subprocess
|
||||
from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
|
||||
|
||||
ANAL = True
|
||||
VERBOSE = False
|
||||
|
||||
MIMEs = {
|
||||
'.css': 'text/css',
|
||||
'.html': 'text/html',
|
||||
'.js': 'application/json',
|
||||
'.json': 'application/json',
|
||||
'.pdf': 'application/pdf',
|
||||
'.xhtml': 'application/xhtml+xml',
|
||||
}
|
||||
|
||||
class State:
|
||||
browsers = [ ]
|
||||
manifest = { }
|
||||
taskResults = { }
|
||||
remaining = 0
|
||||
results = { }
|
||||
done = False
|
||||
|
||||
class Result:
|
||||
def __init__(self, snapshot, failure):
|
||||
self.snapshot = snapshot
|
||||
self.failure = failure
|
||||
|
||||
|
||||
class PDFTestHandler(BaseHTTPRequestHandler):
|
||||
# Disable annoying noise by default
|
||||
def log_request(code=0, size=0):
|
||||
if VERBOSE:
|
||||
BaseHTTPRequestHandler.log_request(code, size)
|
||||
|
||||
def do_GET(self):
|
||||
cwd = os.getcwd()
|
||||
path = os.path.abspath(os.path.realpath(cwd + os.sep + self.path))
|
||||
cwd = os.path.abspath(cwd)
|
||||
prefix = os.path.commonprefix(( path, cwd ))
|
||||
_, ext = os.path.splitext(path)
|
||||
|
||||
if not (prefix == cwd
|
||||
and os.path.isfile(path)
|
||||
and ext in MIMEs):
|
||||
self.send_error(404)
|
||||
return
|
||||
|
||||
if 'Range' in self.headers:
|
||||
# TODO for fetch-as-you-go
|
||||
self.send_error(501)
|
||||
return
|
||||
|
||||
self.send_response(200)
|
||||
self.send_header("Content-Type", MIMEs[ext])
|
||||
self.end_headers()
|
||||
|
||||
# Sigh, os.sendfile() plz
|
||||
f = open(path)
|
||||
self.wfile.write(f.read())
|
||||
f.close()
|
||||
|
||||
|
||||
def do_POST(self):
|
||||
numBytes = int(self.headers['Content-Length'])
|
||||
|
||||
self.send_response(200)
|
||||
self.send_header('Content-Type', 'text/plain')
|
||||
self.end_headers()
|
||||
|
||||
result = json.loads(self.rfile.read(numBytes))
|
||||
browser = 'firefox4'
|
||||
id, failure, round, page, snapshot = result['id'], result['failure'], result['round'], result['page'], result['snapshot']
|
||||
taskResults = State.taskResults[browser][id]
|
||||
taskResults[round][page - 1] = Result(snapshot, failure)
|
||||
|
||||
if result['taskDone']:
|
||||
check(State.manifest[id], taskResults, browser)
|
||||
State.remaining -= 1
|
||||
|
||||
State.done = (0 == State.remaining)
|
||||
|
||||
|
||||
def set_up():
|
||||
# Only serve files from a pdf.js clone
|
||||
assert not ANAL or os.path.isfile('pdf.js') and os.path.isdir('.git')
|
||||
|
||||
testBrowsers = [ b for b in
|
||||
( 'firefox4', )
|
||||
#'chrome12', 'chrome13', 'firefox5', 'firefox6','opera11' ):
|
||||
if os.access(b, os.R_OK | os.X_OK) ]
|
||||
|
||||
mf = open('test_manifest.json')
|
||||
manifestList = json.load(mf)
|
||||
mf.close()
|
||||
|
||||
for b in testBrowsers:
|
||||
State.taskResults[b] = { }
|
||||
for item in manifestList:
|
||||
id, rounds = item['id'], int(item['rounds'])
|
||||
State.manifest[id] = item
|
||||
taskResults = [ ]
|
||||
for r in xrange(rounds):
|
||||
taskResults.append([ None ] * 100)
|
||||
State.taskResults[b][id] = taskResults
|
||||
|
||||
State.remaining = len(manifestList)
|
||||
|
||||
for b in testBrowsers:
|
||||
print 'Launching', b
|
||||
subprocess.Popen(( os.path.abspath(os.path.realpath(b)),
|
||||
'http://localhost:8080/test_slave.html' ))
|
||||
|
||||
|
||||
def check(task, results, browser):
|
||||
failed = False
|
||||
for r in xrange(len(results)):
|
||||
pageResults = results[r]
|
||||
for p in xrange(len(pageResults)):
|
||||
pageResult = pageResults[p]
|
||||
if pageResult is None:
|
||||
continue
|
||||
failure = pageResult.failure
|
||||
if failure:
|
||||
failed = True
|
||||
print 'TEST-UNEXPECTED-FAIL | test failed', task['id'], '| in', browser, '| page', p + 1, 'round', r, '|', failure
|
||||
|
||||
if failed:
|
||||
return
|
||||
|
||||
kind = task['type']
|
||||
if '==' == kind:
|
||||
checkEq(task, results, browser)
|
||||
elif 'fbf' == kind:
|
||||
checkFBF(task, results, browser)
|
||||
elif 'load' == kind:
|
||||
checkLoad(task, results, browser)
|
||||
else:
|
||||
assert 0 and 'Unknown test type'
|
||||
|
||||
|
||||
def checkEq(task, results, browser):
|
||||
print ' !!! [TODO: == tests] !!!'
|
||||
print 'TEST-PASS | == test', task['id'], '| in', browser
|
||||
|
||||
|
||||
printed = [False]
|
||||
|
||||
def checkFBF(task, results, browser):
|
||||
round0, round1 = results[0], results[1]
|
||||
assert len(round0) == len(round1)
|
||||
|
||||
for page in xrange(len(round1)):
|
||||
r0Page, r1Page = round0[page], round1[page]
|
||||
if r0Page is None:
|
||||
break
|
||||
if r0Page.snapshot != r1Page.snapshot:
|
||||
print 'TEST-UNEXPECTED-FAIL | forward-back-forward test', task['id'], '| in', browser, '| first rendering of page', page + 1, '!= second'
|
||||
print 'TEST-PASS | forward-back-forward test', task['id'], '| in', browser
|
||||
|
||||
|
||||
def checkLoad(task, results, browser):
|
||||
# Load just checks for absence of failure, so if we got here the
|
||||
# test has passed
|
||||
print 'TEST-PASS | load test', task['id'], '| in', browser
|
||||
|
||||
|
||||
def main():
|
||||
set_up()
|
||||
server = HTTPServer(('127.0.0.1', 8080), PDFTestHandler)
|
||||
while not State.done:
|
||||
server.handle_request()
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
17
test_manifest.json
Normal file
17
test_manifest.json
Normal file
@ -0,0 +1,17 @@
|
||||
[
|
||||
{ "id": "tracemonkey-==",
|
||||
"file": "tests/tracemonkey.pdf",
|
||||
"rounds": 1,
|
||||
"type": "=="
|
||||
},
|
||||
{ "id": "tracemonkey-fbf",
|
||||
"file": "tests/tracemonkey.pdf",
|
||||
"rounds": 2,
|
||||
"type": "fbf"
|
||||
},
|
||||
{ "id": "html5-canvas-cheat-sheet-load",
|
||||
"file": "tests/canvas.pdf",
|
||||
"rounds": 1,
|
||||
"type": "load"
|
||||
}
|
||||
]
|
149
test_slave.html
Normal file
149
test_slave.html
Normal file
@ -0,0 +1,149 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>pdf.js test slave</title>
|
||||
<script type="text/javascript" src="pdf.js"></script>
|
||||
<script type="text/javascript" src="fonts.js"></script>
|
||||
<script type="text/javascript" src="glyphlist.js"></script>
|
||||
<script type="application/javascript">
|
||||
var canvas, currentTask, currentTaskIdx, failure, manifest, pdfDoc, stdout;
|
||||
|
||||
function load() {
|
||||
canvas = document.createElement("canvas");
|
||||
// 8.5x11in @ 100% ... XXX need something better here
|
||||
canvas.width = 816;
|
||||
canvas.height = 1056;
|
||||
canvas.mozOpaque = true;
|
||||
stdout = document.getElementById("stdout");
|
||||
|
||||
log("Fetching manifest ...");
|
||||
|
||||
var r = new XMLHttpRequest();
|
||||
r.open("GET", "test_manifest.json", false);
|
||||
r.onreadystatechange = function(e) {
|
||||
if (r.readyState == 4) {
|
||||
log("done\n");
|
||||
|
||||
manifest = JSON.parse(r.responseText);
|
||||
currentTaskIdx = 0, nextTask();
|
||||
}
|
||||
};
|
||||
r.send(null);
|
||||
}
|
||||
|
||||
function nextTask() {
|
||||
if (currentTaskIdx == manifest.length) {
|
||||
return done();
|
||||
}
|
||||
currentTask = manifest[currentTaskIdx];
|
||||
currentTask.round = 0;
|
||||
|
||||
log("Loading file "+ currentTask.file +"\n");
|
||||
|
||||
var r = new XMLHttpRequest();
|
||||
r.open("GET", currentTask.file);
|
||||
r.mozResponseType = r.responseType = "arraybuffer";
|
||||
r.onreadystatechange = function() {
|
||||
if (r.readyState == 4) {
|
||||
var data = r.mozResponseArrayBuffer || r.mozResponse ||
|
||||
r.responseArrayBuffer || r.response;
|
||||
pdfDoc = new PDFDoc(new Stream(data));
|
||||
currentTask.pageNum = 1, nextPage();
|
||||
}
|
||||
};
|
||||
r.send(null);
|
||||
}
|
||||
|
||||
function nextPage() {
|
||||
if (currentTask.pageNum > pdfDoc.numPages) {
|
||||
if (++currentTask.round < currentTask.rounds) {
|
||||
log(" Round "+ (1 + currentTask.round) +"\n");
|
||||
currentTask.pageNum = 1;
|
||||
} else {
|
||||
++currentTaskIdx, nextTask();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
failure = '';
|
||||
log(" drawing page "+ currentTask.pageNum +"...");
|
||||
|
||||
currentPage = pdfDoc.getPage(currentTask.pageNum);
|
||||
|
||||
var ctx = canvas.getContext("2d");
|
||||
clear(ctx);
|
||||
|
||||
var fonts = [];
|
||||
var gfx = new CanvasGraphics(ctx);
|
||||
try {
|
||||
currentPage.compile(gfx, fonts);
|
||||
} catch(e) {
|
||||
failure = 'compile: '+ e.toString();
|
||||
}
|
||||
|
||||
// TODO load fonts
|
||||
setTimeout(function() {
|
||||
if (!failure) {
|
||||
try {
|
||||
currentPage.display(gfx);
|
||||
} catch(e) {
|
||||
failure = 'render: '+ e.toString();
|
||||
}
|
||||
}
|
||||
currentTask.taskDone = (currentTask.pageNum == pdfDoc.numPages
|
||||
&& (1 + currentTask.round) == currentTask.rounds);
|
||||
sendTaskResult(canvas.toDataURL("image/png"));
|
||||
log("done"+ (failure ? " (failed!)" : "") +"\n");
|
||||
|
||||
++currentTask.pageNum, nextPage();
|
||||
},
|
||||
0
|
||||
);
|
||||
}
|
||||
|
||||
function done() {
|
||||
log("Done!\n");
|
||||
setTimeout(function() {
|
||||
document.body.innerHTML = "Tests are finished. <h1>CLOSE ME!</h1>";
|
||||
window.close();
|
||||
},
|
||||
100
|
||||
);
|
||||
}
|
||||
|
||||
function sendTaskResult(snapshot) {
|
||||
var result = { id: currentTask.id,
|
||||
taskDone: currentTask.taskDone,
|
||||
failure: failure,
|
||||
file: currentTask.file,
|
||||
round: currentTask.round,
|
||||
page: currentTask.pageNum,
|
||||
snapshot: snapshot };
|
||||
|
||||
var r = new XMLHttpRequest();
|
||||
// (The POST URI is ignored atm.)
|
||||
r.open("POST", "submit_task_results", false);
|
||||
r.setRequestHeader("Content-Type", "application/json");
|
||||
// XXX async
|
||||
r.send(JSON.stringify(result));
|
||||
}
|
||||
|
||||
function clear(ctx) {
|
||||
var ctx = canvas.getContext("2d");
|
||||
ctx.save();
|
||||
ctx.fillStyle = "rgb(255, 255, 255)";
|
||||
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||
ctx.restore();
|
||||
}
|
||||
|
||||
function log(str) {
|
||||
stdout.innerHTML += str;
|
||||
window.scrollTo(0, stdout.getBoundingClientRect().bottom);
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
|
||||
<body onload="load();">
|
||||
<pre id="stdout"></pre>
|
||||
</body>
|
||||
|
||||
</html>
|
BIN
tests/canvas.pdf
Normal file
BIN
tests/canvas.pdf
Normal file
Binary file not shown.
BIN
tests/tracemonkey.pdf
Normal file
BIN
tests/tracemonkey.pdf
Normal file
Binary file not shown.
@ -1,5 +1,10 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
"use strict";
|
||||
|
||||
var CFFStrings = [
|
||||
".notdef",
|
||||
".notdef",
|
||||
"space",
|
||||
"exclam",
|
||||
"quotedbl",
|
||||
@ -490,7 +495,7 @@ var CFFDictDataMap = {
|
||||
},
|
||||
"10": {
|
||||
name: "StdHW"
|
||||
},
|
||||
},
|
||||
"11": {
|
||||
name: "StdVW"
|
||||
},
|
||||
@ -597,7 +602,7 @@ var CFFDictDataMap = {
|
||||
},
|
||||
"18": {
|
||||
name: "Private",
|
||||
operand: "number number"
|
||||
operand: "number number"
|
||||
},
|
||||
"19": {
|
||||
name: "Subrs"
|
||||
|
@ -1,3 +1,8 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
"use strict";
|
||||
|
||||
/**
|
||||
* The Type2 reader code below is only used for debugging purpose since Type2
|
||||
* is only a CharString format and is never used directly as a Font file.
|
||||
|
@ -1,12 +1,14 @@
|
||||
/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- /
|
||||
/* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */
|
||||
|
||||
var pdfDocument, canvas, pageDisplay, pageNum, pageInterval;
|
||||
"use strict";
|
||||
|
||||
var pdfDocument, canvas, numPages, pageDisplay, pageNum, pageInterval;
|
||||
function load(userInput) {
|
||||
canvas = document.getElementById("canvas");
|
||||
canvas.mozOpaque = true;
|
||||
pageNum = parseInt(queryParams().page) || 1;
|
||||
fileName = userInput;
|
||||
var fileName = userInput;
|
||||
if (!userInput) {
|
||||
fileName = queryParams().file || "compressed.tracemonkey-pldi-09.pdf";
|
||||
}
|
||||
@ -26,7 +28,7 @@ function queryParams() {
|
||||
|
||||
function open(url) {
|
||||
document.title = url;
|
||||
req = new XMLHttpRequest();
|
||||
var req = new XMLHttpRequest();
|
||||
req.open("GET", url);
|
||||
req.mozResponseType = req.responseType = "arraybuffer";
|
||||
req.expected = (document.URL.indexOf("file:") == 0) ? 0 : 200;
|
||||
|
Loading…
Reference in New Issue
Block a user