Add the necessary bits to handle Arrays, Procedure and a bigger set of instructions for Type1

This commit is contained in:
Vivien Nicolas 2011-06-03 17:48:32 +02:00
parent e739bcc515
commit e302bd93ee

View File

@ -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;
} }