Get rid of the PostScript interpreter (part 1)
This commit is contained in:
parent
2519e4f53b
commit
650ed04a70
448
PDFFont.js
448
PDFFont.js
@ -14,7 +14,7 @@ var kMaxGlyphsCount = 65526;
|
|||||||
/**
|
/**
|
||||||
* Maximum time to wait for a font to be loaded by @font-face
|
* Maximum time to wait for a font to be loaded by @font-face
|
||||||
*/
|
*/
|
||||||
var kMaxWaitForFontFace = 2000;
|
var kMaxWaitForFontFace = 1000;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Useful for debugging when you want to certains operations depending on how
|
* Useful for debugging when you want to certains operations depending on how
|
||||||
@ -59,8 +59,11 @@ var Fonts = {
|
|||||||
*
|
*
|
||||||
* As an improvment the last parameter can be replaced by an automatic guess
|
* As an improvment the last parameter can be replaced by an automatic guess
|
||||||
* of the font type based on the first byte of the file.
|
* of the font type based on the first byte of the file.
|
||||||
|
*
|
||||||
|
* XXX There is now too many parameters, this should be turned into an
|
||||||
|
* object containing all the required informations about the font
|
||||||
*/
|
*/
|
||||||
var Font = function(aName, aFile, aEncoding, aCharset, aType) {
|
var Font = function(aName, aFile, aEncoding, aCharset, aBBox, aType) {
|
||||||
this.name = aName;
|
this.name = aName;
|
||||||
|
|
||||||
// If the font has already been decoded simply return
|
// If the font has already been decoded simply return
|
||||||
@ -73,7 +76,7 @@ var Font = function(aName, aFile, aEncoding, aCharset, aType) {
|
|||||||
var start = Date.now();
|
var start = Date.now();
|
||||||
switch (aType) {
|
switch (aType) {
|
||||||
case "Type1":
|
case "Type1":
|
||||||
var cff = new CFF(aFile);
|
var cff = new CFF(aName, aBBox, aFile);
|
||||||
this.mimetype = "font/otf";
|
this.mimetype = "font/otf";
|
||||||
|
|
||||||
// Wrap the CFF data inside an OTF font file
|
// Wrap the CFF data inside an OTF font file
|
||||||
@ -175,7 +178,7 @@ Font.prototype = {
|
|||||||
|
|
||||||
if (debug)
|
if (debug)
|
||||||
ctx.fillText(testString, 20, 50);
|
ctx.fillText(testString, 20, 50);
|
||||||
}, 150, this);
|
}, 20, this);
|
||||||
|
|
||||||
/** Hack end */
|
/** Hack end */
|
||||||
|
|
||||||
@ -402,7 +405,7 @@ Font.prototype = {
|
|||||||
this._createTableEntry(otf, offsets, "OS/2", OS2);
|
this._createTableEntry(otf, offsets, "OS/2", OS2);
|
||||||
|
|
||||||
//XXX Getting charstrings here seems wrong since this is another CFF glue
|
//XXX Getting charstrings here seems wrong since this is another CFF glue
|
||||||
var charstrings = aFont.getOrderedCharStrings(aFont.font);
|
var charstrings = aFont.getOrderedCharStrings(aFont.glyphs);
|
||||||
|
|
||||||
/** CMAP */
|
/** CMAP */
|
||||||
cmap = this._createCMAPTable(charstrings);
|
cmap = this._createCMAPTable(charstrings);
|
||||||
@ -851,9 +854,7 @@ var Stack = function(aStackSize) {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
var Type1Parser = function(aAsciiStream, aBinaryStream) {
|
var Type1Parser = function() {
|
||||||
var lexer = aAsciiStream ? new Lexer(aAsciiStream) : null;
|
|
||||||
|
|
||||||
// Turn on this flag for additional debugging logs
|
// Turn on this flag for additional debugging logs
|
||||||
var debug = false;
|
var debug = false;
|
||||||
|
|
||||||
@ -862,30 +863,6 @@ var Type1Parser = function(aAsciiStream, aBinaryStream) {
|
|||||||
log(aData);
|
log(aData);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Hold the fontName as declared inside the /FontName postscript directive
|
|
||||||
// XXX This is a hack but at the moment I need it to map the name declared
|
|
||||||
// in the PDF and the name in the PS code.
|
|
||||||
var fontName = "";
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Parse a whole Type1 font stream (from the first segment to the last)
|
|
||||||
* assuming the 'eexec' block is binary data and fill up the 'Fonts'
|
|
||||||
* dictionary with the font informations.
|
|
||||||
*/
|
|
||||||
var self = this;
|
|
||||||
this.parse = function() {
|
|
||||||
if (!debug) {
|
|
||||||
while (!processNextToken()) {};
|
|
||||||
return fontName;
|
|
||||||
} else {
|
|
||||||
// debug mode is used to debug postcript processing
|
|
||||||
setTimeout(function() {
|
|
||||||
if (!processNextToken())
|
|
||||||
self.parse();
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Decrypt a Sequence of Ciphertext Bytes to Produce the Original Sequence
|
* Decrypt a Sequence of Ciphertext Bytes to Produce the Original Sequence
|
||||||
* of Plaintext Bytes. The function took a key as a parameter which can be
|
* of Plaintext Bytes. The function took a key as a parameter which can be
|
||||||
@ -894,7 +871,7 @@ var Type1Parser = function(aAsciiStream, aBinaryStream) {
|
|||||||
var kEexecEncryptionKey = 55665;
|
var kEexecEncryptionKey = 55665;
|
||||||
var kCharStringsEncryptionKey = 4330;
|
var kCharStringsEncryptionKey = 4330;
|
||||||
|
|
||||||
function decrypt(aStream, aKey, aDiscardNumber) {
|
function decrypt(aStream, aKey, aDiscardNumber, aByteArray) {
|
||||||
var start = Date.now();
|
var start = Date.now();
|
||||||
var r = aKey, c1 = 52845, c2 = 22719;
|
var r = aKey, c1 = 52845, c2 = 22719;
|
||||||
var decryptedString = [];
|
var decryptedString = [];
|
||||||
@ -903,7 +880,10 @@ var Type1Parser = function(aAsciiStream, aBinaryStream) {
|
|||||||
var count = aStream.length;
|
var count = aStream.length;
|
||||||
for (var i = 0; i < count; i++) {
|
for (var i = 0; i < count; i++) {
|
||||||
value = aStream.getByte();
|
value = aStream.getByte();
|
||||||
decryptedString[i] = String.fromCharCode(value ^ (r >> 8));
|
if (aByteArray)
|
||||||
|
decryptedString[i] = value ^ (r >> 8);
|
||||||
|
else
|
||||||
|
decryptedString[i] = String.fromCharCode(value ^ (r >> 8));
|
||||||
r = ((value + r) * c1 + c2) & ((1 << 16) - 1);
|
r = ((value + r) * c1 + c2) & ((1 << 16) - 1);
|
||||||
}
|
}
|
||||||
var end = Date.now();
|
var end = Date.now();
|
||||||
@ -1017,7 +997,7 @@ var Type1Parser = function(aAsciiStream, aBinaryStream) {
|
|||||||
var end = Date.now();
|
var end = Date.now();
|
||||||
dump("Time to decode charString of length " + count + " is " + (end - start));
|
dump("Time to decode charString of length " + count + " is " + (end - start));
|
||||||
return charString;
|
return charString;
|
||||||
}
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* The operand stack holds arbitrary PostScript objects that are the operands
|
* The operand stack holds arbitrary PostScript objects that are the operands
|
||||||
@ -1068,305 +1048,76 @@ var Type1Parser = function(aAsciiStream, aBinaryStream) {
|
|||||||
*/
|
*/
|
||||||
function nextInStack() {
|
function nextInStack() {
|
||||||
var currentProcedure = executionStack.peek();
|
var currentProcedure = executionStack.peek();
|
||||||
if (currentProcedure) {
|
var command = currentProcedure.shift();
|
||||||
var command = currentProcedure.shift();
|
if (!currentProcedure.length)
|
||||||
if (!currentProcedure.length)
|
executionStack.pop();
|
||||||
executionStack.pop();
|
return command;
|
||||||
return command;
|
|
||||||
}
|
|
||||||
|
|
||||||
return lexer.getObj();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/**
|
||||||
* Get the next token from the executionStack and process it.
|
* Returns an object containing a Subrs array and a CharStrings array
|
||||||
* Actually the function does not process the third segment of a Type1 font
|
* extracted from and eexec encrypted block of data
|
||||||
* and end on 'closefile'.
|
|
||||||
*
|
|
||||||
* The method thrown an error if it encounters an unknown token.
|
|
||||||
*/
|
*/
|
||||||
function processNextToken() {
|
this.extractFontInfo = function(aStream) {
|
||||||
var obj = nextInStack();
|
var eexecString = decrypt(new Stream(aStream), kEexecEncryptionKey, 4, true);
|
||||||
if (operandIsArray && !IsCmd(obj, "{") && !IsCmd(obj, "[") &&
|
var subrs = [], glyphs = [];
|
||||||
!IsCmd(obj, "]") && !IsCmd(obj, "}")) {
|
var inSubrs = inGlyphs = false;
|
||||||
dump("Adding an object: " + obj +" to array " + operandIsArray);
|
var glyph = "";
|
||||||
var currentArray = operandStack.peek();
|
|
||||||
for (var i = 1; i < operandIsArray; i++)
|
|
||||||
currentArray = currentArray[currentArray.length - 1];
|
|
||||||
|
|
||||||
currentArray.push(obj);
|
var token = "";
|
||||||
} else if (IsBool(obj) || IsInt(obj) || IsNum(obj) || IsString(obj)) {
|
var index = 0;
|
||||||
dump("Value: " + obj);
|
var length = 0;
|
||||||
operandStack.push(obj);
|
|
||||||
} else if (IsName(obj)) {
|
|
||||||
dump("Name: " + obj.name);
|
|
||||||
operandStack.push(obj.name);
|
|
||||||
} else if (IsCmd(obj)) {
|
|
||||||
var command = obj.cmd;
|
|
||||||
dump(command);
|
|
||||||
|
|
||||||
switch (command) {
|
var count = eexecString.length;
|
||||||
case "[":
|
var c = "";
|
||||||
case "{":
|
for (var i = 0; i < count; i++) {
|
||||||
dump("Start" + (command == "{" ? " Executable " : " ") + "Array");
|
var c = eexecString[i];
|
||||||
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 "]":
|
if (inSubrs && c == 0x52) {
|
||||||
case "}":
|
length = parseInt(length);
|
||||||
var currentArray = operandStack.peek();
|
var stream = new Stream(eexecString.slice(i + 3, i + 3 + length));
|
||||||
for (var i = 1; i < operandIsArray; i++)
|
var encodedSubr = decrypt(stream, kCharStringsEncryptionKey, 4).join("");
|
||||||
currentArray = currentArray[currentArray.length - 1];
|
var subr = decodeCharString(new StringStream(encodedSubr));
|
||||||
dump("End" + (command == "}" ? " Executable " : " ") + "Array: " + currentArray.join(" "));
|
|
||||||
operandIsArray--;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "if":
|
subrs.push(subr);
|
||||||
var procedure = operandStack.pop();
|
i += 3 + length;
|
||||||
var bool = operandStack.pop();
|
} else if (inGlyphs && c == 0x52) {
|
||||||
if (!IsBool(bool)) {
|
length = parseInt(length);
|
||||||
dump("if: " + bool);
|
var stream = new Stream(eexecString.slice(i + 3, i + 3 + length));
|
||||||
// we need to execute things, let be dirty
|
var encodedCharstring = decrypt(stream, kCharStringsEncryptionKey, 4).join("");
|
||||||
executionStack.push(bool);
|
var subr = decodeCharString(new StringStream(encodedCharstring));
|
||||||
} else {
|
|
||||||
dump("if ( " + bool + " ) { " + procedure + " }");
|
|
||||||
if (bool)
|
|
||||||
executionStack.push(procedure);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "ifelse":
|
glyphs.push({
|
||||||
var procedure1 = operandStack.pop();
|
glyph: glyph,
|
||||||
var procedure2 = operandStack.pop();
|
data: subr
|
||||||
var bool = !!operandStack.pop();
|
});
|
||||||
dump("if ( " + bool + " ) { " + procedure2 + " } else { " + procedure1 + " }");
|
i += 3 + length;
|
||||||
executionStack.push(bool ? procedure2 : procedure1);
|
} else if (inGlyphs && c == 0x2F) {
|
||||||
break;
|
token = "";
|
||||||
|
glyph = "";
|
||||||
|
|
||||||
case "for":
|
while ((c = eexecString[++i]) != 0x20 && i < count)
|
||||||
var procedure = operandStack.pop();
|
glyph += String.fromCharCode(c);
|
||||||
var limit = operandStack.pop();
|
} else if (c == 0x2F && eexecString[i+1] == 0x53 && !inGlyphs && !inSubrs) {
|
||||||
var increment = operandStack.pop();
|
while ((c = eexecString[++i]) != 0x20) {};
|
||||||
var initial = operandStack.pop();
|
inSubrs = true;
|
||||||
for (var i = 0; i < limit; i += increment) {
|
} else if (c == 0x20) {
|
||||||
operandStack.push(i);
|
index = length;
|
||||||
executionStack.push(procedure.slice());
|
length = token;
|
||||||
}
|
token = "";
|
||||||
break;
|
} else if (c == 0x2F && eexecString[i+1] == 0x43 && eexecString[i+2] == 0x68) {
|
||||||
|
while ((c = eexecString[++i]) != 0x20) {};
|
||||||
case "dup":
|
inSubrs = false;
|
||||||
dump("duplicate: " + operandStack.peek());
|
inGlyphs = true;
|
||||||
operandStack.push(operandStack.peek());
|
} else {
|
||||||
break;
|
token += String.fromCharCode(c);
|
||||||
|
|
||||||
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 indexOrKey = operandStack.pop();
|
|
||||||
var object = operandStack.pop();
|
|
||||||
dump("put " + data + " in " + object + "[" + indexOrKey + "]");
|
|
||||||
object.set ? object.set(indexOrKey, data)
|
|
||||||
: object[indexOrKey] = data;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "pop":
|
|
||||||
operandStack.pop();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "exch":
|
|
||||||
var operand1 = operandStack.pop();
|
|
||||||
var operand2 = operandStack.pop();
|
|
||||||
operandStack.push(operand1);
|
|
||||||
operandStack.push(operand2);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "get":
|
|
||||||
var indexOrKey = operandStack.pop();
|
|
||||||
var object = operandStack.pop();
|
|
||||||
var data = object.get ? object.get(indexOrKey) : object[indexOrKey];
|
|
||||||
dump("get " + object + "[" + indexOrKey + "]: " + data);
|
|
||||||
operandStack.push(data);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "currentdict":
|
|
||||||
var dict = dictionaryStack.peek();
|
|
||||||
operandStack.push(dict);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "systemdict":
|
|
||||||
operandStack.push(systemDict);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "readonly":
|
|
||||||
case "executeonly":
|
|
||||||
case "noaccess":
|
|
||||||
// Do nothing for the moment
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "currentfile":
|
|
||||||
operandStack.push("currentfile");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "array":
|
|
||||||
var size = operandStack.pop();
|
|
||||||
var array = new Array(size);
|
|
||||||
operandStack.push(array);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "dict":
|
|
||||||
var size = operandStack.pop();
|
|
||||||
var dict = new Dict(size);
|
|
||||||
operandStack.push(dict);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "begin":
|
|
||||||
dictionaryStack.push(operandStack.pop());
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "end":
|
|
||||||
dictionaryStack.pop();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "def":
|
|
||||||
var value = operandStack.pop();
|
|
||||||
var key = operandStack.pop();
|
|
||||||
dump("def: " + key + " = " + value);
|
|
||||||
dictionaryStack.peek().set(key, value);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "definefont":
|
|
||||||
var font = operandStack.pop();
|
|
||||||
var key = operandStack.pop();
|
|
||||||
dump("definefont " + font + " with key: " + key);
|
|
||||||
|
|
||||||
// The key will be the identifier to recognize this font
|
|
||||||
fontName = key;
|
|
||||||
PSFonts.set(key, font);
|
|
||||||
|
|
||||||
operandStack.push(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
|
|
||||||
// and start interpreting it in order to decode it
|
|
||||||
var file = operandStack.pop();
|
|
||||||
var eexecString = decrypt(aBinaryStream, kEexecEncryptionKey, 4).join("");
|
|
||||||
lexer = new Lexer(new StringStream(eexecString));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "LenIV":
|
|
||||||
error("LenIV: argh! we need to modify the length of discard characters for charStrings");
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "closefile":
|
|
||||||
var file = operandStack.pop();
|
|
||||||
return true;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "index":
|
|
||||||
var operands = [];
|
|
||||||
var size = operandStack.pop();
|
|
||||||
for (var i = 0; i < size; i++)
|
|
||||||
operands.push(operandStack.pop());
|
|
||||||
|
|
||||||
var newOperand = operandStack.peek();
|
|
||||||
|
|
||||||
while (operands.length)
|
|
||||||
operandStack.push(operands.pop());
|
|
||||||
|
|
||||||
operandStack.push(newOperand);
|
|
||||||
break;
|
|
||||||
|
|
||||||
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.start + 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);
|
|
||||||
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
|
|
||||||
// because of the uppercase 'S'
|
|
||||||
operandStack.push(obj.cmd);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
var command = null;
|
|
||||||
if (IsCmd(obj)) {
|
|
||||||
for (var i = 0; i < dictionaryStack.count(); i++) {
|
|
||||||
if (command = dictionaryStack.get(i).get(obj.cmd)) {
|
|
||||||
dump("found in dictionnary for " + obj.cmd + " command: " + command);
|
|
||||||
executionStack.push(command.slice());
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!command) {
|
|
||||||
log("operandStack: " + operandStack);
|
|
||||||
log("dictionaryStack: " + dictionaryStack);
|
|
||||||
log(obj);
|
|
||||||
error("Unknow command while parsing font");
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
} else if (obj) {
|
|
||||||
dump("unknow: " + obj);
|
|
||||||
operandStack.push(obj);
|
|
||||||
} else { // The End!
|
|
||||||
operandStack.dump();
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
return {
|
||||||
return false;
|
subrs: subrs,
|
||||||
}
|
charstrings: glyphs
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Flatten the commands by interpreting the postscript code and replacing
|
* Flatten the commands by interpreting the postscript code and replacing
|
||||||
@ -1462,19 +1213,25 @@ var Type1Parser = function(aAsciiStream, aBinaryStream) {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var CFF = function(aFontFile) {
|
var CFF = function(aFontName, aFontBBox, aFontFile) {
|
||||||
var start = Date.now();
|
var start = Date.now();
|
||||||
|
|
||||||
|
// Get the data block containing glyphs and subrs informations
|
||||||
var length1 = aFontFile.dict.get("Length1");
|
var length1 = aFontFile.dict.get("Length1");
|
||||||
var length2 = aFontFile.dict.get("Length2");
|
var length2 = aFontFile.dict.get("Length2");
|
||||||
|
aFontFile.skip(length1);
|
||||||
|
var eexecBlock = aFontFile.getBytes(length2);
|
||||||
|
|
||||||
var ASCIIStream = new Stream(aFontFile.getBytes(length1));
|
// Extract informations from it
|
||||||
var binaryStream = new Stream(aFontFile.getBytes(length2));
|
var parser = new Type1Parser();
|
||||||
|
var fontInfo = parser.extractFontInfo(eexecBlock);
|
||||||
|
fontInfo.name = aFontName;
|
||||||
|
fontInfo.bbox = aFontBBox;
|
||||||
|
|
||||||
this.parser = new Type1Parser(ASCIIStream, binaryStream);
|
// XXX
|
||||||
var fontName = this.parser.parse();
|
this.glyphs = fontInfo.charstrings;
|
||||||
this.font = PSFonts.get(fontName);
|
|
||||||
this.data = this.convertToCFF(this.font);
|
this.data = this.convertToCFF(fontInfo);
|
||||||
var end = Date.now();
|
var end = Date.now();
|
||||||
//log("Time to parse font is:" + (end - start));
|
//log("Time to parse font is:" + (end - start));
|
||||||
};
|
};
|
||||||
@ -1537,11 +1294,11 @@ CFF.prototype = {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
getOrderedCharStrings: function(aFont) {
|
getOrderedCharStrings: function(aGlyphs) {
|
||||||
var charstrings = [];
|
var charstrings = [];
|
||||||
|
|
||||||
var glyphs = aFont.get("CharStrings")
|
for (var i = 0; i < aGlyphs.length; i++) {
|
||||||
glyphs.forEach(function(glyph, glyphData) {
|
var glyph = aGlyphs[i].glyph;
|
||||||
var unicode = GlyphsUnicode[glyph];
|
var unicode = GlyphsUnicode[glyph];
|
||||||
if (!unicode) {
|
if (!unicode) {
|
||||||
if (glyph != ".notdef")
|
if (glyph != ".notdef")
|
||||||
@ -1554,10 +1311,10 @@ CFF.prototype = {
|
|||||||
charstrings.push({
|
charstrings.push({
|
||||||
glyph: glyph,
|
glyph: glyph,
|
||||||
unicode: unicode,
|
unicode: unicode,
|
||||||
charstring: glyphData.slice()
|
charstring: aGlyphs[i].data.slice()
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
|
|
||||||
charstrings.sort(function(a, b) {
|
charstrings.sort(function(a, b) {
|
||||||
return a.unicode > b.unicode;
|
return a.unicode > b.unicode;
|
||||||
@ -1565,20 +1322,20 @@ CFF.prototype = {
|
|||||||
return charstrings;
|
return charstrings;
|
||||||
},
|
},
|
||||||
|
|
||||||
convertToCFF: function(aFont) {
|
convertToCFF: function(aFontInfo) {
|
||||||
var debug = false;
|
var debug = false;
|
||||||
function dump(aMsg) {
|
function dump(aMsg) {
|
||||||
if (debug)
|
if (debug)
|
||||||
log(aMsg);
|
log(aMsg);
|
||||||
};
|
};
|
||||||
|
|
||||||
var charstrings = this.getOrderedCharStrings(aFont);
|
var charstrings = this.getOrderedCharStrings(aFontInfo.charstrings);
|
||||||
|
|
||||||
var charstringsCount = 0;
|
var charstringsCount = 0;
|
||||||
var charstringsDataLength = 0;
|
var charstringsDataLength = 0;
|
||||||
var glyphs = [];
|
var glyphs = [];
|
||||||
var glyphsChecker = {};
|
var glyphsChecker = {};
|
||||||
var subrs = aFont.get("Private").get("Subrs");
|
var subrs = aFontInfo.subrs;
|
||||||
var parser = new Type1Parser();
|
var parser = new Type1Parser();
|
||||||
for (var i = 0; i < charstrings.length; i++) {
|
for (var i = 0; i < charstrings.length; i++) {
|
||||||
var charstring = charstrings[i].charstring.slice();
|
var charstring = charstrings[i].charstring.slice();
|
||||||
@ -1604,19 +1361,18 @@ CFF.prototype = {
|
|||||||
cff.set(header);
|
cff.set(header);
|
||||||
|
|
||||||
// Names Index
|
// Names Index
|
||||||
var nameIndex = this.createCFFIndexHeader([aFont.get("FontName")]);
|
var nameIndex = this.createCFFIndexHeader([aFontInfo.name]);
|
||||||
cff.set(nameIndex, currentOffset);
|
cff.set(nameIndex, currentOffset);
|
||||||
currentOffset += nameIndex.length;
|
currentOffset += nameIndex.length;
|
||||||
|
|
||||||
// Calculate strings before writing the TopDICT index in order
|
// Calculate strings before writing the TopDICT index in order
|
||||||
// to calculate correct relative offsets for storing 'charset'
|
// to calculate correct relative offsets for storing 'charset'
|
||||||
// and 'charstrings' data
|
// and 'charstrings' data
|
||||||
var fontInfo = aFont.get("FontInfo");
|
var version = "";
|
||||||
var version = fontInfo.get("version");
|
var notice = "";
|
||||||
var notice = fontInfo.get("Notice");
|
var fullName = "";
|
||||||
var fullName = fontInfo.get("FullName");
|
var familyName = "";
|
||||||
var familyName = fontInfo.get("FamilyName");
|
var weight = "";
|
||||||
var weight = fontInfo.get("Weight");
|
|
||||||
var strings = [version, notice, fullName,
|
var strings = [version, notice, fullName,
|
||||||
familyName, weight];
|
familyName, weight];
|
||||||
var stringsIndex = this.createCFFIndexHeader(strings);
|
var stringsIndex = this.createCFFIndexHeader(strings);
|
||||||
@ -1692,7 +1448,7 @@ CFF.prototype = {
|
|||||||
248, 31, 4 // Weight
|
248, 31, 4 // Weight
|
||||||
];
|
];
|
||||||
|
|
||||||
var fontBBox = aFont.get("FontBBox");
|
var fontBBox = aFontInfo.bbox;
|
||||||
for (var i = 0; i < fontBBox.length; i++)
|
for (var i = 0; i < fontBBox.length; i++)
|
||||||
topDictIndex = topDictIndex.concat(this.encodeNumber(fontBBox[i]));
|
topDictIndex = topDictIndex.concat(this.encodeNumber(fontBBox[i]));
|
||||||
topDictIndex.push(5) // FontBBox;
|
topDictIndex.push(5) // FontBBox;
|
||||||
|
4
test.js
4
test.js
@ -119,8 +119,10 @@ function displayPage(num) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var fontBBox = descriptor.get("FontBBox");
|
||||||
|
|
||||||
var subtype = fontDict.get("Subtype").name;
|
var subtype = fontDict.get("Subtype").name;
|
||||||
new Font(fontName, fontFile, encodingMap, charset, subtype);
|
new Font(fontName, fontFile, encodingMap, charset, fontBBox, subtype);
|
||||||
return fontsReady = false;
|
return fontsReady = false;
|
||||||
} else if (font.loading) {
|
} else if (font.loading) {
|
||||||
return fontsReady = false;
|
return fontsReady = false;
|
||||||
|
Loading…
Reference in New Issue
Block a user