Extract Type1Parser
from fonts.js
This commit is contained in:
parent
b961e1d21b
commit
ef551e8266
@ -22,26 +22,26 @@
|
|||||||
'pdfjs/core/glyphlist', 'pdfjs/core/charsets',
|
'pdfjs/core/glyphlist', 'pdfjs/core/charsets',
|
||||||
'pdfjs/core/font_renderer', 'pdfjs/core/encodings',
|
'pdfjs/core/font_renderer', 'pdfjs/core/encodings',
|
||||||
'pdfjs/core/standard_fonts', 'pdfjs/core/unicode',
|
'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') {
|
} else if (typeof exports !== 'undefined') {
|
||||||
factory(exports, require('../shared/util.js'), require('./primitives.js'),
|
factory(exports, require('../shared/util.js'), require('./primitives.js'),
|
||||||
require('./stream.js'), require('./parser.js'),
|
require('./stream.js'), require('./parser.js'),
|
||||||
require('./glyphlist.js'), require('./charsets.js'),
|
require('./glyphlist.js'), require('./charsets.js'),
|
||||||
require('./font_renderer.js'), require('./encodings.js'),
|
require('./font_renderer.js'), require('./encodings.js'),
|
||||||
require('./standard_fonts'), require('./unicode.js'),
|
require('./standard_fonts'), require('./unicode.js'),
|
||||||
require('./cff_parser'));
|
require('./type1_parser'), require('./cff_parser'));
|
||||||
} else {
|
} else {
|
||||||
factory((root.pdfjsCoreFonts = {}), root.pdfjsSharedUtil,
|
factory((root.pdfjsCoreFonts = {}), root.pdfjsSharedUtil,
|
||||||
root.pdfjsCorePrimitives, root.pdfjsCoreStream, root.pdfjsCoreParser,
|
root.pdfjsCorePrimitives, root.pdfjsCoreStream, root.pdfjsCoreParser,
|
||||||
root.pdfjsCoreGlyphList, root.pdfjsCoreCharsets,
|
root.pdfjsCoreGlyphList, root.pdfjsCoreCharsets,
|
||||||
root.pdfjsCoreFontRenderer, root.pdfjsCoreEncodings,
|
root.pdfjsCoreFontRenderer, root.pdfjsCoreEncodings,
|
||||||
root.pdfjsCoreStandardFonts, root.pdfjsCoreUnicode,
|
root.pdfjsCoreStandardFonts, root.pdfjsCoreUnicode,
|
||||||
root.pdfjsCoreCFFParser);
|
root.pdfjsCoreType1Parser, root.pdfjsCoreCFFParser);
|
||||||
}
|
}
|
||||||
}(this, function (exports, sharedUtil, corePrimitives, coreStream, coreParser,
|
}(this, function (exports, sharedUtil, corePrimitives, coreStream, coreParser,
|
||||||
coreGlyphList, coreCharsets, coreFontRenderer,
|
coreGlyphList, coreCharsets, coreFontRenderer,
|
||||||
coreEncodings, coreStandardFonts, coreUnicode,
|
coreEncodings, coreStandardFonts, coreUnicode,
|
||||||
coreCFFParser) {
|
coreType1Parser, coreCFFParser) {
|
||||||
|
|
||||||
var FONT_IDENTITY_MATRIX = sharedUtil.FONT_IDENTITY_MATRIX;
|
var FONT_IDENTITY_MATRIX = sharedUtil.FONT_IDENTITY_MATRIX;
|
||||||
var FontType = sharedUtil.FontType;
|
var FontType = sharedUtil.FontType;
|
||||||
@ -82,6 +82,7 @@ var getSupplementalGlyphMapForArialBlack =
|
|||||||
var getUnicodeRangeFor = coreUnicode.getUnicodeRangeFor;
|
var getUnicodeRangeFor = coreUnicode.getUnicodeRangeFor;
|
||||||
var mapSpecialUnicodeValues = coreUnicode.mapSpecialUnicodeValues;
|
var mapSpecialUnicodeValues = coreUnicode.mapSpecialUnicodeValues;
|
||||||
var getUnicodeForGlyph = coreUnicode.getUnicodeForGlyph;
|
var getUnicodeForGlyph = coreUnicode.getUnicodeForGlyph;
|
||||||
|
var Type1Parser = coreType1Parser.Type1Parser;
|
||||||
var CFFStandardStrings = coreCFFParser.CFFStandardStrings;
|
var CFFStandardStrings = coreCFFParser.CFFStandardStrings;
|
||||||
var CFFParser = coreCFFParser.CFFParser;
|
var CFFParser = coreCFFParser.CFFParser;
|
||||||
var CFFCompiler = coreCFFParser.CFFCompiler;
|
var CFFCompiler = coreCFFParser.CFFCompiler;
|
||||||
@ -102,10 +103,6 @@ var SKIP_PRIVATE_USE_RANGE_F000_TO_F01F = false;
|
|||||||
// except for Type 3 fonts
|
// except for Type 3 fonts
|
||||||
var PDF_GLYPH_SPACE_UNITS = 1000;
|
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
|
// Accented charactars are not displayed properly on Windows, using this flag
|
||||||
// to control analysis of seac charstrings.
|
// to control analysis of seac charstrings.
|
||||||
var SEAC_ANALYSIS_ENABLED = false;
|
var SEAC_ANALYSIS_ENABLED = false;
|
||||||
@ -2885,681 +2882,6 @@ function type1FontGlyphMapping(properties, builtInEncoding, glyphNames) {
|
|||||||
return charCodeToGlyphId;
|
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.
|
// Type1Font is also a CIDFontType0.
|
||||||
var Type1Font = (function Type1FontClosure() {
|
var Type1Font = (function Type1FontClosure() {
|
||||||
function findBlock(streamBytes, signature, startIndex) {
|
function findBlock(streamBytes, signature, startIndex) {
|
||||||
@ -3694,7 +3016,8 @@ var Type1Font = (function Type1FontClosure() {
|
|||||||
// Get the data block containing glyphs and subrs informations
|
// Get the data block containing glyphs and subrs informations
|
||||||
var headerBlock = getHeaderBlock(file, headerBlockLength);
|
var headerBlock = getHeaderBlock(file, headerBlockLength);
|
||||||
headerBlockLength = headerBlock.length;
|
headerBlockLength = headerBlock.length;
|
||||||
var headerBlockParser = new Type1Parser(headerBlock.stream);
|
var headerBlockParser = new Type1Parser(headerBlock.stream, false,
|
||||||
|
SEAC_ANALYSIS_ENABLED);
|
||||||
headerBlockParser.extractFontHeader(properties);
|
headerBlockParser.extractFontHeader(properties);
|
||||||
|
|
||||||
if (pfbHeaderPresent) {
|
if (pfbHeaderPresent) {
|
||||||
@ -3706,7 +3029,8 @@ var Type1Font = (function Type1FontClosure() {
|
|||||||
// Decrypt the data blocks and retrieve it's content
|
// Decrypt the data blocks and retrieve it's content
|
||||||
var eexecBlock = getEexecBlock(file, eexecBlockLength);
|
var eexecBlock = getEexecBlock(file, eexecBlockLength);
|
||||||
eexecBlockLength = eexecBlock.length;
|
eexecBlockLength = eexecBlock.length;
|
||||||
var eexecBlockParser = new Type1Parser(eexecBlock.stream, true);
|
var eexecBlockParser = new Type1Parser(eexecBlock.stream, true,
|
||||||
|
SEAC_ANALYSIS_ENABLED);
|
||||||
var data = eexecBlockParser.extractFontProgram();
|
var data = eexecBlockParser.extractFontProgram();
|
||||||
for (var info in data.properties) {
|
for (var info in data.properties) {
|
||||||
properties[info] = data.properties[info];
|
properties[info] = data.properties[info];
|
||||||
@ -3992,6 +3316,5 @@ exports.Font = Font;
|
|||||||
exports.FontFlags = FontFlags;
|
exports.FontFlags = FontFlags;
|
||||||
exports.IdentityToUnicodeMap = IdentityToUnicodeMap;
|
exports.IdentityToUnicodeMap = IdentityToUnicodeMap;
|
||||||
exports.ToUnicodeMap = ToUnicodeMap;
|
exports.ToUnicodeMap = ToUnicodeMap;
|
||||||
exports.Type1Parser = Type1Parser;
|
|
||||||
exports.getFontType = getFontType;
|
exports.getFontType = getFontType;
|
||||||
}));
|
}));
|
||||||
|
722
src/core/type1_parser.js
Normal file
722
src/core/type1_parser.js
Normal 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;
|
||||||
|
}));
|
@ -297,7 +297,7 @@ describe('font', function() {
|
|||||||
|
|
||||||
it('splits tokens', function() {
|
it('splits tokens', function() {
|
||||||
var stream = new StringStream('/BlueValues[-17 0]noaccess def');
|
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('/');
|
||||||
expect(parser.getToken()).toEqual('BlueValues');
|
expect(parser.getToken()).toEqual('BlueValues');
|
||||||
expect(parser.getToken()).toEqual('[');
|
expect(parser.getToken()).toEqual('[');
|
||||||
@ -310,35 +310,35 @@ describe('font', function() {
|
|||||||
});
|
});
|
||||||
it('handles glued tokens', function() {
|
it('handles glued tokens', function() {
|
||||||
var stream = new StringStream('dup/CharStrings');
|
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('dup');
|
||||||
expect(parser.getToken()).toEqual('/');
|
expect(parser.getToken()).toEqual('/');
|
||||||
expect(parser.getToken()).toEqual('CharStrings');
|
expect(parser.getToken()).toEqual('CharStrings');
|
||||||
});
|
});
|
||||||
it('ignores whitespace', function() {
|
it('ignores whitespace', function() {
|
||||||
var stream = new StringStream('\nab c\t');
|
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('ab');
|
||||||
expect(parser.getToken()).toEqual('c');
|
expect(parser.getToken()).toEqual('c');
|
||||||
});
|
});
|
||||||
it('parses numbers', function() {
|
it('parses numbers', function() {
|
||||||
var stream = new StringStream('123');
|
var stream = new StringStream('123');
|
||||||
var parser = new Type1Parser(stream);
|
var parser = new Type1Parser(stream, false, SEAC_ANALYSIS_ENABLED);
|
||||||
expect(parser.readNumber()).toEqual(123);
|
expect(parser.readNumber()).toEqual(123);
|
||||||
});
|
});
|
||||||
it('parses booleans', function() {
|
it('parses booleans', function() {
|
||||||
var stream = new StringStream('true false');
|
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(1);
|
||||||
expect(parser.readBoolean()).toEqual(0);
|
expect(parser.readBoolean()).toEqual(0);
|
||||||
});
|
});
|
||||||
it('parses number arrays', function() {
|
it('parses number arrays', function() {
|
||||||
var stream = new StringStream('[1 2]');
|
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]);
|
expect(parser.readNumberArray()).toEqual([1, 2]);
|
||||||
// Variation on spacing.
|
// Variation on spacing.
|
||||||
stream = new StringStream('[ 1 2 ]');
|
stream = new StringStream('[ 1 2 ]');
|
||||||
parser = new Type1Parser(stream);
|
parser = new Type1Parser(stream, false, SEAC_ANALYSIS_ENABLED);
|
||||||
expect(parser.readNumberArray()).toEqual([1, 2]);
|
expect(parser.readNumberArray()).toEqual([1, 2]);
|
||||||
});
|
});
|
||||||
it('skips comments', function() {
|
it('skips comments', function() {
|
||||||
@ -347,7 +347,7 @@ describe('font', function() {
|
|||||||
'%%Title: CMSY10\n' +
|
'%%Title: CMSY10\n' +
|
||||||
'%Version: 003.002\n' +
|
'%Version: 003.002\n' +
|
||||||
'FontDirectory');
|
'FontDirectory');
|
||||||
var parser = new Type1Parser(stream);
|
var parser = new Type1Parser(stream, false, SEAC_ANALYSIS_ENABLED);
|
||||||
expect(parser.getToken()).toEqual('FontDirectory');
|
expect(parser.getToken()).toEqual('FontDirectory');
|
||||||
});
|
});
|
||||||
it('parses font program', function() {
|
it('parses font program', function() {
|
||||||
@ -359,7 +359,7 @@ describe('font', function() {
|
|||||||
'/CharStrings 46 dict dup begin\n' +
|
'/CharStrings 46 dict dup begin\n' +
|
||||||
'/.notdef 1 RD x ND' + '\n' +
|
'/.notdef 1 RD x ND' + '\n' +
|
||||||
'end');
|
'end');
|
||||||
var parser = new Type1Parser(stream);
|
var parser = new Type1Parser(stream, false, SEAC_ANALYSIS_ENABLED);
|
||||||
var program = parser.extractFontProgram();
|
var program = parser.extractFontProgram();
|
||||||
expect(program.charstrings.length).toEqual(1);
|
expect(program.charstrings.length).toEqual(1);
|
||||||
expect(program.properties.privateData.ExpansionFactor).toEqual(99);
|
expect(program.properties.privateData.ExpansionFactor).toEqual(99);
|
||||||
@ -367,7 +367,7 @@ describe('font', function() {
|
|||||||
it('parses font header font matrix', function() {
|
it('parses font header font matrix', function() {
|
||||||
var stream = new StringStream(
|
var stream = new StringStream(
|
||||||
'/FontMatrix [0.001 0 0 0.001 0 0 ]readonly def\n');
|
'/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 = {};
|
var props = {};
|
||||||
parser.extractFontHeader(props);
|
parser.extractFontHeader(props);
|
||||||
expect(props.fontMatrix).toEqual([0.001, 0, 0, 0.001, 0, 0]);
|
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' +
|
'0 1 255 {1 index exch /.notdef put} for\n' +
|
||||||
'dup 33 /arrowright put\n' +
|
'dup 33 /arrowright put\n' +
|
||||||
'readonly def\n');
|
'readonly def\n');
|
||||||
var parser = new Type1Parser(stream);
|
var parser = new Type1Parser(stream, false, SEAC_ANALYSIS_ENABLED);
|
||||||
var props = { overridableEncoding: true };
|
var props = { overridableEncoding: true };
|
||||||
parser.extractFontHeader(props);
|
parser.extractFontHeader(props);
|
||||||
expect(props.builtInEncoding[33]).toEqual('arrowright');
|
expect(props.builtInEncoding[33]).toEqual('arrowright');
|
||||||
|
@ -48,12 +48,14 @@ function initializePDFJS(callback) {
|
|||||||
'pdfjs/core/annotation', 'pdfjs/core/crypto', 'pdfjs/core/stream',
|
'pdfjs/core/annotation', 'pdfjs/core/crypto', 'pdfjs/core/stream',
|
||||||
'pdfjs/core/fonts', 'pdfjs/core/ps_parser', 'pdfjs/core/function',
|
'pdfjs/core/fonts', 'pdfjs/core/ps_parser', 'pdfjs/core/function',
|
||||||
'pdfjs/core/parser', 'pdfjs/core/evaluator', 'pdfjs/core/cmap',
|
'pdfjs/core/parser', 'pdfjs/core/evaluator', 'pdfjs/core/cmap',
|
||||||
'pdfjs/core/worker', 'pdfjs/core/network', 'pdfjs/core/cff_parser',
|
'pdfjs/core/worker', 'pdfjs/core/network', 'pdfjs/core/type1_parser',
|
||||||
'pdfjs/display/api', 'pdfjs/display/metadata', 'pdfjs/display/dom_utils'],
|
'pdfjs/core/cff_parser', 'pdfjs/display/api', 'pdfjs/display/metadata',
|
||||||
|
'pdfjs/display/dom_utils'],
|
||||||
function (sharedUtil, displayGlobal, corePrimitives, coreAnnotation,
|
function (sharedUtil, displayGlobal, corePrimitives, coreAnnotation,
|
||||||
coreCrypto, coreStream, coreFonts, corePsParser, coreFunction,
|
coreCrypto, coreStream, coreFonts, corePsParser, coreFunction,
|
||||||
coreParser, coreEvaluator, coreCMap, coreWorker, coreNetwork,
|
coreParser, coreEvaluator, coreCMap, coreWorker, coreNetwork,
|
||||||
coreCFFParser, displayAPI, displayMetadata, displayDOMUtils) {
|
coreType1Parser, coreCFFParser, displayAPI, displayMetadata,
|
||||||
|
displayDOMUtils) {
|
||||||
|
|
||||||
pdfjsLibs = {
|
pdfjsLibs = {
|
||||||
sharedUtil: sharedUtil,
|
sharedUtil: sharedUtil,
|
||||||
@ -70,6 +72,7 @@ function initializePDFJS(callback) {
|
|||||||
coreCMap: coreCMap,
|
coreCMap: coreCMap,
|
||||||
coreWorker: coreWorker,
|
coreWorker: coreWorker,
|
||||||
coreNetwork: coreNetwork,
|
coreNetwork: coreNetwork,
|
||||||
|
coreType1Parser: coreType1Parser,
|
||||||
coreCFFParser: coreCFFParser,
|
coreCFFParser: coreCFFParser,
|
||||||
displayAPI: displayAPI,
|
displayAPI: displayAPI,
|
||||||
displayMetadata: displayMetadata,
|
displayMetadata: displayMetadata,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user