Add the necessary bits to handle Arrays, Procedure and a bigger set of instructions for Type1
This commit is contained in:
parent
e739bcc515
commit
e302bd93ee
357
PDFFont.js
357
PDFFont.js
@ -1,9 +1,11 @@
|
|||||||
|
|
||||||
|
var Font = new Dict();
|
||||||
|
|
||||||
var Type1Parser = function(aAsciiStream, aBinaryStream) {
|
var Type1Parser = function(aAsciiStream, aBinaryStream) {
|
||||||
var lexer = new Lexer(aAsciiStream);
|
var lexer = new Lexer(aAsciiStream);
|
||||||
|
|
||||||
// Turn on this flag for additional debugging logs
|
// Turn on this flag for additional debugging logs
|
||||||
var debug = false;
|
var debug = true;
|
||||||
|
|
||||||
var dump = function(aData) {
|
var dump = function(aData) {
|
||||||
if (debug)
|
if (debug)
|
||||||
@ -190,7 +192,7 @@ var Type1Parser = function(aAsciiStream, aBinaryStream) {
|
|||||||
userDict = new Dict();
|
userDict = new Dict();
|
||||||
|
|
||||||
var dictionaryStack = {
|
var dictionaryStack = {
|
||||||
__innerStack__: [systemDict, globalDict],
|
__innerStack__: [systemDict, globalDict, userDict],
|
||||||
|
|
||||||
push: function(aDictionary) {
|
push: function(aDictionary) {
|
||||||
this.__innerStack__.push(aDictionary);
|
this.__innerStack__.push(aDictionary);
|
||||||
@ -216,7 +218,7 @@ var Type1Parser = function(aAsciiStream, aBinaryStream) {
|
|||||||
get length() {
|
get length() {
|
||||||
return this.__innerStack__.length;
|
return this.__innerStack__.length;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The execution stack holds executable objects (mainly procedures and files)
|
* The execution stack holds executable objects (mainly procedures and files)
|
||||||
@ -252,7 +254,7 @@ var Type1Parser = function(aAsciiStream, aBinaryStream) {
|
|||||||
get length() {
|
get length() {
|
||||||
return this.__innerStack__.length;
|
return this.__innerStack__.length;
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
function nextInStack() {
|
function nextInStack() {
|
||||||
var currentProcedure = executionStack.peek();
|
var currentProcedure = executionStack.peek();
|
||||||
@ -260,11 +262,18 @@ var Type1Parser = function(aAsciiStream, aBinaryStream) {
|
|||||||
var command = currentProcedure.shift();
|
var command = currentProcedure.shift();
|
||||||
if (!currentProcedure.length)
|
if (!currentProcedure.length)
|
||||||
executionStack.pop();
|
executionStack.pop();
|
||||||
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
return lexer.getObj();
|
return lexer.getObj();
|
||||||
}
|
};
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
function parseNext() {
|
||||||
|
setTimeout(function() {
|
||||||
|
self.getObj();
|
||||||
|
}, 0);
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Parse a font file from the first segment to the last assuming the eexec
|
* Parse a font file from the first segment to the last assuming the eexec
|
||||||
@ -274,142 +283,203 @@ var Type1Parser = function(aAsciiStream, aBinaryStream) {
|
|||||||
*/
|
*/
|
||||||
this.getObj = function() {
|
this.getObj = function() {
|
||||||
var obj = nextInStack();
|
var obj = nextInStack();
|
||||||
|
|
||||||
if (operandIsArray && !IsCmd(obj, "{") && !IsCmd(obj, "[") &&
|
if (operandIsArray && !IsCmd(obj, "{") && !IsCmd(obj, "[") &&
|
||||||
!IsCmd(obj, "}") && !IsCmd(obj, "]")) {
|
!IsCmd(obj, "]") && !IsCmd(obj, "}")) {
|
||||||
dump("Adding: " + obj);
|
dump("Adding an object: " + obj +" to array " + operandIsArray);
|
||||||
operandStack.peek().push(obj);
|
var currentArray = operandStack.peek();
|
||||||
this.getObj();
|
for (var i = 1; i < operandIsArray; i++)
|
||||||
} else if (IsCmd(obj, "{") || IsCmd(obj, "[")) {
|
currentArray = currentArray[currentArray.length - 1];
|
||||||
dump("Start" + (obj.cmd == "{" ? " Executable " : " ") + "Array");
|
|
||||||
operandIsArray ? operandStack.peek().push([]) : operandStack.push([]);
|
|
||||||
operandIsArray++;
|
|
||||||
this.getObj();
|
|
||||||
} else if (IsCmd(obj, "}") || IsCmd(obj, "]")) {
|
|
||||||
dump("End" + (obj.cmd == "}" ? " Executable " : " ") + "Array");
|
|
||||||
operandIsArray--;
|
|
||||||
this.getObj();
|
|
||||||
} else if (IsCmd(obj, "if")) {
|
|
||||||
log("if");
|
|
||||||
var procedure = operandStack.pop();
|
|
||||||
var bool = operandStack.pop();
|
|
||||||
if (!IsBool(bool)) {
|
|
||||||
executionStack.push(bool);
|
|
||||||
log(".....");
|
|
||||||
this.getObj();
|
|
||||||
}
|
|
||||||
log(bool);
|
|
||||||
if (bool)
|
|
||||||
executionStack.push(procedure);
|
|
||||||
|
|
||||||
this.getObj();
|
currentArray.push(obj);
|
||||||
} else if (IsCmd(obj, "ifelse")) {
|
return parseNext();
|
||||||
log("ifelse");
|
|
||||||
var procedure1 = operandStack.pop();
|
|
||||||
var procedure2 = operandStack.pop();
|
|
||||||
var bool = !!operandStack.pop();
|
|
||||||
operandStack.push(bool ? procedure1 : procedure2);
|
|
||||||
this.getObj();
|
|
||||||
} else if (IsBool(obj) || IsInt(obj) || IsNum(obj) || IsString(obj)) {
|
} else if (IsBool(obj) || IsInt(obj) || IsNum(obj) || IsString(obj)) {
|
||||||
dump("Value: " + obj);
|
dump("Value: " + obj);
|
||||||
operandStack.push(obj);
|
operandStack.push(obj);
|
||||||
this.getObj();
|
return parseNext();
|
||||||
} else if (IsCmd(obj, "dup")) {
|
} else if (IsName(obj)) {
|
||||||
dump("Duplicate");
|
dump("Name: " + obj.name);
|
||||||
operandStack.push(operandStack.peek());
|
operandStack.push(obj.name);
|
||||||
this.getObj();
|
return parseNext();
|
||||||
} else if (IsCmd(obj, "put") || IsCmd(obj, "NP")) {
|
} else if (IsCmd(obj)) {
|
||||||
operandStack.toString();
|
var command = obj.cmd;
|
||||||
|
dump(command);
|
||||||
|
|
||||||
|
switch (command) {
|
||||||
|
case "[":
|
||||||
|
case "{":
|
||||||
|
dump("Start" + (command == "{" ? " Executable " : " ") + "Array");
|
||||||
|
operandIsArray++;
|
||||||
|
var currentArray = operandStack;
|
||||||
|
for (var i = 1; i < operandIsArray; i++)
|
||||||
|
if (currentArray.peek)
|
||||||
|
currentArray = currentArray.peek();
|
||||||
|
else
|
||||||
|
currentArray = currentArray[currentArray.length - 1];
|
||||||
|
currentArray.push([]);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "]":
|
||||||
|
case "}":
|
||||||
|
var currentArray = operandStack.peek();
|
||||||
|
for (var i = 1; i < operandIsArray; i++)
|
||||||
|
currentArray = currentArray[currentArray.length - 1];
|
||||||
|
dump("End" + (command == "}" ? " Executable " : " ") + "Array: " + currentArray.join(" "));
|
||||||
|
operandIsArray--;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "if":
|
||||||
|
var procedure = operandStack.pop();
|
||||||
|
var bool = operandStack.pop();
|
||||||
|
if (!IsBool(bool)) {
|
||||||
|
dump("if: " + bool);
|
||||||
|
// we need to execute things, let be dirty
|
||||||
|
executionStack.push(bool);
|
||||||
|
} else {
|
||||||
|
dump("if ( " + bool + " ) { " + procedure + " }");
|
||||||
|
if (bool)
|
||||||
|
executionStack.push(procedure);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "ifelse":
|
||||||
|
var procedure1 = operandStack.pop();
|
||||||
|
var procedure2 = operandStack.pop();
|
||||||
|
var bool = !!operandStack.pop();
|
||||||
|
dump("if ( " + bool + " ) { " + procedure2 + " } else { " + procedure1 + " }");
|
||||||
|
executionStack.push(bool ? procedure2 : procedure1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "dup":
|
||||||
|
//log("duplicate: " + operandStack.peek());
|
||||||
|
operandStack.push(operandStack.peek());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "mark":
|
||||||
|
operandStack.push("mark");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "cleartomark":
|
||||||
|
var command = "";
|
||||||
|
do {
|
||||||
|
command = operandStack.pop();
|
||||||
|
} while (command != "mark");
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "put":
|
||||||
var data = operandStack.pop();
|
var data = operandStack.pop();
|
||||||
var indexOrKey = operandStack.pop();
|
var indexOrKey = operandStack.pop();
|
||||||
var object = operandStack.pop();
|
var object = operandStack.pop();
|
||||||
log(object);
|
//dump("put " + data + " in " + object + "[" + indexOrKey + "]");
|
||||||
log("put " + data + " in " + obj + "[" + indexOrKey + "]");
|
object.set ? object.set(indexOrKey, data)
|
||||||
|
: object[indexOrKey] = data;
|
||||||
|
|
||||||
if (object.set)
|
break;
|
||||||
object.set(indexOrKey, data);
|
|
||||||
else
|
case "pop":
|
||||||
object[indexOrKey] = data;
|
operandStack.pop();
|
||||||
this.getObj();
|
break;
|
||||||
} else if (IsCmd(obj, "currentdict")) {
|
|
||||||
dump("currentdict");
|
case "exch":
|
||||||
operandStack.push(dictionaryStack.peek());
|
var operand1 = operandStack.pop();
|
||||||
this.getObj();
|
var operand2 = operandStack.pop();
|
||||||
} else if (IsCmd(obj, "systemdict")) {
|
operandStack.push(operand1);
|
||||||
dump("systemdict");
|
operandStack.push(operand2);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "get":
|
||||||
|
var indexOrKey = operandStack.pop();
|
||||||
|
var object = operandStack.pop();
|
||||||
|
log("==============");
|
||||||
|
operandStack.toString();
|
||||||
|
log(dictionaryStack.__innerStack__);
|
||||||
|
log(object + "::" + indexOrKey);
|
||||||
|
var data = object.get ? object.get(indexOrKey) : object[indexOrKey];
|
||||||
|
dump("get " + obj + "[" + indexOrKey + "]: " + data);
|
||||||
|
operandStack.push(data);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "currentdict":
|
||||||
|
var dict = dictionaryStack.peek();
|
||||||
|
operandStack.push(dict);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "systemdict":
|
||||||
operandStack.push(systemDict);
|
operandStack.push(systemDict);
|
||||||
this.getObj();
|
break;
|
||||||
} else if (IsCmd(obj, "readonly") || IsCmd(obj, "executeonly") ||
|
|
||||||
IsCmd(obj, "noaccess") || IsCmd(obj, "currentfile")) {
|
case "readonly":
|
||||||
|
case "executeonly":
|
||||||
|
case "noaccess":
|
||||||
// Do nothing for the moment
|
// Do nothing for the moment
|
||||||
this.getObj();
|
break;
|
||||||
} else if (IsName(obj)) {
|
|
||||||
//dump("Name: " + obj.name);
|
case "currentfile":
|
||||||
operandStack.push(obj.name);
|
operandStack.push("currentfile");
|
||||||
this.getObj();
|
break;
|
||||||
} else if (IsCmd(obj, "array")) {
|
|
||||||
|
case "array":
|
||||||
var size = operandStack.pop();
|
var size = operandStack.pop();
|
||||||
var array = new Array(size);
|
var array = new Array(size);
|
||||||
operandStack.push(array);
|
operandStack.push(array);
|
||||||
this.getObj();
|
break;
|
||||||
} else if (IsCmd(obj, "dict")) {
|
|
||||||
dump("Dict: " + obj);
|
case "dict":
|
||||||
var size = operandStack.pop();
|
var size = operandStack.pop();
|
||||||
var dict = new Dict(size);
|
var dict = new Dict(size);
|
||||||
operandStack.push(dict);
|
operandStack.push(dict);
|
||||||
this.getObj();
|
break;
|
||||||
} else if (IsCmd(obj, "begin")) {
|
|
||||||
dump("begin a dictionary");
|
case "begin":
|
||||||
dictionaryStack.push(operandStack.pop());
|
dictionaryStack.push(operandStack.pop());
|
||||||
this.getObj();
|
break;
|
||||||
} else if (IsCmd(obj, "end")) {
|
|
||||||
dump("Ending a dictionary");
|
case "end":
|
||||||
dictionaryStack.pop();
|
dictionaryStack.pop();
|
||||||
this.getObj();
|
break;
|
||||||
} else if (IsCmd(obj, "def") || IsCmd(obj, "ND")) {
|
|
||||||
|
case "def":
|
||||||
var value = operandStack.pop();
|
var value = operandStack.pop();
|
||||||
var key = operandStack.pop();
|
var key = operandStack.pop();
|
||||||
log("def: " + key + " = " + value);
|
dump("def: " + key + " = " + value);
|
||||||
dictionaryStack.peek().set(key, value);
|
dictionaryStack.peek().set(key, value);
|
||||||
this.getObj();
|
break;
|
||||||
} else if (IsCmd(obj, "eexec")) {
|
|
||||||
|
case "definefont":
|
||||||
|
var font = operandStack.pop();
|
||||||
|
var key = operandStack.pop();
|
||||||
|
dump("definefont " + font + " with key: " + key);
|
||||||
|
Font.set(key, font);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "known":
|
||||||
|
var name = operandStack.pop();
|
||||||
|
var dict = operandStack.pop();
|
||||||
|
var data = !!dict.get(name);
|
||||||
|
dump("known: " + data + " :: " + name + " in dict: " + dict);
|
||||||
|
operandStack.push(data);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "exec":
|
||||||
|
executionStack.push(operandStack.pop());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "eexec":
|
||||||
// All the first segment data has been read, decrypt the second segment
|
// All the first segment data has been read, decrypt the second segment
|
||||||
// and start interpreting it in order to decode it
|
// and start interpreting it in order to decode it
|
||||||
var eexecString = decrypt(aBinaryStream, kEexecEncryptionKey, 4).join("");
|
var eexecString = decrypt(aBinaryStream, kEexecEncryptionKey, 4).join("");
|
||||||
lexer = new Lexer(new StringStream(eexecString));
|
lexer = new Lexer(new StringStream(eexecString));
|
||||||
|
break;
|
||||||
|
|
||||||
this.getObj();
|
case "LenIV":
|
||||||
} else if (IsCmd(obj, "known")) {
|
|
||||||
dump("known");
|
|
||||||
var name = operandStack.pop();
|
|
||||||
var dict = operandStack.pop();
|
|
||||||
operandStack.push(!!dict.get(name));
|
|
||||||
this.getObj();
|
|
||||||
} else if (IsCmd(obj, "RD")) {
|
|
||||||
dump("RD");
|
|
||||||
var size = operandStack.pop();
|
|
||||||
var key = operandStack.pop();
|
|
||||||
|
|
||||||
// Add '1' because of the space separator, this is dirty
|
|
||||||
var stream = lexer.stream.makeSubStream(lexer.stream.pos + 1, size);
|
|
||||||
lexer.stream.skip(size + 1);
|
|
||||||
|
|
||||||
var charString = decrypt(stream, kCharStringsEncryptionKey, 4).join("");
|
|
||||||
var charStream = new StringStream(charString);
|
|
||||||
|
|
||||||
// XXX do we want to store that on the top dictionary or somewhere else
|
|
||||||
dictionaryStack.peek().set(key, charStream);
|
|
||||||
|
|
||||||
var decodedCharString = decodeCharString(charStream);
|
|
||||||
dump(decodedCharString);
|
|
||||||
|
|
||||||
this.getObj();
|
|
||||||
} else if (IsCmd(obj, "LenIV")) {
|
|
||||||
error("LenIV: argh! we need to modify the length of discard characters for charStrings");
|
error("LenIV: argh! we need to modify the length of discard characters for charStrings");
|
||||||
} else if (IsCmd(obj, "closefile")) {
|
break;
|
||||||
// End of binary data;
|
|
||||||
} else if (IsCmd(obj, "index")) {
|
case "closefile":
|
||||||
|
var file = operandStack.pop();
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "index":
|
||||||
var operands = [];
|
var operands = [];
|
||||||
var size = operandStack.pop();
|
var size = operandStack.pop();
|
||||||
for (var i = 0; i < size; i++)
|
for (var i = 0; i < size; i++)
|
||||||
@ -421,34 +491,66 @@ var Type1Parser = function(aAsciiStream, aBinaryStream) {
|
|||||||
operandStack.push(operands.pop());
|
operandStack.push(operands.pop());
|
||||||
|
|
||||||
operandStack.push(newOperand);
|
operandStack.push(newOperand);
|
||||||
this.getObj();
|
break;
|
||||||
} else if (IsCmd(obj, "StandardEncoding")) {
|
|
||||||
|
case "string":
|
||||||
|
var size = operandStack.pop();
|
||||||
|
var str = (new Array(size + 1)).join(" ");
|
||||||
|
operandStack.push(str);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "readstring":
|
||||||
|
var str = operandStack.pop();
|
||||||
|
var size = str.length;
|
||||||
|
|
||||||
|
var file = operandStack.pop();
|
||||||
|
|
||||||
|
// Add '1' because of the space separator, this is dirty
|
||||||
|
var stream = lexer.stream.makeSubStream(lexer.stream.pos + 1, size);
|
||||||
|
lexer.stream.skip(size + 1);
|
||||||
|
|
||||||
|
var charString = decrypt(stream, kCharStringsEncryptionKey, 4).join("");
|
||||||
|
var charStream = new StringStream(charString);
|
||||||
|
var decodedCharString = decodeCharString(charStream);
|
||||||
|
dump("decodedCharString: " + decodedCharString);
|
||||||
|
operandStack.push(decodedCharString);
|
||||||
|
// boolean indicating if the operation is a success or not
|
||||||
|
operandStack.push(true);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case "StandardEncoding":
|
||||||
// For some reason the value is considered as a command, maybe it is
|
// For some reason the value is considered as a command, maybe it is
|
||||||
// because of the uppercae 'S'
|
// because of the uppercase 'S'
|
||||||
operandStack.push(obj.cmd);
|
operandStack.push(obj.cmd);
|
||||||
this.getObj();
|
break;
|
||||||
} else {
|
|
||||||
|
default:
|
||||||
var command = null;
|
var command = null;
|
||||||
if (IsCmd(obj)) {
|
if (IsCmd(obj)) {
|
||||||
for (var i = 0; i < dictionaryStack.length; i++) {
|
for (var i = 0; i < dictionaryStack.length; i++) {
|
||||||
command = dictionaryStack.get(i).get(obj.cmd);
|
if (command = dictionaryStack.get(i).get(obj.cmd)) {
|
||||||
if (command)
|
dump("found in dictionnary for " + obj.cmd + " command: " + command);
|
||||||
|
executionStack.push(command.slice());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (command) {
|
if (!command) {
|
||||||
// XXX add the command to the execution stack
|
|
||||||
this.getObj();
|
|
||||||
} else {
|
|
||||||
log("operandStack: " + operandStack);
|
log("operandStack: " + operandStack);
|
||||||
log("dictionaryStack: " + dictionaryStack);
|
log("dictionaryStack: " + dictionaryStack);
|
||||||
dump(obj);
|
log(obj);
|
||||||
error("Unknow token while parsing font");
|
error("Unknow command while parsing font");
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return operandStack.peek();
|
return parseNext();
|
||||||
|
} else if (obj){
|
||||||
|
log (obj);
|
||||||
|
operandStack.push(obj);
|
||||||
|
return parseNext();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -467,8 +569,7 @@ var Type1Font = function(aFontName, aFontFile) {
|
|||||||
|
|
||||||
this.parser = new Type1Parser(ASCIIStream, binaryStream);
|
this.parser = new Type1Parser(ASCIIStream, binaryStream);
|
||||||
|
|
||||||
var fontDictionary = this.parser.getObj();
|
this.parser.getObj();
|
||||||
log(fontDictionary + "\t" + "fontInfo: " + fontDictionary.get("FontInfo"));
|
|
||||||
hack = true;
|
hack = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user