Starts to decode type2 charStrings
This commit is contained in:
parent
c098f0b31f
commit
87b4cb85be
232
PDFFont.js
232
PDFFont.js
@ -660,80 +660,19 @@ var Type1Font = function(aFontName, aFontFile) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var hack = false;
|
|
||||||
Type1Font.prototype = {
|
|
||||||
convert: function() {
|
|
||||||
var fontName = "TACTGM+NimbusRomNo9L-Medi";
|
|
||||||
var fontData = null;
|
|
||||||
for (var font in Fonts.map) {
|
|
||||||
if (font == fontName) {
|
|
||||||
fontData = Fonts.get(font);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fontData || hack)
|
|
||||||
return;
|
|
||||||
hack = true;
|
|
||||||
|
|
||||||
var t1Only = [
|
/**************************************************************************/
|
||||||
"callothersubr",
|
|
||||||
"closepath",
|
|
||||||
"dotsection",
|
|
||||||
"hsbw",
|
|
||||||
"hstem3",
|
|
||||||
"pop",
|
|
||||||
"sbw",
|
|
||||||
"seac",
|
|
||||||
"setcurrentpoint",
|
|
||||||
"vstem3"
|
|
||||||
];
|
|
||||||
|
|
||||||
/*
|
function decodeType2DictData(aString, aDictionary, aHack) {
|
||||||
* The sequence and form of a Type 2 charstring program may be
|
|
||||||
* represented as:
|
|
||||||
* w? {hs* vs* cm* hm* mt subpath}? {mt subpath}* endchar
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
var t2CharStrings = new Dict();
|
|
||||||
|
|
||||||
var t1CharStrings = fontData.get("CharStrings");
|
|
||||||
for (var key in t1CharStrings.map) {
|
|
||||||
var font = t1CharStrings.get(key);
|
|
||||||
var t2font = [];
|
|
||||||
|
|
||||||
for (var i = 0; i < font.length; i++) {
|
|
||||||
var token = font[i];
|
|
||||||
switch (token) {
|
|
||||||
case "hsbw":
|
|
||||||
var width = t2font.pop();
|
|
||||||
var leftSidebearingPoint = t2font.pop();
|
|
||||||
font.push(width);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (t1Only.indexOf(token) != -1) {
|
|
||||||
log(token + " need convert!\n");
|
|
||||||
throw new Error("Type1 Only token");
|
|
||||||
}
|
|
||||||
t2font.push(token);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log(key + "::" + t1CharStrings.get(key));
|
|
||||||
log("type2::" + t2font);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function decodeType2DictData(aString, aDictionary) {
|
|
||||||
var data = [];
|
var data = [];
|
||||||
|
|
||||||
var value = "";
|
var value = "";
|
||||||
var count = aString.length;
|
var count = aString.length;
|
||||||
for (var i = 0; i < count; i) {
|
for (var i = 0; i < count; i) {
|
||||||
value = aString[i++];
|
value = aString[i++];
|
||||||
|
if (value <= 0) {
|
||||||
if (value < 0) {
|
data.push(value);
|
||||||
continue;
|
continue;
|
||||||
} else if (value == 28) {
|
} else if (value == 28) {
|
||||||
value = aString[i++] << 8 | aString[i++];
|
value = aString[i++] << 8 | aString[i++];
|
||||||
@ -743,11 +682,15 @@ function decodeType2DictData(aString, aDictionary) {
|
|||||||
aString[i++] << 8 |
|
aString[i++] << 8 |
|
||||||
aString[i++];
|
aString[i++];
|
||||||
} else if (value < 32) {
|
} else if (value < 32) {
|
||||||
|
var oldValue = value;
|
||||||
if (value == 12) {
|
if (value == 12) {
|
||||||
value = aDictionary["12"][aString[i++]];
|
value = aDictionary["12"][aString[i++]];
|
||||||
} else {
|
} else {
|
||||||
value = aDictionary[value];
|
value = aDictionary[value];
|
||||||
}
|
}
|
||||||
|
if (!value)
|
||||||
|
throw new Error("This command number does not match anything : " + oldValue);
|
||||||
|
value = aHack ? value.name : value;
|
||||||
} else if (value <= 246) {
|
} else if (value <= 246) {
|
||||||
value = parseInt(value) - 139;
|
value = parseInt(value) - 139;
|
||||||
} else if (value <= 250) {
|
} else if (value <= 250) {
|
||||||
@ -776,15 +719,30 @@ var Type2Parser = function(aFilePath) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
function readIndex(aStream, aIsByte) {
|
function readIndex(aStream, aIsByte) {
|
||||||
var count = aStream.getByte() + aStream.getByte();
|
var count = aStream.getByte() << 8 | aStream.getByte();
|
||||||
var offsize = aStream.getByte();
|
var offsize = aStream.getByte();
|
||||||
var offsets = [];
|
var offsets = [];
|
||||||
for (var i = 0; i < count + 1; i++) {
|
for (var i = 0; i < count + 1; i++) {
|
||||||
var offset = 0;
|
switch (offsize) {
|
||||||
for (var j = 0; j < offsize; j++) {
|
case 0:
|
||||||
// XXX need to do some better code here
|
offset = 0;
|
||||||
var byte = aStream.getByte();
|
break;
|
||||||
offset += byte;
|
case 1:
|
||||||
|
offset = aStream.getByte();
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
offset = aStream.getByte() << 8 | aStream.getByte();
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
offset = aStream.getByte() << 16 | aStream.getByte() << 8 |
|
||||||
|
aStream.getByte();
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
offset = aStream.getByte() << 24 | aStream.getByte() << 16 |
|
||||||
|
aStream.getByte() << 8 | aStream.getByte();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Error("Unsupported offsize: " + offsize);
|
||||||
}
|
}
|
||||||
offsets.push(offset);
|
offsets.push(offset);
|
||||||
}
|
}
|
||||||
@ -800,58 +758,73 @@ var Type2Parser = function(aFilePath) {
|
|||||||
var length = offsets[i + 1] - 1;
|
var length = offsets[i + 1] - 1;
|
||||||
for (var j = offset - 1; j < length; j++)
|
for (var j = offset - 1; j < length; j++)
|
||||||
data.push(aIsByte ? aStream.getByte() : aStream.getChar());
|
data.push(aIsByte ? aStream.getByte() : aStream.getChar());
|
||||||
dump("object at offset " + offset + " is: " + data);
|
//dump("object at offset " + offset + " is: " + data);
|
||||||
objects.push(data);
|
objects.push(data);
|
||||||
}
|
}
|
||||||
return objects;
|
return objects;
|
||||||
};
|
};
|
||||||
|
|
||||||
function parseAsToken(aArray) {
|
function parseAsToken(aString, aDict) {
|
||||||
var objects = [];
|
var decoded = decodeType2DictData(aString, aDict);
|
||||||
|
|
||||||
var count = aArray.length;
|
var stack = [];
|
||||||
|
var count = decoded.length;
|
||||||
for (var i = 0; i < count; i++) {
|
for (var i = 0; i < count; i++) {
|
||||||
var decoded = decodeType2DictData(aArray[i], CFFDictOps);
|
var token = decoded[i];
|
||||||
|
if (IsNum(token)) {
|
||||||
var stack = [];
|
stack.push(token);
|
||||||
var count = decoded.length;
|
} else {
|
||||||
for (var i = 0; i < count; i++) {
|
switch (token.operand) {
|
||||||
var token = decoded[i];
|
case "SID":
|
||||||
if (IsNum(token)) {
|
font.set(token.name, CFFStrings[stack.pop()]);
|
||||||
stack.push(token);
|
break;
|
||||||
} else {
|
case "number number":
|
||||||
switch (token.operand) {
|
font.set(token.name, {
|
||||||
case "SID":
|
size: stack.pop(),
|
||||||
font.set(token.name, CFFStrings[stack.pop()]);
|
offset: stack.pop()
|
||||||
break;
|
});
|
||||||
case "number number":
|
break;
|
||||||
font.set(token.name, {
|
case "boolean":
|
||||||
size: stack.pop(),
|
font.set(token.name, stack.pop());
|
||||||
offset: stack.pop()
|
break;
|
||||||
});
|
case "delta":
|
||||||
break;
|
font.set(token.name, stack.pop());
|
||||||
case "boolean":
|
break;
|
||||||
|
default:
|
||||||
|
if (token.operand && token.operand.length) {
|
||||||
|
var array = [];
|
||||||
|
for (var j = 0; j < token.operand.length; j++)
|
||||||
|
array.push(stack.pop());
|
||||||
|
font.set(token.name, array);
|
||||||
|
} else {
|
||||||
font.set(token.name, stack.pop());
|
font.set(token.name, stack.pop());
|
||||||
break;
|
}
|
||||||
case "delta":
|
break;
|
||||||
font.set(token.name, stack.pop());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
if (token.operand && token.operand.length) {
|
|
||||||
var array = [];
|
|
||||||
for (var j = 0; j < token.operand.length; j++)
|
|
||||||
array.push(stack.pop());
|
|
||||||
font.set(token.name, array);
|
|
||||||
} else {
|
|
||||||
font.set(token.name, stack.pop());
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return objects;
|
|
||||||
|
function readCharset(aStream, aCharStrings) {
|
||||||
|
var charset = {};
|
||||||
|
|
||||||
|
var format = aStream.getByte();
|
||||||
|
if (format == 0) {
|
||||||
|
var count = aCharStrings.length - 1;
|
||||||
|
charset[".notdef"] = decodeType2DictData(aCharStrings[0], CFFDictCommands, true);
|
||||||
|
for (var i = 1; i < count + 1; i++) {
|
||||||
|
var sid = aStream.getByte() << 8 | aStream.getByte();
|
||||||
|
var charString = decodeType2DictData(aCharStrings[i], CFFDictCommands, true);
|
||||||
|
charset[CFFStrings[sid]] = charString;
|
||||||
|
log(CFFStrings[sid] + "::" + charString);
|
||||||
|
}
|
||||||
|
} else if (format == 1) {
|
||||||
|
throw new Error("Format 1 charset are not supported");
|
||||||
|
} else {
|
||||||
|
throw new Error("Invalid charset format");
|
||||||
|
}
|
||||||
|
return charset;
|
||||||
};
|
};
|
||||||
|
|
||||||
this.parse = function(aStream) {
|
this.parse = function(aStream) {
|
||||||
@ -881,15 +854,44 @@ var Type2Parser = function(aFilePath) {
|
|||||||
CFFStrings.push(strings[i].join(""));
|
CFFStrings.push(strings[i].join(""));
|
||||||
|
|
||||||
// Parse the TopDict operator
|
// Parse the TopDict operator
|
||||||
parseAsToken(topDict);
|
var objects = [];
|
||||||
|
var count = topDict.length;
|
||||||
|
for (var i = 0; i < count; i++)
|
||||||
|
parseAsToken(topDict[i], CFFDictOps);
|
||||||
|
|
||||||
for (var p in font.map) {
|
for (var p in font.map)
|
||||||
log(p + "::" + font.get(p));
|
dump(p + "::" + font.get(p));
|
||||||
|
|
||||||
|
// Read the Subr Index
|
||||||
|
dump("Reading Subr Index");
|
||||||
|
var subrs = readIndex(aStream);
|
||||||
|
|
||||||
|
// Read CharStrings Index
|
||||||
|
dump("Read CharStrings Index");
|
||||||
|
var charStringsOffset = font.get("CharStrings");
|
||||||
|
aStream.pos = charStringsOffset;
|
||||||
|
var charStrings = readIndex(aStream, true);
|
||||||
|
|
||||||
|
|
||||||
|
var charsetEntry = font.get("charset");
|
||||||
|
if (charsetEntry == 0) {
|
||||||
|
throw new Error("Need to support CFFISOAdobeCharset");
|
||||||
|
} else if (charsetEntry == 1) {
|
||||||
|
throw new Error("Need to support CFFExpert");
|
||||||
|
} else if (charsetEntry == 2) {
|
||||||
|
throw new Error("Need to support CFFExpertSubsetCharset");
|
||||||
|
} else {
|
||||||
|
aStream.pos = charsetEntry;
|
||||||
|
var charset = readCharset(aStream, charStrings);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Read Encoding data
|
||||||
|
log("Reading encoding data");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//
|
|
||||||
|
// XXX
|
||||||
var xhr = new XMLHttpRequest();
|
var xhr = new XMLHttpRequest();
|
||||||
xhr.open("GET", "titi.cff", false);
|
xhr.open("GET", "titi.cff", false);
|
||||||
xhr.mozResponseType = xhr.responseType = "arraybuffer";
|
xhr.mozResponseType = xhr.responseType = "arraybuffer";
|
||||||
|
@ -65,7 +65,7 @@ var CFFStrings = [
|
|||||||
"asciicircum",
|
"asciicircum",
|
||||||
"underscore",
|
"underscore",
|
||||||
"quoteleft",
|
"quoteleft",
|
||||||
"95 asciitilde",
|
"a",
|
||||||
"b",
|
"b",
|
||||||
"c",
|
"c",
|
||||||
"d",
|
"d",
|
||||||
@ -550,3 +550,150 @@ var CFFDictOps = {
|
|||||||
name: "nominalWidthX"
|
name: "nominalWidthX"
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
var CFFDictCommands = {
|
||||||
|
"1": {
|
||||||
|
name: "hstem"
|
||||||
|
},
|
||||||
|
"3": {
|
||||||
|
name: "vstem"
|
||||||
|
},
|
||||||
|
"4": {
|
||||||
|
name: "vmoveto"
|
||||||
|
},
|
||||||
|
"5": {
|
||||||
|
name: "rlineto"
|
||||||
|
},
|
||||||
|
"6": {
|
||||||
|
name: "hlineto"
|
||||||
|
},
|
||||||
|
"7": {
|
||||||
|
name: "vlineto"
|
||||||
|
},
|
||||||
|
"8": {
|
||||||
|
name: "rrcurveto"
|
||||||
|
},
|
||||||
|
"10": {
|
||||||
|
name: "callsubr"
|
||||||
|
},
|
||||||
|
"11": {
|
||||||
|
name: "return"
|
||||||
|
},
|
||||||
|
"12": {
|
||||||
|
"3": {
|
||||||
|
name: "and"
|
||||||
|
},
|
||||||
|
"4": {
|
||||||
|
name: "or"
|
||||||
|
},
|
||||||
|
"5": {
|
||||||
|
name: "not"
|
||||||
|
},
|
||||||
|
"9": {
|
||||||
|
name: "abs"
|
||||||
|
},
|
||||||
|
"10": {
|
||||||
|
name: "add"
|
||||||
|
},
|
||||||
|
"11": {
|
||||||
|
name: "div"
|
||||||
|
},
|
||||||
|
"12": {
|
||||||
|
name: "sub"
|
||||||
|
},
|
||||||
|
"14": {
|
||||||
|
name: "neg"
|
||||||
|
},
|
||||||
|
"15": {
|
||||||
|
name: "eq"
|
||||||
|
},
|
||||||
|
"18": {
|
||||||
|
name: "drop"
|
||||||
|
},
|
||||||
|
"20": {
|
||||||
|
name: "put"
|
||||||
|
},
|
||||||
|
"21": {
|
||||||
|
name: "get"
|
||||||
|
},
|
||||||
|
"22": {
|
||||||
|
name: "ifelse"
|
||||||
|
},
|
||||||
|
"23": {
|
||||||
|
name: "random"
|
||||||
|
},
|
||||||
|
"24": {
|
||||||
|
name: "mul"
|
||||||
|
},
|
||||||
|
"26": {
|
||||||
|
name: "sqrt"
|
||||||
|
},
|
||||||
|
"27": {
|
||||||
|
name: "dup"
|
||||||
|
},
|
||||||
|
"28": {
|
||||||
|
name: "exch"
|
||||||
|
},
|
||||||
|
"29": {
|
||||||
|
name: "index"
|
||||||
|
},
|
||||||
|
"30": {
|
||||||
|
name: "roll"
|
||||||
|
},
|
||||||
|
"34": {
|
||||||
|
name: "hflex"
|
||||||
|
},
|
||||||
|
"35": {
|
||||||
|
name: "flex"
|
||||||
|
},
|
||||||
|
"36": {
|
||||||
|
name: "hflex1"
|
||||||
|
},
|
||||||
|
"37": {
|
||||||
|
name: "flex1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"14": {
|
||||||
|
name: "endchar"
|
||||||
|
},
|
||||||
|
"18": {
|
||||||
|
name: "hstemhm"
|
||||||
|
},
|
||||||
|
"19": {
|
||||||
|
name: "hintmask"
|
||||||
|
},
|
||||||
|
"20": {
|
||||||
|
name: "cntrmask"
|
||||||
|
},
|
||||||
|
"21": {
|
||||||
|
name: "rmoveto"
|
||||||
|
},
|
||||||
|
"22": {
|
||||||
|
name: "hmoveto"
|
||||||
|
},
|
||||||
|
"23": {
|
||||||
|
name: "vstemhm"
|
||||||
|
},
|
||||||
|
"24": {
|
||||||
|
name: "rcurveline"
|
||||||
|
},
|
||||||
|
"25": {
|
||||||
|
name: "rlivecurve"
|
||||||
|
},
|
||||||
|
"26": {
|
||||||
|
name: "vvcurveto"
|
||||||
|
},
|
||||||
|
"27": {
|
||||||
|
name: "hhcurveto"
|
||||||
|
},
|
||||||
|
"29": {
|
||||||
|
name: "callgsubr"
|
||||||
|
},
|
||||||
|
"30": {
|
||||||
|
name: "vhcurveto"
|
||||||
|
},
|
||||||
|
"31": {
|
||||||
|
name: "hvcurveto"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user