1
0
Fork 0
mirror of https://github.com/mozilla/pdf.js.git synced 2025-04-26 10:08:06 +02:00

Merge with upstream

This commit is contained in:
Vivien Nicolas 2011-09-20 03:42:34 +02:00
commit 06a3b49e49
25 changed files with 1028 additions and 620 deletions

247
fonts.js Executable file → Normal file
View file

@ -12,6 +12,10 @@ var kMaxWaitForFontFace = 1000;
// Unicode Private Use Area
var kCmapGlyphOffset = 0xE000;
// PDF Glyph Space Units are one Thousandth of a TextSpace Unit
// except for Type 3 fonts
var kPDFGlyphSpaceUnits = 1000;
// Until hinting is fully supported this constant can be used
var kHintingEnabled = false;
@ -436,6 +440,7 @@ var Font = (function Font() {
// name ArialBlack for example will be replaced by Helvetica.
this.black = (name.search(/Black/g) != -1);
this.defaultWidth = properties.defaultWidth;
this.loadedName = fontName.split('-')[0];
this.loading = false;
return;
@ -472,6 +477,7 @@ var Font = (function Font() {
this.data = data;
this.type = properties.type;
this.textMatrix = properties.textMatrix;
this.defaultWidth = properties.defaultWidth;
this.loadedName = getUniqueName();
this.composite = properties.composite;
this.loading = true;
@ -534,6 +540,10 @@ var Font = (function Font() {
};
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;
@ -587,19 +597,24 @@ var Font = (function Font() {
var codes = [];
var length = glyphs.length;
for (var n = 0; n < length; ++n)
codes.push(String.fromCharCode(glyphs[n].unicode));
codes.sort();
codes.push({ unicode: glyphs[n].unicode, code: n });
codes.sort(function(a, b) {
return a.unicode - b.unicode;
});
// Split the sorted codes into ranges.
var ranges = [];
for (var n = 0; n < length; ) {
var start = codes[n++].charCodeAt(0);
var start = codes[n].unicode;
var startCode = codes[n].code;
++n;
var end = start;
while (n < length && end + 1 == codes[n].charCodeAt(0)) {
while (n < length && end + 1 == codes[n].unicode) {
++end;
++n;
}
ranges.push([start, end]);
var endCode = codes[n - 1].code;
ranges.push([start, end, startCode, endCode]);
}
return ranges;
@ -628,22 +643,39 @@ var Font = (function Font() {
var idRangeOffsets = '';
var glyphsIds = '';
var bias = 0;
for (var i = 0; i < segCount - 1; i++) {
var range = ranges[i];
var start = range[0];
var end = range[1];
var offset = (segCount - i) * 2 + bias * 2;
bias += (end - start + 1);
startCount += string16(start);
endCount += string16(end);
idDeltas += string16(0);
idRangeOffsets += string16(offset);
if (deltas) {
for (var i = 0; i < segCount - 1; i++) {
var range = ranges[i];
var start = range[0];
var end = range[1];
var offset = (segCount - i) * 2 + bias * 2;
bias += (end - start + 1);
startCount += string16(start);
endCount += string16(end);
idDeltas += string16(0);
idRangeOffsets += string16(offset);
var startCode = range[2];
var endCode = range[3];
for (var j = startCode; j <= endCode; ++j)
glyphsIds += string16(deltas[j]);
}
} else {
for (var i = 0; i < segCount - 1; i++) {
var range = ranges[i];
var start = range[0];
var end = range[1];
var startCode = range[2];
startCount += string16(start);
endCount += string16(end);
idDeltas += string16((startCode - start + 1) & 0xFFFF);
idRangeOffsets += string16(0);
}
}
for (var i = 0; i < glyphs.length; i++)
glyphsIds += string16(deltas ? deltas[i] : i + 1);
endCount += '\xFF\xFF';
startCount += '\xFF\xFF';
idDeltas += '\x00\x01';
@ -663,7 +695,9 @@ var Font = (function Font() {
format314);
};
function createOS2Table(properties) {
function createOS2Table(properties, override) {
var override = override || {};
var ulUnicodeRange1 = 0;
var ulUnicodeRange2 = 0;
var ulUnicodeRange3 = 0;
@ -694,6 +728,24 @@ var Font = (function Font() {
}
}
var unitsPerEm = override.unitsPerEm || kPDFGlyphSpaceUnits;
var typoAscent = override.ascent || properties.ascent;
var typoDescent = override.descent || properties.descent;
var winAscent = override.yMax || typoAscent;
var winDescent = -override.yMin || -typoDescent;
// if there is a units per em value but no other override
// then scale the calculated ascent
if (unitsPerEm != kPDFGlyphSpaceUnits &&
'undefined' == typeof(override.ascent)) {
// if the font units differ to the PDF glyph space units
// then scale up the values
typoAscent = Math.round(typoAscent * unitsPerEm / kPDFGlyphSpaceUnits);
typoDescent = Math.round(typoDescent * unitsPerEm / kPDFGlyphSpaceUnits);
winAscent = typoAscent;
winDescent = -typoDescent;
}
return '\x00\x03' + // version
'\x02\x24' + // xAvgCharWidth
'\x01\xF4' + // usWeightClass
@ -722,11 +774,11 @@ var Font = (function Font() {
string16(firstCharIndex ||
properties.firstChar) + // usFirstCharIndex
string16(lastCharIndex || properties.lastChar) + // usLastCharIndex
string16(properties.ascent) + // sTypoAscender
string16(properties.descent) + // sTypoDescender
string16(typoAscent) + // sTypoAscender
string16(typoDescent) + // sTypoDescender
'\x00\x64' + // sTypoLineGap (7%-10% of the unitsPerEM value)
string16(properties.ascent) + // usWinAscent
string16(-properties.descent) + // usWinDescent
string16(winAscent) + // usWinAscent
string16(winDescent) + // usWinDescent
'\x00\x00\x00\x00' + // ulCodePageRange1 (Bits 0-31)
'\x00\x00\x00\x00' + // ulCodePageRange2 (Bits 32-63)
string16(properties.xHeight) + // sxHeight
@ -833,9 +885,11 @@ var Font = (function Font() {
var data = file.getBytes(length);
file.pos = previousPosition;
if (tag == 'head')
if (tag == 'head') {
// clearing checksum adjustment
data[8] = data[9] = data[10] = data[11] = 0;
data[17] |= 0x20; //Set font optimized for cleartype flag
}
return {
tag: tag,
@ -1001,6 +1055,49 @@ var Font = (function Font() {
}
};
function sanitizeGlyphLocations(loca, glyf, numGlyphs,
isGlyphLocationsLong) {
var itemSize, itemDecode, itemEncode;
if (isGlyphLocationsLong) {
itemSize = 4;
itemDecode = function(data, offset) {
return (data[offset] << 24) | (data[offset + 1] << 16) |
(data[offset + 2] << 8) | data[offset + 3];
};
itemEncode = function(data, offset, value) {
data[offset] = (value >>> 24) & 0xFF;
data[offset + 1] = (value >> 16) & 0xFF;
data[offset + 2] = (value >> 8) & 0xFF;
data[offset + 3] = value & 0xFF;
};
} else {
itemSize = 2;
itemDecode = function(data, offset) {
return (data[offset] << 9) | (data[offset + 1] << 1);
};
itemEncode = function(data, offset, value) {
data[offset] = (value >> 9) & 0xFF;
data[offset + 1] = (value >> 1) & 0xFF;
};
}
var locaData = loca.data;
var startOffset = itemDecode(locaData, 0);
var firstOffset = itemDecode(locaData, itemSize);
if (firstOffset - startOffset < 12 || startOffset > 0) {
// removing first glyph
glyf.data = glyf.data.subarray(firstOffset);
glyf.length -= firstOffset;
itemEncode(locaData, 0, 0);
var i, pos = itemSize;
for (i = 1; i <= numGlyphs; ++i) {
itemEncode(locaData, pos,
itemDecode(locaData, pos) - firstOffset);
pos += itemSize;
}
}
}
// Check that required tables are present
var requiredTables = ['OS/2', 'cmap', 'head', 'hhea',
'hmtx', 'maxp', 'name', 'post'];
@ -1008,7 +1105,7 @@ var Font = (function Font() {
var header = readOpenTypeHeader(font);
var numTables = header.numTables;
var cmap, maxp, hhea, hmtx, vhea, vmtx;
var cmap, maxp, hhea, hmtx, vhea, vmtx, head, loca, glyf;
var tables = [];
for (var i = 0; i < numTables; i++) {
var table = readTableEntry(font);
@ -1022,6 +1119,8 @@ var Font = (function Font() {
hhea = table;
else if (table.tag == 'hmtx')
hmtx = table;
else if (table.tag == 'head')
head = table;
requiredTables.splice(index, 1);
} else {
@ -1029,6 +1128,10 @@ var Font = (function Font() {
vmtx = table;
else if (table.tag == 'vhea')
vhea = table;
else if (table.tag == 'loca')
loca = table;
else if (table.tag == 'glyf')
glyf = table;
}
tables.push(table);
}
@ -1048,9 +1151,19 @@ var Font = (function Font() {
createOpenTypeHeader(header.version, ttf, numTables);
if (requiredTables.indexOf('OS/2') != -1) {
// extract some more font properties from the OpenType head and
// hhea tables; yMin and descent value are always negative
var override = {
unitsPerEm: int16([head.data[18], head.data[19]]),
yMax: int16([head.data[42], head.data[43]]),
yMin: int16([head.data[38], head.data[39]]) - 0x10000,
ascent: int16([hhea.data[4], hhea.data[5]]),
descent: int16([hhea.data[6], hhea.data[7]]) - 0x10000
};
tables.push({
tag: 'OS/2',
data: stringToArray(createOS2Table(properties))
data: stringToArray(createOS2Table(properties, override))
});
}
@ -1063,6 +1176,11 @@ var Font = (function Font() {
sanitizeMetrics(font, hhea, hmtx, numGlyphs);
sanitizeMetrics(font, vhea, vmtx, numGlyphs);
if (head && loca && glyf) {
var isGlyphLocationsLong = int16([head.data[50], head.data[51]]);
sanitizeGlyphLocations(loca, glyf, numGlyphs, isGlyphLocationsLong);
}
// Sanitizer reduces the glyph advanceWidth to the maxAdvanceWidth
// Sometimes it's 0. That needs to be fixed
if (hhea.data[10] == 0 && hhea.data[11] == 0) {
@ -1087,7 +1205,7 @@ var Font = (function Font() {
tables.push(cmap);
}
var encoding = properties.encoding;
var encoding = properties.encoding, i;
if (!encoding[0]) {
// the font is directly characters to glyphs with no encoding
// so create an identity encoding
@ -1095,18 +1213,28 @@ var Font = (function Font() {
for (i = 0; i < numGlyphs; i++) {
var width = widths[i];
encoding[i] = {
unicode: i + kCmapGlyphOffset,
unicode: i <= 0x1f || (i >= 127 && i <= 255) ?
i + kCmapGlyphOffset : i,
width: IsNum(width) ? width : properties.defaultWidth
};
}
} else {
for (var code in encoding)
encoding[code].unicode += kCmapGlyphOffset;
for (i in encoding) {
if (encoding.hasOwnProperty(i)) {
var unicode = encoding[i].unicode;
if (unicode <= 0x1f || (unicode >= 127 && unicode <= 255))
encoding[i].unicode = unicode += kCmapGlyphOffset;
}
}
}
var glyphs = [];
for (var i = 1; i < numGlyphs; i++)
glyphs.push({ unicode: i + kCmapGlyphOffset });
for (i = 1; i < numGlyphs; i++) {
glyphs.push({
unicode: i <= 0x1f || (i >= 127 && i <= 255) ?
i + kCmapGlyphOffset : i
});
}
cmap.data = createCMapTable(glyphs);
} else {
replaceCMapTable(cmap, font, properties);
@ -1292,7 +1420,8 @@ var Font = (function Font() {
var rule = "@font-face { font-family:'" + fontName + "';src:" + url + '}';
var styleSheet = document.styleSheets[0];
if (!styleSheet) {
document.documentElement.firstChild.appendChild( document.createElement('style') );
document.documentElement.firstChild.appendChild(
document.createElement('style'));
styleSheet = document.styleSheets[0];
}
styleSheet.insertRule(rule, styleSheet.cssRules.length);
@ -1300,15 +1429,15 @@ var Font = (function Font() {
return rule;
},
charsToUnicode: function fonts_chars2Unicode(chars) {
charsToGlyphs: function fonts_chars2Glyphs(chars) {
var charsCache = this.charsCache;
var str;
var glyphs;
// if we translated this string before, just grab it from the cache
if (charsCache) {
str = charsCache[chars];
if (str)
return str;
glyphs = charsCache[chars];
if (glyphs)
return glyphs;
}
// lazily create the translation cache
@ -1319,7 +1448,8 @@ var Font = (function Font() {
var encoding = this.encoding;
if (!encoding)
return chars;
str = '';
glyphs = [];
if (this.composite) {
// composite fonts have multi-byte strings convert the string from
@ -1330,38 +1460,39 @@ var Font = (function Font() {
// loop should never end on the last byte
for (var i = 0; i < length; i++) {
var charcode = int16([chars.charCodeAt(i++), chars.charCodeAt(i)]);
var unicode = encoding[charcode];
if ('undefined' == typeof(unicode)) {
var glyph = encoding[charcode];
if ('undefined' == typeof(glyph)) {
warn('Unencoded charcode ' + charcode);
unicode = charcode;
} else {
unicode = unicode.unicode;
glyph = {
unicode: charcode,
width: this.defaultWidth
};
}
str += String.fromCharCode(unicode);
glyphs.push(glyph);
// placing null after each word break charcode (ASCII SPACE)
if (charcode == 0x20)
glyphs.push(null);
}
}
else {
for (var i = 0; i < chars.length; ++i) {
var charcode = chars.charCodeAt(i);
var unicode = encoding[charcode];
if ('undefined' == typeof(unicode)) {
var glyph = encoding[charcode];
if ('undefined' == typeof(glyph)) {
warn('Unencoded charcode ' + charcode);
unicode = charcode;
} else {
unicode = unicode.unicode;
glyph = {
unicode: charcode,
width: this.defaultWidth
};
}
// Handle surrogate pairs
if (unicode > 0xFFFF) {
str += String.fromCharCode(unicode & 0xFFFF);
unicode >>= 16;
}
str += String.fromCharCode(unicode);
glyphs.push(glyph);
if (charcode == 0x20)
glyphs.push(null);
}
}
// Enter the translated string into the cache
return (charsCache[chars] = str);
return (charsCache[chars] = glyphs);
}
};