Browse Source

Merge pull request #264 from vingtetun/master

canvas.pdf + sizes.pdf are readable
sbarman 14 years ago
parent
commit
c184ad9c40
  1. 180
      fonts.js
  2. 36
      pdf.js

180
fonts.js

@ -22,8 +22,7 @@ var kMaxWaitForFontFace = 1000;
*/ */
var FontMeasure = (function FontMeasure() { var FontMeasure = (function FontMeasure() {
var kScalePrecision = 40; var kScalePrecision = 50;
var ctx = document.createElement('canvas').getContext('2d'); var ctx = document.createElement('canvas').getContext('2d');
ctx.scale(1 / kScalePrecision, 1); ctx.scale(1 / kScalePrecision, 1);
@ -341,10 +340,9 @@ function getUnicodeRangeFor(value) {
* var type1Font = new Font("MyFontName", binaryFile, propertiesObject); * var type1Font = new Font("MyFontName", binaryFile, propertiesObject);
* type1Font.bind(); * type1Font.bind();
*/ */
var Font = (function() { 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.textMatrix = properties.textMatrix || IDENTITY_MATRIX;
this.encoding = properties.encoding; this.encoding = properties.encoding;
this.sizes = []; this.sizes = [];
@ -380,6 +378,7 @@ var Font = (function() {
break; break;
} }
this.data = data; this.data = data;
this.textMatrix = properties.textMatrix || IDENTITY_MATRIX;
this.type = properties.type; this.type = properties.type;
this.loadedName = getUniqueName(); this.loadedName = getUniqueName();
this.compositeFont = properties.compositeFont; this.compositeFont = properties.compositeFont;
@ -504,11 +503,11 @@ var Font = (function() {
} }
ranges.push([start, end]); ranges.push([start, end]);
} }
return ranges; return ranges;
}; };
function createCMapTable(glyphs) { function createCMapTable(glyphs, deltas) {
glyphs.push({ unicode: 0x0000 });
var ranges = getRanges(glyphs); var ranges = getRanges(glyphs);
var numTables = 1; var numTables = 1;
@ -535,19 +534,18 @@ var Font = (function() {
var range = ranges[i]; var range = ranges[i];
var start = range[0]; var start = range[0];
var end = range[1]; var end = range[1];
var delta = (bias - start) & 0xffff; var offset = (segCount - i) * 2 + bias * 2;
bias += (end - start + 1); bias += (end - start + 1);
startCount += string16(start); startCount += string16(start);
endCount += string16(end); endCount += string16(end);
idDeltas += string16(delta); idDeltas += string16(0);
idRangeOffsets += 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'; endCount += '\xFF\xFF';
startCount += '\xFF\xFF'; startCount += '\xFF\xFF';
idDeltas += '\x00\x01'; idDeltas += '\x00\x01';
@ -746,8 +744,8 @@ var Font = (function() {
return { return {
tag: tag, tag: tag,
checksum: checksum, checksum: checksum,
length: offset, length: length,
offset: length, offset: offset,
data: data data: data
}; };
}; };
@ -763,26 +761,66 @@ var Font = (function() {
}; };
function replaceCMapTable(cmap, font, properties) { 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 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 format = int16(font.getBytes(2));
var length = int16(font.getBytes(2)); var length = int16(font.getBytes(2));
var language = int16(font.getBytes(2)); var language = int16(font.getBytes(2));
if ((format == 0 && numTables == 1) || if (format == 0) {
(format == 6 && numTables == 1 && !properties.encoding.empty)) { // 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 // Format 0 alone is not allowed by the sanitizer so let's rewrite
// that to a 3-1-4 Unicode BMP table // that to a 3-1-4 Unicode BMP table
TODO('Use an other source of informations than ' + TODO('Use an other source of informations than ' +
'charset here, it is not reliable'); 'charset here, it is not reliable');
var charset = properties.charset;
var glyphs = []; var glyphs = [];
for (var j = 0; j < charset.length; j++) { for (var j = 0; j < charset.length; j++) {
glyphs.push({ glyphs.push({
@ -791,7 +829,7 @@ var Font = (function() {
} }
cmap.data = createCMapTable(glyphs); 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 // 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 // lives glue together even if they are pretty far in the unicode
// table. (This looks weird, so I can have missed something), this // table. (This looks weird, so I can have missed something), this
@ -825,7 +863,6 @@ var Font = (function() {
assert(ranges.length == 1, 'Got ' + ranges.length + assert(ranges.length == 1, 'Got ' + ranges.length +
' ranges in a dense array'); ' ranges in a dense array');
var encoding = properties.encoding;
var denseRange = ranges[0]; var denseRange = ranges[0];
var start = denseRange[0]; var start = denseRange[0];
var end = denseRange[1]; var end = denseRange[1];
@ -844,10 +881,7 @@ var Font = (function() {
var header = readOpenTypeHeader(font); var header = readOpenTypeHeader(font);
var numTables = header.numTables; var numTables = header.numTables;
// This keep a reference to the CMap and the post tables since they can var cmap, maxp, hhea, hmtx;
// be rewritted
var cmap, post, nameTable, maxp;
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);
@ -855,22 +889,18 @@ var Font = (function() {
if (index != -1) { if (index != -1) {
if (table.tag == 'cmap') if (table.tag == 'cmap')
cmap = table; cmap = table;
else if (table.tag == 'post')
post = table;
else if (table.tag == 'name')
nameTable = table;
else if (table.tag == 'maxp') else if (table.tag == 'maxp')
maxp = table; maxp = table;
else if (table.tag == 'hhea')
hhea = table;
else if (table.tag == 'hmtx')
hmtx = table;
requiredTables.splice(index, 1); requiredTables.splice(index, 1);
} }
tables.push(table); 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 // Create a new file to hold the new version of our truetype with a new
// header and new offsets // header and new offsets
var ttf = new Uint8Array(kMaxFontFileSize); var ttf = new Uint8Array(kMaxFontFileSize);
@ -889,11 +919,35 @@ var Font = (function() {
// of missing tables // of missing tables
createOpenTypeHeader('\x00\x01\x00\x00', ttf, offsets, numTables); createOpenTypeHeader('\x00\x01\x00\x00', ttf, offsets, numTables);
// Insert the missing table if (requiredTables.indexOf('OS/2') != -1) {
tables.push({ tables.push({
tag: 'OS/2', tag: 'OS/2',
data: stringToArray(createOS2Table(properties)) 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 // Replace the old CMAP table with a shiny new one
if (properties.type == 'CIDFontType2') { if (properties.type == 'CIDFontType2') {
@ -903,14 +957,7 @@ var Font = (function() {
var glyphs = []; var glyphs = [];
var charset = properties.charset; var charset = properties.charset;
if (!charset.length) { if (!charset.length) {
// PDF did not contain a GIDMap for the font so create an identity cmap // Type2 composite fonts map characters directly to glyphs so the 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
for (var i = 1; i < numGlyphs; i++) { for (var i = 1; i < numGlyphs; i++) {
glyphs.push({ glyphs.push({
unicode: i unicode: i
@ -919,31 +966,29 @@ var Font = (function() {
} else { } else {
for (var i = 1; i < charset.length; i++) { for (var i = 1; i < charset.length; i++) {
var index = charset.indexOf(i); var index = charset.indexOf(i);
if (index != -1) { if (index == -1)
break;
glyphs.push({ glyphs.push({
unicode: index unicode: index
}); });
} else {
break;
}
} }
} }
if (!cmap) { if (!cmap) {
// Font did not contain a cmap cmap = {
tables.push({
tag: 'cmap', tag: 'cmap',
data: createCMapTable(glyphs) data: null
}) };
} else { tables.push(cmap);
cmap.data = createCMapTable(glyphs);
} }
cmap.data = createCMapTable(glyphs);
} else { } else {
replaceCMapTable(cmap, font, properties); replaceCMapTable(cmap, font, properties);
} }
// Rewrite the 'post' table if needed // Rewrite the 'post' table if needed
if (!post) { if (requiredTables.indexOf('post') != -1) {
tables.push({ tables.push({
tag: 'post', tag: 'post',
data: stringToArray(createPostTable(properties)) data: stringToArray(createPostTable(properties))
@ -951,7 +996,7 @@ var Font = (function() {
} }
// Rewrite the 'name' table if needed // Rewrite the 'name' table if needed
if (!nameTable) { if (requiredTables.indexOf('name') != -1) {
tables.push({ tables.push({
tag: 'name', tag: 'name',
data: stringToArray(createNameTable(this.name)) data: stringToArray(createNameTable(this.name))
@ -991,12 +1036,6 @@ var Font = (function() {
fontData.push(ttf[i]); fontData.push(ttf[i]);
return fontData; 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) { convert: function font_convert(fontName, font, properties) {
@ -1719,8 +1758,10 @@ CFF.prototype = {
String.fromCharCode((value >> 8) & 0xFF) + String.fromCharCode((value >> 8) & 0xFF) +
String.fromCharCode(value & 0xFF); String.fromCharCode(value & 0xFF);
} else if (value >= (-2147483648) && value <= 2147483647) { } else if (value >= (-2147483648) && value <= 2147483647) {
value ^= 0xffffffff;
value += 1;
return '\xff' + return '\xff' +
String.fromCharCode((value >>> 24) & 0xFF) + String.fromCharCode((value >> 24) & 0xFF) +
String.fromCharCode((value >> 16) & 0xFF) + String.fromCharCode((value >> 16) & 0xFF) +
String.fromCharCode((value >> 8) & 0xFF) + String.fromCharCode((value >> 8) & 0xFF) +
String.fromCharCode(value & 0xFF); String.fromCharCode(value & 0xFF);
@ -1825,8 +1866,15 @@ CFF.prototype = {
} else { } else {
charstring[i] = cmd; 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 { } else {
charstring.splice(i, 1, 28, command >> 8, command & 0xff); charstring.splice(i, 1, 28, command >> 8, command & 0xff);
}
i += 2; i += 2;
} }
} }

36
pdf.js

@ -3657,7 +3657,7 @@ var PartialEvaluator = (function() {
var fontName = descriptor.get('FontName'); var fontName = descriptor.get('FontName');
assertWellFormed(IsName(fontName), 'invalid font name'); assertWellFormed(IsName(fontName), 'invalid font name');
fontName = fontName.name.replace('+', '_'); fontName = fontName.name.replace(/[\+,\-]/g, '_');
var fontFile = descriptor.get('FontFile', 'FontFile2', 'FontFile3'); var fontFile = descriptor.get('FontFile', 'FontFile2', 'FontFile3');
if (!fontFile) if (!fontFile)
@ -3705,9 +3705,8 @@ var PartialEvaluator = (function() {
var baseName = encoding.get('BaseEncoding'); var baseName = encoding.get('BaseEncoding');
if (baseName) { if (baseName) {
var base = Encodings[baseName.name]; var base = Encodings[baseName.name];
var index = 0;
for (var j = 0, end = base.length; j < end; j++) for (var j = 0, end = base.length; j < end; j++)
encodingMap[index++] = GlyphsUnicode[base[j]]; encodingMap[j] = GlyphsUnicode[base[j]] || 0;
} else { } else {
TODO('need to load default encoding'); TODO('need to load default encoding');
} }
@ -3717,7 +3716,11 @@ var PartialEvaluator = (function() {
var index = 0; var index = 0;
for (var j = 0; j < differences.length; j++) { for (var j = 0; j < differences.length; j++) {
var data = differences[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 // Get the font charset if any
@ -3767,6 +3770,7 @@ var PartialEvaluator = (function() {
error('useCMap is not implemented'); error('useCMap is not implemented');
break; break;
case 'beginbfchar':
case 'beginbfrange': case 'beginbfrange':
case 'begincodespacerange': case 'begincodespacerange':
token = ''; token = '';
@ -3784,17 +3788,18 @@ var PartialEvaluator = (function() {
var code = parseInt('0x' + tokens[j + 2]); var code = parseInt('0x' + tokens[j + 2]);
for (var k = startRange; k <= endRange; k++) { 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'); charset.push(encoding[code++] || '.notdef');
} }
} }
break; break;
case 'beginfbchar': case 'endbfchar':
case 'endfbchar': for (var j = 0; j < tokens.length; j += 2) {
error('fbchar parsing is not implemented'); var index = parseInt('0x' + tokens[j]);
var code = parseInt('0x' + tokens[j + 1]);
encodingMap[index] = GlyphsUnicode[encoding[code]];
charset.push(encoding[code] || '.notdef');
}
break; break;
default: default:
@ -3879,6 +3884,9 @@ 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();
@ -4094,8 +4102,10 @@ var CanvasGraphics = (function() {
if (this.ctx.$setFont) { if (this.ctx.$setFont) {
this.ctx.$setFont(fontName, size); this.ctx.$setFont(fontName, size);
} else { } else {
this.ctx.font = size + 'px "' + fontName + '"';
FontMeasure.setActive(fontObj, size); FontMeasure.setActive(fontObj, size);
size = (size <= kRasterizerMin) ? size * kScalePrecision : size;
this.ctx.font = size + 'px "' + fontName + '"';
} }
}, },
setTextRenderingMode: function(mode) { setTextRenderingMode: function(mode) {
@ -4112,7 +4122,7 @@ var CanvasGraphics = (function() {
} }
}, },
setLeadingMoveText: function(x, y) { setLeadingMoveText: function(x, y) {
this.setLeading(-y); this.setLeading(y);
this.moveText(x, y); this.moveText(x, y);
}, },
setTextMatrix: function(a, b, c, d, e, f) { setTextMatrix: function(a, b, c, d, e, f) {
@ -4143,6 +4153,8 @@ var CanvasGraphics = (function() {
ctx.translate(current.x, -1 * current.y); ctx.translate(current.x, -1 * current.y);
var font = this.current.font; var font = this.current.font;
if (font) { if (font) {
if (this.current.fontSize < kRasterizerMin)
ctx.transform(1 / kScalePrecision, 0, 0, 1 / kScalePrecision, 0, 0);
ctx.transform.apply(ctx, font.textMatrix); ctx.transform.apply(ctx, font.textMatrix);
text = font.charsToUnicode(text); text = font.charsToUnicode(text);
} }

Loading…
Cancel
Save