Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Map all glyphs to the private use area and duplicate the first glyph. #9340

Merged
merged 1 commit into from
Sep 8, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
118 changes: 90 additions & 28 deletions src/core/cff_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -815,19 +815,17 @@ var CFFParser = (function CFFParserClosure() {
return new CFFEncoding(predefined, format, encoding, raw);
},
parseFDSelect: function CFFParser_parseFDSelect(pos, length) {
var start = pos;
var bytes = this.bytes;
var format = bytes[pos++];
var fdSelect = [], rawBytes;
var i, invalidFirstGID = false;
var fdSelect = [];
var i;

switch (format) {
case 0:
for (i = 0; i < length; ++i) {
var id = bytes[pos++];
fdSelect.push(id);
}
rawBytes = bytes.subarray(start, pos);
break;
case 3:
var rangesCount = (bytes[pos++] << 8) | bytes[pos++];
Expand All @@ -836,7 +834,6 @@ var CFFParser = (function CFFParserClosure() {
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++];
Expand All @@ -847,11 +844,6 @@ 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:
throw new FormatError(`parseFDSelect: Unknown format "${format}".`);
Expand All @@ -860,7 +852,7 @@ var CFFParser = (function CFFParserClosure() {
throw new FormatError('parseFDSelect: Invalid font data.');
}

return new CFFFDSelect(fdSelect, rawBytes);
return new CFFFDSelect(format, fdSelect);
},
};
return CFFParser;
Expand All @@ -885,6 +877,30 @@ var CFF = (function CFFClosure() {

this.isCIDFont = false;
}
CFF.prototype = {
duplicateFirstGlyph: function CFF_duplicateFirstGlyph() {
// Browsers will not display a glyph at position 0. Typically glyph 0 is
// notdef, but a number of fonts put a valid glyph there so it must be
// duplicated and appended.
if (this.charStrings.count >= 65535) {
warn('Not enough space in charstrings to duplicate first glyph.');
return;
}
var glyphZero = this.charStrings.get(0);
this.charStrings.add(glyphZero);
if (this.isCIDFont) {
this.fdSelect.fdSelect.push(this.fdSelect.fdSelect[0]);
}
},
hasGlyphId: function CFF_hasGlyphID(id) {
if (id < 0 || id >= this.charStrings.count) {
return false;
}
var glyph = this.charStrings.get(id);
return glyph.length > 0;
},
};

return CFF;
})();

Expand Down Expand Up @@ -1142,9 +1158,9 @@ var CFFEncoding = (function CFFEncodingClosure() {
})();

var CFFFDSelect = (function CFFFDSelectClosure() {
function CFFFDSelect(fdSelect, raw) {
function CFFFDSelect(format, fdSelect) {
this.format = format;
this.fdSelect = fdSelect;
this.raw = raw;
}
CFFFDSelect.prototype = {
getFDIndex: function CFFFDSelect_get(glyphIndex) {
Expand Down Expand Up @@ -1261,6 +1277,7 @@ var CFFCompiler = (function CFFCompilerClosure() {
}
}

cff.topDict.setByName('charset', 0);
var compiled = this.compileTopDicts([cff.topDict],
output.length,
cff.isCIDFont);
Expand All @@ -1284,17 +1301,9 @@ var CFFCompiler = (function CFFCompilerClosure() {
output.add(encoding);
}
}

if (cff.charset && cff.topDict.hasName('charset')) {
if (cff.charset.predefined) {
topDictTracker.setEntryLocation('charset', [cff.charset.format],
output);
} else {
var charset = this.compileCharset(cff.charset);
topDictTracker.setEntryLocation('charset', [output.length], output);
output.add(charset);
}
}
var charset = this.compileCharset(cff.charset);
topDictTracker.setEntryLocation('charset', [output.length], output);
output.add(charset);

var charStrings = this.compileCharStrings(cff.charStrings);
topDictTracker.setEntryLocation('CharStrings', [output.length], output);
Expand All @@ -1304,7 +1313,7 @@ var CFFCompiler = (function CFFCompilerClosure() {
// For some reason FDSelect must be in front of FDArray on windows. OSX
// and linux don't seem to care.
topDictTracker.setEntryLocation('FDSelect', [output.length], output);
var fdSelect = this.compileFDSelect(cff.fdSelect.raw);
var fdSelect = this.compileFDSelect(cff.fdSelect);
output.add(fdSelect);
// It is unclear if the sub font dictionary can have CID related
// dictionary keys, but the sanitizer doesn't like them so remove them.
Expand Down Expand Up @@ -1547,16 +1556,68 @@ var CFFCompiler = (function CFFCompilerClosure() {
this.out.writeByteArray(this.compileIndex(globalSubrIndex));
},
compileCharStrings: function CFFCompiler_compileCharStrings(charStrings) {
return this.compileIndex(charStrings);
var charStringsIndex = new CFFIndex();
for (var i = 0; i < charStrings.count; i++) {
var glyph = charStrings.get(i);
// If the CharString outline is empty, replace it with .notdef to
// prevent OTS from rejecting the font (fixes bug1252420.pdf).
if (glyph.length === 0) {
charStringsIndex.add(new Uint8Array([0x8B, 0x0E]));
continue;
}
charStringsIndex.add(glyph);
}
return this.compileIndex(charStringsIndex);
},
compileCharset: function CFFCompiler_compileCharset(charset) {
return this.compileTypedArray(charset.raw);
let length = 1 + (this.cff.charStrings.count - 1) * 2;
// The contents of the charset doesn't matter, it's just there to make
// freetype happy.
let out = new Uint8Array(length);
return this.compileTypedArray(out);
},
compileEncoding: function CFFCompiler_compileEncoding(encoding) {
return this.compileTypedArray(encoding.raw);
},
compileFDSelect: function CFFCompiler_compileFDSelect(fdSelect) {
return this.compileTypedArray(fdSelect);
let format = fdSelect.format;
let out, i;
switch (format) {
case 0:
out = new Uint8Array(1 + fdSelect.fdSelect.length);
out[0] = format;
for (i = 0; i < fdSelect.fdSelect.length; i++) {
out[i + 1] = fdSelect.fdSelect[i];
}
break;
case 3:
let start = 0;
let lastFD = fdSelect.fdSelect[0];
let ranges = [
format,
0, // nRanges place holder
0, // nRanges place holder
(start >> 8) & 0xFF,
start & 0xFF,
lastFD
];
for (i = 1; i < fdSelect.fdSelect.length; i++) {
let currentFD = fdSelect.fdSelect[i];
if (currentFD !== lastFD) {
ranges.push((i >> 8) & 0xFF, i & 0xFF, currentFD);
lastFD = currentFD;
}
}
// 3 bytes are pushed for every range and there are 3 header bytes.
let numRanges = (ranges.length - 3) / 3;
ranges[1] = (numRanges >> 8) & 0xFF;
ranges[2] = numRanges & 0xFF;
// sentinel
ranges.push((i >> 8) & 0xFF, i & 0xFF);
out = new Uint8Array(ranges);
break;
}
return this.compileTypedArray(out);
},
compileTypedArray: function CFFCompiler_compileTypedArray(data) {
var out = [];
Expand Down Expand Up @@ -1648,4 +1709,5 @@ export {
CFFTopDict,
CFFPrivateDict,
CFFCompiler,
CFFFDSelect,
};
2 changes: 1 addition & 1 deletion src/core/font_renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ var FontRendererFactory = (function FontRendererFactoryClosure() {
}

function lookupCmap(ranges, unicode) {
var code = unicode.charCodeAt(0), gid = 0;
var code = unicode.codePointAt(0), gid = 0;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/codePointAt#Browser_compatibility this (unsurprisingly) isn't supported by IE11.

Please include the necessary string/code-point-at polyfill, using core-js, in the src/shared/compatibility.js file.

var l = 0, r = ranges.length - 1;
while (l < r) {
var c = (l + r + 1) >> 1;
Expand Down
Loading