Merge pull request #1203 from notmasteryet/glyf-sanitize
Sanitizing the font glyphs to avoid OTS rejections
This commit is contained in:
commit
c440dfeee0
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,
|
function sanitizeGlyphLocations(loca, glyf, numGlyphs,
|
||||||
isGlyphLocationsLong) {
|
isGlyphLocationsLong) {
|
||||||
var itemSize, itemDecode, itemEncode;
|
var itemSize, itemDecode, itemEncode;
|
||||||
@ -1579,21 +1634,21 @@ var Font = (function FontClosure() {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
var locaData = loca.data;
|
var locaData = loca.data;
|
||||||
|
// removing the invalid glyphs
|
||||||
|
var oldGlyfData = glyf.data;
|
||||||
|
var newGlyfData = new Uint8Array(oldGlyfData.length);
|
||||||
var startOffset = itemDecode(locaData, 0);
|
var startOffset = itemDecode(locaData, 0);
|
||||||
var firstOffset = itemDecode(locaData, itemSize);
|
var writeOffset = 0;
|
||||||
if (firstOffset - startOffset < 12 || startOffset > 0) {
|
itemEncode(locaData, 0, writeOffset);
|
||||||
// removing first glyph
|
for (var i = 0, j = itemSize; i < numGlyphs; i++, j += itemSize) {
|
||||||
glyf.data = glyf.data.subarray(firstOffset);
|
var endOffset = itemDecode(locaData, j);
|
||||||
glyf.length -= firstOffset;
|
var newLength = sanitizeGlyph(oldGlyfData, startOffset, endOffset,
|
||||||
|
newGlyfData, writeOffset);
|
||||||
itemEncode(locaData, 0, 0);
|
writeOffset += newLength;
|
||||||
var i, pos = itemSize;
|
itemEncode(locaData, j, writeOffset);
|
||||||
for (i = 1; i <= numGlyphs; ++i) {
|
startOffset = endOffset;
|
||||||
itemEncode(locaData, pos,
|
|
||||||
itemDecode(locaData, pos) - firstOffset);
|
|
||||||
pos += itemSize;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
glyf.data = newGlyfData.subarray(0, writeOffset);
|
||||||
}
|
}
|
||||||
|
|
||||||
function readGlyphNameMap(post, properties) {
|
function readGlyphNameMap(post, properties) {
|
||||||
|
1
test/pdfs/html5checker.pdf.link
Normal file
1
test/pdfs/html5checker.pdf.link
Normal file
@ -0,0 +1 @@
|
|||||||
|
http://hsivonen.iki.fi/thesis/html5-conformance-checker.pdf
|
@ -452,6 +452,14 @@
|
|||||||
"link": false,
|
"link": false,
|
||||||
"type": "eq"
|
"type": "eq"
|
||||||
},
|
},
|
||||||
|
{ "id": "html5checker",
|
||||||
|
"file": "pdfs/html5checker.pdf",
|
||||||
|
"md5": "74bbd80d1e7eb5f2951582233ef9ebab",
|
||||||
|
"rounds": 1,
|
||||||
|
"pageLimit": 7,
|
||||||
|
"link": true,
|
||||||
|
"type": "eq"
|
||||||
|
},
|
||||||
{ "id": "issue1133",
|
{ "id": "issue1133",
|
||||||
"file": "pdfs/issue1133.pdf",
|
"file": "pdfs/issue1133.pdf",
|
||||||
"md5": "d1b61580cb100e3df93d33703af1773a",
|
"md5": "d1b61580cb100e3df93d33703af1773a",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user