Skip to content

Commit

Permalink
Add unique glyph names for CFF fonts.
Browse files Browse the repository at this point in the history
Printing on MacOS was broken with the previous approach of just mapping
all the glyphs to notdef.
  • Loading branch information
brendandahl committed Feb 27, 2019
1 parent 9f9d87c commit 8a596ef
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 18 deletions.
65 changes: 55 additions & 10 deletions src/core/cff_parser.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,8 @@ var CFFStandardStrings = [
'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman', 'Semibold'
];

const NUM_STANDARD_CFF_STRINGS = 391;

var CFFParser = (function CFFParserClosure() {
var CharstringValidationData = [
null,
Expand Down Expand Up @@ -931,16 +933,27 @@ var CFFStrings = (function CFFStringsClosure() {
}
CFFStrings.prototype = {
get: function CFFStrings_get(index) {
if (index >= 0 && index <= 390) {
if (index >= 0 && index <= (NUM_STANDARD_CFF_STRINGS - 1)) {
return CFFStandardStrings[index];
}
if (index - 391 <= this.strings.length) {
return this.strings[index - 391];
if (index - NUM_STANDARD_CFF_STRINGS <= this.strings.length) {
return this.strings[index - NUM_STANDARD_CFF_STRINGS];
}
return CFFStandardStrings[0];
},
getSID: function CFFStrings_getSID(str) {
let index = CFFStandardStrings.indexOf(str);
if (index !== -1) {
return index;
}
index = this.strings.indexOf(str);
if (index !== -1) {
return index + NUM_STANDARD_CFF_STRINGS;
}
return -1;
},
add: function CFFStrings_add(value) {
this.strings.push(value);
return this.strings.push(value) + NUM_STANDARD_CFF_STRINGS - 1;
},
get count() {
return this.strings.length;
Expand Down Expand Up @@ -1312,7 +1325,8 @@ var CFFCompiler = (function CFFCompilerClosure() {
output.add(encoding);
}
}
var charset = this.compileCharset(cff.charset);
var charset = this.compileCharset(cff.charset, cff.charStrings.count,
cff.strings, cff.isCIDFont);
topDictTracker.setEntryLocation('charset', [output.length], output);
output.add(charset);

Expand Down Expand Up @@ -1580,11 +1594,42 @@ var CFFCompiler = (function CFFCompilerClosure() {
}
return this.compileIndex(charStringsIndex);
},
compileCharset: function CFFCompiler_compileCharset(charset) {
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);
compileCharset: function CFFCompiler_compileCharset(charset, numGlyphs,
strings, isCIDFont) {
// Freetype requires the number of charset strings be correct and MacOS
// requires a valid mapping for printing.
let out;
let numGlyphsLessNotDef = numGlyphs - 1;
if (isCIDFont) {
// In a CID font, the charset is a mapping of CIDs not SIDs so just
// create an identity mapping.
out = new Uint8Array([
2, // format
0, // first CID upper byte
0, // first CID lower byte
(numGlyphsLessNotDef >> 8) & 0xFF,
numGlyphsLessNotDef & 0xFF,
]);
} else {
let length = 1 + numGlyphsLessNotDef * 2;
out = new Uint8Array(length);
out[0] = 0; // format 0
let charsetIndex = 0;
let numCharsets = charset.charset.length;
for (let i = 1; i < out.length; i += 2) {
let sid = 0;
if (charsetIndex < numCharsets) {
let name = charset.charset[charsetIndex++];
sid = strings.getSID(name);
if (sid === -1) {
sid = 0;
warn(`Couldn't find ${name} in CFF strings`);
}
}
out[i] = (sid >> 8) & 0xFF;
out[i + 1] = sid & 0xFF;
}
}
return this.compileTypedArray(out);
},
compileEncoding: function CFFCompiler_compileEncoding(encoding) {
Expand Down
10 changes: 3 additions & 7 deletions src/core/fonts.js
Original file line number Diff line number Diff line change
Expand Up @@ -3333,16 +3333,12 @@ var Type1Font = (function Type1FontClosure() {
var i, ii;
for (i = 0; i < count; i++) {
var index = CFFStandardStrings.indexOf(charstrings[i].glyphName);
// TODO: Insert the string and correctly map it. Previously it was
// thought mapping names that aren't in the standard strings to .notdef
// was fine, however in issue818 when mapping them all to .notdef the
// adieresis glyph no longer worked.
if (index === -1) {
index = 0;
index = strings.add(charstrings[i].glyphName);
}
charsetArray.push((index >> 8) & 0xff, index & 0xff);
charsetArray.push(index);
}
cff.charset = new CFFCharset(false, 0, [], charsetArray);
cff.charset = new CFFCharset(false, 0, charsetArray);

var charStringsIndex = new CFFIndex();
charStringsIndex.add([0x8B, 0x0E]); // .notdef
Expand Down
32 changes: 31 additions & 1 deletion test/unit/cff_parser_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
*/

import {
CFFCompiler, CFFFDSelect, CFFParser, CFFStrings
CFFCharset, CFFCompiler, CFFFDSelect, CFFParser, CFFStrings
} from '../../src/core/cff_parser';
import { SEAC_ANALYSIS_ENABLED } from '../../src/core/fonts';
import { Stream } from '../../src/core/stream';
Expand Down Expand Up @@ -446,5 +446,35 @@ describe('CFFCompiler', function() {
]);
});

it('compiles charset of CID font', function() {
var charset = new CFFCharset();
var c = new CFFCompiler();
var numGlyphs = 7;
var out = c.compileCharset(charset, numGlyphs, new CFFStrings(), true);
// All CID charsets get turned into a simple format 2.
expect(out).toEqual([
2, // format
0, // cid (high)
0, // cid (low)
0, // nLeft (high)
numGlyphs - 1, // nLeft (low)
]);
});

it('compiles charset of non CID font', function() {
var charset = new CFFCharset(false, 0, ['space', 'exclam']);
var c = new CFFCompiler();
var numGlyphs = 3;
var out = c.compileCharset(charset, numGlyphs, new CFFStrings(), false);
// All non-CID fonts use a format 0 charset.
expect(out).toEqual([
0, // format
0, // sid of 'space' (high)
1, // sid of 'space' (low)
0, // sid of 'exclam' (high)
2, // sid of 'exclam' (low)
]);
});

// TODO a lot more compiler tests
});

0 comments on commit 8a596ef

Please sign in to comment.