Use the built-in widths to calculate glyphs metrics

This commit is contained in:
Vivien Nicolas 2011-09-09 01:35:37 +02:00
parent f759e56a02
commit d4fb9c786c
4 changed files with 3040 additions and 60 deletions

View File

@ -140,11 +140,25 @@ var FontMeasure = (function FontMeasure() {
ctx.font = rule; ctx.font = rule;
current = font; current = font;
}, },
measureText: function fonts_measureText(text) { measureText: function fonts_measureText(text, font, size) {
var width; var width;
if (measureCache && (width = measureCache[text])) if (measureCache && (width = measureCache[text]))
return width; return width;
try {
width = 0.0;
var composite = font.composite;
for (var i = 0; i < text.length; i++) {
var charcode = composite ?
((text.charCodeAt(i++) << 8) + text.charCodeAt(i)) :
text.charCodeAt(i);
var charWidth = parseFloat(font.encoding[charcode].width);
width += charWidth;
}
width = width * size / 1000;
} catch(e) {
width = ctx.measureText(text).width / kScalePrecision; width = ctx.measureText(text).width / kScalePrecision;
}
if (measureCache) if (measureCache)
measureCache[text] = width; measureCache[text] = width;
return width; return width;
@ -444,7 +458,6 @@ 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("+");
@ -469,8 +482,7 @@ var Font = (function Font() {
(fontName.indexOf('Italic') != -1); (fontName.indexOf('Italic') != -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.indexOf("Black") != -1) this.black = (name.indexOf("Black") != -1)
this.loadedName = fontName.split('-')[0]; this.loadedName = fontName.split('-')[0];
@ -1019,7 +1031,9 @@ var Font = (function Font() {
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 = j - firstCode - 1;
encoding[index++] = { unicode: glyphs[code].unicode }; var mapping = encoding[index + 1] || {};
mapping.unicode = glyphs[code].unicode;
encoding[index++] = mapping;
} }
return cmap.data = createCMapTable(glyphs); return cmap.data = createCMapTable(glyphs);
} }
@ -1126,8 +1140,13 @@ var Font = (function Font() {
if (!encoding[0]) { if (!encoding[0]) {
// the font is directly characters to glyphs with no encoding // the font is directly characters to glyphs with no encoding
// so create an identity encoding // so create an identity encoding
for (i = 0; i < numGlyphs; i++) var widths = properties.widths;
encoding[i] = { unicode: i + kCmapGlyphOffset }; for (i = 0; i < numGlyphs; i++) {
encoding[i] = {
unicode: i + kCmapGlyphOffset,
width: widths[i] || properties.defaultWidth
};
}
} else { } else {
for (var code in encoding) for (var code in encoding)
encoding[code].unicode += kCmapGlyphOffset; encoding[code].unicode += kCmapGlyphOffset;
@ -1368,10 +1387,6 @@ var Font = (function Font() {
unicode = charcode; unicode = charcode;
} }
// Check if the glyph has already been converted
if (!IsNum(unicode))
unicode = encoding[charcode].unicode = this.glyphs[unicode].unicode;
// Handle surrogate pairs // Handle surrogate pairs
if (unicode > 0xFFFF) { if (unicode > 0xFFFF) {
str += String.fromCharCode(unicode & 0xFFFF); str += String.fromCharCode(unicode & 0xFFFF);
@ -1824,7 +1839,8 @@ var Type1Parser = function() {
var glyph = getToken(); var glyph = getToken();
if ('undefined' == typeof(properties.differences[index])) { if ('undefined' == typeof(properties.differences[index])) {
var mapping = { unicode: GlyphsUnicode[glyph] || j }; var mapping = properties.encoding[index] || {};
mapping.unicode = GlyphsUnicode[glyph] || j;
properties.glyphs[glyph] = properties.encoding[index] = mapping; properties.glyphs[glyph] = properties.encoding[index] = mapping;
} }
getToken(); // read the in 'put' getToken(); // read the in 'put'
@ -2315,8 +2331,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'];
@ -2334,12 +2348,11 @@ var Type2CFF = (function() {
} }
} }
if (code == -1) {
var mapping = properties.glyphs[glyph] || {}; var mapping = properties.glyphs[glyph] || {};
if (code == -1)
index = code = mapping.unicode || index; index = code = mapping.unicode || index;
}
var width = widths[code] || defaultWidth; var width = mapping.width || defaultWidth;
if (code <= 0x1f || (code >= 127 && code <= 255)) if (code <= 0x1f || (code >= 127 && code <= 255))
code += kCmapGlyphOffset; code += kCmapGlyphOffset;

2938
metrics.js Normal file

File diff suppressed because it is too large Load Diff

90
pdf.js
View File

@ -4197,8 +4197,31 @@ 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) {
for (var j = start; j <= code; j++)
glyphsWidths[j] = widths[++i];
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;
@ -4211,15 +4234,16 @@ var PartialEvaluator = (function() {
var encoding = properties.encoding; var encoding = properties.encoding;
// Set encoding 0 to later verify the font has an encoding // Set encoding 0 to later verify the font has an encoding
encoding[0] = { unicode: 0 }; 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)
continue; continue;
encoding[j >> 1] = { var code = j >> 1;
encoding[code] = {
unicode: glyphID, unicode: glyphID,
width: 0 width: glyphsWidths[code] || defaultWidth
}; };
} }
} else if (type == 'CIDFontType0') { } else if (type == 'CIDFontType0') {
@ -4287,13 +4311,16 @@ 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;
glyphs[glyph] = map[i] = { var width = properties.widths[i] || properties.widths[glyph];
map[i] = {
unicode: index, unicode: index,
width: properties.widths[i - firstChar] || properties.defaultWidth width: width || properties.defaultWidth
}; };
if (glyph)
glyphs[glyph] = map[i];
// If there is no file, the character mapping can't be modified // If there is no file, the character mapping can't be modified
// but this is unlikely that there is any standard encoding with // but this is unlikely that there is any standard encoding with
// chars below 0x1f, so that's fine. // chars below 0x1f, so that's fine.
@ -4303,7 +4330,6 @@ var PartialEvaluator = (function() {
if (index <= 0x1f || (index >= 127 && index <= 255)) if (index <= 0x1f || (index >= 127 && index <= 255))
map[i].unicode += kCmapGlyphOffset; map[i].unicode += kCmapGlyphOffset;
} }
}
if (type == 'TrueType' && dict.has('ToUnicode') && differences) { if (type == 'TrueType' && dict.has('ToUnicode') && differences) {
var cmapObj = xref.fetchIfRef(dict.get('ToUnicode')); var cmapObj = xref.fetchIfRef(dict.get('ToUnicode'));
@ -4339,10 +4365,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] = { var mapping = map[startRange] || {};
unicode: code++, mapping.unicode = code++;
width: 0 map[startRange] = mapping;
}
++startRange; ++startRange;
} }
} }
@ -4353,10 +4378,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] = { var mapping = map[index] || {};
unicode: code, mapping.unicode = code;
width: 0 map[index] = mapping;
};
} }
break; break;
@ -4425,7 +4449,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');
@ -4445,11 +4470,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: widths,
defaultWidth: defaultWidth,
firstChar: 0, firstChar: 0,
lastChar: 256 lastChar: 256
}; };
@ -4508,13 +4540,13 @@ 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: descriptor.get('MissingWidth') || 0, 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: (function() { widths: (function() {
var glyphWidths = {}; var glyphWidths = {};
for (var i = 0; i <= widths.length; i++) for (var i = 0; i < widths.length; i++)
glyphWidths[firstChar++] = widths[i]; glyphWidths[firstChar++] = widths[i];
return glyphWidths; return glyphWidths;
})(), })(),
@ -4903,6 +4935,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);
@ -4921,26 +4954,21 @@ var CanvasGraphics = (function() {
text = font.charsToUnicode(text); text = font.charsToUnicode(text);
} }
var size = 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;
// This is a poor simulation for Arial Narrow while font-stretch
// is not implemented (bug 3512)
if (current.font.narrow) {
textHScale += 0.2;
charSpacing -= (0.09 * current.fontSize);
}
if (charSpacing != 0 || wordSpacing != 0 || textHScale != 1) { if (charSpacing != 0 || wordSpacing != 0 || textHScale != 1) {
scaleFactorX *= textHScale; scaleFactorX *= textHScale;
ctx.scale(1 / textHScale, 1); ctx.scale(1 / textHScale, 1);
var width = 0; var width = 0;
for (var i = 0, ii = text.length; i < ii; ++i) { for (var i = 0, ii = text.length; i < ii; ++i) {
var c = text.charAt(i); ctx.fillText(text.charAt(i), 0, 0);
ctx.fillText(c, 0, 0); var c = originalText.charAt(i);
var charWidth = FontMeasure.measureText(c) + charSpacing; var charWidth = FontMeasure.measureText(c, font, size);
charWidth += charSpacing;
if (c.charCodeAt(0) == 32) if (c.charCodeAt(0) == 32)
charWidth += wordSpacing; charWidth += wordSpacing;
ctx.translate(charWidth * scaleFactorX, 0); ctx.translate(charWidth * scaleFactorX, 0);
@ -4949,7 +4977,7 @@ var CanvasGraphics = (function() {
current.x += width; current.x += width;
} else { } else {
ctx.fillText(text, 0, 0); ctx.fillText(text, 0, 0);
current.x += FontMeasure.measureText(text); current.x += FontMeasure.measureText(originalText, font, size);
} }
this.ctx.restore(); this.ctx.restore();

View File

@ -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>