mirror of
https://github.com/mozilla/pdf.js.git
synced 2025-04-22 16:18:08 +02:00
Merge upstream.
This commit is contained in:
commit
0175f53637
17 changed files with 192 additions and 67 deletions
|
@ -17,11 +17,15 @@ var TextRenderingMode = {
|
|||
ADD_TO_PATH: 7
|
||||
};
|
||||
|
||||
// Minimal font size that would be used during canvas fillText operations.
|
||||
var MIN_FONT_SIZE = 1;
|
||||
|
||||
var CanvasExtraState = (function CanvasExtraStateClosure() {
|
||||
function CanvasExtraState(old) {
|
||||
// Are soft masks and alpha values shapes or opacities?
|
||||
this.alphaIsShape = false;
|
||||
this.fontSize = 0;
|
||||
this.fontSizeScale = 1;
|
||||
this.textMatrix = IDENTITY_MATRIX;
|
||||
this.fontMatrix = IDENTITY_MATRIX;
|
||||
this.leading = 0;
|
||||
|
@ -583,6 +587,9 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||
this.current.font = fontObj;
|
||||
this.current.fontSize = size;
|
||||
|
||||
if (fontObj.coded)
|
||||
return; // we don't need ctx.font for Type3 fonts
|
||||
|
||||
var name = fontObj.loadedName || 'sans-serif';
|
||||
var bold = fontObj.black ? (fontObj.bold ? 'bolder' : 'bold') :
|
||||
(fontObj.bold ? 'bold' : 'normal');
|
||||
|
@ -590,7 +597,16 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||
var italic = fontObj.italic ? 'italic' : 'normal';
|
||||
var serif = fontObj.isSerifFont ? 'serif' : 'sans-serif';
|
||||
var typeface = '"' + name + '", ' + serif;
|
||||
var rule = italic + ' ' + bold + ' ' + size + 'px ' + typeface;
|
||||
|
||||
// Some font backends cannot handle fonts below certain size.
|
||||
// Keeping the font at minimal size and using the fontSizeScale to change
|
||||
// the current transformation matrix before the fillText/strokeText.
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=726227
|
||||
var browserFontSize = size >= MIN_FONT_SIZE ? size : MIN_FONT_SIZE;
|
||||
this.current.fontSizeScale = browserFontSize != MIN_FONT_SIZE ? 1.0 :
|
||||
size / MIN_FONT_SIZE;
|
||||
|
||||
var rule = italic + ' ' + bold + ' ' + browserFontSize + 'px ' + typeface;
|
||||
this.ctx.font = rule;
|
||||
},
|
||||
setTextRenderingMode: function canvasGraphicsSetTextRenderingMode(mode) {
|
||||
|
@ -653,6 +669,7 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||
var font = current.font;
|
||||
var glyphs = font.charsToGlyphs(str);
|
||||
var fontSize = current.fontSize;
|
||||
var fontSizeScale = current.fontSizeScale;
|
||||
var charSpacing = current.charSpacing;
|
||||
var wordSpacing = current.wordSpacing;
|
||||
var textHScale = current.textHScale;
|
||||
|
@ -716,11 +733,16 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||
else
|
||||
lineWidth /= scale;
|
||||
|
||||
ctx.lineWidth = lineWidth;
|
||||
|
||||
if (textSelection)
|
||||
text.geom = this.getTextGeometry();
|
||||
|
||||
if (fontSizeScale != 1.0) {
|
||||
ctx.scale(fontSizeScale, fontSizeScale);
|
||||
lineWidth /= fontSizeScale;
|
||||
}
|
||||
|
||||
ctx.lineWidth = lineWidth;
|
||||
|
||||
var x = 0;
|
||||
for (var i = 0; i < glyphsLength; ++i) {
|
||||
var glyph = glyphs[i];
|
||||
|
@ -734,20 +756,21 @@ var CanvasGraphics = (function CanvasGraphicsClosure() {
|
|||
var charWidth = glyph.width * fontSize * 0.001 +
|
||||
Util.sign(current.fontMatrix[0]) * charSpacing;
|
||||
|
||||
var scaledX = x / fontSizeScale;
|
||||
switch (textRenderingMode) {
|
||||
default: // other unsupported rendering modes
|
||||
case TextRenderingMode.FILL:
|
||||
case TextRenderingMode.FILL_ADD_TO_PATH:
|
||||
ctx.fillText(char, x, 0);
|
||||
ctx.fillText(char, scaledX, 0);
|
||||
break;
|
||||
case TextRenderingMode.STROKE:
|
||||
case TextRenderingMode.STROKE_ADD_TO_PATH:
|
||||
ctx.strokeText(char, x, 0);
|
||||
ctx.strokeText(char, scaledX, 0);
|
||||
break;
|
||||
case TextRenderingMode.FILL_STROKE:
|
||||
case TextRenderingMode.FILL_STROKE_ADD_TO_PATH:
|
||||
ctx.fillText(char, x, 0);
|
||||
ctx.strokeText(char, x, 0);
|
||||
ctx.fillText(char, scaledX, 0);
|
||||
ctx.strokeText(char, scaledX, 0);
|
||||
break;
|
||||
case TextRenderingMode.INVISIBLE:
|
||||
break;
|
||||
|
|
81
src/fonts.js
81
src/fonts.js
|
@ -1553,6 +1553,61 @@ var Font = (function FontClosure() {
|
|||
}
|
||||
};
|
||||
|
||||
function sanitizeGlyph(source, sourceStart, sourceEnd, dest, destStart) {
|
||||
if (sourceEnd - sourceStart <= 12) {
|
||||
// glyph with data less than 12 is invalid one
|
||||
return 0;
|
||||
}
|
||||
var glyf = source.subarray(sourceStart, sourceEnd);
|
||||
var contoursCount = (glyf[0] << 8) | glyf[1];
|
||||
if (contoursCount & 0x8000) {
|
||||
// complex glyph, writing as is
|
||||
dest.set(glyf, destStart);
|
||||
return glyf.length;
|
||||
}
|
||||
|
||||
var j = 10, flagsCount = 0;
|
||||
for (var i = 0; i < contoursCount; i++) {
|
||||
var endPoint = (glyf[j] << 8) | glyf[j + 1];
|
||||
flagsCount = endPoint + 1;
|
||||
j += 2;
|
||||
}
|
||||
// skipping instructions
|
||||
var instructionsLength = (glyf[j] << 8) | glyf[j + 1];
|
||||
j += 2 + instructionsLength;
|
||||
// validating flags
|
||||
var coordinatesLength = 0;
|
||||
for (var i = 0; i < flagsCount; i++) {
|
||||
var flag = glyf[j++];
|
||||
if (flag & 0xC0) {
|
||||
// reserved flags must be zero, rejecting
|
||||
return 0;
|
||||
}
|
||||
var xyLength = ((flag & 2) ? 1 : (flag & 16) ? 0 : 2) +
|
||||
((flag & 4) ? 1 : (flag & 32) ? 0 : 2);
|
||||
coordinatesLength += xyLength;
|
||||
if (flag & 8) {
|
||||
var repeat = glyf[j++];
|
||||
i += repeat;
|
||||
coordinatesLength += repeat * xyLength;
|
||||
}
|
||||
}
|
||||
var glyphDataLength = j + coordinatesLength;
|
||||
if (glyphDataLength > glyf.length) {
|
||||
// not enough data for coordinates
|
||||
return 0;
|
||||
}
|
||||
if (glyf.length - glyphDataLength > 3) {
|
||||
// truncating and aligning to 4 bytes the long glyph data
|
||||
glyphDataLength = (glyphDataLength + 3) & ~3;
|
||||
dest.set(glyf.subarray(0, glyphDataLength), destStart);
|
||||
return glyphDataLength;
|
||||
}
|
||||
// glyph data is fine
|
||||
dest.set(glyf, destStart);
|
||||
return glyf.length;
|
||||
}
|
||||
|
||||
function sanitizeGlyphLocations(loca, glyf, numGlyphs,
|
||||
isGlyphLocationsLong) {
|
||||
var itemSize, itemDecode, itemEncode;
|
||||
|
@ -1579,21 +1634,21 @@ var Font = (function FontClosure() {
|
|||
};
|
||||
}
|
||||
var locaData = loca.data;
|
||||
// removing the invalid glyphs
|
||||
var oldGlyfData = glyf.data;
|
||||
var newGlyfData = new Uint8Array(oldGlyfData.length);
|
||||
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;
|
||||
}
|
||||
var writeOffset = 0;
|
||||
itemEncode(locaData, 0, writeOffset);
|
||||
for (var i = 0, j = itemSize; i < numGlyphs; i++, j += itemSize) {
|
||||
var endOffset = itemDecode(locaData, j);
|
||||
var newLength = sanitizeGlyph(oldGlyfData, startOffset, endOffset,
|
||||
newGlyfData, writeOffset);
|
||||
writeOffset += newLength;
|
||||
itemEncode(locaData, j, writeOffset);
|
||||
startOffset = endOffset;
|
||||
}
|
||||
glyf.data = newGlyfData.subarray(0, writeOffset);
|
||||
}
|
||||
|
||||
function readGlyphNameMap(post, properties) {
|
||||
|
|
|
@ -162,6 +162,10 @@ var Parser = (function ParserClosure() {
|
|||
|
||||
return imageStream;
|
||||
},
|
||||
fetchIfRef: function parserFetchIfRef(obj) {
|
||||
// not relying on the xref.fetchIfRef -- xref might not be set
|
||||
return isRef(obj) ? this.xref.fetch(obj) : obj;
|
||||
},
|
||||
makeStream: function parserMakeStream(dict, cipherTransform) {
|
||||
var lexer = this.lexer;
|
||||
var stream = lexer.stream;
|
||||
|
@ -171,10 +175,7 @@ var Parser = (function ParserClosure() {
|
|||
var pos = stream.pos;
|
||||
|
||||
// get length
|
||||
var length = dict.get('Length');
|
||||
var xref = this.xref;
|
||||
if (xref)
|
||||
length = xref.fetchIfRef(length);
|
||||
var length = this.fetchIfRef(dict.get('Length'));
|
||||
if (!isInt(length)) {
|
||||
error('Bad ' + length + ' attribute in stream');
|
||||
length = 0;
|
||||
|
@ -196,8 +197,8 @@ var Parser = (function ParserClosure() {
|
|||
return stream;
|
||||
},
|
||||
filter: function parserFilter(stream, dict, length) {
|
||||
var filter = dict.get('Filter', 'F');
|
||||
var params = dict.get('DecodeParms', 'DP');
|
||||
var filter = this.fetchIfRef(dict.get('Filter', 'F'));
|
||||
var params = this.fetchIfRef(dict.get('DecodeParms', 'DP'));
|
||||
if (isName(filter))
|
||||
return this.makeFilter(stream, filter.name, length, params);
|
||||
if (isArray(filter)) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue