Merge pull request #7146 from Snuffleupagus/extract-CFFParser

Extract CFFParser and Type1Parser from fonts.js
This commit is contained in:
Yury Delendik 2016-04-02 10:50:38 -05:00
commit 34aa915441
6 changed files with 2462 additions and 2366 deletions

1646
src/core/cff_parser.js Normal file

File diff suppressed because it is too large Load Diff

View File

@ -17,17 +17,19 @@
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define('pdfjs/core/font_renderer', ['exports', 'pdfjs/shared/util',
'pdfjs/core/stream', 'pdfjs/core/glyphlist', 'pdfjs/core/encodings'],
factory);
'pdfjs/core/stream', 'pdfjs/core/glyphlist', 'pdfjs/core/encodings',
'pdfjs/core/cff_parser'], factory);
} else if (typeof exports !== 'undefined') {
factory(exports, require('../shared/util.js'), require('./stream.js'),
require('./glyphlist.js'), require('./encodings.js'));
require('./glyphlist.js'), require('./encodings.js'),
require('./cff_parser.js'));
} else {
factory((root.pdfjsCoreFontRenderer = {}), root.pdfjsSharedUtil,
root.pdfjsCoreStream, root.pdfjsCoreGlyphList, root.pdfjsCoreEncodings);
root.pdfjsCoreStream, root.pdfjsCoreGlyphList, root.pdfjsCoreEncodings,
root.pdfjsCoreCFFParser);
}
}(this, function (exports, sharedUtil, coreStream, coreGlyphList,
coreEncodings) {
coreEncodings, coreCFFParser) {
var Util = sharedUtil.Util;
var bytesToString = sharedUtil.bytesToString;
@ -35,9 +37,7 @@ var error = sharedUtil.error;
var Stream = coreStream.Stream;
var getGlyphsUnicode = coreGlyphList.getGlyphsUnicode;
var StandardEncoding = coreEncodings.StandardEncoding;
var coreFonts; // see _setCoreFonts below
var CFFParser; // = coreFonts.CFFParser;
var CFFParser = coreCFFParser.CFFParser;
var FontRendererFactory = (function FontRendererFactoryClosure() {
function getLong(data, offset) {
@ -99,10 +99,10 @@ var FontRendererFactory = (function FontRendererFactoryClosure() {
error('not supported cmap: ' + format);
}
function parseCff(data, start, end) {
function parseCff(data, start, end, seacAnalysisEnabled) {
var properties = {};
var parser = new CFFParser(new Stream(data, start, end - start),
properties);
properties, seacAnalysisEnabled);
var cff = parser.parse();
return {
glyphs: cff.charStrings.objects,
@ -696,7 +696,7 @@ var FontRendererFactory = (function FontRendererFactoryClosure() {
return {
create: function FontRendererFactory_create(font) {
create: function FontRendererFactory_create(font, seacAnalysisEnabled) {
var data = new Uint8Array(font.data);
var cmap, glyf, loca, cff, indexToLocFormat, unitsPerEm;
var numTables = getUshort(data, 4);
@ -719,7 +719,7 @@ var FontRendererFactory = (function FontRendererFactoryClosure() {
indexToLocFormat = getUshort(data, offset + 50);
break;
case 'CFF ':
cff = parseCff(data, offset, offset + length);
cff = parseCff(data, offset, offset + length, seacAnalysisEnabled);
break;
}
}
@ -736,13 +736,5 @@ var FontRendererFactory = (function FontRendererFactoryClosure() {
};
})();
// TODO refactor to remove cyclic dependency on fonts.js
function _setCoreFonts(coreFonts_) {
coreFonts = coreFonts_;
CFFParser = coreFonts_.CFFParser;
}
exports._setCoreFonts = _setCoreFonts;
exports.FontRendererFactory = FontRendererFactory;
}));

File diff suppressed because it is too large Load Diff

722
src/core/type1_parser.js Normal file
View File

@ -0,0 +1,722 @@
/* Copyright 2012 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
'use strict';
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define('pdfjs/core/type1_parser', ['exports', 'pdfjs/shared/util',
'pdfjs/core/stream', 'pdfjs/core/parser', 'pdfjs/core/encodings'],
factory);
} else if (typeof exports !== 'undefined') {
factory(exports, require('../shared/util.js'), require('./stream.js'),
require('./parser.js'), require('./encodings.js'));
} else {
factory((root.pdfjsCoreType1Parser = {}), root.pdfjsSharedUtil,
root.pdfjsCoreStream, root.pdfjsCoreParser, root.pdfjsCoreEncodings);
}
}(this, function (exports, sharedUtil, coreStream, coreParser, coreEncodings) {
var warn = sharedUtil.warn;
var Stream = coreStream.Stream;
var Lexer = coreParser.Lexer;
var getEncoding = coreEncodings.getEncoding;
// Hinting is currently disabled due to unknown problems on windows
// in tracemonkey and various other pdfs with type1 fonts.
var HINTING_ENABLED = false;
/*
* CharStrings are encoded following the the CharString Encoding sequence
* describe in Chapter 6 of the "Adobe Type1 Font Format" specification.
* The value in a byte indicates a command, a number, or subsequent bytes
* that are to be interpreted in a special way.
*
* CharString Number Encoding:
* A CharString byte containing the values from 32 through 255 inclusive
* indicate an integer. These values are decoded in four ranges.
*
* 1. A CharString byte containing a value, v, between 32 and 246 inclusive,
* indicate the integer v - 139. Thus, the integer values from -107 through
* 107 inclusive may be encoded in single byte.
*
* 2. A CharString byte containing a value, v, between 247 and 250 inclusive,
* indicates an integer involving the next byte, w, according to the formula:
* [(v - 247) x 256] + w + 108
*
* 3. A CharString byte containing a value, v, between 251 and 254 inclusive,
* indicates an integer involving the next byte, w, according to the formula:
* -[(v - 251) * 256] - w - 108
*
* 4. A CharString containing the value 255 indicates that the next 4 bytes
* are a two complement signed integer. The first of these bytes contains the
* highest order bits, the second byte contains the next higher order bits
* and the fourth byte contain the lowest order bits.
*
*
* CharString Command Encoding:
* CharStrings commands are encoded in 1 or 2 bytes.
*
* Single byte commands are encoded in 1 byte that contains a value between
* 0 and 31 inclusive.
* If a command byte contains the value 12, then the value in the next byte
* indicates a command. This "escape" mechanism allows many extra commands
* to be encoded and this encoding technique helps to minimize the length of
* the charStrings.
*/
var Type1CharString = (function Type1CharStringClosure() {
var COMMAND_MAP = {
'hstem': [1],
'vstem': [3],
'vmoveto': [4],
'rlineto': [5],
'hlineto': [6],
'vlineto': [7],
'rrcurveto': [8],
'callsubr': [10],
'flex': [12, 35],
'drop' : [12, 18],
'endchar': [14],
'rmoveto': [21],
'hmoveto': [22],
'vhcurveto': [30],
'hvcurveto': [31]
};
function Type1CharString() {
this.width = 0;
this.lsb = 0;
this.flexing = false;
this.output = [];
this.stack = [];
}
Type1CharString.prototype = {
convert: function Type1CharString_convert(encoded, subrs,
seacAnalysisEnabled) {
var count = encoded.length;
var error = false;
var wx, sbx, subrNumber;
for (var i = 0; i < count; i++) {
var value = encoded[i];
if (value < 32) {
if (value === 12) {
value = (value << 8) + encoded[++i];
}
switch (value) {
case 1: // hstem
if (!HINTING_ENABLED) {
this.stack = [];
break;
}
error = this.executeCommand(2, COMMAND_MAP.hstem);
break;
case 3: // vstem
if (!HINTING_ENABLED) {
this.stack = [];
break;
}
error = this.executeCommand(2, COMMAND_MAP.vstem);
break;
case 4: // vmoveto
if (this.flexing) {
if (this.stack.length < 1) {
error = true;
break;
}
// Add the dx for flex and but also swap the values so they are
// the right order.
var dy = this.stack.pop();
this.stack.push(0, dy);
break;
}
error = this.executeCommand(1, COMMAND_MAP.vmoveto);
break;
case 5: // rlineto
error = this.executeCommand(2, COMMAND_MAP.rlineto);
break;
case 6: // hlineto
error = this.executeCommand(1, COMMAND_MAP.hlineto);
break;
case 7: // vlineto
error = this.executeCommand(1, COMMAND_MAP.vlineto);
break;
case 8: // rrcurveto
error = this.executeCommand(6, COMMAND_MAP.rrcurveto);
break;
case 9: // closepath
// closepath is a Type1 command that does not take argument and is
// useless in Type2 and it can simply be ignored.
this.stack = [];
break;
case 10: // callsubr
if (this.stack.length < 1) {
error = true;
break;
}
subrNumber = this.stack.pop();
error = this.convert(subrs[subrNumber], subrs,
seacAnalysisEnabled);
break;
case 11: // return
return error;
case 13: // hsbw
if (this.stack.length < 2) {
error = true;
break;
}
// To convert to type2 we have to move the width value to the
// first part of the charstring and then use hmoveto with lsb.
wx = this.stack.pop();
sbx = this.stack.pop();
this.lsb = sbx;
this.width = wx;
this.stack.push(wx, sbx);
error = this.executeCommand(2, COMMAND_MAP.hmoveto);
break;
case 14: // endchar
this.output.push(COMMAND_MAP.endchar[0]);
break;
case 21: // rmoveto
if (this.flexing) {
break;
}
error = this.executeCommand(2, COMMAND_MAP.rmoveto);
break;
case 22: // hmoveto
if (this.flexing) {
// Add the dy for flex.
this.stack.push(0);
break;
}
error = this.executeCommand(1, COMMAND_MAP.hmoveto);
break;
case 30: // vhcurveto
error = this.executeCommand(4, COMMAND_MAP.vhcurveto);
break;
case 31: // hvcurveto
error = this.executeCommand(4, COMMAND_MAP.hvcurveto);
break;
case (12 << 8) + 0: // dotsection
// dotsection is a Type1 command to specify some hinting feature
// for dots that do not take a parameter and it can safely be
// ignored for Type2.
this.stack = [];
break;
case (12 << 8) + 1: // vstem3
if (!HINTING_ENABLED) {
this.stack = [];
break;
}
// [vh]stem3 are Type1 only and Type2 supports [vh]stem with
// multiple parameters, so instead of returning [vh]stem3 take a
// shortcut and return [vhstem] instead.
error = this.executeCommand(2, COMMAND_MAP.vstem);
break;
case (12 << 8) + 2: // hstem3
if (!HINTING_ENABLED) {
this.stack = [];
break;
}
// See vstem3.
error = this.executeCommand(2, COMMAND_MAP.hstem);
break;
case (12 << 8) + 6: // seac
// seac is like type 2's special endchar but it doesn't use the
// first argument asb, so remove it.
if (seacAnalysisEnabled) {
this.seac = this.stack.splice(-4, 4);
error = this.executeCommand(0, COMMAND_MAP.endchar);
} else {
error = this.executeCommand(4, COMMAND_MAP.endchar);
}
break;
case (12 << 8) + 7: // sbw
if (this.stack.length < 4) {
error = true;
break;
}
// To convert to type2 we have to move the width value to the
// first part of the charstring and then use rmoveto with
// (dx, dy). The height argument will not be used for vmtx and
// vhea tables reconstruction -- ignoring it.
var wy = this.stack.pop();
wx = this.stack.pop();
var sby = this.stack.pop();
sbx = this.stack.pop();
this.lsb = sbx;
this.width = wx;
this.stack.push(wx, sbx, sby);
error = this.executeCommand(3, COMMAND_MAP.rmoveto);
break;
case (12 << 8) + 12: // div
if (this.stack.length < 2) {
error = true;
break;
}
var num2 = this.stack.pop();
var num1 = this.stack.pop();
this.stack.push(num1 / num2);
break;
case (12 << 8) + 16: // callothersubr
if (this.stack.length < 2) {
error = true;
break;
}
subrNumber = this.stack.pop();
var numArgs = this.stack.pop();
if (subrNumber === 0 && numArgs === 3) {
var flexArgs = this.stack.splice(this.stack.length - 17, 17);
this.stack.push(
flexArgs[2] + flexArgs[0], // bcp1x + rpx
flexArgs[3] + flexArgs[1], // bcp1y + rpy
flexArgs[4], // bcp2x
flexArgs[5], // bcp2y
flexArgs[6], // p2x
flexArgs[7], // p2y
flexArgs[8], // bcp3x
flexArgs[9], // bcp3y
flexArgs[10], // bcp4x
flexArgs[11], // bcp4y
flexArgs[12], // p3x
flexArgs[13], // p3y
flexArgs[14] // flexDepth
// 15 = finalx unused by flex
// 16 = finaly unused by flex
);
error = this.executeCommand(13, COMMAND_MAP.flex, true);
this.flexing = false;
this.stack.push(flexArgs[15], flexArgs[16]);
} else if (subrNumber === 1 && numArgs === 0) {
this.flexing = true;
}
break;
case (12 << 8) + 17: // pop
// Ignore this since it is only used with othersubr.
break;
case (12 << 8) + 33: // setcurrentpoint
// Ignore for now.
this.stack = [];
break;
default:
warn('Unknown type 1 charstring command of "' + value + '"');
break;
}
if (error) {
break;
}
continue;
} else if (value <= 246) {
value = value - 139;
} else if (value <= 250) {
value = ((value - 247) * 256) + encoded[++i] + 108;
} else if (value <= 254) {
value = -((value - 251) * 256) - encoded[++i] - 108;
} else {
value = (encoded[++i] & 0xff) << 24 | (encoded[++i] & 0xff) << 16 |
(encoded[++i] & 0xff) << 8 | (encoded[++i] & 0xff) << 0;
}
this.stack.push(value);
}
return error;
},
executeCommand: function(howManyArgs, command, keepStack) {
var stackLength = this.stack.length;
if (howManyArgs > stackLength) {
return true;
}
var start = stackLength - howManyArgs;
for (var i = start; i < stackLength; i++) {
var value = this.stack[i];
if (value === (value | 0)) { // int
this.output.push(28, (value >> 8) & 0xff, value & 0xff);
} else { // fixed point
value = (65536 * value) | 0;
this.output.push(255,
(value >> 24) & 0xFF,
(value >> 16) & 0xFF,
(value >> 8) & 0xFF,
value & 0xFF);
}
}
this.output.push.apply(this.output, command);
if (keepStack) {
this.stack.splice(start, howManyArgs);
} else {
this.stack.length = 0;
}
return false;
}
};
return Type1CharString;
})();
/*
* Type1Parser encapsulate the needed code for parsing a Type1 font
* program. Some of its logic depends on the Type2 charstrings
* structure.
* Note: this doesn't really parse the font since that would require evaluation
* of PostScript, but it is possible in most cases to extract what we need
* without a full parse.
*/
var Type1Parser = (function Type1ParserClosure() {
/*
* Decrypt a Sequence of Ciphertext Bytes to Produce the Original Sequence
* of Plaintext Bytes. The function took a key as a parameter which can be
* for decrypting the eexec block of for decoding charStrings.
*/
var EEXEC_ENCRYPT_KEY = 55665;
var CHAR_STRS_ENCRYPT_KEY = 4330;
function isHexDigit(code) {
return code >= 48 && code <= 57 || // '0'-'9'
code >= 65 && code <= 70 || // 'A'-'F'
code >= 97 && code <= 102; // 'a'-'f'
}
function decrypt(data, key, discardNumber) {
if (discardNumber >= data.length) {
return new Uint8Array(0);
}
var r = key | 0, c1 = 52845, c2 = 22719, i, j;
for (i = 0; i < discardNumber; i++) {
r = ((data[i] + r) * c1 + c2) & ((1 << 16) - 1);
}
var count = data.length - discardNumber;
var decrypted = new Uint8Array(count);
for (i = discardNumber, j = 0; j < count; i++, j++) {
var value = data[i];
decrypted[j] = value ^ (r >> 8);
r = ((value + r) * c1 + c2) & ((1 << 16) - 1);
}
return decrypted;
}
function decryptAscii(data, key, discardNumber) {
var r = key | 0, c1 = 52845, c2 = 22719;
var count = data.length, maybeLength = count >>> 1;
var decrypted = new Uint8Array(maybeLength);
var i, j;
for (i = 0, j = 0; i < count; i++) {
var digit1 = data[i];
if (!isHexDigit(digit1)) {
continue;
}
i++;
var digit2;
while (i < count && !isHexDigit(digit2 = data[i])) {
i++;
}
if (i < count) {
var value = parseInt(String.fromCharCode(digit1, digit2), 16);
decrypted[j++] = value ^ (r >> 8);
r = ((value + r) * c1 + c2) & ((1 << 16) - 1);
}
}
return Array.prototype.slice.call(decrypted, discardNumber, j);
}
function isSpecial(c) {
return c === 0x2F || // '/'
c === 0x5B || c === 0x5D || // '[', ']'
c === 0x7B || c === 0x7D || // '{', '}'
c === 0x28 || c === 0x29; // '(', ')'
}
function Type1Parser(stream, encrypted, seacAnalysisEnabled) {
if (encrypted) {
var data = stream.getBytes();
var isBinary = !(isHexDigit(data[0]) && isHexDigit(data[1]) &&
isHexDigit(data[2]) && isHexDigit(data[3]));
stream = new Stream(isBinary ? decrypt(data, EEXEC_ENCRYPT_KEY, 4) :
decryptAscii(data, EEXEC_ENCRYPT_KEY, 4));
}
this.seacAnalysisEnabled = !!seacAnalysisEnabled;
this.stream = stream;
this.nextChar();
}
Type1Parser.prototype = {
readNumberArray: function Type1Parser_readNumberArray() {
this.getToken(); // read '[' or '{' (arrays can start with either)
var array = [];
while (true) {
var token = this.getToken();
if (token === null || token === ']' || token === '}') {
break;
}
array.push(parseFloat(token || 0));
}
return array;
},
readNumber: function Type1Parser_readNumber() {
var token = this.getToken();
return parseFloat(token || 0);
},
readInt: function Type1Parser_readInt() {
// Use '| 0' to prevent setting a double into length such as the double
// does not flow into the loop variable.
var token = this.getToken();
return parseInt(token || 0, 10) | 0;
},
readBoolean: function Type1Parser_readBoolean() {
var token = this.getToken();
// Use 1 and 0 since that's what type2 charstrings use.
return token === 'true' ? 1 : 0;
},
nextChar : function Type1_nextChar() {
return (this.currentChar = this.stream.getByte());
},
getToken: function Type1Parser_getToken() {
// Eat whitespace and comments.
var comment = false;
var ch = this.currentChar;
while (true) {
if (ch === -1) {
return null;
}
if (comment) {
if (ch === 0x0A || ch === 0x0D) {
comment = false;
}
} else if (ch === 0x25) { // '%'
comment = true;
} else if (!Lexer.isSpace(ch)) {
break;
}
ch = this.nextChar();
}
if (isSpecial(ch)) {
this.nextChar();
return String.fromCharCode(ch);
}
var token = '';
do {
token += String.fromCharCode(ch);
ch = this.nextChar();
} while (ch >= 0 && !Lexer.isSpace(ch) && !isSpecial(ch));
return token;
},
/*
* Returns an object containing a Subrs array and a CharStrings
* array extracted from and eexec encrypted block of data
*/
extractFontProgram: function Type1Parser_extractFontProgram() {
var stream = this.stream;
var subrs = [], charstrings = [];
var privateData = Object.create(null);
privateData['lenIV'] = 4;
var program = {
subrs: [],
charstrings: [],
properties: {
'privateData': privateData
}
};
var token, length, data, lenIV, encoded;
while ((token = this.getToken()) !== null) {
if (token !== '/') {
continue;
}
token = this.getToken();
switch (token) {
case 'CharStrings':
// The number immediately following CharStrings must be greater or
// equal to the number of CharStrings.
this.getToken();
this.getToken(); // read in 'dict'
this.getToken(); // read in 'dup'
this.getToken(); // read in 'begin'
while(true) {
token = this.getToken();
if (token === null || token === 'end') {
break;
}
if (token !== '/') {
continue;
}
var glyph = this.getToken();
length = this.readInt();
this.getToken(); // read in 'RD' or '-|'
data = stream.makeSubStream(stream.pos, length);
lenIV = program.properties.privateData['lenIV'];
encoded = decrypt(data.getBytes(), CHAR_STRS_ENCRYPT_KEY, lenIV);
// Skip past the required space and binary data.
stream.skip(length);
this.nextChar();
token = this.getToken(); // read in 'ND' or '|-'
if (token === 'noaccess') {
this.getToken(); // read in 'def'
}
charstrings.push({
glyph: glyph,
encoded: encoded
});
}
break;
case 'Subrs':
var num = this.readInt();
this.getToken(); // read in 'array'
while ((token = this.getToken()) === 'dup') {
var index = this.readInt();
length = this.readInt();
this.getToken(); // read in 'RD' or '-|'
data = stream.makeSubStream(stream.pos, length);
lenIV = program.properties.privateData['lenIV'];
encoded = decrypt(data.getBytes(), CHAR_STRS_ENCRYPT_KEY, lenIV);
// Skip past the required space and binary data.
stream.skip(length);
this.nextChar();
token = this.getToken(); // read in 'NP' or '|'
if (token === 'noaccess') {
this.getToken(); // read in 'put'
}
subrs[index] = encoded;
}
break;
case 'BlueValues':
case 'OtherBlues':
case 'FamilyBlues':
case 'FamilyOtherBlues':
var blueArray = this.readNumberArray();
// *Blue* values may contain invalid data: disables reading of
// those values when hinting is disabled.
if (blueArray.length > 0 && (blueArray.length % 2) === 0 &&
HINTING_ENABLED) {
program.properties.privateData[token] = blueArray;
}
break;
case 'StemSnapH':
case 'StemSnapV':
program.properties.privateData[token] = this.readNumberArray();
break;
case 'StdHW':
case 'StdVW':
program.properties.privateData[token] =
this.readNumberArray()[0];
break;
case 'BlueShift':
case 'lenIV':
case 'BlueFuzz':
case 'BlueScale':
case 'LanguageGroup':
case 'ExpansionFactor':
program.properties.privateData[token] = this.readNumber();
break;
case 'ForceBold':
program.properties.privateData[token] = this.readBoolean();
break;
}
}
for (var i = 0; i < charstrings.length; i++) {
glyph = charstrings[i].glyph;
encoded = charstrings[i].encoded;
var charString = new Type1CharString();
var error = charString.convert(encoded, subrs,
this.seacAnalysisEnabled);
var output = charString.output;
if (error) {
// It seems when FreeType encounters an error while evaluating a glyph
// that it completely ignores the glyph so we'll mimic that behaviour
// here and put an endchar to make the validator happy.
output = [14];
}
program.charstrings.push({
glyphName: glyph,
charstring: output,
width: charString.width,
lsb: charString.lsb,
seac: charString.seac
});
}
return program;
},
extractFontHeader: function Type1Parser_extractFontHeader(properties) {
var token;
while ((token = this.getToken()) !== null) {
if (token !== '/') {
continue;
}
token = this.getToken();
switch (token) {
case 'FontMatrix':
var matrix = this.readNumberArray();
properties.fontMatrix = matrix;
break;
case 'Encoding':
var encodingArg = this.getToken();
var encoding;
if (!/^\d+$/.test(encodingArg)) {
// encoding name is specified
encoding = getEncoding(encodingArg);
} else {
encoding = [];
var size = parseInt(encodingArg, 10) | 0;
this.getToken(); // read in 'array'
for (var j = 0; j < size; j++) {
token = this.getToken();
// skipping till first dup or def (e.g. ignoring for statement)
while (token !== 'dup' && token !== 'def') {
token = this.getToken();
if (token === null) {
return; // invalid header
}
}
if (token === 'def') {
break; // read all array data
}
var index = this.readInt();
this.getToken(); // read in '/'
var glyph = this.getToken();
encoding[index] = glyph;
this.getToken(); // read the in 'put'
}
}
properties.builtInEncoding = encoding;
break;
case 'FontBBox':
var fontBBox = this.readNumberArray();
// adjusting ascent/descent
properties.ascent = fontBBox[3];
properties.descent = fontBBox[1];
properties.ascentScaled = true;
break;
}
}
}
};
return Type1Parser;
})();
exports.Type1Parser = Type1Parser;
}));

View File

@ -1,6 +1,5 @@
/* globals expect, it, describe, CFFCompiler, CFFParser, CFFIndex, CFFStrings,
SEAC_ANALYSIS_ENABLED, Type1Parser, StringStream,
_enableSeacAnalysis */
Type1Parser, StringStream, SEAC_ANALYSIS_ENABLED */
'use strict';
@ -38,7 +37,7 @@ describe('font', function() {
}
describe('CFFParser', function() {
var parser = new CFFParser(fontData, {});
var parser = new CFFParser(fontData, {}, SEAC_ANALYSIS_ENABLED);
var cff = parser.parse();
it('parses header', function() {
@ -117,46 +116,42 @@ describe('font', function() {
});
it('parses a CharString endchar with 4 args w/seac enabled', function() {
var seacAnalysisState = SEAC_ANALYSIS_ENABLED;
try {
_enableSeacAnalysis(true);
var bytes = new Uint8Array([0, 1, // count
1, // offsetSize
0, // offset[0]
237, 247, 22, 247, 72, 204, 247, 86, 14]);
parser.bytes = bytes;
var charStringsIndex = parser.parseIndex(0).obj;
var result = parser.parseCharStrings(charStringsIndex);
expect(result.charStrings.count).toEqual(1);
expect(result.charStrings.get(0).length).toEqual(1);
expect(result.seacs.length).toEqual(1);
expect(result.seacs[0].length).toEqual(4);
expect(result.seacs[0][0]).toEqual(130);
expect(result.seacs[0][1]).toEqual(180);
expect(result.seacs[0][2]).toEqual(65);
expect(result.seacs[0][3]).toEqual(194);
} finally {
_enableSeacAnalysis(seacAnalysisState);
}
var parser = new CFFParser(fontData, {},
/* seacAnalysisEnabled = */ true);
var cff = parser.parse();
var bytes = new Uint8Array([0, 1, // count
1, // offsetSize
0, // offset[0]
237, 247, 22, 247, 72, 204, 247, 86, 14]);
parser.bytes = bytes;
var charStringsIndex = parser.parseIndex(0).obj;
var result = parser.parseCharStrings(charStringsIndex);
expect(result.charStrings.count).toEqual(1);
expect(result.charStrings.get(0).length).toEqual(1);
expect(result.seacs.length).toEqual(1);
expect(result.seacs[0].length).toEqual(4);
expect(result.seacs[0][0]).toEqual(130);
expect(result.seacs[0][1]).toEqual(180);
expect(result.seacs[0][2]).toEqual(65);
expect(result.seacs[0][3]).toEqual(194);
});
it('parses a CharString endchar with 4 args w/seac disabled', function() {
var seacAnalysisState = SEAC_ANALYSIS_ENABLED;
try {
_enableSeacAnalysis(false);
var bytes = new Uint8Array([0, 1, // count
1, // offsetSize
0, // offset[0]
237, 247, 22, 247, 72, 204, 247, 86, 14]);
parser.bytes = bytes;
var charStringsIndex = parser.parseIndex(0).obj;
var result = parser.parseCharStrings(charStringsIndex);
expect(result.charStrings.count).toEqual(1);
expect(result.charStrings.get(0).length).toEqual(9);
expect(result.seacs.length).toEqual(0);
} finally {
_enableSeacAnalysis(seacAnalysisState);
}
var parser = new CFFParser(fontData, {},
/* seacAnalysisEnabled = */ false);
var cff = parser.parse();
var bytes = new Uint8Array([0, 1, // count
1, // offsetSize
0, // offset[0]
237, 247, 22, 247, 72, 204, 247, 86, 14]);
parser.bytes = bytes;
var charStringsIndex = parser.parseIndex(0).obj;
var result = parser.parseCharStrings(charStringsIndex);
expect(result.charStrings.count).toEqual(1);
expect(result.charStrings.get(0).length).toEqual(9);
expect(result.seacs.length).toEqual(0);
});
it('parses a CharString endchar no args', function() {
@ -302,7 +297,7 @@ describe('font', function() {
it('splits tokens', function() {
var stream = new StringStream('/BlueValues[-17 0]noaccess def');
var parser = new Type1Parser(stream);
var parser = new Type1Parser(stream, false, SEAC_ANALYSIS_ENABLED);
expect(parser.getToken()).toEqual('/');
expect(parser.getToken()).toEqual('BlueValues');
expect(parser.getToken()).toEqual('[');
@ -315,35 +310,35 @@ describe('font', function() {
});
it('handles glued tokens', function() {
var stream = new StringStream('dup/CharStrings');
var parser = new Type1Parser(stream);
var parser = new Type1Parser(stream, false, SEAC_ANALYSIS_ENABLED);
expect(parser.getToken()).toEqual('dup');
expect(parser.getToken()).toEqual('/');
expect(parser.getToken()).toEqual('CharStrings');
});
it('ignores whitespace', function() {
var stream = new StringStream('\nab c\t');
var parser = new Type1Parser(stream);
var parser = new Type1Parser(stream, false, SEAC_ANALYSIS_ENABLED);
expect(parser.getToken()).toEqual('ab');
expect(parser.getToken()).toEqual('c');
});
it('parses numbers', function() {
var stream = new StringStream('123');
var parser = new Type1Parser(stream);
var parser = new Type1Parser(stream, false, SEAC_ANALYSIS_ENABLED);
expect(parser.readNumber()).toEqual(123);
});
it('parses booleans', function() {
var stream = new StringStream('true false');
var parser = new Type1Parser(stream);
var parser = new Type1Parser(stream, false, SEAC_ANALYSIS_ENABLED);
expect(parser.readBoolean()).toEqual(1);
expect(parser.readBoolean()).toEqual(0);
});
it('parses number arrays', function() {
var stream = new StringStream('[1 2]');
var parser = new Type1Parser(stream);
var parser = new Type1Parser(stream, false, SEAC_ANALYSIS_ENABLED);
expect(parser.readNumberArray()).toEqual([1, 2]);
// Variation on spacing.
stream = new StringStream('[ 1 2 ]');
parser = new Type1Parser(stream);
parser = new Type1Parser(stream, false, SEAC_ANALYSIS_ENABLED);
expect(parser.readNumberArray()).toEqual([1, 2]);
});
it('skips comments', function() {
@ -352,7 +347,7 @@ describe('font', function() {
'%%Title: CMSY10\n' +
'%Version: 003.002\n' +
'FontDirectory');
var parser = new Type1Parser(stream);
var parser = new Type1Parser(stream, false, SEAC_ANALYSIS_ENABLED);
expect(parser.getToken()).toEqual('FontDirectory');
});
it('parses font program', function() {
@ -364,7 +359,7 @@ describe('font', function() {
'/CharStrings 46 dict dup begin\n' +
'/.notdef 1 RD x ND' + '\n' +
'end');
var parser = new Type1Parser(stream);
var parser = new Type1Parser(stream, false, SEAC_ANALYSIS_ENABLED);
var program = parser.extractFontProgram();
expect(program.charstrings.length).toEqual(1);
expect(program.properties.privateData.ExpansionFactor).toEqual(99);
@ -372,7 +367,7 @@ describe('font', function() {
it('parses font header font matrix', function() {
var stream = new StringStream(
'/FontMatrix [0.001 0 0 0.001 0 0 ]readonly def\n');
var parser = new Type1Parser(stream);
var parser = new Type1Parser(stream, false, SEAC_ANALYSIS_ENABLED);
var props = {};
parser.extractFontHeader(props);
expect(props.fontMatrix).toEqual([0.001, 0, 0, 0.001, 0, 0]);
@ -383,7 +378,7 @@ describe('font', function() {
'0 1 255 {1 index exch /.notdef put} for\n' +
'dup 33 /arrowright put\n' +
'readonly def\n');
var parser = new Type1Parser(stream);
var parser = new Type1Parser(stream, false, SEAC_ANALYSIS_ENABLED);
var props = { overridableEncoding: true };
parser.extractFontHeader(props);
expect(props.builtInEncoding[33]).toEqual('arrowright');

View File

@ -48,12 +48,14 @@ function initializePDFJS(callback) {
'pdfjs/core/annotation', 'pdfjs/core/crypto', 'pdfjs/core/stream',
'pdfjs/core/fonts', 'pdfjs/core/ps_parser', 'pdfjs/core/function',
'pdfjs/core/parser', 'pdfjs/core/evaluator', 'pdfjs/core/cmap',
'pdfjs/core/worker', 'pdfjs/core/network', 'pdfjs/display/api',
'pdfjs/display/metadata', 'pdfjs/display/dom_utils'],
'pdfjs/core/worker', 'pdfjs/core/network', 'pdfjs/core/type1_parser',
'pdfjs/core/cff_parser', 'pdfjs/display/api', 'pdfjs/display/metadata',
'pdfjs/display/dom_utils'],
function (sharedUtil, displayGlobal, corePrimitives, coreAnnotation,
coreCrypto, coreStream, coreFonts, corePsParser, coreFunction,
coreParser, coreEvaluator, coreCMap, coreWorker, coreNetwork,
displayAPI, displayMetadata, displayDOMUtils) {
coreType1Parser, coreCFFParser, displayAPI, displayMetadata,
displayDOMUtils) {
pdfjsLibs = {
sharedUtil: sharedUtil,
@ -70,6 +72,8 @@ function initializePDFJS(callback) {
coreCMap: coreCMap,
coreWorker: coreWorker,
coreNetwork: coreNetwork,
coreType1Parser: coreType1Parser,
coreCFFParser: coreCFFParser,
displayAPI: displayAPI,
displayMetadata: displayMetadata,
displayDOMUtils: displayDOMUtils