parse.js
1 import assert from 'assert'; 2 import { unhex } from './testutil'; 3 import { Parser } from '../src/parse'; 4 5 describe('parse.js', function() { 6 describe('parseUShortList', function() { 7 it('can parse an empty list', function() { 8 const p = new Parser(unhex('0000'), 0); 9 assert.deepEqual(p.parseUShortList(), []); 10 }); 11 12 it('can parse a list', function() { 13 const p = new Parser(unhex('0003 1234 DEAD BEEF'), 0); 14 assert.deepEqual(p.parseUShortList(), [0x1234, 0xdead, 0xbeef]); 15 }); 16 17 it('can parse a list of predefined length', function() { 18 const p = new Parser(unhex('1234 DEAD BEEF 5678 9ABC'), 2); 19 assert.deepEqual(p.parseUShortList(3), [0xdead, 0xbeef, 0x5678]); 20 }); 21 }); 22 23 describe('parseList', function() { 24 it('can parse a list of values', function() { 25 const data = '0003 12 34 56 78 9A BC'; 26 const p = new Parser(unhex(data), 0); 27 assert.deepEqual(p.parseList(Parser.uShort), [0x1234, 0x5678, 0x9abc]); 28 assert.equal(p.relativeOffset, 8); 29 }); 30 31 it('can parse a list of values of predefined length', function() { 32 const data = '12 34 56 78 9A BC'; 33 const p = new Parser(unhex(data), 0); 34 assert.deepEqual(p.parseList(3, Parser.uShort), [0x1234, 0x5678, 0x9abc]); 35 assert.equal(p.relativeOffset, 6); 36 }); 37 }); 38 39 describe('parseRecordList', function() { 40 it('can parse a list of records', function() { 41 const data = '0002 12 34 56 78 9A BC'; 42 const p = new Parser(unhex(data), 0); 43 assert.deepEqual(p.parseRecordList({ a: Parser.byte, b: Parser.uShort }), [ 44 { a: 0x12, b: 0x3456 }, 45 { a: 0x78, b: 0x9abc } 46 ]); 47 assert.equal(p.relativeOffset, 8); 48 }); 49 50 it('can parse an empty list of records', function() { 51 const data = '0000'; 52 const p = new Parser(unhex(data), 0); 53 assert.deepEqual(p.parseRecordList({ a: Parser.byte, b: Parser.uShort }), []); 54 assert.equal(p.relativeOffset, 2); 55 }); 56 57 it('can parse a list of records of predefined length', function() { 58 const data = '12 34 56 78 9A BC'; 59 const p = new Parser(unhex(data), 0); 60 assert.deepEqual(p.parseRecordList(2, { a: Parser.byte, b: Parser.uShort }), [ 61 { a: 0x12, b: 0x3456 }, 62 { a: 0x78, b: 0x9abc } 63 ]); 64 assert.equal(p.relativeOffset, 6); 65 }); 66 }); 67 68 describe('parseListOfLists', function() { 69 it('can parse a list of lists of 16-bit integers', function() { 70 const data = '0003 0008 000E 0016' + // 3 lists 71 '0002 1234 5678' + // list 1 72 '0003 DEAD BEEF FADE' + // list 2 73 '0001 9876'; // list 3 74 const p = new Parser(unhex(data), 0); 75 assert.deepEqual(p.parseListOfLists(), [ 76 [0x1234, 0x5678], 77 [0xdead, 0xbeef, 0xfade], 78 [0x9876] 79 ]); 80 }); 81 82 it('can parse an empty list of lists', function() { 83 const p = new Parser(unhex('0000'), 0); 84 assert.deepEqual(p.parseListOfLists(), []); 85 }); 86 87 it('can parse list of empty lists', function() { 88 const p = new Parser(unhex('0001 0004 0000'), 0); 89 assert.deepEqual(p.parseListOfLists(), [[]]); 90 }); 91 92 it('can parse a list of lists of records', function() { 93 const data = '0002 0006 0012' + // 2 lists 94 '0002 0006 0009 12 34 56 78 9A BC' + // list 1 95 '0001 0004 DE F0 12'; // list 2 96 97 const p = new Parser(unhex(data), 0); 98 function parseRecord() { 99 return { a: p.parseByte(), b: p.parseUShort() }; 100 } 101 102 assert.deepEqual(p.parseListOfLists(parseRecord), [ 103 [{ a: 0x12, b: 0x3456 }, { a: 0x78, b: 0x9abc }], 104 [{ a: 0xde, b: 0xf012 }] 105 ]); 106 }); 107 }); 108 109 describe('parseCoverage', function() { 110 it('should parse a CoverageFormat1 table', function() { 111 // https://www.microsoft.com/typography/OTSPEC/chapter2.htm Example 5 112 const data = '0004 1234' + // coverageOffset + filler 113 '0001 0005 0038 003B 0041 0042 004A'; 114 const p = new Parser(unhex(data), 4); 115 assert.deepEqual(p.parseCoverage(), { 116 format: 1, 117 glyphs: [0x38, 0x3b, 0x41, 0x42, 0x4a] 118 }); 119 assert.equal(p.relativeOffset, 14); 120 }); 121 122 it('should parse a CoverageFormat2 table', function() { 123 // https://www.microsoft.com/typography/OTSPEC/chapter2.htm Example 6 124 const data = '0004 1234' + // coverageOffset + filler 125 '0002 0001 004E 0057 0000'; 126 const p = new Parser(unhex(data), 4); 127 assert.deepEqual(p.parseCoverage(), { 128 format: 2, 129 ranges: [{ start: 0x4e, end: 0x57, index: 0 }] 130 }); 131 assert.equal(p.relativeOffset, 10); 132 }); 133 }); 134 135 describe('parseClassDef', function() { 136 it('should parse a ClassDefFormat1 table', function() { 137 // https://www.microsoft.com/typography/OTSPEC/chapter2.htm Example 7 138 const data = '0001 0032 001A' + 139 '0000 0001 0000 0001 0000 0001 0002 0001 0000 0002 0001 0001 0000' + 140 '0000 0000 0002 0002 0000 0000 0001 0000 0000 0000 0000 0002 0001'; 141 const p = new Parser(unhex(data), 0); 142 assert.deepEqual(p.parseClassDef(), { 143 format: 1, 144 startGlyph: 0x32, 145 classes: [ 146 0, 1, 0, 1, 0, 1, 2, 1, 0, 2, 1, 1, 0, 147 0, 0, 2, 2, 0, 0, 1, 0, 0, 0, 0, 2, 1 148 ] 149 }); 150 assert.equal(p.relativeOffset, 58); 151 }); 152 153 it('should parse a ClassDefFormat2 table', function() { 154 // https://www.microsoft.com/typography/OTSPEC/chapter2.htm Example 8 155 const data = '0002 0003 0030 0031 0002 0040 0041 0003 00D2 00D3 0001'; 156 const p = new Parser(unhex(data), 0); 157 assert.deepEqual(p.parseClassDef(), { 158 format: 2, 159 ranges: [ 160 { start: 0x30, end: 0x31, classId: 2 }, 161 { start: 0x40, end: 0x41, classId: 3 }, 162 { start: 0xd2, end: 0xd3, classId: 1 } 163 ] 164 }); 165 assert.equal(p.relativeOffset, 22); 166 }); 167 }); 168 169 describe('parseScriptList', function() { 170 it('should parse a ScriptList table', function() { 171 // https://www.microsoft.com/typography/OTSPEC/chapter2.htm Examples 1 & 2 172 const data = '0004 1234' + // coverageOffset + filler 173 '0003 68616E69 0014 6B616E61 0018 6C61746E 001C' + // Example 1 174 '0000 0000 0000 0000' + // 2 empty Script Tables 175 '000A 0001 55524420 0016' + // Example 2 176 '0000 FFFF 0003 0000 0001 0002' + // DefLangSys 177 '0000 0003 0003 0000 0001 0002'; // UrduLangSys 178 const p = new Parser(unhex(data), 0); 179 assert.deepEqual(p.parseScriptList(), [ 180 { tag: 'hani', script: { defaultLangSys: undefined, langSysRecords: [] } }, 181 { tag: 'kana', script: { defaultLangSys: undefined, langSysRecords: [] } }, 182 { tag: 'latn', script: { 183 defaultLangSys: { 184 reserved: 0, 185 reqFeatureIndex: 0xffff, 186 featureIndexes: [0, 1, 2] 187 }, 188 langSysRecords: [{ 189 tag: 'URD ', 190 langSys: { 191 reserved: 0, 192 reqFeatureIndex: 3, 193 featureIndexes: [0, 1, 2] 194 } 195 }] 196 } }, 197 ]); 198 assert.equal(p.relativeOffset, 2); 199 }); 200 }); 201 202 describe('parseFeatureList', function() { 203 it('should parse a FeatureList table', function() { 204 // https://www.microsoft.com/typography/OTSPEC/chapter2.htm Example 3 205 const data = '0004 0000' + // table offset + filler 206 '0003 6C696761 0014 6C696761 001A 6C696761 0022' + // feature list 207 // There is an error in the online example, count is 3 for the 3rd feature. 208 '0000 0001 0000 0000 0002 0000 0001 0000 0003 0000 0001 0002'; 209 const p = new Parser(unhex(data), 0); 210 assert.deepEqual(p.parseFeatureList(), [ 211 { tag: 'liga', feature: { featureParams: 0, lookupListIndexes: [0] } }, 212 { tag: 'liga', feature: { featureParams: 0, lookupListIndexes: [0, 1] } }, 213 { tag: 'liga', feature: { featureParams: 0, lookupListIndexes: [0, 1, 2] } } 214 ]); 215 assert.equal(p.relativeOffset, 2); 216 }); 217 }); 218 219 describe('parseLookupList', function() { 220 it('should parse a LookupList table', function() { 221 // https://www.microsoft.com/typography/OTSPEC/chapter2.htm Example 4 222 const data = '0004 0000' + // table offset + filler 223 '0003 0008 0010 0018' + // lookup list 224 '0004 000C 0001 0018' + // FfiFi lookup 225 '0004 000C 0001 0028' + // FflFlFf lookup 226 '0004 000C 0001 0038' + // Eszet lookup 227 '1234 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000' + 228 '5678 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000' + 229 '9ABC'; 230 const lookupTableParsers = [0, 0, 0, 0, Parser.uShort]; 231 const p = new Parser(unhex(data), 0); 232 assert.deepEqual(p.parseLookupList(lookupTableParsers), [ 233 { lookupType: 4, lookupFlag: 0x000c, subtables: [0x1234], markFilteringSet: undefined }, 234 { lookupType: 4, lookupFlag: 0x000c, subtables: [0x5678], markFilteringSet: undefined }, 235 { lookupType: 4, lookupFlag: 0x000c, subtables: [0x9abc], markFilteringSet: undefined }, 236 ]); 237 assert.equal(p.relativeOffset, 2); 238 }); 239 }); 240 });