PageRenderTime 53ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/gfx/ots/src/gsub.cc

https://bitbucket.org/Jiten/mozilla-central
C++ | 683 lines | 520 code | 87 blank | 76 comment | 118 complexity | 23d1b52b7dbd9f09711d4ee0974a7a72 MD5 | raw file
Possible License(s): JSON, Apache-2.0, 0BSD, LGPL-3.0, BSD-2-Clause, AGPL-1.0, MPL-2.0, GPL-2.0, LGPL-2.1, MIT, MPL-2.0-no-copyleft-exception, BSD-3-Clause
  1. // Copyright (c) 2011 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "gsub.h"
  5. #include <limits>
  6. #include <vector>
  7. #include "gdef.h"
  8. #include "gpos.h"
  9. #include "layout.h"
  10. #include "maxp.h"
  11. // GSUB - The Glyph Substitution Table
  12. // http://www.microsoft.com/typography/otspec/gsub.htm
  13. #define TABLE_NAME "GSUB"
  14. namespace {
  15. // The GSUB header size
  16. const size_t kGsubHeaderSize = 8;
  17. enum GSUB_TYPE {
  18. GSUB_TYPE_SINGLE = 1,
  19. GSUB_TYPE_MULTIPLE = 2,
  20. GSUB_TYPE_ALTERNATE = 3,
  21. GSUB_TYPE_LIGATURE = 4,
  22. GSUB_TYPE_CONTEXT = 5,
  23. GSUB_TYPE_CHANGING_CONTEXT = 6,
  24. GSUB_TYPE_EXTENSION_SUBSTITUTION = 7,
  25. GSUB_TYPE_REVERSE_CHAINING_CONTEXT_SINGLE = 8,
  26. GSUB_TYPE_RESERVED = 9
  27. };
  28. // Lookup type parsers.
  29. bool ParseSingleSubstitution(const ots::OpenTypeFile *file,
  30. const uint8_t *data, const size_t length);
  31. bool ParseMutipleSubstitution(const ots::OpenTypeFile *file,
  32. const uint8_t *data, const size_t length);
  33. bool ParseAlternateSubstitution(const ots::OpenTypeFile *file,
  34. const uint8_t *data, const size_t length);
  35. bool ParseLigatureSubstitution(const ots::OpenTypeFile *file,
  36. const uint8_t *data, const size_t length);
  37. bool ParseContextSubstitution(const ots::OpenTypeFile *file,
  38. const uint8_t *data, const size_t length);
  39. bool ParseChainingContextSubstitution(const ots::OpenTypeFile *file,
  40. const uint8_t *data,
  41. const size_t length);
  42. bool ParseExtensionSubstitution(const ots::OpenTypeFile *file,
  43. const uint8_t *data, const size_t length);
  44. bool ParseReverseChainingContextSingleSubstitution(
  45. const ots::OpenTypeFile *file, const uint8_t *data, const size_t length);
  46. const ots::LookupSubtableParser::TypeParser kGsubTypeParsers[] = {
  47. {GSUB_TYPE_SINGLE, ParseSingleSubstitution},
  48. {GSUB_TYPE_MULTIPLE, ParseMutipleSubstitution},
  49. {GSUB_TYPE_ALTERNATE, ParseAlternateSubstitution},
  50. {GSUB_TYPE_LIGATURE, ParseLigatureSubstitution},
  51. {GSUB_TYPE_CONTEXT, ParseContextSubstitution},
  52. {GSUB_TYPE_CHANGING_CONTEXT, ParseChainingContextSubstitution},
  53. {GSUB_TYPE_EXTENSION_SUBSTITUTION, ParseExtensionSubstitution},
  54. {GSUB_TYPE_REVERSE_CHAINING_CONTEXT_SINGLE,
  55. ParseReverseChainingContextSingleSubstitution}
  56. };
  57. // TODO(bashi): Port Chromium's arraysize macro and use it instead of sizeof().
  58. const ots::LookupSubtableParser kGsubLookupSubtableParser = {
  59. sizeof(kGsubTypeParsers) / sizeof(kGsubTypeParsers[0]),
  60. GSUB_TYPE_EXTENSION_SUBSTITUTION, kGsubTypeParsers
  61. };
  62. // Lookup Type 1:
  63. // Single Substitution Subtable
  64. bool ParseSingleSubstitution(const ots::OpenTypeFile *file,
  65. const uint8_t *data, const size_t length) {
  66. ots::Buffer subtable(data, length);
  67. uint16_t format = 0;
  68. uint16_t offset_coverage = 0;
  69. if (!subtable.ReadU16(&format) ||
  70. !subtable.ReadU16(&offset_coverage)) {
  71. return OTS_FAILURE();
  72. }
  73. const uint16_t num_glyphs = file->maxp->num_glyphs;
  74. if (format == 1) {
  75. // Parse SingleSubstFormat1
  76. int16_t delta_glyph_id = 0;
  77. if (!subtable.ReadS16(&delta_glyph_id)) {
  78. return OTS_FAILURE();
  79. }
  80. if (std::abs(delta_glyph_id) >= num_glyphs) {
  81. return OTS_FAILURE();
  82. }
  83. } else if (format == 2) {
  84. // Parse SingleSubstFormat2
  85. uint16_t glyph_count = 0;
  86. if (!subtable.ReadU16(&glyph_count)) {
  87. return OTS_FAILURE();
  88. }
  89. if (glyph_count > num_glyphs) {
  90. return OTS_FAILURE();
  91. }
  92. for (unsigned i = 0; i < glyph_count; ++i) {
  93. uint16_t substitute = 0;
  94. if (!subtable.ReadU16(&substitute)) {
  95. return OTS_FAILURE();
  96. }
  97. if (substitute >= num_glyphs) {
  98. OTS_WARNING("too large substitute: %u", substitute);
  99. return OTS_FAILURE();
  100. }
  101. }
  102. } else {
  103. return OTS_FAILURE();
  104. }
  105. if (offset_coverage < subtable.offset() || offset_coverage >= length) {
  106. return OTS_FAILURE();
  107. }
  108. if (!ots::ParseCoverageTable(data + offset_coverage,
  109. length - offset_coverage, num_glyphs)) {
  110. return OTS_FAILURE();
  111. }
  112. return true;
  113. }
  114. bool ParseSequenceTable(const uint8_t *data, const size_t length,
  115. const uint16_t num_glyphs) {
  116. ots::Buffer subtable(data, length);
  117. uint16_t glyph_count = 0;
  118. if (!subtable.ReadU16(&glyph_count)) {
  119. return OTS_FAILURE();
  120. }
  121. if (glyph_count > num_glyphs) {
  122. return OTS_FAILURE();
  123. }
  124. for (unsigned i = 0; i < glyph_count; ++i) {
  125. uint16_t substitute = 0;
  126. if (!subtable.ReadU16(&substitute)) {
  127. return OTS_FAILURE();
  128. }
  129. if (substitute >= num_glyphs) {
  130. return OTS_FAILURE();
  131. }
  132. }
  133. return true;
  134. }
  135. // Lookup Type 2:
  136. // Multiple Substitution Subtable
  137. bool ParseMutipleSubstitution(const ots::OpenTypeFile *file,
  138. const uint8_t *data, const size_t length) {
  139. ots::Buffer subtable(data, length);
  140. uint16_t format = 0;
  141. uint16_t offset_coverage = 0;
  142. uint16_t sequence_count = 0;
  143. if (!subtable.ReadU16(&format) ||
  144. !subtable.ReadU16(&offset_coverage) ||
  145. !subtable.ReadU16(&sequence_count)) {
  146. return OTS_FAILURE();
  147. }
  148. if (format != 1) {
  149. return OTS_FAILURE();
  150. }
  151. const uint16_t num_glyphs = file->maxp->num_glyphs;
  152. const unsigned sequence_end = static_cast<unsigned>(6) +
  153. sequence_count * 2;
  154. if (sequence_end > std::numeric_limits<uint16_t>::max()) {
  155. return OTS_FAILURE();
  156. }
  157. for (unsigned i = 0; i < sequence_count; ++i) {
  158. uint16_t offset_sequence = 0;
  159. if (!subtable.ReadU16(&offset_sequence)) {
  160. return OTS_FAILURE();
  161. }
  162. if (offset_sequence < sequence_end || offset_sequence >= length) {
  163. return OTS_FAILURE();
  164. }
  165. if (!ParseSequenceTable(data + offset_sequence, length - offset_sequence,
  166. num_glyphs)) {
  167. return OTS_FAILURE();
  168. }
  169. }
  170. if (offset_coverage < sequence_end || offset_coverage >= length) {
  171. return OTS_FAILURE();
  172. }
  173. if (!ots::ParseCoverageTable(data + offset_coverage,
  174. length - offset_coverage, num_glyphs)) {
  175. return OTS_FAILURE();
  176. }
  177. return true;
  178. }
  179. bool ParseAlternateSetTable(const uint8_t *data, const size_t length,
  180. const uint16_t num_glyphs) {
  181. ots::Buffer subtable(data, length);
  182. uint16_t glyph_count = 0;
  183. if (!subtable.ReadU16(&glyph_count)) {
  184. return OTS_FAILURE();
  185. }
  186. if (glyph_count > num_glyphs) {
  187. return OTS_FAILURE();
  188. }
  189. for (unsigned i = 0; i < glyph_count; ++i) {
  190. uint16_t alternate = 0;
  191. if (!subtable.ReadU16(&alternate)) {
  192. return OTS_FAILURE();
  193. }
  194. if (alternate >= num_glyphs) {
  195. OTS_WARNING("too large alternate: %u", alternate);
  196. return OTS_FAILURE();
  197. }
  198. }
  199. return true;
  200. }
  201. // Lookup Type 3:
  202. // Alternate Substitution Subtable
  203. bool ParseAlternateSubstitution(const ots::OpenTypeFile *file,
  204. const uint8_t *data, const size_t length) {
  205. ots::Buffer subtable(data, length);
  206. uint16_t format = 0;
  207. uint16_t offset_coverage = 0;
  208. uint16_t alternate_set_count = 0;
  209. if (!subtable.ReadU16(&format) ||
  210. !subtable.ReadU16(&offset_coverage) ||
  211. !subtable.ReadU16(&alternate_set_count)) {
  212. return OTS_FAILURE();
  213. }
  214. if (format != 1) {
  215. return OTS_FAILURE();
  216. }
  217. const uint16_t num_glyphs = file->maxp->num_glyphs;
  218. const unsigned alternate_set_end = static_cast<unsigned>(6) +
  219. alternate_set_count * 2;
  220. if (alternate_set_end > std::numeric_limits<uint16_t>::max()) {
  221. return OTS_FAILURE();
  222. }
  223. for (unsigned i = 0; i < alternate_set_count; ++i) {
  224. uint16_t offset_alternate_set = 0;
  225. if (!subtable.ReadU16(&offset_alternate_set)) {
  226. return OTS_FAILURE();
  227. }
  228. if (offset_alternate_set < alternate_set_end ||
  229. offset_alternate_set >= length) {
  230. return OTS_FAILURE();
  231. }
  232. if (!ParseAlternateSetTable(data + offset_alternate_set,
  233. length - offset_alternate_set,
  234. num_glyphs)) {
  235. return OTS_FAILURE();
  236. }
  237. }
  238. if (offset_coverage < alternate_set_end || offset_coverage >= length) {
  239. return OTS_FAILURE();
  240. }
  241. if (!ots::ParseCoverageTable(data + offset_coverage,
  242. length - offset_coverage, num_glyphs)) {
  243. return OTS_FAILURE();
  244. }
  245. return true;
  246. }
  247. bool ParseLigatureTable(const uint8_t *data, const size_t length,
  248. const uint16_t num_glyphs) {
  249. ots::Buffer subtable(data, length);
  250. uint16_t lig_glyph = 0;
  251. uint16_t comp_count = 0;
  252. if (!subtable.ReadU16(&lig_glyph) ||
  253. !subtable.ReadU16(&comp_count)) {
  254. return OTS_FAILURE();
  255. }
  256. if (lig_glyph >= num_glyphs) {
  257. OTS_WARNING("too large lig_glyph: %u", lig_glyph);
  258. return OTS_FAILURE();
  259. }
  260. if (comp_count == 0 || comp_count > num_glyphs) {
  261. return OTS_FAILURE();
  262. }
  263. for (unsigned i = 0; i < comp_count - static_cast<unsigned>(1); ++i) {
  264. uint16_t component = 0;
  265. if (!subtable.ReadU16(&component)) {
  266. return OTS_FAILURE();
  267. }
  268. if (component >= num_glyphs) {
  269. return OTS_FAILURE();
  270. }
  271. }
  272. return true;
  273. }
  274. bool ParseLigatureSetTable(const uint8_t *data, const size_t length,
  275. const uint16_t num_glyphs) {
  276. ots::Buffer subtable(data, length);
  277. uint16_t ligature_count = 0;
  278. if (!subtable.ReadU16(&ligature_count)) {
  279. return OTS_FAILURE();
  280. }
  281. const unsigned ligature_end = static_cast<unsigned>(2) + ligature_count * 2;
  282. if (ligature_end > std::numeric_limits<uint16_t>::max()) {
  283. return OTS_FAILURE();
  284. }
  285. for (unsigned i = 0; i < ligature_count; ++i) {
  286. uint16_t offset_ligature = 0;
  287. if (!subtable.ReadU16(&offset_ligature)) {
  288. return OTS_FAILURE();
  289. }
  290. if (offset_ligature < ligature_end || offset_ligature >= length) {
  291. return OTS_FAILURE();
  292. }
  293. if (!ParseLigatureTable(data + offset_ligature, length - offset_ligature,
  294. num_glyphs)) {
  295. return OTS_FAILURE();
  296. }
  297. }
  298. return true;
  299. }
  300. // Lookup Type 4:
  301. // Ligature Substitution Subtable
  302. bool ParseLigatureSubstitution(const ots::OpenTypeFile *file,
  303. const uint8_t *data, const size_t length) {
  304. ots::Buffer subtable(data, length);
  305. uint16_t format = 0;
  306. uint16_t offset_coverage = 0;
  307. uint16_t lig_set_count = 0;
  308. if (!subtable.ReadU16(&format) ||
  309. !subtable.ReadU16(&offset_coverage) ||
  310. !subtable.ReadU16(&lig_set_count)) {
  311. return OTS_FAILURE();
  312. }
  313. if (format != 1) {
  314. return OTS_FAILURE();
  315. }
  316. const uint16_t num_glyphs = file->maxp->num_glyphs;
  317. const unsigned ligature_set_end = static_cast<unsigned>(6) +
  318. lig_set_count * 2;
  319. if (ligature_set_end > std::numeric_limits<uint16_t>::max()) {
  320. return OTS_FAILURE();
  321. }
  322. for (unsigned i = 0; i < lig_set_count; ++i) {
  323. uint16_t offset_ligature_set = 0;
  324. if (!subtable.ReadU16(&offset_ligature_set)) {
  325. return OTS_FAILURE();
  326. }
  327. if (offset_ligature_set < ligature_set_end ||
  328. offset_ligature_set >= length) {
  329. return OTS_FAILURE();
  330. }
  331. if (!ParseLigatureSetTable(data + offset_ligature_set,
  332. length - offset_ligature_set, num_glyphs)) {
  333. return OTS_FAILURE();
  334. }
  335. }
  336. if (offset_coverage < ligature_set_end || offset_coverage >= length) {
  337. return OTS_FAILURE();
  338. }
  339. if (!ots::ParseCoverageTable(data + offset_coverage,
  340. length - offset_coverage, num_glyphs)) {
  341. return OTS_FAILURE();
  342. }
  343. return true;
  344. }
  345. // Lookup Type 5:
  346. // Contextual Substitution Subtable
  347. bool ParseContextSubstitution(const ots::OpenTypeFile *file,
  348. const uint8_t *data, const size_t length) {
  349. return ots::ParseContextSubtable(data, length, file->maxp->num_glyphs,
  350. file->gsub->num_lookups);
  351. }
  352. // Lookup Type 6:
  353. // Chaining Contextual Substitution Subtable
  354. bool ParseChainingContextSubstitution(const ots::OpenTypeFile *file,
  355. const uint8_t *data,
  356. const size_t length) {
  357. return ots::ParseChainingContextSubtable(data, length,
  358. file->maxp->num_glyphs,
  359. file->gsub->num_lookups);
  360. }
  361. // Lookup Type 7:
  362. // Extension Substition
  363. bool ParseExtensionSubstitution(const ots::OpenTypeFile *file,
  364. const uint8_t *data, const size_t length) {
  365. return ots::ParseExtensionSubtable(file, data, length,
  366. &kGsubLookupSubtableParser);
  367. }
  368. // Lookup Type 8:
  369. // Reverse Chaining Contexual Single Substitution Subtable
  370. bool ParseReverseChainingContextSingleSubstitution(
  371. const ots::OpenTypeFile *file, const uint8_t *data, const size_t length) {
  372. ots::Buffer subtable(data, length);
  373. uint16_t format = 0;
  374. uint16_t offset_coverage = 0;
  375. if (!subtable.ReadU16(&format) ||
  376. !subtable.ReadU16(&offset_coverage)) {
  377. return OTS_FAILURE();
  378. }
  379. const uint16_t num_glyphs = file->maxp->num_glyphs;
  380. uint16_t backtrack_glyph_count = 0;
  381. if (!subtable.ReadU16(&backtrack_glyph_count)) {
  382. return OTS_FAILURE();
  383. }
  384. if (backtrack_glyph_count > num_glyphs) {
  385. return OTS_FAILURE();
  386. }
  387. std::vector<uint16_t> offsets_backtrack;
  388. offsets_backtrack.reserve(backtrack_glyph_count);
  389. for (unsigned i = 0; i < backtrack_glyph_count; ++i) {
  390. uint16_t offset = 0;
  391. if (!subtable.ReadU16(&offset)) {
  392. return OTS_FAILURE();
  393. }
  394. offsets_backtrack.push_back(offset);
  395. }
  396. uint16_t lookahead_glyph_count = 0;
  397. if (!subtable.ReadU16(&lookahead_glyph_count)) {
  398. return OTS_FAILURE();
  399. }
  400. if (lookahead_glyph_count > num_glyphs) {
  401. return OTS_FAILURE();
  402. }
  403. std::vector<uint16_t> offsets_lookahead;
  404. offsets_lookahead.reserve(lookahead_glyph_count);
  405. for (unsigned i = 0; i < lookahead_glyph_count; ++i) {
  406. uint16_t offset = 0;
  407. if (!subtable.ReadU16(&offset)) {
  408. return OTS_FAILURE();
  409. }
  410. offsets_lookahead.push_back(offset);
  411. }
  412. uint16_t glyph_count = 0;
  413. if (!subtable.ReadU16(&glyph_count)) {
  414. return OTS_FAILURE();
  415. }
  416. if (glyph_count > num_glyphs) {
  417. return OTS_FAILURE();
  418. }
  419. for (unsigned i = 0; i < glyph_count; ++i) {
  420. uint16_t substitute = 0;
  421. if (!subtable.ReadU16(&substitute)) {
  422. return OTS_FAILURE();
  423. }
  424. if (substitute >= num_glyphs) {
  425. return OTS_FAILURE();
  426. }
  427. }
  428. const unsigned substitute_end = static_cast<unsigned>(10) +
  429. (backtrack_glyph_count + lookahead_glyph_count + glyph_count) * 2;
  430. if (substitute_end > std::numeric_limits<uint16_t>::max()) {
  431. return OTS_FAILURE();
  432. }
  433. if (offset_coverage < substitute_end || offset_coverage >= length) {
  434. return OTS_FAILURE();
  435. }
  436. if (!ots::ParseCoverageTable(data + offset_coverage,
  437. length - offset_coverage, num_glyphs)) {
  438. return OTS_FAILURE();
  439. }
  440. for (unsigned i = 0; i < backtrack_glyph_count; ++i) {
  441. if (offsets_backtrack[i] < substitute_end ||
  442. offsets_backtrack[i] >= length) {
  443. return OTS_FAILURE();
  444. }
  445. if (!ots::ParseCoverageTable(data + offsets_backtrack[i],
  446. length - offsets_backtrack[i], num_glyphs)) {
  447. return OTS_FAILURE();
  448. }
  449. }
  450. for (unsigned i = 0; i < lookahead_glyph_count; ++i) {
  451. if (offsets_lookahead[i] < substitute_end ||
  452. offsets_lookahead[i] >= length) {
  453. return OTS_FAILURE();
  454. }
  455. if (!ots::ParseCoverageTable(data + offsets_lookahead[i],
  456. length - offsets_lookahead[i], num_glyphs)) {
  457. return OTS_FAILURE();
  458. }
  459. }
  460. return true;
  461. }
  462. } // namespace
  463. #define DROP_THIS_TABLE \
  464. do { \
  465. file->gsub->data = 0; \
  466. file->gsub->length = 0; \
  467. OTS_FAILURE_MSG("OpenType layout data discarded"); \
  468. } while (0)
  469. namespace ots {
  470. // As far as I checked, following fonts contain invalid values in GSUB table.
  471. // OTS will drop their GSUB table.
  472. //
  473. // # too large substitute (value is 0xFFFF)
  474. // kaiu.ttf
  475. // mingliub2.ttf
  476. // mingliub1.ttf
  477. // mingliub0.ttf
  478. // GraublauWeb.otf
  479. // GraublauWebBold.otf
  480. //
  481. // # too large alternate (value is 0xFFFF)
  482. // ManchuFont.ttf
  483. //
  484. // # bad offset to lang sys table (NULL offset)
  485. // DejaVuMonoSansBold.ttf
  486. // DejaVuMonoSansBoldOblique.ttf
  487. // DejaVuMonoSansOblique.ttf
  488. // DejaVuSansMono-BoldOblique.ttf
  489. // DejaVuSansMono-Oblique.ttf
  490. // DejaVuSansMono-Bold.ttf
  491. //
  492. // # bad start coverage index
  493. // GenBasBI.ttf
  494. // GenBasI.ttf
  495. // AndBasR.ttf
  496. // GenBkBasI.ttf
  497. // CharisSILR.ttf
  498. // CharisSILBI.ttf
  499. // CharisSILI.ttf
  500. // CharisSILB.ttf
  501. // DoulosSILR.ttf
  502. // CharisSILBI.ttf
  503. // GenBkBasB.ttf
  504. // GenBkBasR.ttf
  505. // GenBkBasBI.ttf
  506. // GenBasB.ttf
  507. // GenBasR.ttf
  508. //
  509. // # glyph range is overlapping
  510. // KacstTitleL.ttf
  511. // KacstDecorative.ttf
  512. // KacstTitle.ttf
  513. // KacstArt.ttf
  514. // KacstPoster.ttf
  515. // KacstQurn.ttf
  516. // KacstDigital.ttf
  517. // KacstBook.ttf
  518. // KacstFarsi.ttf
  519. bool ots_gsub_parse(OpenTypeFile *file, const uint8_t *data, size_t length) {
  520. // Parsing gsub table requires |file->maxp->num_glyphs|
  521. if (!file->maxp) {
  522. return OTS_FAILURE();
  523. }
  524. Buffer table(data, length);
  525. OpenTypeGSUB *gsub = new OpenTypeGSUB;
  526. file->gsub = gsub;
  527. uint32_t version = 0;
  528. uint16_t offset_script_list = 0;
  529. uint16_t offset_feature_list = 0;
  530. uint16_t offset_lookup_list = 0;
  531. if (!table.ReadU32(&version) ||
  532. !table.ReadU16(&offset_script_list) ||
  533. !table.ReadU16(&offset_feature_list) ||
  534. !table.ReadU16(&offset_lookup_list)) {
  535. OTS_WARNING("incomplete GSUB table");
  536. DROP_THIS_TABLE;
  537. return true;
  538. }
  539. if (version != 0x00010000) {
  540. OTS_WARNING("bad GSUB version");
  541. DROP_THIS_TABLE;
  542. return true;
  543. }
  544. if ((offset_script_list < kGsubHeaderSize ||
  545. offset_script_list >= length) ||
  546. (offset_feature_list < kGsubHeaderSize ||
  547. offset_feature_list >= length) ||
  548. (offset_lookup_list < kGsubHeaderSize ||
  549. offset_lookup_list >= length)) {
  550. OTS_WARNING("bad offset in GSUB header");
  551. DROP_THIS_TABLE;
  552. return true;
  553. }
  554. if (!ParseLookupListTable(file, data + offset_lookup_list,
  555. length - offset_lookup_list,
  556. &kGsubLookupSubtableParser,
  557. &gsub->num_lookups)) {
  558. OTS_WARNING("failed to parse lookup list table");
  559. DROP_THIS_TABLE;
  560. return true;
  561. }
  562. uint16_t num_features = 0;
  563. if (!ParseFeatureListTable(data + offset_feature_list,
  564. length - offset_feature_list, gsub->num_lookups,
  565. &num_features)) {
  566. OTS_WARNING("failed to parse feature list table");
  567. DROP_THIS_TABLE;
  568. return true;
  569. }
  570. if (!ParseScriptListTable(data + offset_script_list,
  571. length - offset_script_list, num_features)) {
  572. OTS_WARNING("failed to parse script list table");
  573. DROP_THIS_TABLE;
  574. return true;
  575. }
  576. gsub->data = data;
  577. gsub->length = length;
  578. return true;
  579. }
  580. bool ots_gsub_should_serialise(OpenTypeFile *file) {
  581. const bool needed_tables_dropped =
  582. (file->gdef && file->gdef->data == NULL) ||
  583. (file->gpos && file->gpos->data == NULL);
  584. return file->gsub != NULL && file->gsub->data != NULL
  585. && !needed_tables_dropped;
  586. }
  587. bool ots_gsub_serialise(OTSStream *out, OpenTypeFile *file) {
  588. if (!out->Write(file->gsub->data, file->gsub->length)) {
  589. return OTS_FAILURE();
  590. }
  591. return true;
  592. }
  593. void ots_gsub_free(OpenTypeFile *file) {
  594. delete file->gsub;
  595. }
  596. } // namespace ots