diff --git a/docs/glyph-inspector.html b/docs/glyph-inspector.html index cc49e23e..5b809d2b 100644 --- a/docs/glyph-inspector.html +++ b/docs/glyph-inspector.html @@ -387,8 +387,12 @@

Free Software

hline('yMin', font.tables.head.yMin); hline('Ascender', font.tables.hhea.ascender); hline('Descender', font.tables.hhea.descender); - hline('Typo Ascender', font.tables.os2.sTypoAscender); - hline('Typo Descender', font.tables.os2.sTypoDescender); + + // Some PDF-embedded fonts might not have the OS/2 table + if (font.tables.os2) { + hline('Typo Ascender', font.tables.os2.sTypoAscender); + hline('Typo Descender', font.tables.os2.sTypoDescender); + } } window.redraw = function(options = { withColors: true, withVariations: true }) { diff --git a/src/encoding.mjs b/src/encoding.mjs index 0902b8c8..2f7b874c 100644 --- a/src/encoding.mjs +++ b/src/encoding.mjs @@ -324,7 +324,7 @@ function addGlyphNamesAll(font) { glyph = font.glyphs.get(i); if (font.cffEncoding) { glyph.name = font.cffEncoding.charset[i]; - } else if (font.glyphNames.names) { + } else if (font.glyphNames && font.glyphNames.names) { glyph.name = font.glyphNames.glyphIndexToName(i); } } diff --git a/src/tables/cmap.mjs b/src/tables/cmap.mjs index 6193a056..634762db 100644 --- a/src/tables/cmap.mjs +++ b/src/tables/cmap.mjs @@ -99,6 +99,19 @@ function parseCmapTableFormat4(cmap, p, data, start, offset) { } } +function parseCmapTableFormat6(cmap, p) { + cmap.length = p.parseUShort(); + cmap.language = p.parseUShort(); + + cmap.firstCode = p.parseUShort(); + cmap.entryCount = p.parseUShort(); + cmap.glyphIndexMap = {}; + + for (let i = 0; i < cmap.entryCount; i += 1) { + cmap.glyphIndexMap[cmap.firstCode + i] = p.parseUShort(); + } +} + function parseCmapTableFormat14(cmap, p) { const varSelectorList = {}; @@ -216,6 +229,8 @@ function parseCmapTable(data, start) { parseCmapTableFormat12or13(cmap, p, cmap.format); } else if (cmap.format === 4) { parseCmapTableFormat4(cmap, p, data, start, offset); + } else if (cmap.format === 6) { + parseCmapTableFormat6(cmap, p); } else { throw new Error( 'Only format 0 (platformId 1, encodingId 0), 4, 12 and 14 cmap tables are supported ' + @@ -410,4 +425,4 @@ function makeCmapTable(glyphs) { export default { parse: parseCmapTable, make: makeCmapTable }; -export { parseCmapTableFormat0, parseCmapTableFormat14 }; +export { parseCmapTableFormat0, parseCmapTableFormat6, parseCmapTableFormat14 }; diff --git a/test/fonts/PDF-Embedded-Calibri-Subset.ttf b/test/fonts/PDF-Embedded-Calibri-Subset.ttf new file mode 100644 index 00000000..18cae514 Binary files /dev/null and b/test/fonts/PDF-Embedded-Calibri-Subset.ttf differ diff --git a/test/tables/cmap.spec.mjs b/test/tables/cmap.spec.mjs index 4ab388a5..574c97f7 100644 --- a/test/tables/cmap.spec.mjs +++ b/test/tables/cmap.spec.mjs @@ -1,7 +1,7 @@ import assert from 'assert'; import { unhex } from '../testutil.mjs'; import { Parser } from '../../src/parse.mjs'; -import { parseCmapTableFormat14, parseCmapTableFormat0 } from '../../src/tables/cmap.mjs'; +import { parseCmapTableFormat14, parseCmapTableFormat6, parseCmapTableFormat0 } from '../../src/tables/cmap.mjs'; import { parse } from '../../src/opentype.mjs'; import { readFileSync } from 'fs'; const loadSync = (url, opt) => parse(readFileSync(url), opt); @@ -82,4 +82,18 @@ describe('tables/cmap.mjs', function() { const expectedGlyphIds = [1,2,3,4]; assert.deepEqual(glyphIds, expectedGlyphIds); }); + + it('can parse CMAP table format 6', function() { + let font; + assert.doesNotThrow(function() { + font = loadSync('./test/fonts/PDF-Embedded-Calibri-Subset.ttf'); + }); + + // The embedded char IDs for the string "Living Room" in the PDF embedded font + const testString = '!"#"$%&\'(()'; + const glyphIds = font.stringToGlyphIndexes(testString); + + const expectedGlyphIds = [3, 13, 21, 13, 16, 11, 1, 5, 17, 17, 15]; + assert.deepEqual(glyphIds, expectedGlyphIds); + }); }); \ No newline at end of file