Browse Source

Attempt to recover valid `format 3` FDSelect data from broken CFF fonts (bug 1146106)

According to the CFF specification, see http://partners.adobe.com/public/developer/en/font/5176.CFF.pdf#G3.46884, for `format 3` FDSelect data: "The first range must have a ‘first’ GID of 0".
Since the PDF file (attached in the bug) violates that part of the specification, this patch tries to recover valid FDSelect data to prevent OTS from rejecting the font.

Fixes https://bugzilla.mozilla.org/show_bug.cgi?id=1146106.
Jonas Jenwald 9 years ago
parent
commit
6260fc09a3
  1. 23
      src/core/cff_parser.js
  2. 1
      test/pdfs/.gitignore
  3. BIN
      test/pdfs/bug1146106.pdf
  4. 7
      test/test_manifest.json
  5. 35
      test/unit/cff_parser_spec.js

23
src/core/cff_parser.js

@ -856,8 +856,8 @@ var CFFParser = (function CFFParserClosure() { @@ -856,8 +856,8 @@ var CFFParser = (function CFFParserClosure() {
var start = pos;
var bytes = this.bytes;
var format = bytes[pos++];
var fdSelect = [];
var i;
var fdSelect = [], rawBytes;
var i, invalidFirstGID = false;
switch (format) {
case 0:
@ -865,11 +865,18 @@ var CFFParser = (function CFFParserClosure() { @@ -865,11 +865,18 @@ var CFFParser = (function CFFParserClosure() {
var id = bytes[pos++];
fdSelect.push(id);
}
rawBytes = bytes.subarray(start, pos);
break;
case 3:
var rangesCount = (bytes[pos++] << 8) | bytes[pos++];
for (i = 0; i < rangesCount; ++i) {
var first = (bytes[pos++] << 8) | bytes[pos++];
if (i === 0 && first !== 0) {
warn('parseFDSelect: The first range must have a first GID of 0' +
' -- trying to recover.');
invalidFirstGID = true;
first = 0;
}
var fdIndex = bytes[pos++];
var next = (bytes[pos] << 8) | bytes[pos + 1];
for (var j = first; j < next; ++j) {
@ -878,13 +885,19 @@ var CFFParser = (function CFFParserClosure() { @@ -878,13 +885,19 @@ var CFFParser = (function CFFParserClosure() {
}
// Advance past the sentinel(next).
pos += 2;
rawBytes = bytes.subarray(start, pos);
if (invalidFirstGID) {
rawBytes[3] = rawBytes[4] = 0; // Adjust the first range, first GID.
}
break;
default:
error('Unknown fdselect format ' + format);
error('parseFDSelect: Unknown format "' + format + '".');
break;
}
var end = pos;
return new CFFFDSelect(fdSelect, bytes.subarray(start, end));
assert(fdSelect.length === length, 'parseFDSelect: Invalid font data.');
return new CFFFDSelect(fdSelect, rawBytes);
}
};
return CFFParser;

1
test/pdfs/.gitignore vendored

@ -37,6 +37,7 @@ @@ -37,6 +37,7 @@
!bug1050040.pdf
!bug1200096.pdf
!bug1068432.pdf
!bug1146106.pdf
!issue5564_reduced.pdf
!canvas.pdf
!bug1132849.pdf

BIN
test/pdfs/bug1146106.pdf

Binary file not shown.

7
test/test_manifest.json

@ -210,6 +210,13 @@ @@ -210,6 +210,13 @@
"link": false,
"type": "eq"
},
{ "id": "bug1146106",
"file": "pdfs/bug1146106.pdf",
"md5": "a323d3766da49ee40f7d5dff0aeb0cc1",
"rounds": 1,
"link": false,
"type": "eq"
},
{ "id": "issue1512",
"file": "pdfs/issue1512r.pdf",
"md5": "af48ede2658d99cca423147085c6609b",

35
test/unit/cff_parser_spec.js

@ -262,10 +262,12 @@ describe('CFFParser', function() { @@ -262,10 +262,12 @@ describe('CFFParser', function() {
var bytes = new Uint8Array([0x00, // format
0x00, // gid: 0 fd: 0
0x01 // gid: 1 fd: 1
]);
parser.bytes = bytes;
]);
parser.bytes = bytes.slice();
var fdSelect = parser.parseFDSelect(0, 2);
expect(fdSelect.fdSelect).toEqual([0, 1]);
expect(fdSelect.raw).toEqual(bytes);
});
it('parses fdselect format 3', function() {
@ -273,13 +275,32 @@ describe('CFFParser', function() { @@ -273,13 +275,32 @@ describe('CFFParser', function() {
0x00, 0x02, // range count
0x00, 0x00, // first gid
0x09, // font dict 1 id
0x00, 0x02, // nex gid
0x0a, // font dict 2 gid
0x00, 0x02, // next gid
0x0a, // font dict 2 id
0x00, 0x04 // sentinel (last gid)
]);
parser.bytes = bytes;
var fdSelect = parser.parseFDSelect(0, 2);
]);
parser.bytes = bytes.slice();
var fdSelect = parser.parseFDSelect(0, 4);
expect(fdSelect.fdSelect).toEqual([9, 9, 0xa, 0xa]);
expect(fdSelect.raw).toEqual(bytes);
});
it('parses invalid fdselect format 3 (bug 1146106)', function() {
var bytes = new Uint8Array([0x03, // format
0x00, 0x02, // range count
0x00, 0x01, // first gid (invalid)
0x09, // font dict 1 id
0x00, 0x02, // next gid
0x0a, // font dict 2 id
0x00, 0x04 // sentinel (last gid)
]);
parser.bytes = bytes.slice();
var fdSelect = parser.parseFDSelect(0, 4);
expect(fdSelect.fdSelect).toEqual([9, 9, 0xa, 0xa]);
bytes[3] = bytes[4] = 0x00; // The adjusted first range, first gid.
expect(fdSelect.raw).toEqual(bytes);
});
// TODO fdArray

Loading…
Cancel
Save