get rid of Obj

This commit is contained in:
Andreas Gal 2011-05-06 17:27:27 -07:00
parent 4349a5b2bc
commit 36f657b4ad

353
pdf.js
View File

@ -1,25 +1,6 @@
/* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- / /* -*- Mode: Java; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- /
/* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */ /* vim: set shiftwidth=4 tabstop=8 autoindent cindent expandtab: */
var HashMap = (function() {
function constructor() {
}
constructor.prototype = {
get: function(key) {
return this["$" + key];
},
set: function(key, value) {
this["$" + key] = value;
},
contains: function(key) {
return ("$" + key) in this;
}
};
return constructor;
})();
var Stream = (function() { var Stream = (function() {
function constructor(arrayBuffer) { function constructor(arrayBuffer) {
this.bytes = Uint8Array(arrayBuffer); this.bytes = Uint8Array(arrayBuffer);
@ -77,64 +58,121 @@ var Stream = (function() {
return constructor; return constructor;
})(); })();
var Obj = (function() { var Name = (function() {
function constructor(type, value) { function constructor(name) {
this.type = type; this.name = name;
this.value = value;
} }
constructor.prototype = { constructor.prototype = {
}; };
var types = [ return constructor;
"Bool", "Int", "Real", "String", "Name", "Null", })();
"Array", "Dict", "Stream", "Ref",
"Cmd", "Error", "EOF", "None"
];
for (var i = 0; i < types.length; ++i) { var Cmd = (function() {
var typeName = types[i]; function constructor(cmd) {
constructor[typeName] = i; this.cmd = cmd;
constructor.prototype["is" + typeName] =
(function is(value) {
return this.type == is.type &&
(typeof value == "undefined" || value == this.value);
});
constructor.prototype["is" + typeName].type = i;
} }
constructor.prototype.isNum = function(value) { constructor.prototype = {
return this.isInt(value) || this.isReal(value); };
}
constructor.prototype.lookup = function(key) {
function lookup(key) {
if (!(this.value.contains(key)))
return Obj.nullObj;
return this.value.get(key);
}
}
constructor.prototype.lowerToJS = function() {
if (this.isInt() || this.isReal() || this.isString()
|| this.isName() || this.isBool() || this.isNone()) {
return this.value;
} else if (this.isNull()) {
return null;
} else if (this.isArray()) {
return this.value.map(function (e) { return e.lowerToJS(); });
} else {
return undefined;
}
}
constructor.trueObj = new constructor(constructor.Bool, true);
constructor.falseObj = new constructor(constructor.Bool, false);
constructor.nullObj = new constructor(constructor.Null);
constructor.errorObj = new constructor(constructor.Error);
constructor.eofObj = new constructor(constructor.EOF);
return constructor; return constructor;
})(); })();
var Dict = (function() {
function constructor() {
}
constructor.prototype = {
get: function(key) {
return this["$" + key];
},
set: function(key, value) {
this["$" + key] = value;
},
contains: function(key) {
return ("$" + key) in this;
}
};
return constructor;
})();
var Ref = (function() {
function constructor(num, ref) {
this.num = num;
this.ref = ref;
}
constructor.prototype = {
};
return constructor;
})();
function IsBool(v) {
return typeof v == "boolean";
}
function IsInt(v) {
return typeof v == "number" && ((v|0) == v);
}
function IsNum(v) {
return typeof v == "number";
}
function IsString(v) {
return typeof v == "string";
}
function IsNull(v) {
return v == null;
}
function IsName(v) {
return v instanceof Name;
}
function IsCmd(v, cmd) {
return v instanceof Cmd && (!cmd || v.cmd == cmd);
}
function IsDict(v) {
return v instanceof Dict;
}
function IsArray(v) {
return v instanceof Array;
}
function IsStream(v) {
return v instanceof Stream;
}
function IsRef(v) {
return v instanceof Ref;
}
var EOF = {};
function IsEOF(v) {
return v == EOF;
}
var Error = {};
function IsError(v) {
return v == Error;
}
var None = {};
function IsNone(v) {
return v == None;
}
var Lexer = (function() { var Lexer = (function() {
function constructor(stream) { function constructor(stream) {
this.stream = stream; this.stream = stream;
@ -205,18 +243,8 @@ var Lexer = (function() {
} while (true); } while (true);
var value = parseFloat(str); var value = parseFloat(str);
if (isNaN(value)) if (isNaN(value))
return Obj.errorObj; return Error;
if (floating) { return value;
type = Obj.Floating;
} else {
if (value >= MIN_INT && value <= MAX_INT)
type = Obj.Int;
else if (value >= MAX_UINT && value <= MAX_UINT)
type = Obj.Uint;
else
return Obj.errorObj;
}
return new Obj(type, value);
}, },
getString: function(ch) { getString: function(ch) {
var n = 0; var n = 0;
@ -300,8 +328,8 @@ var Lexer = (function() {
} }
} while (!done); } while (!done);
if (!str.length) if (!str.length)
return new Obj(Obj.EOF); return EOF;
return new Obj(Obj.String, str); return str;
}, },
getName: function(ch) { getName: function(ch) {
var str = ""; var str = "";
@ -327,7 +355,7 @@ var Lexer = (function() {
} }
if (str.length > 128) if (str.length > 128)
this.error("Warning: name token is longer than allowed by the specification"); this.error("Warning: name token is longer than allowed by the specification");
return new Obj(Obj.Name, str); return new Name(str);
}, },
getHexString: function(ch) { getHexString: function(ch) {
var str = ""; var str = "";
@ -349,7 +377,7 @@ var Lexer = (function() {
str += String.fromCharCode((x << 4) | x2); str += String.fromCharCode((x << 4) | x2);
} }
} }
return new Obj(Obj.String, str); return str;
}, },
getObj: function() { getObj: function() {
// skip whitespace and comments // skip whitespace and comments
@ -358,7 +386,7 @@ var Lexer = (function() {
var ch; var ch;
while (true) { while (true) {
if (!(ch = stream.getChar())) if (!(ch = stream.getChar()))
return new Obj(Object.EOF); return EOF;
if (comment) { if (comment) {
if (ch == '\r' || ch == '\n') if (ch == '\r' || ch == '\n')
comment = false; comment = false;
@ -382,14 +410,14 @@ var Lexer = (function() {
// array punctuation // array punctuation
case '[': case '[':
case ']': case ']':
return new Obj(Obj.Cmd, ch); return new Cmd(ch);
// hex string or dict punctuation // hex string or dict punctuation
case '<': case '<':
ch = stream.lookChar(); ch = stream.lookChar();
if (ch == '<') { if (ch == '<') {
// dict punctuation // dict punctuation
stream.getChar(); stream.getChar();
return new Obj(Obj.Cmd, ch); return new Cmd(ch);
} }
return this.getHexString(ch); return this.getHexString(ch);
// dict punctuation // dict punctuation
@ -397,14 +425,14 @@ var Lexer = (function() {
ch = stream.lookChar(); ch = stream.lookChar();
if (ch == '>') { if (ch == '>') {
stream.getChar(); stream.getChar();
return new Obj(Obj.Cmd, ch); return new Cmd(ch);
} }
// fall through // fall through
case ')': case ')':
case '{': case '{':
case '}': case '}':
this.error("Illegal character"); this.error("Illegal character");
return Obj.errorObj; return Error;
} }
// command // command
@ -418,12 +446,12 @@ var Lexer = (function() {
str += ch; str += ch;
} }
if (str == "true") if (str == "true")
return Obj.trueObj; return true;
if (str == "false") if (str == "false")
return Obj.falseObj; return false;
if (str == "null") if (str == "null")
return Obj.nullObj; return null;
return new Obj(Obj.Cmd, str); return new Cmd(str);
} }
}; };
@ -452,68 +480,68 @@ var Parser = (function() {
// of a dictionary, we need to reset // of a dictionary, we need to reset
this.inlineImg = 0; this.inlineImg = 0;
} }
} else if (this.buf2.isCmd("ID")) { } else if (IsCmd(this.buf2, "ID")) {
this.lexer.skipChar(); // skip char after 'ID' command this.lexer.skipChar(); // skip char after 'ID' command
this.inlineImg = 1; this.inlineImg = 1;
} }
this.buf1 = this.buf2; this.buf1 = this.buf2;
// don't buffer inline image data // don't buffer inline image data
this.buf2 = (this.inlineImg > 0) ? Obj.nullObj : this.lexer.getObj(); this.buf2 = (this.inlineImg > 0) ? null : this.lexer.getObj();
}, },
getObj: function() { getObj: function() {
// refill buffer after inline image data // refill buffer after inline image data
if (this.inlineImg == 2) if (this.inlineImg == 2)
this.refill(); this.refill();
if (this.buf1.isCmd("[")) { // array if (IsCmd(this.buf1, "[")) { // array
var obj = new Obj(Obj.Array, []); var array = [];
while (!this.buf1.isCmd("]") && !this.buf1.isEOF()) while (!IsCmd(this.buf1, "]") && !IsEOF(this.buf1))
obj.value.push(this.getObj()); array.push(this.getObj());
if (this.buf1.isEOF()) if (IsEOF(this.buf1))
this.error("End of file inside array"); this.error("End of file inside array");
this.shift(); this.shift();
return obj; return array;
} else if (this.buf1.isCmd("<<")) { // dictionary or stream } else if (IsCmd(this.buf1, "<<")) { // dictionary or stream
this.shift(); this.shift();
var obj = new Obj(Obj.Dict, new HashMap()); var dict = new Dict();
while (!this.buf1.isCmd(">>") && !this.buf1.isEOF()) { while (!IsCmd(this.buf1, ">>") && !IsEOF(this.buf1)) {
if (!this.buf1.isName()) { if (!IsName(this.buf1)) {
error("Dictionary key must be a name object"); error("Dictionary key must be a name object");
shift(); shift();
} else { } else {
var key = buf1.value; var key = buf1;
this.shift(); this.shift();
if (this.buf1.isEOF() || this.buf1.isError()) if (IsEOF(this.buf1) || IsError(this.buf1))
break; break;
obj.value.set(key, this.getObj()); dict.set(key, this.getObj());
} }
} }
if (this.buf1.isEOF()) if (IsEOF(this.buf1))
error("End of file inside dictionary"); error("End of file inside dictionary");
// stream objects are not allowed inside content streams or // stream objects are not allowed inside content streams or
// object streams // object streams
if (this.allowStreams && this.buf2.isCmd("stream")) { if (this.allowStreams && IsCmd(this.buf2, "stream")) {
return this.makeStream(); return this.makeStream();
} else { } else {
this.shift(); this.shift();
} }
return obj; return dict;
} else if (this.buf1.isInt()) { // indirect reference or integer } else if (IsInt(this.buf1)) { // indirect reference or integer
var num = this.buf1.value; var num = this.buf1;
this.shift(); this.shift();
if (this.buf1.isInt() && this.buf2.isCmd("R")) { if (IsInt(this.buf1) && IsCmd(this.buf2, "R")) {
var obj = new Obj(Obj.Ref, [num, this.buf1.value]); var ref = new Ref(num, this.buf1);
this.shift(); this.shift();
this.shift(); this.shift();
return obj; return ref;
} }
return new Obj(Obj.Int, num); return num;
} else if (this.buf1.isString()) { // string } else if (IsString(this.buf1)) { // string
var obj = this.decrypt(this.buf1); var str = this.decrypt(this.buf1);
this.shift(); this.shift();
return obj; return str;
} }
// simple object // simple object
@ -527,7 +555,7 @@ var Parser = (function() {
}, },
makeStream: function() { makeStream: function() {
// TODO // TODO
return new Obj(Obj.Error); return Error;
} }
}; };
@ -541,10 +569,10 @@ var Linearization = (function () {
var obj2 = this.parser.getObj(); var obj2 = this.parser.getObj();
var obj3 = this.parser.getObj(); var obj3 = this.parser.getObj();
this.linDict = this.parser.getObj(); this.linDict = this.parser.getObj();
if (obj1.isInt() && obj2.isInt() && obj3.isCmd("obj") && this.linDict.isDict()) { if (IsInt(obj1) && IsInt(obj2) && IsCmd(obj3, "obj") && IsDict(this.linDict)) {
var obj = this.linDict.lookup("Linearized"); var obj = this.linDict.lookup("Linearized");
if (!(obj.isNum() && obj.value > 0)) if (!(IsNum(obj) && obj > 0))
this.linDict = Obj.nullObj; this.linDict = null;
} }
} }
@ -552,9 +580,9 @@ var Linearization = (function () {
getInt: function(name) { getInt: function(name) {
var linDict = this.linDict; var linDict = this.linDict;
var obj; var obj;
if (linDict.isDict() && if (IsDict(linDict) &&
(obj = linDict.lookup(name)).isInt() && IsInt(obj = linDict.lookup(name)) &&
obj.value > 0) { obj > 0) {
return length; return length;
} }
error("'" + name + "' field in linearization table is invalid"); error("'" + name + "' field in linearization table is invalid");
@ -563,18 +591,18 @@ var Linearization = (function () {
getHint: function(index) { getHint: function(index) {
var linDict = this.linDict; var linDict = this.linDict;
var obj1, obj2; var obj1, obj2;
if (linDict.isDict() && if (IsDict(linDict) &&
(obj1 = linDict.lookup("H")).isArray() && IsArray(obj1 = linDict.lookup("H")) &&
obj1.value.length >= 2 && obj1.length >= 2 &&
(obj2 = obj1.value[index]).isInt() && IsInt(obj2 = obj1[index]) &&
obj2.value > 0) { obj2 > 0) {
return obj2.value; return obj2;
} }
this.error("Hints table in linearization table is invalid"); this.error("Hints table in linearization table is invalid");
return 0; return 0;
}, },
get length() { get length() {
if (!this.linDict.isDict()) if (!IsDict(this.linDict))
return 0; return 0;
return this.getInt("L"); return this.getInt("L");
}, },
@ -620,28 +648,28 @@ var XRef = (function () {
readXRefTable: function(parser) { readXRefTable: function(parser) {
while (true) { while (true) {
var obj; var obj;
if ((obj = parser.getObj()).isCmd("trailer")) if (IsCmd(obj = parser.getObj(), "trailer"))
break; break;
if (!obj.isInt()) if (!IsInt(obj))
return false; return false;
var first = obj.value; var first = obj;
if (!(obj = parser.getObj()).isInt()) if (!IsInt(obj = parser.getObj()))
return false; return false;
var n = obj.value; var n = obj;
if (first < 0 || n < 0 || (first + n) != ((first + n) | 0)) if (first < 0 || n < 0 || (first + n) != ((first + n) | 0))
return false; return false;
for (var i = first; i < first + n; ++i) { for (var i = first; i < first + n; ++i) {
var entry = {}; var entry = {};
if (!(obj = parser.getObj()).isInt()) if (!IsInt(obj = parser.getObj()))
return false; return false;
entry.offset = obj.value; entry.offset = obj;
if (!(obj = parser.getObj()).isInt()) if (!IsInt(obj = parser.getObj()))
return false; return false;
entry.gen = obj.value; entry.gen = obj;
obj = parser.getObj(); obj = parser.getObj();
if (obj.isCmd("n")) { if (IsCmd(obj, "n")) {
entry.uncompressed = true; entry.uncompressed = true;
} else if (obj.isCmd("f")) { } else if (IsCmd(obj, "f")) {
entry.free = true; entry.free = true;
} else { } else {
return false; return false;
@ -671,16 +699,16 @@ var XRef = (function () {
var parser = new Parser(new Lexer(stream), false); var parser = new Parser(new Lexer(stream), false);
var obj = parser.getObj(); var obj = parser.getObj();
// parse an old-style xref table // parse an old-style xref table
if (obj.isCmd("xref")) if (IsCmd(obj, "xref"))
return this.readXRefTable(parser); return this.readXRefTable(parser);
// parse an xref stream // parse an xref stream
if (obj.isInt()) { if (IsInt(obj)) {
if (!parser.getObj().isInt() || if (!IsInt(parser.getObj()) ||
!parser.getObj().isCmd("obj") || !IsCmd(parser.getObj(), "obj") ||
!(obj = parser.getObj()).isStream()) { !IsStream(obj = parser.getObj())) {
return false; return false;
} }
return this.readXRefStream(obj.value); return this.readXRefStream(obj);
} }
return false; return false;
} }
@ -895,23 +923,22 @@ var Interpreter = (function() {
var args = [ ]; var args = [ ];
var obj; var obj;
while (!((obj = parser.getObj()).isEOF())) { while (!IsEOF(obj = parser.getObj())) {
if (obj.isCmd()) { if (IsCmd(obj)) {
var cmd = obj.value; var cmd = obj.cmd;
if (!(cmd in CMD_TABLE)) if (!(cmd in CMD_TABLE))
this.error("Unknown command '"+ cmd +"'"); this.error("Unknown command '"+ cmd +"'");
var op = CMD_TABLE[cmd]; var op = CMD_TABLE[cmd];
if (!this.typeCheck(op.params, args)) //if (!this.typeCheck(op.params, args))
this.error("Wrong arguments for command '"+ cmd +"'"); //this.error("Wrong arguments for command '"+ cmd +"'");
op.op.call(this, op.op.call(this, args);
args.map(function (a) { return a.lowerToJS() }));
args.length = 0; args.length = 0;
} else if (MAX_ARGS == args.length) { } else if (MAX_ARGS == args.length) {
this.error("Too many arguments"); this.error("Too many arguments");
} else { } else {
args.push(obj); args.push(IsName(obj) ? obj.name : obj);
} }
} }
@ -1215,13 +1242,13 @@ var MockParser = (function() {
return constructor; return constructor;
})(); })();
function cmd(c) { return new Obj(Obj.Cmd, c); } function cmd(c) { return new Cmd(c); }
function name(n) { return new Obj(Obj.Name, n); } function name(n) { return new Name(n); }
function int(i) { return new Obj(Obj.Int, i); } function int(i) { return i; }
function string(s) { return new Obj(Obj.String, s); } function string(s) { return s; }
function eof() { return Obj.eofObj; } function eof() { return EOF; }
function array(a) { return new Obj(Obj.Array, a); } function array(a) { return a; }
function real(r) { return new Obj(Obj.Real, r); } function real(r) { return r; }
var tests = [ var tests = [
{ name: "Hello world", { name: "Hello world",