Browse Source

Allocate fewer objects when parsing 2 and 4 byte chunks.

This is achieved by adding getBytes2() and getBytes4() to streams, and by
changing int16() and int32() to take multiple scalar args instead of an array
arg.
Nicholas Nethercote 11 years ago
parent
commit
6a75e45309
  1. 14
      src/core/chunked_stream.js
  2. 127
      src/core/fonts.js
  3. 24
      src/core/stream.js

14
src/core/chunked_stream.js

@ -140,6 +140,20 @@ var ChunkedStream = (function ChunkedStreamClosure() {
return this.bytes[this.pos++]; return this.bytes[this.pos++];
}, },
getUint16: function ChunkedStream_getUint16() {
var b0 = this.getByte();
var b1 = this.getByte();
return (b0 << 8) + b1;
},
getUint32: function ChunkedStream_getUint32() {
var b0 = this.getByte();
var b1 = this.getByte();
var b2 = this.getByte();
var b3 = this.getByte();
return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3;
},
// returns subarray of original buffer // returns subarray of original buffer
// should only be read // should only be read
getBytes: function ChunkedStream_getBytes(length) { getBytes: function ChunkedStream_getBytes(length) {

127
src/core/fonts.js

@ -2312,13 +2312,12 @@ var Font = (function FontClosure() {
return strBuf.join(''); return strBuf.join('');
} }
function int16(bytes) { function int16(b0, b1) {
return (bytes[0] << 8) + (bytes[1] & 0xff); return (b0 << 8) + b1;
} }
function int32(bytes) { function int32(b0, b1, b2, b3) {
return (bytes[0] << 24) + (bytes[1] << 16) + return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3;
(bytes[2] << 8) + (bytes[3] & 0xff);
} }
function getMaxPower2(number) { function getMaxPower2(number) {
@ -2397,8 +2396,8 @@ var Font = (function FontClosure() {
// checksum // checksum
var checksum = 0, n = data.length; var checksum = 0, n = data.length;
for (var i = 0; i < n; i += 4) for (var i = 0; i < n; i += 4)
checksum = (checksum + int32([data[i], data[i + 1], data[i + 2], checksum = (checksum + int32(data[i], data[i + 1], data[i + 2],
data[i + 3]])) | 0; data[i + 3])) | 0;
var tableEntry = (tag + string32(checksum) + var tableEntry = (tag + string32(checksum) +
string32(offset) + string32(length)); string32(offset) + string32(length));
@ -2626,21 +2625,21 @@ var Font = (function FontClosure() {
function validateOS2Table(os2) { function validateOS2Table(os2) {
var stream = new Stream(os2.data); var stream = new Stream(os2.data);
var version = int16(stream.getBytes(2)); var version = stream.getUint16();
// TODO verify all OS/2 tables fields, but currently we validate only those // TODO verify all OS/2 tables fields, but currently we validate only those
// that give us issues // that give us issues
stream.getBytes(60); // skipping type, misc sizes, panose, unicode ranges stream.getBytes(60); // skipping type, misc sizes, panose, unicode ranges
var selection = int16(stream.getBytes(2)); var selection = stream.getUint16();
if (version < 4 && (selection & 0x0300)) { if (version < 4 && (selection & 0x0300)) {
return false; return false;
} }
var firstChar = int16(stream.getBytes(2)); var firstChar = stream.getUint16();
var lastChar = int16(stream.getBytes(2)); var lastChar = stream.getUint16();
if (firstChar > lastChar) { if (firstChar > lastChar) {
return false; return false;
} }
stream.getBytes(6); // skipping sTypoAscender/Descender/LineGap stream.getBytes(6); // skipping sTypoAscender/Descender/LineGap
var usWinAscent = int16(stream.getBytes(2)); var usWinAscent = stream.getUint16();
if (usWinAscent === 0) { // makes font unreadable by windows if (usWinAscent === 0) { // makes font unreadable by windows
return false; return false;
} }
@ -2859,9 +2858,9 @@ var Font = (function FontClosure() {
String.fromCharCode(tag[2]) + String.fromCharCode(tag[2]) +
String.fromCharCode(tag[3]); String.fromCharCode(tag[3]);
var checksum = int32(file.getBytes(4)); var checksum = file.getUint32();
var offset = int32(file.getBytes(4)); var offset = file.getUint32();
var length = int32(file.getBytes(4)); var length = file.getUint32();
// Read the table associated data // Read the table associated data
var previousPosition = file.pos; var previousPosition = file.pos;
@ -2888,10 +2887,10 @@ var Font = (function FontClosure() {
function readOpenTypeHeader(ttf) { function readOpenTypeHeader(ttf) {
return { return {
version: arrayToString(ttf.getBytes(4)), version: arrayToString(ttf.getBytes(4)),
numTables: int16(ttf.getBytes(2)), numTables: ttf.getUint16(),
searchRange: int16(ttf.getBytes(2)), searchRange: ttf.getUint16(),
entrySelector: int16(ttf.getBytes(2)), entrySelector: ttf.getUint16(),
rangeShift: int16(ttf.getBytes(2)) rangeShift: ttf.getUint16()
}; };
} }
@ -2903,8 +2902,8 @@ var Font = (function FontClosure() {
var start = (font.start ? font.start : 0) + cmap.offset; var start = (font.start ? font.start : 0) + cmap.offset;
font.pos = start; font.pos = start;
var version = int16(font.getBytes(2)); var version = font.getUint16();
var numTables = int16(font.getBytes(2)); var numTables = font.getUint16();
var potentialTable; var potentialTable;
var canBreak = false; var canBreak = false;
@ -2915,9 +2914,9 @@ var Font = (function FontClosure() {
// The following takes advantage of the fact that the tables are sorted // The following takes advantage of the fact that the tables are sorted
// to work. // to work.
for (var i = 0; i < numTables; i++) { for (var i = 0; i < numTables; i++) {
var platformId = int16(font.getBytes(2)); var platformId = font.getUint16();
var encodingId = int16(font.getBytes(2)); var encodingId = font.getUint16();
var offset = int32(font.getBytes(4)); var offset = font.getUint32();
var useTable = false; var useTable = false;
if (platformId == 1 && encodingId === 0) { if (platformId == 1 && encodingId === 0) {
@ -2950,9 +2949,9 @@ var Font = (function FontClosure() {
} }
font.pos = start + potentialTable.offset; font.pos = start + potentialTable.offset;
var format = int16(font.getBytes(2)); var format = font.getUint16();
var length = int16(font.getBytes(2)); var length = font.getUint16();
var language = int16(font.getBytes(2)); var language = font.getUint16();
var hasShortCmap = false; var hasShortCmap = false;
var mappings = []; var mappings = [];
@ -2973,25 +2972,25 @@ var Font = (function FontClosure() {
} else if (format === 4) { } else if (format === 4) {
// re-creating the table in format 4 since the encoding // re-creating the table in format 4 since the encoding
// might be changed // might be changed
var segCount = (int16(font.getBytes(2)) >> 1); var segCount = (font.getUint16() >> 1);
font.getBytes(6); // skipping range fields font.getBytes(6); // skipping range fields
var segIndex, segments = []; var segIndex, segments = [];
for (segIndex = 0; segIndex < segCount; segIndex++) { for (segIndex = 0; segIndex < segCount; segIndex++) {
segments.push({ end: int16(font.getBytes(2)) }); segments.push({ end: font.getUint16() });
} }
font.getBytes(2); font.getUint16();
for (segIndex = 0; segIndex < segCount; segIndex++) { for (segIndex = 0; segIndex < segCount; segIndex++) {
segments[segIndex].start = int16(font.getBytes(2)); segments[segIndex].start = font.getUint16();
} }
for (segIndex = 0; segIndex < segCount; segIndex++) { for (segIndex = 0; segIndex < segCount; segIndex++) {
segments[segIndex].delta = int16(font.getBytes(2)); segments[segIndex].delta = font.getUint16();
} }
var offsetsCount = 0; var offsetsCount = 0;
for (segIndex = 0; segIndex < segCount; segIndex++) { for (segIndex = 0; segIndex < segCount; segIndex++) {
var segment = segments[segIndex]; var segment = segments[segIndex];
var rangeOffset = int16(font.getBytes(2)); var rangeOffset = font.getUint16();
if (!rangeOffset) { if (!rangeOffset) {
segment.offsetIndex = -1; segment.offsetIndex = -1;
continue; continue;
@ -3005,7 +3004,7 @@ var Font = (function FontClosure() {
var offsets = []; var offsets = [];
for (var j = 0; j < offsetsCount; j++) { for (var j = 0; j < offsetsCount; j++) {
offsets.push(int16(font.getBytes(2))); offsets.push(font.getUint16());
} }
for (segIndex = 0; segIndex < segCount; segIndex++) { for (segIndex = 0; segIndex < segCount; segIndex++) {
@ -3036,13 +3035,13 @@ var Font = (function FontClosure() {
// table. (This looks weird, so I can have missed something), this // table. (This looks weird, so I can have missed something), this
// works on Linux but seems to fails on Mac so let's rewrite the // works on Linux but seems to fails on Mac so let's rewrite the
// cmap table to a 3-1-4 style // cmap table to a 3-1-4 style
var firstCode = int16(font.getBytes(2)); var firstCode = font.getUint16();
var entryCount = int16(font.getBytes(2)); var entryCount = font.getUint16();
var glyphs = []; var glyphs = [];
var ids = []; var ids = [];
for (var j = 0; j < entryCount; j++) { for (var j = 0; j < entryCount; j++) {
var glyphId = int16(font.getBytes(2)); var glyphId = font.getUint16();
var charCode = firstCode + j; var charCode = firstCode + j;
mappings.push({ mappings.push({
@ -3083,7 +3082,7 @@ var Font = (function FontClosure() {
font.pos = (font.start ? font.start : 0) + header.offset; font.pos = (font.start ? font.start : 0) + header.offset;
font.pos += header.length - 2; font.pos += header.length - 2;
var numOfMetrics = int16(font.getBytes(2)); var numOfMetrics = font.getUint16();
if (numOfMetrics > numGlyphs) { if (numOfMetrics > numGlyphs) {
info('The numOfMetrics (' + numOfMetrics + ') should not be ' + info('The numOfMetrics (' + numOfMetrics + ') should not be ' +
@ -3187,7 +3186,7 @@ var Font = (function FontClosure() {
// Validate version: // Validate version:
// Should always be 0x00010000 // Should always be 0x00010000
var version = int32([data[0], data[1], data[2], data[3]]); var version = int32(data[0], data[1], data[2], data[3]);
if (version >> 16 !== 1) { if (version >> 16 !== 1) {
info('Attempting to fix invalid version in head table: ' + version); info('Attempting to fix invalid version in head table: ' + version);
data[0] = 0; data[0] = 0;
@ -3196,7 +3195,7 @@ var Font = (function FontClosure() {
data[3] = 0; data[3] = 0;
} }
var indexToLocFormat = int16([data[50], data[51]]); var indexToLocFormat = int16(data[50], data[51]);
if (indexToLocFormat < 0 || indexToLocFormat > 1) { if (indexToLocFormat < 0 || indexToLocFormat > 1) {
info('Attempting to fix invalid indexToLocFormat in head table: ' + info('Attempting to fix invalid indexToLocFormat in head table: ' +
indexToLocFormat); indexToLocFormat);
@ -3321,7 +3320,7 @@ var Font = (function FontClosure() {
font.pos = start; font.pos = start;
var length = post.length, end = start + length; var length = post.length, end = start + length;
var version = int32(font.getBytes(4)); var version = font.getUint32();
// skip rest to the tables // skip rest to the tables
font.getBytes(28); font.getBytes(28);
@ -3332,14 +3331,14 @@ var Font = (function FontClosure() {
glyphNames = MacStandardGlyphOrdering; glyphNames = MacStandardGlyphOrdering;
break; break;
case 0x00020000: case 0x00020000:
var numGlyphs = int16(font.getBytes(2)); var numGlyphs = font.getUint16();
if (numGlyphs != maxpNumGlyphs) { if (numGlyphs != maxpNumGlyphs) {
valid = false; valid = false;
break; break;
} }
var glyphNameIndexes = []; var glyphNameIndexes = [];
for (var i = 0; i < numGlyphs; ++i) { for (var i = 0; i < numGlyphs; ++i) {
var index = int16(font.getBytes(2)); var index = font.getUint16();
if (index >= 32768) { if (index >= 32768) {
valid = false; valid = false;
break; break;
@ -3385,25 +3384,25 @@ var Font = (function FontClosure() {
var names = [[], []]; var names = [[], []];
var length = nameTable.length, end = start + length; var length = nameTable.length, end = start + length;
var format = int16(font.getBytes(2)); var format = font.getUint16();
var FORMAT_0_HEADER_LENGTH = 6; var FORMAT_0_HEADER_LENGTH = 6;
if (format !== 0 || length < FORMAT_0_HEADER_LENGTH) { if (format !== 0 || length < FORMAT_0_HEADER_LENGTH) {
// unsupported name table format or table "too" small // unsupported name table format or table "too" small
return names; return names;
} }
var numRecords = int16(font.getBytes(2)); var numRecords = font.getUint16();
var stringsStart = int16(font.getBytes(2)); var stringsStart = font.getUint16();
var records = []; var records = [];
var NAME_RECORD_LENGTH = 12; var NAME_RECORD_LENGTH = 12;
for (var i = 0; i < numRecords && for (var i = 0; i < numRecords &&
font.pos + NAME_RECORD_LENGTH <= end; i++) { font.pos + NAME_RECORD_LENGTH <= end; i++) {
var r = { var r = {
platform: int16(font.getBytes(2)), platform: font.getUint16(),
encoding: int16(font.getBytes(2)), encoding: font.getUint16(),
language: int16(font.getBytes(2)), language: font.getUint16(),
name: int16(font.getBytes(2)), name: font.getUint16(),
length: int16(font.getBytes(2)), length: font.getUint16(),
offset: int16(font.getBytes(2)) offset: font.getUint16()
}; };
// using only Macintosh and Windows platform/encoding names // using only Macintosh and Windows platform/encoding names
if ((r.platform == 1 && r.encoding === 0 && r.language === 0) || if ((r.platform == 1 && r.encoding === 0 && r.language === 0) ||
@ -3424,7 +3423,7 @@ var Font = (function FontClosure() {
// unicode // unicode
var str = ''; var str = '';
for (var j = 0, jj = record.length; j < jj; j += 2) { for (var j = 0, jj = record.length; j < jj; j += 2) {
str += String.fromCharCode(int16(font.getBytes(2))); str += String.fromCharCode(font.getUint16());
} }
names[1][nameIndex] = str; names[1][nameIndex] = str;
} else { } else {
@ -3719,19 +3718,19 @@ var Font = (function FontClosure() {
} }
font.pos = (font.start || 0) + tables.maxp.offset; font.pos = (font.start || 0) + tables.maxp.offset;
var version = int32(font.getBytes(4)); var version = font.getUint32();
var numGlyphs = int16(font.getBytes(2)); var numGlyphs = font.getUint16();
var maxFunctionDefs = 0; var maxFunctionDefs = 0;
if (version >= 0x00010000 && tables.maxp.length >= 22) { if (version >= 0x00010000 && tables.maxp.length >= 22) {
// maxZones can be invalid // maxZones can be invalid
font.pos += 8; font.pos += 8;
var maxZones = int16(font.getBytes(2)); var maxZones = font.getUint16();
if (maxZones > 2) { // reset to 2 if font has invalid maxZones if (maxZones > 2) { // reset to 2 if font has invalid maxZones
tables.maxp.data[14] = 0; tables.maxp.data[14] = 0;
tables.maxp.data[15] = 2; tables.maxp.data[15] = 2;
} }
font.pos += 4; font.pos += 4;
maxFunctionDefs = int16(font.getBytes(2)); maxFunctionDefs = font.getUint16();
} }
var dupFirstEntry = false; var dupFirstEntry = false;
@ -3781,8 +3780,8 @@ var Font = (function FontClosure() {
sanitizeHead(tables.head, numGlyphs, isTrueType ? tables.loca.length : 0); sanitizeHead(tables.head, numGlyphs, isTrueType ? tables.loca.length : 0);
if (isTrueType) { if (isTrueType) {
var isGlyphLocationsLong = int16([tables.head.data[50], var isGlyphLocationsLong = int16(tables.head.data[50],
tables.head.data[51]]); tables.head.data[51]);
sanitizeGlyphLocations(tables.loca, tables.glyf, numGlyphs, sanitizeGlyphLocations(tables.loca, tables.glyf, numGlyphs,
isGlyphLocationsLong, hintsValid, dupFirstEntry); isGlyphLocationsLong, hintsValid, dupFirstEntry);
} }
@ -3926,11 +3925,11 @@ var Font = (function FontClosure() {
// extract some more font properties from the OpenType head and // extract some more font properties from the OpenType head and
// hhea tables; yMin and descent value are always negative // hhea tables; yMin and descent value are always negative
var override = { var override = {
unitsPerEm: int16([tables.head.data[18], tables.head.data[19]]), unitsPerEm: int16(tables.head.data[18], tables.head.data[19]),
yMax: int16([tables.head.data[42], tables.head.data[43]]), yMax: int16(tables.head.data[42], tables.head.data[43]),
yMin: int16([tables.head.data[38], tables.head.data[39]]) - 0x10000, yMin: int16(tables.head.data[38], tables.head.data[39]) - 0x10000,
ascent: int16([tables.hhea.data[4], tables.hhea.data[5]]), ascent: int16(tables.hhea.data[4], tables.hhea.data[5]),
descent: int16([tables.hhea.data[6], tables.hhea.data[7]]) - 0x10000 descent: int16(tables.hhea.data[6], tables.hhea.data[7]) - 0x10000
}; };
tables['OS/2'] = { tables['OS/2'] = {

24
src/core/stream.js

@ -40,6 +40,18 @@ var Stream = (function StreamClosure() {
return -1; return -1;
return this.bytes[this.pos++]; return this.bytes[this.pos++];
}, },
getUint16: function Stream_getUint16() {
var b0 = this.getByte();
var b1 = this.getByte();
return (b0 << 8) + b1;
},
getUint32: function Stream_getUint32() {
var b0 = this.getByte();
var b1 = this.getByte();
var b2 = this.getByte();
var b3 = this.getByte();
return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3;
},
// returns subarray of original buffer // returns subarray of original buffer
// should only be read // should only be read
getBytes: function Stream_getBytes(length) { getBytes: function Stream_getBytes(length) {
@ -143,6 +155,18 @@ var DecodeStream = (function DecodeStreamClosure() {
} }
return this.buffer[this.pos++]; return this.buffer[this.pos++];
}, },
getUint16: function DecodeStream_getUint16() {
var b0 = this.getByte();
var b1 = this.getByte();
return (b0 << 8) + b1;
},
getUint32: function DecodeStream_getUint32() {
var b0 = this.getByte();
var b1 = this.getByte();
var b2 = this.getByte();
var b3 = this.getByte();
return (b0 << 24) + (b1 << 16) + (b2 << 8) + b3;
},
getBytes: function DecodeStream_getBytes(length) { getBytes: function DecodeStream_getBytes(length) {
var end, pos = this.pos; var end, pos = this.pos;

Loading…
Cancel
Save