252 lines
9.6 KiB
JavaScript
252 lines
9.6 KiB
JavaScript
/**
|
|
* @file 检查hint设置情况
|
|
* @author mengke01(kekee000@gmail.com)
|
|
*
|
|
* copy from:
|
|
* https://github.com/mozilla/pdf.js/blob/master/src/core/core.js
|
|
*/
|
|
|
|
define(
|
|
function (require) {
|
|
|
|
var TTOpsStackDeltas = [
|
|
0, 0, 0, 0, 0, 0, 0, 0, -2, -2, -2, -2, 0, 0, -2, -5, -1, -1, -1, -1, -1, -1, -1,
|
|
-1, 0, 0, -1, 0, -1, -1, -1, -1, 1, -1, -999, 0, 1, 0, -1, -2, 0, -1, -2, -1, -1,
|
|
0, -1, -1, 0, 0, -999, -999, -1, -1, -1, -1, -2, -999, -2, -2, -999, 0, -2, -2, 0,
|
|
0, -2, 0, -2, 0, 0, 0, -2, -1, -1, 1, 1, 0, 0, -1, -1, -1, -1, -1, -1, -1, 0, 0, -1,
|
|
0, -1, -1, 0, -999, -1, -1, -1, -1, -1, -1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2,
|
|
-999, -999, -999, -999, -999, -1, -1, -2, -2, 0, 0, 0, 0, -1, -1, -999, -2, -2, 0,
|
|
0, -1, -2, -2, 0, 0, 0, -1, -1, -1, -2
|
|
];
|
|
|
|
// 0xC0-DF == -1 and 0xE0-FF == -2
|
|
|
|
function sanitizeTTProgram(data, ttContext) {
|
|
|
|
var i = 0;
|
|
var j;
|
|
var n;
|
|
var b;
|
|
var funcId;
|
|
var pc;
|
|
var lastEndf = 0,
|
|
var lastDeff = 0;
|
|
var stack = [];
|
|
var callstack = [];
|
|
var functionsCalled = [];
|
|
var tooComplexToFollowFunctions =
|
|
ttContext.tooComplexToFollowFunctions;
|
|
var inFDEF = false;
|
|
var ifLevel = 0;
|
|
var inELSE = 0;
|
|
for (var ii = data.length; i < ii;) {
|
|
var op = data[i++];
|
|
// The TrueType instruction set docs can be found at
|
|
// https://developer.apple.com/fonts/TTRefMan/RM05/Chap5.html
|
|
if (op === 0x40) { // NPUSHB - pushes n bytes
|
|
n = data[i++];
|
|
if (inFDEF || inELSE) {
|
|
i += n;
|
|
} else {
|
|
for (j = 0; j < n; j++) {
|
|
stack.push(data[i++]);
|
|
}
|
|
}
|
|
}
|
|
else if (op === 0x41) { // NPUSHW - pushes n words
|
|
n = data[i++];
|
|
if (inFDEF || inELSE) {
|
|
i += n * 2;
|
|
}
|
|
else {
|
|
for (j = 0; j < n; j++) {
|
|
b = data[i++];
|
|
stack.push((b << 8) | data[i++]);
|
|
}
|
|
}
|
|
}
|
|
else if ((op & 0xF8) === 0xB0) { // PUSHB - pushes bytes
|
|
n = op - 0xB0 + 1;
|
|
if (inFDEF || inELSE) {
|
|
i += n;
|
|
}
|
|
else {
|
|
for (j = 0; j < n; j++) {
|
|
stack.push(data[i++]);
|
|
}
|
|
}
|
|
}
|
|
else if ((op & 0xF8) === 0xB8) { // PUSHW - pushes words
|
|
n = op - 0xB8 + 1;
|
|
if (inFDEF || inELSE) {
|
|
i += n * 2;
|
|
}
|
|
else {
|
|
for (j = 0; j < n; j++) {
|
|
b = data[i++];
|
|
stack.push((b << 8) | data[i++]);
|
|
}
|
|
}
|
|
}
|
|
else if (op === 0x2B && !tooComplexToFollowFunctions) { // CALL
|
|
if (!inFDEF && !inELSE) {
|
|
// collecting inforamtion about which functions are used
|
|
funcId = stack[stack.length - 1];
|
|
ttContext.functionsUsed[funcId] = true;
|
|
if (funcId in ttContext.functionsStackDeltas) {
|
|
stack.length += ttContext.functionsStackDeltas[funcId];
|
|
}
|
|
else if (funcId in ttContext.functionsDefined &&
|
|
functionsCalled.indexOf(funcId) < 0) {
|
|
callstack.push({
|
|
data: data,
|
|
i: i,
|
|
stackTop: stack.length - 1
|
|
});
|
|
functionsCalled.push(funcId);
|
|
pc = ttContext.functionsDefined[funcId];
|
|
if (!pc) {
|
|
warn('TT: CALL non-existent function');
|
|
ttContext.hintsValid = false;
|
|
return;
|
|
}
|
|
data = pc.data;
|
|
i = pc.i;
|
|
}
|
|
}
|
|
}
|
|
else if (op === 0x2C && !tooComplexToFollowFunctions) { // FDEF
|
|
if (inFDEF || inELSE) {
|
|
warn('TT: nested FDEFs not allowed');
|
|
tooComplexToFollowFunctions = true;
|
|
}
|
|
inFDEF = true;
|
|
// collecting inforamtion about which functions are defined
|
|
lastDeff = i;
|
|
funcId = stack.pop();
|
|
ttContext.functionsDefined[funcId] = {
|
|
data: data,
|
|
i: i
|
|
};
|
|
}
|
|
else if (op === 0x2D) { // ENDF - end of function
|
|
if (inFDEF) {
|
|
inFDEF = false;
|
|
lastEndf = i;
|
|
}
|
|
else {
|
|
pc = callstack.pop();
|
|
if (!pc) {
|
|
warn('TT: ENDF bad stack');
|
|
ttContext.hintsValid = false;
|
|
return;
|
|
}
|
|
funcId = functionsCalled.pop();
|
|
data = pc.data;
|
|
i = pc.i;
|
|
ttContext.functionsStackDeltas[funcId] =
|
|
stack.length - pc.stackTop;
|
|
}
|
|
}
|
|
else if (op === 0x89) { // IDEF - instruction definition
|
|
if (inFDEF || inELSE) {
|
|
warn('TT: nested IDEFs not allowed');
|
|
tooComplexToFollowFunctions = true;
|
|
}
|
|
inFDEF = true;
|
|
// recording it as a function to track ENDF
|
|
lastDeff = i;
|
|
}
|
|
else if (op === 0x58) { // IF
|
|
++ifLevel;
|
|
}
|
|
else if (op === 0x1B) { // ELSE
|
|
inELSE = ifLevel;
|
|
}
|
|
else if (op === 0x59) { // EIF
|
|
if (inELSE === ifLevel) {
|
|
inELSE = 0;
|
|
}
|
|
--ifLevel;
|
|
}
|
|
else if (op === 0x1C) { // JMPR
|
|
if (!inFDEF && !inELSE) {
|
|
var offset = stack[stack.length - 1];
|
|
// only jumping forward to prevent infinite loop
|
|
if (offset > 0) {
|
|
i += offset - 1;
|
|
}
|
|
}
|
|
}
|
|
// Adjusting stack not extactly, but just enough to get function id
|
|
if (!inFDEF && !inELSE) {
|
|
var stackDelta = op <= 0x8E
|
|
? TTOpsStackDeltas[op]
|
|
: op >= 0xC0 && op <= 0xDF ? -1 : op >= 0xE0 ? -2 : 0;
|
|
if (op >= 0x71 && op <= 0x75) {
|
|
n = stack.pop();
|
|
if (n === n) {
|
|
stackDelta = -n * 2;
|
|
}
|
|
}
|
|
|
|
while (stackDelta < 0 && stack.length > 0) {
|
|
stack.pop();
|
|
stackDelta++;
|
|
}
|
|
|
|
while (stackDelta > 0) {
|
|
stack.push(NaN); // pushing any number into stack
|
|
stackDelta--;
|
|
}
|
|
}
|
|
}
|
|
|
|
ttContext.tooComplexToFollowFunctions = tooComplexToFollowFunctions;
|
|
|
|
var content = [data];
|
|
if (i > data.length) {
|
|
content.push(new Uint8Array(i - data.length));
|
|
}
|
|
|
|
if (lastDeff > lastEndf) {
|
|
warn('TT: complementing a missing function tail');
|
|
// new function definition started, but not finished
|
|
// complete function by [CLEAR, ENDF]
|
|
content.push(new Uint8Array([0x22, 0x2D]));
|
|
}
|
|
|
|
foldTTTable(table, content);
|
|
}
|
|
|
|
function checkInvalidFunctions(ttContext, maxFunctionDefs) {
|
|
if (ttContext.tooComplexToFollowFunctions) {
|
|
return;
|
|
}
|
|
if (ttContext.functionsDefined.length > maxFunctionDefs) {
|
|
warn('TT: more functions defined than expected');
|
|
ttContext.hintsValid = false;
|
|
return;
|
|
}
|
|
for (var j = 0, jj = ttContext.functionsUsed.length; j < jj; j++) {
|
|
if (j > maxFunctionDefs) {
|
|
warn('TT: invalid function id: ' + j);
|
|
ttContext.hintsValid = false;
|
|
return;
|
|
}
|
|
if (ttContext.functionsUsed[j] && !ttContext.functionsDefined[j]) {
|
|
warn('TT: undefined function: ' + j);
|
|
ttContext.hintsValid = false;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
var hinting = {
|
|
};
|
|
|
|
return hinting;
|
|
}
|
|
);
|