[Pkg-javascript-commits] [pdf.js] 325/414: Extract `Type1Parser` from fonts.js
David Prévot
taffit at moszumanska.debian.org
Tue Jun 28 17:12:36 UTC 2016
This is an automated email from the git hooks/post-receive script.
taffit pushed a commit to branch master
in repository pdf.js.
commit ef551e8266490182dfdddf5575d8ed2b5339aeb6
Author: Jonas Jenwald <jonas.jenwald at gmail.com>
Date: Fri Apr 1 13:37:00 2016 +0200
Extract `Type1Parser` from fonts.js
---
src/core/fonts.js | 695 +-------------------------------------------
src/core/type1_parser.js | 722 ++++++++++++++++++++++++++++++++++++++++++++++
test/unit/font_spec.js | 22 +-
test/unit/jasmine-boot.js | 9 +-
4 files changed, 748 insertions(+), 700 deletions(-)
diff --git a/src/core/fonts.js b/src/core/fonts.js
index 78df52e..7acbd0d 100644
--- a/src/core/fonts.js
+++ b/src/core/fonts.js
@@ -22,26 +22,26 @@
'pdfjs/core/glyphlist', 'pdfjs/core/charsets',
'pdfjs/core/font_renderer', 'pdfjs/core/encodings',
'pdfjs/core/standard_fonts', 'pdfjs/core/unicode',
- 'pdfjs/core/cff_parser'], factory);
+ 'pdfjs/core/type1_parser', 'pdfjs/core/cff_parser'], factory);
} else if (typeof exports !== 'undefined') {
factory(exports, require('../shared/util.js'), require('./primitives.js'),
require('./stream.js'), require('./parser.js'),
require('./glyphlist.js'), require('./charsets.js'),
require('./font_renderer.js'), require('./encodings.js'),
require('./standard_fonts'), require('./unicode.js'),
- require('./cff_parser'));
+ require('./type1_parser'), require('./cff_parser'));
} else {
factory((root.pdfjsCoreFonts = {}), root.pdfjsSharedUtil,
root.pdfjsCorePrimitives, root.pdfjsCoreStream, root.pdfjsCoreParser,
root.pdfjsCoreGlyphList, root.pdfjsCoreCharsets,
root.pdfjsCoreFontRenderer, root.pdfjsCoreEncodings,
root.pdfjsCoreStandardFonts, root.pdfjsCoreUnicode,
- root.pdfjsCoreCFFParser);
+ root.pdfjsCoreType1Parser, root.pdfjsCoreCFFParser);
}
}(this, function (exports, sharedUtil, corePrimitives, coreStream, coreParser,
coreGlyphList, coreCharsets, coreFontRenderer,
coreEncodings, coreStandardFonts, coreUnicode,
- coreCFFParser) {
+ coreType1Parser, coreCFFParser) {
var FONT_IDENTITY_MATRIX = sharedUtil.FONT_IDENTITY_MATRIX;
var FontType = sharedUtil.FontType;
@@ -82,6 +82,7 @@ var getSupplementalGlyphMapForArialBlack =
var getUnicodeRangeFor = coreUnicode.getUnicodeRangeFor;
var mapSpecialUnicodeValues = coreUnicode.mapSpecialUnicodeValues;
var getUnicodeForGlyph = coreUnicode.getUnicodeForGlyph;
+var Type1Parser = coreType1Parser.Type1Parser;
var CFFStandardStrings = coreCFFParser.CFFStandardStrings;
var CFFParser = coreCFFParser.CFFParser;
var CFFCompiler = coreCFFParser.CFFCompiler;
@@ -102,10 +103,6 @@ var SKIP_PRIVATE_USE_RANGE_F000_TO_F01F = false;
// except for Type 3 fonts
var PDF_GLYPH_SPACE_UNITS = 1000;
-// Hinting is currently disabled due to unknown problems on windows
-// in tracemonkey and various other pdfs with type1 fonts.
-var HINTING_ENABLED = false;
-
// Accented charactars are not displayed properly on Windows, using this flag
// to control analysis of seac charstrings.
var SEAC_ANALYSIS_ENABLED = false;
@@ -2885,681 +2882,6 @@ function type1FontGlyphMapping(properties, builtInEncoding, glyphNames) {
return charCodeToGlyphId;
}
-/*
- * 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) {
- 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);
- 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 (SEAC_ANALYSIS_ENABLED) {
- 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) {
- 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.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);
- 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;
-})();
-
// Type1Font is also a CIDFontType0.
var Type1Font = (function Type1FontClosure() {
function findBlock(streamBytes, signature, startIndex) {
@@ -3694,7 +3016,8 @@ var Type1Font = (function Type1FontClosure() {
// Get the data block containing glyphs and subrs informations
var headerBlock = getHeaderBlock(file, headerBlockLength);
headerBlockLength = headerBlock.length;
- var headerBlockParser = new Type1Parser(headerBlock.stream);
+ var headerBlockParser = new Type1Parser(headerBlock.stream, false,
+ SEAC_ANALYSIS_ENABLED);
headerBlockParser.extractFontHeader(properties);
if (pfbHeaderPresent) {
@@ -3706,7 +3029,8 @@ var Type1Font = (function Type1FontClosure() {
// Decrypt the data blocks and retrieve it's content
var eexecBlock = getEexecBlock(file, eexecBlockLength);
eexecBlockLength = eexecBlock.length;
- var eexecBlockParser = new Type1Parser(eexecBlock.stream, true);
+ var eexecBlockParser = new Type1Parser(eexecBlock.stream, true,
+ SEAC_ANALYSIS_ENABLED);
var data = eexecBlockParser.extractFontProgram();
for (var info in data.properties) {
properties[info] = data.properties[info];
@@ -3992,6 +3316,5 @@ exports.Font = Font;
exports.FontFlags = FontFlags;
exports.IdentityToUnicodeMap = IdentityToUnicodeMap;
exports.ToUnicodeMap = ToUnicodeMap;
-exports.Type1Parser = Type1Parser;
exports.getFontType = getFontType;
}));
diff --git a/src/core/type1_parser.js b/src/core/type1_parser.js
new file mode 100644
index 0000000..20cfabe
--- /dev/null
+++ b/src/core/type1_parser.js
@@ -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;
+}));
diff --git a/test/unit/font_spec.js b/test/unit/font_spec.js
index 378e588..3d8e188 100644
--- a/test/unit/font_spec.js
+++ b/test/unit/font_spec.js
@@ -297,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('[');
@@ -310,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() {
@@ -347,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() {
@@ -359,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);
@@ -367,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]);
@@ -378,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');
diff --git a/test/unit/jasmine-boot.js b/test/unit/jasmine-boot.js
index 8648c66..8ae3bda 100644
--- a/test/unit/jasmine-boot.js
+++ b/test/unit/jasmine-boot.js
@@ -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/core/cff_parser',
- '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,
- coreCFFParser, displayAPI, displayMetadata, displayDOMUtils) {
+ coreType1Parser, coreCFFParser, displayAPI, displayMetadata,
+ displayDOMUtils) {
pdfjsLibs = {
sharedUtil: sharedUtil,
@@ -70,6 +72,7 @@ function initializePDFJS(callback) {
coreCMap: coreCMap,
coreWorker: coreWorker,
coreNetwork: coreNetwork,
+ coreType1Parser: coreType1Parser,
coreCFFParser: coreCFFParser,
displayAPI: displayAPI,
displayMetadata: displayMetadata,
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-javascript/pdf.js.git
More information about the Pkg-javascript-commits
mailing list