From d50773fb9638d2a30d630d872f3478c6289c033e Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Thu, 9 Feb 2012 18:40:44 -0800 Subject: [PATCH 01/40] Fixing ToUnicode parsing; workaround for invalid UTF16 encoding --- src/evaluator.js | 14 ++++++++++++-- src/fonts.js | 17 ++++++++++++----- 2 files changed, 24 insertions(+), 7 deletions(-) diff --git a/src/evaluator.js b/src/evaluator.js index 1597bed11..e34787e41 100644 --- a/src/evaluator.js +++ b/src/evaluator.js @@ -620,8 +620,18 @@ var PartialEvaluator = (function PartialEvaluatorClosure() { } else { // parsing hex UTF-16BE numbers var str = []; - for (var i = 0, ii = token.length; i < ii; i += 4) - str.push(parseInt(token.substr(i, 4), 16)); + for (var k = 0, kk = token.length; k < kk; k += 4) { + var b = parseInt(token.substr(k, 4), 16); + if (b <= 0x10) { + k += 4; + b = (b << 16) | parseInt(token.substr(k, 4), 16); + b -= 0x10000; + str.push(0xD800 | (b >> 10)); + str.push(0xDC00 | (b & 0x3FF)); + break; + } + str.push(b); + } tokens.push(String.fromCharCode.apply(String, str)); token = ''; } diff --git a/src/fonts.js b/src/fonts.js index f8ae7de4c..aadaa8d71 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -823,6 +823,13 @@ var Font = (function FontClosure() { else this.rebuildToUnicode(properties); + this.toUnicodeOriginal = this.toUnicode.slice(); + for (var i = 0, j = 0xE000; i < this.toUnicode.length; i++) { + if (typeof this.toUnicode[i] == 'number') + break; + this.toUnicode[i] = j++; + } + if (!file) { // The file data is not specified. Trying to fix the font name // to be used with the canvas.font. @@ -1778,7 +1785,7 @@ var Font = (function FontClosure() { for (var i = 1; i < numGlyphs; i++) { var cid = gidToCidMap[i] || i; var unicode = this.toUnicode[cid]; - if (!unicode || isSpecialUnicode(unicode) || + if (!unicode || typeof unicode !== 'number' || isSpecialUnicode(unicode) || unicode in usedUnicodes) { unassignedUnicodeItems.push(i); continue; @@ -1825,7 +1832,7 @@ var Font = (function FontClosure() { var usedUnicodes = [], unassignedUnicodeItems = []; for (var i = 0, ii = glyphs.length; i < ii; i++) { var unicode = toUnicode[i + 1]; - if (!unicode || unicode in usedUnicodes) { + if (!unicode || typeof unicode !== 'number' || unicode in usedUnicodes) { unassignedUnicodeItems.push(i); continue; } @@ -1972,7 +1979,7 @@ var Font = (function FontClosure() { } properties.baseEncoding = encoding; } - if (properties.subtype == 'CIDFontType0C') { + if (false && properties.subtype == 'CIDFontType0C') { var toUnicode = []; for (var i = 0; i < charstrings.length; ++i) { var charstring = charstrings[i]; @@ -2270,8 +2277,8 @@ var Font = (function FontClosure() { break; } - var unicodeChars = !('toUnicode' in this) ? charcode : - this.toUnicode[charcode] || charcode; + var unicodeChars = !('toUnicodeOriginal' in this) ? charcode : + this.toUnicodeOriginal[charcode] || charcode; if (typeof unicodeChars === 'number') unicodeChars = String.fromCharCode(unicodeChars); From 2e1a88f39e46f4131439761800b84bf5b605e83f Mon Sep 17 00:00:00 2001 From: Adil Allawi Date: Fri, 10 Feb 2012 16:06:59 +0000 Subject: [PATCH 02/40] Handle bidi ordering of PDF strings --- src/bidi.js | 368 ++++++++++++++++++++++++++++++++++++++++++++++++ src/canvas.js | 17 ++- src/fonts.js | 10 ++ web/viewer.html | 1 + 4 files changed, 393 insertions(+), 3 deletions(-) create mode 100644 src/bidi.js diff --git a/src/bidi.js b/src/bidi.js new file mode 100644 index 000000000..cbb668764 --- /dev/null +++ b/src/bidi.js @@ -0,0 +1,368 @@ +function bidi(doc, str, startLevel) { + if (str.length == 0) return str; + + var chars= new Array(str.length); + var levels = new Array(str.length); + var types = new Array(str.length); + var oldtypes = new Array(str.length); + +// get types, fill arrays + + for (var i = 0; i < str.length; ++i) { + var c = str.charAt(i); + chars[i] = c; + levels[i] = startLevel; + + var t = "L"; + if ('\u0600' <= c && c <= '\u06ff') t = "AL"; + else if ('0' <= c && c <= '9') t = (c <= 5) ? "EN" : "AN"; + else if (c == '.' || c == ',' || c == ':') t = "CS"; + else if (c == '/') t = "ES"; + else if (c == '#' || c == '$' || c == '%' || c == '+' || c == '-') t = "ET" + else if (c == '>') t = "L" + else if (c == '<') t = "R" + + oldtypes[i] = types[i] = t; + } + + var diffChars = new Array(str.length); + var diffLevels = new Array(str.length); + var diffTypes = new Array(str.length); + + showArray(doc, "Chars: ", chars, diffChars); + showArray(doc, "Types: ", types, diffTypes); + //alert("chars: " + chars.join(",")); + //alert("types: " + types.join(",")); + //alert("levels: " + levels.join(",")); + +/* +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 < types.length; ++i) { + if (types[i] == "NSM") types[i] = lastType; + else lastType = types[i]; + } + + showArray(doc, "W1: ", types, diffTypes); + +/* +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 < types.length; ++i) { + var t = types[i]; + if (t == "EN") types[i] = (lastType == "AL") ? "AN" : "EN"; + else if (t == "R" || t == "L" || t == "AL") lastType = t; + } + + showArray(doc, "W2: ", types, diffTypes); + +/* +W3. Change all ALs to R. +*/ + + for (var i = 0; i < types.length; ++i) { + var t = types[i]; + if (t == "AL") types[i] = "R"; + } + + showArray(doc, "W3: ", types, diffTypes); + +/* +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 < types.length - 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]; + } + + showArray(doc, "W4: ", types, diffTypes); + +/* +W5. A sequence of European terminators adjacent to European numbers changes to all European numbers: +*/ + + for (var i = 0; i < types.length; ++i) { + if (types[i] == "EN") { + // do before + for (j = i-1; j >= 0; --j) { + if (types[j] == "ET") types[j] = "EN"; + else break; + } + // do after + for (j = i+1; j < types.length; --j) { + if (types[j] == "ET") types[j] = "EN"; + else break; + } + } + } + + showArray(doc, "W5: ", types, diffTypes); + + +/* +W6. Otherwise, separators and terminators change to Other Neutral: +*/ + + for (var i = 0; i < types.length; ++i) { + var t = types[i]; + if (t == "ES" || t == "ET" || t == "CS") types[i] = "ON"; + } + + showArray(doc, "W6: ", types, diffTypes); + +/* +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 < types.length; ++i) { + var t = types[i]; + if (t == "EN") types[i] = (lastType == "L") ? "L" : "EN"; + else if (t == "R" || t == "L") lastType = t; + } + + showArray(doc, "W7: ", types, diffTypes); + +/* +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 < types.length; ++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 < types.length) 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) + } + } + + showArray(doc, "N1: ", types, diffTypes); + +/* +N2. Any remaining neutrals take the embedding direction. +*/ + + for (var i = 0; i < types.length; ++i) { + if (types[i] == "ON") types[i] = e; + } + + showArray(doc, "N2: ", types, diffTypes); + +/* +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. +*/ + + showArray(doc, "Levels: ", levels, diffLevels); + + for (var i = 0; i < types.length; ++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; + } + } + } + + showArray(doc, "I1/2: ", levels, diffLevels); + + +/* +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. +*/ + +/* + boolean atEnd = true; + for (var i = levels.length - 1; i >= 0; --i) { + var t = oldTypes[i]; + if (t == "B" || t == "S") { + levels[i] = startLevel; + atEnd = true; + } else if (atEnd && t == "WS") { + levels[i] = startLevel; + } + } +*/ + + showArray(doc, "L1: ", levels, diffLevels); + + +/* +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; + // alert("Levels: " + levels.join(",")); + for (var i = 0; i < levels.length; ++i) { + var level = levels[i]; + if (highestLevel < level) highestLevel = level; + if (lowestOddLevel > level && isOdd(level)) lowestOddLevel = level; + } + // alert("highestLevel: " + highestLevel + "; lowestOddLevel: " + lowestOddLevel); + + // now reverse between those limits + + for (var level = highestLevel; level >= lowestOddLevel; --level) { + // find segments to reverse + var start = -1; + for (var i = 0; i < levels.length; ++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); + } + + showArray(doc, "L2 (" + level + "):", chars, diffChars); + + } + + +/* +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 + + showArray(doc, "L3: ", chars, diffChars); + + +/* +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. +*/ + + for (var i = 0; i < chars.length; ++i) { + chars[i] = mirrorGlyphs(chars[i]); + } + + showArray(doc, "L4: ", chars, diffChars); + +// Finally, return string + + var result = ""; + for (var i = 0; i < chars.length; ++i) { + var ch = chars[i]; + if (ch != '<' && ch != '>') result += ch; + } + return result; +} + + +// UTILITIES + +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; j < arr.length; ++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) { + // alert("reverse: " + arr.join(",") + "; " + start + "; " + end); + for (var i = start, j = end-1; i < j; ++i, --j) { + var temp = arr[i]; + arr[i] = arr[j]; + arr[j] = temp; + } + // alert("reverse: " + chars.join(",")); +} + +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 '�': return '�'; + case '�': return '�'; + default: return c; + } +} + +function showArray(doc, title, arr, diffarr) { + if (doc == null) + return; + var haveDiff = false; + for (var i = 0; i < arr.length; ++i) { + if (arr[i] != diffarr[i]) { + haveDiff = true; + diffarr[i] = arr[i]; + } + } + if (haveDiff) { + doc.writeln("", title, "", arr.join(""), ""); + } +} + + diff --git a/src/canvas.js b/src/canvas.js index f4815a655..1106ba328 100644 --- a/src/canvas.js +++ b/src/canvas.js @@ -749,8 +749,16 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { x += charWidth; - text.str += glyph.unicode === ' ' ? '\u00A0' : glyph.unicode; - text.length++; + var glyphUnicode = glyph.unicode === ' ' ? '\u00A0' : glyph.unicode; + var glyphUniLen = glyphUnicode.length; + //reverse an arabic ligature + if (glyphUniLen > 1 && isRTLRangeFor(glyphUnicode.charCodeAt(0))) + { + for (var ii = glyphUniLen - 1; ii >= 0; ii--) + text.str += glyphUnicode[ii]; + } else + text.str += glyphUnicode; + text.length += glyphUniLen; text.canvasWidth += charWidth; } current.x += x * textHScale2; @@ -823,8 +831,11 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { } } - if (textSelection) + if (textSelection) { + if (isRTLRangeFor(text.str.charCodeAt(0))) //first char is rtl + text.str = bidi(null, text.str, 1); this.textLayer.appendText(text, font.loadedName, fontSize); + } }, nextLineShowText: function canvasGraphicsNextLineShowText(text) { this.nextLine(); diff --git a/src/fonts.js b/src/fonts.js index aadaa8d71..45c83b74d 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -763,6 +763,16 @@ function getUnicodeRangeFor(value) { return -1; } +function isRTLRangeFor(value) { + var range = UnicodeRanges[13]; + if (value >= range.begin && value < range.end) + return true; + range = UnicodeRanges[11]; + if (value >= range.begin && value < range.end) + return true; + return false; +} + function isSpecialUnicode(unicode) { return (unicode <= 0x1F || (unicode >= 127 && unicode < kSizeOfGlyphArea)) || (unicode >= kCmapGlyphOffset && diff --git a/web/viewer.html b/web/viewer.html index 7c55ec735..85b267516 100644 --- a/web/viewer.html +++ b/web/viewer.html @@ -27,6 +27,7 @@ + From 00f6f0e0965777be913248f73520323749e7c19b Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Fri, 10 Feb 2012 15:55:04 -0600 Subject: [PATCH 03/40] Remove lazy encoding initialization --- src/fonts.js | 498 ++++++++++++++++++++++++--------------------------- 1 file changed, 235 insertions(+), 263 deletions(-) diff --git a/src/fonts.js b/src/fonts.js index f8ae7de4c..57d44c771 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -32,269 +32,241 @@ var FontFlags = { }; var Encodings = { - get ExpertEncoding() { - return shadow(this, 'ExpertEncoding', ['', '', '', '', '', '', '', '', '', - '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', - '', '', '', '', '', 'space', 'exclamsmall', 'Hungarumlautsmall', '', - 'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall', - 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', - 'onedotenleader', 'comma', 'hyphen', 'period', 'fraction', - 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', - 'fouroldstyle', 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', - 'eightoldstyle', 'nineoldstyle', 'colon', 'semicolon', 'commasuperior', - 'threequartersemdash', 'periodsuperior', 'questionsmall', '', - 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior', 'esuperior', '', - '', 'isuperior', '', '', 'lsuperior', 'msuperior', 'nsuperior', - 'osuperior', '', '', 'rsuperior', 'ssuperior', 'tsuperior', '', 'ff', - 'fi', 'fl', 'ffi', 'ffl', 'parenleftinferior', '', 'parenrightinferior', - 'Circumflexsmall', 'hyphensuperior', 'Gravesmall', 'Asmall', 'Bsmall', - 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall', 'Ismall', - 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall', - 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', - 'Xsmall', 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', - 'Tildesmall', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', - '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', - '', 'exclamdownsmall', 'centoldstyle', 'Lslashsmall', '', '', - 'Scaronsmall', 'Zcaronsmall', 'Dieresissmall', 'Brevesmall', - 'Caronsmall', '', 'Dotaccentsmall', '', '', 'Macronsmall', '', '', - 'figuredash', 'hypheninferior', '', '', 'Ogoneksmall', 'Ringsmall', - 'Cedillasmall', '', '', '', 'onequarter', 'onehalf', 'threequarters', - 'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', - 'seveneighths', 'onethird', 'twothirds', '', '', 'zerosuperior', - 'onesuperior', 'twosuperior', 'threesuperior', 'foursuperior', - 'fivesuperior', 'sixsuperior', 'sevensuperior', 'eightsuperior', - 'ninesuperior', 'zeroinferior', 'oneinferior', 'twoinferior', - 'threeinferior', 'fourinferior', 'fiveinferior', 'sixinferior', - 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior', - 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall', - 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', - 'Aringsmall', 'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', - 'Ecircumflexsmall', 'Edieresissmall', 'Igravesmall', 'Iacutesmall', - 'Icircumflexsmall', 'Idieresissmall', 'Ethsmall', 'Ntildesmall', - 'Ogravesmall', 'Oacutesmall', 'Ocircumflexsmall', 'Otildesmall', - 'Odieresissmall', 'OEsmall', 'Oslashsmall', 'Ugravesmall', 'Uacutesmall', - 'Ucircumflexsmall', 'Udieresissmall', 'Yacutesmall', 'Thornsmall', - 'Ydieresissmall' - ]); - }, - get MacExpertEncoding() { - return shadow(this, 'MacExpertEncoding', ['', '', '', '', '', '', '', '', - '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', - '', '', '', '', '', '', 'space', 'exclamsmall', 'Hungarumlautsmall', - 'centoldstyle', 'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', - 'Acutesmall', 'parenleftsuperior', 'parenrightsuperior', - 'twodotenleader', 'onedotenleader', 'comma', 'hyphen', 'period', - 'fraction', 'zerooldstyle', 'oneoldstyle', 'twooldstyle', - 'threeoldstyle', 'fouroldstyle', 'fiveoldstyle', 'sixoldstyle', - 'sevenoldstyle', 'eightoldstyle', 'nineoldstyle', 'colon', 'semicolon', - '', 'threequartersemdash', '', 'questionsmall', '', '', '', '', - 'Ethsmall', '', '', 'onequarter', 'onehalf', 'threequarters', - 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths', 'onethird', - 'twothirds', '', '', '', '', '', '', 'ff', 'fi', 'fl', 'ffi', 'ffl', - 'parenleftinferior', '', 'parenrightinferior', 'Circumflexsmall', - 'hypheninferior', 'Gravesmall', 'Asmall', 'Bsmall', 'Csmall', 'Dsmall', - 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall', 'Ismall', 'Jsmall', 'Ksmall', - 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall', 'Qsmall', 'Rsmall', - 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', 'Xsmall', 'Ysmall', - 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall', '', '', - 'asuperior', 'centsuperior', '', '', '', '', 'Aacutesmall', - 'Agravesmall', 'Acircumflexsmall', 'Adieresissmall', 'Atildesmall', - 'Aringsmall', 'Ccedillasmall', 'Eacutesmall', 'Egravesmall', - 'Ecircumflexsmall', 'Edieresissmall', 'Iacutesmall', 'Igravesmall', - 'Icircumflexsmall', 'Idieresissmall', 'Ntildesmall', 'Oacutesmall', - 'Ogravesmall', 'Ocircumflexsmall', 'Odieresissmall', 'Otildesmall', - 'Uacutesmall', 'Ugravesmall', 'Ucircumflexsmall', 'Udieresissmall', '', - 'eightsuperior', 'fourinferior', 'threeinferior', 'sixinferior', - 'eightinferior', 'seveninferior', 'Scaronsmall', '', 'centinferior', - 'twoinferior', '', 'Dieresissmall', '', 'Caronsmall', 'osuperior', - 'fiveinferior', '', 'commainferior', 'periodinferior', 'Yacutesmall', '', - 'dollarinferior', '', 'Thornsmall', '', 'nineinferior', 'zeroinferior', - 'Zcaronsmall', 'AEsmall', 'Oslashsmall', 'questiondownsmall', - 'oneinferior', 'Lslashsmall', '', '', '', '', '', '', 'Cedillasmall', '', - '', '', '', '', 'OEsmall', 'figuredash', 'hyphensuperior', '', '', '', - '', 'exclamdownsmall', '', 'Ydieresissmall', '', 'onesuperior', - 'twosuperior', 'threesuperior', 'foursuperior', 'fivesuperior', - 'sixsuperior', 'sevensuperior', 'ninesuperior', 'zerosuperior', '', - 'esuperior', 'rsuperior', 'tsuperior', '', '', 'isuperior', 'ssuperior', - 'dsuperior', '', '', '', '', '', 'lsuperior', 'Ogoneksmall', - 'Brevesmall', 'Macronsmall', 'bsuperior', 'nsuperior', 'msuperior', - 'commasuperior', 'periodsuperior', 'Dotaccentsmall', 'Ringsmall' - ]); - }, - get MacRomanEncoding() { - return shadow(this, 'MacRomanEncoding', ['', '', '', '', '', '', '', '', - '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', - '', '', '', '', '', '', 'space', 'exclam', 'quotedbl', 'numbersign', - 'dollar', 'percent', 'ampersand', 'quotesingle', 'parenleft', - 'parenright', 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', - 'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', - 'nine', 'colon', 'semicolon', 'less', 'equal', 'greater', 'question', - 'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'bracketleft', 'backslash', 'bracketright', 'asciicircum', 'underscore', - 'grave', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'braceleft', 'bar', 'braceright', 'asciitilde', '', 'Adieresis', 'Aring', - 'Ccedilla', 'Eacute', 'Ntilde', 'Odieresis', 'Udieresis', 'aacute', - 'agrave', 'acircumflex', 'adieresis', 'atilde', 'aring', 'ccedilla', - 'eacute', 'egrave', 'ecircumflex', 'edieresis', 'iacute', 'igrave', - 'icircumflex', 'idieresis', 'ntilde', 'oacute', 'ograve', 'ocircumflex', - 'odieresis', 'otilde', 'uacute', 'ugrave', 'ucircumflex', 'udieresis', - 'dagger', 'degree', 'cent', 'sterling', 'section', 'bullet', 'paragraph', - 'germandbls', 'registered', 'copyright', 'trademark', 'acute', - 'dieresis', 'notequal', 'AE', 'Oslash', 'infinity', 'plusminus', - 'lessequal', 'greaterequal', 'yen', 'mu', 'partialdiff', 'summation', - 'product', 'pi', 'integral', 'ordfeminine', 'ordmasculine', 'Omega', - 'ae', 'oslash', 'questiondown', 'exclamdown', 'logicalnot', 'radical', - 'florin', 'approxequal', 'Delta', 'guillemotleft', 'guillemotright', - 'ellipsis', 'space', 'Agrave', 'Atilde', 'Otilde', 'OE', 'oe', 'endash', - 'emdash', 'quotedblleft', 'quotedblright', 'quoteleft', 'quoteright', - 'divide', 'lozenge', 'ydieresis', 'Ydieresis', 'fraction', 'currency', - 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'daggerdbl', - 'periodcentered', 'quotesinglbase', 'quotedblbase', 'perthousand', - 'Acircumflex', 'Ecircumflex', 'Aacute', 'Edieresis', 'Egrave', 'Iacute', - 'Icircumflex', 'Idieresis', 'Igrave', 'Oacute', 'Ocircumflex', 'apple', - 'Ograve', 'Uacute', 'Ucircumflex', 'Ugrave', 'dotlessi', 'circumflex', - 'tilde', 'macron', 'breve', 'dotaccent', 'ring', 'cedilla', - 'hungarumlaut', 'ogonek', 'caron' - ]); - }, - get StandardEncoding() { - return shadow(this, 'StandardEncoding', ['', '', '', '', '', '', '', '', - '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', - '', '', '', '', '', '', 'space', 'exclam', 'quotedbl', 'numbersign', - 'dollar', 'percent', 'ampersand', 'quoteright', 'parenleft', - 'parenright', 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', - 'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', - 'nine', 'colon', 'semicolon', 'less', 'equal', 'greater', 'question', - 'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'bracketleft', 'backslash', 'bracketright', 'asciicircum', 'underscore', - 'quoteleft', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', - 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'braceleft', 'bar', 'braceright', 'asciitilde', '', '', '', '', '', '', - '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', - '', '', '', '', '', '', '', '', '', '', 'exclamdown', 'cent', 'sterling', - 'fraction', 'yen', 'florin', 'section', 'currency', 'quotesingle', - 'quotedblleft', 'guillemotleft', 'guilsinglleft', 'guilsinglright', 'fi', - 'fl', '', 'endash', 'dagger', 'daggerdbl', 'periodcentered', '', - 'paragraph', 'bullet', 'quotesinglbase', 'quotedblbase', 'quotedblright', - 'guillemotright', 'ellipsis', 'perthousand', '', 'questiondown', '', - 'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve', 'dotaccent', - 'dieresis', '', 'ring', 'cedilla', '', 'hungarumlaut', 'ogonek', 'caron', - 'emdash', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', - 'AE', '', 'ordfeminine', '', '', '', '', 'Lslash', 'Oslash', 'OE', - 'ordmasculine', '', '', '', '', '', 'ae', '', '', '', 'dotlessi', '', '', - 'lslash', 'oslash', 'oe', 'germandbls' - ]); - }, - get WinAnsiEncoding() { - return shadow(this, 'WinAnsiEncoding', ['', '', '', '', '', '', '', '', '', - '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', - '', '', '', '', '', 'space', 'exclam', 'quotedbl', 'numbersign', - 'dollar', 'percent', 'ampersand', 'quotesingle', 'parenleft', - 'parenright', 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', - 'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', 'eight', - 'nine', 'colon', 'semicolon', 'less', 'equal', 'greater', 'question', - 'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - 'bracketleft', 'backslash', 'bracketright', 'asciicircum', 'underscore', - 'grave', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'braceleft', 'bar', 'braceright', 'asciitilde', 'bullet', 'Euro', - 'bullet', 'quotesinglbase', 'florin', 'quotedblbase', 'ellipsis', - 'dagger', 'daggerdbl', 'circumflex', 'perthousand', 'Scaron', - 'guilsinglleft', 'OE', 'bullet', 'Zcaron', 'bullet', 'bullet', - 'quoteleft', 'quoteright', 'quotedblleft', 'quotedblright', 'bullet', - 'endash', 'emdash', 'tilde', 'trademark', 'scaron', 'guilsinglright', - 'oe', 'bullet', 'zcaron', 'Ydieresis', 'space', 'exclamdown', 'cent', - 'sterling', 'currency', 'yen', 'brokenbar', 'section', 'dieresis', - 'copyright', 'ordfeminine', 'guillemotleft', 'logicalnot', 'hyphen', - 'registered', 'macron', 'degree', 'plusminus', 'twosuperior', - 'threesuperior', 'acute', 'mu', 'paragraph', 'periodcentered', - 'cedilla', 'onesuperior', 'ordmasculine', 'guillemotright', 'onequarter', - 'onehalf', 'threequarters', 'questiondown', 'Agrave', 'Aacute', - 'Acircumflex', 'Atilde', 'Adieresis', 'Aring', 'AE', 'Ccedilla', - 'Egrave', 'Eacute', 'Ecircumflex', 'Edieresis', 'Igrave', 'Iacute', - 'Icircumflex', 'Idieresis', 'Eth', 'Ntilde', 'Ograve', 'Oacute', - 'Ocircumflex', 'Otilde', 'Odieresis', 'multiply', 'Oslash', 'Ugrave', - 'Uacute', 'Ucircumflex', 'Udieresis', 'Yacute', 'Thorn', 'germandbls', - 'agrave', 'aacute', 'acircumflex', 'atilde', 'adieresis', 'aring', 'ae', - 'ccedilla', 'egrave', 'eacute', 'ecircumflex', 'edieresis', 'igrave', - 'iacute', 'icircumflex', 'idieresis', 'eth', 'ntilde', 'ograve', - 'oacute', 'ocircumflex', 'otilde', 'odieresis', 'divide', 'oslash', - 'ugrave', 'uacute', 'ucircumflex', 'udieresis', 'yacute', 'thorn', - 'ydieresis' - ]); - }, - get symbolsEncoding() { - return shadow(this, 'symbolsEncoding', ['', '', '', '', '', '', '', '', '', - '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', - '', '', '', '', '', 'space', 'exclam', 'universal', 'numbersign', - 'existential', 'percent', 'ampersand', 'suchthat', 'parenleft', - 'parenright', 'asteriskmath', 'plus', 'comma', 'minus', 'period', - 'slash', 'zero', 'one', 'two', 'three', 'four', 'five', 'six', 'seven', - 'eight', 'nine', 'colon', 'semicolon', 'less', 'equal', 'greater', - 'question', 'congruent', 'Alpha', 'Beta', 'Chi', 'Delta', 'Epsilon', - 'Phi', 'Gamma', 'Eta', 'Iota', 'theta1', 'Kappa', 'Lambda', 'Mu', 'Nu', - 'Omicron', 'Pi', 'Theta', 'Rho', 'Sigma', 'Tau', 'Upsilon', 'sigma1', - 'Omega', 'Xi', 'Psi', 'Zeta', 'bracketleft', 'therefore', 'bracketright', - 'perpendicular', 'underscore', 'radicalex', 'alpha', 'beta', 'chi', - 'delta', 'epsilon', 'phi', 'gamma', 'eta', 'iota', 'phi1', 'kappa', - 'lambda', 'mu', 'nu', 'omicron', 'pi', 'theta', 'rho', 'sigma', 'tau', - 'upsilon', 'omega1', 'omega', 'xi', 'psi', 'zeta', 'braceleft', 'bar', - 'braceright', 'similar', '', '', '', '', '', '', '', '', '', '', '', '', - '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', - '', '', '', 'Euro', 'Upsilon1', 'minute', 'lessequal', 'fraction', - 'infinity', 'florin', 'club', 'diamond', 'heart', 'spade', 'arrowboth', - 'arrowleft', 'arrowup', 'arrowright', 'arrowdown', 'degree', 'plusminus', - 'second', 'greaterequal', 'multiply', 'proportional', 'partialdiff', - 'bullet', 'divide', 'notequal', 'equivalence', 'approxequal', 'ellipsis', - 'arrowvertex', 'arrowhorizex', 'carriagereturn', 'aleph', 'Ifraktur', - 'Rfraktur', 'weierstrass', 'circlemultiply', 'circleplus', 'emptyset', - 'intersection', 'union', 'propersuperset', 'reflexsuperset', 'notsubset', - 'propersubset', 'reflexsubset', 'element', 'notelement', 'angle', - 'gradient', 'registerserif', 'copyrightserif', 'trademarkserif', - 'product', 'radical', 'dotmath', 'logicalnot', 'logicaland', 'logicalor', - 'arrowdblboth', 'arrowdblleft', 'arrowdblup', 'arrowdblright', - 'arrowdbldown', 'lozenge', 'angleleft', 'registersans', 'copyrightsans', - 'trademarksans', 'summation', 'parenlefttp', 'parenleftex', - 'parenleftbt', 'bracketlefttp', 'bracketleftex', 'bracketleftbt', - 'bracelefttp', 'braceleftmid', 'braceleftbt', 'braceex', '', - 'angleright', 'integral', 'integraltp', 'integralex', 'integralbt', - 'parenrighttp', 'parenrightex', 'parenrightbt', 'bracketrighttp', - 'bracketrightex', 'bracketrightbt', 'bracerighttp', 'bracerightmid', - 'bracerightbt' - ]); - }, - get zapfDingbatsEncoding() { - return shadow(this, 'zapfDingbatsEncoding', ['', '', '', '', '', '', '', - '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', - '', '', '', '', '', '', '', 'space', 'a1', 'a2', 'a202', 'a3', 'a4', - 'a5', 'a119', 'a118', 'a117', 'a11', 'a12', 'a13', 'a14', 'a15', 'a16', - 'a105', 'a17', 'a18', 'a19', 'a20', 'a21', 'a22', 'a23', 'a24', 'a25', - 'a26', 'a27', 'a28', 'a6', 'a7', 'a8', 'a9', 'a10', 'a29', 'a30', 'a31', - 'a32', 'a33', 'a34', 'a35', 'a36', 'a37', 'a38', 'a39', 'a40', 'a41', - 'a42', 'a43', 'a44', 'a45', 'a46', 'a47', 'a48', 'a49', 'a50', 'a51', - 'a52', 'a53', 'a54', 'a55', 'a56', 'a57', 'a58', 'a59', 'a60', 'a61', - 'a62', 'a63', 'a64', 'a65', 'a66', 'a67', 'a68', 'a69', 'a70', 'a71', - 'a72', 'a73', 'a74', 'a203', 'a75', 'a204', 'a76', 'a77', 'a78', 'a79', - 'a81', 'a82', 'a83', 'a84', 'a97', 'a98', 'a99', 'a100', '', '', '', '', - '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', - '', '', '', '', '', '', '', '', '', '', '', '', 'a101', 'a102', 'a103', - 'a104', 'a106', 'a107', 'a108', 'a112', 'a111', 'a110', 'a109', 'a120', - 'a121', 'a122', 'a123', 'a124', 'a125', 'a126', 'a127', 'a128', 'a129', - 'a130', 'a131', 'a132', 'a133', 'a134', 'a135', 'a136', 'a137', 'a138', - 'a139', 'a140', 'a141', 'a142', 'a143', 'a144', 'a145', 'a146', 'a147', - 'a148', 'a149', 'a150', 'a151', 'a152', 'a153', 'a154', 'a155', 'a156', - 'a157', 'a158', 'a159', 'a160', 'a161', 'a163', 'a164', 'a196', 'a165', - 'a192', 'a166', 'a167', 'a168', 'a169', 'a170', 'a171', 'a172', 'a173', - 'a162', 'a174', 'a175', 'a176', 'a177', 'a178', 'a179', 'a193', 'a180', - 'a199', 'a181', 'a200', 'a182', '', 'a201', 'a183', 'a184', 'a197', - 'a185', 'a194', 'a198', 'a186', 'a195', 'a187', 'a188', 'a189', 'a190', - 'a191' - ]); - } + ExpertEncoding: ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', + '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', + 'space', 'exclamsmall', 'Hungarumlautsmall', '', 'dollaroldstyle', + 'dollarsuperior', 'ampersandsmall', 'Acutesmall', 'parenleftsuperior', + 'parenrightsuperior', 'twodotenleader', 'onedotenleader', 'comma', + 'hyphen', 'period', 'fraction', 'zerooldstyle', 'oneoldstyle', + 'twooldstyle', 'threeoldstyle', 'fouroldstyle', 'fiveoldstyle', + 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle', 'nineoldstyle', 'colon', + 'semicolon', 'commasuperior', 'threequartersemdash', 'periodsuperior', + 'questionsmall', '', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior', + 'esuperior', '', '', 'isuperior', '', '', 'lsuperior', 'msuperior', + 'nsuperior', 'osuperior', '', '', 'rsuperior', 'ssuperior', 'tsuperior', + '', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'parenleftinferior', '', + 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior', 'Gravesmall', + 'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', + 'Hsmall', 'Ismall', 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', + 'Osmall', 'Psmall', 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', + 'Vsmall', 'Wsmall', 'Xsmall', 'Ysmall', 'Zsmall', 'colonmonetary', + 'onefitted', 'rupiah', 'Tildesmall', '', '', '', '', '', '', '', '', '', + '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', + '', '', '', '', '', '', 'exclamdownsmall', 'centoldstyle', 'Lslashsmall', + '', '', 'Scaronsmall', 'Zcaronsmall', 'Dieresissmall', 'Brevesmall', + 'Caronsmall', '', 'Dotaccentsmall', '', '', 'Macronsmall', '', '', + 'figuredash', 'hypheninferior', '', '', 'Ogoneksmall', 'Ringsmall', + 'Cedillasmall', '', '', '', 'onequarter', 'onehalf', 'threequarters', + 'questiondownsmall', 'oneeighth', 'threeeighths', 'fiveeighths', + 'seveneighths', 'onethird', 'twothirds', '', '', 'zerosuperior', + 'onesuperior', 'twosuperior', 'threesuperior', 'foursuperior', + 'fivesuperior', 'sixsuperior', 'sevensuperior', 'eightsuperior', + 'ninesuperior', 'zeroinferior', 'oneinferior', 'twoinferior', + 'threeinferior', 'fourinferior', 'fiveinferior', 'sixinferior', + 'seveninferior', 'eightinferior', 'nineinferior', 'centinferior', + 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall', + 'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', + 'Aringsmall', 'AEsmall', 'Ccedillasmall', 'Egravesmall', 'Eacutesmall', + 'Ecircumflexsmall', 'Edieresissmall', 'Igravesmall', 'Iacutesmall', + 'Icircumflexsmall', 'Idieresissmall', 'Ethsmall', 'Ntildesmall', + 'Ogravesmall', 'Oacutesmall', 'Ocircumflexsmall', 'Otildesmall', + 'Odieresissmall', 'OEsmall', 'Oslashsmall', 'Ugravesmall', 'Uacutesmall', + 'Ucircumflexsmall', 'Udieresissmall', 'Yacutesmall', 'Thornsmall', + 'Ydieresissmall'], + MacExpertEncoding: ['', '', '', '', '', '', '', '', '', '', '', '', '', '', + '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', + 'space', 'exclamsmall', 'Hungarumlautsmall', 'centoldstyle', + 'dollaroldstyle', 'dollarsuperior', 'ampersandsmall', 'Acutesmall', + 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', + 'onedotenleader', 'comma', 'hyphen', 'period', 'fraction', 'zerooldstyle', + 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle', + 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle', + 'nineoldstyle', 'colon', 'semicolon', '', 'threequartersemdash', '', + 'questionsmall', '', '', '', '', 'Ethsmall', '', '', 'onequarter', + 'onehalf', 'threequarters', 'oneeighth', 'threeeighths', 'fiveeighths', + 'seveneighths', 'onethird', 'twothirds', '', '', '', '', '', '', 'ff', + 'fi', 'fl', 'ffi', 'ffl', 'parenleftinferior', '', 'parenrightinferior', + 'Circumflexsmall', 'hypheninferior', 'Gravesmall', 'Asmall', 'Bsmall', + 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall', 'Ismall', + 'Jsmall', 'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall', + 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall', 'Vsmall', 'Wsmall', + 'Xsmall', 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', + 'Tildesmall', '', '', 'asuperior', 'centsuperior', '', '', '', '', + 'Aacutesmall', 'Agravesmall', 'Acircumflexsmall', 'Adieresissmall', + 'Atildesmall', 'Aringsmall', 'Ccedillasmall', 'Eacutesmall', 'Egravesmall', + 'Ecircumflexsmall', 'Edieresissmall', 'Iacutesmall', 'Igravesmall', + 'Icircumflexsmall', 'Idieresissmall', 'Ntildesmall', 'Oacutesmall', + 'Ogravesmall', 'Ocircumflexsmall', 'Odieresissmall', 'Otildesmall', + 'Uacutesmall', 'Ugravesmall', 'Ucircumflexsmall', 'Udieresissmall', '', + 'eightsuperior', 'fourinferior', 'threeinferior', 'sixinferior', + 'eightinferior', 'seveninferior', 'Scaronsmall', '', 'centinferior', + 'twoinferior', '', 'Dieresissmall', '', 'Caronsmall', 'osuperior', + 'fiveinferior', '', 'commainferior', 'periodinferior', 'Yacutesmall', '', + 'dollarinferior', '', 'Thornsmall', '', 'nineinferior', 'zeroinferior', + 'Zcaronsmall', 'AEsmall', 'Oslashsmall', 'questiondownsmall', + 'oneinferior', 'Lslashsmall', '', '', '', '', '', '', 'Cedillasmall', '', + '', '', '', '', 'OEsmall', 'figuredash', 'hyphensuperior', '', '', '', '', + 'exclamdownsmall', '', 'Ydieresissmall', '', 'onesuperior', 'twosuperior', + 'threesuperior', 'foursuperior', 'fivesuperior', 'sixsuperior', + 'sevensuperior', 'ninesuperior', 'zerosuperior', '', 'esuperior', + 'rsuperior', 'tsuperior', '', '', 'isuperior', 'ssuperior', 'dsuperior', + '', '', '', '', '', 'lsuperior', 'Ogoneksmall', 'Brevesmall', + 'Macronsmall', 'bsuperior', 'nsuperior', 'msuperior', 'commasuperior', + 'periodsuperior', 'Dotaccentsmall', 'Ringsmall'], + MacRomanEncoding: ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', + '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', + 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent', + 'ampersand', 'quotesingle', 'parenleft', 'parenright', 'asterisk', 'plus', + 'comma', 'hyphen', 'period', 'slash', 'zero', 'one', 'two', 'three', + 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', + 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F', + 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', + 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 'bracketright', + 'asciicircum', 'underscore', 'grave', 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright', 'asciitilde', '', + 'Adieresis', 'Aring', 'Ccedilla', 'Eacute', 'Ntilde', 'Odieresis', + 'Udieresis', 'aacute', 'agrave', 'acircumflex', 'adieresis', 'atilde', + 'aring', 'ccedilla', 'eacute', 'egrave', 'ecircumflex', 'edieresis', + 'iacute', 'igrave', 'icircumflex', 'idieresis', 'ntilde', 'oacute', + 'ograve', 'ocircumflex', 'odieresis', 'otilde', 'uacute', 'ugrave', + 'ucircumflex', 'udieresis', 'dagger', 'degree', 'cent', 'sterling', + 'section', 'bullet', 'paragraph', 'germandbls', 'registered', 'copyright', + 'trademark', 'acute', 'dieresis', 'notequal', 'AE', 'Oslash', 'infinity', + 'plusminus', 'lessequal', 'greaterequal', 'yen', 'mu', 'partialdiff', + 'summation', 'product', 'pi', 'integral', 'ordfeminine', 'ordmasculine', + 'Omega', 'ae', 'oslash', 'questiondown', 'exclamdown', 'logicalnot', + 'radical', 'florin', 'approxequal', 'Delta', 'guillemotleft', + 'guillemotright', 'ellipsis', 'space', 'Agrave', 'Atilde', 'Otilde', 'OE', + 'oe', 'endash', 'emdash', 'quotedblleft', 'quotedblright', 'quoteleft', + 'quoteright', 'divide', 'lozenge', 'ydieresis', 'Ydieresis', 'fraction', + 'currency', 'guilsinglleft', 'guilsinglright', 'fi', 'fl', 'daggerdbl', + 'periodcentered', 'quotesinglbase', 'quotedblbase', 'perthousand', + 'Acircumflex', 'Ecircumflex', 'Aacute', 'Edieresis', 'Egrave', 'Iacute', + 'Icircumflex', 'Idieresis', 'Igrave', 'Oacute', 'Ocircumflex', 'apple', + 'Ograve', 'Uacute', 'Ucircumflex', 'Ugrave', 'dotlessi', 'circumflex', + 'tilde', 'macron', 'breve', 'dotaccent', 'ring', 'cedilla', 'hungarumlaut', + 'ogonek', 'caron'], + StandardEncoding: ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', + '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', + 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent', + 'ampersand', 'quoteright', 'parenleft', 'parenright', 'asterisk', 'plus', + 'comma', 'hyphen', 'period', 'slash', 'zero', 'one', 'two', 'three', + 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', + 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F', + 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', + 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 'bracketright', + 'asciicircum', 'underscore', 'quoteleft', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', + 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright', 'asciitilde', + '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', + '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', 'exclamdown', + 'cent', 'sterling', 'fraction', 'yen', 'florin', 'section', 'currency', + 'quotesingle', 'quotedblleft', 'guillemotleft', 'guilsinglleft', + 'guilsinglright', 'fi', 'fl', '', 'endash', 'dagger', 'daggerdbl', + 'periodcentered', '', 'paragraph', 'bullet', 'quotesinglbase', + 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', + 'perthousand', '', 'questiondown', '', 'grave', 'acute', 'circumflex', + 'tilde', 'macron', 'breve', 'dotaccent', 'dieresis', '', 'ring', 'cedilla', + '', 'hungarumlaut', 'ogonek', 'caron', 'emdash', '', '', '', '', '', '', + '', '', '', '', '', '', '', '', '', '', 'AE', '', 'ordfeminine', '', '', + '', '', 'Lslash', 'Oslash', 'OE', 'ordmasculine', '', '', '', '', '', 'ae', + '', '', '', 'dotlessi', '', '', 'lslash', 'oslash', 'oe', 'germandbls'], + WinAnsiEncoding: ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', + + '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', + 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent', + 'ampersand', 'quotesingle', 'parenleft', 'parenright', 'asterisk', 'plus', + 'comma', 'hyphen', 'period', 'slash', 'zero', 'one', 'two', 'three', + 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', + 'less', 'equal', 'greater', 'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F', + 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', + 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 'bracketright', + 'asciicircum', 'underscore', 'grave', 'a', 'b', 'c', 'd', 'e', 'f', 'g', + 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright', 'asciitilde', + 'bullet', 'Euro', 'bullet', 'quotesinglbase', 'florin', 'quotedblbase', + 'ellipsis', 'dagger', 'daggerdbl', 'circumflex', 'perthousand', 'Scaron', + 'guilsinglleft', 'OE', 'bullet', 'Zcaron', 'bullet', 'bullet', 'quoteleft', + 'quoteright', 'quotedblleft', 'quotedblright', 'bullet', 'endash', + 'emdash', 'tilde', 'trademark', 'scaron', 'guilsinglright', 'oe', 'bullet', + 'zcaron', 'Ydieresis', 'space', 'exclamdown', 'cent', 'sterling', + 'currency', 'yen', 'brokenbar', 'section', 'dieresis', 'copyright', + 'ordfeminine', 'guillemotleft', 'logicalnot', 'hyphen', 'registered', + 'macron', 'degree', 'plusminus', 'twosuperior', 'threesuperior', 'acute', + 'mu', 'paragraph', 'periodcentered', 'cedilla', 'onesuperior', + 'ordmasculine', 'guillemotright', 'onequarter', 'onehalf', 'threequarters', + 'questiondown', 'Agrave', 'Aacute', 'Acircumflex', 'Atilde', 'Adieresis', + 'Aring', 'AE', 'Ccedilla', 'Egrave', 'Eacute', 'Ecircumflex', 'Edieresis', + 'Igrave', 'Iacute', 'Icircumflex', 'Idieresis', 'Eth', 'Ntilde', 'Ograve', + 'Oacute', 'Ocircumflex', 'Otilde', 'Odieresis', 'multiply', 'Oslash', + 'Ugrave', 'Uacute', 'Ucircumflex', 'Udieresis', 'Yacute', 'Thorn', + 'germandbls', 'agrave', 'aacute', 'acircumflex', 'atilde', 'adieresis', + 'aring', 'ae', 'ccedilla', 'egrave', 'eacute', 'ecircumflex', 'edieresis', + 'igrave', 'iacute', 'icircumflex', 'idieresis', 'eth', 'ntilde', 'ograve', + 'oacute', 'ocircumflex', 'otilde', 'odieresis', 'divide', 'oslash', + 'ugrave', 'uacute', 'ucircumflex', 'udieresis', 'yacute', 'thorn', + 'ydieresis'], + symbolsEncoding: ['', '', '', '', '', '', '', '', '', '', '', '', '', '', '', + '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', + 'space', 'exclam', 'universal', 'numbersign', 'existential', 'percent', + 'ampersand', 'suchthat', 'parenleft', 'parenright', 'asteriskmath', 'plus', + 'comma', 'minus', 'period', 'slash', 'zero', 'one', 'two', 'three', 'four', + 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less', + 'equal', 'greater', 'question', 'congruent', 'Alpha', 'Beta', 'Chi', + 'Delta', 'Epsilon', 'Phi', 'Gamma', 'Eta', 'Iota', 'theta1', 'Kappa', + 'Lambda', 'Mu', 'Nu', 'Omicron', 'Pi', 'Theta', 'Rho', 'Sigma', 'Tau', + 'Upsilon', 'sigma1', 'Omega', 'Xi', 'Psi', 'Zeta', 'bracketleft', + 'therefore', 'bracketright', 'perpendicular', 'underscore', 'radicalex', + 'alpha', 'beta', 'chi', 'delta', 'epsilon', 'phi', 'gamma', 'eta', 'iota', + 'phi1', 'kappa', 'lambda', 'mu', 'nu', 'omicron', 'pi', 'theta', 'rho', + 'sigma', 'tau', 'upsilon', 'omega1', 'omega', 'xi', 'psi', 'zeta', + 'braceleft', 'bar', 'braceright', 'similar', '', '', '', '', '', '', '', + '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', + '', '', '', '', '', '', '', 'Euro', 'Upsilon1', 'minute', 'lessequal', + 'fraction', 'infinity', 'florin', 'club', 'diamond', 'heart', 'spade', + 'arrowboth', 'arrowleft', 'arrowup', 'arrowright', 'arrowdown', 'degree', + 'plusminus', 'second', 'greaterequal', 'multiply', 'proportional', + 'partialdiff', 'bullet', 'divide', 'notequal', 'equivalence', + 'approxequal', 'ellipsis', 'arrowvertex', 'arrowhorizex', 'carriagereturn', + 'aleph', 'Ifraktur', 'Rfraktur', 'weierstrass', 'circlemultiply', + 'circleplus', 'emptyset', 'intersection', 'union', 'propersuperset', + 'reflexsuperset', 'notsubset', 'propersubset', 'reflexsubset', 'element', + 'notelement', 'angle', 'gradient', 'registerserif', 'copyrightserif', + 'trademarkserif', 'product', 'radical', 'dotmath', 'logicalnot', + 'logicaland', 'logicalor', 'arrowdblboth', 'arrowdblleft', 'arrowdblup', + 'arrowdblright', 'arrowdbldown', 'lozenge', 'angleleft', 'registersans', + 'copyrightsans', 'trademarksans', 'summation', 'parenlefttp', + 'parenleftex', 'parenleftbt', 'bracketlefttp', 'bracketleftex', + 'bracketleftbt', 'bracelefttp', 'braceleftmid', 'braceleftbt', 'braceex', + '', 'angleright', 'integral', 'integraltp', 'integralex', 'integralbt', + 'parenrighttp', 'parenrightex', 'parenrightbt', 'bracketrighttp', + 'bracketrightex', 'bracketrightbt', 'bracerighttp', 'bracerightmid', + 'bracerightbt'], + zapfDingbatsEncoding: ['', '', '', '', '', '', '', '', '', '', '', '', '', '', + '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', + 'space', 'a1', 'a2', 'a202', 'a3', 'a4', 'a5', 'a119', 'a118', 'a117', + 'a11', 'a12', 'a13', 'a14', 'a15', 'a16', 'a105', 'a17', 'a18', 'a19', + 'a20', 'a21', 'a22', 'a23', 'a24', 'a25', 'a26', 'a27', 'a28', 'a6', 'a7', + 'a8', 'a9', 'a10', 'a29', 'a30', 'a31', 'a32', 'a33', 'a34', 'a35', 'a36', + 'a37', 'a38', 'a39', 'a40', 'a41', 'a42', 'a43', 'a44', 'a45', 'a46', + 'a47', 'a48', 'a49', 'a50', 'a51', 'a52', 'a53', 'a54', 'a55', 'a56', + 'a57', 'a58', 'a59', 'a60', 'a61', 'a62', 'a63', 'a64', 'a65', 'a66', + 'a67', 'a68', 'a69', 'a70', 'a71', 'a72', 'a73', 'a74', 'a203', 'a75', + 'a204', 'a76', 'a77', 'a78', 'a79', 'a81', 'a82', 'a83', 'a84', 'a97', + 'a98', 'a99', 'a100', '', '', '', '', '', '', '', '', '', '', '', '', '', + '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', + '', '', 'a101', 'a102', 'a103', 'a104', 'a106', 'a107', 'a108', 'a112', + 'a111', 'a110', 'a109', 'a120', 'a121', 'a122', 'a123', 'a124', 'a125', + 'a126', 'a127', 'a128', 'a129', 'a130', 'a131', 'a132', 'a133', 'a134', + 'a135', 'a136', 'a137', 'a138', 'a139', 'a140', 'a141', 'a142', 'a143', + 'a144', 'a145', 'a146', 'a147', 'a148', 'a149', 'a150', 'a151', 'a152', + 'a153', 'a154', 'a155', 'a156', 'a157', 'a158', 'a159', 'a160', 'a161', + 'a163', 'a164', 'a196', 'a165', 'a192', 'a166', 'a167', 'a168', 'a169', + 'a170', 'a171', 'a172', 'a173', 'a162', 'a174', 'a175', 'a176', 'a177', + 'a178', 'a179', 'a193', 'a180', 'a199', 'a181', 'a200', 'a182', '', 'a201', + 'a183', 'a184', 'a197', 'a185', 'a194', 'a198', 'a186', 'a195', 'a187', + 'a188', 'a189', 'a190', 'a191'] }; /** From ffa0e082a4338d3cb92bf70935dfae7d68e586e1 Mon Sep 17 00:00:00 2001 From: Adil Allawi Date: Sun, 12 Feb 2012 22:31:30 +0000 Subject: [PATCH 04/40] Scale text divs instead of letterspacing --- web/viewer.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/web/viewer.js b/web/viewer.js index d5162a194..9cf97a751 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -1041,12 +1041,11 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv) { textLayerDiv.appendChild(textDiv); if (textDiv.dataset.textLength > 1) { // avoid div by zero - // Adjust div width (via letterSpacing) to match canvas text + // Adjust div width to match canvas text // Due to the .offsetWidth calls, this is slow // This needs to come after appending to the DOM - textDiv.style.letterSpacing = - ((textDiv.dataset.canvasWidth - textDiv.offsetWidth) / - (textDiv.dataset.textLength - 1)) + 'px'; + textDiv.style.MozTransform = 'scale(' + textDiv.dataset.canvasWidth/textDiv.offsetWidth + ',1)'; + textDiv.style.MozTransformOrigin = '0% 0%'; } } // textLength > 0 } From 2bc708305d361b56e6e9b079c73ef199cff6c917 Mon Sep 17 00:00:00 2001 From: Adil Allawi Date: Mon, 13 Feb 2012 02:10:41 +0000 Subject: [PATCH 05/40] Fix up bidi algorithm, and set direction of div to match text direction --- src/bidi.js | 494 +++++++++++++++++++++++++++++--------------------- src/canvas.js | 7 +- web/viewer.js | 3 +- 3 files changed, 290 insertions(+), 214 deletions(-) diff --git a/src/bidi.js b/src/bidi.js index cbb668764..63ca2dde2 100644 --- a/src/bidi.js +++ b/src/bidi.js @@ -1,237 +1,327 @@ -function bidi(doc, str, startLevel) { - if (str.length == 0) return str; +var baseTypes = [ + "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "S", "B", "S", "WS", + "B", "BN", "BN", /*U+000*/ + "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "B", + "B", "B", "S", /*U+001*/ + "WS", "ON", "ON", "ET", "ET", "ET", "ON", "ON", "ON", "ON", "ON", "ON", "CS", + "ON", "CS", "ON", /*U+002*/ + "EN", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "ON", "ON", "ON", + "ON", "ON", "ON", /*U+003*/ + "ON", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", + "L", /*U+004*/ + "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "ON", "ON", "ON", "ON", + "ON", /*U+005*/ + "ON", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", + "L", /*U+006*/ + "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "ON", "ON", "ON", "ON", + "BN", /*U+007*/ + "BN", "BN", "BN", "BN", "BN", "B", "BN", "BN", "BN", "BN", "BN", "BN", "BN", + "BN", "BN", "BN", /*U+008*/ + "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", + "BN", "BN", "BN", /*U+009*/ + "CS", "ON", "ET", "ET", "ET", "ET", "ON", "ON", "ON", "ON", "L", "ON", "ON", + "ON", "ON", "ON", /*U+00a*/ + "ET", "ET", "EN", "EN", "ON", "L", "ON", "ON", "ON", "EN", "L", "ON", "ON", + "ON", "ON", "ON", /*U+00b*/ + "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", + "L", /*U+00c*/ + "L", "L", "L", "L", "L", "L", "L", "ON", "L", "L", "L", "L", "L", "L", "L", + "L", /*U+00d*/ + "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", + "L", /*U+00e*/ + "L", "L", "L", "L", "L", "L", "L", "ON", "L", "L", "L", "L", "L", "L", "L", + "L" /*U+00f*/ +]; - var chars= new Array(str.length); - var levels = new Array(str.length); - var types = new Array(str.length); - var oldtypes = new Array(str.length); +var arabicTypes = [ + "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "CS", + "AL", "ON", "ON", //60 + "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "AL", "AL", "AL", "AL", "AL", "AL", + "AL", "AL", "AL", "AL", //61 + "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", + "AL", "AL", "AL", //62 + "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", + "AL", "AL", "AL", //63 + "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "NSM", + "NSM", "NSM", "NSM", "NSM", //64 + "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "AL", "AL", + "AL", "AL", "AL", "AL", "AL", //65 + "AN", "AN", "AN", "AN", "AN", "AN", "AN", "AN", "AN", "AN", "ET", "AN", "AN", + "AL", "AL", "AL", //66 + "NSM", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", + "AL", "AL", "AL", //67 + "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", + "AL", "AL", "AL", //68 + "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", + "AL", "AL", "AL", //69 + "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", + "AL", "AL", "AL", //6a + "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", + "AL", "AL", "AL", //6b + "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", + "AL", "AL", "AL", //6c + "AL", "AL", "AL", "AL", "AL", "AL", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", + "NSM", "NSM", "NSM", "NSM", //6d + "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "ON", "NSM", + "NSM", "NSM", "NSM", "AL", "AL", //6e + "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", + "AL", "AL", "AL" //6f +]; -// get types, fill arrays +function bidi(text, startLevel) { + var str = text.str; + var strLength = str.length; + if (strLength == 0) return str; - for (var i = 0; i < str.length; ++i) { + // 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) { var c = str.charAt(i); chars[i] = c; - levels[i] = startLevel; var t = "L"; - if ('\u0600' <= c && c <= '\u06ff') t = "AL"; - else if ('0' <= c && c <= '9') t = (c <= 5) ? "EN" : "AN"; - else if (c == '.' || c == ',' || c == ':') t = "CS"; - else if (c == '/') t = "ES"; - else if (c == '#' || c == '$' || c == '%' || c == '+' || c == '-') t = "ET" - else if (c == '>') t = "L" - else if (c == '<') t = "R" + if (c <= '\u00ff') + t = baseTypes[c.charCodeAt(0)]; + else if ('\u0590' <= c && c <= '\u05f4') + t = "R"; + else if ('\u0600' <= c && c <= '\u06ff') + t = arabicTypes[c.charCodeAt(0) & 0xff]; + else if ('\u0700' <= c && c <= '\u08AC') + t = "AL"; + + if (t == "R" || t == "AL" || t == "AN") + numBidi++; oldtypes[i] = types[i] = t; } - var diffChars = new Array(str.length); - var diffLevels = new Array(str.length); - var diffTypes = new Array(str.length); + // 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; + } + else if (startLevel == -1) { + if ((strLength / numBidi) < 0.3) { + text.direction = 'ltr'; + startLevel = 0; + } else { + text.direction = 'rtl'; + startLevel = 1; + } + } - showArray(doc, "Chars: ", chars, diffChars); - showArray(doc, "Types: ", types, diffTypes); - //alert("chars: " + chars.join(",")); - //alert("types: " + types.join(",")); - //alert("levels: " + levels.join(",")); + var levels = new Array(strLength); -/* -X1-X10: skip most of this, since we are NOT doing the embeddings. -*/ + 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. -*/ + /* + 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 < types.length; ++i) { + for (var i = 0; i < strLength; ++i) { if (types[i] == "NSM") types[i] = lastType; else lastType = types[i]; } - showArray(doc, "W1: ", types, diffTypes); - -/* -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. -*/ + /* + 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 < types.length; ++i) { + 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; + if (t == "EN") + types[i] = (lastType == "AL") ? "AN" : "EN"; + else if (t == "R" || t == "L" || t == "AL") + lastType = t; } - showArray(doc, "W2: ", types, diffTypes); + /* + W3. Change all ALs to R. + */ -/* -W3. Change all ALs to R. -*/ - - for (var i = 0; i < types.length; ++i) { + for (var i = 0; i < strLength; ++i) { var t = types[i]; if (t == "AL") types[i] = "R"; } - showArray(doc, "W3: ", types, diffTypes); + /* + 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: + */ -/* -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 < types.length - 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]; + 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]; } - showArray(doc, "W4: ", types, diffTypes); + /* + W5. A sequence of European terminators adjacent to European numbers changes + to all European numbers: + */ -/* -W5. A sequence of European terminators adjacent to European numbers changes to all European numbers: -*/ - - for (var i = 0; i < types.length; ++i) { + for (var i = 0; i < strLength; ++i) { if (types[i] == "EN") { // do before - for (j = i-1; j >= 0; --j) { - if (types[j] == "ET") types[j] = "EN"; + for (j = i - 1; j >= 0; --j) { + if (types[j] == "ET") + types[j] = "EN"; else break; } // do after - for (j = i+1; j < types.length; --j) { - if (types[j] == "ET") types[j] = "EN"; + for (j = i + 1; j < strLength; --j) { + if (types[j] == "ET") + types[j] = "EN"; else break; } } } - showArray(doc, "W5: ", types, diffTypes); + /* + W6. Otherwise, separators and terminators change to Other Neutral: + */ - -/* -W6. Otherwise, separators and terminators change to Other Neutral: -*/ - - for (var i = 0; i < types.length; ++i) { + for (var i = 0; i < strLength; ++i) { var t = types[i]; - if (t == "ES" || t == "ET" || t == "CS") types[i] = "ON"; + if (t == "WS" || t == "ES" || t == "ET" || t == "CS") + types[i] = "ON"; } - showArray(doc, "W6: ", types, diffTypes); - -/* -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. -*/ + /* + 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 < types.length; ++i) { + 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; + if (t == "EN") + types[i] = (lastType == "L") ? "L" : "EN"; + else if (t == "R" || t == "L") + lastType = t; } - showArray(doc, "W7: ", types, diffTypes); + /* + 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. + */ -/* -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 < types.length; ++i) { + for (var i = 0; i < strLength; ++i) { if (types[i] == "ON") { - var end = findUnequal(types, i+1, "ON"); + var end = findUnequal(types, i + 1, "ON"); var before = sor; - if (i > 0) before = types[i-1]; + if (i > 0) + before = types[i - 1]; var after = eor; - if (end+1 < types.length) after = types[end+1]; - if (before != "L") before = "R"; - if (after != "L") after = "R"; - if (before == after) setValues(types, i, end, before); + 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) } } - showArray(doc, "N1: ", types, diffTypes); + /* + N2. Any remaining neutrals take the embedding direction. + */ -/* -N2. Any remaining neutrals take the embedding direction. -*/ - - for (var i = 0; i < types.length; ++i) { - if (types[i] == "ON") types[i] = e; + for (var i = 0; i < strLength; ++i) { + if (types[i] == "ON") + types[i] = e; } - showArray(doc, "N2: ", types, diffTypes); + /* + 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. + */ -/* -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. -*/ - - showArray(doc, "Levels: ", levels, diffLevels); - - for (var i = 0; i < types.length; ++i) { + 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") { + } + else if (t == "AN" || t == "EN") { levels[i] += 2; } - } else { // isOdd, so + } + else { // isOdd, so if (t == "L" || t == "AN" || t == "EN") { levels[i] += 1; } } } - showArray(doc, "I1/2: ", levels, diffLevels); + /* + 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. + */ -/* -L1. On each line, reset the embedding level of the following characters to the paragraph embedding level: + //dont bother as text is only single line -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. -*/ - -/* - boolean atEnd = true; - for (var i = levels.length - 1; i >= 0; --i) { - var t = oldTypes[i]; - if (t == "B" || t == "S") { - levels[i] = startLevel; - atEnd = true; - } else if (atEnd && t == "WS") { - levels[i] = startLevel; - } - } -*/ - - showArray(doc, "L1: ", levels, diffLevels); - - -/* -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. -*/ + /* + 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; - // alert("Levels: " + levels.join(",")); for (var i = 0; i < levels.length; ++i) { var level = levels[i]; - if (highestLevel < level) highestLevel = level; - if (lowestOddLevel > level && isOdd(level)) lowestOddLevel = level; + if (highestLevel < level) + highestLevel = level; + if (lowestOddLevel > level && isOdd(level)) + lowestOddLevel = level; } - // alert("highestLevel: " + highestLevel + "; lowestOddLevel: " + lowestOddLevel); // now reverse between those limits @@ -244,49 +334,44 @@ L2. From the highest level found in the text to the lowest odd level on each lin reverseValues(chars, start, i); start = -1; } - } else if (start < 0) { + } + else if (start < 0) { start = i; } } if (start >= 0) { reverseValues(chars, start, levels.length); } - - showArray(doc, "L2 (" + level + "):", chars, diffChars); - } + /* + 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. + */ -/* -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 -// 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. + */ - showArray(doc, "L3: ", chars, diffChars); + //dont mirror as this is already in the pdf - -/* -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. -*/ - - for (var i = 0; i < chars.length; ++i) { - chars[i] = mirrorGlyphs(chars[i]); - } - - showArray(doc, "L4: ", chars, diffChars); - -// Finally, return string + // Finally, return string var result = ""; for (var i = 0; i < chars.length; ++i) { var ch = chars[i]; - if (ch != '<' && ch != '>') result += ch; + if (ch != '<' && ch != '>') + result += ch; } return result; } - // UTILITIES function isOdd(i) { @@ -312,57 +397,50 @@ function setValues(arr, start, end, value) { } function reverseValues(arr, start, end) { - // alert("reverse: " + arr.join(",") + "; " + start + "; " + end); - for (var i = start, j = end-1; i < j; ++i, --j) { + for (var i = start, j = end - 1; i < j; ++i, --j) { var temp = arr[i]; arr[i] = arr[j]; arr[j] = temp; } - // alert("reverse: " + chars.join(",")); } 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 -*/ + /* + # 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 '�': return '�'; - case '�': return '�'; - default: return c; + case '(': + return ')'; + case ')': + return '('; + case '<': + return '>'; + case '>': + return '<'; + case ']': + return '['; + case '[': + return ']'; + case '}': + return '{'; + case '{': + return '}'; + case '�': + return '�'; + case '�': + return '�'; + default: + return c; } } -function showArray(doc, title, arr, diffarr) { - if (doc == null) - return; - var haveDiff = false; - for (var i = 0; i < arr.length; ++i) { - if (arr[i] != diffarr[i]) { - haveDiff = true; - diffarr[i] = arr[i]; - } - } - if (haveDiff) { - doc.writeln("", title, "", arr.join(""), ""); - } -} - - diff --git a/src/canvas.js b/src/canvas.js index 1106ba328..499a26446 100644 --- a/src/canvas.js +++ b/src/canvas.js @@ -824,18 +824,15 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { text.str += shownText.str; } text.canvasWidth += shownText.canvasWidth; - text.length += e.length; + text.length += shownText.length; } } else { malformed('TJ array element ' + e + ' is not string or num'); } } - if (textSelection) { - if (isRTLRangeFor(text.str.charCodeAt(0))) //first char is rtl - text.str = bidi(null, text.str, 1); + if (textSelection) this.textLayer.appendText(text, font.loadedName, fontSize); - } }, nextLineShowText: function canvasGraphicsNextLineShowText(text) { this.nextLine(); diff --git a/web/viewer.js b/web/viewer.js index 9cf97a751..bbb192a9a 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -1084,7 +1084,8 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv) { textDiv.style.fontSize = fontHeight + 'px'; textDiv.style.left = text.geom.x + 'px'; textDiv.style.top = (text.geom.y - fontHeight) + 'px'; - textDiv.textContent = text.str; + textDiv.textContent = bidi(text, -1); + textDiv.dir = text.direction; textDiv.dataset.textLength = text.length; this.textDivs.push(textDiv); }; From 6902ba51fac5237d1a9bff5ad33ca3b23f093087 Mon Sep 17 00:00:00 2001 From: Adil Allawi Date: Mon, 13 Feb 2012 14:27:59 +0000 Subject: [PATCH 06/40] Properly support custom CSS properties Fix comment in bidi --- src/bidi.js | 2 +- src/util.js | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ web/viewer.js | 6 ++++-- 3 files changed, 56 insertions(+), 3 deletions(-) diff --git a/src/bidi.js b/src/bidi.js index 63ca2dde2..09f8dd7c6 100644 --- a/src/bidi.js +++ b/src/bidi.js @@ -359,7 +359,7 @@ function bidi(text, startLevel) { directionality of that character is R. */ - //dont mirror as this is already in the pdf + //dont mirror as characters are already mirrored in the pdf // Finally, return string diff --git a/src/util.js b/src/util.js index f00fcd1ce..3af6125e3 100644 --- a/src/util.js +++ b/src/util.js @@ -118,6 +118,57 @@ var Util = (function UtilClosure() { return Util; })(); +// optimised CSS custom property getter/setter +var CustomStyle = (function CustomStyleClosure() { + + // As noted on: http://www.zachstronaut.com/posts/2009/02/17/ + // animate-css-transforms-firefox-webkit.html + // in some versions of IE9 it is critical that ms appear in this list + // before Moz + var prefixes = ['ms', 'Moz', 'Webkit', 'O']; + var _cache = { }; + + function CustomStyle() { + } + + CustomStyle.getProp = function get(propName, element) { + // check cache only when no element is given + if (arguments.length == 1 && typeof _cache[propName] == 'string') { + return _cache[propName]; + } + + element = element || document.documentElement; + var style = element.style, prefixed, uPropName; + + // test standard property first + if (typeof style[propName] == 'string') { + return (_cache[propName] = propName); + } + + // capitalize + uPropName = propName.charAt(0).toUpperCase() + propName.slice(1); + + // test vendor specific properties + for (var i = 0, l = prefixes.length; i < l; i++) { + prefixed = prefixes[i] + uPropName; + if (typeof style[prefixed] == 'string') { + return (_cache[propName] = prefixed); + } + } + + //if all fails then set to undefined + return (_cache[propName] = 'undefined'); + } + + CustomStyle.setProp = function set(propName, element, str) { + var prop = this.getProp(propName); + if (prop != 'undefined') + element.style[prop] = str; + } + + return CustomStyle; +})(); + var PDFStringTranslateTable = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2D8, 0x2C7, 0x2C6, 0x2D9, 0x2DD, 0x2DB, 0x2DA, 0x2DC, 0, 0, 0, 0, 0, 0, 0, diff --git a/web/viewer.js b/web/viewer.js index bbb192a9a..ff857a943 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -1044,8 +1044,10 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv) { // Adjust div width to match canvas text // Due to the .offsetWidth calls, this is slow // This needs to come after appending to the DOM - textDiv.style.MozTransform = 'scale(' + textDiv.dataset.canvasWidth/textDiv.offsetWidth + ',1)'; - textDiv.style.MozTransformOrigin = '0% 0%'; + CustomStyle.setProp('transform' , textDiv, 'scale(' + + textDiv.dataset.canvasWidth/textDiv.offsetWidth + + ',1)'); + CustomStyle.setProp('transformOrigin' , textDiv, '0% 0%'); } } // textLength > 0 } From b50cf76ab589fa1bb4a52014470274f887e067cd Mon Sep 17 00:00:00 2001 From: Adil Allawi Date: Mon, 13 Feb 2012 14:38:44 +0000 Subject: [PATCH 07/40] Properly integrate new file bidi.js --- Makefile | 1 + src/worker_loader.js | 3 ++- test/test_slave.html | 1 + test/unit/jsTestDriver.conf | 1 + web/viewer.html | 2 +- 5 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 043cb7fb5..f515bf717 100644 --- a/Makefile +++ b/Makefile @@ -38,6 +38,7 @@ PDF_JS_FILES = \ worker.js \ ../external/jpgjs/jpg.js \ jpx.js \ + bidi.js \ $(NULL) # make server diff --git a/src/worker_loader.js b/src/worker_loader.js index fc3d7a5f7..69eb1414f 100644 --- a/src/worker_loader.js +++ b/src/worker_loader.js @@ -24,7 +24,8 @@ var files = [ 'stream.js', 'worker.js', '../external/jpgjs/jpg.js', - 'jpx.js' + 'jpx.js', + 'bidi.js' ]; // Load all the files. diff --git a/test/test_slave.html b/test/test_slave.html index a2682a5a6..50bb067e1 100644 --- a/test/test_slave.html +++ b/test/test_slave.html @@ -23,6 +23,7 @@ + - + From c8e88def836588302f6ffc4742cce5d378041b5f Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Mon, 13 Feb 2012 20:28:36 -0600 Subject: [PATCH 08/40] Light bidi fixes --- src/bidi.js | 219 ++++++++++++++++++++++++---------------------------- 1 file changed, 100 insertions(+), 119 deletions(-) diff --git a/src/bidi.js b/src/bidi.js index 09f8dd7c6..025847ea9 100644 --- a/src/bidi.js +++ b/src/bidi.js @@ -1,71 +1,54 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */ + +'use strict'; + +// 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", /*U+000*/ - "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "B", - "B", "B", "S", /*U+001*/ - "WS", "ON", "ON", "ET", "ET", "ET", "ON", "ON", "ON", "ON", "ON", "ON", "CS", - "ON", "CS", "ON", /*U+002*/ - "EN", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "EN", "ON", "ON", "ON", - "ON", "ON", "ON", /*U+003*/ - "ON", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", - "L", /*U+004*/ - "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "ON", "ON", "ON", "ON", - "ON", /*U+005*/ - "ON", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", - "L", /*U+006*/ - "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "ON", "ON", "ON", "ON", - "BN", /*U+007*/ - "BN", "BN", "BN", "BN", "BN", "B", "BN", "BN", "BN", "BN", "BN", "BN", "BN", - "BN", "BN", "BN", /*U+008*/ - "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", "BN", - "BN", "BN", "BN", /*U+009*/ - "CS", "ON", "ET", "ET", "ET", "ET", "ON", "ON", "ON", "ON", "L", "ON", "ON", - "ON", "ON", "ON", /*U+00a*/ - "ET", "ET", "EN", "EN", "ON", "L", "ON", "ON", "ON", "EN", "L", "ON", "ON", - "ON", "ON", "ON", /*U+00b*/ - "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", - "L", /*U+00c*/ - "L", "L", "L", "L", "L", "L", "L", "ON", "L", "L", "L", "L", "L", "L", "L", - "L", /*U+00d*/ - "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", "L", - "L", /*U+00e*/ - "L", "L", "L", "L", "L", "L", "L", "ON", "L", "L", "L", "L", "L", "L", "L", - "L" /*U+00f*/ + '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", //60 - "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "AL", "AL", "AL", "AL", "AL", "AL", - "AL", "AL", "AL", "AL", //61 - "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", - "AL", "AL", "AL", //62 - "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", - "AL", "AL", "AL", //63 - "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "NSM", - "NSM", "NSM", "NSM", "NSM", //64 - "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "AL", "AL", - "AL", "AL", "AL", "AL", "AL", //65 - "AN", "AN", "AN", "AN", "AN", "AN", "AN", "AN", "AN", "AN", "ET", "AN", "AN", - "AL", "AL", "AL", //66 - "NSM", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", - "AL", "AL", "AL", //67 - "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", - "AL", "AL", "AL", //68 - "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", - "AL", "AL", "AL", //69 - "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", - "AL", "AL", "AL", //6a - "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", - "AL", "AL", "AL", //6b - "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", - "AL", "AL", "AL", //6c - "AL", "AL", "AL", "AL", "AL", "AL", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", - "NSM", "NSM", "NSM", "NSM", //6d - "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "NSM", "ON", "NSM", - "NSM", "NSM", "NSM", "AL", "AL", //6e - "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", "AL", - "AL", "AL", "AL" //6f + '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 bidi(text, startLevel) { @@ -81,23 +64,23 @@ function bidi(text, startLevel) { var numBidi = 0; for (var i = 0; i < strLength; ++i) { - var c = str.charAt(i); - chars[i] = c; + chars[i] = str.charAt(i); - var t = "L"; - if (c <= '\u00ff') - t = baseTypes[c.charCodeAt(0)]; - else if ('\u0590' <= c && c <= '\u05f4') - t = "R"; - else if ('\u0600' <= c && c <= '\u06ff') - t = arabicTypes[c.charCodeAt(0) & 0xff]; - else if ('\u0700' <= c && c <= '\u08AC') - t = "AL"; + 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 (t == "R" || t == "AL" || t == "AN") + if (charType == 'R' || charType == 'AL' || charType == 'AN') numBidi++; - oldtypes[i] = types[i] = t; + oldtypes[i] = types[i] = charType; } // detect the bidi method @@ -132,7 +115,7 @@ function bidi(text, startLevel) { X1-X10: skip most of this, since we are NOT doing the embeddings. */ - var e = isOdd(startLevel) ? "R" : "L"; + var e = isOdd(startLevel) ? 'R' : 'L'; var sor = e; var eor = sor; @@ -144,7 +127,7 @@ function bidi(text, startLevel) { var lastType = sor; for (var i = 0; i < strLength; ++i) { - if (types[i] == "NSM") types[i] = lastType; + if (types[i] == 'NSM') types[i] = lastType; else lastType = types[i]; } @@ -157,9 +140,9 @@ function bidi(text, startLevel) { 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") + if (t == 'EN') + types[i] = (lastType == 'AL') ? 'AN' : 'EN'; + else if (t == 'R' || t == 'L' || t == 'AL') lastType = t; } @@ -169,7 +152,7 @@ function bidi(text, startLevel) { for (var i = 0; i < strLength; ++i) { var t = types[i]; - if (t == "AL") types[i] = "R"; + if (t == 'AL') types[i] = 'R'; } /* @@ -179,9 +162,9 @@ function bidi(text, startLevel) { */ 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") && + 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]; } @@ -192,17 +175,17 @@ function bidi(text, startLevel) { */ for (var i = 0; i < strLength; ++i) { - if (types[i] == "EN") { + if (types[i] == 'EN') { // do before - for (j = i - 1; j >= 0; --j) { - if (types[j] == "ET") - types[j] = "EN"; + for (var j = i - 1; j >= 0; --j) { + if (types[j] == 'ET') + types[j] = 'EN'; else break; } // do after - for (j = i + 1; j < strLength; --j) { - if (types[j] == "ET") - types[j] = "EN"; + for (var j = i + 1; j < strLength; --j) { + if (types[j] == 'ET') + types[j] = 'EN'; else break; } } @@ -214,8 +197,8 @@ function bidi(text, startLevel) { for (var i = 0; i < strLength; ++i) { var t = types[i]; - if (t == "WS" || t == "ES" || t == "ET" || t == "CS") - types[i] = "ON"; + if (t == 'WS' || t == 'ES' || t == 'ET' || t == 'CS') + types[i] = 'ON'; } /* @@ -227,9 +210,9 @@ function bidi(text, startLevel) { 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") + if (t == 'EN') + types[i] = (lastType == 'L') ? 'L' : 'EN'; + else if (t == 'R' || t == 'L') lastType = t; } @@ -241,18 +224,18 @@ function bidi(text, startLevel) { */ for (var i = 0; i < strLength; ++i) { - if (types[i] == "ON") { - var end = findUnequal(types, i + 1, "ON"); + 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 != '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) @@ -264,7 +247,7 @@ function bidi(text, startLevel) { */ for (var i = 0; i < strLength; ++i) { - if (types[i] == "ON") + if (types[i] == 'ON') types[i] = e; } @@ -278,15 +261,15 @@ function bidi(text, startLevel) { for (var i = 0; i < strLength; ++i) { var t = types[i]; if (isEven(levels[i])) { - if (t == "R") { + if (t == 'R') { levels[i] += 1; } - else if (t == "AN" || t == "EN") { + else if (t == 'AN' || t == 'EN') { levels[i] += 2; } } else { // isOdd, so - if (t == "L" || t == "AN" || t == "EN") { + if (t == 'L' || t == 'AN' || t == 'EN') { levels[i] += 1; } } @@ -303,7 +286,7 @@ function bidi(text, startLevel) { of the line. */ - //dont bother as text is only single 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 @@ -315,7 +298,7 @@ function bidi(text, startLevel) { var highestLevel = -1; var lowestOddLevel = 99; - for (var i = 0; i < levels.length; ++i) { + for (var i = 0, ii = levels.length; i < ii; ++i) { var level = levels[i]; if (highestLevel < level) highestLevel = level; @@ -359,12 +342,12 @@ function bidi(text, startLevel) { directionality of that character is R. */ - //dont mirror as characters are already mirrored in the pdf + // don't mirror as characters are already mirrored in the pdf // Finally, return string - var result = ""; - for (var i = 0; i < chars.length; ++i) { + var result = ''; + for (var i = 0, ii = chars.length; i < ii; ++i) { var ch = chars[i]; if (ch != '<' && ch != '>') result += ch; @@ -372,8 +355,6 @@ function bidi(text, startLevel) { return result; } -// UTILITIES - function isOdd(i) { return (i & 1) != 0; } @@ -435,10 +416,10 @@ function mirrorGlyphs(c) { return '{'; case '{': return '}'; - case '�': - return '�'; - case '�': - return '�'; + case '\u00AB': + return '\u00BB'; + case '\u00BB': + return '\u00AB'; default: return c; } From 16bd59edf0f5e34a4de6271ba070d76845188679 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Tue, 14 Feb 2012 23:00:09 -0600 Subject: [PATCH 09/40] Separating fontChar and unicode --- src/fonts.js | 77 +++++++++++++++++++++++++++++----------------------- 1 file changed, 43 insertions(+), 34 deletions(-) diff --git a/src/fonts.js b/src/fonts.js index 45c83b74d..ea5f20d78 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -833,12 +833,7 @@ var Font = (function FontClosure() { else this.rebuildToUnicode(properties); - this.toUnicodeOriginal = this.toUnicode.slice(); - for (var i = 0, j = 0xE000; i < this.toUnicode.length; i++) { - if (typeof this.toUnicode[i] == 'number') - break; - this.toUnicode[i] = j++; - } + this.toFontChar = this.buildToFontChar(this.toUnicode); if (!file) { // The file data is not specified. Trying to fix the font name @@ -1794,7 +1789,7 @@ var Font = (function FontClosure() { var unassignedUnicodeItems = []; for (var i = 1; i < numGlyphs; i++) { var cid = gidToCidMap[i] || i; - var unicode = this.toUnicode[cid]; + var unicode = this.toFontChar[cid]; if (!unicode || typeof unicode !== 'number' || isSpecialUnicode(unicode) || unicode in usedUnicodes) { unassignedUnicodeItems.push(i); @@ -1815,7 +1810,7 @@ var Font = (function FontClosure() { if (unusedUnicode >= kCmapGlyphOffset + kSizeOfGlyphArea) break; var unicode = unusedUnicode++; - this.toUnicode[cid] = unicode; + this.toFontChar[cid] = unicode; usedUnicodes[unicode] = true; glyphs.push({ unicode: unicode, code: cid }); ids.push(i); @@ -1826,9 +1821,9 @@ var Font = (function FontClosure() { var glyphs = cmapTable.glyphs; var ids = cmapTable.ids; var hasShortCmap = !!cmapTable.hasShortCmap; - var toUnicode = this.toUnicode; + var toFontChar = this.toFontChar; - if (toUnicode && toUnicode.length > 0) { + if (toFontChar && toFontChar.length > 0) { // checking if cmap is just identity map var isIdentity = true; for (var i = 0, ii = glyphs.length; i < ii; i++) { @@ -1837,11 +1832,11 @@ var Font = (function FontClosure() { break; } } - // if it is, replacing with meaningful toUnicode values + // if it is, replacing with meaningful toFontChar values if (isIdentity) { var usedUnicodes = [], unassignedUnicodeItems = []; for (var i = 0, ii = glyphs.length; i < ii; i++) { - var unicode = toUnicode[i + 1]; + var unicode = toFontChar[i + 1]; if (!unicode || typeof unicode !== 'number' || unicode in usedUnicodes) { unassignedUnicodeItems.push(i); continue; @@ -1856,11 +1851,11 @@ var Font = (function FontClosure() { unusedUnicode++; var cid = i + 1; // override only if unicode mapping is not specified - if (!(cid in toUnicode)) - toUnicode[cid] = unusedUnicode; + if (!(cid in toFontChar)) + toFontChar[cid] = unusedUnicode; glyphs[i].unicode = unusedUnicode++; } - this.useToUnicode = true; + this.useToFontChar = true; } } @@ -1990,12 +1985,12 @@ var Font = (function FontClosure() { properties.baseEncoding = encoding; } if (false && properties.subtype == 'CIDFontType0C') { - var toUnicode = []; + var toFontChar = []; for (var i = 0; i < charstrings.length; ++i) { var charstring = charstrings[i]; - toUnicode[charstring.code] = charstring.unicode; + toFontChar[charstring.code] = charstring.unicode; } - this.toUnicode = toUnicode; + this.toFontChar = toFontChar; } var fields = { @@ -2090,6 +2085,18 @@ var Font = (function FontClosure() { return stringToArray(otf.file); }, + buildToFontChar: function font_buildToFontChar(toUnicode) { + var result = []; + var unusedUnicode = kCmapGlyphOffset; + for (var i = 0, ii = toUnicode.length; i < ii; i++) { + var unicode = toUnicode[i]; + var fontCharCode = typeof unicode === 'object' ? unusedUnicode++ : + unicode; + result.push(fontCharCode); + } + return result; + }, + rebuildToUnicode: function font_rebuildToUnicode(properties) { var firstChar = properties.firstChar, lastChar = properties.lastChar; var map = []; @@ -2222,7 +2229,7 @@ var Font = (function FontClosure() { }, charToGlyph: function fonts_charToGlyph(charcode) { - var unicode, width, codeIRQueue; + var fontCharCode, width, codeIRQueue; var width = this.widths[charcode]; @@ -2230,38 +2237,39 @@ var Font = (function FontClosure() { case 'CIDFontType0': if (this.noUnicodeAdaptation) { width = this.widths[this.unicodeToCID[charcode] || charcode]; - unicode = mapPrivateUseChars(charcode); + fontCharCode = mapPrivateUseChars(charcode); break; } - unicode = this.toUnicode[charcode] || charcode; + fontCharCode = this.toFontChar[charcode] || charcode; break; case 'CIDFontType2': if (this.noUnicodeAdaptation) { width = this.widths[this.unicodeToCID[charcode] || charcode]; - unicode = mapPrivateUseChars(charcode); + fontCharCode = mapPrivateUseChars(charcode); break; } - unicode = this.toUnicode[charcode] || charcode; + fontCharCode = this.toFontChar[charcode] || charcode; break; case 'Type1': var glyphName = this.differences[charcode] || this.encoding[charcode]; if (!isNum(width)) width = this.widths[glyphName]; if (this.noUnicodeAdaptation) { - unicode = mapPrivateUseChars(GlyphsUnicode[glyphName] || charcode); + fontCharCode = mapPrivateUseChars(GlyphsUnicode[glyphName] || + charcode); break; } - unicode = this.glyphNameMap[glyphName] || + fontCharCode = this.glyphNameMap[glyphName] || GlyphsUnicode[glyphName] || charcode; break; case 'Type3': var glyphName = this.differences[charcode] || this.encoding[charcode]; codeIRQueue = this.charProcIRQueues[glyphName]; - unicode = charcode; + fontCharCode = charcode; break; case 'TrueType': - if (this.useToUnicode) { - unicode = this.toUnicode[charcode] || charcode; + if (this.useToFontChar) { + fontCharCode = this.toFontChar[charcode] || charcode; break; } var glyphName = this.differences[charcode] || this.encoding[charcode]; @@ -2270,16 +2278,17 @@ var Font = (function FontClosure() { if (!isNum(width)) width = this.widths[glyphName]; if (this.noUnicodeAdaptation) { - unicode = GlyphsUnicode[glyphName] || charcode; + fontCharCode = GlyphsUnicode[glyphName] || charcode; break; } if (!this.hasEncoding || this.isSymbolicFont) { - unicode = this.useToUnicode ? this.toUnicode[charcode] : charcode; + fontCharCode = this.useToFontChar ? this.toFontChar[charcode] : + charcode; break; } // MacRoman encoding address by re-encoding the cmap table - unicode = glyphName in this.glyphNameMap ? + fontCharCode = glyphName in this.glyphNameMap ? this.glyphNameMap[glyphName] : GlyphsUnicode[glyphName]; break; default: @@ -2287,15 +2296,15 @@ var Font = (function FontClosure() { break; } - var unicodeChars = !('toUnicodeOriginal' in this) ? charcode : - this.toUnicodeOriginal[charcode] || charcode; + var unicodeChars = !('toUnicode' in this) ? charcode : + this.toUnicode[charcode] || charcode; if (typeof unicodeChars === 'number') unicodeChars = String.fromCharCode(unicodeChars); width = (isNum(width) ? width : this.defaultWidth) * this.widthMultiplier; return { - fontChar: String.fromCharCode(unicode), + fontChar: String.fromCharCode(fontCharCode), unicode: unicodeChars, width: width, codeIRQueue: codeIRQueue From 6c7e7df6dad89b931977f3144c2ac78d2cdb8cb8 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Tue, 14 Feb 2012 23:06:16 -0600 Subject: [PATCH 10/40] linting --- src/fonts.js | 7 ++++--- web/viewer.js | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/fonts.js b/src/fonts.js index ea5f20d78..87330756d 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -1790,8 +1790,8 @@ var Font = (function FontClosure() { for (var i = 1; i < numGlyphs; i++) { var cid = gidToCidMap[i] || i; var unicode = this.toFontChar[cid]; - if (!unicode || typeof unicode !== 'number' || isSpecialUnicode(unicode) || - unicode in usedUnicodes) { + if (!unicode || typeof unicode !== 'number' || + isSpecialUnicode(unicode) || unicode in usedUnicodes) { unassignedUnicodeItems.push(i); continue; } @@ -1837,7 +1837,8 @@ var Font = (function FontClosure() { var usedUnicodes = [], unassignedUnicodeItems = []; for (var i = 0, ii = glyphs.length; i < ii; i++) { var unicode = toFontChar[i + 1]; - if (!unicode || typeof unicode !== 'number' || unicode in usedUnicodes) { + if (!unicode || typeof unicode !== 'number' || + unicode in usedUnicodes) { unassignedUnicodeItems.push(i); continue; } diff --git a/web/viewer.js b/web/viewer.js index ff857a943..dbb72202f 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -1044,9 +1044,9 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv) { // Adjust div width to match canvas text // Due to the .offsetWidth calls, this is slow // This needs to come after appending to the DOM - CustomStyle.setProp('transform' , textDiv, 'scale(' - + textDiv.dataset.canvasWidth/textDiv.offsetWidth - + ',1)'); + var textScale = textDiv.dataset.canvasWidth / textDiv.offsetWidth; + CustomStyle.setProp('transform' , textDiv, + 'scale(' + textScale + ', 1)'); CustomStyle.setProp('transformOrigin' , textDiv, '0% 0%'); } } // textLength > 0 From 2fa6e025f2fdafcb21ab229504b566558ec53d1c Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Sat, 18 Feb 2012 10:50:02 -0600 Subject: [PATCH 11/40] Recovering from the bad JPX images (#1145) --- src/jpx.js | 454 +++++++++++++++++++++++++++-------------------------- 1 file changed, 231 insertions(+), 223 deletions(-) diff --git a/src/jpx.js b/src/jpx.js index a15c3db54..b420b04e1 100644 --- a/src/jpx.js +++ b/src/jpx.js @@ -1052,7 +1052,7 @@ var JpxImage = (function JpxImageClosure() { } r = 0; } - error('JPX error: Out of packets'); + throw 'Out of packets'; }; } function ResolutionLayerComponentPositionIterator(context) { @@ -1091,7 +1091,7 @@ var JpxImage = (function JpxImageClosure() { } l = 0; } - error('JPX error: Out of packets'); + throw 'Out of packets'; }; } function buildPackets(context) { @@ -1187,7 +1187,7 @@ var JpxImage = (function JpxImageClosure() { new ResolutionLayerComponentPositionIterator(context); break; default: - error('JPX error: Unsupported progression order ' + progressionOrder); + throw 'Unsupported progression order ' + progressionOrder; } } function parseTilePackets(context, data, offset, dataLength) { @@ -1553,6 +1553,7 @@ var JpxImage = (function JpxImageClosure() { } function JpxImage() { + this.failOnCorruptedImage = false; } JpxImage.prototype = { load: function jpxImageLoad(url) { @@ -1612,237 +1613,244 @@ var JpxImage = (function JpxImageClosure() { }, parseCodestream: function jpxImageParseCodestream(data, start, end) { var context = {}; - var position = start; - while (position < end) { - var code = readUint16(data, position); - position += 2; + try { + var position = start; + while (position < end) { + var code = readUint16(data, position); + position += 2; - var length = 0, j; - switch (code) { - case 0xFF4F: // Start of codestream (SOC) - context.mainHeader = true; - break; - case 0xFFD9: // End of codestream (EOC) - break; - case 0xFF51: // Image and tile size (SIZ) - length = readUint16(data, position); - var siz = {}; - siz.Xsiz = readUint32(data, position + 4); - siz.Ysiz = readUint32(data, position + 8); - siz.XOsiz = readUint32(data, position + 12); - siz.YOsiz = readUint32(data, position + 16); - siz.XTsiz = readUint32(data, position + 20); - siz.YTsiz = readUint32(data, position + 24); - siz.XTOsiz = readUint32(data, position + 28); - siz.YTOsiz = readUint32(data, position + 32); - var componentsCount = readUint16(data, position + 36); - siz.Csiz = componentsCount; - var components = []; - j = position + 38; - for (var i = 0; i < componentsCount; i++) { - var component = { - precision: (data[j] & 0x7F) + 1, - isSigned: !!(data[j] & 0x80), - XRsiz: data[j + 1], - YRsiz: data[j + 1] - }; - calculateComponentDimensions(component, siz); - components.push(component); - } - context.SIZ = siz; - context.components = components; - calculateTileGrids(context, components); - context.QCC = []; - context.COC = []; - break; - case 0xFF5C: // Quantization default (QCD) - length = readUint16(data, position); - var qcd = {}; - j = position + 2; - var sqcd = data[j++]; - var spqcdSize, scalarExpounded; - switch (sqcd & 0x1F) { - case 0: - spqcdSize = 8; - scalarExpounded = true; - break; - case 1: - spqcdSize = 16; - scalarExpounded = false; - break; - case 2: - spqcdSize = 16; - scalarExpounded = true; - break; - default: - error('JPX error: Invalid SQcd value ' + sqcd); - } - qcd.noQuantization = spqcdSize == 8; - qcd.scalarExpounded = scalarExpounded; - qcd.guardBits = sqcd >> 5; - var spqcds = []; - while (j < length + position) { - var spqcd = {}; - if (spqcdSize == 8) { - spqcd.epsilon = data[j++] >> 3; - spqcd.mu = 0; - } else { - spqcd.epsilon = data[j] >> 3; - spqcd.mu = ((data[j] & 0x7) << 8) | data[j + 1]; - j += 2; + var length = 0, j; + switch (code) { + case 0xFF4F: // Start of codestream (SOC) + context.mainHeader = true; + break; + case 0xFFD9: // End of codestream (EOC) + break; + case 0xFF51: // Image and tile size (SIZ) + length = readUint16(data, position); + var siz = {}; + siz.Xsiz = readUint32(data, position + 4); + siz.Ysiz = readUint32(data, position + 8); + siz.XOsiz = readUint32(data, position + 12); + siz.YOsiz = readUint32(data, position + 16); + siz.XTsiz = readUint32(data, position + 20); + siz.YTsiz = readUint32(data, position + 24); + siz.XTOsiz = readUint32(data, position + 28); + siz.YTOsiz = readUint32(data, position + 32); + var componentsCount = readUint16(data, position + 36); + siz.Csiz = componentsCount; + var components = []; + j = position + 38; + for (var i = 0; i < componentsCount; i++) { + var component = { + precision: (data[j] & 0x7F) + 1, + isSigned: !!(data[j] & 0x80), + XRsiz: data[j + 1], + YRsiz: data[j + 1] + }; + calculateComponentDimensions(component, siz); + components.push(component); } - spqcds.push(spqcd); - } - qcd.SPqcds = spqcds; - if (context.mainHeader) - context.QCD = qcd; - else { - context.currentTile.QCD = qcd; - context.currentTile.QCC = []; - } - break; - case 0xFF5D: // Quantization component (QCC) - length = readUint16(data, position); - var qcc = {}; - j = position + 2; - var cqcc; - if (context.SIZ.Csiz < 257) - cqcc = data[j++]; - else { - cqcc = readUint16(data, j); - j += 2; - } - var sqcd = data[j++]; - var spqcdSize, scalarExpounded; - switch (sqcd & 0x1F) { - case 0: - spqcdSize = 8; - scalarExpounded = true; - break; - case 1: - spqcdSize = 16; - scalarExpounded = false; - break; - case 2: - spqcdSize = 16; - scalarExpounded = true; - break; - default: - error('JPX error: Invalid SQcd value ' + sqcd); - } - qcc.noQuantization = spqcdSize == 8; - qcc.scalarExpounded = scalarExpounded; - qcc.guardBits = sqcd >> 5; - var spqcds = []; - while (j < length + position) { - var spqcd = {}; - if (spqcdSize == 8) { - spqcd.epsilon = data[j++] >> 3; - spqcd.mu = 0; - } else { - spqcd.epsilon = data[j] >> 3; - spqcd.mu = ((data[j] & 0x7) << 8) | data[j + 1]; - j += 2; + context.SIZ = siz; + context.components = components; + calculateTileGrids(context, components); + context.QCC = []; + context.COC = []; + break; + case 0xFF5C: // Quantization default (QCD) + length = readUint16(data, position); + var qcd = {}; + j = position + 2; + var sqcd = data[j++]; + var spqcdSize, scalarExpounded; + switch (sqcd & 0x1F) { + case 0: + spqcdSize = 8; + scalarExpounded = true; + break; + case 1: + spqcdSize = 16; + scalarExpounded = false; + break; + case 2: + spqcdSize = 16; + scalarExpounded = true; + break; + default: + throw 'Invalid SQcd value ' + sqcd; } - spqcds.push(spqcd); - } - qcc.SPqcds = spqcds; - if (context.mainHeader) - context.QCC[cqcc] = qcc; - else - context.currentTile.QCC[cqcc] = qcc; - break; - case 0xFF52: // Coding style default (COD) - length = readUint16(data, position); - var cod = {}; - j = position + 2; - var scod = data[j++]; - cod.entropyCoderWithCustomPrecincts = !!(scod & 1); - cod.sopMarkerUsed = !!(scod & 2); - cod.ephMarkerUsed = !!(scod & 4); - var codingStyle = {}; - cod.progressionOrder = data[j++]; - cod.layersCount = readUint16(data, j); - j += 2; - cod.multipleComponentTransform = data[j++]; - - cod.decompositionLevelsCount = data[j++]; - cod.xcb = (data[j++] & 0xF) + 2; - cod.ycb = (data[j++] & 0xF) + 2; - var blockStyle = data[j++]; - cod.selectiveArithmeticCodingBypass = !!(blockStyle & 1); - cod.resetContextProbabilities = !!(blockStyle & 2); - cod.terminationOnEachCodingPass = !!(blockStyle & 4); - cod.verticalyStripe = !!(blockStyle & 8); - cod.predictableTermination = !!(blockStyle & 16); - cod.segmentationSymbolUsed = !!(blockStyle & 32); - cod.transformation = data[j++]; - if (cod.entropyCoderWithCustomPrecincts) { - var precinctsSizes = {}; + qcd.noQuantization = spqcdSize == 8; + qcd.scalarExpounded = scalarExpounded; + qcd.guardBits = sqcd >> 5; + var spqcds = []; while (j < length + position) { - var precinctsSize = data[j]; - precinctsSizes.push({ - PPx: precinctsSize & 0xF, - PPy: precinctsSize >> 4 - }); + var spqcd = {}; + if (spqcdSize == 8) { + spqcd.epsilon = data[j++] >> 3; + spqcd.mu = 0; + } else { + spqcd.epsilon = data[j] >> 3; + spqcd.mu = ((data[j] & 0x7) << 8) | data[j + 1]; + j += 2; + } + spqcds.push(spqcd); } - cod.precinctsSizes = precinctsSizes; - } + qcd.SPqcds = spqcds; + if (context.mainHeader) + context.QCD = qcd; + else { + context.currentTile.QCD = qcd; + context.currentTile.QCC = []; + } + break; + case 0xFF5D: // Quantization component (QCC) + length = readUint16(data, position); + var qcc = {}; + j = position + 2; + var cqcc; + if (context.SIZ.Csiz < 257) + cqcc = data[j++]; + else { + cqcc = readUint16(data, j); + j += 2; + } + var sqcd = data[j++]; + var spqcdSize, scalarExpounded; + switch (sqcd & 0x1F) { + case 0: + spqcdSize = 8; + scalarExpounded = true; + break; + case 1: + spqcdSize = 16; + scalarExpounded = false; + break; + case 2: + spqcdSize = 16; + scalarExpounded = true; + break; + default: + throw 'Invalid SQcd value ' + sqcd; + } + qcc.noQuantization = spqcdSize == 8; + qcc.scalarExpounded = scalarExpounded; + qcc.guardBits = sqcd >> 5; + var spqcds = []; + while (j < length + position) { + var spqcd = {}; + if (spqcdSize == 8) { + spqcd.epsilon = data[j++] >> 3; + spqcd.mu = 0; + } else { + spqcd.epsilon = data[j] >> 3; + spqcd.mu = ((data[j] & 0x7) << 8) | data[j + 1]; + j += 2; + } + spqcds.push(spqcd); + } + qcc.SPqcds = spqcds; + if (context.mainHeader) + context.QCC[cqcc] = qcc; + else + context.currentTile.QCC[cqcc] = qcc; + break; + case 0xFF52: // Coding style default (COD) + length = readUint16(data, position); + var cod = {}; + j = position + 2; + var scod = data[j++]; + cod.entropyCoderWithCustomPrecincts = !!(scod & 1); + cod.sopMarkerUsed = !!(scod & 2); + cod.ephMarkerUsed = !!(scod & 4); + var codingStyle = {}; + cod.progressionOrder = data[j++]; + cod.layersCount = readUint16(data, j); + j += 2; + cod.multipleComponentTransform = data[j++]; - if (cod.sopMarkerUsed || cod.ephMarkerUsed || - cod.selectiveArithmeticCodingBypass || - cod.resetContextProbabilities || - cod.terminationOnEachCodingPass || - cod.verticalyStripe || cod.predictableTermination || - cod.segmentationSymbolUsed) - error('JPX error: Unsupported COD options: ' + uneval(cod)); + cod.decompositionLevelsCount = data[j++]; + cod.xcb = (data[j++] & 0xF) + 2; + cod.ycb = (data[j++] & 0xF) + 2; + var blockStyle = data[j++]; + cod.selectiveArithmeticCodingBypass = !!(blockStyle & 1); + cod.resetContextProbabilities = !!(blockStyle & 2); + cod.terminationOnEachCodingPass = !!(blockStyle & 4); + cod.verticalyStripe = !!(blockStyle & 8); + cod.predictableTermination = !!(blockStyle & 16); + cod.segmentationSymbolUsed = !!(blockStyle & 32); + cod.transformation = data[j++]; + if (cod.entropyCoderWithCustomPrecincts) { + var precinctsSizes = {}; + while (j < length + position) { + var precinctsSize = data[j]; + precinctsSizes.push({ + PPx: precinctsSize & 0xF, + PPy: precinctsSize >> 4 + }); + } + cod.precinctsSizes = precinctsSizes; + } - if (context.mainHeader) - context.COD = cod; - else { - context.currentTile.COD = cod; - context.currentTile.COC = []; - } - break; - case 0xFF90: // Start of tile-part (SOT) - length = readUint16(data, position); - var tile = {}; - tile.index = readUint16(data, position + 2); - tile.length = readUint32(data, position + 4); - tile.dataEnd = tile.length + position - 2; - tile.partIndex = data[position + 8]; - tile.partsCount = data[position + 9]; + if (cod.sopMarkerUsed || cod.ephMarkerUsed || + cod.selectiveArithmeticCodingBypass || + cod.resetContextProbabilities || + cod.terminationOnEachCodingPass || + cod.verticalyStripe || cod.predictableTermination || + cod.segmentationSymbolUsed) + throw 'Unsupported COD options: ' + uneval(cod); - context.mainHeader = false; - if (tile.partIndex == 0) { - // reset component specific settings - tile.COD = context.COD; - tile.COC = context.COC.slice(0); // clone of the global COC - tile.QCD = context.QCD; - tile.QCC = context.QCC.slice(0); // clone of the global COC - } - context.currentTile = tile; - break; - case 0xFF93: // Start of data (SOD) - var tile = context.currentTile; - if (tile.partIndex == 0) { - initializeTile(context, tile.index); - buildPackets(context); - } + if (context.mainHeader) + context.COD = cod; + else { + context.currentTile.COD = cod; + context.currentTile.COC = []; + } + break; + case 0xFF90: // Start of tile-part (SOT) + length = readUint16(data, position); + var tile = {}; + tile.index = readUint16(data, position + 2); + tile.length = readUint32(data, position + 4); + tile.dataEnd = tile.length + position - 2; + tile.partIndex = data[position + 8]; + tile.partsCount = data[position + 9]; - // moving to the end of the data - length = tile.dataEnd - position; + context.mainHeader = false; + if (tile.partIndex == 0) { + // reset component specific settings + tile.COD = context.COD; + tile.COC = context.COC.slice(0); // clone of the global COC + tile.QCD = context.QCD; + tile.QCC = context.QCC.slice(0); // clone of the global COC + } + context.currentTile = tile; + break; + case 0xFF93: // Start of data (SOD) + var tile = context.currentTile; + if (tile.partIndex == 0) { + initializeTile(context, tile.index); + buildPackets(context); + } - parseTilePackets(context, data, position, length); - break; - case 0xFF64: // Comment (COM) - length = readUint16(data, position); - // skipping content - break; - default: - error('JPX error: Unknown codestream code: ' + code.toString(16)); + // moving to the end of the data + length = tile.dataEnd - position; + + parseTilePackets(context, data, position, length); + break; + case 0xFF64: // Comment (COM) + length = readUint16(data, position); + // skipping content + break; + default: + throw 'Unknown codestream code: ' + code.toString(16); + } + position += length; } - position += length; + } catch (e) { + if (this.failOnCorruptedImage) + error('JPX error: ' + e); + else + warn('JPX error: ' + e + '. Trying to recover'); } this.tiles = transformComponents(context); this.width = context.SIZ.Xsiz - context.SIZ.XOsiz; From 321750bba6931d929cd3ea457db452bc9b1e3d75 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Sat, 18 Feb 2012 15:01:53 -0600 Subject: [PATCH 12/40] Sets proper unicode range for symbols; sanitize no-glyphs fonts --- src/fonts.js | 20 +++++++++++++++++++- test/pdfs/issue1243.pdf.link | 1 + test/test_manifest.json | 8 ++++++++ 3 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 test/pdfs/issue1243.pdf.link diff --git a/src/fonts.js b/src/fonts.js index 0f9b6f9d0..cdb56efa7 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -1648,6 +1648,18 @@ var Font = (function FontClosure() { itemEncode(locaData, j, writeOffset); startOffset = endOffset; } + + if (writeOffset == 0) { + // glyf table cannot be empty -- redoing the glyf and loca tables + // to have single glyph with one point + var simpleGlyph = new Uint8Array( + [0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 49, 0]); + for (var i = 0, j = itemSize; i < numGlyphs; i++, j += itemSize) + itemEncode(locaData, j, simpleGlyph.length); + glyf.data = simpleGlyph; + return; + } + glyf.data = newGlyfData.subarray(0, writeOffset); } @@ -1928,6 +1940,11 @@ var Font = (function FontClosure() { } } + // If font is symbolic and cmap (3,0) present, the characters are + // located in 0xF000 - 0xF0FF range + this.symbolicGlyphsOffset = + this.isSymbolicFont && !hasShortCmap ? 0xF000 : 0; + // remove glyph references outside range of avaialable glyphs for (var i = 0, ii = ids.length; i < ii; i++) { if (ids[i] >= numGlyphs) @@ -2315,7 +2332,8 @@ var Font = (function FontClosure() { break; } if (!this.hasEncoding || this.isSymbolicFont) { - unicode = this.useToUnicode ? this.toUnicode[charcode] : charcode; + unicode = this.useToUnicode ? this.toUnicode[charcode] : + (this.symbolicGlyphsOffset + charcode); break; } diff --git a/test/pdfs/issue1243.pdf.link b/test/pdfs/issue1243.pdf.link new file mode 100644 index 000000000..89187f785 --- /dev/null +++ b/test/pdfs/issue1243.pdf.link @@ -0,0 +1 @@ +http://www.nsa.gov/public_info/_files/nash_letters/nash_letters1.pdf diff --git a/test/test_manifest.json b/test/test_manifest.json index e09f16da9..e19aeb3a3 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -480,5 +480,13 @@ "rounds": 1, "link": true, "type": "eq" + }, + { "id": "issue1243", + "file": "pdfs/issue1243.pdf", + "md5": "130c849b83513d5ac5e03c6421fc7489", + "rounds": 1, + "pageLimit": 2, + "link": true, + "type": "eq" } ] From 5efccea2afb8c928c3c236370c86d150ffa8eb6d Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Sat, 18 Feb 2012 16:45:47 -0600 Subject: [PATCH 13/40] Using the first glyph code to detect the base glyph offset. --- src/fonts.js | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/fonts.js b/src/fonts.js index cdb56efa7..b7dec9a94 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -1940,10 +1940,11 @@ var Font = (function FontClosure() { } } - // If font is symbolic and cmap (3,0) present, the characters are - // located in 0xF000 - 0xF0FF range - this.symbolicGlyphsOffset = - this.isSymbolicFont && !hasShortCmap ? 0xF000 : 0; + // If font is symbolic and cmap (3,0) present, the characters can be + // located in 0xF000 - 0xF0FF range. Using the first glyph code + // to detect the base glyphs offset. + this.symbolicGlyphsOffset = this.isSymbolicFont && !hasShortCmap ? + (glyphs[i].unicode & 0xFF00) : 0; // remove glyph references outside range of avaialable glyphs for (var i = 0, ii = ids.length; i < ii; i++) { From 2703f3f6928659244dfe3d713d0821d9514f106d Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Sat, 18 Feb 2012 19:18:54 -0600 Subject: [PATCH 14/40] Fixing first glyph index --- src/fonts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fonts.js b/src/fonts.js index b7dec9a94..dc57417de 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -1944,7 +1944,7 @@ var Font = (function FontClosure() { // located in 0xF000 - 0xF0FF range. Using the first glyph code // to detect the base glyphs offset. this.symbolicGlyphsOffset = this.isSymbolicFont && !hasShortCmap ? - (glyphs[i].unicode & 0xFF00) : 0; + (glyphs[0].unicode & 0xFF00) : 0; // remove glyph references outside range of avaialable glyphs for (var i = 0, ii = ids.length; i < ii; i++) { From c68dbd777efa848650d5abec7a7c78fbd6a76339 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Sat, 18 Feb 2012 20:20:25 -0600 Subject: [PATCH 15/40] Relax EI search for inline images --- src/parser.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/parser.js b/src/parser.js index fc448ff66..fc8f5bc66 100644 --- a/src/parser.js +++ b/src/parser.js @@ -120,17 +120,17 @@ var Parser = (function ParserClosure() { // parse image stream var startPos = stream.pos; - // searching for the /\sEI\s/ + // searching for the /EI\s/ var state = 0, ch; while (state != 4 && (ch = stream.getByte()) != null) { switch (ch) { case 0x20: case 0x0D: case 0x0A: - state = state === 3 ? 4 : 1; + state = state === 3 ? 4 : 0; break; case 0x45: - state = state === 1 ? 2 : 0; + state = 2; break; case 0x49: state = state === 2 ? 3 : 0; From 8f3b198c230deaeef7100535ed46cebff5079972 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Sun, 19 Feb 2012 20:12:57 -0600 Subject: [PATCH 16/40] Check if glyph are stored outside the glyf table --- src/fonts.js | 10 +++++++++- test/pdfs/.gitignore | 1 + test/pdfs/issue1249.pdf | Bin 0 -> 68783 bytes test/test_manifest.json | 6 ++++++ 4 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 test/pdfs/issue1249.pdf diff --git a/src/fonts.js b/src/fonts.js index 0f9b6f9d0..23a08c0c7 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -1636,12 +1636,20 @@ var Font = (function FontClosure() { var locaData = loca.data; // removing the invalid glyphs var oldGlyfData = glyf.data; - var newGlyfData = new Uint8Array(oldGlyfData.length); + var oldGlyfDataLength = oldGlyfData.length; + var newGlyfData = new Uint8Array(oldGlyfDataLength); var startOffset = itemDecode(locaData, 0); var writeOffset = 0; itemEncode(locaData, 0, writeOffset); for (var i = 0, j = itemSize; i < numGlyphs; i++, j += itemSize) { var endOffset = itemDecode(locaData, j); + if (endOffset > oldGlyfDataLength) { + // glyph end offset points outside glyf data, rejecting the glyph + itemEncode(locaData, j, writeOffset); + startOffset = endOffset; + continue; + } + var newLength = sanitizeGlyph(oldGlyfData, startOffset, endOffset, newGlyfData, writeOffset); writeOffset += newLength; diff --git a/test/pdfs/.gitignore b/test/pdfs/.gitignore index e0926492b..7e79f90f0 100644 --- a/test/pdfs/.gitignore +++ b/test/pdfs/.gitignore @@ -20,6 +20,7 @@ !scan-bad.pdf !freeculture.pdf !issue918.pdf +!issue1249.pdf !smaskdim.pdf !type4psfunc.pdf !S2.pdf diff --git a/test/pdfs/issue1249.pdf b/test/pdfs/issue1249.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f7bacda0241f7fdf7f069037999e0fda4580a942 GIT binary patch literal 68783 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®GP zg{Awaee-8r?9Czw%)qEYAtIFyB-nJUKR1`xLYt(jL5@-SVAZKwhG5I0q(LEvF zT+M})gxXLd*%A^U#SqweVtrL3|3jHcBlUt<0bGTHQd8{{Hz{!?FFM&&?+p@5nqS1T4+t7A{G0e9{V9Kj7ADnbmon_wTH6(+ zw#r87Y&>o@k)CG8?Y;5)Riajyb`>LglN+rpdKtWu(j#KLGrb%T7q4#>(#7ddQ)L5a z5}fK6rK@g7!5EFRUF!95Qqn}eD;uPTU}Tf4kDF;Pu-?HulH8VE17=OUdfhOzhhBar z_8X1Y7#TBuZQ+*k*M&s_%GPZjB_E}XkB_#jd&JzMniL+ReiFPPEE_&242PTz(X1LX zNY%Sb>1e?V>>czQGxwBDVgLXgU^F8Nw}@0FdXen{fM#$EZhuSvZeX=y1Zy@*#JrN1 z-*I|%2>i%s8Am?3)j$mN=elGy&#KE#%bumKGX-KXh>y5oMm@+t`8aPy-2)xR*FZ_StNdHo=_E8|O3#uzz#mn$_SiUbCu5hTHv z5{AgIBu(LN_|~J1J2n!qs0dLoGXa(wH3A0&qUWafEf8bA5S-P~VUm}b%aAF?*5d#Mi)6v829{x^oP&k`fGuQNx%4K7nKnR*Cg2bkPXpk(s?3&XS+ z&;A!vs81S+DMjni%h}!G*Qk|*Bb40T-uCi^@ZzyH_RDg0aJto)obwX{r9559IStEq420H?&v8cgAMhe5>6zm&5UR{Bq={Q9d9+j=Pg5CoJ+A4&JPoIpl zf_z_|jL8!luxRe2V!w2zr+>D>$7OLnH1-Z_3aD46)!V%>)%<8js)LA+I${~p6Lv;x zIjd!AUvlI#aP7TbFKgJib!b2MUYO+SlFsX$n%N2_`lye!j(QiTabEk1mq(1bNxM|_ zXLwkExHLi-g-E;1@vAff`zgu5^qP1ycycUNGa^xNM4F?$jjD27)!r|ijE*Bb0e{Xg zt)JxD7jyU-<#R5x+ie%2BL^D5Mcr_uD$XPwQ9%h>!G9q}$PFV%GVwo}mibP&Va zC1YK?;;YnF&v4CQXH#EzLeZ>DGgtYiP`^|x)8Yl1eN zCe1fgT@v0Hl>TQy=}7oVI6?!Rf@`&91yL~MusfwPtu5wb8c9e5V@Na<^MK=#?@WIp zt~oPU=EEWx6CickK~0NWn7c{!orHI_X*DQL0tie_mBExrH;0YC=yK!h$>pz5beb+z z*ZHR5e4_!XCl0PP)u!1jrK#|SUX)r{2fGnj7dT5O<;w%nVRZnJ8u-jkJ+rCT@_GI04r@spoV+cx{uGB<)Dge%nZG}yziym`aD*0ln#$-e4U zdaHVQy)_sjr}fvZqw0P9o^3K|B^)uP2R$X?tY7Po`hG~7m?LwuT+PS$2N|wW=&yOy zyf(532=CrN�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 literal 0 HcmV?d00001 diff --git a/test/test_manifest.json b/test/test_manifest.json index e09f16da9..19d25317a 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -417,6 +417,12 @@ "link": true, "type": "eq" }, + { "id": "issue1249-load", + "file": "pdfs/issue1249.pdf", + "md5": "4f81339fa09422a7db980f34ea963609", + "rounds": 1, + "type": "load" + }, { "id": "liveprogramming", "file": "pdfs/liveprogramming.pdf", "md5": "7bd4dad1188232ef597d36fd72c33e52", From b4e3554af25d7dfd09a9496a515c33dbdc1010c4 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Mon, 20 Feb 2012 13:00:45 -0600 Subject: [PATCH 17/40] Remove debug code --- src/fonts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fonts.js b/src/fonts.js index 1ddf5eaa5..e67286d81 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -2040,7 +2040,7 @@ var Font = (function FontClosure() { } properties.baseEncoding = encoding; } - if (false && properties.subtype == 'CIDFontType0C') { + if (properties.subtype == 'CIDFontType0C') { var toFontChar = []; for (var i = 0; i < charstrings.length; ++i) { var charstring = charstrings[i]; From 26b58c0e4f18f3222881ae7c3c17565441308a37 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Mon, 20 Feb 2012 14:26:30 -0600 Subject: [PATCH 18/40] Properly build toFontChar --- src/fonts.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/fonts.js b/src/fonts.js index e67286d81..01e7a7c9f 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -2147,8 +2147,9 @@ var Font = (function FontClosure() { for (var i = 0, ii = toUnicode.length; i < ii; i++) { var unicode = toUnicode[i]; var fontCharCode = typeof unicode === 'object' ? unusedUnicode++ : - unicode; - result.push(fontCharCode); + unicode; + if (typeof unicode !== 'undefined') + result[i] = fontCharCode; } return result; }, From a46a6849352c84299cfbb460ddffbe77a4408186 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Mon, 20 Feb 2012 17:19:12 -0600 Subject: [PATCH 19/40] Moving all symbolic font glyphs into 0xF000 - 0xF0FF range --- src/fonts.js | 17 +++++++++++------ test/pdfs/issue1257.pdf.link | 1 + test/test_manifest.json | 8 ++++++++ 3 files changed, 20 insertions(+), 6 deletions(-) create mode 100644 test/pdfs/issue1257.pdf.link diff --git a/src/fonts.js b/src/fonts.js index dc57417de..632d137b2 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -11,6 +11,7 @@ var kMaxWaitForFontFace = 1000; // Unicode Private Use Area var kCmapGlyphOffset = 0xE000; var kSizeOfGlyphArea = 0x1900; +var kSymbolicFontGlyphOffset = 0xF000; // PDF Glyph Space Units are one Thousandth of a TextSpace Unit // except for Type 3 fonts @@ -1888,7 +1889,7 @@ var Font = (function FontClosure() { } } // if it is, replacing with meaningful toUnicode values - if (isIdentity) { + if (isIdentity && !this.isSymbolicFont) { var usedUnicodes = [], unassignedUnicodeItems = []; for (var i = 0, ii = glyphs.length; i < ii; i++) { var unicode = toUnicode[i + 1]; @@ -1940,11 +1941,15 @@ var Font = (function FontClosure() { } } - // If font is symbolic and cmap (3,0) present, the characters can be - // located in 0xF000 - 0xF0FF range. Using the first glyph code - // to detect the base glyphs offset. - this.symbolicGlyphsOffset = this.isSymbolicFont && !hasShortCmap ? - (glyphs[0].unicode & 0xFF00) : 0; + // Moving all symbolic font glyphs into 0xF000 - 0xF0FF range. + this.symbolicGlyphsOffset = 0; + if (this.isSymbolicFont) { + for (var i = 0, ii = glyphs.length; i < ii; i++) { + var code = glyphs[i].unicode; + glyphs[i].unicode = kSymbolicFontGlyphOffset | (code & 0xFF); + } + this.symbolicGlyphsOffset = kSymbolicFontGlyphOffset; + } // remove glyph references outside range of avaialable glyphs for (var i = 0, ii = ids.length; i < ii; i++) { diff --git a/test/pdfs/issue1257.pdf.link b/test/pdfs/issue1257.pdf.link new file mode 100644 index 000000000..5a8bdd15c --- /dev/null +++ b/test/pdfs/issue1257.pdf.link @@ -0,0 +1 @@ +http://hse-econ.fi/tervio/MediocritiesAndSuperstars.pdf diff --git a/test/test_manifest.json b/test/test_manifest.json index e19aeb3a3..9297bf4d8 100644 --- a/test/test_manifest.json +++ b/test/test_manifest.json @@ -488,5 +488,13 @@ "pageLimit": 2, "link": true, "type": "eq" + }, + { "id": "issue1257", + "file": "pdfs/issue1257.pdf", + "md5": "9111533826bc21ed774e8e01603a2f54", + "rounds": 1, + "pageLimit": 2, + "link": true, + "type": "eq" } ] From 3a824f69d4395c50bbc8fdd0d5178a62c37a688c Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Tue, 21 Feb 2012 13:37:22 -0800 Subject: [PATCH 20/40] Fix the stepper beyond page 1. --- web/debugger.js | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/web/debugger.js b/web/debugger.js index c759a5ee8..aee0c3c77 100644 --- a/web/debugger.js +++ b/web/debugger.js @@ -180,7 +180,7 @@ var StepperManager = (function StepperManagerClosure() { this.selectStepper(pageNumber, false); return stepper; }, - selectStepper: function selectStepper(pageNumber, selectPanel) { + selectStepper: function selectStepper(pageNumber, selectPanel, change) { if (selectPanel) this.manager.selectPanel(1); for (var i = 0; i < steppers.length; ++i) { @@ -190,6 +190,11 @@ var StepperManager = (function StepperManagerClosure() { else stepper.panel.setAttribute('hidden', true); } + var options = stepperChooser.options; + for (var i = 0; i < options.length; ++i) { + var option = options[i]; + option.selected = option.value == pageNumber; + } }, saveBreakPoints: function saveBreakPoints(pageNumber, bps) { breakPoints[pageNumber] = bps; @@ -234,7 +239,7 @@ var Stepper = (function StepperClosure() { for (var i = 0; i < IRQueue.fnArray.length; i++) { var line = c('tr'); line.className = 'line'; - line.id = 'idx' + i; + line.dataset.idx = i; table.appendChild(line); var checked = this.breakPoints.indexOf(i) != -1; var args = IRQueue.argsArray[i] ? IRQueue.argsArray[i] : []; @@ -299,13 +304,14 @@ var Stepper = (function StepperClosure() { }, goTo: function goTo(idx) { var allRows = this.panel.getElementsByClassName('line'); - for (var x = 0; x < allRows.length; x++) { - allRows[x].style.backgroundColor = null; - } - var row = document.getElementById('idx' + idx); - if (row) { - row.style.backgroundColor = 'rgb(251,250,207)'; - row.scrollIntoView(); + for (var x = 0, xx = allRows.length; x < xx; ++x) { + var row = allRows[x]; + if (row.dataset.idx == idx) { + row.style.backgroundColor = 'rgb(251,250,207)'; + row.scrollIntoView(); + } else { + row.style.backgroundColor = null; + } } } }; From 4b887fe1c205a6a9d16cc9b77f96e640df6c8b60 Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Tue, 21 Feb 2012 13:39:04 -0800 Subject: [PATCH 21/40] Remove unused variable. --- web/debugger.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/debugger.js b/web/debugger.js index aee0c3c77..43407fdaa 100644 --- a/web/debugger.js +++ b/web/debugger.js @@ -180,7 +180,7 @@ var StepperManager = (function StepperManagerClosure() { this.selectStepper(pageNumber, false); return stepper; }, - selectStepper: function selectStepper(pageNumber, selectPanel, change) { + selectStepper: function selectStepper(pageNumber, selectPanel) { if (selectPanel) this.manager.selectPanel(1); for (var i = 0; i < steppers.length; ++i) { From 7cad586e6315b4a88078de43d1278f0ed4805808 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Tue, 21 Feb 2012 18:27:31 -0600 Subject: [PATCH 22/40] Fix merge with symbol font fix --- src/fonts.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/fonts.js b/src/fonts.js index e0890c6fa..0651485fc 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -1957,9 +1957,10 @@ var Font = (function FontClosure() { // Moving all symbolic font glyphs into 0xF000 - 0xF0FF range. if (this.isSymbolicFont) { for (var i = 0, ii = glyphs.length; i < ii; i++) { + var cid = i + 1; var code = glyphs[i].unicode; code = kSymbolicFontGlyphOffset | (code & 0xFF); - glyphs[i].unicode = toFontChar[i] = code; + glyphs[i].unicode = toFontChar[cid] = code; } this.useToFontChar = true; } From 31d8d13ba294f2e16ca984aedd932a23748b33f5 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Tue, 21 Feb 2012 19:56:35 -0600 Subject: [PATCH 23/40] Fix merge with symbol font fix (part 2) --- src/fonts.js | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/fonts.js b/src/fonts.js index 0651485fc..3955f9136 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -1957,10 +1957,9 @@ var Font = (function FontClosure() { // Moving all symbolic font glyphs into 0xF000 - 0xF0FF range. if (this.isSymbolicFont) { for (var i = 0, ii = glyphs.length; i < ii; i++) { - var cid = i + 1; - var code = glyphs[i].unicode; - code = kSymbolicFontGlyphOffset | (code & 0xFF); - glyphs[i].unicode = toFontChar[cid] = code; + var code = glyphs[i].unicode & 0xFF; + var fontCharCode = kSymbolicFontGlyphOffset | code; + glyphs[i].unicode = toFontChar[code] = fontCharCode; } this.useToFontChar = true; } From e8ca7b44d9d6590bbdb3246c4c27d7c2d7c556ca Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Wed, 22 Feb 2012 17:56:47 -0600 Subject: [PATCH 24/40] Fixing review nits; moving small functions inside bidi --- src/bidi.js | 180 +++++++++++++++++++++++++------------------------- src/canvas.js | 10 +-- 2 files changed, 96 insertions(+), 94 deletions(-) diff --git a/src/bidi.js b/src/bidi.js index 025847ea9..b24721045 100644 --- a/src/bidi.js +++ b/src/bidi.js @@ -52,9 +52,81 @@ var arabicTypes = [ ]; function bidi(text, startLevel) { + 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; + } + } + var str = text.str; var strLength = str.length; - if (strLength == 0) return str; + if (strLength == 0) + return str; // get types, fill arrays @@ -91,7 +163,8 @@ function bidi(text, startLevel) { text.direction = 'ltr'; return str; } - else if (startLevel == -1) { + + if (startLevel == -1) { if ((strLength / numBidi) < 0.3) { text.direction = 'ltr'; startLevel = 0; @@ -127,8 +200,10 @@ function bidi(text, startLevel) { var lastType = sor; for (var i = 0; i < strLength; ++i) { - if (types[i] == 'NSM') types[i] = lastType; - else lastType = types[i]; + if (types[i] == 'NSM') + types[i] = lastType; + else + lastType = types[i]; } /* @@ -152,7 +227,8 @@ function bidi(text, startLevel) { for (var i = 0; i < strLength; ++i) { var t = types[i]; - if (t == 'AL') types[i] = 'R'; + if (t == 'AL') + types[i] = 'R'; } /* @@ -178,15 +254,15 @@ function bidi(text, startLevel) { if (types[i] == 'EN') { // do before for (var j = i - 1; j >= 0; --j) { - if (types[j] == 'ET') - types[j] = 'EN'; - else break; + if (types[j] != 'ET') + break; + types[j] = 'EN'; } // do after for (var j = i + 1; j < strLength; --j) { - if (types[j] == 'ET') - types[j] = 'EN'; - else break; + if (types[j] != 'ET') + break; + types[j] = 'EN'; } } } @@ -263,12 +339,10 @@ function bidi(text, startLevel) { if (isEven(levels[i])) { if (t == 'R') { levels[i] += 1; - } - else if (t == 'AN' || t == 'EN') { + } else if (t == 'AN' || t == 'EN') { levels[i] += 2; } - } - else { // isOdd, so + } else { // isOdd, so if (t == 'L' || t == 'AN' || t == 'EN') { levels[i] += 1; } @@ -311,14 +385,13 @@ function bidi(text, startLevel) { for (var level = highestLevel; level >= lowestOddLevel; --level) { // find segments to reverse var start = -1; - for (var i = 0; i < levels.length; ++i) { + 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) { + } else if (start < 0) { start = i; } } @@ -354,74 +427,3 @@ function bidi(text, startLevel) { } return result; } - -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; j < arr.length; ++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; - } -} - diff --git a/src/canvas.js b/src/canvas.js index 551148341..54ab5b4d0 100644 --- a/src/canvas.js +++ b/src/canvas.js @@ -779,15 +779,15 @@ var CanvasGraphics = (function CanvasGraphicsClosure() { x += charWidth; var glyphUnicode = glyph.unicode === ' ' ? '\u00A0' : glyph.unicode; - var glyphUniLen = glyphUnicode.length; + var glyphUnicodeLength = glyphUnicode.length; //reverse an arabic ligature - if (glyphUniLen > 1 && isRTLRangeFor(glyphUnicode.charCodeAt(0))) - { - for (var ii = glyphUniLen - 1; ii >= 0; ii--) + if (glyphUnicodeLength > 1 && + isRTLRangeFor(glyphUnicode.charCodeAt(0))) { + for (var ii = glyphUnicodeLength - 1; ii >= 0; ii--) text.str += glyphUnicode[ii]; } else text.str += glyphUnicode; - text.length += glyphUniLen; + text.length += glyphUnicodeLength; text.canvasWidth += charWidth; } current.x += x * textHScale2; From 3b297368820b22738b5000a7ca291634209719c5 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Wed, 22 Feb 2012 18:26:43 -0600 Subject: [PATCH 25/40] create bidi closure --- src/bidi.js | 630 ++++++++++++++++++++++++++-------------------------- 1 file changed, 317 insertions(+), 313 deletions(-) diff --git a/src/bidi.js b/src/bidi.js index b24721045..a3308fa2c 100644 --- a/src/bidi.js +++ b/src/bidi.js @@ -3,55 +3,56 @@ 'use strict'; -// 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' -]; +var 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' -]; + // 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 bidi(text, startLevel) { function isOdd(i) { return (i & 1) != 0; } @@ -123,307 +124,310 @@ function bidi(text, startLevel) { } } - var str = text.str; - var strLength = str.length; - if (strLength == 0) - return str; + return (function bidi(text, startLevel) { + var str = text.str; + var strLength = str.length; + if (strLength == 0) + return str; - // get types, fill arrays + // get types, fill arrays - var chars = new Array(strLength); - var types = new Array(strLength); - var oldtypes = new Array(strLength); - var numBidi = 0; + 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); + 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'; + 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++; + if (charType == 'R' || charType == 'AL' || charType == 'AN') + numBidi++; - oldtypes[i] = types[i] = charType; - } + 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) { + // 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'; - startLevel = 0; - } else { - text.direction = 'rtl'; - startLevel = 1; + return str; } - } - 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'; + if (startLevel == -1) { + if ((strLength / numBidi) < 0.3) { + text.direction = 'ltr'; + startLevel = 0; + } else { + text.direction = 'rtl'; + startLevel = 1; } } - } - /* - W6. Otherwise, separators and terminators change to Other Neutral: - */ + var levels = new Array(strLength); - 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) + for (var i = 0; i < strLength; ++i) { + levels[i] = startLevel; } - } - /* - N2. Any remaining neutrals take the embedding direction. - */ + var diffChars = new Array(strLength); + var diffLevels = new Array(strLength); + var diffTypes = new Array(strLength); - for (var i = 0; i < strLength; ++i) { - if (types[i] == 'ON') - types[i] = e; - } + /* + X1-X10: skip most of this, since we are NOT doing the embeddings. + */ - /* - 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. - */ + var e = isOdd(startLevel) ? 'R' : 'L'; + var sor = e; + var eor = sor; - 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; - } + /* + 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]; } - } - /* - L1. On each line, reset the embedding level of the following characters to - the paragraph embedding level: + /* + 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. + */ - 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. - */ + 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; + } - // don't bother as text is only single line + /* + W3. Change all ALs to R. + */ - /* - 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. - */ + for (var i = 0; i < strLength; ++i) { + var t = types[i]; + if (t == 'AL') + types[i] = 'R'; + } - // find highest level & lowest odd level + /* + 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: + */ - 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; - } + 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]; + } - // now reverse between those limits + /* + W5. A sequence of European terminators adjacent to European numbers changes + to all European numbers: + */ - 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; + 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'; } - } else if (start < 0) { - start = i; } } - if (start >= 0) { - reverseValues(chars, start, levels.length); + + /* + 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'; } - } - /* - 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. - */ + /* + 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. + */ - // don't bother for now + 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; + } - /* - 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. - */ + /* + 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. + */ - // don't mirror as characters are already mirrored in the pdf + 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) + } + } - // Finally, return string + /* + N2. Any remaining neutrals take the embedding direction. + */ - var result = ''; - for (var i = 0, ii = chars.length; i < ii; ++i) { - var ch = chars[i]; - if (ch != '<' && ch != '>') - result += ch; - } - return result; -} + 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; + }); +})(); From 0ea87068ed9683c7483abd45b110e264cfa4fee9 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Wed, 22 Feb 2012 21:52:29 -0600 Subject: [PATCH 26/40] Skipping HTTP POST requests in the addon --- extensions/firefox/components/PdfStreamConverter.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/extensions/firefox/components/PdfStreamConverter.js b/extensions/firefox/components/PdfStreamConverter.js index 06db4e2d3..c6dab9ba7 100644 --- a/extensions/firefox/components/PdfStreamConverter.js +++ b/extensions/firefox/components/PdfStreamConverter.js @@ -124,6 +124,19 @@ PdfStreamConverter.prototype = { asyncConvertData: function(aFromType, aToType, aListener, aCtxt) { if (!Services.prefs.getBoolPref('extensions.pdf.js.active')) throw Cr.NS_ERROR_NOT_IMPLEMENTED; + + // Ignoring HTTP POST requests -- pdf.js has to repeat the request. + var skipConversion = false; + try { + var request = aCtxt; + request.QueryInterface(Ci.nsIHttpChannel); + skipConversion = (request.requestMethod === 'POST'); + } catch (e) { + // Non-HTTP request... continue normally. + } + if (skipConversion) + throw Cr.NS_ERROR_NOT_IMPLEMENTED; + // Store the listener passed to us this.listener = aListener; }, From d0143cc2894307a7d0e652e190f9d2a67874dfb2 Mon Sep 17 00:00:00 2001 From: Brendan Dahl Date: Thu, 23 Feb 2012 09:21:35 -0800 Subject: [PATCH 27/40] Fix missing bidi for extension. --- src/bidi.js | 2 +- web/viewer.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/bidi.js b/src/bidi.js index a3308fa2c..a6e3e429f 100644 --- a/src/bidi.js +++ b/src/bidi.js @@ -3,7 +3,7 @@ 'use strict'; -var bidi = (function bidiClosure() { +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', diff --git a/web/viewer.js b/web/viewer.js index 0a1ad7508..9f477f3a7 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -1097,7 +1097,7 @@ var TextLayerBuilder = function textLayerBuilder(textLayerDiv) { textDiv.style.fontSize = fontHeight + 'px'; textDiv.style.left = text.geom.x + 'px'; textDiv.style.top = (text.geom.y - fontHeight) + 'px'; - textDiv.textContent = bidi(text, -1); + textDiv.textContent = PDFJS.bidi(text, -1); textDiv.dir = text.direction; textDiv.dataset.textLength = text.length; this.textDivs.push(textDiv); From 2cc89735aea412870c5b3cc0ace446d8b621916c Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Wed, 29 Feb 2012 18:31:03 -0600 Subject: [PATCH 28/40] Fixing cap1 statement --- src/fonts.js | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/fonts.js b/src/fonts.js index 542c33f55..9d11d81cc 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -1863,6 +1863,13 @@ var Font = (function FontClosure() { var hasShortCmap = !!cmapTable.hasShortCmap; var toFontChar = this.toFontChar; + if (hasShortCmap && ids.length == numGlyphs) { + // Fixes the short cmap tables -- some generators use incorrect + // glyph id. + for (var i = 0, ii = ids.length; i < ii; i++) + ids[i] = i; + } + if (toFontChar && toFontChar.length > 0) { // checking if cmap is just identity map var isIdentity = true; @@ -1906,11 +1913,14 @@ var Font = (function FontClosure() { // copying all characters to private use area, all mapping all known // glyphs to the unicodes. The glyphs and ids arrays will grow. var usedUnicodes = []; + var glyphNames = properties.glyphNames || []; for (var i = 0, ii = glyphs.length; i < ii; i++) { var code = glyphs[i].unicode; + var gid = ids[i]; glyphs[i].unicode += kCmapGlyphOffset; + toFontChar[code] = glyphs[i].unicode; - var glyphName = properties.baseEncoding[code]; + var glyphName = glyphNames[gid] || properties.baseEncoding[code]; if (glyphName in GlyphsUnicode) { var unicode = GlyphsUnicode[glyphName]; if (unicode in usedUnicodes) @@ -1921,9 +1931,11 @@ var Font = (function FontClosure() { unicode: unicode, code: glyphs[i].code }); - ids.push(ids[i]); + ids.push(gid); + toFontChar[code] = unicode; } } + this.useToFontChar = true; } // Moving all symbolic font glyphs into 0xF000 - 0xF0FF range. From a6b9efc06bc7ef12806c26cbcefc1feebbb7467f Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Wed, 29 Feb 2012 22:11:32 -0600 Subject: [PATCH 29/40] Re-encode cmap based on post table or current encoding; fix GlyphsUnicode table entries --- src/fonts.js | 58 ++++++++++++++++++++++++++++++++-- src/glyphlist.js | 81 ------------------------------------------------ 2 files changed, 55 insertions(+), 84 deletions(-) diff --git a/src/fonts.js b/src/fonts.js index 9d11d81cc..d04fd1a4e 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -1870,6 +1870,9 @@ var Font = (function FontClosure() { ids[i] = i; } + var unusedUnicode = kCmapGlyphOffset; + var glyphNames = properties.glyphNames || []; + var encoding = properties.baseEncoding; if (toFontChar && toFontChar.length > 0) { // checking if cmap is just identity map var isIdentity = true; @@ -1892,7 +1895,6 @@ var Font = (function FontClosure() { glyphs[i].unicode = unicode; usedUnicodes[unicode] = true; } - var unusedUnicode = kCmapGlyphOffset; for (var j = 0, jj = unassignedUnicodeItems.length; j < jj; j++) { var i = unassignedUnicodeItems[j]; while (unusedUnicode in usedUnicodes) @@ -1913,14 +1915,13 @@ var Font = (function FontClosure() { // copying all characters to private use area, all mapping all known // glyphs to the unicodes. The glyphs and ids arrays will grow. var usedUnicodes = []; - var glyphNames = properties.glyphNames || []; for (var i = 0, ii = glyphs.length; i < ii; i++) { var code = glyphs[i].unicode; var gid = ids[i]; glyphs[i].unicode += kCmapGlyphOffset; toFontChar[code] = glyphs[i].unicode; - var glyphName = glyphNames[gid] || properties.baseEncoding[code]; + var glyphName = glyphNames[gid] || encoding[code]; if (glyphName in GlyphsUnicode) { var unicode = GlyphsUnicode[glyphName]; if (unicode in usedUnicodes) @@ -1936,6 +1937,57 @@ var Font = (function FontClosure() { } } this.useToFontChar = true; + } else if (!this.isSymbolicFont && + (this.hasEncoding || properties.glyphNames)) { + // Re-encode cmap encoding to unicode, based on the 'post' table data + // or base encoding + var reverseMap = []; + for (var i = 0, ii = glyphs.length; i < ii; i++) + reverseMap[glyphs[i].unicode] = i; + + for (var i = 0, ii = glyphs.length; i < ii; i++) { + var code = glyphs[i].unicode; + var gid = ids[i] + + var glyphName = glyphNames[gid] || encoding[code]; + if (glyphName in GlyphsUnicode) { + var unicode = GlyphsUnicode[glyphName]; + if (!unicode || reverseMap[unicode] === i) + continue; // unknown glyph name or in its own place + + var destination = reverseMap[unicode]; + var j = i; + // Flipping unicodes while next destination unicode has assigned + // glyph and future glyph can be assigned to unicode. + while (typeof destination === 'number') { + glyphs[j].unicode = unicode; + reverseMap[unicode] = j; + + code = glyphs[destination].unicode; + gid = ids[destination]; + glyphName = glyphNames[gid] || encoding[code]; + + unicode = GlyphsUnicode[glyphName]; + if (!unicode || reverseMap[unicode] === j) { + unicode = 0; + break; // unknown glyph name or in its own place + } + + j = destination; + destination = reverseMap[unicode]; + } + + if (!unicode) { + // Future glyph cannot be assigned to unicode, generate new one. + while (reverseMap[unusedUnicode]) + unusedUnicode++; + unicode = unusedUnicode++; + } + + glyphs[j].unicode = unicode; + reverseMap[unicode] = j; + } + } } // Moving all symbolic font glyphs into 0xF000 - 0xF0FF range. diff --git a/src/glyphlist.js b/src/glyphlist.js index 01b94442a..694134840 100644 --- a/src/glyphlist.js +++ b/src/glyphlist.js @@ -1508,27 +1508,7 @@ var GlyphsUnicode = { dalet: 0x05D3, daletdagesh: 0xFB33, daletdageshhebrew: 0xFB33, - dalethatafpatah: 0x05D305B2, - dalethatafpatahhebrew: 0x05D305B2, - dalethatafsegol: 0x05D305B1, - dalethatafsegolhebrew: 0x05D305B1, dalethebrew: 0x05D3, - dalethiriq: 0x05D305B4, - dalethiriqhebrew: 0x05D305B4, - daletholam: 0x05D305B9, - daletholamhebrew: 0x05D305B9, - daletpatah: 0x05D305B7, - daletpatahhebrew: 0x05D305B7, - daletqamats: 0x05D305B8, - daletqamatshebrew: 0x05D305B8, - daletqubuts: 0x05D305BB, - daletqubutshebrew: 0x05D305BB, - daletsegol: 0x05D305B6, - daletsegolhebrew: 0x05D305B6, - daletsheva: 0x05D305B0, - daletshevahebrew: 0x05D305B0, - dalettsere: 0x05D305B5, - dalettserehebrew: 0x05D305B5, dalfinalarabic: 0xFEAA, dammaarabic: 0x064F, dammalowarabic: 0x064F, @@ -1845,10 +1825,6 @@ var GlyphsUnicode = { finalkafdagesh: 0xFB3A, finalkafdageshhebrew: 0xFB3A, finalkafhebrew: 0x05DA, - finalkafqamats: 0x05DA05B8, - finalkafqamatshebrew: 0x05DA05B8, - finalkafsheva: 0x05DA05B0, - finalkafshevahebrew: 0x05DA05B0, finalmem: 0x05DD, finalmemhebrew: 0x05DD, finalnun: 0x05DF, @@ -2037,14 +2013,7 @@ var GlyphsUnicode = { hakatakanahalfwidth: 0xFF8A, halantgurmukhi: 0x0A4D, hamzaarabic: 0x0621, - hamzadammaarabic: 0x0621064F, - hamzadammatanarabic: 0x0621064C, - hamzafathaarabic: 0x0621064E, - hamzafathatanarabic: 0x0621064B, hamzalowarabic: 0x0621, - hamzalowkasraarabic: 0x06210650, - hamzalowkasratanarabic: 0x0621064D, - hamzasukunarabic: 0x06210652, hangulfiller: 0x3164, hardsigncyrillic: 0x044A, harpoonleftbarbup: 0x21BC, @@ -2476,10 +2445,6 @@ var GlyphsUnicode = { lameddagesh: 0xFB3C, lameddageshhebrew: 0xFB3C, lamedhebrew: 0x05DC, - lamedholam: 0x05DC05B9, - lamedholamdagesh: '05DC 05B9 05BC', - lamedholamdageshhebrew: '05DC 05B9 05BC', - lamedholamhebrew: 0x05DC05B9, lamfinalarabic: 0xFEDE, lamhahinitialarabic: 0xFCCA, laminitialarabic: 0xFEDF, @@ -2489,8 +2454,6 @@ var GlyphsUnicode = { lammedialarabic: 0xFEE0, lammeemhahinitialarabic: 0xFD88, lammeeminitialarabic: 0xFCCC, - lammeemjeeminitialarabic: 'FEDF FEE4 FEA0', - lammeemkhahinitialarabic: 'FEDF FEE4 FEA8', largecircle: 0x25EF, lbar: 0x019A, lbelt: 0x026C, @@ -2787,7 +2750,6 @@ var GlyphsUnicode = { noonfinalarabic: 0xFEE6, noonghunnaarabic: 0x06BA, noonghunnafinalarabic: 0xFB9F, - noonhehinitialarabic: 0xFEE7FEEC, nooninitialarabic: 0xFEE7, noonjeeminitialarabic: 0xFCD2, noonjeemisolatedarabic: 0xFC4B, @@ -3159,27 +3121,7 @@ var GlyphsUnicode = { qof: 0x05E7, qofdagesh: 0xFB47, qofdageshhebrew: 0xFB47, - qofhatafpatah: 0x05E705B2, - qofhatafpatahhebrew: 0x05E705B2, - qofhatafsegol: 0x05E705B1, - qofhatafsegolhebrew: 0x05E705B1, qofhebrew: 0x05E7, - qofhiriq: 0x05E705B4, - qofhiriqhebrew: 0x05E705B4, - qofholam: 0x05E705B9, - qofholamhebrew: 0x05E705B9, - qofpatah: 0x05E705B7, - qofpatahhebrew: 0x05E705B7, - qofqamats: 0x05E705B8, - qofqamatshebrew: 0x05E705B8, - qofqubuts: 0x05E705BB, - qofqubutshebrew: 0x05E705BB, - qofsegol: 0x05E705B6, - qofsegolhebrew: 0x05E705B6, - qofsheva: 0x05E705B0, - qofshevahebrew: 0x05E705B0, - qoftsere: 0x05E705B5, - qoftserehebrew: 0x05E705B5, qparen: 0x24AC, quarternote: 0x2669, qubuts: 0x05BB, @@ -3253,32 +3195,11 @@ var GlyphsUnicode = { reharmenian: 0x0580, rehfinalarabic: 0xFEAE, rehiragana: 0x308C, - rehyehaleflamarabic: '0631 FEF3 FE8E 0644', rekatakana: 0x30EC, rekatakanahalfwidth: 0xFF9A, resh: 0x05E8, reshdageshhebrew: 0xFB48, - reshhatafpatah: 0x05E805B2, - reshhatafpatahhebrew: 0x05E805B2, - reshhatafsegol: 0x05E805B1, - reshhatafsegolhebrew: 0x05E805B1, reshhebrew: 0x05E8, - reshhiriq: 0x05E805B4, - reshhiriqhebrew: 0x05E805B4, - reshholam: 0x05E805B9, - reshholamhebrew: 0x05E805B9, - reshpatah: 0x05E805B7, - reshpatahhebrew: 0x05E805B7, - reshqamats: 0x05E805B8, - reshqamatshebrew: 0x05E805B8, - reshqubuts: 0x05E805BB, - reshqubutshebrew: 0x05E805BB, - reshsegol: 0x05E805B6, - reshsegolhebrew: 0x05E805B6, - reshsheva: 0x05E805B0, - reshshevahebrew: 0x05E805B0, - reshtsere: 0x05E805B5, - reshtserehebrew: 0x05E805B5, reversedtilde: 0x223D, reviahebrew: 0x0597, reviamugrashhebrew: 0x0597, @@ -3477,7 +3398,6 @@ var GlyphsUnicode = { shaddadammaarabic: 0xFC61, shaddadammatanarabic: 0xFC5E, shaddafathaarabic: 0xFC60, - shaddafathatanarabic: 0x0651064B, shaddakasraarabic: 0xFC62, shaddakasratanarabic: 0xFC5F, shade: 0x2592, @@ -3674,7 +3594,6 @@ var GlyphsUnicode = { tchehfinalarabic: 0xFB7B, tchehinitialarabic: 0xFB7C, tchehmedialarabic: 0xFB7D, - tchehmeeminitialarabic: 0xFB7CFEE4, tcircle: 0x24E3, tcircumflexbelow: 0x1E71, tcommaaccent: 0x0163, From 8cb8de3092c90e1ceb6753372ef6ab4b0b33bd57 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Wed, 29 Feb 2012 22:57:54 -0600 Subject: [PATCH 30/40] Lint error --- src/fonts.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fonts.js b/src/fonts.js index d04fd1a4e..1992c0c7d 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -1947,7 +1947,7 @@ var Font = (function FontClosure() { for (var i = 0, ii = glyphs.length; i < ii; i++) { var code = glyphs[i].unicode; - var gid = ids[i] + var gid = ids[i]; var glyphName = glyphNames[gid] || encoding[code]; if (glyphName in GlyphsUnicode) { From 447098936cde2ceeb62d080edfd7418fceabc91d Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Thu, 1 Mar 2012 12:31:53 -0500 Subject: [PATCH 31/40] dummy README - testing bot's gh-pages update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 33bb30b66..c9631df61 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # PDF.JS - + pdf.js is an HTML5 technology experiment that explores building a faithful and efficient Portable Document Format (PDF) renderer without native code From fb1276c00f232fadbc269f04102fcf251c6f21f9 Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Thu, 1 Mar 2012 12:35:38 -0500 Subject: [PATCH 32/40] testing bot --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c9631df61..33bb30b66 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # PDF.JS - + pdf.js is an HTML5 technology experiment that explores building a faithful and efficient Portable Document Format (PDF) renderer without native code From 65811a7bfe5e730c770a1d368e51995f4040943f Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Thu, 1 Mar 2012 12:41:06 -0500 Subject: [PATCH 33/40] another bot test --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 33bb30b66..c9631df61 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # PDF.JS - + pdf.js is an HTML5 technology experiment that explores building a faithful and efficient Portable Document Format (PDF) renderer without native code From 38e3f32557a8a96fba2fea3851a0e2d6b0563598 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Thu, 1 Mar 2012 21:23:36 -0600 Subject: [PATCH 34/40] Add and fix pdfkit_compressed.pdf --- src/fonts.js | 30 +++++++++++++++++++++++++----- test/pdfs/.gitignore | 1 + test/pdfs/pdfkit_compressed.pdf | Bin 0 -> 8286 bytes test/test_manifest.json | 6 ++++++ 4 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 test/pdfs/pdfkit_compressed.pdf diff --git a/src/fonts.js b/src/fonts.js index 261a907f2..0d0e12fec 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -1881,6 +1881,7 @@ var Font = (function FontClosure() { var unusedUnicode = kCmapGlyphOffset; var glyphNames = properties.glyphNames || []; var encoding = properties.baseEncoding; + var differences = properties.differences; if (toFontChar && toFontChar.length > 0) { // checking if cmap is just identity map var isIdentity = true; @@ -1945,35 +1946,51 @@ var Font = (function FontClosure() { } } this.useToFontChar = true; - } else if (!this.isSymbolicFont && - (this.hasEncoding || properties.glyphNames)) { + } else if (!this.isSymbolicFont && (this.hasEncoding || + properties.glyphNames || differences.length > 0)) { // Re-encode cmap encoding to unicode, based on the 'post' table data - // or base encoding + // diffrence array or base encoding var reverseMap = []; for (var i = 0, ii = glyphs.length; i < ii; i++) reverseMap[glyphs[i].unicode] = i; for (var i = 0, ii = glyphs.length; i < ii; i++) { var code = glyphs[i].unicode; + var changeCode = false; var gid = ids[i]; - var glyphName = glyphNames[gid] || encoding[code]; + var glyphName = glyphNames[gid]; + if (!glyphName) { + glyphName = differences[code] || encoding[code]; + changeCode = true; + } if (glyphName in GlyphsUnicode) { var unicode = GlyphsUnicode[glyphName]; if (!unicode || reverseMap[unicode] === i) continue; // unknown glyph name or in its own place var destination = reverseMap[unicode]; + if (typeof destination === 'number' && destination > i) + continue; + var j = i; // Flipping unicodes while next destination unicode has assigned // glyph and future glyph can be assigned to unicode. while (typeof destination === 'number') { glyphs[j].unicode = unicode; reverseMap[unicode] = j; + if (changeCode) { + toFontChar[code] = unicode; + changeCode = false; + } code = glyphs[destination].unicode; gid = ids[destination]; - glyphName = glyphNames[gid] || encoding[code]; + glyphName = glyphNames[gid]; + if (!glyphName) { + glyphName = differences[code] || encoding[code]; + changeCode = true; + } unicode = GlyphsUnicode[glyphName]; if (!unicode || reverseMap[unicode] === j) { @@ -1994,7 +2011,10 @@ var Font = (function FontClosure() { glyphs[j].unicode = unicode; reverseMap[unicode] = j; + if (changeCode) + toFontChar[code] = unicode; } + this.useToFontChar = true; } } diff --git a/test/pdfs/.gitignore b/test/pdfs/.gitignore index 81b63290d..f14236860 100644 --- a/test/pdfs/.gitignore +++ b/test/pdfs/.gitignore @@ -19,6 +19,7 @@ !issue840.pdf !scan-bad.pdf !freeculture.pdf +!pdfkit_compressed.pdf !issue918.pdf !issue1249.pdf !smaskdim.pdf diff --git a/test/pdfs/pdfkit_compressed.pdf b/test/pdfs/pdfkit_compressed.pdf new file mode 100644 index 0000000000000000000000000000000000000000..f3e25216d231f4b18c14e33b00bfc7877650c30c GIT binary patch literal 8286 zcmZ{qcU;p+(D0Qip$P~GD2Cn%od8m#9layHgb+FjkkEVYh9XGsMUbvYlMaG(REi*= z2m%TLrS~UzchC9WJ@0-#ncbb=?CkE$>|fuo=_o4m3GxdA*eBC%91(Cu2nq(|RFo7F5EK%?3PCXeu&|&h*KfW> zfAg*E=z_v}1}VE>*D1oF2wNBcqydB5qa1)>iK|6O6m~CHfKLwf@B&eY014iwLycsj z7ws2ZG2~%dN%$`s$uY7aMmSbv{1O_kkXiBh%qga+$6=4&!@>z~cHv~wWYt-(c4RUd z(ECoV57V)uf(%zac{OTlonGfAr(wAY_+NYc75b}?{x|ejiSmG94KDfmhem1Az2kNQ9>c6q{o4Uy6THd^P`jA8d@F`Y;sG z41|p_5M%)J!6r)86l;J>~8MTx8pyH?avbUC)*yL zu&WQhSH**0jYkK9&4eQy!6%P!vHf!=MHmw5;pldiZ^5g(VlSoa=z&BjI6ypr!ot{m zLH?SEfB_&QM_ZHw5{Qk*FH0pjHm8nodmu;?0@XvfLg0UEunz%Y2zR_nIZ*KGef(bj z62D#HuW}F*78m`yHUxnZ!a@R9=lE~={9ZHFCPY4F{f&7o&&1S?ve|~yQLY~vzDTFD zAsW~~?m0ee#+HuuZ@~pNEJpLo^))Rv>fJT`j3qSkL5_LjgW?1yC%Vj=iHUvk^G5M zCsgJ=@?NV4VU)Lvf5~Wp!rt(;^;(8Ud-%@}RtvqO2;M)D0aMO*U_4syG)C*T!QFIj zI9VBWgwJVzIb`YHDsHgDPZ1v}6qBW=LwD0J6TO#-Kkn_OD17Tqh#yja>)L*~%BxE> zixi_`QRI3%p~cOlYb*xW0Xyreh4Nx739p&4W(|D=`0QAMI;F8#$QrK#wrR$c#M zjPkj(Z&~)^q_>>Jk0U+lzY*diXesrx&cAVpbiUQWkHq9;i|7=K4VACdnUk#w*G<+U z=if={ZB5nTzIfQp?M*h~%P5A!($Za9sp|{?RA{^=WXcH*4or`Ioa!iy2Tq|=O~zg7 zd4+KAFnT7O_5kxPan$`U=WbK47jw6SaKmxlT-b&4qjb-RxX$`Zj`-eb;9n!My4eCu*KsAaa=tWjc*Fz--?! z9ld-1&~Y25BdIii_!%*zwxb!~nBgGO*T@dES3^;!`P99BBqF*Q6WDw?9$cMY^=R|t zorHr6*J4BA#P+vmhj8Ao&cMw>Xs}e^aOYMXZSmBgAy_g~#Axt5gCEp=*BHs)AYD+z4%c1MU*O+GaSTf!$&NO}wFi(uD^tmXeL2Mqr zwk5nKdw=eUs~ATn#j2InJYo1(Rw?c4(KY(P0?DM9s>KZzNN?yvT6`&K3x?L$t#Rt} zb1Wh{n~u?26f!(-;P7qT*IKlPw)j7LU>ub={@jpN62~2+H0gDJv+=B**InW`{(jcv z7!e*8>GM#Xu2+0j3KlLvCV|R`q4qByKP(;}3)|DQ+pmqg*$;eOqaDx5K~wG8%g8ZC zUZ%Mt(AV?dNTK~SE%Uu0-QMu)Qp^X*wCX?0-zJ&-pw zuOOXzAGKgCIYa23lzMqwk1@s6$qf~#uzHjW+&vbQR`;V|8p`30 zN)46$Wp>m_VI&m>8=daFb4dXS<4E{YX5_s<7E&=B?$q6us`=K)Mknfi@$_SHdZq_* zX?k!z^>0Bd$MAN(nG*JtEFNcn0}|UCTD9=Ii-$hXQyy+CEO2#HV4TlQ2_!o~B>8wW>*h7hIW{rg&2~&{1aDWQVQK7F>hBck;4Cme$`?5=~{Tcr(8< zf^iLPXcal%iiSJ7(L2OBmdOdk1#}*swXKjG>(>FALqF9LJ;lws#_<$Cu}gE1eMP^4 z>-aNjl{-U?+RAm?1MHx1p-wQ}68|u4Q$PS|x2Tad%BJ>*Zia$$TW|QZ*AG_HqOirt z)aA0Sy)zsm$4uekG@N!i-f(ecnGJsB;Ygan@Q3)zh7V*#lX2Z6b{;3?X}r2dvM9ZR z!bFXI8$QL{ZRK5nd?iIoeivUl>oaA3V`vXQd*3rPnAcwKqCRZKc_!M}99I!H-*$lD z6LQ|2w>cHlA{E(*s+*QMc|XNPlgWIYF0=Wvr9{K07dOaCbIW_x#(Yfnl6F}}l5Qn@ z7P;lpmN4O3YNwKGwH94r%_H!F?nYwaRNV(TdOMX=*)~Jp>J%NjvNBU|J8sWbgDE0) zIVb>EEa`5+S&P3%Ino}vG+2rxZf>z$>E)oEbgiknc0@Cr0vP%DX7PDD)SeGyMzdD0 zy9c)=_7(&r=TW8-DD_d=7HdS)_-FG_>_g^(w5Zm7xNTcvEP=^{niWj9?&tgLZ!fs} zr-7=5kISl;TlPM_A5oL>qDnKD4@fsE`wE9QhZ#}AqPl-5#F^c&+1R;wj3Zw1!EnRZ z(R4L!!R>tMOA8l6>A(_`)lLXJSnyf&3(GuhontTQwCgEHBpJGuy5W@d#hhG)`lI#f zVpdm8 z&%SixOz)x5viMa7X_+fE+EEtyeh#vTm^UhQpjuZpYlSWkM!g+SX^7M#U?;qE?n$dk zYPyNn8T5TrT%uCpW}Mik@yqZJ;VwGiHk*^oQYF$TA`7+C4X5G(W1GDvY_-~!&}}V@ z)(5-!{_US1zXqxtCRtj!L>n$O8Iu}PKa8KUQC*&y+gbv7(+#;Eco-SZ+KJC@8j5=P zH&^!P=;}r;*Tbs&vmW2F3k`)`M|h{LHORSssg`|7Io&v&1@M=6no*Nc4dn6o9-^eb z>qn|vT(i2B$xp+&F1$M!F}BHHqy8jKy)u(KJFU^oiF-(wG@SM`=`D~I4U_|r3$`pG z1}SfEwjEtyc%8Iq_OgO0(2cD^>BFCGQxO?NAF5OuB&rgI3tQfp$bW@Nn2?#a3*NGI z~EPxClJNm)%Iu`VGStgm&4!4| z7it;j3NvZS5^OA{sttzyPWzeau$?RhjQO4DG10p%PDq@@C7ql8_eR4Qs&nO^tQ)sk92cl|25W?POUXhc15O zJOY?2U2*+8k&FvQci-*0f?Sh$v=on>AWC6sg3f?oEM7vb3fB!kB%aZ`KDy6j@XOa_ z_FlSJ!J@?W9H@19XWRVt{OtL#Zl2yCR|}KZ_JyWhT7n)MIVNbuY_FIh@FJ0cVj$SF z`pE_#+io52Ko@HlJHIr`=Z^XGJfFB3K_UZUw%39N1ow)xL)dquWe@8PKda%CM$pTr zZtXwx>+h@mtVhY+KC$+VH|PZ(>*iiPkgf6Ew3654!@LMUVo}4`aE?v{WW2SjP}11D zwSP|EujlrJZ*L7_#{k_Rk;_<^iGQ}yTd>+>hH}}>T&}!E+IGuZ7vuaY7$Yy;8q8=j z=MTyLzBr+V=6zi@2MzB!40lmqG156+*eHOMfowPSSbk*LhQEv~kYnkhL|zjPFxbyn zB~jvDq4(c?%W)8lnist_Jg5?7lSbouo;zci$yuCNgK$|eL6oYbYdO1?a`tDlY09>U z8mvyA);JzJ7pX=5WS<&psD7+btlW0uWbAES`LJJ)S1ILtBbV0Hz#pAWs~B4&e=D<} z``J9`-B`&Leb+Tv;F6+y@ePA8|1)`BNZ9RrT;vdXkge)Slm||0 zWmmt%OOKEcHQ0VC@42>YY|eXNgW;#=n^;os*4QEiPnkRW zv`m=UOh`w}$NGlTl`uq8Pm?*>zlaaC&6}fIQi#;j`sA`S{W7y#Gp?O3ZjBX>I~yw1RGslg_m#fG z)T{=kl`qm0VbQsg$^_ffEjqpiRyow;O4NgBbg^j@{>Rld#!8<-hC?X-yxHgKt|0U6 z>bm2Pxq0ERD*9u$c)oUF>eIS~(16)_9g4$G`=QuUA((Y*9bME7-F7g&c6{H?Z} z>VSRIFx6zEg+EhCE6bpOijlR@xjasDYyqY>Bq}7CM6J>^m{h2b6Hw4a5bFBe6`lBk zI?9)HuQ1$!NbM3zjBJO_HoykUiri_nPWWrS_9Lmd8>uw$=9buX`wZFXhES- z0p%3_L7OW*Ocb=YwuAg5D%g_mGI=B=xy_Q;s5gA4dy=HZ!(Dr~5xw6$8nCNhc*WFzSt))ePsWEFZAJ+eF?-TyWQebto#bM)Ct0dlNzc=FYSAVJNhPYHoqKk% z#butF_FC%4)B6-q1zgo3Q}M^*vk6(3XH$yW?ZT@aB#qUtS{<9g54w}SCyHj3KF3$j z2f+@f8`IeRtaneTO zO}DM~YPe)%V@U6dY0fT>K5=WD87vzXp+S&RHJ)mkq~US#{(Y`$9i`V%Yh?)Y2l~NL zZZc;a0Q)t+vj(v~^TY%ny9>h?g==GZeX)KNuA9hvF$1@@@7MIS+t6V={QGbWvtzY` z@_dr^9&9GFzwV0@<)PA+=Zr6Y8r#(RaLAk;s%+Y8Ta<1a``8RoM71KR@NJgNS^x7l zy+?x$)b?rRnXt+?WL7wu9`gLf-%IG`9PHr6ejjI8^?9oMbqJA6&;3Z*W^QeAYmY5R z=Gab=9TbUXPA~4#oy7oPNAxUiv04Jyxe!wk|1C0sv^M z4QBCk+sLH7k#H3&W!6jS6uw5a8=rOOLNohpDqF0Z@S$sS@Ga8@dAe`5TC3q=nxhw` zz3NNjc^?h7wl^N!Kk{cZ=&kO%%NiZztxr(mo4{ooG`z_7{EcNAddQw`h2*_IP&qN2 zY#LChBl=F=dWsSEIA+X_5%+%443P07Twxr5DlK-7co9ZWMDj7A^FZ*Bw9-0d|*AZh0l4V^^SljI~{G=Cb`s4c-f{Cbr zCwRHmRI@Ra)yg`A{0eESrgdbAI9=et{-KY(Hp;~z0O^?`nzF4J4Bj6%HH%a3?khz0 zk%y94zC2r?)Tv|W(`L`WP&>+(gfnY0o08!tm)+Ft5r_%i?Ayyw5stBgXq1@L2bZf1 zD}Hd6JL3UiI+aSO`Yz^`OgY9HX(w+^4ddx_`781Ip#*nAj!36jf8J-_h}jrmYv0L; zzL^{Git_MY`W|yADPyVwfVE3P^$(3p-M19)?x{=a$q~vvK`A0{5~V!K&`Lomo+(C^ zuwX#gQG;R{c7#Ypm4xK2C!_irD2QasYSVY17xSz+BEVRG)~xFUHu;Lrtwn!CMC9Wi z_q9<5(HJDD5y=t*2K}&dXpjgiKd$Ig2950IMcjL%CrKnsY;N#8g6`dnYR07bdkXB_ z-|SmMWUrmeeQ86&PVzs#qK#V>67kD1$`G4V6Eg17Od`zm$lN# zMRTswNix_jIY{ym-6Y_|=VNf+539IS^z7ws^Cd8=DERInRdSG7j|R@8AS0}lZU;h? zg-DVFQssIxSVveVgtUl+%hXv%GT>NU*C0wgWAaGDxF^B4CRqTQCrxQLvRe?5i6`{c zgh-fZWrUd04z+0)#0`0gi@Vzz@qvtY#GlemYCYmw zm9d*85p;)3Opw=S+G-hFa>E^*5arvmGpr+C1vZzgSYHyS_1KlRn@emXWD#qxB0gN# zgAout$DM`2v2v%u3oA2Vg4m-z3GBUI)j-HLVtf#Rb$a=rXY(5ShoT6&Q66!XjJsXz zR%{TjPnU7Y0>wX8pQvWsm184U&3GQ7Dx=Flw?IKkO7i+B{LIOKbTut)^`}x~Bx8pk z3T6DsnT4g8N{}gQxR&^Eb=L|8TAue#0GzERe~vZgq-1<@`&o3fZ?bz18t_FPrMEsl z{>^N^cEmk&>n+s2I^<|3>J)G~Lvnf=5vfYa%ES=!CS#@_RQ!qN@j zJb35(2=J2xzXnr`{tXt{9{oP~-f&svgQFQmHq{4sv@IgdgHLYpk+W~%jM?84j_-js(|Hb>$ zT8+-1-zvmqvo4N@YcjbN{@HZ^4v zaspzZ@}G=i+){6H@S{`|Z&967IYggP^PO_$ghV}U&pxlCWhK1!X5We!ZtQfFI&c1bOA>!y0i876F>aJ)=P;4n()a6yB}g-Eq62#8N@dWHJnAGwJ*CIV4@3IT4bFo#HOxdfvkP05>zc#GMB5S1I~CgQ+#_Xh=x+0JK06k)WQ zyl3zFz9>vih>5Jq8B;#H;HaOYuJ`IgL3Ehrcrnp$tQ`bsCir*WEe7{K)2Fbl zXq-W0eamQtIc@h&658kG2Cg?o7o%T!omG7BKC-{`_}PV6^uz4h+b!M?k!Bs7i3|%I z94nMzRJc7L?!sL`yTwN_;Fgq6qD06Z6{)Pt4d(qx0?Bi?ZTkUZi2KR2JX@$&YjX!YSSWGWnWAhuN%0Zi zn`sMPh7~EyYrj4iD2C{5z!Q<7_a_zY`b#-;_YI{CrKiG3cr~U9IxO^;6r|+ zX%~Hdu7A$YJ3GvTp8Z%F?--O7-aUJFRuOF(5G%XiAJ2i74&G~T3OXHrj(Bx26yGQx z?BH|6qkj#U8z!d?`_i5*x`@=_p+c*WOXjze$=bhT?8NMncN&*FrY*%DtqgOlToM$} z2rd4XVEw{0|0O{GfSf< zpoAb+2ug@tF)0^_JrXD^goTPInCn9zmd>bR38y3U0o>jNb~Sx~gkJG8un-m*T^;3W z!Y3pyj%8*LHx-zpy~AH0jsKbm{00qw5u(4Z^nYQWzp0_nzp$ajq&gy8faKaI|DZ_p zLgDhHycrRb=0apIBmF%2jo5d}^h|)yU7c2&4|8AOuB+c!gV?WON|9pn2Ndj6P&{ZZ z8CY9g=LW826_pavT&2&wA6AKP<9XQ*zu$4+%*iZp#FIP7>_L+7xa94Ne4-j{Crs>a zC3fi;mmc4XKa>R*#V5tT#7|^*J>)*mzC$6TwcsV$?`qz}^&Qba^Nm7{k+zAaLiNjL zX%BD#UnzCX6{^8Au@PKY)Bz(=wt(H#G_=c zj!887;mI3)n!wIrjbyR^qj>&C&t_uSpo@x%0l{Dips0WVP)Jk&%lm|XjltOdYW&Lv z`|Ya#`zMUG7ZL|sT=~9AnU4p|4j=#&6b1niVBMY?STKQ6T|}3s|JMsqr=wN zzjcB_SXTHiJ8T{PM<)cvlJI}oiHQ7HCn|#Fu>Z0X6UH*&f9bFk`` Date: Thu, 1 Mar 2012 22:01:39 -0600 Subject: [PATCH 35/40] Optimization --- src/fonts.js | 48 ++++-------------------------------------------- 1 file changed, 4 insertions(+), 44 deletions(-) diff --git a/src/fonts.js b/src/fonts.js index 0d0e12fec..df0acbbc5 100644 --- a/src/fonts.js +++ b/src/fonts.js @@ -1966,51 +1966,11 @@ var Font = (function FontClosure() { } if (glyphName in GlyphsUnicode) { var unicode = GlyphsUnicode[glyphName]; - if (!unicode || reverseMap[unicode] === i) - continue; // unknown glyph name or in its own place + if (!unicode || (unicode in reverseMap)) + continue; // unknown glyph name or its place is taken - var destination = reverseMap[unicode]; - if (typeof destination === 'number' && destination > i) - continue; - - var j = i; - // Flipping unicodes while next destination unicode has assigned - // glyph and future glyph can be assigned to unicode. - while (typeof destination === 'number') { - glyphs[j].unicode = unicode; - reverseMap[unicode] = j; - if (changeCode) { - toFontChar[code] = unicode; - changeCode = false; - } - - code = glyphs[destination].unicode; - gid = ids[destination]; - glyphName = glyphNames[gid]; - if (!glyphName) { - glyphName = differences[code] || encoding[code]; - changeCode = true; - } - - unicode = GlyphsUnicode[glyphName]; - if (!unicode || reverseMap[unicode] === j) { - unicode = 0; - break; // unknown glyph name or in its own place - } - - j = destination; - destination = reverseMap[unicode]; - } - - if (!unicode) { - // Future glyph cannot be assigned to unicode, generate new one. - while (reverseMap[unusedUnicode]) - unusedUnicode++; - unicode = unusedUnicode++; - } - - glyphs[j].unicode = unicode; - reverseMap[unicode] = j; + glyphs[i].unicode = unicode; + reverseMap[unicode] = i; if (changeCode) toFontChar[code] = unicode; } From b870cbad0f38235d1bc19efd8f331f85aec34e76 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Fri, 2 Mar 2012 07:11:24 -0600 Subject: [PATCH 36/40] Move custom style --- src/util.js | 51 --------------------------------------------------- web/viewer.js | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 51 deletions(-) diff --git a/src/util.js b/src/util.js index b0739ec3a..93bd36b55 100644 --- a/src/util.js +++ b/src/util.js @@ -174,57 +174,6 @@ var Util = (function UtilClosure() { return Util; })(); -// optimised CSS custom property getter/setter -var CustomStyle = (function CustomStyleClosure() { - - // As noted on: http://www.zachstronaut.com/posts/2009/02/17/ - // animate-css-transforms-firefox-webkit.html - // in some versions of IE9 it is critical that ms appear in this list - // before Moz - var prefixes = ['ms', 'Moz', 'Webkit', 'O']; - var _cache = { }; - - function CustomStyle() { - } - - CustomStyle.getProp = function get(propName, element) { - // check cache only when no element is given - if (arguments.length == 1 && typeof _cache[propName] == 'string') { - return _cache[propName]; - } - - element = element || document.documentElement; - var style = element.style, prefixed, uPropName; - - // test standard property first - if (typeof style[propName] == 'string') { - return (_cache[propName] = propName); - } - - // capitalize - uPropName = propName.charAt(0).toUpperCase() + propName.slice(1); - - // test vendor specific properties - for (var i = 0, l = prefixes.length; i < l; i++) { - prefixed = prefixes[i] + uPropName; - if (typeof style[prefixed] == 'string') { - return (_cache[propName] = prefixed); - } - } - - //if all fails then set to undefined - return (_cache[propName] = 'undefined'); - } - - CustomStyle.setProp = function set(propName, element, str) { - var prop = this.getProp(propName); - if (prop != 'undefined') - element.style[prop] = str; - } - - return CustomStyle; -})(); - var PDFStringTranslateTable = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x2D8, 0x2C7, 0x2C6, 0x2D9, 0x2DD, 0x2DB, 0x2DA, 0x2DC, 0, 0, 0, 0, 0, 0, 0, diff --git a/web/viewer.js b/web/viewer.js index 9f477f3a7..77bbc2c0e 100644 --- a/web/viewer.js +++ b/web/viewer.js @@ -1022,6 +1022,57 @@ var DocumentOutlineView = function documentOutlineView(outline) { } }; +// optimised CSS custom property getter/setter +var CustomStyle = (function CustomStyleClosure() { + + // As noted on: http://www.zachstronaut.com/posts/2009/02/17/ + // animate-css-transforms-firefox-webkit.html + // in some versions of IE9 it is critical that ms appear in this list + // before Moz + var prefixes = ['ms', 'Moz', 'Webkit', 'O']; + var _cache = { }; + + function CustomStyle() { + } + + CustomStyle.getProp = function get(propName, element) { + // check cache only when no element is given + if (arguments.length == 1 && typeof _cache[propName] == 'string') { + return _cache[propName]; + } + + element = element || document.documentElement; + var style = element.style, prefixed, uPropName; + + // test standard property first + if (typeof style[propName] == 'string') { + return (_cache[propName] = propName); + } + + // capitalize + uPropName = propName.charAt(0).toUpperCase() + propName.slice(1); + + // test vendor specific properties + for (var i = 0, l = prefixes.length; i < l; i++) { + prefixed = prefixes[i] + uPropName; + if (typeof style[prefixed] == 'string') { + return (_cache[propName] = prefixed); + } + } + + //if all fails then set to undefined + return (_cache[propName] = 'undefined'); + } + + CustomStyle.setProp = function set(propName, element, str) { + var prop = this.getProp(propName); + if (prop != 'undefined') + element.style[prop] = str; + } + + return CustomStyle; +})(); + var TextLayerBuilder = function textLayerBuilder(textLayerDiv) { this.textLayerDiv = textLayerDiv; From f6ba3843bd59337a6760e8df85aedd53685d237e Mon Sep 17 00:00:00 2001 From: Artur Adib Date: Sat, 3 Mar 2012 14:01:31 -0500 Subject: [PATCH 37/40] Using ShellJS --- external/shelljs/LICENSE | 26 + external/shelljs/README.md | 316 +++++++++ external/shelljs/global.js | 3 + external/shelljs/make.js | 46 ++ external/shelljs/package.json | 12 + external/shelljs/shell.js | 1207 +++++++++++++++++++++++++++++++++ make.js | 401 +++++++++++ 7 files changed, 2011 insertions(+) create mode 100644 external/shelljs/LICENSE create mode 100644 external/shelljs/README.md create mode 100644 external/shelljs/global.js create mode 100644 external/shelljs/make.js create mode 100644 external/shelljs/package.json create mode 100644 external/shelljs/shell.js create mode 100755 make.js diff --git a/external/shelljs/LICENSE b/external/shelljs/LICENSE new file mode 100644 index 000000000..1b35ee9fb --- /dev/null +++ b/external/shelljs/LICENSE @@ -0,0 +1,26 @@ +Copyright (c) 2012, Artur Adib +All rights reserved. + +You may use this project under the terms of the New BSD license as follows: + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Artur Adib nor the + names of the contributors may be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL ARTUR ADIB BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/external/shelljs/README.md b/external/shelljs/README.md new file mode 100644 index 000000000..01b99a5f8 --- /dev/null +++ b/external/shelljs/README.md @@ -0,0 +1,316 @@ +# ShellJS - Unix shell commands for Node.js [![Build Status](https://secure.travis-ci.org/arturadib/shelljs.png)](http://travis-ci.org/arturadib/shelljs) + +ShellJS is a **portable** (Windows included) implementation of Unix shell commands on top of the Node.js API. You can use it to eliminate your shell script's dependency on Unix while still keeping its familiar and powerful commands. + +The project is both [unit-tested](http://travis-ci.org/arturadib/shelljs) and battle-tested at Mozilla's [pdf.js](http://github.com/mozilla/pdf.js). + + +### Example + +```javascript +require('shelljs/global'); + +// Copy files to release dir +mkdir('-p', 'out/Release'); +cp('-R', 'lib/*.js', 'out/Release'); + +// Replace macros in each .js file +cd('lib'); +for (file in ls('*.js')) { + sed('-i', 'BUILD_VERSION', 'v0.1.2', file); + sed('-i', /.*REMOVE_THIS_LINE.*\n/, '', file); + sed('-i', /.*REPLACE_LINE_WITH_MACRO.*\n/, cat('macro.js'), file); +} +cd('..'); + +// Run external tool synchronously +if (exec('git commit -am "Auto-commit"').code !== 0) { + echo('Error: Git commit failed'); + exit(1); +} +``` + +### Global vs. Local + +The example above uses the convenience script `shelljs/global` to reduce verbosity. If polluting your global namespace is not desirable, simply require `shelljs`. + +Example: + +```javascript +var shell = require('shelljs'); +shell.echo('hello world'); +``` + +### Make tool + +A convenience script `shelljs/make` is also provided to mimic the behavior of a Unix Makefile. In this case all shell objects are global, and command line arguments will cause the script to execute only the corresponding function in the global `target` object. To avoid redundant calls, target functions are executed only once per script. + +Example: + +```javascript +// +// Example file: make.js +// +require('shelljs/make'); + +target.all = function() { + target.bundle(); + target.docs(); +} + +// Bundle JS files +target.bundle = function() { + cd(__dirname); + mkdir('build'); + cd('lib'); + cat('*.js').to('../build/output.js'); +} + +// Generate docs +target.docs = function() { + cd(__dirname); + mkdir('docs'); + cd('lib'); + for (file in ls('*.js')) { + var text = grep('//@', file); // extract special comments + text.replace('//@', ''); // remove comment tags + text.to('docs/my_docs.md'); + } +} +``` + +To run the target `all`, call the above script without arguments: `$ node make`. To run the target `docs`: `$ node make docs`, and so on. + +### Installing + +Via npm: + +```bash +$ npm install shelljs +``` + +Or simply copy `shell.js` into your project's directory, and `require()` accordingly. + + + + + +# Command reference + + +All commands run synchronously, unless otherwise stated. + + +#### cd('dir') +Changes to directory `dir` for the duration of the script + +#### pwd() +Returns the current directory. + +#### ls([options ,] path [,path ...]) +#### ls([options ,] path_array) +Available options: + ++ `-R`: recursive ++ `-a`: all files (include files beginning with `.`) + +Examples: + +```javascript +ls('projs/*.js'); +ls('-R', '/users/me', '/tmp'); +ls('-R', ['/users/me', '/tmp']); // same as above +``` + +Returns list of files in the given path, or in current directory if no path provided. +For convenient iteration via `for (file in ls())`, the format returned is a hash object: +`{ 'file1':null, 'dir1/file2':null, ...}`. + +#### cp('[options ,] source [,source ...], dest') +#### cp('[options ,] source_array, dest') +Available options: + ++ `-f`: force ++ `-r, -R`: recursive + +Examples: + +```javascript +cp('file1', 'dir1'); +cp('-Rf', '/tmp/*', '/usr/local/*', '/home/tmp'); +cp('-Rf', ['/tmp/*', '/usr/local/*'], '/home/tmp'); // same as above +``` + +Copies files. The wildcard `*` is accepted. + +#### rm([options ,] file [, file ...]) +#### rm([options ,] file_array) +Available options: + ++ `-f`: force ++ `-r, -R`: recursive + +Examples: + +```javascript +rm('-rf', '/tmp/*'); +rm('some_file.txt', 'another_file.txt'); +rm(['some_file.txt', 'another_file.txt']); // same as above +``` + +Removes files. The wildcard `*` is accepted. + +#### mv(source [, source ...], dest') +#### mv(source_array, dest') +Available options: + ++ `f`: force + +Examples: + +```javascript +mv('-f', 'file', 'dir/'); +mv('file1', 'file2', 'dir/'); +mv(['file1', 'file2'], 'dir/'); // same as above +``` + +Moves files. The wildcard `*` is accepted. + +#### mkdir([options ,] dir [, dir ...]) +#### mkdir([options ,] dir_array) +Available options: + ++ `p`: full path (will create intermediate dirs if necessary) + +Examples: + +```javascript +mkdir('-p', '/tmp/a/b/c/d', '/tmp/e/f/g'); +mkdir('-p', ['/tmp/a/b/c/d', '/tmp/e/f/g']); // same as above +``` + +Creates directories. + +#### cat(file [, file ...]) +#### cat(file_array) + +Examples: + +```javascript +var str = cat('file*.txt'); +var str = cat('file1', 'file2'); +var str = cat(['file1', 'file2']); // same as above +``` + +Returns a string containing the given file, or a concatenated string +containing the files if more than one file is given (a new line character is +introduced between each file). Wildcard `*` accepted. + +#### 'string'.to(file) + +Examples: + +```javascript +cat('input.txt').to('output.txt'); +``` + +Analogous to the redirection operator `>` in Unix, but works with JavaScript strings (such as +those returned by `cat`, `grep`, etc). _Like Unix redirections, `to()` will overwrite any existing file!_ + +#### sed([options ,] search_regex, replace_str, file) +Available options: + ++ `-i`: Replace contents of 'file' in-place. _Note that no backups will be created!_ + +Examples: + +```javascript +sed('-i', 'PROGRAM_VERSION', 'v0.1.3', 'source.js'); +sed(/.*DELETE_THIS_LINE.*\n/, '', 'source.js'); +``` + +Reads an input string from `file` and performs a JavaScript `replace()` on the input +using the given search regex and replacement string. Returns the new string after replacement. + +#### grep(regex_filter, file [, file ...]) +#### grep(regex_filter, file_array) + +Examples: + +```javascript +grep('GLOBAL_VARIABLE', '*.js'); +``` + +Reads input string from given files and returns a string containing all lines of the +file that match the given `regex_filter`. Wildcard `*` accepted. + +#### which(command) + +Examples: + +```javascript +var nodeExec = which('node'); +``` + +Searches for `command` in the system's PATH. On Windows looks for `.exe`, `.cmd`, and `.bat` extensions. +Returns string containing the absolute path to the command. + +#### echo(string [,string ...]) + +Examples: + +```javascript +echo('hello world'); +var str = echo('hello world'); +``` + +Prints string to stdout, and returns string with additional utility methods +like `.to()`. + +#### exit(code) +Exits the current process with the given exit code. + +#### env['VAR_NAME'] +Object containing environment variables (both getter and setter). Shortcut to process.env. + +#### exec(command [, options] [, callback]) +Available options (all `false` by default): + ++ `async`: Asynchronous execution. Needs callback. ++ `silent`: Do not echo program output to console. + +Examples: + +```javascript +var version = exec('node --version', {silent:true}).output; +``` + +Executes the given `command` _synchronously_, unless otherwise specified. +When in synchronous mode returns the object `{ code:..., output:... }`, containing the program's +`output` (stdout + stderr) and its exit `code`. Otherwise the `callback` gets the +arguments `(code, output)`. + +## Non-Unix commands + + +#### tempdir() +Searches and returns string containing a writeable, platform-dependent temporary directory. +Follows Python's [tempfile algorithm](http://docs.python.org/library/tempfile.html#tempfile.tempdir). + +#### exists(path [, path ...]) +#### exists(path_array) +Returns true if all the given paths exist. + +#### error() +Tests if error occurred in the last command. Returns `null` if no error occurred, +otherwise returns string explaining the error + +#### verbose() +Enables all output (default) + +#### silent() +Suppresses all output, except for explict `echo()` calls diff --git a/external/shelljs/global.js b/external/shelljs/global.js new file mode 100644 index 000000000..83a27e69a --- /dev/null +++ b/external/shelljs/global.js @@ -0,0 +1,3 @@ +var shell = require('./shell.js'); +for (cmd in shell) + global[cmd] = shell[cmd]; diff --git a/external/shelljs/make.js b/external/shelljs/make.js new file mode 100644 index 000000000..9c92dadc8 --- /dev/null +++ b/external/shelljs/make.js @@ -0,0 +1,46 @@ +require('./global'); + +global.target = {}; + +// This ensures we only execute the script targets after the entire script has +// been evaluated +var args = process.argv.slice(2); +setTimeout(function() { + + if (args.length === 1 && args[0] === '--help') { + console.log('Available targets:'); + for (t in target) + console.log(' ' + t); + return; + } + + // Wrap targets to prevent duplicate execution + for (t in target) { + (function(t, oldTarget){ + + // Wrap it + target[t] = function(force) { + if (oldTarget.done && !force) + return; + oldTarget.done = true; + return oldTarget(arguments); + } + + })(t, target[t]); + } + + // Execute desired targets + if (args.length > 0) { + args.forEach(function(arg) { + if (arg in target) + target[arg](); + else { + console.log('no such target: ' + arg); + exit(1); + } + }); + } else if ('all' in target) { + target.all(); + } + +}, 0); diff --git a/external/shelljs/package.json b/external/shelljs/package.json new file mode 100644 index 000000000..9499680e9 --- /dev/null +++ b/external/shelljs/package.json @@ -0,0 +1,12 @@ +{ "name": "shelljs" +, "version": "0.0.2pre1" +, "author": "Artur Adib " +, "description": "Portable Unix shell commands for Node.js" +, "keywords": ["unix", "shell", "makefile", "make", "jake", "synchronous"] +, "repository": "git://github.com/arturadib/shelljs" +, "homepage": "http://github.com/arturadib/shelljs" +, "main": "./shell.js" +, "scripts": { + "test": "node scripts/run-tests" + } +} diff --git a/external/shelljs/shell.js b/external/shelljs/shell.js new file mode 100644 index 000000000..2d9a5a787 --- /dev/null +++ b/external/shelljs/shell.js @@ -0,0 +1,1207 @@ +// +// ShellJS +// Unix shell commands on top of Node's API +// +// Copyright (c) 2012 Artur Adib +// http://github.com/arturadib/shelljs +// + +var fs = require('fs'), + path = require('path'), + util = require('util'), + vm = require('vm'), + child = require('child_process'), + os = require('os'); + +// Node shims for < v0.7 +fs.existsSync = fs.existsSync || path.existsSync; + +var state = { + error: null, + fatal: false, + silent: false, + currentCmd: 'shell.js', + tempDir: null + }, + platform = os.type().match(/^Win/) ? 'win' : 'unix'; + + +//@ +//@ All commands run synchronously, unless otherwise stated. +//@ + + +//@ +//@ #### cd('dir') +//@ Changes to directory `dir` for the duration of the script +function _cd(options, dir) { + if (!dir) + error('directory not specified'); + + if (!fs.existsSync(dir)) + error('no such file or directory: ' + dir); + + if (fs.existsSync(dir) && !fs.statSync(dir).isDirectory()) + error('not a directory: ' + dir); + + process.chdir(dir); +}; +exports.cd = wrap('cd', _cd); + +//@ +//@ #### pwd() +//@ Returns the current directory. +function _pwd(options) { + var pwd = path.resolve(process.cwd()); + return ShellString(pwd); +}; +exports.pwd = wrap('pwd', _pwd); + +//@ +//@ #### ls([options ,] path [,path ...]) +//@ #### ls([options ,] path_array) +//@ Available options: +//@ +//@ + `-R`: recursive +//@ + `-a`: all files (include files beginning with `.`) +//@ +//@ Examples: +//@ +//@ ```javascript +//@ ls('projs/*.js'); +//@ ls('-R', '/users/me', '/tmp'); +//@ ls('-R', ['/users/me', '/tmp']); // same as above +//@ ``` +//@ +//@ Returns list of files in the given path, or in current directory if no path provided. +//@ For convenient iteration via `for (file in ls())`, the format returned is a hash object: +//@ `{ 'file1':null, 'dir1/file2':null, ...}`. +function _ls(options, paths) { + options = parseOptions(options, { + 'R': 'recursive', + 'a': 'all' + }); + + if (!paths) + paths = ['.']; + else if (typeof paths === 'object') + paths = paths; // assume array + else if (typeof paths === 'string') + paths = [].slice.call(arguments, 1); + + var hash = {}; + + function pushHash(file, query) { + // hidden file? + if (path.basename(file)[0] === '.') { + // not explicitly asking for hidden files? + if (!options.all && !(path.basename(query)[0] === '.' && path.basename(query).length > 1)) + return; + } + + hash[file] = null; + } + + paths.forEach(function(p) { + if (fs.existsSync(p)) { + // Simple file? + if (fs.statSync(p).isFile()) { + pushHash(p, p); + return; // continue + } + + // Simple dir? + if (fs.statSync(p).isDirectory()) { + // Iterate over p contents + fs.readdirSync(p).forEach(function(file) { + pushHash(file, p); + + // Recursive + var oldDir = _pwd(); + _cd('', p); + if (fs.statSync(file).isDirectory() && options.recursive) + hash = extend(hash, _ls('-R', file+'/*')); + _cd('', oldDir); + }); + return; // continue + } + } + + // p does not exist - possible wildcard present + + var basename = path.basename(p); + var dirname = path.dirname(p); + // Wildcard present on an existing dir? (e.g. '/tmp/*.js') + if (basename.search(/\*/) > -1 && fs.existsSync(dirname) && fs.statSync(dirname).isDirectory) { + // Escape special regular expression chars + var regexp = basename.replace(/(\^|\$|\(|\)|\<|\>|\[|\]|\{|\}|\.|\+|\?)/g, '\\$1'); + // Translates wildcard into regex + regexp = '^' + regexp.replace(/\*/g, '.*'); + // Iterate over directory contents + fs.readdirSync(dirname).forEach(function(file) { + if (file.match(new RegExp(regexp))) { + pushHash(path.normalize(dirname+'/'+file), basename); + + // Recursive + var pp = dirname + '/' + file; + if (fs.statSync(pp).isDirectory() && options.recursive) + hash = extend(hash, _ls('-R', pp+'/*')); + } + }); // forEach + return; + } + + error('no such file or directory: ' + p, true); + }); + + return hash; +}; +exports.ls = wrap('ls', _ls); + + +//@ +//@ #### cp('[options ,] source [,source ...], dest') +//@ #### cp('[options ,] source_array, dest') +//@ Available options: +//@ +//@ + `-f`: force +//@ + `-r, -R`: recursive +//@ +//@ Examples: +//@ +//@ ```javascript +//@ cp('file1', 'dir1'); +//@ cp('-Rf', '/tmp/*', '/usr/local/*', '/home/tmp'); +//@ cp('-Rf', ['/tmp/*', '/usr/local/*'], '/home/tmp'); // same as above +//@ ``` +//@ +//@ Copies files. The wildcard `*` is accepted. +function _cp(options, sources, dest) { + options = parseOptions(options, { + 'f': 'force', + 'R': 'recursive', + 'r': 'recursive' + }); + + // Get sources, dest + if (arguments.length < 3) { + error('missing and/or '); + } else if (arguments.length > 3) { + sources = [].slice.call(arguments, 1, arguments.length - 1); + dest = arguments[arguments.length - 1]; + } else if (typeof sources === 'string') { + sources = [sources]; + } else if ('length' in sources) { + sources = sources; // no-op for array + } else { + error('invalid arguments'); + } + + // Dest is not existing dir, but multiple sources given + if ((!fs.existsSync(dest) || !fs.statSync(dest).isDirectory()) && sources.length > 1) + error('dest is not a directory (too many sources)'); + + // Dest is an existing file, but no -f given + if (fs.existsSync(dest) && fs.statSync(dest).isFile() && !options.force) + error('dest file already exists: ' + dest); + + sources = expand(sources); + + sources.forEach(function(src) { + if (!fs.existsSync(src)) { + error('no such file or directory: '+src, true); + return; // skip file + } + + // If here, src exists + + if (fs.statSync(src).isDirectory()) { + if (!options.recursive) { + // Non-Recursive + log(src + ' is a directory (not copied)'); + } else { + // Recursive + // 'cp /a/source dest' should create 'source' in 'dest' + var newDest = dest+'/'+path.basename(src), + checkDir = fs.statSync(src); + try { + fs.mkdirSync(newDest, checkDir.mode); + } catch (e) { + //if the directory already exists, that's okay + if (e.code !== 'EEXIST') throw e; + } + cpdirSyncRecursive(src, newDest, {force: options.force}); + } + return; // done with dir + } + + // If here, src is a file + + // When copying to '/path/dir': + // thisDest = '/path/dir/file1' + var thisDest = dest; + if (fs.existsSync(dest) && fs.statSync(dest).isDirectory()) + thisDest = path.normalize(dest + '/' + path.basename(src)); + + if (fs.existsSync(thisDest) && !options.force) { + error('dest file already exists: ' + thisDest, true); + return; // skip file + } + + copyFileSync(src, thisDest); + }); // forEach(src) +}; // cp +exports.cp = wrap('cp', _cp); + +//@ +//@ #### rm([options ,] file [, file ...]) +//@ #### rm([options ,] file_array) +//@ Available options: +//@ +//@ + `-f`: force +//@ + `-r, -R`: recursive +//@ +//@ Examples: +//@ +//@ ```javascript +//@ rm('-rf', '/tmp/*'); +//@ rm('some_file.txt', 'another_file.txt'); +//@ rm(['some_file.txt', 'another_file.txt']); // same as above +//@ ``` +//@ +//@ Removes files. The wildcard `*` is accepted. +function _rm(options, files) { + options = parseOptions(options, { + 'f': 'force', + 'r': 'recursive', + 'R': 'recursive' + }); + if (!files) + error('no paths given'); + + if (typeof files === 'string') + files = [].slice.call(arguments, 1); + // if it's array leave it as it is + + files = expand(files); + + files.forEach(function(file) { + if (!fs.existsSync(file)) { + // Path does not exist, no force flag given + if (!options.force) + error('no such file or directory: '+file, true); + + return; // skip file + } + + // If here, path exists + + // Remove simple file + if (fs.statSync(file).isFile()) { + fs.unlinkSync(file); + return; + } + + // Path is an existing directory, but no -r flag given + if (fs.statSync(file).isDirectory() && !options.recursive) { + error('path is a directory', true); + return; // skip path + } + + // Recursively remove existing directory + if (fs.statSync(file).isDirectory() && options.recursive) { + rmdirSyncRecursive(file); + } + }); // forEach(file) +}; // rm +exports.rm = wrap('rm', _rm); + +//@ +//@ #### mv(source [, source ...], dest') +//@ #### mv(source_array, dest') +//@ Available options: +//@ +//@ + `f`: force +//@ +//@ Examples: +//@ +//@ ```javascript +//@ mv('-f', 'file', 'dir/'); +//@ mv('file1', 'file2', 'dir/'); +//@ mv(['file1', 'file2'], 'dir/'); // same as above +//@ ``` +//@ +//@ Moves files. The wildcard `*` is accepted. +function _mv(options, sources, dest) { + options = parseOptions(options, { + 'f': 'force' + }); + + // Get sources, dest + if (arguments.length < 3) { + error('missing and/or '); + } else if (arguments.length > 3) { + sources = [].slice.call(arguments, 1, arguments.length - 1); + dest = arguments[arguments.length - 1]; + } else if (typeof sources === 'string') { + sources = [sources]; + } else if ('length' in sources) { + sources = sources; // no-op for array + } else { + error('invalid arguments'); + } + + sources = expand(sources); + + // Dest is not existing dir, but multiple sources given + if ((!fs.existsSync(dest) || !fs.statSync(dest).isDirectory()) && sources.length > 1) + error('dest is not a directory (too many sources)'); + + // Dest is an existing file, but no -f given + if (fs.existsSync(dest) && fs.statSync(dest).isFile() && !options.force) + error('dest file already exists: ' + dest); + + sources.forEach(function(src) { + if (!fs.existsSync(src)) { + error('no such file or directory: '+src, true); + return; // skip file + } + + // If here, src exists + + // When copying to '/path/dir': + // thisDest = '/path/dir/file1' + var thisDest = dest; + if (fs.existsSync(dest) && fs.statSync(dest).isDirectory()) + thisDest = path.normalize(dest + '/' + path.basename(src)); + + if (fs.existsSync(thisDest) && !options.force) { + error('dest file already exists: ' + thisDest, true); + return; // skip file + } + + if (path.resolve(src) === path.dirname(path.resolve(thisDest))) { + error('cannot move to self: '+src, true); + return; // skip file + } + + fs.renameSync(src, thisDest); + }); // forEach(src) +}; // mv +exports.mv = wrap('mv', _mv); + +//@ +//@ #### mkdir([options ,] dir [, dir ...]) +//@ #### mkdir([options ,] dir_array) +//@ Available options: +//@ +//@ + `p`: full path (will create intermediate dirs if necessary) +//@ +//@ Examples: +//@ +//@ ```javascript +//@ mkdir('-p', '/tmp/a/b/c/d', '/tmp/e/f/g'); +//@ mkdir('-p', ['/tmp/a/b/c/d', '/tmp/e/f/g']); // same as above +//@ ``` +//@ +//@ Creates directories. +function _mkdir(options, dirs) { + options = parseOptions(options, { + 'p': 'fullpath' + }); + if (!dirs) + error('no paths given'); + + if (typeof dirs === 'string') + dirs = [].slice.call(arguments, 1); + // if it's array leave it as it is + + dirs.forEach(function(dir) { + if (fs.existsSync(dir)) { + if (!options.fullpath) + error('path already exists: ' + dir, true); + return; // skip dir + } + + // Base dir does not exist, and no -p option given + var baseDir = path.dirname(dir); + if (!fs.existsSync(baseDir) && !options.fullpath) { + error('no such file or directory: ' + baseDir, true); + return; // skip dir + } + + if (options.fullpath) + mkdirSyncRecursive(dir); + else + fs.mkdirSync(dir, 0777); + }); +}; // mkdir +exports.mkdir = wrap('mkdir', _mkdir); + +//@ +//@ #### cat(file [, file ...]) +//@ #### cat(file_array) +//@ +//@ Examples: +//@ +//@ ```javascript +//@ var str = cat('file*.txt'); +//@ var str = cat('file1', 'file2'); +//@ var str = cat(['file1', 'file2']); // same as above +//@ ``` +//@ +//@ Returns a string containing the given file, or a concatenated string +//@ containing the files if more than one file is given (a new line character is +//@ introduced between each file). Wildcard `*` accepted. +function _cat(options, files) { + var cat = ''; + + if (!files) + error('no paths given'); + + if (typeof files === 'string') + files = [].slice.call(arguments, 1); + // if it's array leave it as it is + + files = expand(files); + + files.forEach(function(file) { + if (!fs.existsSync(file)) + error('no such file or directory: ' + file); + + cat += fs.readFileSync(file, 'utf8') + '\n'; + }); + + if (cat[cat.length-1] === '\n') + cat = cat.substring(0, cat.length-1); + + return ShellString(cat); +}; +exports.cat = wrap('cat', _cat); + +//@ +//@ #### 'string'.to(file) +//@ +//@ Examples: +//@ +//@ ```javascript +//@ cat('input.txt').to('output.txt'); +//@ ``` +//@ +//@ Analogous to the redirection operator `>` in Unix, but works with JavaScript strings (such as +//@ those returned by `cat`, `grep`, etc). _Like Unix redirections, `to()` will overwrite any existing file!_ +function _to(options, file) { + if (!file) + error('wrong arguments'); + + if (!fs.existsSync( path.dirname(file) )) + error('no such file or directory: ' + path.dirname(file)); + + fs.writeFileSync(file, this.toString(), 'utf8'); +}; +// In the future, when Proxies are default, we can add methods like `.to()` to primitive strings. +// For now, this is a dummy function to bookmark places we need such strings +function ShellString(str) { + return str; +} +String.prototype.to = wrap('to', _to); + +//@ +//@ #### sed([options ,] search_regex, replace_str, file) +//@ Available options: +//@ +//@ + `-i`: Replace contents of 'file' in-place. _Note that no backups will be created!_ +//@ +//@ Examples: +//@ +//@ ```javascript +//@ sed('-i', 'PROGRAM_VERSION', 'v0.1.3', 'source.js'); +//@ sed(/.*DELETE_THIS_LINE.*\n/, '', 'source.js'); +//@ ``` +//@ +//@ Reads an input string from `file` and performs a JavaScript `replace()` on the input +//@ using the given search regex and replacement string. Returns the new string after replacement. +function _sed(options, regex, replacement, file) { + options = parseOptions(options, { + 'i': 'inplace' + }); + + if (typeof replacement === 'string') + replacement = replacement; // no-op + else if (typeof replacement === 'number') + replacement = replacement.toString(); // fallback + else + error('invalid replacement string'); + + if (!file) + error('no file given'); + + if (!fs.existsSync(file)) + error('no such file or directory: ' + file); + + var result = fs.readFileSync(file, 'utf8').replace(regex, replacement); + if (options.inplace) + fs.writeFileSync(file, result, 'utf8'); + + return ShellString(result); +}; +exports.sed = wrap('sed', _sed); + +//@ +//@ #### grep(regex_filter, file [, file ...]) +//@ #### grep(regex_filter, file_array) +//@ +//@ Examples: +//@ +//@ ```javascript +//@ grep('GLOBAL_VARIABLE', '*.js'); +//@ ``` +//@ +//@ Reads input string from given files and returns a string containing all lines of the +//@ file that match the given `regex_filter`. Wildcard `*` accepted. +function _grep(options, regex, files) { + if (!files) + error('no paths given'); + + if (typeof files === 'string') + files = [].slice.call(arguments, 2); + // if it's array leave it as it is + + files = expand(files); + + var grep = ''; + files.forEach(function(file) { + if (!fs.existsSync(file)) { + error('no such file or directory: ' + file, true); + return; + } + + var contents = fs.readFileSync(file, 'utf8'), + lines = contents.split(/\r*\n/); + lines.forEach(function(line) { + if (line.match(regex)) + grep += line + '\n'; + }); + }); + + return ShellString(grep); +}; +exports.grep = wrap('grep', _grep); + + +//@ +//@ #### which(command) +//@ +//@ Examples: +//@ +//@ ```javascript +//@ var nodeExec = which('node'); +//@ ``` +//@ +//@ Searches for `command` in the system's PATH. On Windows looks for `.exe`, `.cmd`, and `.bat` extensions. +//@ Returns string containing the absolute path to the command. +function _which(options, cmd) { + if (!cmd) + error('must specify command'); + + var pathEnv = process.env.path || process.env.Path || process.env.PATH, + pathArray = splitPath(pathEnv), + where = null; + + // No relative/absolute paths provided? + if (cmd.search(/\//) === -1) { + // Search for command in PATH + pathArray.forEach(function(dir) { + if (where) + return; // already found it + + var attempt = path.resolve(dir + '/' + cmd); + if (fs.existsSync(attempt)) { + where = attempt; + return; + } + + if (platform === 'win') { + var baseAttempt = attempt; + attempt = baseAttempt + '.exe'; + if (fs.existsSync(attempt)) { + where = attempt; + return; + } + attempt = baseAttempt + '.cmd'; + if (fs.existsSync(attempt)) { + where = attempt; + return; + } + attempt = baseAttempt + '.bat'; + if (fs.existsSync(attempt)) { + where = attempt; + return; + } + } // if 'win' + }); + } + + // Command not found anywhere? + if (!fs.existsSync(cmd) && !where) + return null; + + where = where || path.resolve(cmd); + + return ShellString(where); +}; +exports.which = wrap('which', _which); + +//@ +//@ #### echo(string [,string ...]) +//@ +//@ Examples: +//@ +//@ ```javascript +//@ echo('hello world'); +//@ var str = echo('hello world'); +//@ ``` +//@ +//@ Prints string to stdout, and returns string with additional utility methods +//@ like `.to()`. +function _echo(options) { + var messages = [].slice.call(arguments, 1); + log.apply(this, messages); + return ShellString(messages.join(' ')); +}; +exports.echo = wrap('echo', _echo); + +//@ +//@ #### exit(code) +//@ Exits the current process with the given exit code. +exports.exit = process.exit; + +//@ +//@ #### env['VAR_NAME'] +//@ Object containing environment variables (both getter and setter). Shortcut to process.env. +exports.env = process.env; + +//@ +//@ #### exec(command [, options] [, callback]) +//@ Available options (all `false` by default): +//@ +//@ + `async`: Asynchronous execution. Needs callback. +//@ + `silent`: Do not echo program output to console. +//@ +//@ Examples: +//@ +//@ ```javascript +//@ var version = exec('node --version', {silent:true}).output; +//@ ``` +//@ +//@ Executes the given `command` _synchronously_, unless otherwise specified. +//@ When in synchronous mode returns the object `{ code:..., output:... }`, containing the program's +//@ `output` (stdout + stderr) and its exit `code`. Otherwise the `callback` gets the +//@ arguments `(code, output)`. +function _exec(command, options, callback) { + if (!command) + error('must specify command'); + + if (typeof options === 'function') { + callback = options; + options = {}; + } + + options = extend({ + silent: false, + async: false + }, options); + + if (options.async) + execAsync(command, options, callback); + else + return execSync(command, options); +}; +exports.exec = wrap('exec', _exec, {notUnix:true}); + + + + +//@ +//@ ## Non-Unix commands +//@ + + + + + + +//@ +//@ #### tempdir() +//@ Searches and returns string containing a writeable, platform-dependent temporary directory. +//@ Follows Python's [tempfile algorithm](http://docs.python.org/library/tempfile.html#tempfile.tempdir). +exports.tempdir = wrap('tempdir', tempDir); + +//@ +//@ #### exists(path [, path ...]) +//@ #### exists(path_array) +//@ Returns true if all the given paths exist. +function _exists(options, paths) { + if (!paths) + error('no paths given'); + + if (typeof paths === 'string') + paths = [].slice.call(arguments, 1); + // if it's array leave it as it is + + var exists = true; + paths.forEach(function(p) { + if (!fs.existsSync(p)) + exists = false; + }); + + return exists; +}; +exports.exists = wrap('exists', _exists); + +//@ +//@ #### error() +//@ Tests if error occurred in the last command. Returns `null` if no error occurred, +//@ otherwise returns string explaining the error +exports.error = function() { + return state.error; +} + +//@ +//@ #### verbose() +//@ Enables all output (default) +exports.verbose = function() { + state.silent = false; +} + +//@ +//@ #### silent() +//@ Suppresses all output, except for explict `echo()` calls +exports.silent = function() { + state.silent = true; +} + + + + + + + + + + + + + + +//////////////////////////////////////////////////////////////////////////////////////////////// +// +// Auxiliary functions (internal use only) +// + +function log() { + if (!state.silent) + console.log.apply(this, arguments); +} + +function write(msg) { + if (!state.silent) + process.stdout.write(msg); +} + +// Shows error message. Throws unless '_continue = true'. +function error(msg, _continue) { + if (state.error === null) + state.error = ''; + state.error += state.currentCmd + ': ' + msg + '\n'; + + log(state.error); + + if (!_continue) + throw ''; +} + +// Returns {'alice': true, 'bob': false} when passed: +// parseOptions('-a', {'a':'alice', 'b':'bob'}); +function parseOptions(str, map) { + if (!map) + error('parseOptions() internal error: no map given'); + + // All options are false by default + var options = {}; + for (letter in map) + options[map[letter]] = false; + + if (!str) + return options; // defaults + + if (typeof str !== 'string') + error('parseOptions() internal error: wrong str'); + + // e.g. match[1] = 'Rf' for str = '-Rf' + var match = str.match(/^\-(.+)/); + if (!match) + return options; + + // e.g. chars = ['R', 'f'] + var chars = match[1].split(''); + + chars.forEach(function(char) { + if (char in map) + options[map[char]] = true; + else + error('option not recognized: '+char); + }); + + return options; +} + +// Common wrapper for all Unix-like commands +function wrap(cmd, fn, options) { + return function() { + var retValue = null; + + state.currentCmd = cmd; + state.error = null; + + try { + var args = [].slice.call(arguments, 0); + + if (options && options.notUnix) { + retValue = fn.apply(this, args); + } else { + if (args.length === 0 || typeof args[0] !== 'string' || args[0][0] !== '-') + args.unshift(''); // only add dummy option if '-option' not already present + retValue = fn.apply(this, args); + } + } catch (e) { + if (!state.error) { + // If state.error hasn't been set it's an error thrown by Node, not us - probably a bug... + console.log('maker.js: internal error'); + console.log(e.stack || e); + process.exit(1); + } + if (state.fatal) + throw e; + } + + state.currentCmd = 'maker.js'; + return retValue; + } +} // wrap + +// Buffered file copy, synchronous +// (Using readFileSync() + writeFileSync() could easily cause a memory overflow +// with large files) +function copyFileSync(srcFile, destFile) { + if (!fs.existsSync(srcFile)) + error('copyFileSync: no such file or directory: ' + srcFile); + + var BUF_LENGTH = 64*1024, + buf = new Buffer(BUF_LENGTH), + fdr = fs.openSync(srcFile, 'r'), + fdw = fs.openSync(destFile, 'w'), + bytesRead = BUF_LENGTH, + pos = 0; + + while (bytesRead === BUF_LENGTH) { + bytesRead = fs.readSync(fdr, buf, 0, BUF_LENGTH, pos); + fs.writeSync(fdw, buf, 0, bytesRead); + pos += bytesRead; + } + + fs.closeSync(fdr); + fs.closeSync(fdw); +} + +// Recursively copies 'sourceDir' into 'destDir' +// Adapted from https://github.com/ryanmcgrath/wrench-js +// +// Copyright (c) 2010 Ryan McGrath +// Copyright (c) 2012 Artur Adib +// +// Licensed under the MIT License +// http://www.opensource.org/licenses/mit-license.php +function cpdirSyncRecursive(sourceDir, destDir, opts) { + if (!opts) opts = {}; + + /* Create the directory where all our junk is moving to; read the mode of the source directory and mirror it */ + var checkDir = fs.statSync(sourceDir); + try { + fs.mkdirSync(destDir, checkDir.mode); + } catch (e) { + //if the directory already exists, that's okay + if (e.code !== 'EEXIST') throw e; + } + + var files = fs.readdirSync(sourceDir); + + for(var i = 0; i < files.length; i++) { + var currFile = fs.lstatSync(sourceDir + "/" + files[i]); + + if (currFile.isDirectory()) { + /* recursion this thing right on back. */ + cpdirSyncRecursive(sourceDir + "/" + files[i], destDir + "/" + files[i], opts); + } else if (currFile.isSymbolicLink()) { + var symlinkFull = fs.readlinkSync(sourceDir + "/" + files[i]); + fs.symlinkSync(symlinkFull, destDir + "/" + files[i]); + } else { + /* At this point, we've hit a file actually worth copying... so copy it on over. */ + if (fs.existsSync(destDir + "/" + files[i]) && !opts.force) { + log('skipping existing file: ' + files[i]); + } else { + copyFileSync(sourceDir + "/" + files[i], destDir + "/" + files[i]); + } + } + + } // for files +}; // cpdirSyncRecursive + +// Recursively removes 'dir' +// Adapted from https://github.com/ryanmcgrath/wrench-js +// +// Copyright (c) 2010 Ryan McGrath +// Copyright (c) 2012 Artur Adib +// +// Licensed under the MIT License +// http://www.opensource.org/licenses/mit-license.php +function rmdirSyncRecursive(dir) { + var files; + + files = fs.readdirSync(dir); + + // Loop through and delete everything in the sub-tree after checking it + for(var i = 0; i < files.length; i++) { + var currFile = fs.lstatSync(dir + "/" + files[i]); + + if(currFile.isDirectory()) // Recursive function back to the beginning + rmdirSyncRecursive(dir + "/" + files[i]); + + else if(currFile.isSymbolicLink()) // Unlink symlinks + fs.unlinkSync(dir + "/" + files[i]); + + else // Assume it's a file - perhaps a try/catch belongs here? + fs.unlinkSync(dir + "/" + files[i]); + } + + // Now that we know everything in the sub-tree has been deleted, we can delete the main directory. + // Huzzah for the shopkeep. + return fs.rmdirSync(dir); +}; // rmdirSyncRecursive + +// Recursively creates 'dir' +function mkdirSyncRecursive(dir) { + var baseDir = path.dirname(dir); + + // Base dir exists, no recursion necessary + if (fs.existsSync(baseDir)) { + fs.mkdirSync(dir, 0777); + return; + } + + // Base dir does not exist, go recursive + mkdirSyncRecursive(baseDir); + + // Base dir created, can create dir + fs.mkdirSync(dir, 0777); +}; + +// e.g. 'makerjs_a5f185d0443ca...' +function randomFileName() { + function randomHash(count) { + if (count === 1) + return parseInt(16*Math.random()).toString(16); + else { + var hash = ''; + for (var i=0; i&1'; // works on both win/unix + + var script = + "var child = require('child_process'), \ + fs = require('fs'); \ + child.exec('"+escape(cmd)+"', {env: process.env}, function(err) { \ + fs.writeFileSync('"+escape(codeFile)+"', err ? err.code.toString() : '0'); \ + });"; + + if (fs.existsSync(scriptFile)) fs.unlinkSync(scriptFile); + if (fs.existsSync(stdoutFile)) fs.unlinkSync(stdoutFile); + if (fs.existsSync(codeFile)) fs.unlinkSync(codeFile); + + fs.writeFileSync(scriptFile, script); + child.exec('node '+scriptFile, { + env: process.env, + cwd: exports.pwd() + }); + + // The wait loop + while (!fs.existsSync(codeFile)) { updateStdout(); }; + while (!fs.existsSync(stdoutFile)) { updateStdout(); }; + + // At this point codeFile exists, but it's not necessarily flushed yet. + // Keep reading it until it is. + var code = parseInt(''); + while (isNaN(code)) + code = parseInt(fs.readFileSync(codeFile, 'utf8')); + + var stdout = fs.readFileSync(stdoutFile, 'utf8'); + + fs.unlinkSync(scriptFile); + fs.unlinkSync(stdoutFile); + fs.unlinkSync(codeFile); + + // True if successful, false if not + var obj = { + code: code, + output: stdout + }; + return obj; +} // execSync() + +// Expands wildcards with matching file names. For a given array of file names 'list', returns +// another array containing all file names as per ls(list[i]). +// For example: expand(['file*.js']) = ['file1.js', 'file2.js', ...] +// (if the files 'file1.js', 'file2.js', etc, exist in the current dir) +function expand(list) { + var expanded = []; + list.forEach(function(listEl) { + // Wildcard present? + if (listEl.search(/\*/) > -1) { + for (file in _ls('', listEl)) + expanded.push(file); + } else { + expanded.push(listEl); + } + }); + return expanded; +} + +// Cross-platform method for splitting environment PATH variables +function splitPath(p) { + if (!p) + return []; + + if (platform === 'win') + return p.split(';'); + else + return p.split(':'); +} + +// extend(target_obj, source_obj1 [, source_obj2 ...]) +// Shallow extend, e.g.: +// aux.extend({a:1}, {b:2}, {c:3}) +// returns {a:1, b:2, c:3} +function extend(target) { + var sources = [].slice.call(arguments, 1); + sources.forEach(function(source) { + for (key in source) + target[key] = source[key]; + }); + + return target; +} diff --git a/make.js b/make.js new file mode 100755 index 000000000..873e14d74 --- /dev/null +++ b/make.js @@ -0,0 +1,401 @@ +#!/usr/bin/env node +require('./external/shelljs/make'); + +var ROOT_DIR = __dirname+'/', // absolute path to project's root + BUILD_DIR = 'build/', + BUILD_TARGET = BUILD_DIR+'pdf.js', + FIREFOX_BUILD_DIR = BUILD_DIR+'/firefox/', + EXTENSION_SRC_DIR = 'extensions/', + GH_PAGES_DIR = BUILD_DIR+'gh-pages/', + REPO = 'git@github.com:mozilla/pdf.js.git', + PYTHON_BIN = 'python2.7'; + +// +// make all +// +target.all = function() { + // Don't do anything by default + echo('Please specify a target. Available targets:'); + for (t in target) + if (t !== 'all') echo(' ' + t); +} + + + +/////////////////////////////////////////////////////////////////////////////////////////// +// +// Production stuff +// + +// +// make web +// Generates the website for the project, by checking out the gh-pages branch underneath +// the build directory, and then moving the various viewer files into place. +// +target.web = function() { + target.production(); + target.extension(); + target.pagesrepo(); + + cd(ROOT_DIR); + echo(); + echo('### Creating web site'); + + cp(BUILD_TARGET, GH_PAGES_DIR+BUILD_TARGET); + cp('-R', 'web/*', GH_PAGES_DIR+'/web'); + cp(FIREFOX_BUILD_DIR+'/*.xpi', FIREFOX_BUILD_DIR+'/*.rdf', GH_PAGES_DIR+EXTENSION_SRC_DIR+'firefox/'); + cp(GH_PAGES_DIR+'/web/index.html.template', GH_PAGES_DIR+'/index.html'); + mv('-f', GH_PAGES_DIR+'/web/viewer-production.html', GH_PAGES_DIR+'/web/viewer.html'); + cd(GH_PAGES_DIR); + exec('git add -A'); + + echo(); + echo("Website built in "+GH_PAGES_DIR); + echo("Don't forget to cd into "+GH_PAGES_DIR+" and issue 'git commit' to push changes."); +} + +// +// make production +// Creates production output (pdf.js, and corresponding changes to web/ files) +// +target.production = function() { + target.bundle(); + target.viewer(); +} + +// +// make bundle +// Bundles all source files into one wrapper 'pdf.js' file, in the given order. +// + +target.bundle = function() { + cd(ROOT_DIR); + echo(); + echo('### Bundling files into '+BUILD_TARGET); + + // File order matters + var SRC_FILES = + ['core.js', + 'util.js', + 'canvas.js', + 'obj.js', + 'function.js', + 'charsets.js', + 'cidmaps.js', + 'colorspace.js', + 'crypto.js', + 'evaluator.js', + 'fonts.js', + 'glyphlist.js', + 'image.js', + 'metrics.js', + 'parser.js', + 'pattern.js', + 'stream.js', + 'worker.js', + '../external/jpgjs/jpg.js', + 'jpx.js', + 'bidi.js']; + + if (!exists(BUILD_DIR)) + mkdir(BUILD_DIR); + + cd('src'); + var bundle = cat(SRC_FILES), + bundleVersion = exec('git log --format="%h" -n 1', {silent:true}).output.replace('\n', ''); + + sed(/.*PDFJSSCRIPT_INCLUDE_ALL.*\n/, bundle, 'pdf.js').to(ROOT_DIR+BUILD_TARGET); + sed('-i', 'PDFJSSCRIPT_BUNDLE_VER', bundleVersion, ROOT_DIR+BUILD_TARGET); +} + +// +// make viewer +// Changes development From 30888e94bd10423de3112d8569aeec6ac3ff3cf3 Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Sun, 4 Mar 2012 20:26:57 -0600 Subject: [PATCH 39/40] Exclude make.js from the linting; fixes few lint make.js errors --- Makefile | 4 +- make.js | 176 +++++++++++++++++++++++++++++-------------------------- 2 files changed, 94 insertions(+), 86 deletions(-) diff --git a/Makefile b/Makefile index b11b015b6..d32c1a252 100644 --- a/Makefile +++ b/Makefile @@ -144,9 +144,9 @@ browser-test: # To install gjslint, see: # # -SRC_DIRS := . src utils web test examples/helloworld extensions/firefox \ +SRC_DIRS := src utils web test examples/helloworld extensions/firefox \ extensions/firefox/components extensions/chrome test/unit -GJSLINT_FILES = $(foreach DIR,$(SRC_DIRS),$(wildcard $(DIR)/*.js)) +GJSLINT_FILES = $(foreach DIR, $(SRC_DIRS), $(wildcard $(DIR)/*.js)) lint: gjslint --nojsdoc $(GJSLINT_FILES) diff --git a/make.js b/make.js index 873e14d74..7c514a34a 100755 --- a/make.js +++ b/make.js @@ -1,25 +1,24 @@ #!/usr/bin/env node require('./external/shelljs/make'); -var ROOT_DIR = __dirname+'/', // absolute path to project's root +var ROOT_DIR = __dirname + '/', // absolute path to project's root BUILD_DIR = 'build/', - BUILD_TARGET = BUILD_DIR+'pdf.js', - FIREFOX_BUILD_DIR = BUILD_DIR+'/firefox/', + BUILD_TARGET = BUILD_DIR + 'pdf.js', + FIREFOX_BUILD_DIR = BUILD_DIR + '/firefox/', EXTENSION_SRC_DIR = 'extensions/', - GH_PAGES_DIR = BUILD_DIR+'gh-pages/', + GH_PAGES_DIR = BUILD_DIR + 'gh-pages/', REPO = 'git@github.com:mozilla/pdf.js.git', PYTHON_BIN = 'python2.7'; // // make all // -target.all = function() { +target.all = function() { // Don't do anything by default echo('Please specify a target. Available targets:'); for (t in target) if (t !== 'all') echo(' ' + t); -} - +}; /////////////////////////////////////////////////////////////////////////////////////////// @@ -29,30 +28,33 @@ target.all = function() { // // make web -// Generates the website for the project, by checking out the gh-pages branch underneath +// Generates the website for the project, by checking out the gh-pages branch underneath // the build directory, and then moving the various viewer files into place. // target.web = function() { target.production(); target.extension(); target.pagesrepo(); - + cd(ROOT_DIR); echo(); echo('### Creating web site'); - cp(BUILD_TARGET, GH_PAGES_DIR+BUILD_TARGET); - cp('-R', 'web/*', GH_PAGES_DIR+'/web'); - cp(FIREFOX_BUILD_DIR+'/*.xpi', FIREFOX_BUILD_DIR+'/*.rdf', GH_PAGES_DIR+EXTENSION_SRC_DIR+'firefox/'); - cp(GH_PAGES_DIR+'/web/index.html.template', GH_PAGES_DIR+'/index.html'); - mv('-f', GH_PAGES_DIR+'/web/viewer-production.html', GH_PAGES_DIR+'/web/viewer.html'); + cp(BUILD_TARGET, GH_PAGES_DIR + BUILD_TARGET); + cp('-R', 'web/*', GH_PAGES_DIR + '/web'); + cp(FIREFOX_BUILD_DIR + '/*.xpi', FIREFOX_BUILD_DIR + '/*.rdf', + GH_PAGES_DIR + EXTENSION_SRC_DIR + 'firefox/'); + cp(GH_PAGES_DIR + '/web/index.html.template', GH_PAGES_DIR + '/index.html'); + mv('-f', GH_PAGES_DIR + '/web/viewer-production.html', + GH_PAGES_DIR + '/web/viewer.html'); cd(GH_PAGES_DIR); exec('git add -A'); - + echo(); - echo("Website built in "+GH_PAGES_DIR); - echo("Don't forget to cd into "+GH_PAGES_DIR+" and issue 'git commit' to push changes."); -} + echo("Website built in " + GH_PAGES_DIR); + echo("Don't forget to cd into " + GH_PAGES_DIR + + " and issue 'git commit' to push changes."); +}; // // make production @@ -61,7 +63,7 @@ target.web = function() { target.production = function() { target.bundle(); target.viewer(); -} +}; // // make bundle @@ -71,10 +73,10 @@ target.production = function() { target.bundle = function() { cd(ROOT_DIR); echo(); - echo('### Bundling files into '+BUILD_TARGET); + echo('### Bundling files into ' + BUILD_TARGET); // File order matters - var SRC_FILES = + var SRC_FILES = ['core.js', 'util.js', 'canvas.js', @@ -102,11 +104,13 @@ target.bundle = function() { cd('src'); var bundle = cat(SRC_FILES), - bundleVersion = exec('git log --format="%h" -n 1', {silent:true}).output.replace('\n', ''); + bundleVersion = exec('git log --format="%h" -n 1', + {silent: true}).output.replace('\n', ''); - sed(/.*PDFJSSCRIPT_INCLUDE_ALL.*\n/, bundle, 'pdf.js').to(ROOT_DIR+BUILD_TARGET); - sed('-i', 'PDFJSSCRIPT_BUNDLE_VER', bundleVersion, ROOT_DIR+BUILD_TARGET); -} + sed(/.*PDFJSSCRIPT_INCLUDE_ALL.*\n/, bundle, 'pdf.js') + .to(ROOT_DIR + BUILD_TARGET); + sed('-i', 'PDFJSSCRIPT_BUNDLE_VER', bundleVersion, ROOT_DIR + BUILD_TARGET); +}; // // make viewer @@ -120,15 +124,17 @@ target.viewer = function() { cd('web'); // Remove development lines - sed(/.*PDFJSSCRIPT_REMOVE_CORE.*\n/g, '', 'viewer.html').to('viewer-production.html'); + sed(/.*PDFJSSCRIPT_REMOVE_CORE.*\n/g, '', 'viewer.html') + .to('viewer-production.html'); // Introduce snippet - sed('-i', /.*PDFJSSCRIPT_INCLUDE_BUILD.*\n/g, cat('viewer-snippet.html'), 'viewer-production.html'); -} + sed('-i', /.*PDFJSSCRIPT_INCLUDE_BUILD.*\n/g, cat('viewer-snippet.html'), + 'viewer-production.html'); +}; // // make pagesrepo // -// This target clones the gh-pages repo into the build directory. It deletes the current contents +// This target clones the gh-pages repo into the build directory. It deletes the current contents // of the repo, since we overwrite everything with data from the master repo. The 'make web' target // then uses 'git add -A' to track additions, modifications, moves, and deletions. target.pagesrepo = function() { @@ -143,16 +149,17 @@ target.pagesrepo = function() { echo(); echo('Cloning project repo...'); echo('(This operation can take a while, depending on network conditions)'); - exec('git clone -b gh-pages --depth=1 '+REPO+' '+GH_PAGES_DIR, {silent:true}); + exec('git clone -b gh-pages --depth=1 ' + REPO + ' ' + ßGH_PAGES_DIR, + {silent: true}); echo('Done.'); } - rm('-rf', GH_PAGES_DIR+'/*'); - mkdir('-p', GH_PAGES_DIR+'/web'); - mkdir('-p', GH_PAGES_DIR+'/web/images'); - mkdir('-p', GH_PAGES_DIR+BUILD_DIR); - mkdir('-p', GH_PAGES_DIR+EXTENSION_SRC_DIR+'/firefox'); -} + rm('-rf', GH_PAGES_DIR + '/*'); + mkdir('-p', GH_PAGES_DIR + '/web'); + mkdir('-p', GH_PAGES_DIR + '/web/images'); + mkdir('-p', GH_PAGES_DIR + BUILD_DIR); + mkdir('-p', GH_PAGES_DIR + EXTENSION_SRC_DIR + '/firefox'); +}; /////////////////////////////////////////////////////////////////////////////////////////// @@ -180,7 +187,7 @@ target.extension = function() { target.production(); target.firefox(); target.chrome(); -} +}; target.buildnumber = function() { cd(ROOT_DIR); @@ -188,11 +195,12 @@ target.buildnumber = function() { echo('### Getting extension build number'); // Build number is the number of commits since base version - EXTENSION_BUILD_NUMBER = exec('git log --format=oneline '+EXTENSION_BASE_VERSION+'..', {silent:true}) + EXTENSION_BUILD_NUMBER = exec('git log --format=oneline ' + + EXTENSION_BASE_VERSION + '..', {silent: true}) .output.match(/\n/g).length; // get # of lines in git output - - echo('Extension build number: ' + EXTENSION_BUILD_NUMBER); -} + + echo('Extension build number: ' + EXTENSION_BUILD_NUMBER); +}; // // make firefox @@ -202,8 +210,8 @@ target.firefox = function() { echo(); echo('### Building Firefox extension'); - var FIREFOX_BUILD_CONTENT_DIR = FIREFOX_BUILD_DIR+'/content/', - FIREFOX_CONTENT_DIR = EXTENSION_SRC_DIR+'/firefox/content/', + var FIREFOX_BUILD_CONTENT_DIR = FIREFOX_BUILD_DIR + '/content/', + FIREFOX_CONTENT_DIR = EXTENSION_SRC_DIR + '/firefox/content/', FIREFOX_EXTENSION_FILES_TO_COPY = ['*.js', '*.rdf', @@ -224,50 +232,50 @@ target.firefox = function() { // Clear out everything in the firefox extension build directory rm('-rf', FIREFOX_BUILD_DIR); mkdir('-p', FIREFOX_BUILD_CONTENT_DIR); - mkdir('-p', FIREFOX_BUILD_CONTENT_DIR+BUILD_DIR); - mkdir('-p', FIREFOX_BUILD_CONTENT_DIR+'/web'); - - // Copy extension files + mkdir('-p', FIREFOX_BUILD_CONTENT_DIR + BUILD_DIR); + mkdir('-p', FIREFOX_BUILD_CONTENT_DIR + '/web'); + + // Copy extension files cd('extensions/firefox'); - cp('-R', FIREFOX_EXTENSION_FILES_TO_COPY, ROOT_DIR+FIREFOX_BUILD_DIR); + cp('-R', FIREFOX_EXTENSION_FILES_TO_COPY, ROOT_DIR + FIREFOX_BUILD_DIR); cd(ROOT_DIR); // Copy a standalone version of pdf.js inside the content directory - cp(BUILD_TARGET, FIREFOX_BUILD_CONTENT_DIR+BUILD_DIR); - cp('-R', EXTENSION_WEB_FILES, FIREFOX_BUILD_CONTENT_DIR+'/web'); - rm(FIREFOX_BUILD_CONTENT_DIR+'/web/viewer-production.html'); + cp(BUILD_TARGET, FIREFOX_BUILD_CONTENT_DIR + BUILD_DIR); + cp('-R', EXTENSION_WEB_FILES, FIREFOX_BUILD_CONTENT_DIR + '/web'); + rm(FIREFOX_BUILD_CONTENT_DIR + '/web/viewer-production.html'); // Copy over the firefox extension snippet so we can inline pdf.js in it - cp('web/viewer-snippet-firefox-extension.html', FIREFOX_BUILD_CONTENT_DIR+'/web'); + cp('web/viewer-snippet-firefox-extension.html', FIREFOX_BUILD_CONTENT_DIR + '/web'); // Modify the viewer so it does all the extension-only stuff. - cd(FIREFOX_BUILD_CONTENT_DIR+'/web'); - sed('-i', /.*PDFJSSCRIPT_INCLUDE_BUNDLE.*\n/, cat(ROOT_DIR+BUILD_TARGET), 'viewer-snippet-firefox-extension.html'); + cd(FIREFOX_BUILD_CONTENT_DIR + '/web'); + sed('-i', /.*PDFJSSCRIPT_INCLUDE_BUNDLE.*\n/, cat(ROOT_DIR + BUILD_TARGET), 'viewer-snippet-firefox-extension.html'); sed('-i', /.*PDFJSSCRIPT_REMOVE_CORE.*\n/g, '', 'viewer.html'); sed('-i', /.*PDFJSSCRIPT_REMOVE_FIREFOX_EXTENSION.*\n/g, '', 'viewer.html'); sed('-i', /.*PDFJSSCRIPT_INCLUDE_FIREFOX_EXTENSION.*\n/, cat('viewer-snippet-firefox-extension.html'), 'viewer.html'); cd(ROOT_DIR); // We don't need pdf.js anymore since its inlined - rm('-Rf', FIREFOX_BUILD_CONTENT_DIR+BUILD_DIR); + rm('-Rf', FIREFOX_BUILD_CONTENT_DIR + BUILD_DIR); // Update the build version number - sed('-i', /PDFJSSCRIPT_BUILD/, EXTENSION_BUILD_NUMBER, FIREFOX_BUILD_DIR+'/install.rdf'); - sed('-i', /PDFJSSCRIPT_BUILD/, EXTENSION_BUILD_NUMBER, FIREFOX_BUILD_DIR+'/update.rdf'); + sed('-i', /PDFJSSCRIPT_BUILD/, EXTENSION_BUILD_NUMBER, FIREFOX_BUILD_DIR + '/install.rdf'); + sed('-i', /PDFJSSCRIPT_BUILD/, EXTENSION_BUILD_NUMBER, FIREFOX_BUILD_DIR + '/update.rdf'); // Create the xpi cd(FIREFOX_BUILD_DIR); - exec('zip -r '+FIREFOX_EXTENSION_NAME+' '+FIREFOX_EXTENSION_FILES.join(' ')); + exec('zip -r ' + FIREFOX_EXTENSION_NAME + ' ' + FIREFOX_EXTENSION_FILES.join(' ')); echo('extension created: ' + FIREFOX_EXTENSION_NAME); cd(ROOT_DIR); // Build the amo extension too (remove the updateUrl) cd(FIREFOX_BUILD_DIR); sed('-i', /.*updateURL.*\n/, '', 'install.rdf'); - exec('zip -r '+FIREFOX_AMO_EXTENSION_NAME+' '+FIREFOX_EXTENSION_FILES.join(' ')); + exec('zip -r ' + FIREFOX_AMO_EXTENSION_NAME + ' ' + FIREFOX_EXTENSION_FILES.join(' ')); echo('AMO extension created: ' + FIREFOX_AMO_EXTENSION_NAME); cd(ROOT_DIR); -} +}; // // make chrome @@ -277,9 +285,9 @@ target.chrome = function() { echo(); echo('### Building Chrome extension'); - var CHROME_BUILD_DIR = BUILD_DIR+'/chrome/', - CHROME_CONTENT_DIR = EXTENSION_SRC_DIR+'/chrome/content/', - CHROME_BUILD_CONTENT_DIR = CHROME_BUILD_DIR+'/content/', + var CHROME_BUILD_DIR = BUILD_DIR + '/chrome/', + CHROME_CONTENT_DIR = EXTENSION_SRC_DIR + '/chrome/content/', + CHROME_BUILD_CONTENT_DIR = CHROME_BUILD_DIR + '/content/', CHROME_EXTENSION_FILES = ['extensions/chrome/*.json', 'extensions/chrome/*.html']; @@ -291,17 +299,18 @@ target.chrome = function() { // Clear out everything in the chrome extension build directory rm('-Rf', CHROME_BUILD_DIR); mkdir('-p', CHROME_BUILD_CONTENT_DIR); - mkdir('-p', CHROME_BUILD_CONTENT_DIR+BUILD_DIR); - mkdir('-p', CHROME_BUILD_CONTENT_DIR+'/web'); + mkdir('-p', CHROME_BUILD_CONTENT_DIR + BUILD_DIR); + mkdir('-p', CHROME_BUILD_CONTENT_DIR + '/web'); - // Copy extension files + // Copy extension files cp('-R', CHROME_EXTENSION_FILES, CHROME_BUILD_DIR); // Copy a standalone version of pdf.js inside the content directory - cp(BUILD_TARGET, CHROME_BUILD_CONTENT_DIR+BUILD_DIR); - cp('-R', EXTENSION_WEB_FILES, CHROME_BUILD_CONTENT_DIR+'/web'); - mv('-f', CHROME_BUILD_CONTENT_DIR+'/web/viewer-production.html', CHROME_BUILD_CONTENT_DIR+'/web/viewer.html'); -} + cp(BUILD_TARGET, CHROME_BUILD_CONTENT_DIR + BUILD_DIR); + cp('-R', EXTENSION_WEB_FILES, CHROME_BUILD_CONTENT_DIR + '/web'); + mv('-f', CHROME_BUILD_CONTENT_DIR + '/web/viewer-production.html', + CHROME_BUILD_CONTENT_DIR + '/web/viewer.html'); +}; /////////////////////////////////////////////////////////////////////////////////////////// @@ -315,7 +324,7 @@ target.chrome = function() { target.test = function() { target.browsertest(); target.unittest(); -} +}; // // make browsertest @@ -328,15 +337,16 @@ target.browsertest = function() { var PDF_TEST = env['PDF_TEST'] || 'test_manifest.json', PDF_BROWSERS = env['PDF_BROWSERS'] || 'resources/browser_manifests/browser_manifest.json'; - if (!exists('test/'+PDF_BROWSERS)) { - echo('Browser manifest file test/'+PDF_BROWSERS+' does not exist.'); + if (!exists('test/' + PDF_BROWSERS)) { + echo('Browser manifest file test/' + PDF_BROWSERS + ' does not exist.'); echo('Try copying one of the examples in test/resources/browser_manifests/'); exit(1); } cd('test'); - exec(PYTHON_BIN+' test.py --reftest --browserManifestFile='+PDF_BROWSERS+' --manifestFile='+PDF_TEST, {async:true}); -} + exec(PYTHON_BIN + ' test.py --reftest --browserManifestFile=' + PDF_BROWSERS + + ' --manifestFile=' + PDF_TEST, {async: true}); +}; // // make unittest @@ -347,10 +357,8 @@ target.unittest = function() { echo('### Running unit tests'); cd('test/unit'); - exec('make', {async:true}); -} - - + exec('make', {async: true}); +}; /////////////////////////////////////////////////////////////////////////////////////////// @@ -367,8 +375,8 @@ target.server = function() { echo('### Starting local server'); cd('test'); - exec(PYTHON_BIN+' -u test.py --port=8888', {async:true}); -} + exec(PYTHON_BIN + ' -u test.py --port=8888', {async: true}); +}; // // make lint @@ -386,8 +394,8 @@ target.lint = function() { extensions/firefox/components/*.js \ extensions/chrome/*.js'; - exec('gjslint --nojsdoc '+LINT_FILES); -} + exec('gjslint --nojsdoc ' + LINT_FILES); +}; // // make clean @@ -398,4 +406,4 @@ target.clean = function() { echo('### Cleaning up project builds'); rm('-rf', BUILD_DIR); -} +}; From 02711c761062c544aebadd235cd1d3b937373d0b Mon Sep 17 00:00:00 2001 From: notmasteryet Date: Wed, 7 Mar 2012 19:12:05 -0600 Subject: [PATCH 40/40] Fixes unnecessary style attributes in svg --- web/images/zoom-in.svg | 4 ++-- web/images/zoom-out.svg | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/web/images/zoom-in.svg b/web/images/zoom-in.svg index db28218a8..48ee42dd9 100644 --- a/web/images/zoom-in.svg +++ b/web/images/zoom-in.svg @@ -419,12 +419,12 @@ d="M 33.278212 34.94062 A 10.31934 2.320194 0 1 1 12.639532,34.94062 A 10.31934 2.320194 0 1 1 33.278212 34.94062 z" transform="matrix(1.550487,0,0,1.978714,-12.4813,-32.49103)" /> diff --git a/web/images/zoom-out.svg b/web/images/zoom-out.svg index c58d9c4c6..eb13b60e3 100644 --- a/web/images/zoom-out.svg +++ b/web/images/zoom-out.svg @@ -407,12 +407,12 @@ inkscape:label="Layer 1" inkscape:groupmode="layer">