Evaluate type 1 charstrings for conversion to type 2.
This commit is contained in:
parent
2ccad4ca45
commit
b5278c5e27
770
src/fonts.js
770
src/fonts.js
@ -26,7 +26,8 @@ var SYMBOLIC_FONT_GLYPH_OFFSET = 0xF000;
|
||||
// except for Type 3 fonts
|
||||
var PDF_GLYPH_SPACE_UNITS = 1000;
|
||||
|
||||
// Until hinting is fully supported this constant can be used
|
||||
// Hinting is currently disabled due to unknown problems on windows
|
||||
// in tracemonkey and various other pdfs with type1 fonts.
|
||||
var HINTING_ENABLED = false;
|
||||
|
||||
var FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0];
|
||||
@ -4351,12 +4352,324 @@ var ErrorFont = (function ErrorFontClosure() {
|
||||
return ErrorFont;
|
||||
})();
|
||||
|
||||
var CallothersubrCmd = (function CallothersubrCmdClosure() {
|
||||
function CallothersubrCmd(index) {
|
||||
this.index = index;
|
||||
/*
|
||||
* 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 = [];
|
||||
}
|
||||
|
||||
return CallothersubrCmd;
|
||||
Type1CharString.prototype = {
|
||||
convert: function Type1CharString_convert(encoded, subrs) {
|
||||
var count = encoded.length;
|
||||
var error = false;
|
||||
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;
|
||||
}
|
||||
var subrNumber = this.stack.pop();
|
||||
error = this.convert(subrs[subrNumber], subrs);
|
||||
break;
|
||||
case 11: // return
|
||||
return error;
|
||||
break;
|
||||
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.
|
||||
var wx = this.stack.pop();
|
||||
var sbx = this.stack.pop();
|
||||
this.lsb = sbx;
|
||||
this.width = wx;
|
||||
this.stack.push(sbx);
|
||||
error = this.executeCommand(1, 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.
|
||||
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();
|
||||
var wx = this.stack.pop();
|
||||
var sby = this.stack.pop();
|
||||
var sbx = this.stack.pop();
|
||||
this.lsb = sbx;
|
||||
this.width = wx;
|
||||
this.stack.push(sbx, sby);
|
||||
error = this.executeCommand(2, 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;
|
||||
}
|
||||
var 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 = [];
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
return Type1CharString;
|
||||
})();
|
||||
|
||||
/*
|
||||
@ -4387,338 +4700,6 @@ var Type1Parser = function type1Parser() {
|
||||
return decryptedString.slice(discardNumber);
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 charStringDictionary = {
|
||||
'1': 'hstem',
|
||||
'3': 'vstem',
|
||||
'4': 'vmoveto',
|
||||
'5': 'rlineto',
|
||||
'6': 'hlineto',
|
||||
'7': 'vlineto',
|
||||
'8': 'rrcurveto',
|
||||
|
||||
// closepath is a Type1 command that do not take argument and is useless
|
||||
// in Type2 and it can simply be ignored.
|
||||
'9': null, // closepath
|
||||
|
||||
'10': 'callsubr',
|
||||
|
||||
// return is normally used inside sub-routines to tells to the execution
|
||||
// flow that it can be back to normal.
|
||||
// During the translation process Type1 charstrings will be flattened and
|
||||
// sub-routines will be embedded directly into the charstring directly, so
|
||||
// this can be ignored safely.
|
||||
'11': 'return',
|
||||
|
||||
'12': {
|
||||
// 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.
|
||||
'0': null, // dotsection
|
||||
|
||||
// [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.
|
||||
'1': 'vstem',
|
||||
'2': 'hstem',
|
||||
|
||||
'6': 'endchar', // seac
|
||||
// Type1 only command with command not (yet) built-in ,throw an error
|
||||
'7': -1, // sbw
|
||||
|
||||
'10': 'add',
|
||||
'11': 'sub',
|
||||
'12': 'div',
|
||||
|
||||
// callothersubr is a mechanism to make calls on the postscript
|
||||
// interpreter, this is not supported by Type2 charstring but hopefully
|
||||
// most of the default commands can be ignored safely.
|
||||
'16': 'callothersubr',
|
||||
|
||||
'17': 'pop',
|
||||
|
||||
// setcurrentpoint sets the current point to x, y without performing a
|
||||
// moveto (this is a one shot positionning command). This is used only
|
||||
// with the return of an OtherSubrs call.
|
||||
// TODO Implement the OtherSubrs charstring embedding and replace this
|
||||
// call by a no-op, like 2 'pop' commands for example.
|
||||
'33': null // setcurrentpoint
|
||||
},
|
||||
'13': 'hsbw',
|
||||
'14': 'endchar',
|
||||
'21': 'rmoveto',
|
||||
'22': 'hmoveto',
|
||||
'30': 'vhcurveto',
|
||||
'31': 'hvcurveto'
|
||||
};
|
||||
|
||||
var ESCAPE_CMD = 12;
|
||||
|
||||
// Breaks up the stack by arguments and also calculates the value.
|
||||
function breakUpArgs(stack, numArgs) {
|
||||
var args = [];
|
||||
var index = stack.length - 1;
|
||||
for (var i = 0; i < numArgs; i++) {
|
||||
if (index < 0) {
|
||||
args.unshift({ arg: [0],
|
||||
value: 0,
|
||||
offset: 0 });
|
||||
warn('Malformed charstring stack: not enough values on stack.');
|
||||
continue;
|
||||
}
|
||||
var token = stack[index];
|
||||
if (token === 'div') {
|
||||
var a = stack[index - 2];
|
||||
var b = stack[index - 1];
|
||||
if (!isInt(a) || !isInt(b)) {
|
||||
warn('Malformed charsting stack: expected ints on stack for div.');
|
||||
a = 0;
|
||||
b = 1;
|
||||
}
|
||||
args.unshift({ arg: [a, b, 'div'],
|
||||
value: a / b,
|
||||
offset: index - 2 });
|
||||
index -= 3;
|
||||
} else if (isInt(token)) {
|
||||
args.unshift({ arg: stack.slice(index, index + 1),
|
||||
value: token,
|
||||
offset: index });
|
||||
index--;
|
||||
} else {
|
||||
warn('Malformed charsting stack: found bad token ' + token + '.');
|
||||
}
|
||||
}
|
||||
return args;
|
||||
}
|
||||
|
||||
function decodeCharString(array) {
|
||||
var charstring = [];
|
||||
var lsb = 0;
|
||||
var width = 0;
|
||||
var flexing = false;
|
||||
|
||||
var value = '';
|
||||
var count = array.length;
|
||||
for (var i = 0; i < count; i++) {
|
||||
value = array[i];
|
||||
|
||||
if (value < 32) {
|
||||
var command = null;
|
||||
if (value == ESCAPE_CMD) {
|
||||
var escape = array[++i];
|
||||
|
||||
// TODO Clean this code
|
||||
if (escape == 16) {
|
||||
var index = charstring.pop();
|
||||
var argc = charstring.pop();
|
||||
for (var j = 0; j < argc; j++)
|
||||
charstring.push('drop');
|
||||
|
||||
// If the flex mechanism is not used in a font program, Adobe
|
||||
// states that entries 0, 1 and 2 can simply be replaced by
|
||||
// {}, which means that we can simply ignore them.
|
||||
if (index < 3) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// This is the same things about hint replacement, if it is not used
|
||||
// entry 3 can be replaced by {3}
|
||||
// TODO support hint replacment
|
||||
if (index == 3) {
|
||||
charstring.push(3);
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
|
||||
assert(argc == 0, 'callothersubr with arguments is not supported');
|
||||
charstring.push(new CallothersubrCmd(index));
|
||||
continue;
|
||||
} else if (escape == 7) { // sbw
|
||||
var args = breakUpArgs(charstring, 4);
|
||||
var arg0 = args[0];
|
||||
var arg1 = args[1];
|
||||
var arg2 = args[2];
|
||||
lsb = arg0.value;
|
||||
width = arg2.value;
|
||||
// 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.
|
||||
charstring = arg2.arg;
|
||||
charstring = charstring.concat(arg0.arg, arg1.arg);
|
||||
charstring.push('rmoveto');
|
||||
continue;
|
||||
} else if (escape == 17 || escape == 33) {
|
||||
// pop or setcurrentpoint commands can be ignored
|
||||
// since we are not doing callothersubr
|
||||
continue;
|
||||
} else if (escape == 6) {
|
||||
// seac is like type 2's special endchar but it doesn't use the
|
||||
// first argument asb, so remove it.
|
||||
var args = breakUpArgs(charstring, 5);
|
||||
var arg0 = args[0];
|
||||
charstring.splice(arg0.offset, arg0.arg.length);
|
||||
} else if (!HINTING_ENABLED && (escape == 1 || escape == 2)) {
|
||||
charstring.push('drop', 'drop', 'drop', 'drop', 'drop', 'drop');
|
||||
continue;
|
||||
}
|
||||
|
||||
command = charStringDictionary['12'][escape];
|
||||
} else {
|
||||
if (value == 13) { // hsbw
|
||||
var args = breakUpArgs(charstring, 2);
|
||||
var arg0 = args[0];
|
||||
var arg1 = args[1];
|
||||
lsb = arg0.value;
|
||||
width = arg1.value;
|
||||
// To convert to type2 we have to move the width value to the first
|
||||
// part of the charstring and then use hmoveto with lsb.
|
||||
charstring = arg1.arg;
|
||||
charstring = charstring.concat(arg0.arg);
|
||||
charstring.push('hmoveto');
|
||||
continue;
|
||||
} else if (value == 10) { // callsubr
|
||||
if (charstring[charstring.length - 1] < 3) { // subr #0..2
|
||||
// XXX: According to the spec if flex or hinting is not used then
|
||||
// subroutines 0-3 can actually be anything defined by the font,
|
||||
// so we really shouldn't be doing flex here but when
|
||||
// callothersubr 0-2 is used. There hasn't been a real world
|
||||
// example of this yet so we'll keep doing it here.
|
||||
var subrNumber = charstring.pop();
|
||||
switch (subrNumber) {
|
||||
case 1:
|
||||
flexing = true; // prepare for flex coordinates
|
||||
break;
|
||||
case 0:
|
||||
var flexArgs = breakUpArgs(charstring, 17);
|
||||
|
||||
// removing all flex arguments from the stack
|
||||
charstring.splice(flexArgs[0].offset,
|
||||
charstring.length - flexArgs[0].offset);
|
||||
|
||||
charstring = charstring.concat(
|
||||
flexArgs[0].arg, // bcp1x +
|
||||
flexArgs[2].arg, // rpx
|
||||
['add'],
|
||||
flexArgs[1].arg, // bcp1y +
|
||||
flexArgs[3].arg, // rpy
|
||||
['add'],
|
||||
flexArgs[4].arg, // bcp2x
|
||||
flexArgs[5].arg, // bcp2y
|
||||
flexArgs[6].arg, // p2x
|
||||
flexArgs[7].arg, // p2y
|
||||
flexArgs[8].arg, // bcp3x
|
||||
flexArgs[9].arg, // bcp3y
|
||||
flexArgs[10].arg, // bcp4x
|
||||
flexArgs[11].arg, // bcp4y
|
||||
flexArgs[12].arg, // p3x
|
||||
flexArgs[13].arg, // p3y
|
||||
flexArgs[14].arg, // flexDepth
|
||||
// 15 = finalx unused by flex
|
||||
// 16 = finaly unused by flex
|
||||
['flex']
|
||||
);
|
||||
|
||||
flexing = false;
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
} else if (value == 21 && flexing) { // rmoveto
|
||||
continue; // ignoring rmoveto
|
||||
} else if (value == 22 && flexing) { // hmoveto
|
||||
// Add the dy for flex.
|
||||
charstring.push(0);
|
||||
continue; // ignoring hmoveto
|
||||
} else if (value == 4 && flexing) { // vmoveto
|
||||
// Insert the dx for flex before dy.
|
||||
var flexArgs = breakUpArgs(charstring, 1);
|
||||
charstring.splice(flexArgs[0].offset, 0, 0);
|
||||
continue; // ignoring vmoveto
|
||||
} else if (!HINTING_ENABLED && (value == 1 || value == 3)) {
|
||||
charstring.push('drop', 'drop');
|
||||
continue;
|
||||
}
|
||||
command = charStringDictionary[value];
|
||||
}
|
||||
|
||||
// Some charstring commands are meaningless in Type2 and will return
|
||||
// a null, let's just ignored them
|
||||
if (!command && i < count) {
|
||||
continue;
|
||||
} else if (!command) {
|
||||
break;
|
||||
} else if (command == -1) {
|
||||
warn('Support for Type1 command ' + value +
|
||||
' (' + escape + ') is not implemented in charstring: ' +
|
||||
charstring);
|
||||
if (value == 12) {
|
||||
// we know how to ignore only some the Type1 commands
|
||||
switch (escape) {
|
||||
case 7:
|
||||
charstring.push('drop', 'drop', 'drop', 'drop');
|
||||
continue;
|
||||
case 8:
|
||||
charstring.push('drop');
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
value = command;
|
||||
} else if (value <= 246) {
|
||||
value = value - 139;
|
||||
} else if (value <= 250) {
|
||||
value = ((value - 247) * 256) + array[++i] + 108;
|
||||
} else if (value <= 254) {
|
||||
value = -((value - 251) * 256) - array[++i] - 108;
|
||||
} else {
|
||||
value = (array[++i] & 0xff) << 24 | (array[++i] & 0xff) << 16 |
|
||||
(array[++i] & 0xff) << 8 | (array[++i] & 0xff) << 0;
|
||||
}
|
||||
|
||||
charstring.push(value);
|
||||
}
|
||||
|
||||
return { charstring: charstring, width: width, lsb: lsb };
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns an object containing a Subrs array and a CharStrings
|
||||
* array extracted from and eexec encrypted block of data
|
||||
@ -4786,6 +4767,7 @@ var Type1Parser = function type1Parser() {
|
||||
eexecStr += String.fromCharCode(eexec[i]);
|
||||
|
||||
var glyphsSection = false, subrsSection = false;
|
||||
var subrs = [], charstrings = [];
|
||||
var program = {
|
||||
subrs: [],
|
||||
charstrings: [],
|
||||
@ -4821,17 +4803,14 @@ var Type1Parser = function type1Parser() {
|
||||
var data = eexec.slice(i, i + length);
|
||||
var lenIV = program.properties.privateData['lenIV'];
|
||||
var encoded = decrypt(data, CHAR_STRS_ENCRYPT_KEY, lenIV);
|
||||
var str = decodeCharString(encoded);
|
||||
|
||||
if (glyphsSection) {
|
||||
program.charstrings.push({
|
||||
charstrings.push({
|
||||
glyph: glyph,
|
||||
data: str.charstring,
|
||||
lsb: str.lsb,
|
||||
width: str.width
|
||||
encoded: encoded
|
||||
});
|
||||
} else {
|
||||
program.subrs.push(str.charstring);
|
||||
subrs.push(encoded);
|
||||
}
|
||||
i += length;
|
||||
token = '';
|
||||
@ -4863,12 +4842,11 @@ var Type1Parser = function type1Parser() {
|
||||
var data = eexec.slice(i + 1, i + 1 + length);
|
||||
var lenIV = program.properties.privateData['lenIV'];
|
||||
var encoded = decrypt(data, CHAR_STRS_ENCRYPT_KEY, lenIV);
|
||||
var str = decodeCharString(encoded);
|
||||
i = i + 1 + length;
|
||||
t = getToken(); // read in 'NP'
|
||||
if (t == 'noaccess')
|
||||
getToken(); // read in 'put'
|
||||
program.subrs[index] = str.charstring;
|
||||
subrs[index] = encoded;
|
||||
}
|
||||
break;
|
||||
case '/BlueValues':
|
||||
@ -4915,6 +4893,26 @@ var Type1Parser = function type1Parser() {
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < charstrings.length; i++) {
|
||||
var glyph = charstrings[i].glyph;
|
||||
var 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({
|
||||
glyph: glyph,
|
||||
data: output,
|
||||
lsb: charString.lsb,
|
||||
width: charString.width
|
||||
});
|
||||
}
|
||||
|
||||
return program;
|
||||
};
|
||||
|
||||
@ -5106,14 +5104,11 @@ Type1Font.prototype = {
|
||||
},
|
||||
|
||||
getType2Charstrings: function Type1Font_getType2Charstrings(
|
||||
type1Subrs) {
|
||||
type1Charstrings) {
|
||||
var type2Charstrings = [];
|
||||
var count = type1Subrs.length;
|
||||
var type1Charstrings = [];
|
||||
for (var i = 0; i < count; i++)
|
||||
type1Charstrings.push(type1Subrs[i].charstring.slice());
|
||||
for (var i = 0; i < count; i++)
|
||||
type2Charstrings.push(this.flattenCharstring(type1Charstrings, i));
|
||||
for (var i = 0, ii = type1Charstrings.length; i < ii; i++) {
|
||||
type2Charstrings.push(type1Charstrings[i].charstring);
|
||||
}
|
||||
return type2Charstrings;
|
||||
},
|
||||
|
||||
@ -5133,81 +5128,12 @@ Type1Font.prototype = {
|
||||
type2Subrs.push([0x0B]);
|
||||
|
||||
for (var i = 0; i < count; i++) {
|
||||
type2Subrs.push(this.flattenCharstring(type1Subrs, i));
|
||||
type2Subrs.push(type1Subrs[i]);
|
||||
}
|
||||
|
||||
return type2Subrs;
|
||||
},
|
||||
|
||||
/*
|
||||
* Flatten the commands by interpreting the postscript code and replacing
|
||||
* every 'callsubr', 'callothersubr' by the real commands.
|
||||
*/
|
||||
commandsMap: {
|
||||
'hstem': 1,
|
||||
'vstem': 3,
|
||||
'vmoveto': 4,
|
||||
'rlineto': 5,
|
||||
'hlineto': 6,
|
||||
'vlineto': 7,
|
||||
'rrcurveto': 8,
|
||||
'callsubr': 10,
|
||||
'return': 11,
|
||||
'add': [12, 10],
|
||||
'sub': [12, 11],
|
||||
'div': [12, 12],
|
||||
'exch': [12, 28],
|
||||
'flex': [12, 35],
|
||||
'drop' : [12, 18],
|
||||
'endchar': 14,
|
||||
'rmoveto': 21,
|
||||
'hmoveto': 22,
|
||||
'vhcurveto': 30,
|
||||
'hvcurveto': 31
|
||||
},
|
||||
|
||||
flattenCharstring: function Type1Font_flattenCharstring(charstrings, index) {
|
||||
var charstring = charstrings[index];
|
||||
if (!charstring)
|
||||
return [0x0B];
|
||||
var map = this.commandsMap;
|
||||
// charstring changes size - can't cache .length in loop
|
||||
for (var i = 0; i < charstring.length; i++) {
|
||||
var command = charstring[i];
|
||||
if (typeof command === 'string') {
|
||||
var cmd = map[command];
|
||||
assert(cmd, 'Unknow command: ' + command);
|
||||
|
||||
if (isArray(cmd))
|
||||
charstring.splice(i++, 1, cmd[0], cmd[1]);
|
||||
else
|
||||
charstring[i] = cmd;
|
||||
} else if (command instanceof CallothersubrCmd) {
|
||||
var otherSubrCharstring = charstrings[command.index];
|
||||
if (otherSubrCharstring) {
|
||||
var lastCommand = otherSubrCharstring.indexOf('return');
|
||||
if (lastCommand >= 0)
|
||||
otherSubrCharstring = otherSubrCharstring.slice(0, lastCommand);
|
||||
charstring.splice.apply(charstring,
|
||||
[i, 1].concat(otherSubrCharstring));
|
||||
} else
|
||||
charstring.splice(i, 1); // ignoring empty subr call
|
||||
i--;
|
||||
} else {
|
||||
// Type1 charstring use a division for number above 32000
|
||||
if (command > 32000) {
|
||||
var divisor = charstring[i + 1];
|
||||
command /= divisor;
|
||||
charstring.splice(i, 3, 28, (command >> 8) & 0xff, command & 0xff);
|
||||
} else {
|
||||
charstring.splice(i, 1, 28, (command >> 8) & 0xff, command & 0xff);
|
||||
}
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
return charstring;
|
||||
},
|
||||
|
||||
wrap: function Type1Font_wrap(name, glyphs, charstrings, subrs, properties) {
|
||||
var cff = new CFF();
|
||||
cff.header = new CFFHeader(1, 0, 4, 4);
|
||||
|
2
test/pdfs/issue1936.pdf.link
Normal file
2
test/pdfs/issue1936.pdf.link
Normal file
@ -0,0 +1,2 @@
|
||||
http://sci2s.ugr.es/keel/pdf/specific/articulo/alp99.pdf
|
||||
|
@ -776,6 +776,14 @@
|
||||
"link": true,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "issue1936",
|
||||
"file": "pdfs/issue1936.pdf",
|
||||
"md5": "7302eb9b6a626308e2a933aaed9e1756",
|
||||
"rounds": 1,
|
||||
"pageLimit": 1,
|
||||
"link": true,
|
||||
"type": "eq"
|
||||
},
|
||||
{ "id": "issue2337",
|
||||
"file": "pdfs/issue2337.pdf",
|
||||
"md5": "ea10f4131202b9b8f2a6cb7770d3f185",
|
||||
|
Loading…
Reference in New Issue
Block a user