Merge pull request #458 from vingtetun/metrics
Fix for issue #441, #412, #379
This commit is contained in:
commit
e523e4dc56
254
fonts.js
254
fonts.js
@ -12,6 +12,9 @@ var kMaxWaitForFontFace = 1000;
|
|||||||
// Unicode Private Use Area
|
// Unicode Private Use Area
|
||||||
var kCmapGlyphOffset = 0xE000;
|
var kCmapGlyphOffset = 0xE000;
|
||||||
|
|
||||||
|
// Until hinting is fully supported this constant can be used
|
||||||
|
var kHintingEnabled = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hold a map of decoded fonts and of the standard fourteen Type1
|
* Hold a map of decoded fonts and of the standard fourteen Type1
|
||||||
* fonts and their acronyms.
|
* fonts and their acronyms.
|
||||||
@ -114,44 +117,6 @@ var serifFonts = {
|
|||||||
'Wide Latin': true, 'Windsor': true, 'XITS': true
|
'Wide Latin': true, 'Windsor': true, 'XITS': true
|
||||||
};
|
};
|
||||||
|
|
||||||
var FontMeasure = (function FontMeasure() {
|
|
||||||
var kScalePrecision = 30;
|
|
||||||
var ctx = document.createElement('canvas').getContext('2d');
|
|
||||||
ctx.scale(1 / kScalePrecision, 1);
|
|
||||||
|
|
||||||
var current;
|
|
||||||
var measureCache;
|
|
||||||
|
|
||||||
return {
|
|
||||||
setActive: function fonts_setActive(font, size) {
|
|
||||||
if (current == font) {
|
|
||||||
var sizes = current.sizes;
|
|
||||||
if (!(measureCache = sizes[size]))
|
|
||||||
measureCache = sizes[size] = Object.create(null);
|
|
||||||
} else {
|
|
||||||
measureCache = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var name = font.loadedName;
|
|
||||||
var bold = font.bold ? 'bold' : 'normal';
|
|
||||||
var italic = font.italic ? 'italic' : 'normal';
|
|
||||||
size *= kScalePrecision;
|
|
||||||
var rule = italic + ' ' + bold + ' ' + size + 'px "' + name + '"';
|
|
||||||
ctx.font = rule;
|
|
||||||
current = font;
|
|
||||||
},
|
|
||||||
measureText: function fonts_measureText(text) {
|
|
||||||
var width;
|
|
||||||
if (measureCache && (width = measureCache[text]))
|
|
||||||
return width;
|
|
||||||
width = ctx.measureText(text).width / kScalePrecision;
|
|
||||||
if (measureCache)
|
|
||||||
measureCache[text] = width;
|
|
||||||
return width;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
})();
|
|
||||||
|
|
||||||
var FontLoader = {
|
var FontLoader = {
|
||||||
listeningForFontLoad: false,
|
listeningForFontLoad: false,
|
||||||
|
|
||||||
@ -444,13 +409,12 @@ var Font = (function Font() {
|
|||||||
var constructor = function font_constructor(name, file, properties) {
|
var constructor = function font_constructor(name, file, properties) {
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.encoding = properties.encoding;
|
this.encoding = properties.encoding;
|
||||||
this.glyphs = properties.glyphs;
|
|
||||||
this.sizes = [];
|
this.sizes = [];
|
||||||
|
|
||||||
var names = name.split('+');
|
var names = name.split('+');
|
||||||
names = names.length > 1 ? names[1] : names[0];
|
names = names.length > 1 ? names[1] : names[0];
|
||||||
names = names.split(/[-,_]/g)[0];
|
names = names.split(/[-,_]/g)[0];
|
||||||
this.serif = serifFonts[names] || (name.indexOf('Serif') != -1);
|
this.serif = serifFonts[names] || (name.search(/serif/gi) != -1);
|
||||||
|
|
||||||
// If the font is to be ignored, register it like an already loaded font
|
// If the font is to be ignored, register it like an already loaded font
|
||||||
// to avoid the cost of waiting for it be be loaded by the platform.
|
// to avoid the cost of waiting for it be be loaded by the platform.
|
||||||
@ -464,14 +428,13 @@ var Font = (function Font() {
|
|||||||
// The file data is not specified. Trying to fix the font name
|
// The file data is not specified. Trying to fix the font name
|
||||||
// to be used with the canvas.font.
|
// to be used with the canvas.font.
|
||||||
var fontName = stdFontMap[name] || name.replace('_', '-');
|
var fontName = stdFontMap[name] || name.replace('_', '-');
|
||||||
this.bold = (fontName.indexOf('Bold') != -1);
|
this.bold = (fontName.search(/bold/gi) != -1);
|
||||||
this.italic = (fontName.indexOf('Oblique') != -1) ||
|
this.italic = (fontName.search(/oblique/gi) != -1) ||
|
||||||
(fontName.indexOf('Italic') != -1);
|
(fontName.search(/italic/gi) != -1);
|
||||||
|
|
||||||
// Use 'name' instead of 'fontName' here because the original
|
// Use 'name' instead of 'fontName' here because the original
|
||||||
// name ArialNarrow for example will be replaced by Helvetica.
|
// name ArialBlack for example will be replaced by Helvetica.
|
||||||
this.narrow = (name.indexOf('Narrow') != -1);
|
this.black = (name.search(/Black/g) != -1)
|
||||||
this.black = (name.indexOf('Black') != -1);
|
|
||||||
|
|
||||||
this.loadedName = fontName.split('-')[0];
|
this.loadedName = fontName.split('-')[0];
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
@ -711,7 +674,7 @@ var Font = (function Font() {
|
|||||||
|
|
||||||
var encoding = properties.encoding;
|
var encoding = properties.encoding;
|
||||||
for (var index in encoding) {
|
for (var index in encoding) {
|
||||||
var code = encoding[index];
|
var code = encoding[index].unicode;
|
||||||
if (firstCharIndex > code || !firstCharIndex)
|
if (firstCharIndex > code || !firstCharIndex)
|
||||||
firstCharIndex = code;
|
firstCharIndex = code;
|
||||||
if (lastCharIndex < code)
|
if (lastCharIndex < code)
|
||||||
@ -970,15 +933,11 @@ var Font = (function Font() {
|
|||||||
if (index) {
|
if (index) {
|
||||||
deltas.push(index);
|
deltas.push(index);
|
||||||
|
|
||||||
var code = encoding[index];
|
|
||||||
for (var glyph in properties.glyphs) {
|
|
||||||
if (properties.glyphs[glyph] == code)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var unicode = j + kCmapGlyphOffset;
|
var unicode = j + kCmapGlyphOffset;
|
||||||
properties.glyphs[glyph] = encoding[j] = unicode;
|
var mapping = encoding[j] || {};
|
||||||
glyphs.push({ glyph: glyph, unicode: unicode });
|
mapping.unicode = unicode;
|
||||||
|
encoding[j] = mapping;
|
||||||
|
glyphs.push({ unicode: unicode });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -993,44 +952,55 @@ var Font = (function Font() {
|
|||||||
var entryCount = int16(font.getBytes(2));
|
var entryCount = int16(font.getBytes(2));
|
||||||
|
|
||||||
var glyphs = [];
|
var glyphs = [];
|
||||||
var min = 0xffff, max = 0;
|
var ids = [];
|
||||||
for (var j = 0; j < entryCount; j++) {
|
for (var j = 0; j < firstCode + entryCount; j++) {
|
||||||
var charcode = int16(font.getBytes(2));
|
var code = (j >= firstCode) ? int16(font.getBytes(2)) : j;
|
||||||
if (!charcode)
|
glyphs.push({ unicode: j + kCmapGlyphOffset });
|
||||||
continue;
|
ids.push(code);
|
||||||
glyphs.push(charcode);
|
|
||||||
|
|
||||||
if (charcode < min)
|
var mapping = encoding[j] || {};
|
||||||
min = charcode;
|
mapping.unicode = glyphs[j].unicode;
|
||||||
if (charcode > max)
|
encoding[j] = mapping;
|
||||||
max = charcode;
|
|
||||||
}
|
}
|
||||||
|
return cmap.data = createCMapTable(glyphs, ids);
|
||||||
// Since Format 6 is a dense array, check for gaps
|
|
||||||
for (var j = min; j < max; j++) {
|
|
||||||
if (glyphs.indexOf(j) == -1)
|
|
||||||
glyphs.push(j);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var j = 0; j < glyphs.length; j++)
|
|
||||||
glyphs[j] = { unicode: glyphs[j] + firstCode };
|
|
||||||
|
|
||||||
var ranges = getRanges(glyphs);
|
|
||||||
assert(ranges.length == 1, 'Got ' + ranges.length +
|
|
||||||
' ranges in a dense array');
|
|
||||||
|
|
||||||
var denseRange = ranges[0];
|
|
||||||
var start = denseRange[0];
|
|
||||||
var end = denseRange[1];
|
|
||||||
var index = firstCode;
|
|
||||||
for (var j = start; j <= end; j++)
|
|
||||||
encoding[index++] = glyphs[j - firstCode - 1].unicode;
|
|
||||||
return cmap.data = createCMapTable(glyphs);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
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'];
|
||||||
@ -1038,7 +1008,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);
|
||||||
@ -1054,6 +1024,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);
|
||||||
}
|
}
|
||||||
@ -1079,28 +1054,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
|
||||||
@ -1118,23 +1079,6 @@ var Font = (function Font() {
|
|||||||
// U+00AD (soft hyphen) is not drawn.
|
// U+00AD (soft hyphen) is not drawn.
|
||||||
// So, offset all the glyphs by 0xFF to avoid these cases and use
|
// So, offset all the glyphs by 0xFF to avoid these cases and use
|
||||||
// the encoding to map incoming characters to the new glyph positions
|
// the encoding to map incoming characters to the new glyph positions
|
||||||
|
|
||||||
var glyphs = [];
|
|
||||||
var encoding = properties.encoding;
|
|
||||||
|
|
||||||
for (var i = 1; i < numGlyphs; i++)
|
|
||||||
glyphs.push({ unicode: i + kCmapGlyphOffset });
|
|
||||||
|
|
||||||
if ('undefined' == typeof(encoding[0])) {
|
|
||||||
// the font is directly characters to glyphs with no encoding
|
|
||||||
// so create an identity encoding
|
|
||||||
for (i = 0; i < numGlyphs; i++)
|
|
||||||
encoding[i] = i + kCmapGlyphOffset;
|
|
||||||
} else {
|
|
||||||
for (var code in encoding)
|
|
||||||
encoding[code] += kCmapGlyphOffset;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!cmap) {
|
if (!cmap) {
|
||||||
cmap = {
|
cmap = {
|
||||||
tag: 'cmap',
|
tag: 'cmap',
|
||||||
@ -1142,6 +1086,27 @@ var Font = (function Font() {
|
|||||||
};
|
};
|
||||||
tables.push(cmap);
|
tables.push(cmap);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var encoding = properties.encoding;
|
||||||
|
if (!encoding[0]) {
|
||||||
|
// the font is directly characters to glyphs with no encoding
|
||||||
|
// so create an identity encoding
|
||||||
|
var widths = properties.widths;
|
||||||
|
for (i = 0; i < numGlyphs; i++) {
|
||||||
|
var width = widths[i];
|
||||||
|
encoding[i] = {
|
||||||
|
unicode: i + kCmapGlyphOffset,
|
||||||
|
width: IsNum(width) ? width : properties.defaultWidth
|
||||||
|
};
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (var code in encoding)
|
||||||
|
encoding[code].unicode += kCmapGlyphOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
var glyphs = [];
|
||||||
|
for (var i = 1; i < numGlyphs; i++)
|
||||||
|
glyphs.push({ unicode: i + kCmapGlyphOffset });
|
||||||
cmap.data = createCMapTable(glyphs);
|
cmap.data = createCMapTable(glyphs);
|
||||||
} else {
|
} else {
|
||||||
replaceCMapTable(cmap, font, properties);
|
replaceCMapTable(cmap, font, properties);
|
||||||
@ -1362,6 +1327,12 @@ var Font = (function Font() {
|
|||||||
for (var i = 0; i < length; i++) {
|
for (var i = 0; i < length; i++) {
|
||||||
var charcode = int16([chars.charCodeAt(i++), chars.charCodeAt(i)]);
|
var charcode = int16([chars.charCodeAt(i++), chars.charCodeAt(i)]);
|
||||||
var unicode = encoding[charcode];
|
var unicode = encoding[charcode];
|
||||||
|
if ('undefined' == typeof(unicode)) {
|
||||||
|
warn('Unencoded charcode ' + charcode);
|
||||||
|
unicode = charcode;
|
||||||
|
} else {
|
||||||
|
unicode = unicode.unicode;
|
||||||
|
}
|
||||||
str += String.fromCharCode(unicode);
|
str += String.fromCharCode(unicode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1372,12 +1343,10 @@ var Font = (function Font() {
|
|||||||
if ('undefined' == typeof(unicode)) {
|
if ('undefined' == typeof(unicode)) {
|
||||||
warn('Unencoded charcode ' + charcode);
|
warn('Unencoded charcode ' + charcode);
|
||||||
unicode = charcode;
|
unicode = charcode;
|
||||||
|
} else {
|
||||||
|
unicode = unicode.unicode;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if the glyph has already been converted
|
|
||||||
if (!IsNum(unicode))
|
|
||||||
unicode = encoding[charcode] = this.glyphs[unicode];
|
|
||||||
|
|
||||||
// Handle surrogate pairs
|
// Handle surrogate pairs
|
||||||
if (unicode > 0xFFFF) {
|
if (unicode > 0xFFFF) {
|
||||||
str += String.fromCharCode(unicode & 0xFFFF);
|
str += String.fromCharCode(unicode & 0xFFFF);
|
||||||
@ -1562,6 +1531,9 @@ var Type1Parser = function() {
|
|||||||
i++;
|
i++;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
} else if (!kHintingEnabled && (value == 1 || value == 2)) {
|
||||||
|
charstring.push('drop', 'drop', 'drop', 'drop', 'drop', 'drop');
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
command = charStringDictionary['12'][escape];
|
command = charStringDictionary['12'][escape];
|
||||||
@ -1586,6 +1558,9 @@ var Type1Parser = function() {
|
|||||||
|
|
||||||
charstring.push(lsb, 'hmoveto');
|
charstring.push(lsb, 'hmoveto');
|
||||||
continue;
|
continue;
|
||||||
|
} else if (!kHintingEnabled && (value == 1 || value == 3)) {
|
||||||
|
charstring.push('drop', 'drop');
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
command = charStringDictionary[value];
|
command = charStringDictionary[value];
|
||||||
}
|
}
|
||||||
@ -1830,8 +1805,9 @@ var Type1Parser = function() {
|
|||||||
var glyph = getToken();
|
var glyph = getToken();
|
||||||
|
|
||||||
if ('undefined' == typeof(properties.differences[index])) {
|
if ('undefined' == typeof(properties.differences[index])) {
|
||||||
properties.encoding[index] = glyph;
|
var mapping = properties.encoding[index] || {};
|
||||||
properties.glyphs[glyph] = GlyphsUnicode[glyph] || index;
|
mapping.unicode = GlyphsUnicode[glyph] || index;
|
||||||
|
properties.glyphs[glyph] = properties.encoding[index] = mapping;
|
||||||
}
|
}
|
||||||
getToken(); // read the in 'put'
|
getToken(); // read the in 'put'
|
||||||
}
|
}
|
||||||
@ -2000,14 +1976,14 @@ CFF.prototype = {
|
|||||||
|
|
||||||
for (var i = 0; i < glyphs.length; i++) {
|
for (var i = 0; i < glyphs.length; i++) {
|
||||||
var glyph = glyphs[i];
|
var glyph = glyphs[i];
|
||||||
var unicode = properties.glyphs[glyph.glyph];
|
var mapping = properties.glyphs[glyph.glyph];
|
||||||
if (!unicode) {
|
if (!mapping) {
|
||||||
if (glyph.glyph != '.notdef')
|
if (glyph.glyph != '.notdef')
|
||||||
missings.push(glyph.glyph);
|
missings.push(glyph.glyph);
|
||||||
} else {
|
} else {
|
||||||
charstrings.push({
|
charstrings.push({
|
||||||
glyph: glyph.glyph,
|
glyph: glyph.glyph,
|
||||||
unicode: unicode,
|
unicode: mapping.unicode,
|
||||||
charstring: glyph.data,
|
charstring: glyph.data,
|
||||||
width: glyph.width,
|
width: glyph.width,
|
||||||
lsb: glyph.lsb
|
lsb: glyph.lsb
|
||||||
@ -2321,8 +2297,6 @@ var Type2CFF = (function() {
|
|||||||
|
|
||||||
getCharStrings: function cff_charstrings(charsets, charStrings,
|
getCharStrings: function cff_charstrings(charsets, charStrings,
|
||||||
privDict, properties) {
|
privDict, properties) {
|
||||||
var widths = properties.widths;
|
|
||||||
|
|
||||||
var defaultWidth = privDict['defaultWidthX'];
|
var defaultWidth = privDict['defaultWidthX'];
|
||||||
var nominalWidth = privDict['nominalWidthX'];
|
var nominalWidth = privDict['nominalWidthX'];
|
||||||
|
|
||||||
@ -2340,17 +2314,23 @@ var Type2CFF = (function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var mapping = properties.glyphs[glyph] || {};
|
||||||
if (code == -1)
|
if (code == -1)
|
||||||
index = code = properties.glyphs[glyph] || index;
|
index = code = mapping.unicode || index;
|
||||||
|
|
||||||
var width = widths[code] || defaultWidth;
|
|
||||||
if (code <= 0x1f || (code >= 127 && code <= 255))
|
if (code <= 0x1f || (code >= 127 && code <= 255))
|
||||||
code += kCmapGlyphOffset;
|
code += kCmapGlyphOffset;
|
||||||
|
|
||||||
properties.encoding[index] = code;
|
var width = mapping.width;
|
||||||
|
properties.glyphs[glyph] = properties.encoding[index] = {
|
||||||
|
unicode: code,
|
||||||
|
width: IsNum(width) ? width : defaultWidth
|
||||||
|
};
|
||||||
|
|
||||||
charstrings.push({
|
charstrings.push({
|
||||||
unicode: code,
|
unicode: code,
|
||||||
width: width, gid: i
|
width: width,
|
||||||
|
gid: i
|
||||||
});
|
});
|
||||||
index++;
|
index++;
|
||||||
}
|
}
|
||||||
|
2938
metrics.js
Normal file
2938
metrics.js
Normal file
File diff suppressed because it is too large
Load Diff
169
pdf.js
169
pdf.js
@ -4226,8 +4226,32 @@ var PartialEvaluator = (function() {
|
|||||||
extractEncoding: function(dict, xref, properties) {
|
extractEncoding: function(dict, xref, properties) {
|
||||||
var type = properties.type;
|
var type = properties.type;
|
||||||
if (properties.composite) {
|
if (properties.composite) {
|
||||||
// XXX only CIDFontType2 supported for now
|
|
||||||
if (type == 'CIDFontType2') {
|
if (type == 'CIDFontType2') {
|
||||||
|
var defaultWidth = xref.fetchIfRef(dict.get('DW')) || 1000;
|
||||||
|
properties.defaultWidth = defaultWidth;
|
||||||
|
|
||||||
|
var glyphsWidths = {};
|
||||||
|
var widths = xref.fetchIfRef(dict.get('W'));
|
||||||
|
if (widths) {
|
||||||
|
var start = 0, end = 0;
|
||||||
|
for (var i = 0; i < widths.length; i++) {
|
||||||
|
var code = widths[i];
|
||||||
|
if (IsArray(code)) {
|
||||||
|
for (var j = 0; j < code.length; j++)
|
||||||
|
glyphsWidths[start++] = code[j];
|
||||||
|
start = 0;
|
||||||
|
} else if (start) {
|
||||||
|
var width = widths[++i];
|
||||||
|
for (var j = start; j <= code; j++)
|
||||||
|
glyphsWidths[j] = width;
|
||||||
|
start = 0;
|
||||||
|
} else {
|
||||||
|
start = code;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
properties.widths = glyphsWidths;
|
||||||
|
|
||||||
var cidToGidMap = dict.get('CIDToGIDMap');
|
var cidToGidMap = dict.get('CIDToGIDMap');
|
||||||
if (!cidToGidMap || !IsRef(cidToGidMap))
|
if (!cidToGidMap || !IsRef(cidToGidMap))
|
||||||
return GlyphsUnicode;
|
return GlyphsUnicode;
|
||||||
@ -4237,13 +4261,21 @@ var PartialEvaluator = (function() {
|
|||||||
var glyphsData = glyphsStream.getBytes(0);
|
var glyphsData = glyphsStream.getBytes(0);
|
||||||
|
|
||||||
// Glyph ids are big-endian 2-byte values
|
// Glyph ids are big-endian 2-byte values
|
||||||
// Set this to 0 to verify the font has an encoding.
|
|
||||||
var encoding = properties.encoding;
|
var encoding = properties.encoding;
|
||||||
encoding[0] = 0;
|
|
||||||
|
// Set encoding 0 to later verify the font has an encoding
|
||||||
|
encoding[0] = { unicode: 0, width: 0 };
|
||||||
for (var j = 0; j < glyphsData.length; j++) {
|
for (var j = 0; j < glyphsData.length; j++) {
|
||||||
var glyphID = (glyphsData[j++] << 8) | glyphsData[j];
|
var glyphID = (glyphsData[j++] << 8) | glyphsData[j];
|
||||||
if (glyphID != 0)
|
if (glyphID == 0)
|
||||||
encoding[j >> 1] = glyphID;
|
continue;
|
||||||
|
|
||||||
|
var code = j >> 1;
|
||||||
|
var width = glyphsWidths[code];
|
||||||
|
encoding[code] = {
|
||||||
|
unicode: glyphID,
|
||||||
|
width: IsNum(width) ? width : defaultWidth
|
||||||
|
};
|
||||||
}
|
}
|
||||||
} else if (type == 'CIDFontType0') {
|
} else if (type == 'CIDFontType0') {
|
||||||
var encoding = xref.fetchIfRef(dict.get('Encoding'));
|
var encoding = xref.fetchIfRef(dict.get('Encoding'));
|
||||||
@ -4310,19 +4342,24 @@ var PartialEvaluator = (function() {
|
|||||||
var glyphs = {};
|
var glyphs = {};
|
||||||
for (var i = firstChar; i <= lastChar; i++) {
|
for (var i = firstChar; i <= lastChar; i++) {
|
||||||
var glyph = differences[i] || baseEncoding[i];
|
var glyph = differences[i] || baseEncoding[i];
|
||||||
if (glyph) {
|
var index = GlyphsUnicode[glyph] || i;
|
||||||
var index = GlyphsUnicode[glyph] || i;
|
var width = properties.widths[i] || properties.widths[glyph];
|
||||||
glyphs[glyph] = map[i] = index;
|
map[i] = {
|
||||||
|
unicode: index,
|
||||||
|
width: IsNum(width) ? width : properties.defaultWidth
|
||||||
|
};
|
||||||
|
|
||||||
// If there is no file, the character mapping can't be modified
|
if (glyph)
|
||||||
// but this is unlikely that there is any standard encoding with
|
glyphs[glyph] = map[i];
|
||||||
// chars below 0x1f, so that's fine.
|
|
||||||
if (!properties.file)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (index <= 0x1f || (index >= 127 && index <= 255))
|
// If there is no file, the character mapping can't be modified
|
||||||
glyphs[glyph] = map[i] += kCmapGlyphOffset;
|
// but this is unlikely that there is any standard encoding with
|
||||||
}
|
// chars below 0x1f, so that's fine.
|
||||||
|
if (!properties.file)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (index <= 0x1f || (index >= 127 && index <= 255))
|
||||||
|
map[i].unicode += kCmapGlyphOffset;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type == 'TrueType' && dict.has('ToUnicode') && differences) {
|
if (type == 'TrueType' && dict.has('ToUnicode') && differences) {
|
||||||
@ -4359,7 +4396,9 @@ var PartialEvaluator = (function() {
|
|||||||
var endRange = tokens[j + 1];
|
var endRange = tokens[j + 1];
|
||||||
var code = tokens[j + 2];
|
var code = tokens[j + 2];
|
||||||
while (startRange < endRange) {
|
while (startRange < endRange) {
|
||||||
map[startRange] = code++;
|
var mapping = map[startRange] || {};
|
||||||
|
mapping.unicode = code++;
|
||||||
|
map[startRange] = mapping;
|
||||||
++startRange;
|
++startRange;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4370,7 +4409,9 @@ var PartialEvaluator = (function() {
|
|||||||
for (var j = 0; j < tokens.length; j += 2) {
|
for (var j = 0; j < tokens.length; j += 2) {
|
||||||
var index = tokens[j];
|
var index = tokens[j];
|
||||||
var code = tokens[j + 1];
|
var code = tokens[j + 1];
|
||||||
map[index] = code;
|
var mapping = map[index] || {};
|
||||||
|
mapping.unicode = code;
|
||||||
|
map[index] = mapping;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@ -4439,7 +4480,8 @@ var PartialEvaluator = (function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Before PDF 1.5 if the font was one of the base 14 fonts, having a
|
// Before PDF 1.5 if the font was one of the base 14 fonts, having a
|
||||||
// FontDescriptor was not required. This case is here for compatibility.
|
// FontDescriptor was not required.
|
||||||
|
// This case is here for compatibility.
|
||||||
var descriptor = xref.fetchIfRef(dict.get('FontDescriptor'));
|
var descriptor = xref.fetchIfRef(dict.get('FontDescriptor'));
|
||||||
if (!descriptor) {
|
if (!descriptor) {
|
||||||
var baseFontName = dict.get('BaseFont');
|
var baseFontName = dict.get('BaseFont');
|
||||||
@ -4459,10 +4501,18 @@ var PartialEvaluator = (function() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var defaultWidth = 0;
|
||||||
|
var widths = Metrics[stdFontMap[baseFontName] || baseFontName];
|
||||||
|
if (IsNum(widths)) {
|
||||||
|
defaultWidth = widths;
|
||||||
|
widths = null;
|
||||||
|
}
|
||||||
var properties = {
|
var properties = {
|
||||||
type: type.name,
|
type: type.name,
|
||||||
encoding: map,
|
encoding: map,
|
||||||
differences: [],
|
differences: [],
|
||||||
|
widths: widths,
|
||||||
|
defaultWidth: defaultWidth,
|
||||||
firstChar: 0,
|
firstChar: 0,
|
||||||
lastChar: 256
|
lastChar: 256
|
||||||
};
|
};
|
||||||
@ -4521,19 +4571,18 @@ var PartialEvaluator = (function() {
|
|||||||
descent: descriptor.get('Descent'),
|
descent: descriptor.get('Descent'),
|
||||||
xHeight: descriptor.get('XHeight'),
|
xHeight: descriptor.get('XHeight'),
|
||||||
capHeight: descriptor.get('CapHeight'),
|
capHeight: descriptor.get('CapHeight'),
|
||||||
|
defaultWidth: parseFloat(descriptor.get('MissingWidth')) || 0,
|
||||||
flags: descriptor.get('Flags'),
|
flags: descriptor.get('Flags'),
|
||||||
italicAngle: descriptor.get('ItalicAngle'),
|
italicAngle: descriptor.get('ItalicAngle'),
|
||||||
differences: [],
|
differences: [],
|
||||||
widths: [],
|
widths: (function() {
|
||||||
|
var glyphWidths = {};
|
||||||
|
for (var i = 0; i < widths.length; i++)
|
||||||
|
glyphWidths[firstChar++] = widths[i];
|
||||||
|
return glyphWidths;
|
||||||
|
})(),
|
||||||
encoding: {}
|
encoding: {}
|
||||||
};
|
};
|
||||||
|
|
||||||
// XXX Encoding and Glyphs should point to the same object so it will
|
|
||||||
// be hard to be out of sync. The object could contains the unicode and
|
|
||||||
// the width of the glyph.
|
|
||||||
for (var i = 0; i <= widths.length; i++)
|
|
||||||
properties.widths[firstChar++] = widths[i];
|
|
||||||
|
|
||||||
properties.glyphs = this.extractEncoding(dict, xref, properties);
|
properties.glyphs = this.extractEncoding(dict, xref, properties);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
@ -4596,9 +4645,6 @@ function ScratchCanvas(width, height) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var CanvasGraphics = (function() {
|
var CanvasGraphics = (function() {
|
||||||
var kScalePrecision = 50;
|
|
||||||
var kRasterizerMin = 14;
|
|
||||||
|
|
||||||
function constructor(canvasCtx, imageCanvas) {
|
function constructor(canvasCtx, imageCanvas) {
|
||||||
this.ctx = canvasCtx;
|
this.ctx = canvasCtx;
|
||||||
this.current = new CanvasExtraState();
|
this.current = new CanvasExtraState();
|
||||||
@ -4859,22 +4905,13 @@ var CanvasGraphics = (function() {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
var fontObj = font.fontObj;
|
var fontObj = font.fontObj;
|
||||||
var name = fontObj.loadedName;
|
|
||||||
if (!name) {
|
|
||||||
// TODO: fontDescriptor is not available, fallback to default font
|
|
||||||
name = 'sans-serif';
|
|
||||||
}
|
|
||||||
|
|
||||||
this.current.font = fontObj;
|
this.current.font = fontObj;
|
||||||
this.current.fontSize = size;
|
this.current.fontSize = size;
|
||||||
|
|
||||||
|
var name = fontObj.loadedName || 'sans-serif';
|
||||||
if (this.ctx.$setFont) {
|
if (this.ctx.$setFont) {
|
||||||
this.ctx.$setFont(name, size);
|
this.ctx.$setFont(name, size);
|
||||||
} else {
|
} else {
|
||||||
FontMeasure.setActive(fontObj, size);
|
|
||||||
|
|
||||||
size = (size <= kRasterizerMin) ? size * kScalePrecision : size;
|
|
||||||
|
|
||||||
var bold = fontObj.black ? (fontObj.bold ? 'bolder' : 'bold') :
|
var bold = fontObj.black ? (fontObj.bold ? 'bolder' : 'bold') :
|
||||||
(fontObj.bold ? 'bold' : 'normal');
|
(fontObj.bold ? 'bold' : 'normal');
|
||||||
|
|
||||||
@ -4917,6 +4954,7 @@ var CanvasGraphics = (function() {
|
|||||||
showText: function(text) {
|
showText: function(text) {
|
||||||
var ctx = this.ctx;
|
var ctx = this.ctx;
|
||||||
var current = this.current;
|
var current = this.current;
|
||||||
|
var originalText = text;
|
||||||
|
|
||||||
ctx.save();
|
ctx.save();
|
||||||
ctx.transform.apply(ctx, current.textMatrix);
|
ctx.transform.apply(ctx, current.textMatrix);
|
||||||
@ -4924,47 +4962,40 @@ var CanvasGraphics = (function() {
|
|||||||
|
|
||||||
ctx.translate(current.x, -1 * current.y);
|
ctx.translate(current.x, -1 * current.y);
|
||||||
|
|
||||||
var scaleFactorX = 1, scaleFactorY = 1;
|
|
||||||
var font = current.font;
|
var font = current.font;
|
||||||
if (font) {
|
if (font) {
|
||||||
if (current.fontSize <= kRasterizerMin) {
|
|
||||||
scaleFactorX = scaleFactorY = kScalePrecision;
|
|
||||||
ctx.scale(1 / scaleFactorX, 1 / scaleFactorY);
|
|
||||||
}
|
|
||||||
ctx.transform.apply(ctx, font.textMatrix || IDENTITY_MATRIX);
|
ctx.transform.apply(ctx, font.textMatrix || IDENTITY_MATRIX);
|
||||||
text = font.charsToUnicode(text);
|
text = font.charsToUnicode(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var composite = font.composite;
|
||||||
|
var encoding = font.encoding;
|
||||||
|
var fontSize = current.fontSize;
|
||||||
var charSpacing = current.charSpacing;
|
var charSpacing = current.charSpacing;
|
||||||
var wordSpacing = current.wordSpacing;
|
var wordSpacing = current.wordSpacing;
|
||||||
var textHScale = current.textHScale;
|
var textHScale = current.textHScale;
|
||||||
|
ctx.scale(1 / textHScale, 1);
|
||||||
|
|
||||||
// This is a poor simulation for Arial Narrow while font-stretch
|
var width = 0;
|
||||||
// is not implemented (bug 3512)
|
for (var i = 0; i < text.length; i++) {
|
||||||
if (current.font.narrow) {
|
if (composite) {
|
||||||
textHScale += 0.2;
|
var position = i * 2 + 1;
|
||||||
charSpacing -= (0.09 * current.fontSize);
|
var charcode = (originalText.charCodeAt(position - 1) << 8) +
|
||||||
}
|
originalText.charCodeAt(position);
|
||||||
|
} else {
|
||||||
if (charSpacing != 0 || wordSpacing != 0 || textHScale != 1) {
|
var charcode = originalText.charCodeAt(i);
|
||||||
scaleFactorX *= textHScale;
|
|
||||||
ctx.scale(1 / textHScale, 1);
|
|
||||||
var width = 0;
|
|
||||||
|
|
||||||
for (var i = 0, ii = text.length; i < ii; ++i) {
|
|
||||||
var c = text.charAt(i);
|
|
||||||
ctx.fillText(c, 0, 0);
|
|
||||||
var charWidth = FontMeasure.measureText(c) + charSpacing;
|
|
||||||
if (c.charCodeAt(0) == 32)
|
|
||||||
charWidth += wordSpacing;
|
|
||||||
ctx.translate(charWidth * scaleFactorX, 0);
|
|
||||||
width += charWidth;
|
|
||||||
}
|
}
|
||||||
current.x += width;
|
|
||||||
} else {
|
var charWidth = font.encoding[charcode].width * fontSize * 0.001;
|
||||||
ctx.fillText(text, 0, 0);
|
charWidth += charSpacing;
|
||||||
current.x += FontMeasure.measureText(text);
|
if (charcode == 32)
|
||||||
|
charWidth += wordSpacing;
|
||||||
|
|
||||||
|
ctx.fillText(text.charAt(i), 0, 0);
|
||||||
|
ctx.translate(charWidth, 0);
|
||||||
|
width += charWidth;
|
||||||
}
|
}
|
||||||
|
current.x += width;
|
||||||
|
|
||||||
this.ctx.restore();
|
this.ctx.restore();
|
||||||
},
|
},
|
||||||
|
@ -10,6 +10,7 @@
|
|||||||
<script type="text/javascript" src="../fonts.js"></script>
|
<script type="text/javascript" src="../fonts.js"></script>
|
||||||
<script type="text/javascript" src="../crypto.js"></script>
|
<script type="text/javascript" src="../crypto.js"></script>
|
||||||
<script type="text/javascript" src="../glyphlist.js"></script>
|
<script type="text/javascript" src="../glyphlist.js"></script>
|
||||||
|
<script type="text/javascript" src="../metrics.js"></script>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user