1
0
Fork 0
mirror of https://github.com/mozilla/pdf.js.git synced 2025-04-22 16:18:08 +02:00

Merge upstream.

This commit is contained in:
Brendan Dahl 2012-03-21 16:13:24 -07:00
commit 2d7e1d6346
33 changed files with 1046 additions and 769 deletions

View file

@ -1,433 +1,436 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
'use strict';
var bidi = PDFJS.bidi = (function bidiClosure() {
// Character types for symbols from 0000 to 00FF.
var baseTypes = [
'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'S', 'B', 'S', 'WS',
'B', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN',
'BN', 'BN', 'B', 'B', 'B', 'S', 'WS', 'ON', 'ON', 'ET', 'ET', 'ET', 'ON',
'ON', 'ON', 'ON', 'ON', 'ON', 'CS', 'ON', 'CS', 'ON', 'EN', 'EN', 'EN',
'EN', 'EN', 'EN', 'EN', 'EN', 'EN', 'EN', 'ON', 'ON', 'ON', 'ON', 'ON',
'ON', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'ON', 'ON',
'ON', 'ON', 'ON', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
'L', 'ON', 'ON', 'ON', 'ON', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'B', 'BN',
'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN',
'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN',
'BN', 'CS', 'ON', 'ET', 'ET', 'ET', 'ET', 'ON', 'ON', 'ON', 'ON', 'L', 'ON',
'ON', 'ON', 'ON', 'ON', 'ET', 'ET', 'EN', 'EN', 'ON', 'L', 'ON', 'ON', 'ON',
'EN', 'L', 'ON', 'ON', 'ON', 'ON', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
'L', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
'L', 'L', 'L', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L'
];
// Character types for symbols from 0600 to 06FF
var arabicTypes = [
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'CS', 'AL', 'ON', 'ON', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM',
'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AN', 'AN', 'AN', 'AN', 'AN', 'AN', 'AN', 'AN', 'AN',
'AN', 'ET', 'AN', 'AN', 'AL', 'AL', 'AL', 'NSM', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM',
'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'ON', 'NSM',
'NSM', 'NSM', 'NSM', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL'
];
function isOdd(i) {
return (i & 1) != 0;
}
function isEven(i) {
return (i & 1) == 0;
}
function findUnequal(arr, start, value) {
var j;
for (var j = start, jj = arr.length; j < jj; ++j) {
if (arr[j] != value)
return j;
}
return j;
}
function setValues(arr, start, end, value) {
for (var j = start; j < end; ++j) {
arr[j] = value;
}
}
function reverseValues(arr, start, end) {
for (var i = start, j = end - 1; i < j; ++i, --j) {
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
function mirrorGlyphs(c) {
/*
# BidiMirroring-1.txt
0028; 0029 # LEFT PARENTHESIS
0029; 0028 # RIGHT PARENTHESIS
003C; 003E # LESS-THAN SIGN
003E; 003C # GREATER-THAN SIGN
005B; 005D # LEFT SQUARE BRACKET
005D; 005B # RIGHT SQUARE BRACKET
007B; 007D # LEFT CURLY BRACKET
007D; 007B # RIGHT CURLY BRACKET
00AB; 00BB # LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
00BB; 00AB # RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
*/
switch (c) {
case '(':
return ')';
case ')':
return '(';
case '<':
return '>';
case '>':
return '<';
case ']':
return '[';
case '[':
return ']';
case '}':
return '{';
case '{':
return '}';
case '\u00AB':
return '\u00BB';
case '\u00BB':
return '\u00AB';
default:
return c;
}
}
return (function bidi(text, startLevel) {
var str = text.str;
var strLength = str.length;
if (strLength == 0)
return str;
// get types, fill arrays
var chars = new Array(strLength);
var types = new Array(strLength);
var oldtypes = new Array(strLength);
var numBidi = 0;
for (var i = 0; i < strLength; ++i) {
chars[i] = str.charAt(i);
var charCode = str.charCodeAt(i);
var charType = 'L';
if (charCode <= 0x00ff)
charType = baseTypes[charCode];
else if (0x0590 <= charCode && charCode <= 0x05f4)
charType = 'R';
else if (0x0600 <= charCode && charCode <= 0x06ff)
charType = arabicTypes[charCode & 0xff];
else if (0x0700 <= charCode && charCode <= 0x08AC)
charType = 'AL';
if (charType == 'R' || charType == 'AL' || charType == 'AN')
numBidi++;
oldtypes[i] = types[i] = charType;
}
// detect the bidi method
// if there are no rtl characters then no bidi needed
// if less than 30% chars are rtl then string is primarily ltr
// if more than 30% chars are rtl then string is primarily rtl
if (numBidi == 0) {
text.direction = 'ltr';
return str;
}
if (startLevel == -1) {
if ((strLength / numBidi) < 0.3) {
text.direction = 'ltr';
startLevel = 0;
} else {
text.direction = 'rtl';
startLevel = 1;
}
}
var levels = new Array(strLength);
for (var i = 0; i < strLength; ++i) {
levels[i] = startLevel;
}
var diffChars = new Array(strLength);
var diffLevels = new Array(strLength);
var diffTypes = new Array(strLength);
/*
X1-X10: skip most of this, since we are NOT doing the embeddings.
*/
var e = isOdd(startLevel) ? 'R' : 'L';
var sor = e;
var eor = sor;
/*
W1. Examine each non-spacing mark (NSM) in the level run, and change the
type of the NSM to the type of the previous character. If the NSM is at the
start of the level run, it will get the type of sor.
*/
var lastType = sor;
for (var i = 0; i < strLength; ++i) {
if (types[i] == 'NSM')
types[i] = lastType;
else
lastType = types[i];
}
/*
W2. Search backwards from each instance of a European number until the
first strong type (R, L, AL, or sor) is found. If an AL is found, change
the type of the European number to Arabic number.
*/
var lastType = sor;
for (var i = 0; i < strLength; ++i) {
var t = types[i];
if (t == 'EN')
types[i] = (lastType == 'AL') ? 'AN' : 'EN';
else if (t == 'R' || t == 'L' || t == 'AL')
lastType = t;
}
/*
W3. Change all ALs to R.
*/
for (var i = 0; i < strLength; ++i) {
var t = types[i];
if (t == 'AL')
types[i] = 'R';
}
/*
W4. A single European separator between two European numbers changes to a
European number. A single common separator between two numbers of the same
type changes to that type:
*/
for (var i = 1; i < strLength - 1; ++i) {
if (types[i] == 'ES' && types[i - 1] == 'EN' && types[i + 1] == 'EN')
types[i] = 'EN';
if (types[i] == 'CS' && (types[i - 1] == 'EN' || types[i - 1] == 'AN') &&
types[i + 1] == types[i - 1])
types[i] = types[i - 1];
}
/*
W5. A sequence of European terminators adjacent to European numbers changes
to all European numbers:
*/
for (var i = 0; i < strLength; ++i) {
if (types[i] == 'EN') {
// do before
for (var j = i - 1; j >= 0; --j) {
if (types[j] != 'ET')
break;
types[j] = 'EN';
}
// do after
for (var j = i + 1; j < strLength; --j) {
if (types[j] != 'ET')
break;
types[j] = 'EN';
}
}
}
/*
W6. Otherwise, separators and terminators change to Other Neutral:
*/
for (var i = 0; i < strLength; ++i) {
var t = types[i];
if (t == 'WS' || t == 'ES' || t == 'ET' || t == 'CS')
types[i] = 'ON';
}
/*
W7. Search backwards from each instance of a European number until the
first strong type (R, L, or sor) is found. If an L is found, then change
the type of the European number to L.
*/
var lastType = sor;
for (var i = 0; i < strLength; ++i) {
var t = types[i];
if (t == 'EN')
types[i] = (lastType == 'L') ? 'L' : 'EN';
else if (t == 'R' || t == 'L')
lastType = t;
}
/*
N1. A sequence of neutrals takes the direction of the surrounding strong
text if the text on both sides has the same direction. European and Arabic
numbers are treated as though they were R. Start-of-level-run (sor) and
end-of-level-run (eor) are used at level run boundaries.
*/
for (var i = 0; i < strLength; ++i) {
if (types[i] == 'ON') {
var end = findUnequal(types, i + 1, 'ON');
var before = sor;
if (i > 0)
before = types[i - 1];
var after = eor;
if (end + 1 < strLength)
after = types[end + 1];
if (before != 'L')
before = 'R';
if (after != 'L')
after = 'R';
if (before == after)
setValues(types, i, end, before);
i = end - 1; // reset to end (-1 so next iteration is ok)
}
}
/*
N2. Any remaining neutrals take the embedding direction.
*/
for (var i = 0; i < strLength; ++i) {
if (types[i] == 'ON')
types[i] = e;
}
/*
I1. For all characters with an even (left-to-right) embedding direction,
those of type R go up one level and those of type AN or EN go up two
levels.
I2. For all characters with an odd (right-to-left) embedding direction,
those of type L, EN or AN go up one level.
*/
for (var i = 0; i < strLength; ++i) {
var t = types[i];
if (isEven(levels[i])) {
if (t == 'R') {
levels[i] += 1;
} else if (t == 'AN' || t == 'EN') {
levels[i] += 2;
}
} else { // isOdd, so
if (t == 'L' || t == 'AN' || t == 'EN') {
levels[i] += 1;
}
}
}
/*
L1. On each line, reset the embedding level of the following characters to
the paragraph embedding level:
segment separators,
paragraph separators,
any sequence of whitespace characters preceding a segment separator or
paragraph separator, and any sequence of white space characters at the end
of the line.
*/
// don't bother as text is only single line
/*
L2. From the highest level found in the text to the lowest odd level on
each line, reverse any contiguous sequence of characters that are at that
level or higher.
*/
// find highest level & lowest odd level
var highestLevel = -1;
var lowestOddLevel = 99;
for (var i = 0, ii = levels.length; i < ii; ++i) {
var level = levels[i];
if (highestLevel < level)
highestLevel = level;
if (lowestOddLevel > level && isOdd(level))
lowestOddLevel = level;
}
// now reverse between those limits
for (var level = highestLevel; level >= lowestOddLevel; --level) {
// find segments to reverse
var start = -1;
for (var i = 0, ii = levels.length; i < ii; ++i) {
if (levels[i] < level) {
if (start >= 0) {
reverseValues(chars, start, i);
start = -1;
}
} else if (start < 0) {
start = i;
}
}
if (start >= 0) {
reverseValues(chars, start, levels.length);
}
}
/*
L3. Combining marks applied to a right-to-left base character will at this
point precede their base character. If the rendering engine expects them to
follow the base characters in the final display process, then the ordering
of the marks and the base character must be reversed.
*/
// don't bother for now
/*
L4. A character that possesses the mirrored property as specified by
Section 4.7, Mirrored, must be depicted by a mirrored glyph if the resolved
directionality of that character is R.
*/
// don't mirror as characters are already mirrored in the pdf
// Finally, return string
var result = '';
for (var i = 0, ii = chars.length; i < ii; ++i) {
var ch = chars[i];
if (ch != '<' && ch != '>')
result += ch;
}
return result;
});
})();
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
'use strict';
var bidi = PDFJS.bidi = (function bidiClosure() {
// Character types for symbols from 0000 to 00FF.
var baseTypes = [
'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'S', 'B', 'S', 'WS',
'B', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN',
'BN', 'BN', 'B', 'B', 'B', 'S', 'WS', 'ON', 'ON', 'ET', 'ET', 'ET', 'ON',
'ON', 'ON', 'ON', 'ON', 'ON', 'CS', 'ON', 'CS', 'ON', 'EN', 'EN', 'EN',
'EN', 'EN', 'EN', 'EN', 'EN', 'EN', 'EN', 'ON', 'ON', 'ON', 'ON', 'ON',
'ON', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'ON', 'ON',
'ON', 'ON', 'ON', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
'L', 'ON', 'ON', 'ON', 'ON', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'B', 'BN',
'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN',
'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN', 'BN',
'BN', 'CS', 'ON', 'ET', 'ET', 'ET', 'ET', 'ON', 'ON', 'ON', 'ON', 'L', 'ON',
'ON', 'ON', 'ON', 'ON', 'ET', 'ET', 'EN', 'EN', 'ON', 'L', 'ON', 'ON', 'ON',
'EN', 'L', 'ON', 'ON', 'ON', 'ON', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
'L', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L',
'L', 'L', 'L', 'ON', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L'
];
// Character types for symbols from 0600 to 06FF
var arabicTypes = [
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'CS', 'AL', 'ON', 'ON', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM',
'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AN', 'AN', 'AN', 'AN', 'AN', 'AN', 'AN', 'AN', 'AN',
'AN', 'ET', 'AN', 'AN', 'AL', 'AL', 'AL', 'NSM', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM',
'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'NSM', 'ON', 'NSM',
'NSM', 'NSM', 'NSM', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL',
'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL', 'AL'
];
function isOdd(i) {
return (i & 1) != 0;
}
function isEven(i) {
return (i & 1) == 0;
}
function findUnequal(arr, start, value) {
var j;
for (var j = start, jj = arr.length; j < jj; ++j) {
if (arr[j] != value)
return j;
}
return j;
}
function setValues(arr, start, end, value) {
for (var j = start; j < end; ++j) {
arr[j] = value;
}
}
function reverseValues(arr, start, end) {
for (var i = start, j = end - 1; i < j; ++i, --j) {
var temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
function mirrorGlyphs(c) {
/*
# BidiMirroring-1.txt
0028; 0029 # LEFT PARENTHESIS
0029; 0028 # RIGHT PARENTHESIS
003C; 003E # LESS-THAN SIGN
003E; 003C # GREATER-THAN SIGN
005B; 005D # LEFT SQUARE BRACKET
005D; 005B # RIGHT SQUARE BRACKET
007B; 007D # LEFT CURLY BRACKET
007D; 007B # RIGHT CURLY BRACKET
00AB; 00BB # LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
00BB; 00AB # RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
*/
switch (c) {
case '(':
return ')';
case ')':
return '(';
case '<':
return '>';
case '>':
return '<';
case ']':
return '[';
case '[':
return ']';
case '}':
return '{';
case '{':
return '}';
case '\u00AB':
return '\u00BB';
case '\u00BB':
return '\u00AB';
default:
return c;
}
}
function bidi(text, startLevel) {
var str = text.str;
var strLength = str.length;
if (strLength == 0)
return str;
// get types, fill arrays
var chars = new Array(strLength);
var types = new Array(strLength);
var oldtypes = new Array(strLength);
var numBidi = 0;
for (var i = 0; i < strLength; ++i) {
chars[i] = str.charAt(i);
var charCode = str.charCodeAt(i);
var charType = 'L';
if (charCode <= 0x00ff)
charType = baseTypes[charCode];
else if (0x0590 <= charCode && charCode <= 0x05f4)
charType = 'R';
else if (0x0600 <= charCode && charCode <= 0x06ff)
charType = arabicTypes[charCode & 0xff];
else if (0x0700 <= charCode && charCode <= 0x08AC)
charType = 'AL';
if (charType == 'R' || charType == 'AL' || charType == 'AN')
numBidi++;
oldtypes[i] = types[i] = charType;
}
// detect the bidi method
// if there are no rtl characters then no bidi needed
// if less than 30% chars are rtl then string is primarily ltr
// if more than 30% chars are rtl then string is primarily rtl
if (numBidi == 0) {
text.direction = 'ltr';
return str;
}
if (startLevel == -1) {
if ((strLength / numBidi) < 0.3) {
text.direction = 'ltr';
startLevel = 0;
} else {
text.direction = 'rtl';
startLevel = 1;
}
}
var levels = new Array(strLength);
for (var i = 0; i < strLength; ++i) {
levels[i] = startLevel;
}
var diffChars = new Array(strLength);
var diffLevels = new Array(strLength);
var diffTypes = new Array(strLength);
/*
X1-X10: skip most of this, since we are NOT doing the embeddings.
*/
var e = isOdd(startLevel) ? 'R' : 'L';
var sor = e;
var eor = sor;
/*
W1. Examine each non-spacing mark (NSM) in the level run, and change the
type of the NSM to the type of the previous character. If the NSM is at the
start of the level run, it will get the type of sor.
*/
var lastType = sor;
for (var i = 0; i < strLength; ++i) {
if (types[i] == 'NSM')
types[i] = lastType;
else
lastType = types[i];
}
/*
W2. Search backwards from each instance of a European number until the
first strong type (R, L, AL, or sor) is found. If an AL is found, change
the type of the European number to Arabic number.
*/
var lastType = sor;
for (var i = 0; i < strLength; ++i) {
var t = types[i];
if (t == 'EN')
types[i] = (lastType == 'AL') ? 'AN' : 'EN';
else if (t == 'R' || t == 'L' || t == 'AL')
lastType = t;
}
/*
W3. Change all ALs to R.
*/
for (var i = 0; i < strLength; ++i) {
var t = types[i];
if (t == 'AL')
types[i] = 'R';
}
/*
W4. A single European separator between two European numbers changes to a
European number. A single common separator between two numbers of the same
type changes to that type:
*/
for (var i = 1; i < strLength - 1; ++i) {
if (types[i] == 'ES' && types[i - 1] == 'EN' && types[i + 1] == 'EN')
types[i] = 'EN';
if (types[i] == 'CS' && (types[i - 1] == 'EN' || types[i - 1] == 'AN') &&
types[i + 1] == types[i - 1])
types[i] = types[i - 1];
}
/*
W5. A sequence of European terminators adjacent to European numbers changes
to all European numbers:
*/
for (var i = 0; i < strLength; ++i) {
if (types[i] == 'EN') {
// do before
for (var j = i - 1; j >= 0; --j) {
if (types[j] != 'ET')
break;
types[j] = 'EN';
}
// do after
for (var j = i + 1; j < strLength; --j) {
if (types[j] != 'ET')
break;
types[j] = 'EN';
}
}
}
/*
W6. Otherwise, separators and terminators change to Other Neutral:
*/
for (var i = 0; i < strLength; ++i) {
var t = types[i];
if (t == 'WS' || t == 'ES' || t == 'ET' || t == 'CS')
types[i] = 'ON';
}
/*
W7. Search backwards from each instance of a European number until the
first strong type (R, L, or sor) is found. If an L is found, then change
the type of the European number to L.
*/
var lastType = sor;
for (var i = 0; i < strLength; ++i) {
var t = types[i];
if (t == 'EN')
types[i] = (lastType == 'L') ? 'L' : 'EN';
else if (t == 'R' || t == 'L')
lastType = t;
}
/*
N1. A sequence of neutrals takes the direction of the surrounding strong
text if the text on both sides has the same direction. European and Arabic
numbers are treated as though they were R. Start-of-level-run (sor) and
end-of-level-run (eor) are used at level run boundaries.
*/
for (var i = 0; i < strLength; ++i) {
if (types[i] == 'ON') {
var end = findUnequal(types, i + 1, 'ON');
var before = sor;
if (i > 0)
before = types[i - 1];
var after = eor;
if (end + 1 < strLength)
after = types[end + 1];
if (before != 'L')
before = 'R';
if (after != 'L')
after = 'R';
if (before == after)
setValues(types, i, end, before);
i = end - 1; // reset to end (-1 so next iteration is ok)
}
}
/*
N2. Any remaining neutrals take the embedding direction.
*/
for (var i = 0; i < strLength; ++i) {
if (types[i] == 'ON')
types[i] = e;
}
/*
I1. For all characters with an even (left-to-right) embedding direction,
those of type R go up one level and those of type AN or EN go up two
levels.
I2. For all characters with an odd (right-to-left) embedding direction,
those of type L, EN or AN go up one level.
*/
for (var i = 0; i < strLength; ++i) {
var t = types[i];
if (isEven(levels[i])) {
if (t == 'R') {
levels[i] += 1;
} else if (t == 'AN' || t == 'EN') {
levels[i] += 2;
}
} else { // isOdd, so
if (t == 'L' || t == 'AN' || t == 'EN') {
levels[i] += 1;
}
}
}
/*
L1. On each line, reset the embedding level of the following characters to
the paragraph embedding level:
segment separators,
paragraph separators,
any sequence of whitespace characters preceding a segment separator or
paragraph separator, and any sequence of white space characters at the end
of the line.
*/
// don't bother as text is only single line
/*
L2. From the highest level found in the text to the lowest odd level on
each line, reverse any contiguous sequence of characters that are at that
level or higher.
*/
// find highest level & lowest odd level
var highestLevel = -1;
var lowestOddLevel = 99;
for (var i = 0, ii = levels.length; i < ii; ++i) {
var level = levels[i];
if (highestLevel < level)
highestLevel = level;
if (lowestOddLevel > level && isOdd(level))
lowestOddLevel = level;
}
// now reverse between those limits
for (var level = highestLevel; level >= lowestOddLevel; --level) {
// find segments to reverse
var start = -1;
for (var i = 0, ii = levels.length; i < ii; ++i) {
if (levels[i] < level) {
if (start >= 0) {
reverseValues(chars, start, i);
start = -1;
}
} else if (start < 0) {
start = i;
}
}
if (start >= 0) {
reverseValues(chars, start, levels.length);
}
}
/*
L3. Combining marks applied to a right-to-left base character will at this
point precede their base character. If the rendering engine expects them to
follow the base characters in the final display process, then the ordering
of the marks and the base character must be reversed.
*/
// don't bother for now
/*
L4. A character that possesses the mirrored property as specified by
Section 4.7, Mirrored, must be depicted by a mirrored glyph if the resolved
directionality of that character is R.
*/
// don't mirror as characters are already mirrored in the pdf
// Finally, return string
var result = '';
for (var i = 0, ii = chars.length; i < ii; ++i) {
var ch = chars[i];
if (ch != '<' && ch != '>')
result += ch;
}
return result;
}
return bidi;
})();

View file

@ -70,7 +70,7 @@ var CanvasExtraState = (function CanvasExtraStateClosure() {
return CanvasExtraState;
})();
function ScratchCanvas(width, height) {
function createScratchCanvas(width, height) {
var canvas = document.createElement('canvas');
canvas.width = width;
canvas.height = height;
@ -188,9 +188,9 @@ function addContextCurrentTransform(ctx) {
}
var CanvasGraphics = (function CanvasGraphicsClosure() {
// Defines the time the executeIRQueue is going to be executing
// Defines the time the executeOperatorList is going to be executing
// before it stops and shedules a continue of execution.
var kExecutionTime = 50;
var kExecutionTime = 15;
function CanvasGraphics(canvasCtx, objs, textLayer) {
this.ctx = canvasCtx;
@ -199,7 +199,6 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
this.pendingClip = null;
this.res = null;
this.xobjs = null;
this.ScratchCanvas = ScratchCanvas;
this.objs = objs;
this.textLayer = textLayer;
if (canvasCtx) {
@ -229,7 +228,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
'setStrokeColor': true,
'setStrokeColorN': true,
'setFillColor': true,
'setFillColorN_IR': true,
'setFillColorN': true,
'setStrokeGray': true,
'setFillGray': true,
'setStrokeRGBColor': true,
@ -268,15 +267,16 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
this.textLayer.beginLayout();
},
executeIRQueue: function canvasGraphicsExecuteIRQueue(codeIR,
executionStartIdx, continueCallback,
stepper) {
var argsArray = codeIR.argsArray;
var fnArray = codeIR.fnArray;
executeOperatorList: function canvasGraphicsExecuteOperatorList(
operatorList,
executionStartIdx, continueCallback,
stepper) {
var argsArray = operatorList.argsArray;
var fnArray = operatorList.fnArray;
var i = executionStartIdx || 0;
var argsArrayLen = argsArray.length;
// Sometimes the IRQueue to execute is empty.
// Sometimes the OperatorList to execute is empty.
if (argsArrayLen == i) {
return i;
}
@ -314,7 +314,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
i++;
// If the entire IRQueue was executed, stop as were done.
// If the entire operatorList was executed, stop as were done.
if (i == argsArrayLen) {
return i;
}
@ -327,8 +327,8 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
return i;
}
// If the IRQueue isn't executed completly yet OR the execution time
// was short enough, do another execution round.
// If the operatorList isn't executed completely yet OR the execution
// time was short enough, do another execution round.
}
},
@ -556,7 +556,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
this.current.leading = -leading;
},
setFont: function canvasGraphicsSetFont(fontRefName, size) {
var fontObj = this.objs.get(fontRefName).fontObj;
var fontObj = this.objs.get(fontRefName);
var current = this.current;
if (!fontObj)
@ -707,7 +707,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
this.save();
ctx.scale(fontSize, fontSize);
ctx.transform.apply(ctx, fontMatrix);
this.executeIRQueue(glyph.codeIRQueue);
this.executeOperatorList(glyph.operatorList);
this.restore();
var transformed = Util.applyTransform([glyph.width, 0], fontMatrix);
@ -908,7 +908,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
this.ctx.strokeStyle = color;
this.current.strokeColor = color;
},
getColorN_IR_Pattern: function canvasGraphicsGetColorN_IR_Pattern(IR, cs) {
getColorN_Pattern: function canvasGraphicsGetColorN_Pattern(IR, cs) {
if (IR[0] == 'TilingPattern') {
var args = IR[1];
var base = cs.base;
@ -930,11 +930,11 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
}
return pattern;
},
setStrokeColorN_IR: function canvasGraphicsSetStrokeColorN(/*...*/) {
setStrokeColorN: function canvasGraphicsSetStrokeColorN(/*...*/) {
var cs = this.current.strokeColorSpace;
if (cs.name == 'Pattern') {
this.current.strokeColor = this.getColorN_IR_Pattern(arguments, cs);
this.current.strokeColor = this.getColorN_Pattern(arguments, cs);
} else {
this.setStrokeColor.apply(this, arguments);
}
@ -946,11 +946,11 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
this.ctx.fillStyle = color;
this.current.fillColor = color;
},
setFillColorN_IR: function canvasGraphicsSetFillColorN(/*...*/) {
setFillColorN: function canvasGraphicsSetFillColorN(/*...*/) {
var cs = this.current.fillColorSpace;
if (cs.name == 'Pattern') {
this.current.fillColor = this.getColorN_IR_Pattern(arguments, cs);
this.current.fillColor = this.getColorN_Pattern(arguments, cs);
} else {
this.setFillColor.apply(this, arguments);
}
@ -1116,7 +1116,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
// scale the image to the unit square
ctx.scale(1 / w, -1 / h);
var tmpCanvas = new this.ScratchCanvas(w, h);
var tmpCanvas = createScratchCanvas(w, h);
var tmpCtx = tmpCanvas.getContext('2d');
var fillColor = this.current.fillColor;
@ -1147,7 +1147,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
// scale the image to the unit square
ctx.scale(1 / w, -1 / h);
var tmpCanvas = new this.ScratchCanvas(w, h);
var tmpCanvas = createScratchCanvas(w, h);
var tmpCtx = tmpCanvas.getContext('2d');
this.putBinaryImageData(tmpCtx, imgData, w, h);

View file

@ -135,6 +135,7 @@ var ColorSpace = (function ColorSpaceClosure() {
basePatternCS = ColorSpace.parseToIR(basePatternCS, xref, res);
return ['PatternCS', basePatternCS];
case 'Indexed':
case 'I':
var baseIndexedCS = ColorSpace.parseToIR(cs[1], xref, res);
var hiVal = cs[2] + 1;
var lookup = xref.fetchIfRef(cs[3]);

View file

@ -63,13 +63,8 @@ var Page = (function PageClosure() {
function Page(xref, pageNumber, pageDict, ref) {
this.pageNumber = pageNumber;
this.pageDict = pageDict;
this.stats = {
create: Date.now(),
compile: 0.0,
fonts: 0.0,
images: 0.0,
render: 0.0
};
this.stats = new StatTimer();
this.stats.enabled = !!globalScope.PDFJS.enableStats;
this.xref = xref;
this.ref = ref;
@ -175,10 +170,10 @@ var Page = (function PageClosure() {
return shadow(this, 'rotate', rotate);
},
startRenderingFromIRQueue: function pageStartRenderingFromIRQueue(
IRQueue, fonts) {
startRenderingFromOperatorList: function pageStartRenderingFromOperatorList(
operatorList, fonts) {
var self = this;
this.IRQueue = IRQueue;
this.operatorList = operatorList;
var displayContinuation = function pageDisplayContinuation() {
// Always defer call to display() to work around bug in
@ -189,17 +184,20 @@ var Page = (function PageClosure() {
};
this.ensureFonts(fonts,
function pageStartRenderingFromIRQueueEnsureFonts() {
displayContinuation();
});
function pageStartRenderingFromOperatorListEnsureFonts() {
displayContinuation();
}
);
},
getIRQueue: function pageGetIRQueue(handler, dependency) {
if (this.IRQueue) {
getOperatorList: function pageGetOperatorList(handler, dependency) {
if (this.operatorList) {
// content was compiled
return this.IRQueue;
return this.operatorList;
}
this.stats.time('Build IR Queue');
var xref = this.xref;
var content = xref.fetchIfRef(this.content);
var resources = xref.fetchIfRef(this.resources);
@ -216,30 +214,33 @@ var Page = (function PageClosure() {
var pe = this.pe = new PartialEvaluator(
xref, handler, 'p' + this.pageNumber + '_');
var IRQueue = {};
return (this.IRQueue = pe.getIRQueue(content, resources, IRQueue,
dependency));
this.operatorList = pe.getOperatorList(content, resources, dependency);
this.stats.timeEnd('Build IR Queue');
return this.operatorList;
},
ensureFonts: function pageEnsureFonts(fonts, callback) {
this.stats.time('Font Loading');
// Convert the font names to the corresponding font obj.
for (var i = 0, ii = fonts.length; i < ii; i++) {
fonts[i] = this.objs.objs[fonts[i]].data;
}
// Load all the fonts
var fontObjs = FontLoader.bind(
FontLoader.bind(
fonts,
function pageEnsureFontsFontObjs(fontObjs) {
this.stats.fonts = Date.now();
this.stats.timeEnd('Font Loading');
callback.call(this);
}.bind(this),
this.objs
}.bind(this)
);
},
display: function pageDisplay(gfx, callback) {
var stats = this.stats;
stats.time('Rendering');
var xref = this.xref;
var resources = xref.fetchIfRef(this.resources);
var mediaBox = xref.fetchIfRef(this.mediaBox);
@ -253,21 +254,23 @@ var Page = (function PageClosure() {
rotate: this.rotate });
var startIdx = 0;
var length = this.IRQueue.fnArray.length;
var IRQueue = this.IRQueue;
var length = this.operatorList.fnArray.length;
var operatorList = this.operatorList;
var stepper = null;
if (PDFJS.pdfBug && StepperManager.enabled) {
stepper = StepperManager.create(this.pageNumber);
stepper.init(IRQueue);
stepper.init(operatorList);
stepper.nextBreakPoint = stepper.getNextBreakPoint();
}
var self = this;
function next() {
startIdx = gfx.executeIRQueue(IRQueue, startIdx, next, stepper);
startIdx =
gfx.executeOperatorList(operatorList, startIdx, next, stepper);
if (startIdx == length) {
self.stats.render = Date.now();
gfx.endDrawing();
stats.timeEnd('Rendering');
stats.timeEnd('Overall');
if (callback) callback();
}
}
@ -310,6 +313,22 @@ var Page = (function PageClosure() {
return null;
return item.get(name);
}
function isValidUrl(url) {
if (!url)
return false;
var colon = url.indexOf(':');
if (colon < 0)
return false;
var protocol = url.substr(0, colon);
switch (protocol) {
case 'http':
case 'https':
case 'ftp':
return true;
default:
return false;
}
}
var annotations = xref.fetchIfRef(this.annotations) || [];
var i, n = annotations.length;
@ -338,7 +357,12 @@ var Page = (function PageClosure() {
if (a) {
switch (a.get('S').name) {
case 'URI':
item.url = a.get('URI');
var url = a.get('URI');
// TODO: pdf spec mentions urls can be relative to a Base
// entry in the dictionary.
if (!isValidUrl(url))
url = '';
item.url = url;
break;
case 'GoTo':
item.dest = a.get('D');
@ -410,16 +434,16 @@ var Page = (function PageClosure() {
return items;
},
startRendering: function pageStartRendering(ctx, callback, textLayer) {
this.startRenderingTime = Date.now();
// If there is no displayReadyPromise yet, then the IRQueue was never
var stats = this.stats;
stats.time('Overall');
// If there is no displayReadyPromise yet, then the operatorList was never
// requested before. Make the request and create the promise.
if (!this.displayReadyPromise) {
this.pdf.startRendering(this);
this.displayReadyPromise = new Promise();
}
// Once the IRQueue and fonts are loaded, perform the actual rendering.
// Once the operatorList and fonts are loaded, do the actual rendering.
this.displayReadyPromise.then(
function pageDisplayReadyPromise() {
var gfx = new CanvasGraphics(ctx, this.objs, textLayer);
@ -451,9 +475,6 @@ var Page = (function PageClosure() {
* Right now there exists one PDFDocModel on the main thread + one object
* for each worker. If there is no worker support enabled, there are two
* `PDFDocModel` objects on the main thread created.
* TODO: Refactor the internal object structure, such that there is no
* need for the `PDFDocModel` anymore and there is only one object on the
* main thread and not one entire copy on each worker instance.
*/
var PDFDocModel = (function PDFDocModelClosure() {
function PDFDocModel(arg, callback) {
@ -622,9 +643,9 @@ var PDFDoc = (function PDFDocClosure() {
this.data = data;
this.stream = stream;
this.pdf = new PDFDocModel(stream);
this.fingerprint = this.pdf.getFingerprint();
this.catalog = this.pdf.catalog;
this.pdfModel = new PDFDocModel(stream);
this.fingerprint = this.pdfModel.getFingerprint();
this.catalog = this.pdfModel.catalog;
this.objs = new PDFObjects();
this.pageCache = [];
@ -711,7 +732,8 @@ var PDFDoc = (function PDFDocClosure() {
var page = this.pageCache[pageNum];
var depFonts = data.depFonts;
page.startRenderingFromIRQueue(data.IRQueue, depFonts);
page.stats.timeEnd('Page Request');
page.startRenderingFromOperatorList(data.operatorList, depFonts);
}, this);
messageHandler.on('obj', function pdfDocObj(data) {
@ -738,31 +760,16 @@ var PDFDoc = (function PDFDocClosure() {
file = new Stream(file, 0, file.length, fontFileDict);
}
// For now, resolve the font object here direclty. The real font
// object is then created in FontLoader.bind().
this.objs.resolve(id, {
name: name,
file: file,
properties: properties
});
// At this point, only the font object is created but the font is
// not yet attached to the DOM. This is done in `FontLoader.bind`.
var font = new Font(name, file, properties);
this.objs.resolve(id, font);
break;
default:
error('Got unkown object type ' + type);
}
}, this);
messageHandler.on('font_ready', function pdfDocFontReady(data) {
var id = data[0];
var font = new FontShape(data[1]);
// If there is no string, then there is nothing to attach to the DOM.
if (!font.str) {
this.objs.resolve(id, font);
} else {
this.objs.setData(id, font);
}
}.bind(this));
messageHandler.on('page_error', function pdfDocError(data) {
var page = this.pageCache[data.pageNum];
if (page.displayReadyPromise)
@ -784,7 +791,7 @@ var PDFDoc = (function PDFDocClosure() {
var size = width * height;
var rgbaLength = size * 4;
var buf = new Uint8Array(size * components);
var tmpCanvas = new ScratchCanvas(width, height);
var tmpCanvas = createScratchCanvas(width, height);
var tmpCtx = tmpCanvas.getContext('2d');
tmpCtx.drawImage(img, 0, 0);
var data = tmpCtx.getImageData(0, 0, width, height).data;
@ -813,12 +820,13 @@ var PDFDoc = (function PDFDocClosure() {
},
get numPages() {
return this.pdf.numPages;
return this.pdfModel.numPages;
},
startRendering: function pdfDocStartRendering(page) {
// The worker might not be ready to receive the page request yet.
this.workerReadyPromise.then(function pdfDocStartRenderingThen() {
page.stats.time('Page Request');
this.messageHandler.send('page_request', page.pageNumber + 1);
}.bind(this));
},
@ -827,7 +835,7 @@ var PDFDoc = (function PDFDocClosure() {
if (this.pageCache[n])
return this.pageCache[n];
var page = this.pdf.getPage(n);
var page = this.pdfModel.getPage(n);
// Add a reference to the objects such that Page can forward the reference
// to the CanvasGraphics and so on.
page.objs = this.objs;

View file

@ -570,7 +570,6 @@ var CipherTransformFactory = (function CipherTransformFactoryClosure() {
};
}
error('Unknown crypto method');
return null;
}
CipherTransformFactory.prototype = {

View file

@ -112,8 +112,8 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
};
PartialEvaluator.prototype = {
getIRQueue: function partialEvaluatorGetIRQueue(stream, resources,
queue, dependency) {
getOperatorList: function partialEvaluatorGetOperatorList(stream, resources,
dependency, queue) {
var self = this;
var xref = this.xref;
@ -136,8 +136,6 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
var fontRes = resources.get('Font');
// TODO: TOASK: Is it possible to get here? If so, what does
// args[0].name should be like???
assert(fontRes, 'fontRes not available');
fontRes = xref.fetchIfRef(fontRes);
@ -177,7 +175,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
// Ensure the font is ready before the font is set
// and later on used for drawing.
// TODO: This should get insert to the IRQueue only once per
// OPTIMIZE: This should get insert to the operatorList only once per
// page.
insertDependency([loadedName]);
return loadedName;
@ -239,6 +237,9 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
}, handler, xref, resources, image, inline);
}
if (!queue)
queue = {};
if (!queue.argsArray) {
queue.argsArray = [];
}
@ -280,9 +281,6 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
// TODO figure out how to type-check vararg functions
if ((cmd == 'SCN' || cmd == 'scn') && !args[args.length - 1].code) {
// Use the IR version for setStroke/FillColorN.
fn += '_IR';
// compile tiling patterns
var patternName = args[args.length - 1];
// SCN/scn applies patterns along with normal colors
@ -295,15 +293,14 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
if (typeNum == TILING_PATTERN) {
// Create an IR of the pattern code.
var depIdx = dependencyArray.length;
var queueObj = {};
var codeIR = this.getIRQueue(pattern, dict.get('Resources') ||
resources, queueObj, dependencyArray);
var operatorList = this.getOperatorList(pattern,
dict.get('Resources') || resources, dependencyArray);
// Add the dependencies that are required to execute the
// codeIR.
// operatorList.
insertDependency(dependencyArray.slice(depIdx));
args = TilingPattern.getIR(codeIR, dict, args);
args = TilingPattern.getIR(operatorList, dict, args);
}
else if (typeNum == SHADING_PATTERN) {
var shading = xref.fetchIfRef(dict.get('Shading'));
@ -337,14 +334,18 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
fnArray.push('paintFormXObjectBegin');
argsArray.push([matrix, bbox]);
// This adds the IRQueue of the xObj to the current queue.
// This adds the operatorList of the xObj to the current queue.
var depIdx = dependencyArray.length;
this.getIRQueue(xobj, xobj.dict.get('Resources') || resources,
queue, dependencyArray);
// Pass in the current `queue` object. That means the `fnArray`
// and the `argsArray` in this scope is reused and new commands
// are added to them.
this.getOperatorList(xobj,
xobj.dict.get('Resources') || resources,
dependencyArray, queue);
// Add the dependencies that are required to execute the
// codeIR.
// operatorList.
insertDependency(dependencyArray.slice(depIdx));
fn = 'paintFormXObjectEnd';
@ -454,10 +455,7 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
}
}
return {
fnArray: fnArray,
argsArray: argsArray
};
return queue;
},
extractDataStructures: function
@ -855,12 +853,11 @@ var PartialEvaluator = (function PartialEvaluatorClosure() {
var charProcs = xref.fetchIfRef(dict.get('CharProcs'));
var fontResources = xref.fetchIfRef(dict.get('Resources')) || resources;
properties.resources = fontResources;
properties.charProcIRQueues = {};
properties.charProcOperatorList = {};
for (var key in charProcs.map) {
var glyphStream = xref.fetchIfRef(charProcs.map[key]);
var queueObj = {};
properties.charProcIRQueues[key] =
this.getIRQueue(glyphStream, fontResources, queueObj, dependency);
properties.charProcOperatorList[key] =
this.getOperatorList(glyphStream, fontResources, dependency);
}
}

View file

@ -409,8 +409,8 @@ var FontLoader = {
bind: function fontLoaderBind(fonts, callback) {
function checkFontsLoaded() {
for (var i = 0, ii = objs.length; i < ii; i++) {
var fontObj = objs[i];
for (var i = 0, ii = fonts.length; i < ii; i++) {
var fontObj = fonts[i];
if (fontObj.loading) {
return false;
}
@ -423,52 +423,45 @@ var FontLoader = {
return true;
}
var rules = [], names = [], objs = [];
var rules = [], names = [], fontsToLoad = [];
var fontCreateTimer = 0;
for (var i = 0, ii = fonts.length; i < ii; i++) {
var font = fonts[i];
// If there is already a fontObj on the font, then it was loaded/attached
// to the page already and we don't have to do anything for this font
// here future.
if (font.fontObj) {
// Add the font to the DOM only once or skip if the font
// is already loaded.
if (font.attached || font.loading == false) {
continue;
}
font.attached = true;
var obj = new Font(font.name, font.file, font.properties);
// Store the fontObj on the font such that `setFont` in CanvasGraphics
// can reuse it later again.
font.fontObj = obj;
objs.push(obj);
fontsToLoad.push(font);
var str = '';
var data = obj.data;
var data = font.data;
if (data) {
var length = data.length;
for (var j = 0; j < length; j++)
str += String.fromCharCode(data[j]);
var rule = isWorker ? obj.bindWorker(str) : obj.bindDOM(str);
var rule = font.bindDOM(str);
if (rule) {
rules.push(rule);
names.push(obj.loadedName);
names.push(font.loadedName);
}
}
}
this.listeningForFontLoad = false;
if (!isWorker && rules.length) {
FontLoader.prepareFontLoadEvent(rules, names, objs);
FontLoader.prepareFontLoadEvent(rules, names, fontsToLoad);
}
if (!checkFontsLoaded()) {
document.documentElement.addEventListener(
'pdfjsFontLoad', checkFontsLoaded, false);
}
return objs;
},
// Set things up so that at least one pdfjsFontLoad event is
// dispatched when all the @font-face |rules| for |names| have been
@ -476,7 +469,7 @@ var FontLoader = {
// has already started in this (outer) document, so that they should
// be ordered before the load in the subdocument.
prepareFontLoadEvent: function fontLoaderPrepareFontLoadEvent(rules, names,
objs) {
fonts) {
/** Hack begin */
// There's no event when a font has finished downloading so the
// following code is a dirty hack to 'guess' when a font is
@ -500,6 +493,15 @@ var FontLoader = {
// The postMessage() hackery was added to work around chrome bug
// 82402.
// Validate the names parameter -- the values can used to construct HTML.
if (!/^\w+$/.test(names.join(''))) {
error('Invalid font name(s): ' + names.join());
// Normally the error-function throws. But if a malicious code
// intercepts the function call then the return is needed.
return;
}
var div = document.createElement('div');
div.setAttribute('style',
'visibility: hidden;' +
@ -517,8 +519,8 @@ var FontLoader = {
'message',
function fontLoaderMessage(e) {
var fontNames = JSON.parse(e.data);
for (var i = 0, ii = objs.length; i < ii; ++i) {
var font = objs[i];
for (var i = 0, ii = fonts.length; i < ii; ++i) {
var font = fonts[i];
font.loading = false;
}
var evt = document.createEvent('Events');
@ -764,7 +766,7 @@ var Font = (function FontClosure() {
function Font(name, file, properties) {
this.name = name;
this.coded = properties.coded;
this.charProcIRQueues = properties.charProcIRQueues;
this.charProcOperatorList = properties.charProcOperatorList;
this.resources = properties.resources;
this.sizes = [];
@ -829,8 +831,6 @@ var Font = (function FontClosure() {
return;
}
this.loadedName = getUniqueName();
properties.id = this.loadedName;
var data;
switch (type) {
case 'Type1':
@ -864,6 +864,7 @@ var Font = (function FontClosure() {
this.widthMultiplier = !properties.fontMatrix ? 1.0 :
1.0 / properties.fontMatrix[0];
this.encoding = properties.baseEncoding;
this.loadedName = properties.loadedName;
this.loading = true;
};
@ -2273,17 +2274,6 @@ var Font = (function FontClosure() {
}
},
bindWorker: function font_bindWorker(data) {
postMessage({
action: 'font',
data: {
raw: data,
fontName: this.loadedName,
mimetype: this.mimetype
}
});
},
bindDOM: function font_bindDom(data) {
var fontName = this.loadedName;
@ -2337,7 +2327,7 @@ var Font = (function FontClosure() {
},
charToGlyph: function fonts_charToGlyph(charcode) {
var fontCharCode, width, codeIRQueue;
var fontCharCode, width, operatorList;
var width = this.widths[charcode];
@ -2372,7 +2362,7 @@ var Font = (function FontClosure() {
break;
case 'Type3':
var glyphName = this.differences[charcode] || this.encoding[charcode];
codeIRQueue = this.charProcIRQueues[glyphName];
operatorList = this.charProcOperatorList[glyphName];
fontCharCode = charcode;
break;
case 'TrueType':
@ -2415,7 +2405,7 @@ var Font = (function FontClosure() {
fontChar: String.fromCharCode(fontCharCode),
unicode: unicodeChars,
width: width,
codeIRQueue: codeIRQueue
operatorList: operatorList
};
},

View file

@ -134,6 +134,8 @@ var Catalog = (function CatalogClosure() {
while (queue.length > 0) {
var i = queue.shift();
var outlineDict = xref.fetch(i.obj);
if (outlineDict === null)
continue;
if (!outlineDict.has('Title'))
error('Invalid outline item');
var dest = outlineDict.get('A');
@ -512,7 +514,6 @@ var XRef = (function XRefClosure() {
return dict;
// nothing helps
error('Invalid PDF structure');
return null;
},
readXRef: function readXref(startXRef) {
var stream = this.stream;
@ -569,6 +570,8 @@ var XRef = (function XRefClosure() {
},
getEntry: function xRefGetEntry(i) {
var e = this.entries[i];
if (e === null)
return null;
return e.free ? null : e; // returns null is the entry is free
},
fetchIfRef: function xRefFetchIfRef(obj) {
@ -719,12 +722,10 @@ var PDFObjects = (function PDFObjectsClosure() {
// If there isn't an object yet or the object isn't resolved, then the
// data isn't ready yet!
if (!obj || !obj.isResolved) {
if (!obj || !obj.isResolved)
error('Requesting object that isn\'t resolved yet ' + objId);
return null;
} else {
return obj.data;
}
return obj.data;
},
/**

View file

@ -53,15 +53,14 @@ var Parser = (function ParserClosure() {
this.shift();
var dict = new Dict();
while (!isCmd(this.buf1, '>>') && !isEOF(this.buf1)) {
if (!isName(this.buf1)) {
if (!isName(this.buf1))
error('Dictionary key must be a name object');
} else {
var key = this.buf1.name;
this.shift();
if (isEOF(this.buf1))
break;
dict.set(key, this.getObj(cipherTransform));
}
var key = this.buf1.name;
this.shift();
if (isEOF(this.buf1))
break;
dict.set(key, this.getObj(cipherTransform));
}
if (isEOF(this.buf1))
error('End of file inside dictionary');
@ -106,15 +105,14 @@ var Parser = (function ParserClosure() {
// parse dictionary
var dict = new Dict();
while (!isCmd(this.buf1, 'ID') && !isEOF(this.buf1)) {
if (!isName(this.buf1)) {
if (!isName(this.buf1))
error('Dictionary key must be a name object');
} else {
var key = this.buf1.name;
this.shift();
if (isEOF(this.buf1))
break;
dict.set(key, this.getObj(cipherTransform));
}
var key = this.buf1.name;
this.shift();
if (isEOF(this.buf1))
break;
dict.set(key, this.getObj(cipherTransform));
}
// parse image stream
@ -176,10 +174,8 @@ var Parser = (function ParserClosure() {
// get length
var length = this.fetchIfRef(dict.get('Length'));
if (!isInt(length)) {
if (!isInt(length))
error('Bad ' + length + ' attribute in stream');
length = 0;
}
// skip over the stream data
stream.pos = pos + length;
@ -208,14 +204,13 @@ var Parser = (function ParserClosure() {
filter = filterArray[i];
if (!isName(filter))
error('Bad filter name: ' + filter);
else {
params = null;
if (isArray(paramsArray) && (i in paramsArray))
params = paramsArray[i];
stream = this.makeFilter(stream, filter.name, length, params);
// after the first stream the length variable is invalid
length = null;
}
params = null;
if (isArray(paramsArray) && (i in paramsArray))
params = paramsArray[i];
stream = this.makeFilter(stream, filter.name, length, params);
// after the first stream the length variable is invalid
length = null;
}
}
return stream;
@ -527,17 +522,15 @@ var Lexer = (function LexerClosure() {
// fall through
case ')':
error('Illegal character: ' + ch);
return Error;
}
// command
var str = ch;
while (!!(ch = stream.lookChar()) && !specialChars[ch.charCodeAt(0)]) {
stream.skip();
if (str.length == 128) {
if (str.length == 128)
error('Command token too long: ' + str.length);
break;
}
str += ch;
}
if (str == 'true')
@ -594,7 +587,6 @@ var Linearization = (function LinearizationClosure() {
return obj;
}
error('"' + name + '" field in linearization table is invalid');
return 0;
},
getHint: function linearizationGetHint(index) {
var linDict = this.linDict;
@ -607,7 +599,6 @@ var Linearization = (function LinearizationClosure() {
return obj2;
}
error('Hints table in linearization table is invalid: ' + index);
return 0;
},
get length() {
if (!isDict(this.linDict))

View file

@ -82,7 +82,7 @@ Shadings.RadialAxial = (function RadialAxialClosure() {
fnObj = xref.fetchIfRef(fnObj);
if (isArray(fnObj))
error('No support for array of functions');
else if (!isPDFFunction(fnObj))
if (!isPDFFunction(fnObj))
error('Invalid function');
var fn = PDFFunction.parse(xref, fnObj);
@ -190,7 +190,7 @@ var TilingPattern = (function TilingPatternClosure() {
var MAX_PATTERN_SIZE = 512;
function TilingPattern(IR, color, ctx, objs) {
var IRQueue = IR[2];
var operatorList = IR[2];
this.matrix = IR[3];
var bbox = IR[4];
var xstep = IR[5];
@ -222,7 +222,7 @@ var TilingPattern = (function TilingPatternClosure() {
width = height = MAX_PATTERN_SIZE;
}
var tmpCanvas = new ScratchCanvas(width, height);
var tmpCanvas = createScratchCanvas(width, height);
// set the new canvas element context as the graphics context
var tmpCtx = tmpCanvas.getContext('2d');
@ -259,12 +259,12 @@ var TilingPattern = (function TilingPatternClosure() {
graphics.endPath();
}
graphics.executeIRQueue(IRQueue);
graphics.executeOperatorList(operatorList);
this.canvas = tmpCanvas;
}
TilingPattern.getIR = function tiling_getIR(codeIR, dict, args) {
TilingPattern.getIR = function tiling_getIR(operatorList, dict, args) {
var matrix = dict.get('Matrix');
var bbox = dict.get('BBox');
var xstep = dict.get('XStep');
@ -272,7 +272,7 @@ var TilingPattern = (function TilingPatternClosure() {
var paintType = dict.get('PaintType');
return [
'TilingPattern', args, codeIR, matrix, bbox, xstep, ystep, paintType
'TilingPattern', args, operatorList, matrix, bbox, xstep, ystep, paintType
];
};

View file

@ -402,7 +402,7 @@ var Promise = (function PromiseClosure() {
if (this.isResolved) {
var data = this.data;
callback.call(null, data);
} else if (this.isRejected && errorback) {
} else if (this.isRejected && errback) {
var error = this.error;
errback.call(null, error);
} else {
@ -416,3 +416,55 @@ var Promise = (function PromiseClosure() {
return Promise;
})();
var StatTimer = (function StatTimerClosure() {
function rpad(str, pad, length) {
while (str.length < length)
str += pad;
return str;
}
function StatTimer() {
this.started = {};
this.times = [];
this.enabled = true;
}
StatTimer.prototype = {
time: function statTimerTime(name) {
if (!this.enabled)
return;
if (name in this.started)
throw 'Timer is already running for ' + name;
this.started[name] = Date.now();
},
timeEnd: function statTimerTimeEnd(name) {
if (!this.enabled)
return;
if (!(name in this.started))
throw 'Timer has not been started for ' + name;
this.times.push({
'name': name,
'start': this.started[name],
'end': Date.now()
});
// Remove timer from started so it can be called again.
delete this.started[name];
},
toString: function statTimerToString() {
var times = this.times;
var out = '';
// Find the longest name for padding purposes.
var longest = 0;
for (var i = 0, ii = times.length; i < ii; ++i) {
var name = times[i]['name'];
if (name.length > longest)
longest = name.length;
}
for (var i = 0, ii = times.length; i < ii; ++i) {
var span = times[i];
var duration = span.end - span.start;
out += rpad(span['name'], ' ', longest) + ' ' + duration + 'ms\n';
}
return out;
}
};
return StatTimer;
})();

View file

@ -79,7 +79,7 @@ MessageHandler.prototype = {
var WorkerMessageHandler = {
setup: function wphSetup(handler) {
var pdfDoc = null;
var pdfModel = null;
handler.on('test', function wphSetupTest(data) {
handler.send('test', data instanceof Uint8Array);
@ -88,7 +88,7 @@ var WorkerMessageHandler = {
handler.on('doc', function wphSetupDoc(data) {
// Create only the model of the PDFDoc, which is enough for
// processing the content of the pdf.
pdfDoc = new PDFDocModel(new Stream(data));
pdfModel = new PDFDocModel(new Stream(data));
});
handler.on('page_request', function wphSetupPageRequest(pageNum) {
@ -103,14 +103,14 @@ var WorkerMessageHandler = {
var start = Date.now();
var dependency = [];
var IRQueue = null;
var operatorList = null;
try {
var page = pdfDoc.getPage(pageNum);
var page = pdfModel.getPage(pageNum);
// Pre compile the pdf page and fetch the fonts/images.
IRQueue = page.getIRQueue(handler, dependency);
operatorList = page.getOperatorList(handler, dependency);
} catch (e) {
var minimumStackMessage =
'worker.js: while trying to getPage() and getIRQueue()';
'worker.js: while trying to getPage() and getOperatorList()';
// Turn the error into an obj that can be serialized
if (typeof e === 'string') {
@ -137,8 +137,8 @@ var WorkerMessageHandler = {
return;
}
console.log('page=%d - getIRQueue: time=%dms, len=%d', pageNum,
Date.now() - start, IRQueue.fnArray.length);
console.log('page=%d - getOperatorList: time=%dms, len=%d', pageNum,
Date.now() - start, operatorList.fnArray.length);
// Filter the dependecies for fonts.
var fonts = {};
@ -151,59 +151,10 @@ var WorkerMessageHandler = {
handler.send('page', {
pageNum: pageNum,
IRQueue: IRQueue,
operatorList: operatorList,
depFonts: Object.keys(fonts)
});
}, this);
handler.on('font', function wphSetupFont(data) {
var objId = data[0];
var name = data[1];
var file = data[2];
var properties = data[3];
var font = {
name: name,
file: file,
properties: properties
};
// Some fonts don't have a file, e.g. the build in ones like Arial.
if (file) {
var fontFileDict = new Dict();
fontFileDict.map = file.dict.map;
var fontFile = new Stream(file.bytes, file.start,
file.end - file.start, fontFileDict);
// Check if this is a FlateStream. Otherwise just use the created
// Stream one. This makes complex_ttf_font.pdf work.
var cmf = file.bytes[0];
if ((cmf & 0x0f) == 0x08) {
font.file = new FlateStream(fontFile);
} else {
font.file = fontFile;
}
}
var obj = new Font(font.name, font.file, font.properties);
var str = '';
var objData = obj.data;
if (objData) {
var length = objData.length;
for (var j = 0; j < length; ++j)
str += String.fromCharCode(objData[j]);
}
obj.str = str;
// Remove the data array form the font object, as it's not needed
// anymore as we sent over the ready str.
delete obj.data;
handler.send('font_ready', [objId, obj]);
});
}
};