diff --git a/bower.json b/bower.json index 8a47da6d6..8af6bf947 100644 --- a/bower.json +++ b/bower.json @@ -1,6 +1,6 @@ { "name": "pdfjs-dist", - "version": "1.0.635", + "version": "1.0.639", "keywords": [ "Mozilla", "pdf", diff --git a/build/pdf.combined.js b/build/pdf.combined.js index 1b89af47d..8a87480c0 100644 --- a/build/pdf.combined.js +++ b/build/pdf.combined.js @@ -22,8 +22,8 @@ if (typeof PDFJS === 'undefined') { (typeof window !== 'undefined' ? window : this).PDFJS = {}; } -PDFJS.version = '1.0.635'; -PDFJS.build = '5e18614'; +PDFJS.version = '1.0.639'; +PDFJS.build = '29d116f'; (function pdfjsWrapper() { // Use strict in our context only - users might not want it @@ -465,15 +465,6 @@ function bytesToString(bytes) { return strBuf.join(''); } -function stringToArray(str) { - var length = str.length; - var array = new Uint16Array(length); - for (var i = 0; i < length; ++i) { - array[i] = str.charCodeAt(i); - } - return array; -} - function stringToBytes(str) { var length = str.length; var bytes = new Uint8Array(length); @@ -20596,20 +20587,20 @@ var IdentityToUnicodeMap = (function IdentityToUnicodeMapClosure() { error('should not access .length'); }, - forEach: function(callback) { + forEach: function (callback) { for (var i = this.firstChar, ii = this.lastChar; i <= ii; i++) { callback(i, i); } }, - get: function(i) { + get: function (i) { if (this.firstChar <= i && i <= this.lastChar) { return String.fromCharCode(i); } return undefined; }, - charCodeOf: function(v) { + charCodeOf: function (v) { error('should not call .charCodeOf'); } }; @@ -20617,6 +20608,147 @@ var IdentityToUnicodeMap = (function IdentityToUnicodeMapClosure() { return IdentityToUnicodeMap; })(); +var OpenTypeFileBuilder = (function OpenTypeFileBuilderClosure() { + function writeInt16(dest, offset, num) { + dest[offset] = (num >> 8) & 0xFF; + dest[offset + 1] = num & 0xFF; + } + + function writeInt32(dest, offset, num) { + dest[offset] = (num >> 24) & 0xFF; + dest[offset + 1] = (num >> 16) & 0xFF; + dest[offset + 2] = (num >> 8) & 0xFF; + dest[offset + 3] = num & 0xFF; + } + + function writeData(dest, offset, data) { + var i, ii; + if (data instanceof Uint8Array) { + dest.set(data, offset); + } else if (typeof data === 'string') { + for (i = 0, ii = data.length; i < ii; i++) { + dest[offset++] = data.charCodeAt(i) & 0xFF; + } + } else { + // treating everything else as array + for (i = 0, ii = data.length; i < ii; i++) { + dest[offset++] = data[i] & 0xFF; + } + } + } + + function OpenTypeFileBuilder(sfnt) { + this.sfnt = sfnt; + this.tables = Object.create(null); + } + + OpenTypeFileBuilder.getSearchParams = + function OpenTypeFileBuilder_getSearchParams(entriesCount, entrySize) { + var maxPower2 = 1, log2 = 0; + while ((maxPower2 ^ entriesCount) > maxPower2) { + maxPower2 <<= 1; + log2++; + } + var searchRange = maxPower2 * entrySize; + return { + range: searchRange, + entry: log2, + rangeShift: entrySize * entriesCount - searchRange + }; + }; + + var OTF_HEADER_SIZE = 12; + var OTF_TABLE_ENTRY_SIZE = 16; + + OpenTypeFileBuilder.prototype = { + toArray: function OpenTypeFileBuilder_toArray() { + var sfnt = this.sfnt; + + // Tables needs to be written by ascendant alphabetic order + var tables = this.tables; + var tablesNames = Object.keys(tables); + tablesNames.sort(); + var numTables = tablesNames.length; + + var i, j, jj, table, tableName; + // layout the tables data + var offset = OTF_HEADER_SIZE + numTables * OTF_TABLE_ENTRY_SIZE; + var tableOffsets = [offset]; + for (i = 0; i < numTables; i++) { + table = tables[tablesNames[i]]; + var paddedLength = ((table.length + 3) & ~3) >>> 0; + offset += paddedLength; + tableOffsets.push(offset); + } + + var file = new Uint8Array(offset); + // write the table data first (mostly for checksum) + for (i = 0; i < numTables; i++) { + table = tables[tablesNames[i]]; + writeData(file, tableOffsets[i], table); + } + + // sfnt version (4 bytes) + if (sfnt === 'true') { + // Windows hates the Mac TrueType sfnt version number + sfnt = string32(0x00010000); + } + file[0] = sfnt.charCodeAt(0) & 0xFF; + file[1] = sfnt.charCodeAt(1) & 0xFF; + file[2] = sfnt.charCodeAt(2) & 0xFF; + file[3] = sfnt.charCodeAt(3) & 0xFF; + + // numTables (2 bytes) + writeInt16(file, 4, numTables); + + var searchParams = OpenTypeFileBuilder.getSearchParams(numTables, 16); + + // searchRange (2 bytes) + writeInt16(file, 6, searchParams.range); + // entrySelector (2 bytes) + writeInt16(file, 8, searchParams.entry); + // rangeShift (2 bytes) + writeInt16(file, 10, searchParams.rangeShift); + + offset = OTF_HEADER_SIZE; + // writing table entries + for (i = 0; i < numTables; i++) { + tableName = tablesNames[i]; + file[offset] = tableName.charCodeAt(0) & 0xFF; + file[offset + 1] = tableName.charCodeAt(1) & 0xFF; + file[offset + 2] = tableName.charCodeAt(2) & 0xFF; + file[offset + 3] = tableName.charCodeAt(3) & 0xFF; + + // checksum + var checksum = 0; + for (j = tableOffsets[i], jj = tableOffsets[i + 1]; j < jj; j += 4) { + var quad = (file[j] << 24) + (file[j + 1] << 16) + + (file[j + 2] << 8) + file[j + 3]; + checksum = (checksum + quad) | 0; + } + writeInt32(file, offset + 4, checksum); + + // offset + writeInt32(file, offset + 8, tableOffsets[i]); + // length + writeInt32(file, offset + 12, tables[tableName].length); + + offset += OTF_TABLE_ENTRY_SIZE; + } + return file; + }, + + addTable: function OpenTypeFileBuilder_addTable(tag, data) { + if (tag in this.tables) { + throw new Error('Table ' + tag + ' already exists'); + } + this.tables[tag] = data; + } + }; + + return OpenTypeFileBuilder; +})(); + /** * 'Font' is the class the outside world should use, it encapsulate all the font * decoding logics whatever type it is (assuming the font type is supported). @@ -20826,21 +20958,6 @@ var Font = (function FontClosure() { return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3; } - function getMaxPower2(number) { - var maxPower = 0; - var value = number; - while (value >= 2) { - value /= 2; - maxPower++; - } - - value = 2; - for (var i = 1; i < maxPower; i++) { - value *= 2; - } - return value; - } - function string16(value) { return String.fromCharCode((value >> 8) & 0xff, value & 0xff); } @@ -20851,89 +20968,6 @@ var Font = (function FontClosure() { return String.fromCharCode((value >> 8) & 0xff, value & 0xff); } - function createOpenTypeHeader(sfnt, file, numTables) { - // Windows hates the Mac TrueType sfnt version number - if (sfnt === 'true') { - sfnt = string32(0x00010000); - } - - // sfnt version (4 bytes) - var header = sfnt; - - // numTables (2 bytes) - header += string16(numTables); - - // searchRange (2 bytes) - var tablesMaxPower2 = getMaxPower2(numTables); - var searchRange = tablesMaxPower2 * 16; - header += string16(searchRange); - - // entrySelector (2 bytes) - header += string16(Math.log(tablesMaxPower2) / Math.log(2)); - - // rangeShift (2 bytes) - header += string16(numTables * 16 - searchRange); - - file.file += header; - file.virtualOffset += header.length; - } - - function createTableEntry(file, tag, data) { - // offset - var offset = file.virtualOffset; - - // length - var length = data.length; - - // Per spec tables must be 4-bytes align so add padding as needed. - var paddedLength = length; - while (paddedLength & 3) { - paddedLength++; - } - var i; - var padding = paddedLength - length; - if (padding !== 0) { - // Padding is required. |data| can be an Array, Uint8Array, or - // Uint16Array. In the latter two cases we need to create slightly larger - // typed arrays and copy the old contents in. Fortunately that's not a - // common case. - var data2; - if (data instanceof Array) { - for (i = 0; i < padding; i++) { - data.push(0); - } - } else if (data instanceof Uint8Array) { - data2 = new Uint8Array(paddedLength); - data2.set(data); - data = data2; - } else if (data instanceof Uint16Array) { - data2 = new Uint16Array(paddedLength); - data2.set(data); - data = data2; - } else { - error('bad array kind in createTableEntry'); - } - } - - while (file.virtualOffset & 3) { - file.virtualOffset++; - } - - // checksum - var checksum = 0, n = data.length; - for (i = 0; i < n; i += 4) { - checksum = (checksum + int32(data[i], data[i + 1], data[i + 2], - data[i + 3])) | 0; - } - - var tableEntry = (tag + string32(checksum) + - string32(offset) + string32(length)); - file.file += tableEntry; - file.virtualOffset += data.length; - - return data; - } - function isTrueTypeFile(file) { var header = file.peekBytes(4); return readUint32(header, 0) === 0x00010000; @@ -21070,10 +21104,7 @@ var Font = (function FontClosure() { } var trailingRangesCount = ranges[i][1] < 0xFFFF ? 1 : 0; var segCount = bmpLength + trailingRangesCount; - var segCount2 = segCount * 2; - var searchRange = getMaxPower2(segCount) * 2; - var searchEntry = Math.log(segCount) / Math.log(2); - var rangeShift = 2 * segCount - searchRange; + var searchParams = OpenTypeFileBuilder.getSearchParams(segCount, 2); // Fill up the 4 parallel arrays describing the segments. var startCount = ''; @@ -21124,10 +21155,10 @@ var Font = (function FontClosure() { } var format314 = '\x00\x00' + // language - string16(segCount2) + - string16(searchRange) + - string16(searchEntry) + - string16(rangeShift) + + string16(2 * segCount) + + string16(searchParams.range) + + string16(searchParams.entry) + + string16(searchParams.rangeShift) + endCount + '\x00\x00' + startCount + idDeltas + idRangeOffsets + glyphsIds; @@ -21165,10 +21196,9 @@ var Font = (function FontClosure() { string32(format31012.length / 12); // nGroups } - return stringToArray(cmap + - '\x00\x04' + // format - string16(format314.length + 4) + // length - format314 + header31012 + format31012); + return cmap + '\x00\x04' + // format + string16(format314.length + 4) + // length + format314 + header31012 + format31012; } function validateOS2Table(os2) { @@ -21662,7 +21692,7 @@ var Font = (function FontClosure() { for (i = 0; i < numMissing; i++) { entries += '\x00\x00'; } - metrics.data = stringToArray(entries); + metrics.data = entries; } } @@ -22248,7 +22278,7 @@ var Font = (function FontClosure() { var tables = { 'OS/2': null, cmap: null, head: null, hhea: null, hmtx: null, maxp: null, name: null, post: null }; - var table, tableData; + var table; for (var i = 0; i < numTables; i++) { table = readTableEntry(font); if (VALID_TABLES.indexOf(table.tag) < 0) { @@ -22322,24 +22352,6 @@ var Font = (function FontClosure() { delete tables['cvt ']; } - // Tables needs to be written by ascendant alphabetic order - var tablesNames = Object.keys(tables); - tablesNames.sort(); - - numTables = tablesNames.length; - - // header and new offsets. Table entry information is appended to the - // end of file. The virtualOffset represents where to put the actual - // data of a particular table; - var ttf = { - file: '', - virtualOffset: numTables * (4 * 4) - }; - - // The new numbers of tables will be the last one plus the num - // of missing tables - createOpenTypeHeader(header.version, ttf, numTables); - // Ensure the hmtx table contains the advance width and // sidebearings information for numGlyphs in the maxp table sanitizeMetrics(font, tables.hhea, tables.hmtx, numGlyphs); @@ -22502,9 +22514,8 @@ var Font = (function FontClosure() { tables['OS/2'] = { tag: 'OS/2', - data: stringToArray(createOS2Table(properties, - newMapping.charCodeToGlyphId, - override)) + data: createOS2Table(properties, newMapping.charCodeToGlyphId, + override) }; } @@ -22512,7 +22523,7 @@ var Font = (function FontClosure() { if (!tables.post) { tables.post = { tag: 'post', - data: stringToArray(createPostTable(properties)) + data: createPostTable(properties) }; } @@ -22533,47 +22544,22 @@ var Font = (function FontClosure() { if (!tables.name) { tables.name = { tag: 'name', - data: stringToArray(createNameTable(this.name)) + data: createNameTable(this.name) }; } else { // ... using existing 'name' table as prototype var namePrototype = readNameTable(tables.name); - tables.name.data = stringToArray(createNameTable(name, namePrototype)); + tables.name.data = createNameTable(name, namePrototype); } - // rewrite the tables but tweak offsets - for (i = 0; i < numTables; i++) { - table = tables[tablesNames[i]]; - table.data = createTableEntry(ttf, table.tag, table.data); + var builder = new OpenTypeFileBuilder(header.version); + for (var tableTag in tables) { + builder.addTable(tableTag, tables[tableTag].data); } - - // Add the table datas - for (i = 0; i < numTables; i++) { - table = tables[tablesNames[i]]; - tableData = table.data; - ttf.file += bytesToString(new Uint8Array(tableData)); - - // 4-byte aligned data - while (ttf.file.length & 3) { - ttf.file += String.fromCharCode(0); - } - } - - return stringToArray(ttf.file); + return builder.toArray(); }, convert: function Font_convert(fontName, font, properties) { - // The offsets object holds at the same time a representation of where - // to write the table entry information about a table and another offset - // representing the offset where to draw the actual data of a particular - // table - var otf = { - file: '', - virtualOffset: 9 * (4 * 4) - }; - - createOpenTypeHeader('\x4F\x54\x54\x4F', otf, 9); - // TODO: Check the charstring widths to determine this. properties.fixedPitch = false; @@ -22653,20 +22639,16 @@ var Font = (function FontClosure() { var unitsPerEm = 1 / (properties.fontMatrix || FONT_IDENTITY_MATRIX)[0]; - var fields = { - // PostScript Font Program - 'CFF ': font.data, - - // OS/2 and Windows Specific metrics - 'OS/2': stringToArray(createOS2Table(properties, - newMapping.charCodeToGlyphId)), - - // Character to glyphs mapping - 'cmap': createCmapTable(newMapping.charCodeToGlyphId), - - // Font header - 'head': (function fontFieldsHead() { - return stringToArray( + var builder = new OpenTypeFileBuilder('\x4F\x54\x54\x4F'); + // PostScript Font Program + builder.addTable('CFF ', font.data); + // OS/2 and Windows Specific metrics + builder.addTable('OS/2', createOS2Table(properties, + newMapping.charCodeToGlyphId)); + // Character to glyphs mapping + builder.addTable('cmap', createCmapTable(newMapping.charCodeToGlyphId)); + // Font header + builder.addTable('head', '\x00\x01\x00\x00' + // Version number '\x00\x00\x10\x00' + // fontRevision '\x00\x00\x00\x00' + // checksumAdjustement @@ -22684,11 +22666,9 @@ var Font = (function FontClosure() { '\x00\x00' + // fontDirectionHint '\x00\x00' + // indexToLocFormat '\x00\x00'); // glyphDataFormat - })(), - // Horizontal header - 'hhea': (function fontFieldsHhea() { - return stringToArray( + // Horizontal header + builder.addTable('hhea', '\x00\x01\x00\x00' + // Version number safeString16(properties.ascent) + // Typographic Ascent safeString16(properties.descent) + // Typographic Descent @@ -22707,10 +22687,9 @@ var Font = (function FontClosure() { '\x00\x00' + // -reserved- '\x00\x00' + // metricDataFormat string16(numGlyphs)); // Number of HMetrics - })(), - // Horizontal metrics - 'hmtx': (function fontFieldsHmtx() { + // Horizontal metrics + builder.addTable('hmtx', (function fontFieldsHmtx() { var charstrings = font.charstrings; var hmtx = '\x00\x00\x00\x00'; // Fake .notdef for (var i = 1, ii = numGlyphs; i < ii; i++) { @@ -22720,33 +22699,21 @@ var Font = (function FontClosure() { var width = 'width' in charstring ? charstring.width : 0; hmtx += string16(width) + string16(0); } - return stringToArray(hmtx); - })(), + return hmtx; + })()); - // Maximum profile - 'maxp': (function fontFieldsMaxp() { - return stringToArray( + // Maximum profile + builder.addTable('maxp', '\x00\x00\x50\x00' + // Version number string16(numGlyphs)); // Num of glyphs - })(), - // Naming tables - 'name': stringToArray(createNameTable(fontName)), + // Naming tables + builder.addTable('name', createNameTable(fontName)); - // PostScript informations - 'post': stringToArray(createPostTable(properties)) - }; - - var field; - for (field in fields) { - fields[field] = createTableEntry(otf, field, fields[field]); - } - for (field in fields) { - var table = fields[field]; - otf.file += bytesToString(new Uint8Array(table)); - } + // PostScript informations + builder.addTable('post', createPostTable(properties)); - return stringToArray(otf.file); + return builder.toArray(); }, /** @@ -25296,7 +25263,7 @@ var CFFCompiler = (function CFFCompilerClosure() { compileNameIndex: function CFFCompiler_compileNameIndex(names) { var nameIndex = new CFFIndex(); for (var i = 0, ii = names.length; i < ii; ++i) { - nameIndex.add(stringToArray(names[i])); + nameIndex.add(stringToBytes(names[i])); } return this.compileIndex(nameIndex); }, @@ -25421,7 +25388,7 @@ var CFFCompiler = (function CFFCompilerClosure() { compileStringIndex: function CFFCompiler_compileStringIndex(strings) { var stringIndex = new CFFIndex(); for (var i = 0, ii = strings.length; i < ii; ++i) { - stringIndex.add(stringToArray(strings[i])); + stringIndex.add(stringToBytes(strings[i])); } return this.compileIndex(stringIndex); }, diff --git a/build/pdf.js b/build/pdf.js index 7cf50c53e..0f33cf6b3 100644 --- a/build/pdf.js +++ b/build/pdf.js @@ -22,8 +22,8 @@ if (typeof PDFJS === 'undefined') { (typeof window !== 'undefined' ? window : this).PDFJS = {}; } -PDFJS.version = '1.0.635'; -PDFJS.build = '5e18614'; +PDFJS.version = '1.0.639'; +PDFJS.build = '29d116f'; (function pdfjsWrapper() { // Use strict in our context only - users might not want it @@ -465,15 +465,6 @@ function bytesToString(bytes) { return strBuf.join(''); } -function stringToArray(str) { - var length = str.length; - var array = new Uint16Array(length); - for (var i = 0; i < length; ++i) { - array[i] = str.charCodeAt(i); - } - return array; -} - function stringToBytes(str) { var length = str.length; var bytes = new Uint8Array(length); diff --git a/build/pdf.worker.js b/build/pdf.worker.js index ed29f483b..cba3dc5f7 100644 --- a/build/pdf.worker.js +++ b/build/pdf.worker.js @@ -22,8 +22,8 @@ if (typeof PDFJS === 'undefined') { (typeof window !== 'undefined' ? window : this).PDFJS = {}; } -PDFJS.version = '1.0.635'; -PDFJS.build = '5e18614'; +PDFJS.version = '1.0.639'; +PDFJS.build = '29d116f'; (function pdfjsWrapper() { // Use strict in our context only - users might not want it @@ -465,15 +465,6 @@ function bytesToString(bytes) { return strBuf.join(''); } -function stringToArray(str) { - var length = str.length; - var array = new Uint16Array(length); - for (var i = 0; i < length; ++i) { - array[i] = str.charCodeAt(i); - } - return array; -} - function stringToBytes(str) { var length = str.length; var bytes = new Uint8Array(length); @@ -15739,20 +15730,20 @@ var IdentityToUnicodeMap = (function IdentityToUnicodeMapClosure() { error('should not access .length'); }, - forEach: function(callback) { + forEach: function (callback) { for (var i = this.firstChar, ii = this.lastChar; i <= ii; i++) { callback(i, i); } }, - get: function(i) { + get: function (i) { if (this.firstChar <= i && i <= this.lastChar) { return String.fromCharCode(i); } return undefined; }, - charCodeOf: function(v) { + charCodeOf: function (v) { error('should not call .charCodeOf'); } }; @@ -15760,6 +15751,147 @@ var IdentityToUnicodeMap = (function IdentityToUnicodeMapClosure() { return IdentityToUnicodeMap; })(); +var OpenTypeFileBuilder = (function OpenTypeFileBuilderClosure() { + function writeInt16(dest, offset, num) { + dest[offset] = (num >> 8) & 0xFF; + dest[offset + 1] = num & 0xFF; + } + + function writeInt32(dest, offset, num) { + dest[offset] = (num >> 24) & 0xFF; + dest[offset + 1] = (num >> 16) & 0xFF; + dest[offset + 2] = (num >> 8) & 0xFF; + dest[offset + 3] = num & 0xFF; + } + + function writeData(dest, offset, data) { + var i, ii; + if (data instanceof Uint8Array) { + dest.set(data, offset); + } else if (typeof data === 'string') { + for (i = 0, ii = data.length; i < ii; i++) { + dest[offset++] = data.charCodeAt(i) & 0xFF; + } + } else { + // treating everything else as array + for (i = 0, ii = data.length; i < ii; i++) { + dest[offset++] = data[i] & 0xFF; + } + } + } + + function OpenTypeFileBuilder(sfnt) { + this.sfnt = sfnt; + this.tables = Object.create(null); + } + + OpenTypeFileBuilder.getSearchParams = + function OpenTypeFileBuilder_getSearchParams(entriesCount, entrySize) { + var maxPower2 = 1, log2 = 0; + while ((maxPower2 ^ entriesCount) > maxPower2) { + maxPower2 <<= 1; + log2++; + } + var searchRange = maxPower2 * entrySize; + return { + range: searchRange, + entry: log2, + rangeShift: entrySize * entriesCount - searchRange + }; + }; + + var OTF_HEADER_SIZE = 12; + var OTF_TABLE_ENTRY_SIZE = 16; + + OpenTypeFileBuilder.prototype = { + toArray: function OpenTypeFileBuilder_toArray() { + var sfnt = this.sfnt; + + // Tables needs to be written by ascendant alphabetic order + var tables = this.tables; + var tablesNames = Object.keys(tables); + tablesNames.sort(); + var numTables = tablesNames.length; + + var i, j, jj, table, tableName; + // layout the tables data + var offset = OTF_HEADER_SIZE + numTables * OTF_TABLE_ENTRY_SIZE; + var tableOffsets = [offset]; + for (i = 0; i < numTables; i++) { + table = tables[tablesNames[i]]; + var paddedLength = ((table.length + 3) & ~3) >>> 0; + offset += paddedLength; + tableOffsets.push(offset); + } + + var file = new Uint8Array(offset); + // write the table data first (mostly for checksum) + for (i = 0; i < numTables; i++) { + table = tables[tablesNames[i]]; + writeData(file, tableOffsets[i], table); + } + + // sfnt version (4 bytes) + if (sfnt === 'true') { + // Windows hates the Mac TrueType sfnt version number + sfnt = string32(0x00010000); + } + file[0] = sfnt.charCodeAt(0) & 0xFF; + file[1] = sfnt.charCodeAt(1) & 0xFF; + file[2] = sfnt.charCodeAt(2) & 0xFF; + file[3] = sfnt.charCodeAt(3) & 0xFF; + + // numTables (2 bytes) + writeInt16(file, 4, numTables); + + var searchParams = OpenTypeFileBuilder.getSearchParams(numTables, 16); + + // searchRange (2 bytes) + writeInt16(file, 6, searchParams.range); + // entrySelector (2 bytes) + writeInt16(file, 8, searchParams.entry); + // rangeShift (2 bytes) + writeInt16(file, 10, searchParams.rangeShift); + + offset = OTF_HEADER_SIZE; + // writing table entries + for (i = 0; i < numTables; i++) { + tableName = tablesNames[i]; + file[offset] = tableName.charCodeAt(0) & 0xFF; + file[offset + 1] = tableName.charCodeAt(1) & 0xFF; + file[offset + 2] = tableName.charCodeAt(2) & 0xFF; + file[offset + 3] = tableName.charCodeAt(3) & 0xFF; + + // checksum + var checksum = 0; + for (j = tableOffsets[i], jj = tableOffsets[i + 1]; j < jj; j += 4) { + var quad = (file[j] << 24) + (file[j + 1] << 16) + + (file[j + 2] << 8) + file[j + 3]; + checksum = (checksum + quad) | 0; + } + writeInt32(file, offset + 4, checksum); + + // offset + writeInt32(file, offset + 8, tableOffsets[i]); + // length + writeInt32(file, offset + 12, tables[tableName].length); + + offset += OTF_TABLE_ENTRY_SIZE; + } + return file; + }, + + addTable: function OpenTypeFileBuilder_addTable(tag, data) { + if (tag in this.tables) { + throw new Error('Table ' + tag + ' already exists'); + } + this.tables[tag] = data; + } + }; + + return OpenTypeFileBuilder; +})(); + /** * 'Font' is the class the outside world should use, it encapsulate all the font * decoding logics whatever type it is (assuming the font type is supported). @@ -15969,21 +16101,6 @@ var Font = (function FontClosure() { return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3; } - function getMaxPower2(number) { - var maxPower = 0; - var value = number; - while (value >= 2) { - value /= 2; - maxPower++; - } - - value = 2; - for (var i = 1; i < maxPower; i++) { - value *= 2; - } - return value; - } - function string16(value) { return String.fromCharCode((value >> 8) & 0xff, value & 0xff); } @@ -15994,89 +16111,6 @@ var Font = (function FontClosure() { return String.fromCharCode((value >> 8) & 0xff, value & 0xff); } - function createOpenTypeHeader(sfnt, file, numTables) { - // Windows hates the Mac TrueType sfnt version number - if (sfnt === 'true') { - sfnt = string32(0x00010000); - } - - // sfnt version (4 bytes) - var header = sfnt; - - // numTables (2 bytes) - header += string16(numTables); - - // searchRange (2 bytes) - var tablesMaxPower2 = getMaxPower2(numTables); - var searchRange = tablesMaxPower2 * 16; - header += string16(searchRange); - - // entrySelector (2 bytes) - header += string16(Math.log(tablesMaxPower2) / Math.log(2)); - - // rangeShift (2 bytes) - header += string16(numTables * 16 - searchRange); - - file.file += header; - file.virtualOffset += header.length; - } - - function createTableEntry(file, tag, data) { - // offset - var offset = file.virtualOffset; - - // length - var length = data.length; - - // Per spec tables must be 4-bytes align so add padding as needed. - var paddedLength = length; - while (paddedLength & 3) { - paddedLength++; - } - var i; - var padding = paddedLength - length; - if (padding !== 0) { - // Padding is required. |data| can be an Array, Uint8Array, or - // Uint16Array. In the latter two cases we need to create slightly larger - // typed arrays and copy the old contents in. Fortunately that's not a - // common case. - var data2; - if (data instanceof Array) { - for (i = 0; i < padding; i++) { - data.push(0); - } - } else if (data instanceof Uint8Array) { - data2 = new Uint8Array(paddedLength); - data2.set(data); - data = data2; - } else if (data instanceof Uint16Array) { - data2 = new Uint16Array(paddedLength); - data2.set(data); - data = data2; - } else { - error('bad array kind in createTableEntry'); - } - } - - while (file.virtualOffset & 3) { - file.virtualOffset++; - } - - // checksum - var checksum = 0, n = data.length; - for (i = 0; i < n; i += 4) { - checksum = (checksum + int32(data[i], data[i + 1], data[i + 2], - data[i + 3])) | 0; - } - - var tableEntry = (tag + string32(checksum) + - string32(offset) + string32(length)); - file.file += tableEntry; - file.virtualOffset += data.length; - - return data; - } - function isTrueTypeFile(file) { var header = file.peekBytes(4); return readUint32(header, 0) === 0x00010000; @@ -16213,10 +16247,7 @@ var Font = (function FontClosure() { } var trailingRangesCount = ranges[i][1] < 0xFFFF ? 1 : 0; var segCount = bmpLength + trailingRangesCount; - var segCount2 = segCount * 2; - var searchRange = getMaxPower2(segCount) * 2; - var searchEntry = Math.log(segCount) / Math.log(2); - var rangeShift = 2 * segCount - searchRange; + var searchParams = OpenTypeFileBuilder.getSearchParams(segCount, 2); // Fill up the 4 parallel arrays describing the segments. var startCount = ''; @@ -16267,10 +16298,10 @@ var Font = (function FontClosure() { } var format314 = '\x00\x00' + // language - string16(segCount2) + - string16(searchRange) + - string16(searchEntry) + - string16(rangeShift) + + string16(2 * segCount) + + string16(searchParams.range) + + string16(searchParams.entry) + + string16(searchParams.rangeShift) + endCount + '\x00\x00' + startCount + idDeltas + idRangeOffsets + glyphsIds; @@ -16308,10 +16339,9 @@ var Font = (function FontClosure() { string32(format31012.length / 12); // nGroups } - return stringToArray(cmap + - '\x00\x04' + // format - string16(format314.length + 4) + // length - format314 + header31012 + format31012); + return cmap + '\x00\x04' + // format + string16(format314.length + 4) + // length + format314 + header31012 + format31012; } function validateOS2Table(os2) { @@ -16805,7 +16835,7 @@ var Font = (function FontClosure() { for (i = 0; i < numMissing; i++) { entries += '\x00\x00'; } - metrics.data = stringToArray(entries); + metrics.data = entries; } } @@ -17391,7 +17421,7 @@ var Font = (function FontClosure() { var tables = { 'OS/2': null, cmap: null, head: null, hhea: null, hmtx: null, maxp: null, name: null, post: null }; - var table, tableData; + var table; for (var i = 0; i < numTables; i++) { table = readTableEntry(font); if (VALID_TABLES.indexOf(table.tag) < 0) { @@ -17465,24 +17495,6 @@ var Font = (function FontClosure() { delete tables['cvt ']; } - // Tables needs to be written by ascendant alphabetic order - var tablesNames = Object.keys(tables); - tablesNames.sort(); - - numTables = tablesNames.length; - - // header and new offsets. Table entry information is appended to the - // end of file. The virtualOffset represents where to put the actual - // data of a particular table; - var ttf = { - file: '', - virtualOffset: numTables * (4 * 4) - }; - - // The new numbers of tables will be the last one plus the num - // of missing tables - createOpenTypeHeader(header.version, ttf, numTables); - // Ensure the hmtx table contains the advance width and // sidebearings information for numGlyphs in the maxp table sanitizeMetrics(font, tables.hhea, tables.hmtx, numGlyphs); @@ -17645,9 +17657,8 @@ var Font = (function FontClosure() { tables['OS/2'] = { tag: 'OS/2', - data: stringToArray(createOS2Table(properties, - newMapping.charCodeToGlyphId, - override)) + data: createOS2Table(properties, newMapping.charCodeToGlyphId, + override) }; } @@ -17655,7 +17666,7 @@ var Font = (function FontClosure() { if (!tables.post) { tables.post = { tag: 'post', - data: stringToArray(createPostTable(properties)) + data: createPostTable(properties) }; } @@ -17676,47 +17687,22 @@ var Font = (function FontClosure() { if (!tables.name) { tables.name = { tag: 'name', - data: stringToArray(createNameTable(this.name)) + data: createNameTable(this.name) }; } else { // ... using existing 'name' table as prototype var namePrototype = readNameTable(tables.name); - tables.name.data = stringToArray(createNameTable(name, namePrototype)); + tables.name.data = createNameTable(name, namePrototype); } - // rewrite the tables but tweak offsets - for (i = 0; i < numTables; i++) { - table = tables[tablesNames[i]]; - table.data = createTableEntry(ttf, table.tag, table.data); + var builder = new OpenTypeFileBuilder(header.version); + for (var tableTag in tables) { + builder.addTable(tableTag, tables[tableTag].data); } - - // Add the table datas - for (i = 0; i < numTables; i++) { - table = tables[tablesNames[i]]; - tableData = table.data; - ttf.file += bytesToString(new Uint8Array(tableData)); - - // 4-byte aligned data - while (ttf.file.length & 3) { - ttf.file += String.fromCharCode(0); - } - } - - return stringToArray(ttf.file); + return builder.toArray(); }, convert: function Font_convert(fontName, font, properties) { - // The offsets object holds at the same time a representation of where - // to write the table entry information about a table and another offset - // representing the offset where to draw the actual data of a particular - // table - var otf = { - file: '', - virtualOffset: 9 * (4 * 4) - }; - - createOpenTypeHeader('\x4F\x54\x54\x4F', otf, 9); - // TODO: Check the charstring widths to determine this. properties.fixedPitch = false; @@ -17796,20 +17782,16 @@ var Font = (function FontClosure() { var unitsPerEm = 1 / (properties.fontMatrix || FONT_IDENTITY_MATRIX)[0]; - var fields = { - // PostScript Font Program - 'CFF ': font.data, - - // OS/2 and Windows Specific metrics - 'OS/2': stringToArray(createOS2Table(properties, - newMapping.charCodeToGlyphId)), - - // Character to glyphs mapping - 'cmap': createCmapTable(newMapping.charCodeToGlyphId), - - // Font header - 'head': (function fontFieldsHead() { - return stringToArray( + var builder = new OpenTypeFileBuilder('\x4F\x54\x54\x4F'); + // PostScript Font Program + builder.addTable('CFF ', font.data); + // OS/2 and Windows Specific metrics + builder.addTable('OS/2', createOS2Table(properties, + newMapping.charCodeToGlyphId)); + // Character to glyphs mapping + builder.addTable('cmap', createCmapTable(newMapping.charCodeToGlyphId)); + // Font header + builder.addTable('head', '\x00\x01\x00\x00' + // Version number '\x00\x00\x10\x00' + // fontRevision '\x00\x00\x00\x00' + // checksumAdjustement @@ -17827,11 +17809,9 @@ var Font = (function FontClosure() { '\x00\x00' + // fontDirectionHint '\x00\x00' + // indexToLocFormat '\x00\x00'); // glyphDataFormat - })(), - // Horizontal header - 'hhea': (function fontFieldsHhea() { - return stringToArray( + // Horizontal header + builder.addTable('hhea', '\x00\x01\x00\x00' + // Version number safeString16(properties.ascent) + // Typographic Ascent safeString16(properties.descent) + // Typographic Descent @@ -17850,10 +17830,9 @@ var Font = (function FontClosure() { '\x00\x00' + // -reserved- '\x00\x00' + // metricDataFormat string16(numGlyphs)); // Number of HMetrics - })(), - // Horizontal metrics - 'hmtx': (function fontFieldsHmtx() { + // Horizontal metrics + builder.addTable('hmtx', (function fontFieldsHmtx() { var charstrings = font.charstrings; var hmtx = '\x00\x00\x00\x00'; // Fake .notdef for (var i = 1, ii = numGlyphs; i < ii; i++) { @@ -17863,33 +17842,21 @@ var Font = (function FontClosure() { var width = 'width' in charstring ? charstring.width : 0; hmtx += string16(width) + string16(0); } - return stringToArray(hmtx); - })(), + return hmtx; + })()); - // Maximum profile - 'maxp': (function fontFieldsMaxp() { - return stringToArray( + // Maximum profile + builder.addTable('maxp', '\x00\x00\x50\x00' + // Version number string16(numGlyphs)); // Num of glyphs - })(), - // Naming tables - 'name': stringToArray(createNameTable(fontName)), + // Naming tables + builder.addTable('name', createNameTable(fontName)); - // PostScript informations - 'post': stringToArray(createPostTable(properties)) - }; - - var field; - for (field in fields) { - fields[field] = createTableEntry(otf, field, fields[field]); - } - for (field in fields) { - var table = fields[field]; - otf.file += bytesToString(new Uint8Array(table)); - } + // PostScript informations + builder.addTable('post', createPostTable(properties)); - return stringToArray(otf.file); + return builder.toArray(); }, /** @@ -20439,7 +20406,7 @@ var CFFCompiler = (function CFFCompilerClosure() { compileNameIndex: function CFFCompiler_compileNameIndex(names) { var nameIndex = new CFFIndex(); for (var i = 0, ii = names.length; i < ii; ++i) { - nameIndex.add(stringToArray(names[i])); + nameIndex.add(stringToBytes(names[i])); } return this.compileIndex(nameIndex); }, @@ -20564,7 +20531,7 @@ var CFFCompiler = (function CFFCompilerClosure() { compileStringIndex: function CFFCompiler_compileStringIndex(strings) { var stringIndex = new CFFIndex(); for (var i = 0, ii = strings.length; i < ii; ++i) { - stringIndex.add(stringToArray(strings[i])); + stringIndex.add(stringToBytes(strings[i])); } return this.compileIndex(stringIndex); }, diff --git a/package.json b/package.json index 71ac724d4..459db2021 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "pdfjs-dist", - "version": "1.0.635", + "version": "1.0.639", "description": "Generic build of Mozilla's PDF.js library.", "keywords": [ "Mozilla",