Merge branch 'master' into issue-1090

This commit is contained in:
James Campos 2012-01-21 22:56:33 -08:00
commit 51d7ad06c4
15 changed files with 4629 additions and 36 deletions

View File

@ -36,6 +36,7 @@ PDF_JS_FILES = \
stream.js \ stream.js \
worker.js \ worker.js \
../external/jpgjs/jpg.js \ ../external/jpgjs/jpg.js \
jpx.js \
$(NULL) $(NULL)
# make server # make server

View File

@ -1,9 +1,6 @@
# pdf.js # PDF.JS
## Overview
pdf.js is an HTML5 technology experiment that explores building a faithful pdf.js is an HTML5 technology experiment that explores building a faithful
and efficient Portable Document Format (PDF) renderer without native code and efficient Portable Document Format (PDF) renderer without native code
assistance. assistance.
@ -16,7 +13,7 @@ successful.
## Getting started # Getting started
### Online demo ### Online demo
@ -29,11 +26,12 @@ using the pdf.js API.
### Extension ### Extension
An up-to-date Firefox extension is also available: A Firefox extension is also available:
+ http://mozilla.github.com/pdf.js/extensions/firefox/pdf.js.xpi + http://mozilla.github.com/pdf.js/extensions/firefox/pdf.js.xpi
(The above link is updated upon every merge to our master branch). Note that this extension is self-updating, and by default Firefox will auto-update extensions on a
daily basis (you can change this through the `extensions.update.interval` option in `about:config`).
For an experimental Chrome extension, get the code as explained below and issue `make extension`. For an experimental Chrome extension, get the code as explained below and issue `make extension`.
Then open Chrome with the flag `--enable-experimental-extension-apis`, go to `Tools > Extension` Then open Chrome with the flag `--enable-experimental-extension-apis`, go to `Tools > Extension`
@ -68,12 +66,12 @@ In order to bundle all `src/` files into a final `pdf.js`, issue:
This will generate the file `build/pdf.js` that can be included in your final project. (WARNING: That's a large file! Consider minifying it). This will generate the file `build/pdf.js` that can be included in your final project. (WARNING: That's a large file! Consider minifying it).
## Learning # Learning
Here are some initial pointers to help contributors get off the ground. Here are some initial pointers to help contributors get off the ground.
Additional resources are available in a separate section below. Additional resources are available in a separate section below.
#### Hello world ### Hello world
For a "hello world" example, take a look at: For a "hello world" example, take a look at:
@ -82,7 +80,7 @@ For a "hello world" example, take a look at:
This example illustrates the bare minimum ingredients for integrating pdf.js This example illustrates the bare minimum ingredients for integrating pdf.js
in a custom project. in a custom project.
#### Introductory video ### Introductory video
Check out the presentation by our contributor Julian Viereck on the inner Check out the presentation by our contributor Julian Viereck on the inner
workings of PDF and pdf.js: workings of PDF and pdf.js:
@ -92,7 +90,7 @@ workings of PDF and pdf.js:
## Contributing # Contributing
pdf.js is a community-driven project, so contributors are always welcome. pdf.js is a community-driven project, so contributors are always welcome.
Simply fork our repo and contribute away. Good starting places for picking Simply fork our repo and contribute away. Good starting places for picking
@ -122,7 +120,7 @@ You can add your name to it! :)
## Running the tests # Running the tests
pdf.js comes with browser-level regression tests that allow one to probe pdf.js comes with browser-level regression tests that allow one to probe
whether it's able to successfully parse PDFs, as well as compare its output whether it's able to successfully parse PDFs, as well as compare its output
@ -148,7 +146,7 @@ images. The test type `load` simply tests whether the file loads without
raising any errors. raising any errors.
## Running tests through our bot ### Running tests through our bot
If you are a reviewer, you can use our remote bot to issue comprehensive tests If you are a reviewer, you can use our remote bot to issue comprehensive tests
against reference images before merging pull requests. against reference images before merging pull requests.
@ -158,7 +156,7 @@ See the bot repo for details:
+ https://github.com/mozilla/pdf.js-bot + https://github.com/mozilla/pdf.js-bot
## Additional resources # Additional resources
Gallery of user projects and modifications: Gallery of user projects and modifications:
@ -188,7 +186,7 @@ Follow us on twitter: @pdfjs
## PDF-related resources ### PDF-related resources
A really basic overview of PDF is described here: A really basic overview of PDF is described here:

View File

@ -23,6 +23,7 @@
<script type="text/javascript" src="../../src/stream.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="../../src/worker.js"></script>
<script type="text/javascript" src="../../external/jpgjs/jpg.js"></script> <script type="text/javascript" src="../../external/jpgjs/jpg.js"></script>
<script type="text/javascript" src="../../src/jpx.js"></script>
<script type="text/javascript"> <script type="text/javascript">
// Specify the main script used to create a new PDF.JS web worker. // Specify the main script used to create a new PDF.JS web worker.

View File

@ -23,6 +23,7 @@
<script type="text/javascript" src="../../src/stream.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="../../src/worker.js"></script>
<script type="text/javascript" src="../../external/jpgjs/jpg.js"></script> <script type="text/javascript" src="../../external/jpgjs/jpg.js"></script>
<script type="text/javascript" src="../../src/jpx.js"></script>
<script type="text/javascript"> <script type="text/javascript">
// Specify the main script used to create a new PDF.JS web worker. // Specify the main script used to create a new PDF.JS web worker.

View File

@ -3340,15 +3340,9 @@ var Type2CFF = (function Type2CFFClosure() {
inverseEncoding[encoding[charcode]] = charcode | 0; 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];
if (glyph == '.notdef') { if (glyph == '.notdef')
charstrings.push({
unicode: 0,
code: 0,
gid: i,
glyph: glyph
});
continue; continue;
}
var code = inverseEncoding[i]; var code = inverseEncoding[i];
if (!code || isSpecialUnicode(code)) { if (!code || isSpecialUnicode(code)) {
unassignedUnicodeItems.push(i); unassignedUnicodeItems.push(i);

1854
src/jpx.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -240,6 +240,10 @@ var Parser = (function ParserClosure() {
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);
} }
if (name == 'JPXDecode' || name == 'JPX') {
var bytes = stream.getBytes(length);
return new JpxStream(bytes, stream.dict);
}
if (name == 'ASCII85Decode' || name == 'A85') { if (name == 'ASCII85Decode' || name == 'A85') {
return new Ascii85Stream(stream); return new Ascii85Stream(stream);
} }
@ -249,6 +253,9 @@ var Parser = (function ParserClosure() {
if (name == 'CCITTFaxDecode' || name == 'CCF') { if (name == 'CCITTFaxDecode' || name == 'CCF') {
return new CCITTFaxStream(stream, params); return new CCITTFaxStream(stream, params);
} }
if (name == 'RunLengthDecode') {
return new RunLengthStream(stream);
}
warn('filter "' + name + '" not supported yet'); warn('filter "' + name + '" not supported yet');
return stream; return stream;
} }

View File

@ -835,7 +835,7 @@ var JpegStream = (function JpegStreamClosure() {
return bytesToString(this.bytes); return bytesToString(this.bytes);
}; };
JpegStream.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');
}; };
/** /**
* Checks if the image can be decoded and displayed by the browser without any * Checks if the image can be decoded and displayed by the browser without any
@ -869,6 +869,107 @@ var JpegStream = (function JpegStreamClosure() {
return JpegStream; return JpegStream;
})(); })();
/**
* For JPEG 2000's we use a library to decode these images and
* the stream behaves like all the other DecodeStreams.
*/
var JpxStream = (function JpxStreamClosure() {
function JpxStream(bytes, dict) {
this.dict = dict;
this.bytes = bytes;
DecodeStream.call(this);
}
JpxStream.prototype = Object.create(DecodeStream.prototype);
JpxStream.prototype.ensureBuffer = function jpxStreamEnsureBuffer(req) {
if (this.bufferLength)
return;
var jpxImage = new JpxImage();
jpxImage.parse(this.bytes);
var width = jpxImage.width;
var height = jpxImage.height;
var componentsCount = jpxImage.componentsCount;
if (componentsCount != 1 && componentsCount != 3 && componentsCount != 4)
error('JPX with ' + componentsCount + ' components is not supported');
var data = new Uint8Array(width * height * componentsCount);
for (var k = 0, kk = jpxImage.tiles.length; k < kk; k++) {
var tileCompoments = jpxImage.tiles[k];
var tileWidth = tileCompoments[0].width;
var tileHeight = tileCompoments[0].height;
var tileLeft = tileCompoments[0].left;
var tileTop = tileCompoments[0].top;
var dataPosition, sourcePosition, data0, data1, data2, data3, rowFeed;
switch (componentsCount) {
case 1:
data0 = tileCompoments[0].items;
dataPosition = width * tileTop + tileLeft;
rowFeed = width - tileWidth;
sourcePosition = 0;
for (var j = 0; j < tileHeight; j++) {
for (var i = 0; i < tileWidth; i++)
data[dataPosition++] = data0[sourcePosition++];
dataPosition += rowFeed;
}
break;
case 3:
data0 = tileCompoments[0].items;
data1 = tileCompoments[1].items;
data2 = tileCompoments[2].items;
dataPosition = (width * tileTop + tileLeft) * 3;
rowFeed = (width - tileWidth) * 3;
sourcePosition = 0;
for (var j = 0; j < tileHeight; j++) {
for (var i = 0; i < tileWidth; i++) {
data[dataPosition++] = data0[sourcePosition];
data[dataPosition++] = data1[sourcePosition];
data[dataPosition++] = data2[sourcePosition];
sourcePosition++;
}
dataPosition += rowFeed;
}
break;
case 4:
data0 = tileCompoments[0].items;
data1 = tileCompoments[1].items;
data2 = tileCompoments[2].items;
data3 = tileCompoments[3].items;
dataPosition = (width * tileTop + tileLeft) * 4;
rowFeed = (width - tileWidth) * 4;
sourcePosition = 0;
for (var j = 0; j < tileHeight; j++) {
for (var i = 0; i < tileWidth; i++) {
data[dataPosition++] = data0[sourcePosition];
data[dataPosition++] = data1[sourcePosition];
data[dataPosition++] = data2[sourcePosition];
data[dataPosition++] = data3[sourcePosition];
sourcePosition++;
}
dataPosition += rowFeed;
}
break;
}
}
this.buffer = data;
this.bufferLength = data.length;
};
JpxStream.prototype.getChar = function jpxStreamGetChar() {
error('internal error: getChar is not valid on JpxStream');
};
return JpxStream;
})();
var DecryptStream = (function DecryptStreamClosure() { var DecryptStream = (function DecryptStreamClosure() {
function DecryptStream(str, decrypt) { function DecryptStream(str, decrypt) {
this.str = str; this.str = str;
@ -1041,6 +1142,51 @@ var AsciiHexStream = (function AsciiHexStreamClosure() {
return AsciiHexStream; return AsciiHexStream;
})(); })();
var RunLengthStream = (function RunLengthStreamClosure() {
function RunLengthStream(str) {
this.str = str;
this.dict = str.dict;
DecodeStream.call(this);
}
RunLengthStream.prototype = Object.create(DecodeStream.prototype);
RunLengthStream.prototype.readBlock = function runLengthStreamReadBlock() {
// The repeatHeader has following format. The first byte defines type of run
// and amount of bytes to repeat/copy: n = 0 through 127 - copy next n bytes
// (in addition to the second byte from the header), n = 129 through 255 -
// duplicate the second byte from the header (257 - n) times, n = 128 - end.
var repeatHeader = this.str.getBytes(2);
if (!repeatHeader || repeatHeader.length < 2 || repeatHeader[0] == 128) {
this.eof = true;
return;
}
var bufferLength = this.bufferLength;
var n = repeatHeader[0];
if (n < 128) {
// copy n bytes
var buffer = this.ensureBuffer(bufferLength + n + 1);
buffer[bufferLength++] = repeatHeader[1];
if (n > 0) {
var source = this.str.getBytes(n);
buffer.set(source, bufferLength);
bufferLength += n;
}
} else {
n = 257 - n;
var b = repeatHeader[1];
var buffer = this.ensureBuffer(bufferLength + n + 1);
for (var i = 0; i < n; i++)
buffer[bufferLength++] = b;
}
this.bufferLength = bufferLength;
};
return RunLengthStream;
})();
var CCITTFaxStream = (function CCITTFaxStreamClosure() { var CCITTFaxStream = (function CCITTFaxStreamClosure() {
var ccittEOL = -2; var ccittEOL = -2;

View File

@ -23,7 +23,8 @@ var files = [
'pattern.js', 'pattern.js',
'stream.js', 'stream.js',
'worker.js', 'worker.js',
'../external/jpgjs/jpg.js' '../external/jpgjs/jpg.js',
'jpx.js'
]; ];
// Load all the files. // Load all the files.

View File

@ -22,4 +22,5 @@
!issue918.pdf !issue918.pdf
!smaskdim.pdf !smaskdim.pdf
!type4psfunc.pdf !type4psfunc.pdf
!S2.pdf
!zerowidthline.pdf !zerowidthline.pdf

2549
test/pdfs/S2.pdf Normal file

File diff suppressed because one or more lines are too long

View File

@ -176,7 +176,6 @@
"md5": "eb7b224107205db4fea9f7df0185f77d", "md5": "eb7b224107205db4fea9f7df0185f77d",
"link": true, "link": true,
"rounds": 1, "rounds": 1,
"skipPages": [12,31],
"type": "eq" "type": "eq"
}, },
{ "id": "fips197", { "id": "fips197",
@ -411,6 +410,12 @@
"link": true, "link": true,
"type": "load" "type": "load"
}, },
{ "id": "S2-eq",
"file": "pdfs/S2.pdf",
"md5": "d0b6137846df6e0fe058f234a87fb588",
"rounds": 1,
"type": "eq"
},
{ "id": "issue1055", { "id": "issue1055",
"file": "pdfs/issue1055.pdf", "file": "pdfs/issue1055.pdf",
"md5": "3ba56c2e48dce81da8669b1b9cf98ff0", "md5": "3ba56c2e48dce81da8669b1b9cf98ff0",

View File

@ -22,6 +22,7 @@
<script type="text/javascript" src="/src/stream.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="/src/worker.js"></script>
<script type="text/javascript" src="/external/jpgjs/jpg.js"></script> <script type="text/javascript" src="/external/jpgjs/jpg.js"></script>
<script type="text/javascript" src="/src/jpx.js"></script>
<script type="text/javascript" src="driver.js"></script> <script type="text/javascript" src="driver.js"></script>
<script type="text/javascript"> <script type="text/javascript">

View File

@ -26,6 +26,7 @@
<script type="text/javascript" src="../src/stream.js"></script> <!-- PDFJSSCRIPT_REMOVE --> <script type="text/javascript" src="../src/stream.js"></script> <!-- PDFJSSCRIPT_REMOVE -->
<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" src="../src/jpx.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>

View File

@ -960,22 +960,55 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv) {
var self = this; var self = this;
var textDivs = this.textDivs; var textDivs = this.textDivs;
var textLayerDiv = this.textLayerDiv; var textLayerDiv = this.textLayerDiv;
this.textLayerTimer = setInterval(function renderTextLayer() { var renderTimer = null;
var renderingDone = false;
var renderInterval = 0;
var resumeInterval = 500; // in ms
// Render the text layer, one div at a time
function renderTextLayer() {
if (textDivs.length === 0) { if (textDivs.length === 0) {
clearInterval(self.textLayerTimer); clearInterval(renderTimer);
renderingDone = true;
return; return;
} }
var textDiv = textDivs.shift(); var textDiv = textDivs.shift();
if (textDiv.dataset.textLength >= 1) { // avoid div by zero if (textDiv.dataset.textLength > 0) {
textLayerDiv.appendChild(textDiv); textLayerDiv.appendChild(textDiv);
// Adjust div width (via letterSpacing) to match canvas text
// Due to the .offsetWidth calls, this is slow if (textDiv.dataset.textLength > 1) { // avoid div by zero
textDiv.style.letterSpacing = // Adjust div width (via letterSpacing) to match canvas text
((textDiv.dataset.canvasWidth - textDiv.offsetWidth) / // Due to the .offsetWidth calls, this is slow
(textDiv.dataset.textLength - 1)) + 'px'; // This needs to come after appending to the DOM
textDiv.style.letterSpacing =
((textDiv.dataset.canvasWidth - textDiv.offsetWidth) /
(textDiv.dataset.textLength - 1)) + 'px';
}
} // textLength > 0
}
renderTimer = setInterval(renderTextLayer, renderInterval);
// Stop rendering when user scrolls. Resume after XXX milliseconds
// of no scroll events
var scrollTimer = null;
function textLayerOnScroll() {
if (renderingDone) {
window.removeEventListener('scroll', textLayerOnScroll, false);
return;
} }
}, 0);
}; // Immediately pause rendering
clearInterval(renderTimer);
clearTimeout(scrollTimer);
scrollTimer = setTimeout(function textLayerScrollTimer() {
// Resume rendering
renderTimer = setInterval(renderTextLayer, renderInterval);
}, resumeInterval);
}; // textLayerOnScroll
window.addEventListener('scroll', textLayerOnScroll, false);
}; // endLayout
this.appendText = function textLayerBuilderAppendText(text, this.appendText = function textLayerBuilderAppendText(text,
fontName, fontSize) { fontName, fontSize) {