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
|
// except for Type 3 fonts
|
||||||
var PDF_GLYPH_SPACE_UNITS = 1000;
|
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 HINTING_ENABLED = false;
|
||||||
|
|
||||||
var FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0];
|
var FONT_IDENTITY_MATRIX = [0.001, 0, 0, 0.001, 0, 0];
|
||||||
@ -4351,12 +4352,324 @@ var ErrorFont = (function ErrorFontClosure() {
|
|||||||
return ErrorFont;
|
return ErrorFont;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
var CallothersubrCmd = (function CallothersubrCmdClosure() {
|
/*
|
||||||
function CallothersubrCmd(index) {
|
* CharStrings are encoded following the the CharString Encoding sequence
|
||||||
this.index = index;
|
* 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);
|
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
|
* Returns an object containing a Subrs array and a CharStrings
|
||||||
* array extracted from and eexec encrypted block of data
|
* array extracted from and eexec encrypted block of data
|
||||||
@ -4786,6 +4767,7 @@ var Type1Parser = function type1Parser() {
|
|||||||
eexecStr += String.fromCharCode(eexec[i]);
|
eexecStr += String.fromCharCode(eexec[i]);
|
||||||
|
|
||||||
var glyphsSection = false, subrsSection = false;
|
var glyphsSection = false, subrsSection = false;
|
||||||
|
var subrs = [], charstrings = [];
|
||||||
var program = {
|
var program = {
|
||||||
subrs: [],
|
subrs: [],
|
||||||
charstrings: [],
|
charstrings: [],
|
||||||
@ -4821,17 +4803,14 @@ var Type1Parser = function type1Parser() {
|
|||||||
var data = eexec.slice(i, i + length);
|
var data = eexec.slice(i, i + length);
|
||||||
var lenIV = program.properties.privateData['lenIV'];
|
var lenIV = program.properties.privateData['lenIV'];
|
||||||
var encoded = decrypt(data, CHAR_STRS_ENCRYPT_KEY, lenIV);
|
var encoded = decrypt(data, CHAR_STRS_ENCRYPT_KEY, lenIV);
|
||||||
var str = decodeCharString(encoded);
|
|
||||||
|
|
||||||
if (glyphsSection) {
|
if (glyphsSection) {
|
||||||
program.charstrings.push({
|
charstrings.push({
|
||||||
glyph: glyph,
|
glyph: glyph,
|
||||||
data: str.charstring,
|
encoded: encoded
|
||||||
lsb: str.lsb,
|
|
||||||
width: str.width
|
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
program.subrs.push(str.charstring);
|
subrs.push(encoded);
|
||||||
}
|
}
|
||||||
i += length;
|
i += length;
|
||||||
token = '';
|
token = '';
|
||||||
@ -4863,12 +4842,11 @@ var Type1Parser = function type1Parser() {
|
|||||||
var data = eexec.slice(i + 1, i + 1 + length);
|
var data = eexec.slice(i + 1, i + 1 + length);
|
||||||
var lenIV = program.properties.privateData['lenIV'];
|
var lenIV = program.properties.privateData['lenIV'];
|
||||||
var encoded = decrypt(data, CHAR_STRS_ENCRYPT_KEY, lenIV);
|
var encoded = decrypt(data, CHAR_STRS_ENCRYPT_KEY, lenIV);
|
||||||
var str = decodeCharString(encoded);
|
|
||||||
i = i + 1 + length;
|
i = i + 1 + length;
|
||||||
t = getToken(); // read in 'NP'
|
t = getToken(); // read in 'NP'
|
||||||
if (t == 'noaccess')
|
if (t == 'noaccess')
|
||||||
getToken(); // read in 'put'
|
getToken(); // read in 'put'
|
||||||
program.subrs[index] = str.charstring;
|
subrs[index] = encoded;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case '/BlueValues':
|
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;
|
return program;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -5106,14 +5104,11 @@ Type1Font.prototype = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
getType2Charstrings: function Type1Font_getType2Charstrings(
|
getType2Charstrings: function Type1Font_getType2Charstrings(
|
||||||
type1Subrs) {
|
type1Charstrings) {
|
||||||
var type2Charstrings = [];
|
var type2Charstrings = [];
|
||||||
var count = type1Subrs.length;
|
for (var i = 0, ii = type1Charstrings.length; i < ii; i++) {
|
||||||
var type1Charstrings = [];
|
type2Charstrings.push(type1Charstrings[i].charstring);
|
||||||
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));
|
|
||||||
return type2Charstrings;
|
return type2Charstrings;
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -5133,81 +5128,12 @@ Type1Font.prototype = {
|
|||||||
type2Subrs.push([0x0B]);
|
type2Subrs.push([0x0B]);
|
||||||
|
|
||||||
for (var i = 0; i < count; i++) {
|
for (var i = 0; i < count; i++) {
|
||||||
type2Subrs.push(this.flattenCharstring(type1Subrs, i));
|
type2Subrs.push(type1Subrs[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return type2Subrs;
|
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) {
|
wrap: function Type1Font_wrap(name, glyphs, charstrings, subrs, properties) {
|
||||||
var cff = new CFF();
|
var cff = new CFF();
|
||||||
cff.header = new CFFHeader(1, 0, 4, 4);
|
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,
|
"link": true,
|
||||||
"type": "eq"
|
"type": "eq"
|
||||||
},
|
},
|
||||||
|
{ "id": "issue1936",
|
||||||
|
"file": "pdfs/issue1936.pdf",
|
||||||
|
"md5": "7302eb9b6a626308e2a933aaed9e1756",
|
||||||
|
"rounds": 1,
|
||||||
|
"pageLimit": 1,
|
||||||
|
"link": true,
|
||||||
|
"type": "eq"
|
||||||
|
},
|
||||||
{ "id": "issue2337",
|
{ "id": "issue2337",
|
||||||
"file": "pdfs/issue2337.pdf",
|
"file": "pdfs/issue2337.pdf",
|
||||||
"md5": "ea10f4131202b9b8f2a6cb7770d3f185",
|
"md5": "ea10f4131202b9b8f2a6cb7770d3f185",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user