Merge branch 'master' into font
This commit is contained in:
commit
870027136a
180
fonts.js
180
fonts.js
@ -22,8 +22,7 @@ var kMaxWaitForFontFace = 1000;
|
||||
*/
|
||||
|
||||
var FontMeasure = (function FontMeasure() {
|
||||
var kScalePrecision = 40;
|
||||
|
||||
var kScalePrecision = 50;
|
||||
var ctx = document.createElement('canvas').getContext('2d');
|
||||
ctx.scale(1 / kScalePrecision, 1);
|
||||
|
||||
@ -341,10 +340,9 @@ function getUnicodeRangeFor(value) {
|
||||
* var type1Font = new Font("MyFontName", binaryFile, propertiesObject);
|
||||
* type1Font.bind();
|
||||
*/
|
||||
var Font = (function() {
|
||||
var Font = (function Font() {
|
||||
var constructor = function font_constructor(name, file, properties) {
|
||||
this.name = name;
|
||||
this.textMatrix = properties.textMatrix || IDENTITY_MATRIX;
|
||||
this.encoding = properties.encoding;
|
||||
this.sizes = [];
|
||||
|
||||
@ -387,6 +385,7 @@ var Font = (function() {
|
||||
}
|
||||
|
||||
this.data = data;
|
||||
this.textMatrix = properties.textMatrix || IDENTITY_MATRIX;
|
||||
this.type = properties.type;
|
||||
this.loadedName = getUniqueName();
|
||||
this.compositeFont = properties.compositeFont;
|
||||
@ -511,11 +510,11 @@ var Font = (function() {
|
||||
}
|
||||
ranges.push([start, end]);
|
||||
}
|
||||
|
||||
return ranges;
|
||||
};
|
||||
|
||||
function createCMapTable(glyphs) {
|
||||
glyphs.push({ unicode: 0x0000 });
|
||||
function createCMapTable(glyphs, deltas) {
|
||||
var ranges = getRanges(glyphs);
|
||||
|
||||
var numTables = 1;
|
||||
@ -542,18 +541,17 @@ var Font = (function() {
|
||||
var range = ranges[i];
|
||||
var start = range[0];
|
||||
var end = range[1];
|
||||
var delta = (bias - start) & 0xffff;
|
||||
var offset = (segCount - i) * 2 + bias * 2;
|
||||
bias += (end - start + 1);
|
||||
|
||||
startCount += string16(start);
|
||||
endCount += string16(end);
|
||||
idDeltas += string16(delta);
|
||||
idRangeOffsets += string16(0);
|
||||
idDeltas += string16(0);
|
||||
idRangeOffsets += string16(offset);
|
||||
}
|
||||
|
||||
for (var j = start; j <= end; j++) {
|
||||
glyphsIds += string16(j);
|
||||
}
|
||||
}
|
||||
for (var i = 0; i < glyphs.length; i++)
|
||||
glyphsIds += string16(deltas ? deltas[i] : i + 1);
|
||||
|
||||
endCount += '\xFF\xFF';
|
||||
startCount += '\xFF\xFF';
|
||||
@ -753,8 +751,8 @@ var Font = (function() {
|
||||
return {
|
||||
tag: tag,
|
||||
checksum: checksum,
|
||||
length: offset,
|
||||
offset: length,
|
||||
length: length,
|
||||
offset: offset,
|
||||
data: data
|
||||
};
|
||||
};
|
||||
@ -770,26 +768,66 @@ var Font = (function() {
|
||||
};
|
||||
|
||||
function replaceCMapTable(cmap, font, properties) {
|
||||
font.pos = (font.start ? font.start : 0) + cmap.length;
|
||||
var start = (font.start ? font.start : 0) + cmap.offset;
|
||||
font.pos = start;
|
||||
|
||||
var version = int16(font.getBytes(2));
|
||||
var numTables = int16(font.getBytes(2));
|
||||
var numRecords = int16(font.getBytes(2));
|
||||
|
||||
var records = [];
|
||||
for (var i = 0; i < numRecords; i++) {
|
||||
records.push({
|
||||
platformID: int16(font.getBytes(2)),
|
||||
encodingID: int16(font.getBytes(2)),
|
||||
offset: int32(font.getBytes(4))
|
||||
});
|
||||
};
|
||||
|
||||
var encoding = properties.encoding;
|
||||
var charset = properties.charset;
|
||||
for (var i = 0; i < numRecords; i++) {
|
||||
var table = records[i];
|
||||
font.pos = start + table.offset;
|
||||
|
||||
for (var i = 0; i < numTables; i++) {
|
||||
var platformID = int16(font.getBytes(2));
|
||||
var encodingID = int16(font.getBytes(2));
|
||||
var offset = int32(font.getBytes(4));
|
||||
var format = int16(font.getBytes(2));
|
||||
var length = int16(font.getBytes(2));
|
||||
var language = int16(font.getBytes(2));
|
||||
|
||||
if ((format == 0 && numTables == 1) ||
|
||||
(format == 6 && numTables == 1 && !properties.encoding.empty)) {
|
||||
if (format == 0) {
|
||||
// Characters below 0x20 are controls characters that are hardcoded
|
||||
// into the platform so if some characters in the font are assigned
|
||||
// under this limit they will not be displayed so let's rewrite the
|
||||
// CMap.
|
||||
var glyphs = [];
|
||||
var deltas = [];
|
||||
for (var j = 0; j < 256; j++) {
|
||||
var index = font.getByte();
|
||||
if (index) {
|
||||
deltas.push(index);
|
||||
glyphs.push({ unicode : j });
|
||||
}
|
||||
}
|
||||
|
||||
var rewrite = false;
|
||||
for (var code in encoding) {
|
||||
if (code < 0x20 && encoding[code])
|
||||
rewrite = true;
|
||||
|
||||
if (rewrite)
|
||||
encoding[code] = parseInt(code) + 0x1F;
|
||||
}
|
||||
|
||||
if (rewrite) {
|
||||
for (var j = 0; j < glyphs.length; j++) {
|
||||
glyphs[j].unicode += 0x1F;
|
||||
}
|
||||
}
|
||||
cmap.data = createCMapTable(glyphs, deltas);
|
||||
} else if (format == 6 && numRecords == 1 && !encoding.empty) {
|
||||
// Format 0 alone is not allowed by the sanitizer so let's rewrite
|
||||
// that to a 3-1-4 Unicode BMP table
|
||||
TODO('Use an other source of informations than ' +
|
||||
'charset here, it is not reliable');
|
||||
var charset = properties.charset;
|
||||
var glyphs = [];
|
||||
for (var j = 0; j < charset.length; j++) {
|
||||
glyphs.push({
|
||||
@ -798,7 +836,7 @@ var Font = (function() {
|
||||
}
|
||||
|
||||
cmap.data = createCMapTable(glyphs);
|
||||
} else if (format == 6 && numTables == 1) {
|
||||
} else if (format == 6 && numRecords == 1) {
|
||||
// Format 6 is a 2-bytes dense mapping, which means the font data
|
||||
// lives glue together even if they are pretty far in the unicode
|
||||
// table. (This looks weird, so I can have missed something), this
|
||||
@ -832,7 +870,6 @@ var Font = (function() {
|
||||
assert(ranges.length == 1, 'Got ' + ranges.length +
|
||||
' ranges in a dense array');
|
||||
|
||||
var encoding = properties.encoding;
|
||||
var denseRange = ranges[0];
|
||||
var start = denseRange[0];
|
||||
var end = denseRange[1];
|
||||
@ -851,10 +888,7 @@ var Font = (function() {
|
||||
var header = readOpenTypeHeader(font);
|
||||
var numTables = header.numTables;
|
||||
|
||||
// This keep a reference to the CMap and the post tables since they can
|
||||
// be rewritted
|
||||
var cmap, post, nameTable, maxp;
|
||||
|
||||
var cmap, maxp, hhea, hmtx;
|
||||
var tables = [];
|
||||
for (var i = 0; i < numTables; i++) {
|
||||
var table = readTableEntry(font);
|
||||
@ -862,22 +896,18 @@ var Font = (function() {
|
||||
if (index != -1) {
|
||||
if (table.tag == 'cmap')
|
||||
cmap = table;
|
||||
else if (table.tag == 'post')
|
||||
post = table;
|
||||
else if (table.tag == 'name')
|
||||
nameTable = table;
|
||||
else if (table.tag == 'maxp')
|
||||
maxp = table;
|
||||
else if (table.tag == 'hhea')
|
||||
hhea = table;
|
||||
else if (table.tag == 'hmtx')
|
||||
hmtx = table;
|
||||
|
||||
requiredTables.splice(index, 1);
|
||||
}
|
||||
tables.push(table);
|
||||
}
|
||||
|
||||
// If any tables are still in the array this means some required
|
||||
// tables are missing, which means that we need to rebuild the
|
||||
// font in order to pass the sanitizer.
|
||||
if (requiredTables.length && requiredTables[0] == 'OS/2') {
|
||||
// Create a new file to hold the new version of our truetype with a new
|
||||
// header and new offsets
|
||||
var ttf = new Uint8Array(kMaxFontFileSize);
|
||||
@ -896,11 +926,35 @@ var Font = (function() {
|
||||
// of missing tables
|
||||
createOpenTypeHeader('\x00\x01\x00\x00', ttf, offsets, numTables);
|
||||
|
||||
// Insert the missing table
|
||||
if (requiredTables.indexOf('OS/2') != -1) {
|
||||
tables.push({
|
||||
tag: 'OS/2',
|
||||
data: stringToArray(createOS2Table(properties))
|
||||
});
|
||||
}
|
||||
|
||||
// Ensure the hmtx tables contains an advance width and a sidebearing
|
||||
// for the number of glyphs declared in the maxp table
|
||||
font.pos = (font.start ? font.start : 0) + maxp.offset;
|
||||
var version = int16(font.getBytes(4));
|
||||
var numGlyphs = int16(font.getBytes(2));
|
||||
|
||||
font.pos = (font.start ? font.start : 0) + hhea.offset;
|
||||
font.pos += hhea.length - 2;
|
||||
var numOfHMetrics = int16(font.getBytes(2));
|
||||
|
||||
var numOfSidebearings = numGlyphs - numOfHMetrics;
|
||||
var numMissing = numOfSidebearings - (hmtx.length - numOfHMetrics * 4);
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
// Replace the old CMAP table with a shiny new one
|
||||
if (properties.type == 'CIDFontType2') {
|
||||
@ -910,14 +964,7 @@ var Font = (function() {
|
||||
var glyphs = [];
|
||||
var charset = properties.charset;
|
||||
if (!charset.length) {
|
||||
// PDF did not contain a GIDMap for the font so create an identity cmap
|
||||
|
||||
// First get the number of glyphs from the maxp table
|
||||
font.pos = (font.start ? font.start : 0) + maxp.length;
|
||||
var version = int16(font.getBytes(4));
|
||||
var numGlyphs = int16(font.getBytes(2));
|
||||
|
||||
// Now create an identity mapping
|
||||
// Type2 composite fonts map characters directly to glyphs so the cmap
|
||||
for (var i = 1; i < numGlyphs; i++) {
|
||||
glyphs.push({
|
||||
unicode: i
|
||||
@ -926,31 +973,29 @@ var Font = (function() {
|
||||
} else {
|
||||
for (var i = 1; i < charset.length; i++) {
|
||||
var index = charset.indexOf(i);
|
||||
if (index != -1) {
|
||||
if (index == -1)
|
||||
break;
|
||||
|
||||
glyphs.push({
|
||||
unicode: index
|
||||
});
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!cmap) {
|
||||
// Font did not contain a cmap
|
||||
tables.push({
|
||||
cmap = {
|
||||
tag: 'cmap',
|
||||
data: createCMapTable(glyphs)
|
||||
})
|
||||
} else {
|
||||
cmap.data = createCMapTable(glyphs);
|
||||
data: null
|
||||
};
|
||||
tables.push(cmap);
|
||||
}
|
||||
cmap.data = createCMapTable(glyphs);
|
||||
} else {
|
||||
replaceCMapTable(cmap, font, properties);
|
||||
}
|
||||
|
||||
// Rewrite the 'post' table if needed
|
||||
if (!post) {
|
||||
if (requiredTables.indexOf('post') != -1) {
|
||||
tables.push({
|
||||
tag: 'post',
|
||||
data: stringToArray(createPostTable(properties))
|
||||
@ -958,7 +1003,7 @@ var Font = (function() {
|
||||
}
|
||||
|
||||
// Rewrite the 'name' table if needed
|
||||
if (!nameTable) {
|
||||
if (requiredTables.indexOf('name') != -1) {
|
||||
tables.push({
|
||||
tag: 'name',
|
||||
data: stringToArray(createNameTable(this.name))
|
||||
@ -998,12 +1043,6 @@ var Font = (function() {
|
||||
fontData.push(ttf[i]);
|
||||
|
||||
return fontData;
|
||||
} else if (requiredTables.length) {
|
||||
error('Table ' + requiredTables[0] +
|
||||
' is missing from the TrueType font');
|
||||
}
|
||||
|
||||
return font.getBytes();
|
||||
},
|
||||
|
||||
convert: function font_convert(fontName, font, properties) {
|
||||
@ -1726,8 +1765,10 @@ CFF.prototype = {
|
||||
String.fromCharCode((value >> 8) & 0xFF) +
|
||||
String.fromCharCode(value & 0xFF);
|
||||
} else if (value >= (-2147483648) && value <= 2147483647) {
|
||||
value ^= 0xffffffff;
|
||||
value += 1;
|
||||
return '\xff' +
|
||||
String.fromCharCode((value >>> 24) & 0xFF) +
|
||||
String.fromCharCode((value >> 24) & 0xFF) +
|
||||
String.fromCharCode((value >> 16) & 0xFF) +
|
||||
String.fromCharCode((value >> 8) & 0xFF) +
|
||||
String.fromCharCode(value & 0xFF);
|
||||
@ -1832,8 +1873,15 @@ CFF.prototype = {
|
||||
} else {
|
||||
charstring[i] = cmd;
|
||||
}
|
||||
} else {
|
||||
// Type1 charstring use a division for number above 32000
|
||||
if (command > 32000) {
|
||||
var divisor = charstring[i + 1];
|
||||
command /= divisor;
|
||||
charstring.splice(i, 3, 28, command >> 8, command & 0xff);
|
||||
} else {
|
||||
charstring.splice(i, 1, 28, command >> 8, command & 0xff);
|
||||
}
|
||||
i += 2;
|
||||
}
|
||||
}
|
||||
|
36
pdf.js
36
pdf.js
@ -3660,7 +3660,7 @@ var PartialEvaluator = (function() {
|
||||
|
||||
var fontName = descriptor.get('FontName');
|
||||
assertWellFormed(IsName(fontName), 'invalid font name');
|
||||
fontName = fontName.name.replace('+', '_');
|
||||
fontName = fontName.name.replace(/[\+,\-]/g, '_');
|
||||
|
||||
var fontFile = descriptor.get('FontFile', 'FontFile2', 'FontFile3');
|
||||
if (!fontFile)
|
||||
@ -3708,9 +3708,8 @@ var PartialEvaluator = (function() {
|
||||
var baseName = encoding.get('BaseEncoding');
|
||||
if (baseName) {
|
||||
var base = Encodings[baseName.name];
|
||||
var index = 0;
|
||||
for (var j = 0, end = base.length; j < end; j++)
|
||||
encodingMap[index++] = GlyphsUnicode[base[j]];
|
||||
encodingMap[j] = GlyphsUnicode[base[j]] || 0;
|
||||
} else {
|
||||
TODO('need to load default encoding');
|
||||
}
|
||||
@ -3720,7 +3719,11 @@ var PartialEvaluator = (function() {
|
||||
var index = 0;
|
||||
for (var j = 0; j < differences.length; j++) {
|
||||
var data = differences[j];
|
||||
IsNum(data) ? index = data : encodingMap[index++] = data;
|
||||
if (subType.name == 'TrueType') {
|
||||
IsNum(data) ? index = data : encodingMap[index++] = j;
|
||||
} else {
|
||||
IsNum(data) ? index = data : encodingMap[index++] = GlyphsUnicode[data.name];
|
||||
}
|
||||
}
|
||||
|
||||
// Get the font charset if any
|
||||
@ -3770,6 +3773,7 @@ var PartialEvaluator = (function() {
|
||||
error('useCMap is not implemented');
|
||||
break;
|
||||
|
||||
case 'beginbfchar':
|
||||
case 'beginbfrange':
|
||||
case 'begincodespacerange':
|
||||
token = '';
|
||||
@ -3787,17 +3791,18 @@ var PartialEvaluator = (function() {
|
||||
var code = parseInt('0x' + tokens[j + 2]);
|
||||
|
||||
for (var k = startRange; k <= endRange; k++) {
|
||||
// The encoding mapping table will be filled
|
||||
// later during the building phase
|
||||
//encodingMap[k] = GlyphsUnicode[encoding[code]];
|
||||
charset.push(encoding[code++] || '.notdef');
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 'beginfbchar':
|
||||
case 'endfbchar':
|
||||
error('fbchar parsing is not implemented');
|
||||
case 'endbfchar':
|
||||
for (var j = 0; j < tokens.length; j += 2) {
|
||||
var index = parseInt('0x' + tokens[j]);
|
||||
var code = parseInt('0x' + tokens[j + 1]);
|
||||
encodingMap[index] = GlyphsUnicode[encoding[code]];
|
||||
charset.push(encoding[code] || '.notdef');
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -3898,6 +3903,9 @@ function ScratchCanvas(width, height) {
|
||||
}
|
||||
|
||||
var CanvasGraphics = (function() {
|
||||
var kScalePrecision = 50;
|
||||
var kRasterizerMin = 14;
|
||||
|
||||
function constructor(canvasCtx, imageCanvas) {
|
||||
this.ctx = canvasCtx;
|
||||
this.current = new CanvasExtraState();
|
||||
@ -4113,8 +4121,10 @@ var CanvasGraphics = (function() {
|
||||
if (this.ctx.$setFont) {
|
||||
this.ctx.$setFont(fontName, size);
|
||||
} else {
|
||||
this.ctx.font = size + 'px "' + fontName + '"';
|
||||
FontMeasure.setActive(fontObj, size);
|
||||
|
||||
size = (size <= kRasterizerMin) ? size * kScalePrecision : size;
|
||||
this.ctx.font = size + 'px "' + fontName + '"';
|
||||
}
|
||||
},
|
||||
setTextRenderingMode: function(mode) {
|
||||
@ -4131,7 +4141,7 @@ var CanvasGraphics = (function() {
|
||||
}
|
||||
},
|
||||
setLeadingMoveText: function(x, y) {
|
||||
this.setLeading(-y);
|
||||
this.setLeading(y);
|
||||
this.moveText(x, y);
|
||||
},
|
||||
setTextMatrix: function(a, b, c, d, e, f) {
|
||||
@ -4162,6 +4172,8 @@ var CanvasGraphics = (function() {
|
||||
ctx.translate(current.x, -1 * current.y);
|
||||
var font = this.current.font;
|
||||
if (font) {
|
||||
if (this.current.fontSize < kRasterizerMin)
|
||||
ctx.transform(1 / kScalePrecision, 0, 0, 1 / kScalePrecision, 0, 0);
|
||||
ctx.transform.apply(ctx, font.textMatrix);
|
||||
text = font.charsToUnicode(text);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user