Skip to content

Commit 7aa959c

Browse files
authored
fix(charset): Implemented charset according to specification (#616)
* fix(charset): Implemented charset according to specification * fix(coding-style): Format coding-style
1 parent 6c55400 commit 7aa959c

File tree

4 files changed

+121
-26
lines changed

4 files changed

+121
-26
lines changed

src/encoding.js

Lines changed: 69 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,71 @@ const cffStandardStrings = [
4545
'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall', 'Yacutesmall', 'Thornsmall', 'Ydieresissmall', '001.000',
4646
'001.001', '001.002', '001.003', 'Black', 'Bold', 'Book', 'Light', 'Medium', 'Regular', 'Roman', 'Semibold'];
4747

48+
// Strings below index 392 are standard CFF strings and are not encoded in the font.
49+
50+
const cffISOAdobeStrings = [
51+
'.notdef', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent', 'ampersand', 'quoteright',
52+
'parenleft', 'parenright', 'asterisk', 'plus', 'comma', 'hyphen', 'period', 'slash', 'zero', 'one', 'two',
53+
'three', 'four', 'five', 'six', 'seven', 'eight', 'nine', 'colon', 'semicolon', 'less', 'equal', 'greater',
54+
'question', 'at', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S',
55+
'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'bracketleft', 'backslash', 'bracketright', 'asciicircum', 'underscore',
56+
'quoteleft', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
57+
'u', 'v', 'w', 'x', 'y', 'z', 'braceleft', 'bar', 'braceright', 'asciitilde', 'exclamdown', 'cent', 'sterling',
58+
'fraction', 'yen', 'florin', 'section', 'currency', 'quotesingle', 'quotedblleft', 'guillemotleft', 'guilsinglleft',
59+
'guilsinglright', 'fi', 'fl', 'endash', 'dagger', 'daggerdbl', 'periodcentered', 'paragraph', 'bullet',
60+
'quotesinglbase', 'quotedblbase', 'quotedblright', 'guillemotright', 'ellipsis', 'perthousand', 'questiondown',
61+
'grave', 'acute', 'circumflex', 'tilde', 'macron', 'breve', 'dotaccent', 'dieresis', 'ring', 'cedilla',
62+
'hungarumlaut', 'ogonek', 'caron', 'emdash', 'AE', 'ordfeminine', 'Lslash', 'Oslash', 'OE', 'ordmasculine', 'ae',
63+
'dotlessi', 'lslash', 'oslash', 'oe', 'germandbls', 'onesuperior', 'logicalnot', 'mu', 'trademark', 'Eth',
64+
'onehalf', 'plusminus', 'Thorn', 'onequarter', 'divide', 'brokenbar', 'degree', 'thorn', 'threequarters',
65+
'twosuperior', 'registered', 'minus', 'eth', 'multiply', 'threesuperior', 'copyright', 'Aacute', 'Acircumflex',
66+
'Adieresis', 'Agrave', 'Aring', 'Atilde', 'Ccedilla', 'Eacute', 'Ecircumflex', 'Edieresis', 'Egrave', 'Iacute',
67+
'Icircumflex', 'Idieresis', 'Igrave', 'Ntilde', 'Oacute', 'Ocircumflex', 'Odieresis', 'Ograve', 'Otilde', 'Scaron',
68+
'Uacute', 'Ucircumflex', 'Udieresis', 'Ugrave', 'Yacute', 'Ydieresis', 'Zcaron', 'aacute', 'acircumflex',
69+
'adieresis', 'agrave', 'aring', 'atilde', 'ccedilla', 'eacute', 'ecircumflex', 'edieresis', 'egrave', 'iacute',
70+
'icircumflex', 'idieresis', 'igrave', 'ntilde', 'oacute', 'ocircumflex', 'odieresis', 'ograve', 'otilde', 'scaron',
71+
'uacute', 'ucircumflex', 'udieresis', 'ugrave', 'yacute', 'ydieresis', 'zcaron'];
72+
73+
const cffIExpertStrings = [
74+
'.notdef', 'space', 'exclamsmall', 'Hungarumlautsmall', 'dollaroldstyle', 'dollarsuperior', 'ampersandsmall',
75+
'Acutesmall', 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader', 'onedotenleader', 'comma', 'hyphen',
76+
'period', 'fraction', 'zerooldstyle', 'oneoldstyle', 'twooldstyle', 'threeoldstyle', 'fouroldstyle', 'fiveoldstyle',
77+
'sixoldstyle', 'sevenoldstyle', 'eightoldstyle', 'nineoldstyle', 'colon', 'semicolon', 'commasuperior',
78+
'threequartersemdash', 'periodsuperior', 'questionsmall', 'asuperior', 'bsuperior', 'centsuperior', 'dsuperior',
79+
'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior', 'rsuperior', 'ssuperior', 'tsuperior',
80+
'ff', 'fi', 'fl', 'ffi', 'ffl', 'parenleftinferior', 'parenrightinferior', 'Circumflexsmall', 'hyphensuperior',
81+
'Gravesmall', 'Asmall', 'Bsmall', 'Csmall', 'Dsmall', 'Esmall', 'Fsmall', 'Gsmall', 'Hsmall', 'Ismall', 'Jsmall',
82+
'Ksmall', 'Lsmall', 'Msmall', 'Nsmall', 'Osmall', 'Psmall', 'Qsmall', 'Rsmall', 'Ssmall', 'Tsmall', 'Usmall',
83+
'Vsmall', 'Wsmall', 'Xsmall', 'Ysmall', 'Zsmall', 'colonmonetary', 'onefitted', 'rupiah', 'Tildesmall',
84+
'exclamdownsmall', 'centoldstyle', 'Lslashsmall', 'Scaronsmall', 'Zcaronsmall', 'Dieresissmall', 'Brevesmall',
85+
'Caronsmall', 'Dotaccentsmall', 'Macronsmall', 'figuredash', 'hypheninferior', 'Ogoneksmall', 'Ringsmall',
86+
'Cedillasmall', 'onequarter', 'onehalf', 'threequarters', 'questiondownsmall', 'oneeighth', 'threeeighths',
87+
'fiveeighths', 'seveneighths', 'onethird', 'twothirds', 'zerosuperior', 'onesuperior', 'twosuperior', 'threesuperior',
88+
'foursuperior', 'fivesuperior', 'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior',
89+
'oneinferior', 'twoinferior', 'threeinferior', 'fourinferior', 'fiveinferior', 'sixinferior', 'seveninferior',
90+
'eightinferior', 'nineinferior', 'centinferior', 'dollarinferior', 'periodinferior', 'commainferior', 'Agravesmall',
91+
'Aacutesmall', 'Acircumflexsmall', 'Atildesmall', 'Adieresissmall', 'Aringsmall', 'AEsmall', 'Ccedillasmall',
92+
'Egravesmall', 'Eacutesmall', 'Ecircumflexsmall', 'Edieresissmall', 'Igravesmall', 'Iacutesmall', 'Icircumflexsmall',
93+
'Idieresissmall', 'Ethsmall', 'Ntildesmall', 'Ogravesmall', 'Oacutesmall', 'Ocircumflexsmall', 'Otildesmall',
94+
'Odieresissmall', 'OEsmall', 'Oslashsmall', 'Ugravesmall', 'Uacutesmall', 'Ucircumflexsmall', 'Udieresissmall',
95+
'Yacutesmall', 'Thornsmall', 'Ydieresissmall'
96+
];
97+
98+
const cffExpertSubsetStrings = [
99+
'.notdef', 'space', 'dollaroldstyle', 'dollarsuperior', 'parenleftsuperior', 'parenrightsuperior', 'twodotenleader',
100+
'onedotenleader', 'comma', 'hyphen', 'period', 'fraction', 'zerooldstyle', 'oneoldstyle', 'twooldstyle',
101+
'threeoldstyle', 'fouroldstyle', 'fiveoldstyle', 'sixoldstyle', 'sevenoldstyle', 'eightoldstyle', 'nineoldstyle',
102+
'colon', 'semicolon', 'commasuperior', 'threequartersemdash', 'periodsuperior', 'asuperior', 'bsuperior',
103+
'centsuperior', 'dsuperior', 'esuperior', 'isuperior', 'lsuperior', 'msuperior', 'nsuperior', 'osuperior',
104+
'rsuperior', 'ssuperior', 'tsuperior', 'ff', 'fi', 'fl', 'ffi', 'ffl', 'parenleftinferior', 'parenrightinferior',
105+
'hyphensuperior', 'colonmonetary', 'onefitted', 'rupiah', 'centoldstyle', 'figuredash', 'hypheninferior',
106+
'onequarter', 'onehalf', 'threequarters', 'oneeighth', 'threeeighths', 'fiveeighths', 'seveneighths', 'onethird',
107+
'twothirds', 'zerosuperior', 'onesuperior', 'twosuperior', 'threesuperior', 'foursuperior', 'fivesuperior',
108+
'sixsuperior', 'sevensuperior', 'eightsuperior', 'ninesuperior', 'zeroinferior', 'oneinferior', 'twoinferior',
109+
'threeinferior', 'fourinferior', 'fiveinferior', 'sixinferior', 'seveninferior', 'eightinferior', 'nineinferior',
110+
'centinferior', 'dollarinferior', 'periodinferior', 'commainferior'
111+
];
112+
48113
const cffStandardEncoding = [
49114
'', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '', '',
50115
'', '', '', '', 'space', 'exclam', 'quotedbl', 'numbersign', 'dollar', 'percent', 'ampersand', 'quoteright',
@@ -258,11 +323,7 @@ function addGlyphNamesAll(font) {
258323
for (let i = 0; i < font.glyphs.length; i += 1) {
259324
glyph = font.glyphs.get(i);
260325
if (font.cffEncoding) {
261-
if (font.isCIDFont) {
262-
glyph.name = 'gid' + i;
263-
} else {
264-
glyph.name = font.cffEncoding.charset[i];
265-
}
326+
glyph.name = font.cffEncoding.charset[i];
266327
} else if (font.glyphNames.names) {
267328
glyph.name = font.glyphNames.glyphIndexToName(i);
268329
}
@@ -303,6 +364,9 @@ function addGlyphNames(font, opt) {
303364

304365
export {
305366
cffStandardStrings,
367+
cffISOAdobeStrings,
368+
cffIExpertStrings,
369+
cffExpertSubsetStrings,
306370
cffStandardEncoding,
307371
cffExpertEncoding,
308372
standardNames,

src/glyphset.js

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -78,11 +78,7 @@ GlyphSet.prototype.get = function(index) {
7878
}
7979

8080
if (this.font.cffEncoding) {
81-
if (this.font.isCIDFont) {
82-
glyph.name = 'gid' + index;
83-
} else {
84-
glyph.name = this.font.cffEncoding.charset[index];
85-
}
81+
glyph.name = this.font.cffEncoding.charset[index];
8682
} else if (this.font.glyphNames.names) {
8783
glyph.name = this.font.glyphNames.glyphIndexToName(index);
8884
}

src/tables/cff.js

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,14 @@
55

66
// @TODO: refactor parsing using stateful parser?
77

8-
import { CffEncoding, cffStandardEncoding, cffExpertEncoding, cffStandardStrings } from '../encoding.js';
8+
import {
9+
CffEncoding,
10+
cffStandardEncoding,
11+
cffExpertEncoding,
12+
cffStandardStrings,
13+
cffISOAdobeStrings,
14+
cffIExpertStrings,
15+
cffExpertSubsetStrings } from '../encoding.js';
916
import glyphset from '../glyphset.js';
1017
import parse from '../parse.js';
1118
import Path from '../path.js';
@@ -499,7 +506,7 @@ function gatherCFFTopDicts(data, start, cffIndex, strings, version) {
499506
// Parse the CFF charset table, which contains internal names for all the glyphs.
500507
// This function will return a list of glyph names.
501508
// See Adobe TN #5176 chapter 13, "Charsets".
502-
function parseCFFCharset(data, start, nGlyphs, strings) {
509+
function parseCFFCharset(data, start, nGlyphs, strings, isCIDFont) {
503510
let sid;
504511
let count;
505512
const parser = new parse.Parser(data, start);
@@ -512,14 +519,24 @@ function parseCFFCharset(data, start, nGlyphs, strings) {
512519
if (format === 0) {
513520
for (let i = 0; i < nGlyphs; i += 1) {
514521
sid = parser.parseSID();
515-
charset.push(getCFFString(strings, sid) || sid);
522+
523+
if(isCIDFont) {
524+
charset.push(sid);
525+
} else {
526+
charset.push(getCFFString(strings, sid) || sid);
527+
}
528+
516529
}
517530
} else if (format === 1) {
518531
while (charset.length <= nGlyphs) {
519532
sid = parser.parseSID();
520533
count = parser.parseCard8();
521534
for (let i = 0; i <= count; i += 1) {
522-
charset.push(getCFFString(strings, sid) || sid);
535+
if(isCIDFont) {
536+
charset.push('cid' + ('00000' + sid).slice(-5));
537+
} else {
538+
charset.push(getCFFString(strings, sid) || sid);
539+
}
523540
sid += 1;
524541
}
525542
}
@@ -528,7 +545,11 @@ function parseCFFCharset(data, start, nGlyphs, strings) {
528545
sid = parser.parseSID();
529546
count = parser.parseCard16();
530547
for (let i = 0; i <= count; i += 1) {
531-
charset.push(getCFFString(strings, sid) || sid);
548+
if(isCIDFont) {
549+
charset.push('cid' + ('00000' + sid).slice(-5));
550+
} else {
551+
charset.push(getCFFString(strings, sid) || sid);
552+
}
532553
sid += 1;
533554
}
534555
}
@@ -541,16 +562,16 @@ function parseCFFCharset(data, start, nGlyphs, strings) {
541562

542563
// Parse the CFF encoding data. Only one encoding can be specified per font.
543564
// See Adobe TN #5176 chapter 12, "Encodings".
544-
function parseCFFEncoding(data, start, charset) {
565+
function parseCFFEncoding(data, start) {
545566
let code;
546-
const enc = {};
567+
const encoding = {};
547568
const parser = new parse.Parser(data, start);
548569
const format = parser.parseCard8();
549570
if (format === 0) {
550571
const nCodes = parser.parseCard8();
551572
for (let i = 0; i < nCodes; i += 1) {
552573
code = parser.parseCard8();
553-
enc[code] = i;
574+
encoding[code] = i;
554575
}
555576
} else if (format === 1) {
556577
const nRanges = parser.parseCard8();
@@ -559,15 +580,15 @@ function parseCFFEncoding(data, start, charset) {
559580
const first = parser.parseCard8();
560581
const nLeft = parser.parseCard8();
561582
for (let j = first; j <= first + nLeft; j += 1) {
562-
enc[j] = code;
583+
encoding[j] = code;
563584
code += 1;
564585
}
565586
}
566587
} else {
567588
throw new Error('Unknown encoding format ' + format);
568589
}
569590

570-
return new CffEncoding(enc, charset);
591+
return encoding;
571592
}
572593

573594
function parseBlend(operands) {
@@ -1265,17 +1286,31 @@ function parseCFFTable(data, start, font, opt) {
12651286
}
12661287

12671288
if (header.formatMajor < 2) {
1268-
const charset = parseCFFCharset(data, start + topDict.charset, font.nGlyphs, stringIndex.objects);
1289+
let charset = [];
1290+
let encoding = [];
1291+
1292+
if(topDict.charset === 0) {
1293+
charset = cffISOAdobeStrings;
1294+
} else if(topDict.charset === 1) {
1295+
charset = cffIExpertStrings;
1296+
} else if (topDict.charset === 2) {
1297+
charset = cffExpertSubsetStrings;
1298+
} else {
1299+
charset = parseCFFCharset(data, start + topDict.charset, font.nGlyphs, stringIndex.objects, font.isCIDFont);
1300+
}
1301+
12691302
if (topDict.encoding === 0) {
12701303
// Standard encoding
1271-
font.cffEncoding = new CffEncoding(cffStandardEncoding, charset);
1304+
encoding = cffStandardEncoding;
12721305
} else if (topDict.encoding === 1) {
12731306
// Expert encoding
1274-
font.cffEncoding = new CffEncoding(cffExpertEncoding, charset);
1307+
encoding = cffExpertEncoding;
12751308
} else {
1276-
font.cffEncoding = parseCFFEncoding(data, start + topDict.encoding, charset);
1309+
encoding = parseCFFEncoding(data, start + topDict.encoding);
12771310
}
12781311

1312+
font.cffEncoding = new CffEncoding(encoding, charset);
1313+
12791314
// Prefer the CMAP encoding to the CFF encoding.
12801315
font.encoding = font.encoding || font.cffEncoding;
12811316
}

test/opentypeSpec.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ describe('opentype.js', function() {
6666
assert.equal(font.unitsPerEm, 1000);
6767
assert.equal(font.glyphs.length, 257);
6868
const aGlyph = font.glyphs.get(2);
69-
assert.equal(aGlyph.name, 'gid2');
69+
assert.equal(aGlyph.name, 'cid00002');
7070
assert.equal(aGlyph.unicode, 1);
7171
assert.equal(aGlyph.path.commands.length, 24);
7272
assert.deepEqual(font.stringToGlyphIndexes('🌺'), [59]);
@@ -156,7 +156,7 @@ describe('opentype.js on low memory mode', function() {
156156
assert.equal(font.unitsPerEm, 1000);
157157
assert.equal(font.glyphs.length, 0);
158158
const aGlyph = font.glyphs.get(2);
159-
assert.equal(aGlyph.name, 'gid2');
159+
assert.equal(aGlyph.name, 'cid00002');
160160
assert.equal(aGlyph.unicode, 1);
161161
assert.equal(aGlyph.path.commands.length, 24);
162162
assert.deepEqual(font.stringToGlyphIndexes('🌺'), [59]);

0 commit comments

Comments
 (0)