Starts to decode type2 charStrings

This commit is contained in:
Vivien Nicolas 2011-06-08 20:37:25 +02:00
parent c098f0b31f
commit 87b4cb85be
2 changed files with 265 additions and 116 deletions

View File

@ -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"
];
/**************************************************************************/
/*
* 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) {
function decodeType2DictData(aString, aDictionary, aHack) {
var data = [];
var value = "";
var count = aString.length;
for (var i = 0; i < count; i) {
value = aString[i++];
if (value < 0) {
if (value <= 0) {
data.push(value);
continue;
} else if (value == 28) {
value = aString[i++] << 8 | aString[i++];
@ -743,11 +682,15 @@ function decodeType2DictData(aString, aDictionary) {
aString[i++] << 8 |
aString[i++];
} else if (value < 32) {
var oldValue = value;
if (value == 12) {
value = aDictionary["12"][aString[i++]];
} else {
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) {
value = parseInt(value) - 139;
} else if (value <= 250) {
@ -776,15 +719,30 @@ var Type2Parser = function(aFilePath) {
};
function readIndex(aStream, aIsByte) {
var count = aStream.getByte() + aStream.getByte();
var count = aStream.getByte() << 8 | aStream.getByte();
var offsize = aStream.getByte();
var offsets = [];
for (var i = 0; i < count + 1; i++) {
var offset = 0;
for (var j = 0; j < offsize; j++) {
// XXX need to do some better code here
var byte = aStream.getByte();
offset += byte;
switch (offsize) {
case 0:
offset = 0;
break;
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);
}
@ -800,58 +758,73 @@ var Type2Parser = function(aFilePath) {
var length = offsets[i + 1] - 1;
for (var j = offset - 1; j < length; j++)
data.push(aIsByte ? aStream.getByte() : aStream.getChar());
dump("object at offset " + offset + " is: " + data);
//dump("object at offset " + offset + " is: " + data);
objects.push(data);
}
return objects;
};
function parseAsToken(aArray) {
var objects = [];
function parseAsToken(aString, aDict) {
var decoded = decodeType2DictData(aString, aDict);
var count = aArray.length;
var stack = [];
var count = decoded.length;
for (var i = 0; i < count; i++) {
var decoded = decodeType2DictData(aArray[i], CFFDictOps);
var stack = [];
var count = decoded.length;
for (var i = 0; i < count; i++) {
var token = decoded[i];
if (IsNum(token)) {
stack.push(token);
} else {
switch (token.operand) {
case "SID":
font.set(token.name, CFFStrings[stack.pop()]);
break;
case "number number":
font.set(token.name, {
size: stack.pop(),
offset: stack.pop()
});
break;
case "boolean":
var token = decoded[i];
if (IsNum(token)) {
stack.push(token);
} else {
switch (token.operand) {
case "SID":
font.set(token.name, CFFStrings[stack.pop()]);
break;
case "number number":
font.set(token.name, {
size: stack.pop(),
offset: stack.pop()
});
break;
case "boolean":
font.set(token.name, stack.pop());
break;
case "delta":
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;
case "delta":
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;
}
}
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) {
@ -881,15 +854,44 @@ var Type2Parser = function(aFilePath) {
CFFStrings.push(strings[i].join(""));
// 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) {
log(p + "::" + font.get(p));
for (var p in font.map)
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();
xhr.open("GET", "titi.cff", false);
xhr.mozResponseType = xhr.responseType = "arraybuffer";

View File

@ -65,7 +65,7 @@ var CFFStrings = [
"asciicircum",
"underscore",
"quoteleft",
"95 asciitilde",
"a",
"b",
"c",
"d",
@ -550,3 +550,150 @@ var CFFDictOps = {
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"
}
};