From 7ee383a7f8adaffc58ca950fd6125a5e673781d5 Mon Sep 17 00:00:00 2001 From: sbarman Date: Wed, 13 Jul 2011 17:58:54 -0700 Subject: [PATCH 01/15] added reset --- pdf.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pdf.js b/pdf.js index a3179406c..9aef8aa28 100644 --- a/pdf.js +++ b/pdf.js @@ -221,6 +221,9 @@ var DecodeStream = (function() { if (!n) n = 1; this.pos += n; + }, + reset: function decodestream_reset() { + this.pos = 0; } }; From 0090b2e696c078aa98f44d68091a8877c01a5c3a Mon Sep 17 00:00:00 2001 From: sbarman Date: Fri, 15 Jul 2011 08:58:09 -0700 Subject: [PATCH 02/15] added support for index, dictionary in cff --- fonts.js | 171 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 170 insertions(+), 1 deletion(-) diff --git a/fonts.js b/fonts.js index 607e2aab9..cffb96473 100755 --- a/fonts.js +++ b/fonts.js @@ -390,8 +390,14 @@ var Font = (function() { switch (properties.type) { case 'Type1': case 'CIDFontType0': - var cff = new CFF(name, file, properties); this.mimetype = 'font/opentype'; + + var subtype = file.dict.get('Subtype'); + if (subtype.name === 'Type1C') { + var cff = new ActualCFF(file); + } else { + var cff = new CFF(name, file, properties); + } // Wrap the CFF data inside an OTF font file data = this.convert(name, cff, properties); @@ -410,6 +416,13 @@ var Font = (function() { warn('Font ' + properties.type + ' is not supported'); break; } + file.reset(); + var bytes = file.getBytes(); + var fileArr = []; + for (var i = 0, ii = bytes.length; i < ii; ++i) + fileArr.push(bytes[i]); + writeToFile(fileArr, '/tmp/' + name); + this.data = data; this.type = properties.type; this.id = Fonts.registerFont(name, data, properties); @@ -417,6 +430,9 @@ var Font = (function() { this.compositeFont = properties.compositeFont; }; + function parseCFF(file) { + }; + function stringToArray(str) { var array = []; for (var i = 0; i < str.length; ++i) @@ -1987,3 +2003,156 @@ CFF.prototype = { } }; +var ActualCFF = (function() { + function constructor(file) { + this.bytes = file.getBytes(); + this.parse(); + }; + + constructor.prototype = { + parse: function cff_parse() { + var header = this.parseHeader(); + var nameIndex = this.parseIndex(header.endPos); + var dictIndex = this.parseIndex(nameIndex.endPos); + var stringIndex = this.parseIndex(dictIndex.endPos); + var gsubrIndex = this.parseIndex(stringIndex.endPos); + + var dict = this.parseDict(dictIndex.get(0)); + + var dict = dictIndex.get(0); + log('blah'); + }, + parseHeader: function cff_parseHeader() { + var bytes = this.bytes; + var offset = 0; + + while(bytes[offset] != 1) + ++offset; + if (offset != 0) { + warning("cff data is shifted"); + bytes = bytes.subarray(offset); + this.bytes = bytes; + } + + return { + size: bytes[2], + endPos: bytes[2], + offsetSize: bytes[3] + } + }, + parseDict: function cff_parseDict(dict) { + var pos = 0; + + function parseOperand() { + var value = dict[pos++]; + if (value === 30) { + return parseFloat(pos); + } else if (value === 28) { + return (value << 8) | dict[pos++]; + } else if (value === 29) { + value = (value << 8) | dict[pos++]; + value = (value << 8) | dict[pos++]; + value = (value << 8) | dict[pos++]; + return value; + } else if (value <= 246) { + return value - 139; + } else if (value <= 250) { + return ((value - 247) * 256) + dict[pos++] + 108; + } else if (value <= 254) { + return -((value - 251) * 256) - dict[pos++] - 108; + } else { + error('Incorrect byte'); + } + }; + + function parseFloat() { + var str = ""; + var eof = 15; + var lookup = ['0', '1', '2', '3', '4', '5', '6', '7', '8', + '9', '.', 'E', 'E-', null, '-']; + var length = dict.length; + while (pos < length) { + var b = dict[pos++]; + var b1 = b >> 4; + var b2 = b & 15; + + if (b1 == eof) + break; + str += lookup[b1]; + + if (b2 == eof) + break; + str += lookup[b2]; + } + return parseFloat(str); + }; + + var operands = []; + var entries = []; + + var pos = 0; + var end = dict.length; + while (pos < end) { + var b = dict[pos]; + if (b <= 21) { + if (pos === 12) { + ++pos; + var b = (b << 8) | dict[pos]; + } + entries.push([b, operands]); + operands = []; + ++pos; + } else { + operands.push(parseOperand()); + } + } + return entries; + }, + parseIndex: function cff_parseIndex(pos) { + var bytes = this.bytes; + // add 1 to determine size of last object + var count = bytes[pos++] << 8 | bytes[pos++]; + if (count == 0) { + var offsets = []; + var end = pos; + } else { + var offsetSize = bytes[pos++]; + var startPos = pos + ((count + 1) * offsetSize) - 1; + + var offsets = []; + for (var i = 0, ii = count + 1; i < ii; ++i) { + var offset = 0; + for (var j = 0; j < offsetSize; ++j) { + offset <<= 8; + offset += bytes[pos++]; + } + offsets.push(startPos + offset); + } + var end = offsets[count]; + } + + return { + get: function index_get(index) { + if (index >= count) + return null; + + var start = offsets[index]; + var end = offsets[index + 1]; + return bytes.subarray(start, end); + }, + size: function index_size() { + return count; + }, + endPos: end + } + }, + bytesToString: function cff_bytestostring(bytes) { + var s = ""; + for (var i = 0, ii = bytes.length; i < ii; ++i) + s += String.fromCharCode(bytes[i]); + return s; + } + }; + + return constructor; +})(); From a7d6f857da1272d9d2f45a94474031b3bf60d9ed Mon Sep 17 00:00:00 2001 From: sbarman Date: Mon, 18 Jul 2011 08:24:57 -0700 Subject: [PATCH 03/15] working on cff fonts --- fonts.js | 136 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 124 insertions(+), 12 deletions(-) diff --git a/fonts.js b/fonts.js index cffb96473..d895d5e08 100755 --- a/fonts.js +++ b/fonts.js @@ -393,7 +393,7 @@ var Font = (function() { this.mimetype = 'font/opentype'; var subtype = file.dict.get('Subtype'); - if (subtype.name === 'Type1C') { + if (subtype && subtype.name === 'Type1C') { var cff = new ActualCFF(file); } else { var cff = new CFF(name, file, properties); @@ -2004,6 +2004,7 @@ CFF.prototype = { }; var ActualCFF = (function() { + function constructor(file) { this.bytes = file.getBytes(); this.parse(); @@ -2017,11 +2018,122 @@ var ActualCFF = (function() { var stringIndex = this.parseIndex(dictIndex.endPos); var gsubrIndex = this.parseIndex(stringIndex.endPos); - var dict = this.parseDict(dictIndex.get(0)); + if (dictIndex.length != 1) + error('More than 1 font'); + + var strings = this.getStrings(stringIndex); + + var baseDict = this.parseDict(dictIndex.get(0)); + var topDict = this.getTopDict(baseDict, strings); + + var bytes = this.bytes; - var dict = dictIndex.get(0); + var privInfo = topDict['Private']; + var privOffset = privInfo[1], privLength = privInfo[0]; + var privBytes = bytes.subarray(privOffset, privOffset + privLength); + baseDict = this.parseDict(privBytes); + // var privDict = this.getPrivDict(baseDict, strings); + + var encodings = this.parseEncoding(topDict['Encoding']); + var charStrings = this.parseIndex(topDict['CharStrings']); + var charsets = this.parseCharsets(topDict['charset'], charStrings.length, + strings); + +// var dict = dictIndex.get(0); log('blah'); }, + parseEncoding: function cff_parseencoding(pos) { + if (pos == 0) { + return Encodings.StandardEncoding; + } else if (pos == 1) { + return Encodings.ExpertEncoding; + } + + error('not implemented encodings'); + }, + parseCharsets: function cff_parsecharsets(pos, length, strings) { + var bytes = this.bytes; + var format = bytes[pos++]; + var charset = ['.notdef']; + switch (format) { + case 0: + for (var i = 0, ii = length - 1; i < ii; ++i) { + var id = bytes[pos++]; + id = (id << 8) | bytes[pos++]; + charset.push(strings[id]); + } + return charset; + case 1: + // subtract 1 for the .notdef glyph + length -= 1; + while (charset.length <= length) { + var first = bytes[pos++]; + first = (first << 8) | bytes[pos++]; + var numLeft = bytes[pos++]; + for (var i = 0; i <= numLeft; ++i) + charset.push(strings[first++]); + } + return charset; + case 2: + default: + } + + }, + getTopDict: function cff_gettopdict(baseDict, strings) { + var dict = {}; + + // default values + dict['Encoding'] = 0; + dict['charset'] = 0; + + for (var i = 0, ii = baseDict.length; i < ii; ++i) { + var pair = baseDict[i]; + var key = pair[0]; + var value = pair[1]; + switch(key) { + case 1: + dict['Notice'] = strings[value[0]]; + break; + case 4: + dict['Weight'] = strings[value[0]]; + break; + case 3094: + dict['BaseFontName'] = strings[value[0]]; + break; + case 5: + dict['FontBBox'] = value; + break; + case 13: + dict['UniqueID'] = value[0]; + break; + case 15: + dict['charset'] = value[0]; + break; + case 16: + dict['Encoding'] = value[0]; + break; + case 17: + dict['CharStrings'] = value[0]; + break; + case 18: + dict['Private'] = value; + break; + default: + TODO('interpret top dict key'); + } + } + return dict; + }, + getStrings: function cff_getstrings(stringIndex) { + var stringArray = []; + for (var i = 0, ii = CFFStrings.length; i < ii; ++i) + stringArray.push(CFFStrings[i]); + + for (var i = 0, ii = stringIndex.length; i < ii; ++i) + stringArray.push(this.bytesToString(stringIndex.get(i))); + + return stringArray; + }, parseHeader: function cff_parseHeader() { var bytes = this.bytes; var offset = 0; @@ -2035,7 +2147,6 @@ var ActualCFF = (function() { } return { - size: bytes[2], endPos: bytes[2], offsetSize: bytes[3] } @@ -2046,10 +2157,13 @@ var ActualCFF = (function() { function parseOperand() { var value = dict[pos++]; if (value === 30) { - return parseFloat(pos); + return parseFloatOperand(pos); } else if (value === 28) { - return (value << 8) | dict[pos++]; + value = dict[pos++]; + value = (value << 8) | dict[pos++]; + return value; } else if (value === 29) { + value = dict[pos++]; value = (value << 8) | dict[pos++]; value = (value << 8) | dict[pos++]; value = (value << 8) | dict[pos++]; @@ -2065,7 +2179,7 @@ var ActualCFF = (function() { } }; - function parseFloat() { + function parseFloatOperand() { var str = ""; var eof = 15; var lookup = ['0', '1', '2', '3', '4', '5', '6', '7', '8', @@ -2095,7 +2209,7 @@ var ActualCFF = (function() { while (pos < end) { var b = dict[pos]; if (b <= 21) { - if (pos === 12) { + if (b === 12) { ++pos; var b = (b << 8) | dict[pos]; } @@ -2110,13 +2224,13 @@ var ActualCFF = (function() { }, parseIndex: function cff_parseIndex(pos) { var bytes = this.bytes; - // add 1 to determine size of last object var count = bytes[pos++] << 8 | bytes[pos++]; if (count == 0) { var offsets = []; var end = pos; } else { var offsetSize = bytes[pos++]; + // add 1 for offset to determine size of last object var startPos = pos + ((count + 1) * offsetSize) - 1; var offsets = []; @@ -2140,9 +2254,7 @@ var ActualCFF = (function() { var end = offsets[index + 1]; return bytes.subarray(start, end); }, - size: function index_size() { - return count; - }, + length: count, endPos: end } }, From ed42423e3bbc723e95cd473d7af7fee1d4b90245 Mon Sep 17 00:00:00 2001 From: sbarman Date: Mon, 18 Jul 2011 12:22:04 -0700 Subject: [PATCH 04/15] working version, spacing not working --- fonts.js | 34 +++++++++++++++++++++++++++------- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/fonts.js b/fonts.js index d895d5e08..02f8b721f 100755 --- a/fonts.js +++ b/fonts.js @@ -421,7 +421,8 @@ var Font = (function() { var fileArr = []; for (var i = 0, ii = bytes.length; i < ii; ++i) fileArr.push(bytes[i]); - writeToFile(fileArr, '/tmp/' + name); + writeToFile(data, '/tmp/' + name); + writeToFile(fileArr, '/tmp/' + name + 'file'); this.data = data; this.type = properties.type; @@ -2006,7 +2007,14 @@ CFF.prototype = { var ActualCFF = (function() { function constructor(file) { - this.bytes = file.getBytes(); + var bytes = file.getBytes(); + this.bytes = bytes; + + var data = [] + for (var i = 0, ii = bytes.length; i < ii; ++i) + data.push(bytes[i]); + this.data = data; + this.parse(); }; @@ -2034,14 +2042,26 @@ var ActualCFF = (function() { baseDict = this.parseDict(privBytes); // var privDict = this.getPrivDict(baseDict, strings); - var encodings = this.parseEncoding(topDict['Encoding']); + var encoding = this.parseEncoding(topDict['Encoding']); var charStrings = this.parseIndex(topDict['CharStrings']); - var charsets = this.parseCharsets(topDict['charset'], charStrings.length, + var charset = this.parseCharsets(topDict['charset'], charStrings.length, strings); + this.charstrings = this.getCharStrings(encoding, charset); + // var dict = dictIndex.get(0); log('blah'); }, + getCharStrings: function cff_charstrings(encoding, charsets) { + var charstrings = []; + for (var i = 0, ii = charsets.length; i < ii; ++i) { + var charName = charsets[i]; + var charCode = GlyphsUnicode[charName]; + if (charCode) + charstrings.push( {unicode: charCode, width: 0}); + } + return charstrings; + }, parseEncoding: function cff_parseencoding(pos) { if (pos == 0) { return Encodings.StandardEncoding; @@ -2258,10 +2278,10 @@ var ActualCFF = (function() { endPos: end } }, - bytesToString: function cff_bytestostring(bytes) { + bytesToString: function cff_bytestostring(bytesArr) { var s = ""; - for (var i = 0, ii = bytes.length; i < ii; ++i) - s += String.fromCharCode(bytes[i]); + for (var i = 0, ii = bytesArr.length; i < ii; ++i) + s += String.fromCharCode(bytesArr[i]); return s; } }; From 226f3fa3ee68ec7fa2340de60f893476c02acfef Mon Sep 17 00:00:00 2001 From: sbarman Date: Mon, 18 Jul 2011 15:49:32 -0700 Subject: [PATCH 05/15] add correct char width --- fonts.js | 76 +++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 70 insertions(+), 6 deletions(-) diff --git a/fonts.js b/fonts.js index 02f8b721f..5d1fccf5f 100755 --- a/fonts.js +++ b/fonts.js @@ -2040,28 +2040,69 @@ var ActualCFF = (function() { var privOffset = privInfo[1], privLength = privInfo[0]; var privBytes = bytes.subarray(privOffset, privOffset + privLength); baseDict = this.parseDict(privBytes); - // var privDict = this.getPrivDict(baseDict, strings); + var privDict = this.getPrivDict(baseDict, strings); var encoding = this.parseEncoding(topDict['Encoding']); var charStrings = this.parseIndex(topDict['CharStrings']); var charset = this.parseCharsets(topDict['charset'], charStrings.length, strings); - this.charstrings = this.getCharStrings(encoding, charset); + this.charstrings = this.getCharStrings(encoding, charset, charStrings, + privDict); // var dict = dictIndex.get(0); log('blah'); }, - getCharStrings: function cff_charstrings(encoding, charsets) { + getCharStrings: function cff_charstrings(encoding, charsets, charStrings, + privDict) { + + var defaultWidth = privDict['defaultWidthX']; + var nominalWidth = privDict['nominalWidthX']; + var charstrings = []; for (var i = 0, ii = charsets.length; i < ii; ++i) { var charName = charsets[i]; - var charCode = GlyphsUnicode[charName]; - if (charCode) - charstrings.push( {unicode: charCode, width: 0}); + var charCode = GlyphsUnicode[charName]; + if (charCode) { + var charString = this.parseCharString(charStrings.get(i), + defaultWidth, nominalWidth); + charstrings.push({unicode: charCode, width: charString.width}); + } } return charstrings; }, + parseCharString: function cff_parsecs(bytes, defaultWidth, nominalWidth) { + var pos = 0; + + function parseInt() { + var value = bytes[pos++]; + if (value < 32) + return null; + + if (value <= 246) { + return value - 139; + } else if (value <= 250) { + return ((value - 247) * 256) + bytes[pos++] + 108; + } else if (value <= 254) { + return -((value - 251) * 256) - bytes[pos++] - 108; + } else { + error('Incorrect byte'); + } + }; + + var val = bytes[pos]; + var w; + if (val >= 32 && val <= 254) { + w = parseInt(); + } + + if (w) + w += nominalWidth; + else + w = defaultWidth; + + return {width: w} + }, parseEncoding: function cff_parseencoding(pos) { if (pos == 0) { return Encodings.StandardEncoding; @@ -2099,6 +2140,29 @@ var ActualCFF = (function() { } }, + getPrivDict: function cff_getprivdict(baseDict, strings) { + var dict = {}; + + dict['defaultWidthX'] = 0; + dict['nominalWidthX'] = 0; + + // default values + + for (var i = 0, ii = baseDict.length; i < ii; ++i) { + var pair = baseDict[i]; + var key = pair[0]; + var value = pair[1]; + switch(key) { + case 20: + dict['defaultWidthX'] = value[0]; + case 21: + dict['nominalWidthX'] = value[0]; + default: + TODO('interpret top dict key'); + } + } + return dict; + }, getTopDict: function cff_gettopdict(baseDict, strings) { var dict = {}; From 5f133a8c0788da4a3fe673ca4a0557a7df1add30 Mon Sep 17 00:00:00 2001 From: sbarman Date: Mon, 18 Jul 2011 21:01:05 -0700 Subject: [PATCH 06/15] fixed missing entries in charstring data --- fonts.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/fonts.js b/fonts.js index 5d1fccf5f..dee123e07 100755 --- a/fonts.js +++ b/fonts.js @@ -2067,6 +2067,8 @@ var ActualCFF = (function() { var charString = this.parseCharString(charStrings.get(i), defaultWidth, nominalWidth); charstrings.push({unicode: charCode, width: charString.width}); + } else { + charstrings.push({unicode: 0, width: 0}); } } return charstrings; From 173c68ef866b9da359823309b99adb3b61ff8372 Mon Sep 17 00:00:00 2001 From: sbarman Date: Tue, 19 Jul 2011 11:30:42 -0700 Subject: [PATCH 07/15] cleanup --- fonts.js | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/fonts.js b/fonts.js index dee123e07..8950de883 100755 --- a/fonts.js +++ b/fonts.js @@ -394,7 +394,7 @@ var Font = (function() { var subtype = file.dict.get('Subtype'); if (subtype && subtype.name === 'Type1C') { - var cff = new ActualCFF(file); + var cff = new Type2CFF(file); } else { var cff = new CFF(name, file, properties); } @@ -416,13 +416,6 @@ var Font = (function() { warn('Font ' + properties.type + ' is not supported'); break; } - file.reset(); - var bytes = file.getBytes(); - var fileArr = []; - for (var i = 0, ii = bytes.length; i < ii; ++i) - fileArr.push(bytes[i]); - writeToFile(data, '/tmp/' + name); - writeToFile(fileArr, '/tmp/' + name + 'file'); this.data = data; this.type = properties.type; @@ -2004,12 +1997,13 @@ CFF.prototype = { } }; -var ActualCFF = (function() { +var Type2CFF = (function() { function constructor(file) { var bytes = file.getBytes(); this.bytes = bytes; - + + // Other classes expect this.data to be a Javascript array var data = [] for (var i = 0, ii = bytes.length; i < ii; ++i) data.push(bytes[i]); @@ -2047,11 +2041,10 @@ var ActualCFF = (function() { var charset = this.parseCharsets(topDict['charset'], charStrings.length, strings); + // charstrings contains info about glyphs (one element per glyph + // containing mappings for {unicode, width} this.charstrings = this.getCharStrings(encoding, charset, charStrings, privDict); - -// var dict = dictIndex.get(0); - log('blah'); }, getCharStrings: function cff_charstrings(encoding, charsets, charStrings, privDict) { @@ -2067,8 +2060,6 @@ var ActualCFF = (function() { var charString = this.parseCharString(charStrings.get(i), defaultWidth, nominalWidth); charstrings.push({unicode: charCode, width: charString.width}); - } else { - charstrings.push({unicode: 0, width: 0}); } } return charstrings; From b8df2eb68bd147028c97d124884f5b7e2bc9011b Mon Sep 17 00:00:00 2001 From: sbarman Date: Tue, 19 Jul 2011 11:33:54 -0700 Subject: [PATCH 08/15] cleanup --- fonts.js | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/fonts.js b/fonts.js index 8950de883..8cbe6fe2f 100755 --- a/fonts.js +++ b/fonts.js @@ -424,9 +424,6 @@ var Font = (function() { this.compositeFont = properties.compositeFont; }; - function parseCFF(file) { - }; - function stringToArray(str) { var array = []; for (var i = 0; i < str.length; ++i) @@ -1999,6 +1996,7 @@ CFF.prototype = { var Type2CFF = (function() { + // TODO: replace parsing code with the Type2Parser in font_utils.js function constructor(file) { var bytes = file.getBytes(); this.bytes = bytes; From 8763002c4452dd51036de0b5a283171489f0639a Mon Sep 17 00:00:00 2001 From: sbarman Date: Wed, 20 Jul 2011 11:25:00 -0700 Subject: [PATCH 09/15] extracted filetype in pdf.js instead of fonts.js --- fonts.js | 4 ++-- pdf.js | 7 +++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/fonts.js b/fonts.js index 8cbe6fe2f..e241bb2f9 100755 --- a/fonts.js +++ b/fonts.js @@ -392,8 +392,8 @@ var Font = (function() { case 'CIDFontType0': this.mimetype = 'font/opentype'; - var subtype = file.dict.get('Subtype'); - if (subtype && subtype.name === 'Type1C') { + var subtype = properties.subtype; + if (subtype === 'Type1C') { var cff = new Type2CFF(file); } else { var cff = new CFF(name, file, properties); diff --git a/pdf.js b/pdf.js index 281adfe40..5ce22955c 100644 --- a/pdf.js +++ b/pdf.js @@ -3814,8 +3814,15 @@ var PartialEvaluator = (function() { } } + if (fontFile && fontFile.dict) { + var fileType = fontFile.dict.get('Subtype'); + if (fileType) + fileType = fileType.name; + } + var properties = { type: subType.name, + subtype: fileType, encoding: encodingMap, charset: charset, firstChar: fontDict.get('FirstChar'), From c7aec8ac8ab7e20b6a01d03baaaef29ce19139d4 Mon Sep 17 00:00:00 2001 From: sbarman Date: Wed, 20 Jul 2011 12:57:47 -0700 Subject: [PATCH 10/15] used width info in fontDict --- fonts.js | 12 ++++++++++++ pdf.js | 13 +++++++++++++ 2 files changed, 25 insertions(+) diff --git a/fonts.js b/fonts.js index e241bb2f9..7ca0daa30 100755 --- a/fonts.js +++ b/fonts.js @@ -399,6 +399,18 @@ var Font = (function() { var cff = new CFF(name, file, properties); } + var widths = properties.glyphWidths; + if (widths) { + var charstrings = cff.charstrings; + for (var i = 0, ii = charstrings.length; i < ii; ++i) { + var charstring = charstrings[i]; + var unicode = charstring.unicode; + var width = widths[unicode]; + if (width) + charstring.width = width; + } + } + // Wrap the CFF data inside an OTF font file data = this.convert(name, cff, properties); break; diff --git a/pdf.js b/pdf.js index 5ce22955c..8e30858bf 100644 --- a/pdf.js +++ b/pdf.js @@ -3820,9 +3820,22 @@ var PartialEvaluator = (function() { fileType = fileType.name; } + var widths = fontDict.get('Widths'); + var firstChar = fontDict.get('FirstChar'); + var lastChar = fontDict.get('LastChar'); + if (widths) { + var glyphWidths = []; + for (var i = 0; i < firstChar; ++i) + glyphWidths.push(0); + + for (var i = 0, ii = widths.length; i < ii; ++i) + glyphWidths.push(widths[i]); + } + var properties = { type: subType.name, subtype: fileType, + glyphWidths: glyphWidths, encoding: encodingMap, charset: charset, firstChar: fontDict.get('FirstChar'), From 8824ff9539b978531c54e068d88f0859ccd10ecb Mon Sep 17 00:00:00 2001 From: sbarman Date: Wed, 20 Jul 2011 14:43:44 -0700 Subject: [PATCH 11/15] removed code to parse type2 ccharstrings --- fonts.js | 38 +++----------------------------------- pdf.js | 10 +++------- 2 files changed, 6 insertions(+), 42 deletions(-) diff --git a/fonts.js b/fonts.js index 7ca0daa30..5d77f8980 100755 --- a/fonts.js +++ b/fonts.js @@ -408,6 +408,8 @@ var Font = (function() { var width = widths[unicode]; if (width) charstring.width = width; + else + charstring.width = 0; } } @@ -2067,45 +2069,11 @@ var Type2CFF = (function() { var charName = charsets[i]; var charCode = GlyphsUnicode[charName]; if (charCode) { - var charString = this.parseCharString(charStrings.get(i), - defaultWidth, nominalWidth); - charstrings.push({unicode: charCode, width: charString.width}); + charstrings.push({unicode: charCode, width: 0}); } } return charstrings; }, - parseCharString: function cff_parsecs(bytes, defaultWidth, nominalWidth) { - var pos = 0; - - function parseInt() { - var value = bytes[pos++]; - if (value < 32) - return null; - - if (value <= 246) { - return value - 139; - } else if (value <= 250) { - return ((value - 247) * 256) + bytes[pos++] + 108; - } else if (value <= 254) { - return -((value - 251) * 256) - bytes[pos++] - 108; - } else { - error('Incorrect byte'); - } - }; - - var val = bytes[pos]; - var w; - if (val >= 32 && val <= 254) { - w = parseInt(); - } - - if (w) - w += nominalWidth; - else - w = defaultWidth; - - return {width: w} - }, parseEncoding: function cff_parseencoding(pos) { if (pos == 0) { return Encodings.StandardEncoding; diff --git a/pdf.js b/pdf.js index 8e30858bf..d38f7391f 100644 --- a/pdf.js +++ b/pdf.js @@ -3821,15 +3821,11 @@ var PartialEvaluator = (function() { } var widths = fontDict.get('Widths'); - var firstChar = fontDict.get('FirstChar'); - var lastChar = fontDict.get('LastChar'); if (widths) { - var glyphWidths = []; - for (var i = 0; i < firstChar; ++i) - glyphWidths.push(0); - + var glyphWidths = {}; + var unicode = fontDict.get('FirstChar'); for (var i = 0, ii = widths.length; i < ii; ++i) - glyphWidths.push(widths[i]); + glyphWidths[unicode++] = widths[i]; } var properties = { From 2f30dd51746266c45fd337157a9c32b727db1d9a Mon Sep 17 00:00:00 2001 From: sbarman Date: Wed, 20 Jul 2011 15:00:16 -0700 Subject: [PATCH 12/15] moved width setting code --- fonts.js | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/fonts.js b/fonts.js index 5d77f8980..ec510a7d1 100755 --- a/fonts.js +++ b/fonts.js @@ -394,25 +394,11 @@ var Font = (function() { var subtype = properties.subtype; if (subtype === 'Type1C') { - var cff = new Type2CFF(file); + var cff = new Type2CFF(file, properties); } else { var cff = new CFF(name, file, properties); } - var widths = properties.glyphWidths; - if (widths) { - var charstrings = cff.charstrings; - for (var i = 0, ii = charstrings.length; i < ii; ++i) { - var charstring = charstrings[i]; - var unicode = charstring.unicode; - var width = widths[unicode]; - if (width) - charstring.width = width; - else - charstring.width = 0; - } - } - // Wrap the CFF data inside an OTF font file data = this.convert(name, cff, properties); break; @@ -2011,9 +1997,10 @@ CFF.prototype = { var Type2CFF = (function() { // TODO: replace parsing code with the Type2Parser in font_utils.js - function constructor(file) { + function constructor(file, properties) { var bytes = file.getBytes(); this.bytes = bytes; + this.properties = properties; // Other classes expect this.data to be a Javascript array var data = [] @@ -2056,10 +2043,11 @@ var Type2CFF = (function() { // charstrings contains info about glyphs (one element per glyph // containing mappings for {unicode, width} this.charstrings = this.getCharStrings(encoding, charset, charStrings, - privDict); + privDict, this.properties); }, getCharStrings: function cff_charstrings(encoding, charsets, charStrings, - privDict) { + privDict, properties) { + var widths = properties.glyphWidths; var defaultWidth = privDict['defaultWidthX']; var nominalWidth = privDict['nominalWidthX']; @@ -2069,7 +2057,10 @@ var Type2CFF = (function() { var charName = charsets[i]; var charCode = GlyphsUnicode[charName]; if (charCode) { - charstrings.push({unicode: charCode, width: 0}); + var width = widths[charCode]; + if (!width) + width = defaultWidth; + charstrings.push({unicode: charCode, width: width}); } } return charstrings; From 5b43c25d5669ae94674797ff421987eeebb2a3f0 Mon Sep 17 00:00:00 2001 From: sbarman Date: Thu, 21 Jul 2011 14:02:31 -0700 Subject: [PATCH 13/15] removed unnecessary call to parseEncoding --- fonts.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/fonts.js b/fonts.js index 77ad85a00..2e54ec391 100755 --- a/fonts.js +++ b/fonts.js @@ -2056,17 +2056,17 @@ var Type2CFF = (function() { baseDict = this.parseDict(privBytes); var privDict = this.getPrivDict(baseDict, strings); - var encoding = this.parseEncoding(topDict['Encoding']); +// var encoding = this.parseEncoding(topDict['Encoding']); var charStrings = this.parseIndex(topDict['CharStrings']); var charset = this.parseCharsets(topDict['charset'], charStrings.length, strings); // charstrings contains info about glyphs (one element per glyph // containing mappings for {unicode, width} - this.charstrings = this.getCharStrings(encoding, charset, charStrings, + this.charstrings = this.getCharStrings(charset, charStrings, privDict, this.properties); }, - getCharStrings: function cff_charstrings(encoding, charsets, charStrings, + getCharStrings: function cff_charstrings(charsets, charStrings, privDict, properties) { var widths = properties.glyphWidths; From d50286611195a6efd3800a1ce0efc4c7dc7eeea2 Mon Sep 17 00:00:00 2001 From: sbarman Date: Fri, 22 Jul 2011 09:21:38 -0700 Subject: [PATCH 14/15] cleanup code --- fonts.js | 57 +++++++++++++++++++++++++++++++++++--------------------- pdf.js | 2 +- 2 files changed, 37 insertions(+), 22 deletions(-) diff --git a/fonts.js b/fonts.js index 2e54ec391..486ca4aea 100755 --- a/fonts.js +++ b/fonts.js @@ -2036,12 +2036,14 @@ var Type2CFF = (function() { parse: function cff_parse() { var header = this.parseHeader(); var nameIndex = this.parseIndex(header.endPos); + var dictIndex = this.parseIndex(nameIndex.endPos); + if (dictIndex.length != 1) + error('More than 1 font'); + var stringIndex = this.parseIndex(dictIndex.endPos); var gsubrIndex = this.parseIndex(stringIndex.endPos); - if (dictIndex.length != 1) - error('More than 1 font'); var strings = this.getStrings(stringIndex); @@ -2055,20 +2057,20 @@ var Type2CFF = (function() { var privBytes = bytes.subarray(privOffset, privOffset + privLength); baseDict = this.parseDict(privBytes); var privDict = this.getPrivDict(baseDict, strings); - -// var encoding = this.parseEncoding(topDict['Encoding']); + + TODO('Parse encoding'); var charStrings = this.parseIndex(topDict['CharStrings']); var charset = this.parseCharsets(topDict['charset'], charStrings.length, strings); // charstrings contains info about glyphs (one element per glyph - // containing mappings for {unicode, width} + // containing mappings for {unicode, width}) this.charstrings = this.getCharStrings(charset, charStrings, privDict, this.properties); }, getCharStrings: function cff_charstrings(charsets, charStrings, privDict, properties) { - var widths = properties.glyphWidths; + var widths = properties.widths; var defaultWidth = privDict['defaultWidthX']; var nominalWidth = privDict['nominalWidthX']; @@ -2078,10 +2080,11 @@ var Type2CFF = (function() { var charName = charsets[i]; var charCode = GlyphsUnicode[charName]; if (charCode) { - var width = widths[charCode]; - if (!width) - width = defaultWidth; + var width = widths[charCode] || defaultWidth; charstrings.push({unicode: charCode, width: width}); + } else { + if (charName !== '.notdef') + warn('Cannot find unicode for glyph ' + charName); } } return charstrings; @@ -2099,17 +2102,18 @@ var Type2CFF = (function() { var bytes = this.bytes; var format = bytes[pos++]; var charset = ['.notdef']; + // subtract 1 for the .notdef glyph + length -= 1; + switch (format) { case 0: - for (var i = 0, ii = length - 1; i < ii; ++i) { + for (var i = 0; i < length; ++i) { var id = bytes[pos++]; id = (id << 8) | bytes[pos++]; charset.push(strings[id]); } return charset; case 1: - // subtract 1 for the .notdef glyph - length -= 1; while (charset.length <= length) { var first = bytes[pos++]; first = (first << 8) | bytes[pos++]; @@ -2119,18 +2123,27 @@ var Type2CFF = (function() { } return charset; case 2: + while (charset.length <= length) { + var first = bytes[pos++]; + first = (first << 8) | bytes[pos++]; + var numLeft = bytes[pos++]; + numLeft = (numLeft << 8) | bytes[pos++]; + for (var i = 0; i <= numLeft; ++i) + charset.push(strings[first++]); + } + return charset; default: + error('Unknown charset format'); } }, getPrivDict: function cff_getprivdict(baseDict, strings) { var dict = {}; + // default values dict['defaultWidthX'] = 0; dict['nominalWidthX'] = 0; - // default values - for (var i = 0, ii = baseDict.length; i < ii; ++i) { var pair = baseDict[i]; var key = pair[0]; @@ -2192,12 +2205,19 @@ var Type2CFF = (function() { return dict; }, getStrings: function cff_getstrings(stringIndex) { + function bytesToString(bytesArr) { + var s = ""; + for (var i = 0, ii = bytesArr.length; i < ii; ++i) + s += String.fromCharCode(bytesArr[i]); + return s; + } + var stringArray = []; for (var i = 0, ii = CFFStrings.length; i < ii; ++i) stringArray.push(CFFStrings[i]); for (var i = 0, ii = stringIndex.length; i < ii; ++i) - stringArray.push(this.bytesToString(stringIndex.get(i))); + stringArray.push(bytesToString(stringIndex.get(i))); return stringArray; }, @@ -2207,6 +2227,7 @@ var Type2CFF = (function() { while(bytes[offset] != 1) ++offset; + if (offset != 0) { warning("cff data is shifted"); bytes = bytes.subarray(offset); @@ -2325,12 +2346,6 @@ var Type2CFF = (function() { endPos: end } }, - bytesToString: function cff_bytestostring(bytesArr) { - var s = ""; - for (var i = 0, ii = bytesArr.length; i < ii; ++i) - s += String.fromCharCode(bytesArr[i]); - return s; - } }; return constructor; diff --git a/pdf.js b/pdf.js index 9b37f9f39..446275d1e 100644 --- a/pdf.js +++ b/pdf.js @@ -3838,7 +3838,7 @@ var PartialEvaluator = (function() { var properties = { type: subType.name, subtype: fileType, - glyphWidths: glyphWidths, + widths: glyphWidths, encoding: encodingMap, charset: charset, firstChar: fontDict.get('FirstChar'), From f17739877795bde053fa9562fd2116857007f554 Mon Sep 17 00:00:00 2001 From: sbarman Date: Fri, 22 Jul 2011 09:57:26 -0700 Subject: [PATCH 15/15] Fixed the mapping from charstring to glyphs --- fonts.js | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/fonts.js b/fonts.js index 486ca4aea..7f2e82773 100755 --- a/fonts.js +++ b/fonts.js @@ -1069,6 +1069,7 @@ var Font = (function Font() { var charstrings = font.charstrings; properties.fixedPitch = isFixedPitch(charstrings); + var fields = { // PostScript Font Program 'CFF ': font.data, @@ -1077,7 +1078,7 @@ var Font = (function Font() { 'OS/2': stringToArray(createOS2Table(properties)), // Character to glyphs mapping - 'cmap': createCMapTable(charstrings.slice()), + 'cmap': createCMapTable(charstrings.slice(), font.glyphIds), // Font header 'head': (function() { @@ -2065,8 +2066,17 @@ var Type2CFF = (function() { // charstrings contains info about glyphs (one element per glyph // containing mappings for {unicode, width}) - this.charstrings = this.getCharStrings(charset, charStrings, + var charstrings = this.getCharStrings(charset, charStrings, privDict, this.properties); + + // create the mapping between charstring and glyph id + var glyphIds = []; + for (var i = 0, ii = charstrings.length; i < ii; ++i) { + glyphIds.push(charstrings[i].gid); + } + + this.charstrings = charstrings; + this.glyphIds = glyphIds; }, getCharStrings: function cff_charstrings(charsets, charStrings, privDict, properties) { @@ -2081,12 +2091,15 @@ var Type2CFF = (function() { var charCode = GlyphsUnicode[charName]; if (charCode) { var width = widths[charCode] || defaultWidth; - charstrings.push({unicode: charCode, width: width}); + charstrings.push({unicode: charCode, width: width, gid: i}); } else { if (charName !== '.notdef') warn('Cannot find unicode for glyph ' + charName); } } + + // sort the arry by the unicode value + charstrings.sort(function(a, b) {return a.unicode - b.unicode}); return charstrings; }, parseEncoding: function cff_parseencoding(pos) {