Merge with upstream
This commit is contained in:
commit
42859177fd
1
LICENSE
1
LICENSE
@ -8,6 +8,7 @@
|
||||
Justin D'Arcangelo <justindarc@gmail.com>
|
||||
Yury Delendik
|
||||
Kalervo Kujala
|
||||
Adil Allawi <@ironymark>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
|
72
Makefile
72
Makefile
@ -55,30 +55,30 @@ browser-test:
|
||||
--browserManifestFile=$(PDF_BROWSERS) \
|
||||
--manifestFile=$(PDF_TESTS)
|
||||
|
||||
# make shell-test
|
||||
#
|
||||
# This target runs all of the tests that can be run in a JS shell.
|
||||
# The shell used is taken from the JS_SHELL environment variable. If
|
||||
# that variable is not defined, the script will attempt to use the copy
|
||||
# of Rhino that comes with the Closure compiler used for producing the
|
||||
# website.
|
||||
SHELL_TARGET = $(NULL)
|
||||
ifeq ($(JS_SHELL),)
|
||||
JS_SHELL := "java -cp $(BUILD_DIR)/compiler.jar"
|
||||
JS_SHELL += "com.google.javascript.jscomp.mozilla.rhino.tools.shell.Main"
|
||||
SHELL_TARGET = compiler
|
||||
endif
|
||||
|
||||
shell-test: shell-msg $(SHELL_TARGET) font-test
|
||||
shell-msg:
|
||||
ifeq ($(SHELL_TARGET), compiler)
|
||||
@echo "No JS_SHELL env variable present."
|
||||
@echo "The default is to find a copy of Rhino and try that."
|
||||
endif
|
||||
@echo "JS shell command is: $(JS_SHELL)"
|
||||
|
||||
font-test:
|
||||
@echo "font test stub."
|
||||
# # make shell-test
|
||||
# #
|
||||
# # This target runs all of the tests that can be run in a JS shell.
|
||||
# # The shell used is taken from the JS_SHELL environment variable. If
|
||||
# # that variable is not defined, the script will attempt to use the copy
|
||||
# # of Rhino that comes with the Closure compiler used for producing the
|
||||
# # website.
|
||||
# SHELL_TARGET = $(NULL)
|
||||
# ifeq ($(JS_SHELL),)
|
||||
# JS_SHELL := "java -cp $(BUILD_DIR)/compiler.jar"
|
||||
# JS_SHELL += "com.google.javascript.jscomp.mozilla.rhino.tools.shell.Main"
|
||||
# SHELL_TARGET = compiler
|
||||
# endif
|
||||
#
|
||||
# shell-test: shell-msg $(SHELL_TARGET) font-test
|
||||
# shell-msg:
|
||||
# ifeq ($(SHELL_TARGET), compiler)
|
||||
# @echo "No JS_SHELL env variable present."
|
||||
# @echo "The default is to find a copy of Rhino and try that."
|
||||
# endif
|
||||
# @echo "JS shell command is: $(JS_SHELL)"
|
||||
#
|
||||
# font-test:
|
||||
# @echo "font test stub."
|
||||
|
||||
# make lint
|
||||
#
|
||||
@ -133,18 +133,18 @@ $(GH_PAGES)/web/%: web/%
|
||||
$(GH_PAGES)/web/images/%: web/images/%
|
||||
@cp $< $@
|
||||
|
||||
# make compiler
|
||||
#
|
||||
# This target downloads the Closure compiler, and places it in the
|
||||
# build directory. This target is also useful when the user doesn't
|
||||
# have a JS shell available--we can have them use the Rhino shell that
|
||||
# comes with Closure.
|
||||
COMPILER_URL = http://closure-compiler.googlecode.com/files/compiler-latest.zip
|
||||
|
||||
compiler: $(BUILD_DIR)/compiler.zip
|
||||
$(BUILD_DIR)/compiler.zip: | $(BUILD_DIR)
|
||||
curl $(COMPILER_URL) > $(BUILD_DIR)/compiler.zip;
|
||||
cd $(BUILD_DIR); unzip compiler.zip compiler.jar;
|
||||
# # make compiler
|
||||
# #
|
||||
# # This target downloads the Closure compiler, and places it in the
|
||||
# # build directory. This target is also useful when the user doesn't
|
||||
# # have a JS shell available--we can have them use the Rhino shell that
|
||||
# # comes with Closure.
|
||||
# COMPILER_URL = http://closure-compiler.googlecode.com/files/compiler-latest.zip
|
||||
#
|
||||
# compiler: $(BUILD_DIR)/compiler.zip
|
||||
# $(BUILD_DIR)/compiler.zip: | $(BUILD_DIR)
|
||||
# curl $(COMPILER_URL) > $(BUILD_DIR)/compiler.zip;
|
||||
# cd $(BUILD_DIR); unzip compiler.zip compiler.jar;
|
||||
|
||||
# make firefox-extension
|
||||
#
|
||||
|
94
README.md
94
README.md
@ -1,17 +1,84 @@
|
||||
# pdf.js
|
||||
|
||||
pdf.js is a technology demonstrator prototype to explore whether the HTML5
|
||||
platform is complete enough to faithfully and efficiently render the ISO
|
||||
32000-1:2008 Portable Document Format (PDF) without native code assistance.
|
||||
|
||||
pdf.js is not currently part of the Mozilla project, and there is no plan
|
||||
yet to integrate it into Firefox. We will explore that possibility once
|
||||
pdf.js is production ready. Until then we aim to publish a Firefox
|
||||
PDF reader extension powered by pdf.js.
|
||||
|
||||
## Overview
|
||||
|
||||
pdf.js is an HTML5 technology experiment that explores building a faithful
|
||||
and efficient Portable Document Format (PDF) renderer without native code
|
||||
assistance.
|
||||
|
||||
pdf.js is community-driven and supported by Mozilla Labs. Our goal is to
|
||||
create a general-purpose, web standards-based platform for parsing and
|
||||
rendering PDFs, and eventually release a PDF reader extension powered by
|
||||
pdf.js. Integration with Firefox is a possibility if the experiment proves
|
||||
successful.
|
||||
|
||||
|
||||
|
||||
## Getting started
|
||||
|
||||
**Online demo**
|
||||
|
||||
For an online demo, visit:
|
||||
|
||||
http://andreasgal.github.com/pdf.js/web/viewer.html
|
||||
|
||||
This demo provides an interactive interface for displaying and browsing PDFs
|
||||
using the pdf.js API.
|
||||
|
||||
**Hello world**
|
||||
|
||||
For a "hello world" example, take a look at:
|
||||
|
||||
examples/helloworld/
|
||||
|
||||
This example illustrates the bare minimum ingredients for integrating pdf.js
|
||||
in a custom project.
|
||||
|
||||
|
||||
|
||||
## Running the Tests
|
||||
|
||||
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
|
||||
against reference images, pixel-by-pixel.
|
||||
|
||||
To run the tests, first configure the browser manifest file at:
|
||||
|
||||
test/resources/browser_manifests/browser_manifest.json
|
||||
|
||||
Sample manifests for different platforms are provided in that directory.
|
||||
|
||||
To run all the bundled tests, type:
|
||||
|
||||
$ make test
|
||||
|
||||
and cross your fingers. Different types of tests are available, see the test
|
||||
manifest file at:
|
||||
|
||||
test/test_manifest.json
|
||||
|
||||
The test type `eq` tests whether the output images are identical to reference
|
||||
images. The test type `load` simply tests whether the file loads without
|
||||
raising any errors.
|
||||
|
||||
|
||||
## Contributing
|
||||
|
||||
pdf.js is a community-driver project, so contributors are always welcome.
|
||||
Simply fork our repo and contribute away. A great place to start is our
|
||||
open issues.
|
||||
|
||||
For better consistency and long-term stability, please do look around the
|
||||
code and try to follow our conventions.
|
||||
|
||||
|
||||
## Additional resources
|
||||
|
||||
Our demo site is here:
|
||||
|
||||
http://andreasgal.github.com/pdf.js/
|
||||
http://andreasgal.github.com/pdf.js/web/viewer.html
|
||||
|
||||
You can read more about pdf.js here:
|
||||
|
||||
@ -19,14 +86,19 @@ You can read more about pdf.js here:
|
||||
|
||||
http://blog.mozilla.com/cjones/2011/06/15/overview-of-pdf-js-guts/
|
||||
|
||||
follow us on twitter: @pdfjs
|
||||
Follow us on twitter: @pdfjs
|
||||
|
||||
http://twitter.com/#!/pdfjs
|
||||
|
||||
join our mailing list:
|
||||
Join our mailing list:
|
||||
|
||||
dev-pdf-js@lists.mozilla.org
|
||||
|
||||
Subscribe either using lists.mozilla.org or Google Groups:
|
||||
|
||||
https://lists.mozilla.org/listinfo/dev-pdf-js
|
||||
https://groups.google.com/group/mozilla.dev.pdf-js/topics
|
||||
|
||||
and talk to us on IRC:
|
||||
Talk to us on IRC:
|
||||
|
||||
#pdfjs on irc.mozilla.org
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
'use strict';
|
||||
|
16
examples/helloworld/README.md
Normal file
16
examples/helloworld/README.md
Normal file
@ -0,0 +1,16 @@
|
||||
## "Hello World" overview
|
||||
|
||||
This example is a minimalistic application of the pdf.js project. The file
|
||||
`helloworld.pdf` is from the GNUpdf project (see [Introduction to PDF at GNUpdf](http://gnupdf.org/Introduction_to_PDF), and contains a simple and
|
||||
human-readable PDF.
|
||||
|
||||
|
||||
## Getting started
|
||||
|
||||
Point your browser to `index.html`. Voila. Take a peek at `hello.js` to see
|
||||
how to make basic calls to `pdf.js`.
|
||||
|
||||
|
||||
## Additional resources
|
||||
|
||||
+ [GNUpdf - Introduction to PDF](http://gnupdf.org/Introduction_to_PDF)
|
49
examples/helloworld/hello.js
Normal file
49
examples/helloworld/hello.js
Normal file
@ -0,0 +1,49 @@
|
||||
//
|
||||
// See README for overview
|
||||
//
|
||||
|
||||
|
||||
//
|
||||
// Ajax GET request, for binary files
|
||||
// (like jQuery's $.get(), but supports the binary type ArrayBuffer)
|
||||
//
|
||||
var ajaxGet = function(url, callback){
|
||||
var xhr = new XMLHttpRequest();
|
||||
xhr.open('GET', url);
|
||||
xhr.mozResponseType = xhr.responseType = 'arraybuffer';
|
||||
xhr.expected = (document.URL.indexOf('file:') === 0) ? 0 : 200;
|
||||
xhr.onreadystatechange = function() {
|
||||
if (xhr.readyState === 4 && xhr.status === xhr.expected) {
|
||||
var data = (xhr.mozResponseArrayBuffer || xhr.mozResponse ||
|
||||
xhr.responseArrayBuffer || xhr.response);
|
||||
|
||||
callback(data);
|
||||
}
|
||||
};
|
||||
xhr.send(null);
|
||||
}
|
||||
|
||||
//
|
||||
// This is where the fun happens
|
||||
//
|
||||
ajaxGet('helloworld.pdf', function(data){
|
||||
//
|
||||
// Instantiate PDFDoc with PDF data
|
||||
//
|
||||
var pdf = new PDFDoc(new Stream(data));
|
||||
var page = pdf.getPage(1);
|
||||
var scale = 1.5;
|
||||
|
||||
//
|
||||
// Prepare canvas using PDF page dimensions
|
||||
//
|
||||
var canvas = document.getElementById('the-canvas');
|
||||
var context = canvas.getContext('2d');
|
||||
canvas.height = page.height * scale;
|
||||
canvas.width = page.width * scale;
|
||||
|
||||
//
|
||||
// Render PDF page into canvas context
|
||||
//
|
||||
page.startRendering(context);
|
||||
});
|
68
examples/helloworld/helloworld.pdf
Normal file
68
examples/helloworld/helloworld.pdf
Normal file
@ -0,0 +1,68 @@
|
||||
%PDF-1.7
|
||||
|
||||
1 0 obj % entry point
|
||||
<<
|
||||
/Type /Catalog
|
||||
/Pages 2 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
2 0 obj
|
||||
<<
|
||||
/Type /Pages
|
||||
/MediaBox [ 0 0 200 200 ]
|
||||
/Count 1
|
||||
/Kids [ 3 0 R ]
|
||||
>>
|
||||
endobj
|
||||
|
||||
3 0 obj
|
||||
<<
|
||||
/Type /Page
|
||||
/Parent 2 0 R
|
||||
/Resources <<
|
||||
/Font <<
|
||||
/F1 4 0 R
|
||||
>>
|
||||
>>
|
||||
/Contents 5 0 R
|
||||
>>
|
||||
endobj
|
||||
|
||||
4 0 obj
|
||||
<<
|
||||
/Type /Font
|
||||
/Subtype /Type1
|
||||
/BaseFont /Times-Roman
|
||||
>>
|
||||
endobj
|
||||
|
||||
5 0 obj % page content
|
||||
<<
|
||||
/Length 44
|
||||
>>
|
||||
stream
|
||||
BT
|
||||
70 50 TD
|
||||
/F1 12 Tf
|
||||
(Hello, world!) Tj
|
||||
ET
|
||||
endstream
|
||||
endobj
|
||||
|
||||
xref
|
||||
0 6
|
||||
0000000000 65535 f
|
||||
0000000010 00000 n
|
||||
0000000079 00000 n
|
||||
0000000173 00000 n
|
||||
0000000301 00000 n
|
||||
0000000380 00000 n
|
||||
trailer
|
||||
<<
|
||||
/Size 6
|
||||
/Root 1 0 R
|
||||
>>
|
||||
startxref
|
||||
492
|
||||
%%EOF
|
18
examples/helloworld/index.html
Normal file
18
examples/helloworld/index.html
Normal file
@ -0,0 +1,18 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<!-- PDF.js-specific -->
|
||||
<script type="text/javascript" src="../../pdf.js"></script>
|
||||
<script type="text/javascript" src="../../metrics.js"></script>
|
||||
<script type="text/javascript" src="../../fonts.js"></script>
|
||||
<script type="text/javascript" src="../../glyphlist.js"></script>
|
||||
|
||||
<script type="text/javascript" src="hello.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<canvas id="the-canvas" style="border:1px solid black;"/>
|
||||
</body>
|
||||
|
||||
</html>
|
512
fonts.js
Executable file → Normal file
512
fonts.js
Executable file → Normal file
@ -1,4 +1,4 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
'use strict';
|
||||
@ -12,6 +12,13 @@ var kMaxWaitForFontFace = 1000;
|
||||
// Unicode Private Use Area
|
||||
var kCmapGlyphOffset = 0xE000;
|
||||
|
||||
// PDF Glyph Space Units are one Thousandth of a TextSpace Unit
|
||||
// except for Type 3 fonts
|
||||
var kPDFGlyphSpaceUnits = 1000;
|
||||
|
||||
// Until hinting is fully supported this constant can be used
|
||||
var kHintingEnabled = false;
|
||||
|
||||
/**
|
||||
* Hold a map of decoded fonts and of the standard fourteen Type1
|
||||
* fonts and their acronyms.
|
||||
@ -114,54 +121,6 @@ var serifFonts = {
|
||||
'Wide Latin': true, 'Windsor': true, 'XITS': true
|
||||
};
|
||||
|
||||
var FontMeasure = (function FontMeasure() {
|
||||
var kScalePrecision = 30;
|
||||
var ctx = document.createElement('canvas').getContext('2d');
|
||||
ctx.scale(1 / kScalePrecision, 1);
|
||||
|
||||
var current;
|
||||
var measureCache;
|
||||
|
||||
return {
|
||||
setActive: function fonts_setActive(font, size) {
|
||||
if (current == font) {
|
||||
var sizes = current.sizes;
|
||||
if (!(measureCache = sizes[size]))
|
||||
measureCache = sizes[size] = Object.create(null);
|
||||
} else {
|
||||
measureCache = null;
|
||||
}
|
||||
|
||||
var name = font.loadedName;
|
||||
var bold = font.bold ? 'bold' : 'normal';
|
||||
var italic = font.italic ? 'italic' : 'normal';
|
||||
size *= kScalePrecision;
|
||||
var rule = italic + ' ' + bold + ' ' + size + 'px "' + name + '"';
|
||||
ctx.font = rule;
|
||||
current = font;
|
||||
},
|
||||
measureText: function fonts_measureText(text, encoding, size) {
|
||||
var width;
|
||||
if (measureCache && (width = measureCache[text]))
|
||||
return width;
|
||||
|
||||
try {
|
||||
width = 0.0;
|
||||
for (var i = 0; i < text.length; i++) {
|
||||
var charWidth = encoding[text.charCodeAt(i)].width;
|
||||
width += parseFloat(charWidth);
|
||||
}
|
||||
width = width * size / 1000;
|
||||
} catch(e) {
|
||||
width = ctx.measureText(text).width / kScalePrecision;
|
||||
}
|
||||
if (measureCache)
|
||||
measureCache[text] = width;
|
||||
return width;
|
||||
}
|
||||
};
|
||||
})();
|
||||
|
||||
var FontLoader = {
|
||||
listeningForFontLoad: false,
|
||||
|
||||
@ -456,10 +415,10 @@ var Font = (function Font() {
|
||||
this.encoding = properties.encoding;
|
||||
this.sizes = [];
|
||||
|
||||
var names = name.split("+");
|
||||
var names = name.split('+');
|
||||
names = names.length > 1 ? names[1] : names[0];
|
||||
names = names.split(/[-,_]/g)[0];
|
||||
this.serif = serifFonts[names] || (name.indexOf("Serif") != -1);
|
||||
this.serif = serifFonts[names] || (name.search(/serif/gi) != -1);
|
||||
|
||||
// If the font is to be ignored, register it like an already loaded font
|
||||
// to avoid the cost of waiting for it be be loaded by the platform.
|
||||
@ -473,14 +432,15 @@ var Font = (function Font() {
|
||||
// The file data is not specified. Trying to fix the font name
|
||||
// to be used with the canvas.font.
|
||||
var fontName = stdFontMap[name] || name.replace('_', '-');
|
||||
this.bold = (fontName.indexOf('Bold') != -1);
|
||||
this.italic = (fontName.indexOf('Oblique') != -1) ||
|
||||
(fontName.indexOf('Italic') != -1);
|
||||
this.bold = (fontName.search(/bold/gi) != -1);
|
||||
this.italic = (fontName.search(/oblique/gi) != -1) ||
|
||||
(fontName.search(/italic/gi) != -1);
|
||||
|
||||
// Use 'name' instead of 'fontName' here because the original
|
||||
// name ArialBlack for example will be replaced by Helvetica.
|
||||
this.black = (name.indexOf("Black") != -1)
|
||||
this.black = (name.search(/Black/g) != -1);
|
||||
|
||||
this.defaultWidth = properties.defaultWidth;
|
||||
this.loadedName = fontName.split('-')[0];
|
||||
this.loading = false;
|
||||
return;
|
||||
@ -493,8 +453,8 @@ var Font = (function Font() {
|
||||
this.mimetype = 'font/opentype';
|
||||
|
||||
var subtype = properties.subtype;
|
||||
var cff = (subtype === 'Type1C') ? new Type2CFF(file, properties)
|
||||
: new CFF(name, file, properties);
|
||||
var cff = (subtype === 'Type1C') ?
|
||||
new Type2CFF(file, properties) : new CFF(name, file, properties);
|
||||
|
||||
// Wrap the CFF data inside an OTF font file
|
||||
data = this.convert(name, cff, properties);
|
||||
@ -517,6 +477,7 @@ var Font = (function Font() {
|
||||
this.data = data;
|
||||
this.type = properties.type;
|
||||
this.textMatrix = properties.textMatrix;
|
||||
this.defaultWidth = properties.defaultWidth;
|
||||
this.loadedName = getUniqueName();
|
||||
this.composite = properties.composite;
|
||||
this.loading = true;
|
||||
@ -579,6 +540,10 @@ var Font = (function Font() {
|
||||
};
|
||||
|
||||
function createOpenTypeHeader(sfnt, file, numTables) {
|
||||
// Windows hates the Mac TrueType sfnt version number
|
||||
if (sfnt == 'true')
|
||||
sfnt = string32(0x00010000);
|
||||
|
||||
// sfnt version (4 bytes)
|
||||
var header = sfnt;
|
||||
|
||||
@ -632,19 +597,24 @@ var Font = (function Font() {
|
||||
var codes = [];
|
||||
var length = glyphs.length;
|
||||
for (var n = 0; n < length; ++n)
|
||||
codes.push(String.fromCharCode(glyphs[n].unicode));
|
||||
codes.sort();
|
||||
codes.push({ unicode: glyphs[n].unicode, code: n });
|
||||
codes.sort(function(a, b) {
|
||||
return a.unicode - b.unicode;
|
||||
});
|
||||
|
||||
// Split the sorted codes into ranges.
|
||||
var ranges = [];
|
||||
for (var n = 0; n < length; ) {
|
||||
var start = codes[n++].charCodeAt(0);
|
||||
var start = codes[n].unicode;
|
||||
var startCode = codes[n].code;
|
||||
++n;
|
||||
var end = start;
|
||||
while (n < length && end + 1 == codes[n].charCodeAt(0)) {
|
||||
while (n < length && end + 1 == codes[n].unicode) {
|
||||
++end;
|
||||
++n;
|
||||
}
|
||||
ranges.push([start, end]);
|
||||
var endCode = codes[n - 1].code;
|
||||
ranges.push([start, end, startCode, endCode]);
|
||||
}
|
||||
|
||||
return ranges;
|
||||
@ -673,22 +643,39 @@ var Font = (function Font() {
|
||||
var idRangeOffsets = '';
|
||||
var glyphsIds = '';
|
||||
var bias = 0;
|
||||
for (var i = 0; i < segCount - 1; i++) {
|
||||
var range = ranges[i];
|
||||
var start = range[0];
|
||||
var end = range[1];
|
||||
var offset = (segCount - i) * 2 + bias * 2;
|
||||
bias += (end - start + 1);
|
||||
|
||||
startCount += string16(start);
|
||||
endCount += string16(end);
|
||||
idDeltas += string16(0);
|
||||
idRangeOffsets += string16(offset);
|
||||
if (deltas) {
|
||||
for (var i = 0; i < segCount - 1; i++) {
|
||||
var range = ranges[i];
|
||||
var start = range[0];
|
||||
var end = range[1];
|
||||
var offset = (segCount - i) * 2 + bias * 2;
|
||||
bias += (end - start + 1);
|
||||
|
||||
startCount += string16(start);
|
||||
endCount += string16(end);
|
||||
idDeltas += string16(0);
|
||||
idRangeOffsets += string16(offset);
|
||||
|
||||
var startCode = range[2];
|
||||
var endCode = range[3];
|
||||
for (var j = startCode; j <= endCode; ++j)
|
||||
glyphsIds += string16(deltas[j]);
|
||||
}
|
||||
} else {
|
||||
for (var i = 0; i < segCount - 1; i++) {
|
||||
var range = ranges[i];
|
||||
var start = range[0];
|
||||
var end = range[1];
|
||||
var startCode = range[2];
|
||||
|
||||
startCount += string16(start);
|
||||
endCount += string16(end);
|
||||
idDeltas += string16((startCode - start + 1) & 0xFFFF);
|
||||
idRangeOffsets += string16(0);
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < glyphs.length; i++)
|
||||
glyphsIds += string16(deltas ? deltas[i] : i + 1);
|
||||
|
||||
endCount += '\xFF\xFF';
|
||||
startCount += '\xFF\xFF';
|
||||
idDeltas += '\x00\x01';
|
||||
@ -708,7 +695,9 @@ var Font = (function Font() {
|
||||
format314);
|
||||
};
|
||||
|
||||
function createOS2Table(properties) {
|
||||
function createOS2Table(properties, override) {
|
||||
var override = override || {};
|
||||
|
||||
var ulUnicodeRange1 = 0;
|
||||
var ulUnicodeRange2 = 0;
|
||||
var ulUnicodeRange3 = 0;
|
||||
@ -739,6 +728,24 @@ var Font = (function Font() {
|
||||
}
|
||||
}
|
||||
|
||||
var unitsPerEm = override.unitsPerEm || kPDFGlyphSpaceUnits;
|
||||
var typoAscent = override.ascent || properties.ascent;
|
||||
var typoDescent = override.descent || properties.descent;
|
||||
var winAscent = override.yMax || typoAscent;
|
||||
var winDescent = -override.yMin || -typoDescent;
|
||||
|
||||
// if there is a units per em value but no other override
|
||||
// then scale the calculated ascent
|
||||
if (unitsPerEm != kPDFGlyphSpaceUnits &&
|
||||
'undefined' == typeof(override.ascent)) {
|
||||
// if the font units differ to the PDF glyph space units
|
||||
// then scale up the values
|
||||
typoAscent = Math.round(typoAscent * unitsPerEm / kPDFGlyphSpaceUnits);
|
||||
typoDescent = Math.round(typoDescent * unitsPerEm / kPDFGlyphSpaceUnits);
|
||||
winAscent = typoAscent;
|
||||
winDescent = -typoDescent;
|
||||
}
|
||||
|
||||
return '\x00\x03' + // version
|
||||
'\x02\x24' + // xAvgCharWidth
|
||||
'\x01\xF4' + // usWeightClass
|
||||
@ -767,11 +774,11 @@ var Font = (function Font() {
|
||||
string16(firstCharIndex ||
|
||||
properties.firstChar) + // usFirstCharIndex
|
||||
string16(lastCharIndex || properties.lastChar) + // usLastCharIndex
|
||||
string16(properties.ascent) + // sTypoAscender
|
||||
string16(properties.descent) + // sTypoDescender
|
||||
string16(typoAscent) + // sTypoAscender
|
||||
string16(typoDescent) + // sTypoDescender
|
||||
'\x00\x64' + // sTypoLineGap (7%-10% of the unitsPerEM value)
|
||||
string16(properties.ascent) + // usWinAscent
|
||||
string16(-properties.descent) + // usWinDescent
|
||||
string16(winAscent) + // usWinAscent
|
||||
string16(winDescent) + // usWinDescent
|
||||
'\x00\x00\x00\x00' + // ulCodePageRange1 (Bits 0-31)
|
||||
'\x00\x00\x00\x00' + // ulCodePageRange2 (Bits 32-63)
|
||||
string16(properties.xHeight) + // sxHeight
|
||||
@ -878,9 +885,11 @@ var Font = (function Font() {
|
||||
var data = file.getBytes(length);
|
||||
file.pos = previousPosition;
|
||||
|
||||
if (tag == 'head')
|
||||
if (tag == 'head') {
|
||||
// clearing checksum adjustment
|
||||
data[8] = data[9] = data[10] = data[11] = 0;
|
||||
data[17] |= 0x20; //Set font optimized for cleartype flag
|
||||
}
|
||||
|
||||
return {
|
||||
tag: tag,
|
||||
@ -920,7 +929,7 @@ var Font = (function Font() {
|
||||
// Check that table are sorted by platformID then encodingID,
|
||||
records.sort(function(a, b) {
|
||||
return ((a.platformID << 16) + a.encodingID) -
|
||||
((b.platformID << 16) + b.encodingID)
|
||||
((b.platformID << 16) + b.encodingID);
|
||||
});
|
||||
|
||||
var tables = [records[0]];
|
||||
@ -952,7 +961,7 @@ var Font = (function Font() {
|
||||
}
|
||||
|
||||
for (var i = 0; i < data.length; i++)
|
||||
cmap.data[i] = data.charCodeAt(i);
|
||||
cmap.data[i] = data.charCodeAt(i);
|
||||
}
|
||||
|
||||
var encoding = properties.encoding;
|
||||
@ -979,11 +988,13 @@ var Font = (function Font() {
|
||||
deltas.push(index);
|
||||
|
||||
var unicode = j + kCmapGlyphOffset;
|
||||
encoding[j].unicode = unicode;
|
||||
var mapping = encoding[j] || {};
|
||||
mapping.unicode = unicode;
|
||||
encoding[j] = mapping;
|
||||
glyphs.push({ unicode: unicode });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return cmap.data = createCMapTable(glyphs, deltas);
|
||||
} else if (format == 6) {
|
||||
// Format 6 is a 2-bytes dense mapping, which means the font data
|
||||
@ -995,48 +1006,98 @@ var Font = (function Font() {
|
||||
var entryCount = int16(font.getBytes(2));
|
||||
|
||||
var glyphs = [];
|
||||
var min = 0xffff, max = 0;
|
||||
for (var j = 0; j < entryCount; j++) {
|
||||
var charcode = int16(font.getBytes(2));
|
||||
if (!charcode)
|
||||
continue;
|
||||
glyphs.push(charcode);
|
||||
var ids = [];
|
||||
for (var j = 0; j < firstCode + entryCount; j++) {
|
||||
var code = (j >= firstCode) ? int16(font.getBytes(2)) : j;
|
||||
glyphs.push({ unicode: j + kCmapGlyphOffset });
|
||||
ids.push(code);
|
||||
|
||||
if (charcode < min)
|
||||
min = charcode;
|
||||
if (charcode > max)
|
||||
max = charcode;
|
||||
var mapping = encoding[j] || {};
|
||||
mapping.unicode = glyphs[j].unicode;
|
||||
encoding[j] = mapping;
|
||||
}
|
||||
|
||||
// Since Format 6 is a dense array, check for gaps
|
||||
for (var j = min; j < max; j++) {
|
||||
if (glyphs.indexOf(j) == -1)
|
||||
glyphs.push(j);
|
||||
}
|
||||
|
||||
for (var j = 0; j < glyphs.length; j++)
|
||||
glyphs[j] = { unicode: glyphs[j] + firstCode };
|
||||
|
||||
var ranges = getRanges(glyphs);
|
||||
assert(ranges.length == 1, 'Got ' + ranges.length +
|
||||
' ranges in a dense array');
|
||||
|
||||
var denseRange = ranges[0];
|
||||
var start = denseRange[0];
|
||||
var end = denseRange[1];
|
||||
var index = firstCode;
|
||||
for (var j = start; j <= end; j++) {
|
||||
var code = j - firstCode - 1;
|
||||
var mapping = encoding[index + 1] || {};
|
||||
mapping.unicode = glyphs[code].unicode;
|
||||
encoding[index++] = mapping;
|
||||
}
|
||||
return cmap.data = createCMapTable(glyphs);
|
||||
return cmap.data = createCMapTable(glyphs, ids);
|
||||
}
|
||||
}
|
||||
return cmap.data;
|
||||
};
|
||||
|
||||
function sanitizeMetrics(font, header, metrics, numGlyphs) {
|
||||
if (!header && !metrics)
|
||||
return;
|
||||
|
||||
// The vhea/vmtx tables are not required, so it happens that
|
||||
// some fonts embed a vmtx table without a vhea table. In this
|
||||
// situation the sanitizer assume numOfLongVerMetrics = 1. As
|
||||
// a result it tries to read numGlyphs - 1 SHORT from the vmtx
|
||||
// table, and if it is not possible, the font is rejected.
|
||||
// So remove the vmtx table if there is no vhea table.
|
||||
if (!header && metrics) {
|
||||
metrics.data = null;
|
||||
return;
|
||||
}
|
||||
|
||||
font.pos = (font.start ? font.start : 0) + header.offset;
|
||||
font.pos += header.length - 2;
|
||||
var numOfMetrics = int16(font.getBytes(2));
|
||||
|
||||
var numOfSidebearings = numGlyphs - numOfMetrics;
|
||||
var numMissing = numOfSidebearings -
|
||||
((hmtx.length - numOfMetrics * 4) >> 1);
|
||||
if (numMissing > 0) {
|
||||
font.pos = (font.start ? font.start : 0) + metrics.offset;
|
||||
var entries = '';
|
||||
for (var i = 0; i < hmtx.length; i++)
|
||||
entries += String.fromCharCode(font.getByte());
|
||||
for (var i = 0; i < numMissing; i++)
|
||||
entries += '\x00\x00';
|
||||
metrics.data = stringToArray(entries);
|
||||
}
|
||||
};
|
||||
|
||||
function sanitizeGlyphLocations(loca, glyf, numGlyphs,
|
||||
isGlyphLocationsLong) {
|
||||
var itemSize, itemDecode, itemEncode;
|
||||
if (isGlyphLocationsLong) {
|
||||
itemSize = 4;
|
||||
itemDecode = function(data, offset) {
|
||||
return (data[offset] << 24) | (data[offset + 1] << 16) |
|
||||
(data[offset + 2] << 8) | data[offset + 3];
|
||||
};
|
||||
itemEncode = function(data, offset, value) {
|
||||
data[offset] = (value >>> 24) & 0xFF;
|
||||
data[offset + 1] = (value >> 16) & 0xFF;
|
||||
data[offset + 2] = (value >> 8) & 0xFF;
|
||||
data[offset + 3] = value & 0xFF;
|
||||
};
|
||||
} else {
|
||||
itemSize = 2;
|
||||
itemDecode = function(data, offset) {
|
||||
return (data[offset] << 9) | (data[offset + 1] << 1);
|
||||
};
|
||||
itemEncode = function(data, offset, value) {
|
||||
data[offset] = (value >> 9) & 0xFF;
|
||||
data[offset + 1] = (value >> 1) & 0xFF;
|
||||
};
|
||||
}
|
||||
var locaData = loca.data;
|
||||
var startOffset = itemDecode(locaData, 0);
|
||||
var firstOffset = itemDecode(locaData, itemSize);
|
||||
if (firstOffset - startOffset < 12 || startOffset > 0) {
|
||||
// removing first glyph
|
||||
glyf.data = glyf.data.subarray(firstOffset);
|
||||
glyf.length -= firstOffset;
|
||||
|
||||
itemEncode(locaData, 0, 0);
|
||||
var i, pos = itemSize;
|
||||
for (i = 1; i <= numGlyphs; ++i) {
|
||||
itemEncode(locaData, pos,
|
||||
itemDecode(locaData, pos) - firstOffset);
|
||||
pos += itemSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check that required tables are present
|
||||
var requiredTables = ['OS/2', 'cmap', 'head', 'hhea',
|
||||
'hmtx', 'maxp', 'name', 'post'];
|
||||
@ -1044,7 +1105,7 @@ var Font = (function Font() {
|
||||
var header = readOpenTypeHeader(font);
|
||||
var numTables = header.numTables;
|
||||
|
||||
var cmap, maxp, hhea, hmtx;
|
||||
var cmap, maxp, hhea, hmtx, vhea, vmtx, head, loca, glyf;
|
||||
var tables = [];
|
||||
for (var i = 0; i < numTables; i++) {
|
||||
var table = readTableEntry(font);
|
||||
@ -1058,8 +1119,19 @@ var Font = (function Font() {
|
||||
hhea = table;
|
||||
else if (table.tag == 'hmtx')
|
||||
hmtx = table;
|
||||
else if (table.tag == 'head')
|
||||
head = table;
|
||||
|
||||
requiredTables.splice(index, 1);
|
||||
} else {
|
||||
if (table.tag == 'vmtx')
|
||||
vmtx = table;
|
||||
else if (table.tag == 'vhea')
|
||||
vhea = table;
|
||||
else if (table.tag == 'loca')
|
||||
loca = table;
|
||||
else if (table.tag == 'glyf')
|
||||
glyf = table;
|
||||
}
|
||||
tables.push(table);
|
||||
}
|
||||
@ -1079,33 +1151,34 @@ var Font = (function Font() {
|
||||
createOpenTypeHeader(header.version, ttf, numTables);
|
||||
|
||||
if (requiredTables.indexOf('OS/2') != -1) {
|
||||
// extract some more font properties from the OpenType head and
|
||||
// hhea tables; yMin and descent value are always negative
|
||||
var override = {
|
||||
unitsPerEm: int16([head.data[18], head.data[19]]),
|
||||
yMax: int16([head.data[42], head.data[43]]),
|
||||
yMin: int16([head.data[38], head.data[39]]) - 0x10000,
|
||||
ascent: int16([hhea.data[4], hhea.data[5]]),
|
||||
descent: int16([hhea.data[6], hhea.data[7]]) - 0x10000
|
||||
};
|
||||
|
||||
tables.push({
|
||||
tag: 'OS/2',
|
||||
data: stringToArray(createOS2Table(properties))
|
||||
data: stringToArray(createOS2Table(properties, override))
|
||||
});
|
||||
}
|
||||
|
||||
// Ensure the hmtx tables contains an advance width and a sidebearing
|
||||
// for the number of glyphs declared in the maxp table
|
||||
font.pos = (font.start ? font.start : 0) + maxp.offset;
|
||||
// Ensure the [h/v]mtx tables contains the advance width and
|
||||
// sidebearings information for numGlyphs in the maxp table
|
||||
font.pos = (font.start || 0) + maxp.offset;
|
||||
var version = int16(font.getBytes(4));
|
||||
var numGlyphs = int16(font.getBytes(2));
|
||||
|
||||
font.pos = (font.start ? font.start : 0) + hhea.offset;
|
||||
font.pos += hhea.length - 2;
|
||||
var numOfHMetrics = int16(font.getBytes(2));
|
||||
sanitizeMetrics(font, hhea, hmtx, numGlyphs);
|
||||
sanitizeMetrics(font, vhea, vmtx, numGlyphs);
|
||||
|
||||
var numOfSidebearings = numGlyphs - numOfHMetrics;
|
||||
var numMissing = numOfSidebearings -
|
||||
((hmtx.length - numOfHMetrics * 4) >> 1);
|
||||
if (numMissing > 0) {
|
||||
font.pos = (font.start ? font.start : 0) + hmtx.offset;
|
||||
var metrics = '';
|
||||
for (var i = 0; i < hmtx.length; i++)
|
||||
metrics += String.fromCharCode(font.getByte());
|
||||
for (var i = 0; i < numMissing; i++)
|
||||
metrics += '\x00\x00';
|
||||
hmtx.data = stringToArray(metrics);
|
||||
if (head && loca && glyf) {
|
||||
var isGlyphLocationsLong = int16([head.data[50], head.data[51]]);
|
||||
sanitizeGlyphLocations(loca, glyf, numGlyphs, isGlyphLocationsLong);
|
||||
}
|
||||
|
||||
// Sanitizer reduces the glyph advanceWidth to the maxAdvanceWidth
|
||||
@ -1132,20 +1205,36 @@ var Font = (function Font() {
|
||||
tables.push(cmap);
|
||||
}
|
||||
|
||||
var encoding = properties.encoding;
|
||||
var encoding = properties.encoding, i;
|
||||
if (!encoding[0]) {
|
||||
// the font is directly characters to glyphs with no encoding
|
||||
// so create an identity encoding
|
||||
for (i = 0; i < numGlyphs; i++)
|
||||
encoding[i] = { unicode: i + kCmapGlyphOffset };
|
||||
var widths = properties.widths;
|
||||
for (i = 0; i < numGlyphs; i++) {
|
||||
var width = widths[i];
|
||||
encoding[i] = {
|
||||
unicode: i <= 0x1f || (i >= 127 && i <= 255) ?
|
||||
i + kCmapGlyphOffset : i,
|
||||
width: IsNum(width) ? width : properties.defaultWidth
|
||||
};
|
||||
}
|
||||
} else {
|
||||
for (var code in encoding)
|
||||
encoding[code].unicode += kCmapGlyphOffset;
|
||||
for (i in encoding) {
|
||||
if (encoding.hasOwnProperty(i)) {
|
||||
var unicode = encoding[i].unicode;
|
||||
if (unicode <= 0x1f || (unicode >= 127 && unicode <= 255))
|
||||
encoding[i].unicode = unicode += kCmapGlyphOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var glyphs = [];
|
||||
for (var i = 1; i < numGlyphs; i++)
|
||||
glyphs.push({ unicode: i + kCmapGlyphOffset });
|
||||
for (i = 1; i < numGlyphs; i++) {
|
||||
glyphs.push({
|
||||
unicode: i <= 0x1f || (i >= 127 && i <= 255) ?
|
||||
i + kCmapGlyphOffset : i
|
||||
});
|
||||
}
|
||||
cmap.data = createCMapTable(glyphs);
|
||||
} else {
|
||||
replaceCMapTable(cmap, font, properties);
|
||||
@ -1204,7 +1293,7 @@ var Font = (function Font() {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
// The offsets object holds at the same time a representation of where
|
||||
// to write the table entry information about a table and another offset
|
||||
@ -1330,20 +1419,25 @@ var Font = (function Font() {
|
||||
window.btoa(data) + ');');
|
||||
var rule = "@font-face { font-family:'" + fontName + "';src:" + url + '}';
|
||||
var styleSheet = document.styleSheets[0];
|
||||
if (!styleSheet) {
|
||||
document.documentElement.firstChild.appendChild(
|
||||
document.createElement('style'));
|
||||
styleSheet = document.styleSheets[0];
|
||||
}
|
||||
styleSheet.insertRule(rule, styleSheet.cssRules.length);
|
||||
|
||||
return rule;
|
||||
},
|
||||
|
||||
charsToUnicode: function fonts_chars2Unicode(chars) {
|
||||
charsToGlyphs: function fonts_chars2Glyphs(chars) {
|
||||
var charsCache = this.charsCache;
|
||||
var str;
|
||||
var glyphs;
|
||||
|
||||
// if we translated this string before, just grab it from the cache
|
||||
if (charsCache) {
|
||||
str = charsCache[chars];
|
||||
if (str)
|
||||
return str;
|
||||
glyphs = charsCache[chars];
|
||||
if (glyphs)
|
||||
return glyphs;
|
||||
}
|
||||
|
||||
// lazily create the translation cache
|
||||
@ -1354,7 +1448,8 @@ var Font = (function Font() {
|
||||
var encoding = this.encoding;
|
||||
if (!encoding)
|
||||
return chars;
|
||||
str = '';
|
||||
|
||||
glyphs = [];
|
||||
|
||||
if (this.composite) {
|
||||
// composite fonts have multi-byte strings convert the string from
|
||||
@ -1365,30 +1460,39 @@ var Font = (function Font() {
|
||||
// loop should never end on the last byte
|
||||
for (var i = 0; i < length; i++) {
|
||||
var charcode = int16([chars.charCodeAt(i++), chars.charCodeAt(i)]);
|
||||
var unicode = encoding[charcode].unicode;
|
||||
str += String.fromCharCode(unicode);
|
||||
var glyph = encoding[charcode];
|
||||
if ('undefined' == typeof(glyph)) {
|
||||
warn('Unencoded charcode ' + charcode);
|
||||
glyph = {
|
||||
unicode: charcode,
|
||||
width: this.defaultWidth
|
||||
};
|
||||
}
|
||||
glyphs.push(glyph);
|
||||
// placing null after each word break charcode (ASCII SPACE)
|
||||
if (charcode == 0x20)
|
||||
glyphs.push(null);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (var i = 0; i < chars.length; ++i) {
|
||||
var charcode = chars.charCodeAt(i);
|
||||
var unicode = encoding[charcode].unicode;
|
||||
if ('undefined' == typeof(unicode)) {
|
||||
var glyph = encoding[charcode];
|
||||
if ('undefined' == typeof(glyph)) {
|
||||
warn('Unencoded charcode ' + charcode);
|
||||
unicode = charcode;
|
||||
glyph = {
|
||||
unicode: charcode,
|
||||
width: this.defaultWidth
|
||||
};
|
||||
}
|
||||
|
||||
// Handle surrogate pairs
|
||||
if (unicode > 0xFFFF) {
|
||||
str += String.fromCharCode(unicode & 0xFFFF);
|
||||
unicode >>= 16;
|
||||
}
|
||||
str += String.fromCharCode(unicode);
|
||||
glyphs.push(glyph);
|
||||
if (charcode == 0x20)
|
||||
glyphs.push(null);
|
||||
}
|
||||
}
|
||||
|
||||
// Enter the translated string into the cache
|
||||
return charsCache[chars] = str;
|
||||
return (charsCache[chars] = glyphs);
|
||||
}
|
||||
};
|
||||
|
||||
@ -1421,7 +1525,7 @@ var Type1Parser = function() {
|
||||
r = ((value + r) * c1 + c2) & ((1 << 16) - 1);
|
||||
}
|
||||
return decryptedString.slice(discardNumber);
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* CharStrings are encoded following the the CharString Encoding sequence
|
||||
@ -1562,6 +1666,9 @@ var Type1Parser = function() {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
} else if (!kHintingEnabled && (value == 1 || value == 2)) {
|
||||
charstring.push('drop', 'drop', 'drop', 'drop', 'drop', 'drop');
|
||||
continue;
|
||||
}
|
||||
|
||||
command = charStringDictionary['12'][escape];
|
||||
@ -1586,6 +1693,9 @@ var Type1Parser = function() {
|
||||
|
||||
charstring.push(lsb, 'hmoveto');
|
||||
continue;
|
||||
} else if (!kHintingEnabled && (value == 1 || value == 3)) {
|
||||
charstring.push('drop', 'drop');
|
||||
continue;
|
||||
}
|
||||
command = charStringDictionary[value];
|
||||
}
|
||||
@ -1618,7 +1728,7 @@ var Type1Parser = function() {
|
||||
}
|
||||
|
||||
return { charstring: charstring, width: width, lsb: lsb };
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns an object containing a Subrs array and a CharStrings
|
||||
@ -1638,7 +1748,7 @@ var Type1Parser = function() {
|
||||
for (var i = 0; i < array.length; i++)
|
||||
array[i] = parseFloat(array[i] || 0);
|
||||
return array;
|
||||
};
|
||||
}
|
||||
|
||||
function readNumber(str, index) {
|
||||
while (str[index] == ' ')
|
||||
@ -1651,11 +1761,11 @@ var Type1Parser = function() {
|
||||
count++;
|
||||
|
||||
return parseFloat(str.substr(start, count) || 0);
|
||||
};
|
||||
}
|
||||
|
||||
function isSeparator(c) {
|
||||
return c == ' ' || c == '\n' || c == '\x0d';
|
||||
};
|
||||
}
|
||||
|
||||
this.extractFontProgram = function t1_extractFontProgram(stream) {
|
||||
var eexec = decrypt(stream, kEexecEncryptionKey, 4);
|
||||
@ -1693,7 +1803,7 @@ var Type1Parser = function() {
|
||||
};
|
||||
var c = eexecStr[i];
|
||||
|
||||
if ((glyphsSection || subrsSection) &&
|
||||
if ((glyphsSection || subrsSection) &&
|
||||
(token == 'RD' || token == '-|')) {
|
||||
i++;
|
||||
var data = eexec.slice(i, i + length);
|
||||
@ -1729,7 +1839,7 @@ var Type1Parser = function() {
|
||||
getToken(); // read in 'array'
|
||||
for (var j = 0; j < num; ++j) {
|
||||
var t = getToken(); // read in 'dup'
|
||||
if (t == 'ND' || t == '|-' || t == 'noaccess')
|
||||
if (t == 'ND' || t == '|-' || t == 'noaccess')
|
||||
break;
|
||||
var index = parseInt(getToken(), 10);
|
||||
if (index > j)
|
||||
@ -1780,7 +1890,7 @@ var Type1Parser = function() {
|
||||
}
|
||||
|
||||
return program;
|
||||
},
|
||||
};
|
||||
|
||||
this.extractFontHeader = function t1_extractFontHeader(stream, properties) {
|
||||
var headerString = '';
|
||||
@ -1828,10 +1938,12 @@ var Type1Parser = function() {
|
||||
if (token == 'dup') {
|
||||
var index = parseInt(getToken(), 10);
|
||||
var glyph = getToken();
|
||||
|
||||
|
||||
if ('undefined' == typeof(properties.differences[index])) {
|
||||
var mapping = { unicode: GlyphsUnicode[glyph] || j };
|
||||
properties.glyphs[glyph] = properties.encoding[index] = mapping;
|
||||
var mapping = properties.encoding[index] || {};
|
||||
mapping.unicode = GlyphsUnicode[glyph] || index;
|
||||
properties.glyphs[glyph] = properties.encoding[index] =
|
||||
mapping;
|
||||
}
|
||||
getToken(); // read the in 'put'
|
||||
}
|
||||
@ -1847,7 +1959,7 @@ var Type1Parser = function() {
|
||||
};
|
||||
|
||||
/**
|
||||
* The CFF class takes a Type1 file and wrap it into a
|
||||
* The CFF class takes a Type1 file and wrap it into a
|
||||
* 'Compact Font Format' which itself embed Type2 charstrings.
|
||||
*/
|
||||
var CFFStrings = [
|
||||
@ -2177,7 +2289,7 @@ CFF.prototype = {
|
||||
'globalSubrs': this.createCFFIndexHeader([]),
|
||||
|
||||
'charset': (function charset(self) {
|
||||
var charset = '\x00'; // Encoding
|
||||
var charsetString = '\x00'; // Encoding
|
||||
|
||||
var count = glyphs.length;
|
||||
for (var i = 0; i < count; i++) {
|
||||
@ -2189,9 +2301,9 @@ CFF.prototype = {
|
||||
if (index == -1)
|
||||
index = 0;
|
||||
|
||||
charset += String.fromCharCode(index >> 8, index & 0xff);
|
||||
charsetString += String.fromCharCode(index >> 8, index & 0xff);
|
||||
}
|
||||
return charset;
|
||||
return charsetString;
|
||||
})(this),
|
||||
|
||||
'charstrings': this.createCFFIndexHeader([[0x8B, 0x0E]].concat(glyphs),
|
||||
@ -2258,7 +2370,7 @@ var Type2CFF = (function() {
|
||||
this.properties = properties;
|
||||
|
||||
this.data = this.parse();
|
||||
};
|
||||
}
|
||||
|
||||
constructor.prototype = {
|
||||
parse: function cff_parse() {
|
||||
@ -2289,7 +2401,7 @@ var Type2CFF = (function() {
|
||||
var charStrings = this.parseIndex(topDict.CharStrings);
|
||||
var charset = this.parseCharsets(topDict.charset,
|
||||
charStrings.length, strings);
|
||||
var hasSupplement = this.parseEncoding(topDict.Encoding, properties,
|
||||
var hasSupplement = this.parseEncoding(topDict.Encoding, properties,
|
||||
strings, charset);
|
||||
|
||||
// The font sanitizer does not support CFF encoding with a
|
||||
@ -2321,8 +2433,6 @@ var Type2CFF = (function() {
|
||||
|
||||
getCharStrings: function cff_charstrings(charsets, charStrings,
|
||||
privDict, properties) {
|
||||
var widths = properties.widths;
|
||||
|
||||
var defaultWidth = privDict['defaultWidthX'];
|
||||
var nominalWidth = privDict['nominalWidthX'];
|
||||
|
||||
@ -2344,13 +2454,13 @@ var Type2CFF = (function() {
|
||||
if (code == -1)
|
||||
index = code = mapping.unicode || index;
|
||||
|
||||
var width = mapping.width || defaultWidth;
|
||||
if (code <= 0x1f || (code >= 127 && code <= 255))
|
||||
code += kCmapGlyphOffset;
|
||||
|
||||
var width = mapping.width;
|
||||
properties.glyphs[glyph] = properties.encoding[index] = {
|
||||
unicode: code,
|
||||
width: width
|
||||
width: IsNum(width) ? width : defaultWidth
|
||||
};
|
||||
|
||||
charstrings.push({
|
||||
@ -2366,7 +2476,8 @@ var Type2CFF = (function() {
|
||||
return charstrings;
|
||||
},
|
||||
|
||||
parseEncoding: function cff_parseencoding(pos, properties, strings, charset) {
|
||||
parseEncoding: function cff_parseencoding(pos, properties, strings,
|
||||
charset) {
|
||||
var encoding = {};
|
||||
var bytes = this.bytes;
|
||||
|
||||
@ -2381,8 +2492,8 @@ var Type2CFF = (function() {
|
||||
|
||||
if (pos == 0 || pos == 1) {
|
||||
var gid = 1;
|
||||
var baseEncoding = pos ? Encodings.ExpertEncoding
|
||||
: Encodings.StandardEncoding;
|
||||
var baseEncoding =
|
||||
pos ? Encodings.ExpertEncoding : Encodings.StandardEncoding;
|
||||
for (var i = 0; i < charset.length; i++) {
|
||||
var index = baseEncoding.indexOf(charset[i]);
|
||||
if (index != -1)
|
||||
@ -2393,7 +2504,7 @@ var Type2CFF = (function() {
|
||||
switch (format & 0x7f) {
|
||||
case 0:
|
||||
var glyphsCount = bytes[pos++];
|
||||
for (var i = 1; i <= glyphsCount; i++)
|
||||
for (var i = 1; i <= glyphsCount; i++)
|
||||
encoding[bytes[pos++]] = i;
|
||||
|
||||
if (format & 0x80) {
|
||||
@ -2419,7 +2530,7 @@ var Type2CFF = (function() {
|
||||
break;
|
||||
|
||||
default:
|
||||
error('Unknow encoding format: ' + format + " in CFF");
|
||||
error('Unknow encoding format: ' + format + ' in CFF');
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -2482,7 +2593,7 @@ var Type2CFF = (function() {
|
||||
case 21:
|
||||
dict['nominalWidthX'] = value[0];
|
||||
default:
|
||||
TODO('interpret top dict key');
|
||||
TODO('interpret top dict key: ' + key);
|
||||
}
|
||||
}
|
||||
return dict;
|
||||
@ -2594,7 +2705,7 @@ var Type2CFF = (function() {
|
||||
error('Incorrect byte');
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
}
|
||||
|
||||
function parseFloatOperand() {
|
||||
var str = '';
|
||||
@ -2616,7 +2727,7 @@ var Type2CFF = (function() {
|
||||
str += lookup[b2];
|
||||
}
|
||||
return parseFloat(str);
|
||||
};
|
||||
}
|
||||
|
||||
var operands = [];
|
||||
var entries = [];
|
||||
@ -2642,15 +2753,14 @@ var Type2CFF = (function() {
|
||||
parseIndex: function cff_parseIndex(pos) {
|
||||
var bytes = this.bytes;
|
||||
var count = bytes[pos++] << 8 | bytes[pos++];
|
||||
if (count == 0) {
|
||||
var offsets = [];
|
||||
var end = pos;
|
||||
} else {
|
||||
var offsets = [];
|
||||
var end = pos;
|
||||
|
||||
if (count != 0) {
|
||||
var offsetSize = bytes[pos++];
|
||||
// add 1 for offset to determine size of last object
|
||||
var startPos = pos + ((count + 1) * offsetSize) - 1;
|
||||
|
||||
var offsets = [];
|
||||
for (var i = 0, ii = count + 1; i < ii; ++i) {
|
||||
var offset = 0;
|
||||
for (var j = 0; j < offsetSize; ++j) {
|
||||
@ -2659,7 +2769,7 @@ var Type2CFF = (function() {
|
||||
}
|
||||
offsets.push(startPos + offset);
|
||||
}
|
||||
var end = offsets[count];
|
||||
end = offsets[count];
|
||||
}
|
||||
|
||||
return {
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
'use strict';
|
||||
|
2941
metrics.js
Normal file
2941
metrics.js
Normal file
File diff suppressed because it is too large
Load Diff
@ -1,4 +1,4 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
/*
|
||||
@ -8,6 +8,7 @@
|
||||
'use strict';
|
||||
|
||||
var appPath, browser, canvas, currentTaskIdx, manifest, stdout;
|
||||
var inFlightRequests = 0;
|
||||
|
||||
function queryParams() {
|
||||
var qs = window.location.search.substring(1);
|
||||
@ -42,12 +43,12 @@ function load() {
|
||||
if (r.readyState == 4) {
|
||||
log('done\n');
|
||||
manifest = JSON.parse(r.responseText);
|
||||
currentTaskIdx = 0, nextTask();
|
||||
currentTaskIdx = 0;
|
||||
nextTask();
|
||||
}
|
||||
};
|
||||
r.send(null);
|
||||
}
|
||||
window.onload = load;
|
||||
|
||||
function nextTask() {
|
||||
if (currentTaskIdx == manifest.length) {
|
||||
@ -73,7 +74,8 @@ function nextTask() {
|
||||
failure = 'load PDF doc : ' + e.toString();
|
||||
}
|
||||
|
||||
task.pageNum = 1, nextPage(task, failure);
|
||||
task.pageNum = 1;
|
||||
nextPage(task, failure);
|
||||
}
|
||||
};
|
||||
r.send(null);
|
||||
@ -89,7 +91,8 @@ function nextPage(task, loadError) {
|
||||
if (!task.pdfDoc) {
|
||||
sendTaskResult(canvas.toDataURL('image/png'), task, failure);
|
||||
log('done' + (failure ? ' (failed !: ' + failure + ')' : '') + '\n');
|
||||
++currentTaskIdx, nextTask();
|
||||
++currentTaskIdx;
|
||||
nextTask();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -98,7 +101,8 @@ function nextPage(task, loadError) {
|
||||
log(' Round ' + (1 + task.round) + '\n');
|
||||
task.pageNum = 1;
|
||||
} else {
|
||||
++currentTaskIdx, nextTask();
|
||||
++currentTaskIdx;
|
||||
nextTask();
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -123,7 +127,7 @@ function nextPage(task, loadError) {
|
||||
page.startRendering(
|
||||
ctx,
|
||||
function(e) {
|
||||
snapshotCurrentPage(page, task, (!failure && e) ?
|
||||
snapshotCurrentPage(task, (!failure && e) ?
|
||||
('render : ' + e) : failure);
|
||||
}
|
||||
);
|
||||
@ -135,11 +139,11 @@ function nextPage(task, loadError) {
|
||||
if (failure) {
|
||||
// Skip right to snapshotting if there was a failure, since the
|
||||
// fonts might be in an inconsistent state.
|
||||
snapshotCurrentPage(page, task, failure);
|
||||
snapshotCurrentPage(task, failure);
|
||||
}
|
||||
}
|
||||
|
||||
function snapshotCurrentPage(page, task, failure) {
|
||||
function snapshotCurrentPage(task, failure) {
|
||||
log('done, snapshotting... ');
|
||||
|
||||
sendTaskResult(canvas.toDataURL('image/png'), task, failure);
|
||||
@ -149,7 +153,8 @@ function snapshotCurrentPage(page, task, failure) {
|
||||
var backoff = (inFlightRequests > 0) ? inFlightRequests * 10 : 0;
|
||||
setTimeout(
|
||||
function() {
|
||||
++task.pageNum, nextPage(task);
|
||||
++task.pageNum;
|
||||
nextPage(task);
|
||||
},
|
||||
backoff
|
||||
);
|
||||
@ -182,7 +187,6 @@ function done() {
|
||||
}
|
||||
}
|
||||
|
||||
var inFlightRequests = 0;
|
||||
function sendTaskResult(snapshot, task, failure) {
|
||||
var result = { browser: browser,
|
||||
id: task.id,
|
||||
@ -201,7 +205,7 @@ function sendTaskResult(snapshot, task, failure) {
|
||||
if (r.readyState == 4) {
|
||||
inFlightRequests--;
|
||||
}
|
||||
}
|
||||
};
|
||||
document.getElementById('inFlightCount').innerHTML = inFlightRequests++;
|
||||
r.send(JSON.stringify(result));
|
||||
}
|
||||
|
12
test/pdfs/.gitignore
vendored
12
test/pdfs/.gitignore
vendored
@ -1,3 +1,13 @@
|
||||
pdf.pdf
|
||||
DiwanProfile.pdf
|
||||
artofwar.pdf
|
||||
cable.pdf
|
||||
ecma262.pdf
|
||||
hmm.pdf
|
||||
i9.pdf
|
||||
intelisa.pdf
|
||||
openweb_tm-PRINT.pdf
|
||||
pdf.pdf
|
||||
pdkids.pdf
|
||||
shavian.pdf
|
||||
jai.pdf
|
||||
|
||||
|
1
test/pdfs/artofwar.pdf.link
Normal file
1
test/pdfs/artofwar.pdf.link
Normal file
@ -0,0 +1 @@
|
||||
http://www.puppetpress.com/classics/ArtofWarbySunTzu.pdf
|
1
test/pdfs/fips197.pdf.link
Normal file
1
test/pdfs/fips197.pdf.link
Normal file
@ -0,0 +1 @@
|
||||
http://csrc.nist.gov/publications/fips/fips197/fips-197.pdf
|
1
test/pdfs/wdsg_fitc.pdf.link
Normal file
1
test/pdfs/wdsg_fitc.pdf.link
Normal file
@ -0,0 +1 @@
|
||||
http://www.airgid.com/book/wdsg_fitc.pdf
|
1
test/pdfs/wnv_chinese.pdf.link
Normal file
1
test/pdfs/wnv_chinese.pdf.link
Normal file
@ -0,0 +1 @@
|
||||
http://www.cdc.gov/ncidod/dvbid/westnile/languages/chinese.pdf
|
2
test/resources/browser_manifests/.gitignore
vendored
Normal file
2
test/resources/browser_manifests/.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
browser_manifest.json
|
||||
|
@ -69,6 +69,12 @@
|
||||
"rounds": 1,
|
||||
"type": "load"
|
||||
},
|
||||
{ "id": "wnv_chinese-pdf",
|
||||
"file": "pdfs/wnv_chinese.pdf",
|
||||
"link": true,
|
||||
"rounds": 1,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "i9-pdf",
|
||||
"file": "pdfs/i9.pdf",
|
||||
"link": true,
|
||||
@ -109,5 +115,23 @@
|
||||
"link": true,
|
||||
"rounds": 1,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "artofwar",
|
||||
"file": "pdfs/artofwar.pdf",
|
||||
"link": true,
|
||||
"rounds": 1,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "wdsg_fitc",
|
||||
"file": "pdfs/wdsg_fitc.pdf",
|
||||
"link": true,
|
||||
"rounds": 1,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "fips197",
|
||||
"file": "pdfs/fips197.pdf",
|
||||
"link": true,
|
||||
"rounds": 1,
|
||||
"type": "load"
|
||||
}
|
||||
]
|
||||
|
@ -6,6 +6,7 @@
|
||||
<script type="text/javascript" src="/fonts.js"></script>
|
||||
<script type="text/javascript" src="/crypto.js"></script>
|
||||
<script type="text/javascript" src="/glyphlist.js"></script>
|
||||
<script type="text/javascript" src="/metrics.js"></script>
|
||||
<script type="text/javascript" src="driver.js"></script>
|
||||
</head>
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
'use strict';
|
||||
@ -560,7 +560,7 @@ var CFFDictDataMap = {
|
||||
'18': {
|
||||
name: 'ExpansionFactor'
|
||||
},
|
||||
'9': {
|
||||
'19': {
|
||||
name: 'initialRandomSeed'
|
||||
},
|
||||
'20': {
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
'use strict';
|
||||
@ -69,7 +69,7 @@ function readCharstringEncoding(aString) {
|
||||
} else if (value <= 31) {
|
||||
token = CFFEncodingMap[value];
|
||||
} else if (value < 247) {
|
||||
token = parseInt(value) - 139;
|
||||
token = parseInt(value, 10) - 139;
|
||||
} else if (value < 251) {
|
||||
token = ((value - 247) * 256) + aString[i++] + 108;
|
||||
} else if (value < 255) {
|
||||
@ -113,7 +113,7 @@ function readFontDictData(aString, aMap) {
|
||||
while (!parsed) {
|
||||
var byte = aString[i++];
|
||||
|
||||
var nibbles = [parseInt(byte / 16), parseInt(byte % 16)];
|
||||
var nibbles = [parseInt(byte / 16, 10), parseInt(byte % 16, 10)];
|
||||
for (var j = 0; j < nibbles.length; j++) {
|
||||
var nibble = nibbles[j];
|
||||
switch (nibble) {
|
||||
@ -144,7 +144,7 @@ function readFontDictData(aString, aMap) {
|
||||
} else if (value <= 31) {
|
||||
token = aMap[value];
|
||||
} else if (value <= 246) {
|
||||
token = parseInt(value) - 139;
|
||||
token = parseInt(value, 10) - 139;
|
||||
} else if (value <= 250) {
|
||||
token = ((value - 247) * 256) + aString[i++] + 108;
|
||||
} else if (value <= 254) {
|
||||
@ -193,7 +193,7 @@ function readFontIndexData(aStream, aIsByte) {
|
||||
}
|
||||
error(offsize + ' is not a valid offset size');
|
||||
return null;
|
||||
};
|
||||
}
|
||||
|
||||
var offsets = [];
|
||||
for (var i = 0; i < count + 1; i++)
|
||||
@ -236,7 +236,7 @@ var Type2Parser = function(aFilePath) {
|
||||
function dump(aStr) {
|
||||
if (debug)
|
||||
log(aStr);
|
||||
};
|
||||
}
|
||||
|
||||
function parseAsToken(aString, aMap) {
|
||||
var decoded = readFontDictData(aString, aMap);
|
||||
@ -277,7 +277,7 @@ var Type2Parser = function(aFilePath) {
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
this.parse = function(aStream) {
|
||||
font.set('major', aStream.getByte());
|
||||
@ -353,7 +353,7 @@ var Type2Parser = function(aFilePath) {
|
||||
aStream.pos = charsetEntry;
|
||||
var charset = readCharset(aStream, charStrings);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
/*
|
||||
|
21
web/compatibility.js
Executable file → Normal file
21
web/compatibility.js
Executable file → Normal file
@ -1,6 +1,8 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
'use strict';
|
||||
|
||||
// Checking if the typed arrays are supported
|
||||
(function() {
|
||||
if (typeof Uint8Array !== 'undefined')
|
||||
@ -10,8 +12,9 @@
|
||||
return this.slice(start, end);
|
||||
}
|
||||
|
||||
function set_(array, offset) {
|
||||
if (arguments.length < 2) offset = 0;
|
||||
function set_function(array, offset) {
|
||||
if (arguments.length < 2)
|
||||
offset = 0;
|
||||
for (var i = 0, n = array.length; i < n; ++i, ++offset)
|
||||
this[offset] = array[i] & 0xFF;
|
||||
}
|
||||
@ -19,15 +22,17 @@
|
||||
function TypedArray(arg1) {
|
||||
var result;
|
||||
if (typeof arg1 === 'number') {
|
||||
result = new Array(arg1);
|
||||
for (var i = 0; i < arg1; ++i)
|
||||
result[i] = 0;
|
||||
result = [];
|
||||
for (var i = 0; i < arg1; ++i)
|
||||
result[i] = 0;
|
||||
} else
|
||||
result = arg1.slice(0);
|
||||
result = arg1.slice(0);
|
||||
|
||||
result.subarray = subarray;
|
||||
result.buffer = result;
|
||||
result.byteLength = result.length;
|
||||
result.set = set_;
|
||||
result.set = set_function;
|
||||
|
||||
if (typeof arg1 === 'object' && arg1.buffer)
|
||||
result.buffer = arg1.buffer;
|
||||
|
||||
|
0
web/viewer.css
Executable file → Normal file
0
web/viewer.css
Executable file → Normal file
@ -10,6 +10,7 @@
|
||||
<script type="text/javascript" src="../fonts.js"></script>
|
||||
<script type="text/javascript" src="../crypto.js"></script>
|
||||
<script type="text/javascript" src="../glyphlist.js"></script>
|
||||
<script type="text/javascript" src="../metrics.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
'use strict';
|
||||
@ -98,7 +98,7 @@ var PDFView = {
|
||||
},
|
||||
|
||||
get page() {
|
||||
return parseInt(document.location.hash.substring(1)) || 1;
|
||||
return parseInt(document.location.hash.substring(1), 10) || 1;
|
||||
},
|
||||
|
||||
open: function(url, scale) {
|
||||
@ -170,7 +170,7 @@ var PDFView = {
|
||||
}
|
||||
|
||||
this.setScale(scale || kDefaultScale, true);
|
||||
this.page = parseInt(document.location.hash.substring(1)) || 1;
|
||||
this.page = parseInt(document.location.hash.substring(1), 10) || 1;
|
||||
this.pagesRefMap = pagesRefMap;
|
||||
this.destinations = pdf.catalog.destinations;
|
||||
if (pdf.catalog.documentOutline) {
|
||||
@ -209,7 +209,7 @@ var PDFView = {
|
||||
|
||||
var currentHeight = kBottomMargin;
|
||||
var windowTop = window.pageYOffset;
|
||||
for (var i = 1; i <= pages.length; i++) {
|
||||
for (var i = 1; i <= pages.length; ++i) {
|
||||
var page = pages[i - 1];
|
||||
var pageHeight = page.height * page.scale + kBottomMargin;
|
||||
if (currentHeight + pageHeight > windowTop)
|
||||
@ -219,10 +219,11 @@ var PDFView = {
|
||||
}
|
||||
|
||||
var windowBottom = window.pageYOffset + window.innerHeight;
|
||||
for (; i <= pages.length && currentHeight < windowBottom; i++) {
|
||||
var page = pages[i - 1];
|
||||
visiblePages.push({ id: page.id, y: currentHeight, view: page });
|
||||
currentHeight += page.height * page.scale + kBottomMargin;
|
||||
for (; i <= pages.length && currentHeight < windowBottom; ++i) {
|
||||
var singlePage = pages[i - 1];
|
||||
visiblePages.push({ id: singlePage.id, y: currentHeight,
|
||||
view: singlePage });
|
||||
currentHeight += singlePage.height * singlePage.scale + kBottomMargin;
|
||||
}
|
||||
|
||||
return visiblePages;
|
||||
@ -256,13 +257,13 @@ var PageView = function(container, content, id, width, height,
|
||||
div.removeAttribute('data-loaded');
|
||||
};
|
||||
|
||||
function setupLinks(canvas, content, scale) {
|
||||
function setupLinks(content, scale) {
|
||||
function bindLink(link, dest) {
|
||||
link.onclick = function() {
|
||||
if (dest)
|
||||
PDFView.navigateTo(dest);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
}
|
||||
var links = content.getLinks();
|
||||
for (var i = 0; i < links.length; i++) {
|
||||
@ -283,8 +284,6 @@ var PageView = function(container, content, id, width, height,
|
||||
var width = 0, height = 0, widthScale, heightScale;
|
||||
var scale = 0;
|
||||
switch (dest[1].name) {
|
||||
default:
|
||||
return;
|
||||
case 'XYZ':
|
||||
x = dest[2];
|
||||
y = dest[3];
|
||||
@ -315,6 +314,8 @@ var PageView = function(container, content, id, width, height,
|
||||
height / kCssUnits;
|
||||
scale = Math.min(widthScale, heightScale);
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
var boundingRect = [
|
||||
@ -369,7 +370,7 @@ var PageView = function(container, content, id, width, height,
|
||||
stats.begin = Date.now();
|
||||
this.content.startRendering(ctx, this.updateStats);
|
||||
|
||||
setupLinks(canvas, this.content, this.scale);
|
||||
setupLinks(this.content, this.scale);
|
||||
div.setAttribute('data-loaded', true);
|
||||
|
||||
return true;
|
||||
@ -593,7 +594,7 @@ window.addEventListener('pagechange', function pagechange(evt) {
|
||||
}, true);
|
||||
|
||||
window.addEventListener('keydown', function keydown(evt) {
|
||||
switch(evt.keyCode) {
|
||||
switch (evt.keyCode) {
|
||||
case 61: // FF/Mac '='
|
||||
case 107: // FF '+' and '='
|
||||
case 187: // Chrome '+'
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
'use strict';
|
||||
@ -39,7 +39,7 @@ function GradientProxy(cmdQueue, x0, y0, x1, y1) {
|
||||
cmdQueue.push(['$createLinearGradient', [x0, y0, x1, y1]]);
|
||||
this.addColorStop = function(i, rgba) {
|
||||
cmdQueue.push(['$addColorStop', [i, rgba]]);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Really simple PatternProxy.
|
||||
@ -72,7 +72,7 @@ function CanvasProxy(width, height) {
|
||||
throw 'CanvasProxy can only provide a 2d context.';
|
||||
}
|
||||
return ctx;
|
||||
}
|
||||
};
|
||||
|
||||
// Expose only the minimum of the canvas object - there is no dom to do
|
||||
// more here.
|
||||
@ -127,7 +127,7 @@ function CanvasProxy(width, height) {
|
||||
return function() {
|
||||
// console.log("funcCall", name)
|
||||
cmdQueue.push([name, Array.prototype.slice.call(arguments)]);
|
||||
}
|
||||
};
|
||||
}
|
||||
var name;
|
||||
for (var i = 0; i < ctxFunc.length; i++) {
|
||||
@ -139,11 +139,11 @@ function CanvasProxy(width, height) {
|
||||
|
||||
ctx.createPattern = function(object, kind) {
|
||||
return new PatternProxy(cmdQueue, object, kind);
|
||||
}
|
||||
};
|
||||
|
||||
ctx.createLinearGradient = function(x0, y0, x1, y1) {
|
||||
return new GradientProxy(cmdQueue, x0, y0, x1, y1);
|
||||
}
|
||||
};
|
||||
|
||||
ctx.getImageData = function(x, y, w, h) {
|
||||
return {
|
||||
@ -151,11 +151,11 @@ function CanvasProxy(width, height) {
|
||||
height: h,
|
||||
data: Uint8ClampedArray(w * h * 4)
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
ctx.putImageData = function(data, x, y, width, height) {
|
||||
cmdQueue.push(['$putImageData', [data, x, y, width, height]]);
|
||||
}
|
||||
};
|
||||
|
||||
ctx.drawImage = function(image, x, y, width, height,
|
||||
sx, sy, swidth, sheight) {
|
||||
@ -168,7 +168,7 @@ function CanvasProxy(width, height) {
|
||||
} else {
|
||||
throw 'unkown type to drawImage';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Setup property access to `ctx`.
|
||||
var ctxProp = {
|
||||
@ -195,14 +195,14 @@ function CanvasProxy(width, height) {
|
||||
function buildGetter(name) {
|
||||
return function() {
|
||||
return ctx['$' + name];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function buildSetter(name) {
|
||||
return function(value) {
|
||||
cmdQueue.push(['$', name, value]);
|
||||
return ctx['$' + name] = value;
|
||||
}
|
||||
return (ctx['$' + name] = value);
|
||||
};
|
||||
}
|
||||
|
||||
// Setting the value to `stroke|fillStyle` needs special handling, as it
|
||||
@ -215,9 +215,9 @@ function CanvasProxy(width, height) {
|
||||
cmdQueue.push(['$' + name + 'Pattern', [value.id]]);
|
||||
} else {
|
||||
cmdQueue.push(['$', name, value]);
|
||||
return ctx['$' + name] = value;
|
||||
return (ctx['$' + name] = value);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
for (var name in ctxProp) {
|
||||
|
112
worker/client.js
112
worker/client.js
@ -1,25 +1,28 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
'use strict';
|
||||
|
||||
if (typeof console.time == 'undefined') {
|
||||
var consoleUtils = (function() {
|
||||
var consoleTimer = {};
|
||||
console.time = function(name) {
|
||||
|
||||
var obj = {};
|
||||
obj.time = function(name) {
|
||||
consoleTimer[name] = Date.now();
|
||||
};
|
||||
|
||||
console.timeEnd = function(name) {
|
||||
obj.timeEnd = function(name) {
|
||||
var time = consoleTimer[name];
|
||||
if (time == null) {
|
||||
throw 'Unkown timer name ' + name;
|
||||
}
|
||||
this.log('Timer:', name, Date.now() - time);
|
||||
console.log('Timer:', name, Date.now() - time);
|
||||
};
|
||||
}
|
||||
|
||||
return obj;
|
||||
})();
|
||||
|
||||
function FontWorker() {
|
||||
this.worker = new Worker('worker/font.js');
|
||||
this.worker = new Worker('../worker/font.js');
|
||||
this.fontsWaiting = 0;
|
||||
this.fontsWaitingCallbacks = [];
|
||||
|
||||
@ -58,7 +61,7 @@ FontWorker.prototype = {
|
||||
|
||||
'fonts': function(data) {
|
||||
// console.log("got processed fonts from worker", Object.keys(data));
|
||||
for (name in data) {
|
||||
for (var name in data) {
|
||||
// Update the encoding property.
|
||||
var font = Fonts.lookup(name);
|
||||
font.properties = {
|
||||
@ -96,7 +99,7 @@ FontWorker.prototype = {
|
||||
this.fontsWaiting++;
|
||||
}
|
||||
|
||||
console.time('ensureFonts');
|
||||
consoleUtils.time('ensureFonts');
|
||||
// If there are fonts, that need to get loaded, tell the FontWorker to get
|
||||
// started and push the callback on the waiting-callback-stack.
|
||||
if (notLoaded.length != 0) {
|
||||
@ -124,7 +127,7 @@ function WorkerPDFDoc(canvas) {
|
||||
|
||||
this.ctx = canvas.getContext('2d');
|
||||
this.canvas = canvas;
|
||||
this.worker = new Worker('worker/pdf.js');
|
||||
this.worker = new Worker('../worker/pdf.js');
|
||||
this.fontWorker = new FontWorker();
|
||||
this.waitingForFonts = false;
|
||||
this.waitingForFontsCallback = [];
|
||||
@ -167,7 +170,8 @@ function WorkerPDFDoc(canvas) {
|
||||
},
|
||||
|
||||
'$putImageData': function(imageData, x, y) {
|
||||
var imgData = this.getImageData(0, 0, imageData.width, imageData.height);
|
||||
var imgData = this.getImageData(0, 0,
|
||||
imageData.width, imageData.height);
|
||||
|
||||
// Store the .data property to avaid property lookups.
|
||||
var imageRealData = imageData.data;
|
||||
@ -176,7 +180,7 @@ function WorkerPDFDoc(canvas) {
|
||||
// Copy over the imageData.
|
||||
var len = imageRealData.length;
|
||||
while (len--)
|
||||
imgRealData[len] = imageRealData[len];
|
||||
imgRealData[len] = imageRealData[len];
|
||||
|
||||
this.putImageData(imgData, x, y);
|
||||
},
|
||||
@ -273,7 +277,7 @@ function WorkerPDFDoc(canvas) {
|
||||
},
|
||||
|
||||
'pdf_num_pages': function(data) {
|
||||
this.numPages = parseInt(data);
|
||||
this.numPages = parseInt(data, 10);
|
||||
if (this.loadCallback) {
|
||||
this.loadCallback();
|
||||
}
|
||||
@ -302,8 +306,8 @@ function WorkerPDFDoc(canvas) {
|
||||
'setup_page': function(data) {
|
||||
var size = data.split(',');
|
||||
var canvas = this.canvas, ctx = this.ctx;
|
||||
canvas.width = parseInt(size[0]);
|
||||
canvas.height = parseInt(size[1]);
|
||||
canvas.width = parseInt(size[0], 10);
|
||||
canvas.height = parseInt(size[1], 10);
|
||||
},
|
||||
|
||||
'fonts': function(data) {
|
||||
@ -339,7 +343,7 @@ function WorkerPDFDoc(canvas) {
|
||||
|
||||
var renderData = function() {
|
||||
if (id == 0) {
|
||||
console.time('main canvas rendering');
|
||||
consoleUtils.time('main canvas rendering');
|
||||
var ctx = this.ctx;
|
||||
ctx.save();
|
||||
ctx.fillStyle = 'rgb(255, 255, 255)';
|
||||
@ -348,8 +352,8 @@ function WorkerPDFDoc(canvas) {
|
||||
}
|
||||
renderProxyCanvas(canvasList[id], cmdQueue);
|
||||
if (id == 0) {
|
||||
console.timeEnd('main canvas rendering');
|
||||
console.timeEnd('>>> total page display time:');
|
||||
consoleUtils.timeEnd('main canvas rendering');
|
||||
consoleUtils.timeEnd('>>> total page display time:');
|
||||
}
|
||||
}.bind(this);
|
||||
|
||||
@ -368,50 +372,52 @@ function WorkerPDFDoc(canvas) {
|
||||
};
|
||||
|
||||
// Listen to the WebWorker for data and call actionHandler on it.
|
||||
this.worker.onmessage = function(event) {
|
||||
this.worker.addEventListener('message', function(event) {
|
||||
var data = event.data;
|
||||
if (data.action in actionHandler) {
|
||||
actionHandler[data.action].call(this, data.data);
|
||||
} else {
|
||||
throw 'Unkown action from worker: ' + data.action;
|
||||
}
|
||||
}.bind(this);
|
||||
}.bind(this));
|
||||
}
|
||||
|
||||
WorkerPDFDoc.prototype.open = function(url, callback) {
|
||||
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;
|
||||
WorkerPDFDoc.prototype = {
|
||||
open: function(url, callback) {
|
||||
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;
|
||||
|
||||
this.loadCallback = callback;
|
||||
this.worker.postMessage(data);
|
||||
this.showPage(this.numPage);
|
||||
this.loadCallback = callback;
|
||||
this.worker.postMessage(data);
|
||||
this.showPage(this.numPage);
|
||||
}
|
||||
}.bind(this);
|
||||
req.send(null);
|
||||
},
|
||||
|
||||
showPage: function(numPage) {
|
||||
this.numPage = parseInt(numPage, 10);
|
||||
console.log('=== start rendering page ' + numPage + ' ===');
|
||||
consoleUtils.time('>>> total page display time:');
|
||||
this.worker.postMessage(numPage);
|
||||
if (this.onChangePage) {
|
||||
this.onChangePage(numPage);
|
||||
}
|
||||
}.bind(this);
|
||||
req.send(null);
|
||||
};
|
||||
},
|
||||
|
||||
WorkerPDFDoc.prototype.showPage = function(numPage) {
|
||||
this.numPage = parseInt(numPage);
|
||||
console.log('=== start rendering page ' + numPage + ' ===');
|
||||
console.time('>>> total page display time:');
|
||||
this.worker.postMessage(numPage);
|
||||
if (this.onChangePage) {
|
||||
this.onChangePage(numPage);
|
||||
nextPage: function() {
|
||||
if (this.numPage != this.numPages)
|
||||
this.showPage(++this.numPage);
|
||||
},
|
||||
|
||||
prevPage: function() {
|
||||
if (this.numPage != 1)
|
||||
this.showPage(--this.numPage);
|
||||
}
|
||||
};
|
||||
|
||||
WorkerPDFDoc.prototype.nextPage = function() {
|
||||
if (this.numPage == this.numPages) return;
|
||||
this.showPage(++this.numPage);
|
||||
};
|
||||
|
||||
WorkerPDFDoc.prototype.prevPage = function() {
|
||||
if (this.numPage == 1) return;
|
||||
this.showPage(--this.numPage);
|
||||
};
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
'use strict';
|
||||
@ -25,3 +25,4 @@ var console = {
|
||||
this.log('Timer:', name, Date.now() - time);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
'use strict';
|
||||
@ -56,11 +56,12 @@ var actionHandler = {
|
||||
};
|
||||
|
||||
// Listen to the MainThread for data and call actionHandler on it.
|
||||
this.onmessage = function(event) {
|
||||
addEventListener('message', function(event) {
|
||||
var data = event.data;
|
||||
if (data.action in actionHandler) {
|
||||
actionHandler[data.action].call(this, data.data);
|
||||
} else {
|
||||
throw 'Unkown action from worker: ' + data.action;
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- /
|
||||
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
|
||||
|
||||
'use strict';
|
||||
@ -11,8 +11,10 @@ var console = {
|
||||
action: 'log',
|
||||
data: args
|
||||
});
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
var consoleUtils = {
|
||||
time: function(name) {
|
||||
consoleTimer[name] = Date.now();
|
||||
},
|
||||
@ -22,7 +24,7 @@ var console = {
|
||||
if (time == null) {
|
||||
throw 'Unkown timer name ' + name;
|
||||
}
|
||||
this.log('Timer:', name, Date.now() - time);
|
||||
console.log('Timer:', name, Date.now() - time);
|
||||
}
|
||||
};
|
||||
|
||||
@ -42,7 +44,7 @@ var canvas = new CanvasProxy(1224, 1584);
|
||||
|
||||
// Listen for messages from the main thread.
|
||||
var pdfDocument = null;
|
||||
onmessage = function(event) {
|
||||
addEventListener('message', function(event) {
|
||||
var data = event.data;
|
||||
// If there is no pdfDocument yet, then the sent data is the PDFDocument.
|
||||
if (!pdfDocument) {
|
||||
@ -55,10 +57,10 @@ onmessage = function(event) {
|
||||
}
|
||||
// User requested to render a certain page.
|
||||
else {
|
||||
console.time('compile');
|
||||
consoleUtils.time('compile');
|
||||
|
||||
// Let's try to render the first page...
|
||||
var page = pdfDocument.getPage(parseInt(data));
|
||||
var page = pdfDocument.getPage(parseInt(data, 10));
|
||||
|
||||
var pdfToCssUnitsCoef = 96.0 / 72.0;
|
||||
var pageWidth = (page.mediaBox[2] - page.mediaBox[0]) * pdfToCssUnitsCoef;
|
||||
@ -77,19 +79,19 @@ onmessage = function(event) {
|
||||
var fonts = [];
|
||||
var gfx = new CanvasGraphics(canvas.getContext('2d'), CanvasProxy);
|
||||
page.compile(gfx, fonts);
|
||||
console.timeEnd('compile');
|
||||
consoleUtils.timeEnd('compile');
|
||||
|
||||
// Send fonts to the main thread.
|
||||
console.time('fonts');
|
||||
consoleUtils.time('fonts');
|
||||
postMessage({
|
||||
action: 'fonts',
|
||||
data: fonts
|
||||
});
|
||||
console.timeEnd('fonts');
|
||||
consoleUtils.timeEnd('fonts');
|
||||
|
||||
console.time('display');
|
||||
consoleUtils.time('display');
|
||||
page.display(gfx);
|
||||
canvas.flush();
|
||||
console.timeEnd('display');
|
||||
consoleUtils.timeEnd('display');
|
||||
}
|
||||
};
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user