|
|
@ -1032,9 +1032,9 @@ var Font = (function Font() { |
|
|
|
var end = denseRange[1]; |
|
|
|
var end = denseRange[1]; |
|
|
|
var index = firstCode; |
|
|
|
var index = firstCode; |
|
|
|
for (var j = start; j <= end; j++) { |
|
|
|
for (var j = start; j <= end; j++) { |
|
|
|
var code = j - firstCode - 1; |
|
|
|
var code = glyphs[j - start]; |
|
|
|
var mapping = encoding[index] || {}; |
|
|
|
var mapping = encoding[index] || {}; |
|
|
|
mapping.unicode = glyphs[code].unicode; |
|
|
|
mapping.unicode = code.unicode; |
|
|
|
encoding[index++] = mapping; |
|
|
|
encoding[index++] = mapping; |
|
|
|
} |
|
|
|
} |
|
|
|
return cmap.data = createCMapTable(glyphs); |
|
|
|
return cmap.data = createCMapTable(glyphs); |
|
|
@ -1043,6 +1043,39 @@ var Font = (function Font() { |
|
|
|
return cmap.data; |
|
|
|
return cmap.data; |
|
|
|
}; |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
function sanitizeMetrics(font, header, metrics, numGlyphs) { |
|
|
|
|
|
|
|
if (!header && !metrics) |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// The vhea/vmtx tables are not required, so it happens that
|
|
|
|
|
|
|
|
// some fonts embed a vmtx table without a vhea table. In this
|
|
|
|
|
|
|
|
// situation the sanitizer assume numOfLongVerMetrics = 1. As
|
|
|
|
|
|
|
|
// a result it tries to read numGlyphs - 1 SHORT from the vmtx
|
|
|
|
|
|
|
|
// table, and if it is not possible, the font is rejected.
|
|
|
|
|
|
|
|
// So remove the vmtx table if there is no vhea table.
|
|
|
|
|
|
|
|
if (!header && metrics) { |
|
|
|
|
|
|
|
metrics.data = null; |
|
|
|
|
|
|
|
return; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
font.pos = (font.start ? font.start : 0) + header.offset; |
|
|
|
|
|
|
|
font.pos += header.length - 2; |
|
|
|
|
|
|
|
var numOfMetrics = int16(font.getBytes(2)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var numOfSidebearings = numGlyphs - numOfMetrics; |
|
|
|
|
|
|
|
var numMissing = numOfSidebearings - |
|
|
|
|
|
|
|
((hmtx.length - numOfMetrics * 4) >> 1); |
|
|
|
|
|
|
|
if (numMissing > 0) { |
|
|
|
|
|
|
|
font.pos = (font.start ? font.start : 0) + metrics.offset; |
|
|
|
|
|
|
|
var entries = ''; |
|
|
|
|
|
|
|
for (var i = 0; i < hmtx.length; i++) |
|
|
|
|
|
|
|
entries += String.fromCharCode(font.getByte()); |
|
|
|
|
|
|
|
for (var i = 0; i < numMissing; i++) |
|
|
|
|
|
|
|
entries += '\x00\x00'; |
|
|
|
|
|
|
|
metrics.data = stringToArray(entries); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
// Check that required tables are present
|
|
|
|
// Check that required tables are present
|
|
|
|
var requiredTables = ['OS/2', 'cmap', 'head', 'hhea', |
|
|
|
var requiredTables = ['OS/2', 'cmap', 'head', 'hhea', |
|
|
|
'hmtx', 'maxp', 'name', 'post']; |
|
|
|
'hmtx', 'maxp', 'name', 'post']; |
|
|
@ -1050,7 +1083,7 @@ var Font = (function Font() { |
|
|
|
var header = readOpenTypeHeader(font); |
|
|
|
var header = readOpenTypeHeader(font); |
|
|
|
var numTables = header.numTables; |
|
|
|
var numTables = header.numTables; |
|
|
|
|
|
|
|
|
|
|
|
var cmap, maxp, hhea, hmtx; |
|
|
|
var cmap, maxp, hhea, hmtx, vhea, vmtx; |
|
|
|
var tables = []; |
|
|
|
var tables = []; |
|
|
|
for (var i = 0; i < numTables; i++) { |
|
|
|
for (var i = 0; i < numTables; i++) { |
|
|
|
var table = readTableEntry(font); |
|
|
|
var table = readTableEntry(font); |
|
|
@ -1066,6 +1099,11 @@ var Font = (function Font() { |
|
|
|
hmtx = table; |
|
|
|
hmtx = table; |
|
|
|
|
|
|
|
|
|
|
|
requiredTables.splice(index, 1); |
|
|
|
requiredTables.splice(index, 1); |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
if (table.tag == 'vmtx') |
|
|
|
|
|
|
|
vmtx = table; |
|
|
|
|
|
|
|
else if (table.tag == 'vhea') |
|
|
|
|
|
|
|
vhea = table; |
|
|
|
} |
|
|
|
} |
|
|
|
tables.push(table); |
|
|
|
tables.push(table); |
|
|
|
} |
|
|
|
} |
|
|
@ -1091,28 +1129,14 @@ var Font = (function Font() { |
|
|
|
}); |
|
|
|
}); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Ensure the hmtx tables contains an advance width and a sidebearing
|
|
|
|
// Ensure the [h/v]mtx tables contains the advance width and
|
|
|
|
// for the number of glyphs declared in the maxp table
|
|
|
|
// sidebearings information for numGlyphs in the maxp table
|
|
|
|
font.pos = (font.start ? font.start : 0) + maxp.offset; |
|
|
|
font.pos = (font.start ? font.start : 0) + maxp.offset; |
|
|
|
var version = int16(font.getBytes(4)); |
|
|
|
var version = int16(font.getBytes(4)); |
|
|
|
var numGlyphs = int16(font.getBytes(2)); |
|
|
|
var numGlyphs = int16(font.getBytes(2)); |
|
|
|
|
|
|
|
|
|
|
|
font.pos = (font.start ? font.start : 0) + hhea.offset; |
|
|
|
sanitizeMetrics(font, hhea, hmtx, numGlyphs); |
|
|
|
font.pos += hhea.length - 2; |
|
|
|
sanitizeMetrics(font, vhea, vmtx, numGlyphs); |
|
|
|
var numOfHMetrics = int16(font.getBytes(2)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var numOfSidebearings = numGlyphs - numOfHMetrics; |
|
|
|
|
|
|
|
var numMissing = numOfSidebearings - |
|
|
|
|
|
|
|
((hmtx.length - numOfHMetrics * 4) >> 1); |
|
|
|
|
|
|
|
if (numMissing > 0) { |
|
|
|
|
|
|
|
font.pos = (font.start ? font.start : 0) + hmtx.offset; |
|
|
|
|
|
|
|
var metrics = ''; |
|
|
|
|
|
|
|
for (var i = 0; i < hmtx.length; i++) |
|
|
|
|
|
|
|
metrics += String.fromCharCode(font.getByte()); |
|
|
|
|
|
|
|
for (var i = 0; i < numMissing; i++) |
|
|
|
|
|
|
|
metrics += '\x00\x00'; |
|
|
|
|
|
|
|
hmtx.data = stringToArray(metrics); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Sanitizer reduces the glyph advanceWidth to the maxAdvanceWidth
|
|
|
|
// Sanitizer reduces the glyph advanceWidth to the maxAdvanceWidth
|
|
|
|
// Sometimes it's 0. That needs to be fixed
|
|
|
|
// Sometimes it's 0. That needs to be fixed
|
|
|
|