PageRenderTime 45ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/gfx/harfbuzz/src/hb-ot-layout-gsub-private.hh

https://bitbucket.org/careytilden/mozilla-release
C++ Header | 932 lines | 698 code | 164 blank | 70 comment | 91 complexity | 119273eec7945233a3b62a800eaa0e53 MD5 | raw file
Possible License(s): Apache-2.0, LGPL-2.1, 0BSD, AGPL-1.0, BSD-3-Clause, GPL-2.0, LGPL-3.0, MIT, MPL-2.0-no-copyleft-exception, JSON
  1. /*
  2. * Copyright (C) 2007,2008,2009,2010 Red Hat, Inc.
  3. * Copyright (C) 2010 Google, Inc.
  4. *
  5. * This is part of HarfBuzz, a text shaping library.
  6. *
  7. * Permission is hereby granted, without written agreement and without
  8. * license or royalty fees, to use, copy, modify, and distribute this
  9. * software and its documentation for any purpose, provided that the
  10. * above copyright notice and the following two paragraphs appear in
  11. * all copies of this software.
  12. *
  13. * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
  14. * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
  15. * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
  16. * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
  17. * DAMAGE.
  18. *
  19. * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
  20. * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  21. * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
  22. * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
  23. * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
  24. *
  25. * Red Hat Author(s): Behdad Esfahbod
  26. * Google Author(s): Behdad Esfahbod
  27. */
  28. #ifndef HB_OT_LAYOUT_GSUB_PRIVATE_HH
  29. #define HB_OT_LAYOUT_GSUB_PRIVATE_HH
  30. #include "hb-ot-layout-gsubgpos-private.hh"
  31. HB_BEGIN_DECLS
  32. struct SingleSubstFormat1
  33. {
  34. friend struct SingleSubst;
  35. private:
  36. inline bool apply (hb_apply_context_t *c) const
  37. {
  38. TRACE_APPLY ();
  39. hb_codepoint_t glyph_id = c->buffer->info[c->buffer->i].codepoint;
  40. unsigned int index = (this+coverage) (glyph_id);
  41. if (likely (index == NOT_COVERED))
  42. return false;
  43. glyph_id += deltaGlyphID;
  44. c->replace_glyph (glyph_id);
  45. return true;
  46. }
  47. inline bool sanitize (hb_sanitize_context_t *c) {
  48. TRACE_SANITIZE ();
  49. return coverage.sanitize (c, this)
  50. && deltaGlyphID.sanitize (c);
  51. }
  52. private:
  53. USHORT format; /* Format identifier--format = 1 */
  54. OffsetTo<Coverage>
  55. coverage; /* Offset to Coverage table--from
  56. * beginning of Substitution table */
  57. SHORT deltaGlyphID; /* Add to original GlyphID to get
  58. * substitute GlyphID */
  59. public:
  60. DEFINE_SIZE_STATIC (6);
  61. };
  62. struct SingleSubstFormat2
  63. {
  64. friend struct SingleSubst;
  65. private:
  66. inline bool apply (hb_apply_context_t *c) const
  67. {
  68. TRACE_APPLY ();
  69. hb_codepoint_t glyph_id = c->buffer->info[c->buffer->i].codepoint;
  70. unsigned int index = (this+coverage) (glyph_id);
  71. if (likely (index == NOT_COVERED))
  72. return false;
  73. if (unlikely (index >= substitute.len))
  74. return false;
  75. glyph_id = substitute[index];
  76. c->replace_glyph (glyph_id);
  77. return true;
  78. }
  79. inline bool sanitize (hb_sanitize_context_t *c) {
  80. TRACE_SANITIZE ();
  81. return coverage.sanitize (c, this)
  82. && substitute.sanitize (c);
  83. }
  84. private:
  85. USHORT format; /* Format identifier--format = 2 */
  86. OffsetTo<Coverage>
  87. coverage; /* Offset to Coverage table--from
  88. * beginning of Substitution table */
  89. ArrayOf<GlyphID>
  90. substitute; /* Array of substitute
  91. * GlyphIDs--ordered by Coverage Index */
  92. public:
  93. DEFINE_SIZE_ARRAY (6, substitute);
  94. };
  95. struct SingleSubst
  96. {
  97. friend struct SubstLookupSubTable;
  98. private:
  99. inline bool apply (hb_apply_context_t *c) const
  100. {
  101. TRACE_APPLY ();
  102. switch (u.format) {
  103. case 1: return u.format1.apply (c);
  104. case 2: return u.format2.apply (c);
  105. default:return false;
  106. }
  107. }
  108. inline bool sanitize (hb_sanitize_context_t *c) {
  109. TRACE_SANITIZE ();
  110. if (!u.format.sanitize (c)) return false;
  111. switch (u.format) {
  112. case 1: return u.format1.sanitize (c);
  113. case 2: return u.format2.sanitize (c);
  114. default:return true;
  115. }
  116. }
  117. private:
  118. union {
  119. USHORT format; /* Format identifier */
  120. SingleSubstFormat1 format1;
  121. SingleSubstFormat2 format2;
  122. } u;
  123. };
  124. struct Sequence
  125. {
  126. friend struct MultipleSubstFormat1;
  127. private:
  128. inline bool apply (hb_apply_context_t *c) const
  129. {
  130. TRACE_APPLY ();
  131. if (unlikely (!substitute.len))
  132. return false;
  133. if (c->property & HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE)
  134. c->guess_glyph_class (HB_OT_LAYOUT_GLYPH_CLASS_BASE_GLYPH);
  135. c->replace_glyphs_be16 (1, substitute.len, (const uint16_t *) substitute.array);
  136. return true;
  137. }
  138. public:
  139. inline bool sanitize (hb_sanitize_context_t *c) {
  140. TRACE_SANITIZE ();
  141. return substitute.sanitize (c);
  142. }
  143. private:
  144. ArrayOf<GlyphID>
  145. substitute; /* String of GlyphIDs to substitute */
  146. public:
  147. DEFINE_SIZE_ARRAY (2, substitute);
  148. };
  149. struct MultipleSubstFormat1
  150. {
  151. friend struct MultipleSubst;
  152. private:
  153. inline bool apply (hb_apply_context_t *c) const
  154. {
  155. TRACE_APPLY ();
  156. unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
  157. if (likely (index == NOT_COVERED))
  158. return false;
  159. return (this+sequence[index]).apply (c);
  160. }
  161. inline bool sanitize (hb_sanitize_context_t *c) {
  162. TRACE_SANITIZE ();
  163. return coverage.sanitize (c, this)
  164. && sequence.sanitize (c, this);
  165. }
  166. private:
  167. USHORT format; /* Format identifier--format = 1 */
  168. OffsetTo<Coverage>
  169. coverage; /* Offset to Coverage table--from
  170. * beginning of Substitution table */
  171. OffsetArrayOf<Sequence>
  172. sequence; /* Array of Sequence tables
  173. * ordered by Coverage Index */
  174. public:
  175. DEFINE_SIZE_ARRAY (6, sequence);
  176. };
  177. struct MultipleSubst
  178. {
  179. friend struct SubstLookupSubTable;
  180. private:
  181. inline bool apply (hb_apply_context_t *c) const
  182. {
  183. TRACE_APPLY ();
  184. switch (u.format) {
  185. case 1: return u.format1.apply (c);
  186. default:return false;
  187. }
  188. }
  189. inline bool sanitize (hb_sanitize_context_t *c) {
  190. TRACE_SANITIZE ();
  191. if (!u.format.sanitize (c)) return false;
  192. switch (u.format) {
  193. case 1: return u.format1.sanitize (c);
  194. default:return true;
  195. }
  196. }
  197. private:
  198. union {
  199. USHORT format; /* Format identifier */
  200. MultipleSubstFormat1 format1;
  201. } u;
  202. };
  203. typedef ArrayOf<GlyphID> AlternateSet; /* Array of alternate GlyphIDs--in
  204. * arbitrary order */
  205. struct AlternateSubstFormat1
  206. {
  207. friend struct AlternateSubst;
  208. private:
  209. inline bool apply (hb_apply_context_t *c) const
  210. {
  211. TRACE_APPLY ();
  212. hb_codepoint_t glyph_id = c->buffer->info[c->buffer->i].codepoint;
  213. hb_mask_t glyph_mask = c->buffer->info[c->buffer->i].mask;
  214. hb_mask_t lookup_mask = c->lookup_mask;
  215. unsigned int index = (this+coverage) (glyph_id);
  216. if (likely (index == NOT_COVERED))
  217. return false;
  218. const AlternateSet &alt_set = this+alternateSet[index];
  219. if (unlikely (!alt_set.len))
  220. return false;
  221. /* Note: This breaks badly if two features enabled this lookup together. */
  222. unsigned int shift = _hb_ctz (lookup_mask);
  223. unsigned int alt_index = ((lookup_mask & glyph_mask) >> shift);
  224. if (unlikely (alt_index > alt_set.len || alt_index == 0))
  225. return false;
  226. glyph_id = alt_set[alt_index - 1];
  227. c->replace_glyph (glyph_id);
  228. return true;
  229. }
  230. inline bool sanitize (hb_sanitize_context_t *c) {
  231. TRACE_SANITIZE ();
  232. return coverage.sanitize (c, this)
  233. && alternateSet.sanitize (c, this);
  234. }
  235. private:
  236. USHORT format; /* Format identifier--format = 1 */
  237. OffsetTo<Coverage>
  238. coverage; /* Offset to Coverage table--from
  239. * beginning of Substitution table */
  240. OffsetArrayOf<AlternateSet>
  241. alternateSet; /* Array of AlternateSet tables
  242. * ordered by Coverage Index */
  243. public:
  244. DEFINE_SIZE_ARRAY (6, alternateSet);
  245. };
  246. struct AlternateSubst
  247. {
  248. friend struct SubstLookupSubTable;
  249. private:
  250. inline bool apply (hb_apply_context_t *c) const
  251. {
  252. TRACE_APPLY ();
  253. switch (u.format) {
  254. case 1: return u.format1.apply (c);
  255. default:return false;
  256. }
  257. }
  258. inline bool sanitize (hb_sanitize_context_t *c) {
  259. TRACE_SANITIZE ();
  260. if (!u.format.sanitize (c)) return false;
  261. switch (u.format) {
  262. case 1: return u.format1.sanitize (c);
  263. default:return true;
  264. }
  265. }
  266. private:
  267. union {
  268. USHORT format; /* Format identifier */
  269. AlternateSubstFormat1 format1;
  270. } u;
  271. };
  272. struct Ligature
  273. {
  274. friend struct LigatureSet;
  275. private:
  276. inline bool apply (hb_apply_context_t *c) const
  277. {
  278. TRACE_APPLY ();
  279. unsigned int i;
  280. unsigned int j = c->buffer->i;
  281. unsigned int count = component.len;
  282. unsigned int end = MIN (c->buffer->len, j + c->context_length);
  283. if (unlikely (j >= end))
  284. return false;
  285. bool first_was_mark = (c->property & HB_OT_LAYOUT_GLYPH_CLASS_MARK);
  286. bool found_non_mark = false;
  287. for (i = 1; i < count; i++)
  288. {
  289. unsigned int property;
  290. do
  291. {
  292. j++;
  293. if (unlikely (j == end))
  294. return false;
  295. } while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->info[j], c->lookup_props, &property));
  296. found_non_mark |= !(property & HB_OT_LAYOUT_GLYPH_CLASS_MARK);
  297. if (likely (c->buffer->info[j].codepoint != component[i]))
  298. return false;
  299. }
  300. if (first_was_mark && found_non_mark)
  301. c->guess_glyph_class (HB_OT_LAYOUT_GLYPH_CLASS_LIGATURE);
  302. /* Allocate new ligature id */
  303. unsigned int lig_id = allocate_lig_id (c->buffer);
  304. c->buffer->info[c->buffer->i].lig_comp() = 0;
  305. c->buffer->info[c->buffer->i].lig_id() = lig_id;
  306. if (j < c->buffer->i + count) /* No input glyphs skipped */
  307. {
  308. c->replace_glyphs_be16 (count, 1, (const uint16_t *) &ligGlyph);
  309. }
  310. else
  311. {
  312. c->replace_glyph (ligGlyph);
  313. /* Now we must do a second loop to copy the skipped glyphs to
  314. `out' and assign component values to it. We start with the
  315. glyph after the first component. Glyphs between component
  316. i and i+1 belong to component i. Together with the lig_id
  317. value it is later possible to check whether a specific
  318. component value really belongs to a given ligature. */
  319. for (i = 1; i < count; i++)
  320. {
  321. while (_hb_ot_layout_skip_mark (c->layout->face, &c->buffer->info[c->buffer->i], c->lookup_props, NULL))
  322. {
  323. c->buffer->info[c->buffer->i].lig_comp() = i;
  324. c->buffer->info[c->buffer->i].lig_id() = lig_id;
  325. c->replace_glyph (c->buffer->info[c->buffer->i].codepoint);
  326. }
  327. /* Skip the base glyph */
  328. c->buffer->i++;
  329. }
  330. }
  331. return true;
  332. }
  333. inline uint16_t allocate_lig_id (hb_buffer_t *buffer) const {
  334. uint16_t lig_id = buffer->next_serial ();
  335. if (unlikely (!lig_id)) lig_id = buffer->next_serial (); /* in case of overflows */
  336. return lig_id;
  337. }
  338. public:
  339. inline bool sanitize (hb_sanitize_context_t *c) {
  340. TRACE_SANITIZE ();
  341. return ligGlyph.sanitize (c)
  342. && component.sanitize (c);
  343. }
  344. private:
  345. GlyphID ligGlyph; /* GlyphID of ligature to substitute */
  346. HeadlessArrayOf<GlyphID>
  347. component; /* Array of component GlyphIDs--start
  348. * with the second component--ordered
  349. * in writing direction */
  350. public:
  351. DEFINE_SIZE_ARRAY (4, component);
  352. };
  353. struct LigatureSet
  354. {
  355. friend struct LigatureSubstFormat1;
  356. private:
  357. inline bool apply (hb_apply_context_t *c) const
  358. {
  359. TRACE_APPLY ();
  360. unsigned int num_ligs = ligature.len;
  361. for (unsigned int i = 0; i < num_ligs; i++)
  362. {
  363. const Ligature &lig = this+ligature[i];
  364. if (lig.apply (c))
  365. return true;
  366. }
  367. return false;
  368. }
  369. public:
  370. inline bool sanitize (hb_sanitize_context_t *c) {
  371. TRACE_SANITIZE ();
  372. return ligature.sanitize (c, this);
  373. }
  374. private:
  375. OffsetArrayOf<Ligature>
  376. ligature; /* Array LigatureSet tables
  377. * ordered by preference */
  378. public:
  379. DEFINE_SIZE_ARRAY (2, ligature);
  380. };
  381. struct LigatureSubstFormat1
  382. {
  383. friend struct LigatureSubst;
  384. private:
  385. inline bool apply (hb_apply_context_t *c) const
  386. {
  387. TRACE_APPLY ();
  388. hb_codepoint_t glyph_id = c->buffer->info[c->buffer->i].codepoint;
  389. unsigned int index = (this+coverage) (glyph_id);
  390. if (likely (index == NOT_COVERED))
  391. return false;
  392. const LigatureSet &lig_set = this+ligatureSet[index];
  393. return lig_set.apply (c);
  394. }
  395. inline bool sanitize (hb_sanitize_context_t *c) {
  396. TRACE_SANITIZE ();
  397. return coverage.sanitize (c, this)
  398. && ligatureSet.sanitize (c, this);
  399. }
  400. private:
  401. USHORT format; /* Format identifier--format = 1 */
  402. OffsetTo<Coverage>
  403. coverage; /* Offset to Coverage table--from
  404. * beginning of Substitution table */
  405. OffsetArrayOf<LigatureSet>
  406. ligatureSet; /* Array LigatureSet tables
  407. * ordered by Coverage Index */
  408. public:
  409. DEFINE_SIZE_ARRAY (6, ligatureSet);
  410. };
  411. struct LigatureSubst
  412. {
  413. friend struct SubstLookupSubTable;
  414. private:
  415. inline bool apply (hb_apply_context_t *c) const
  416. {
  417. TRACE_APPLY ();
  418. switch (u.format) {
  419. case 1: return u.format1.apply (c);
  420. default:return false;
  421. }
  422. }
  423. inline bool sanitize (hb_sanitize_context_t *c) {
  424. TRACE_SANITIZE ();
  425. if (!u.format.sanitize (c)) return false;
  426. switch (u.format) {
  427. case 1: return u.format1.sanitize (c);
  428. default:return true;
  429. }
  430. }
  431. private:
  432. union {
  433. USHORT format; /* Format identifier */
  434. LigatureSubstFormat1 format1;
  435. } u;
  436. };
  437. HB_BEGIN_DECLS
  438. static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index);
  439. HB_END_DECLS
  440. struct ContextSubst : Context
  441. {
  442. friend struct SubstLookupSubTable;
  443. private:
  444. inline bool apply (hb_apply_context_t *c) const
  445. {
  446. TRACE_APPLY ();
  447. return Context::apply (c, substitute_lookup);
  448. }
  449. };
  450. struct ChainContextSubst : ChainContext
  451. {
  452. friend struct SubstLookupSubTable;
  453. private:
  454. inline bool apply (hb_apply_context_t *c) const
  455. {
  456. TRACE_APPLY ();
  457. return ChainContext::apply (c, substitute_lookup);
  458. }
  459. };
  460. struct ExtensionSubst : Extension
  461. {
  462. friend struct SubstLookupSubTable;
  463. friend struct SubstLookup;
  464. private:
  465. inline const struct SubstLookupSubTable& get_subtable (void) const
  466. {
  467. unsigned int offset = get_offset ();
  468. if (unlikely (!offset)) return Null(SubstLookupSubTable);
  469. return StructAtOffset<SubstLookupSubTable> (this, offset);
  470. }
  471. inline bool apply (hb_apply_context_t *c) const;
  472. inline bool sanitize (hb_sanitize_context_t *c);
  473. inline bool is_reverse (void) const;
  474. };
  475. struct ReverseChainSingleSubstFormat1
  476. {
  477. friend struct ReverseChainSingleSubst;
  478. private:
  479. inline bool apply (hb_apply_context_t *c) const
  480. {
  481. TRACE_APPLY ();
  482. if (unlikely (c->context_length != NO_CONTEXT))
  483. return false; /* No chaining to this type */
  484. unsigned int index = (this+coverage) (c->buffer->info[c->buffer->i].codepoint);
  485. if (likely (index == NOT_COVERED))
  486. return false;
  487. const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
  488. const ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
  489. if (match_backtrack (c,
  490. backtrack.len, (USHORT *) backtrack.array,
  491. match_coverage, this) &&
  492. match_lookahead (c,
  493. lookahead.len, (USHORT *) lookahead.array,
  494. match_coverage, this,
  495. 1))
  496. {
  497. c->buffer->info[c->buffer->i].codepoint = substitute[index];
  498. c->buffer->i--; /* Reverse! */
  499. return true;
  500. }
  501. return false;
  502. }
  503. inline bool sanitize (hb_sanitize_context_t *c) {
  504. TRACE_SANITIZE ();
  505. if (!(coverage.sanitize (c, this)
  506. && backtrack.sanitize (c, this)))
  507. return false;
  508. OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
  509. if (!lookahead.sanitize (c, this))
  510. return false;
  511. ArrayOf<GlyphID> &substitute = StructAfter<ArrayOf<GlyphID> > (lookahead);
  512. return substitute.sanitize (c);
  513. }
  514. private:
  515. USHORT format; /* Format identifier--format = 1 */
  516. OffsetTo<Coverage>
  517. coverage; /* Offset to Coverage table--from
  518. * beginning of table */
  519. OffsetArrayOf<Coverage>
  520. backtrack; /* Array of coverage tables
  521. * in backtracking sequence, in glyph
  522. * sequence order */
  523. OffsetArrayOf<Coverage>
  524. lookaheadX; /* Array of coverage tables
  525. * in lookahead sequence, in glyph
  526. * sequence order */
  527. ArrayOf<GlyphID>
  528. substituteX; /* Array of substitute
  529. * GlyphIDs--ordered by Coverage Index */
  530. public:
  531. DEFINE_SIZE_MIN (10);
  532. };
  533. struct ReverseChainSingleSubst
  534. {
  535. friend struct SubstLookupSubTable;
  536. private:
  537. inline bool apply (hb_apply_context_t *c) const
  538. {
  539. TRACE_APPLY ();
  540. switch (u.format) {
  541. case 1: return u.format1.apply (c);
  542. default:return false;
  543. }
  544. }
  545. inline bool sanitize (hb_sanitize_context_t *c) {
  546. TRACE_SANITIZE ();
  547. if (!u.format.sanitize (c)) return false;
  548. switch (u.format) {
  549. case 1: return u.format1.sanitize (c);
  550. default:return true;
  551. }
  552. }
  553. private:
  554. union {
  555. USHORT format; /* Format identifier */
  556. ReverseChainSingleSubstFormat1 format1;
  557. } u;
  558. };
  559. /*
  560. * SubstLookup
  561. */
  562. struct SubstLookupSubTable
  563. {
  564. friend struct SubstLookup;
  565. enum {
  566. Single = 1,
  567. Multiple = 2,
  568. Alternate = 3,
  569. Ligature = 4,
  570. Context = 5,
  571. ChainContext = 6,
  572. Extension = 7,
  573. ReverseChainSingle = 8
  574. };
  575. inline bool apply (hb_apply_context_t *c, unsigned int lookup_type) const
  576. {
  577. TRACE_APPLY ();
  578. switch (lookup_type) {
  579. case Single: return u.single.apply (c);
  580. case Multiple: return u.multiple.apply (c);
  581. case Alternate: return u.alternate.apply (c);
  582. case Ligature: return u.ligature.apply (c);
  583. case Context: return u.c.apply (c);
  584. case ChainContext: return u.chainContext.apply (c);
  585. case Extension: return u.extension.apply (c);
  586. case ReverseChainSingle: return u.reverseChainContextSingle.apply (c);
  587. default:return false;
  588. }
  589. }
  590. inline bool sanitize (hb_sanitize_context_t *c, unsigned int lookup_type) {
  591. TRACE_SANITIZE ();
  592. switch (lookup_type) {
  593. case Single: return u.single.sanitize (c);
  594. case Multiple: return u.multiple.sanitize (c);
  595. case Alternate: return u.alternate.sanitize (c);
  596. case Ligature: return u.ligature.sanitize (c);
  597. case Context: return u.c.sanitize (c);
  598. case ChainContext: return u.chainContext.sanitize (c);
  599. case Extension: return u.extension.sanitize (c);
  600. case ReverseChainSingle: return u.reverseChainContextSingle.sanitize (c);
  601. default:return true;
  602. }
  603. }
  604. private:
  605. union {
  606. USHORT sub_format;
  607. SingleSubst single;
  608. MultipleSubst multiple;
  609. AlternateSubst alternate;
  610. LigatureSubst ligature;
  611. ContextSubst c;
  612. ChainContextSubst chainContext;
  613. ExtensionSubst extension;
  614. ReverseChainSingleSubst reverseChainContextSingle;
  615. } u;
  616. public:
  617. DEFINE_SIZE_UNION (2, sub_format);
  618. };
  619. struct SubstLookup : Lookup
  620. {
  621. inline const SubstLookupSubTable& get_subtable (unsigned int i) const
  622. { return this+CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable)[i]; }
  623. inline static bool lookup_type_is_reverse (unsigned int lookup_type)
  624. { return lookup_type == SubstLookupSubTable::ReverseChainSingle; }
  625. inline bool is_reverse (void) const
  626. {
  627. unsigned int type = get_type ();
  628. if (unlikely (type == SubstLookupSubTable::Extension))
  629. return CastR<ExtensionSubst> (get_subtable(0)).is_reverse ();
  630. return lookup_type_is_reverse (type);
  631. }
  632. inline bool apply_once (hb_ot_layout_context_t *layout,
  633. hb_buffer_t *buffer,
  634. hb_mask_t lookup_mask,
  635. unsigned int context_length,
  636. unsigned int nesting_level_left) const
  637. {
  638. unsigned int lookup_type = get_type ();
  639. hb_apply_context_t c[1] = {{0}};
  640. c->layout = layout;
  641. c->buffer = buffer;
  642. c->lookup_mask = lookup_mask;
  643. c->context_length = context_length;
  644. c->nesting_level_left = nesting_level_left;
  645. c->lookup_props = get_props ();
  646. if (!_hb_ot_layout_check_glyph_property (c->layout->face, &c->buffer->info[c->buffer->i], c->lookup_props, &c->property))
  647. return false;
  648. if (unlikely (lookup_type == SubstLookupSubTable::Extension))
  649. {
  650. /* The spec says all subtables should have the same type.
  651. * This is specially important if one has a reverse type!
  652. *
  653. * This is rather slow to do this here for every glyph,
  654. * but it's easiest, and who uses extension lookups anyway?!*/
  655. unsigned int count = get_subtable_count ();
  656. unsigned int type = get_subtable(0).u.extension.get_type ();
  657. for (unsigned int i = 1; i < count; i++)
  658. if (get_subtable(i).u.extension.get_type () != type)
  659. return false;
  660. }
  661. unsigned int count = get_subtable_count ();
  662. for (unsigned int i = 0; i < count; i++)
  663. if (get_subtable (i).apply (c, lookup_type))
  664. return true;
  665. return false;
  666. }
  667. inline bool apply_string (hb_ot_layout_context_t *layout,
  668. hb_buffer_t *buffer,
  669. hb_mask_t mask) const
  670. {
  671. bool ret = false;
  672. if (unlikely (!buffer->len))
  673. return false;
  674. if (likely (!is_reverse ()))
  675. {
  676. /* in/out forward substitution */
  677. buffer->clear_output ();
  678. buffer->i = 0;
  679. while (buffer->i < buffer->len)
  680. {
  681. if ((buffer->info[buffer->i].mask & mask) &&
  682. apply_once (layout, buffer, mask, NO_CONTEXT, MAX_NESTING_LEVEL))
  683. ret = true;
  684. else
  685. buffer->next_glyph ();
  686. }
  687. if (ret)
  688. buffer->swap ();
  689. }
  690. else
  691. {
  692. /* in-place backward substitution */
  693. buffer->i = buffer->len - 1;
  694. do
  695. {
  696. if ((buffer->info[buffer->i].mask & mask) &&
  697. apply_once (layout, buffer, mask, NO_CONTEXT, MAX_NESTING_LEVEL))
  698. ret = true;
  699. else
  700. buffer->i--;
  701. }
  702. while ((int) buffer->i >= 0);
  703. }
  704. return ret;
  705. }
  706. inline bool sanitize (hb_sanitize_context_t *c) {
  707. TRACE_SANITIZE ();
  708. if (unlikely (!Lookup::sanitize (c))) return false;
  709. OffsetArrayOf<SubstLookupSubTable> &list = CastR<OffsetArrayOf<SubstLookupSubTable> > (subTable);
  710. return list.sanitize (c, this, get_type ());
  711. }
  712. };
  713. typedef OffsetListOf<SubstLookup> SubstLookupList;
  714. /*
  715. * GSUB
  716. */
  717. struct GSUB : GSUBGPOS
  718. {
  719. static const hb_tag_t Tag = HB_OT_TAG_GSUB;
  720. inline const SubstLookup& get_lookup (unsigned int i) const
  721. { return CastR<SubstLookup> (GSUBGPOS::get_lookup (i)); }
  722. inline bool substitute_lookup (hb_ot_layout_context_t *layout,
  723. hb_buffer_t *buffer,
  724. unsigned int lookup_index,
  725. hb_mask_t mask) const
  726. { return get_lookup (lookup_index).apply_string (layout, buffer, mask); }
  727. inline bool sanitize (hb_sanitize_context_t *c) {
  728. TRACE_SANITIZE ();
  729. if (unlikely (!GSUBGPOS::sanitize (c))) return false;
  730. OffsetTo<SubstLookupList> &list = CastR<OffsetTo<SubstLookupList> > (lookupList);
  731. return list.sanitize (c, this);
  732. }
  733. public:
  734. DEFINE_SIZE_STATIC (10);
  735. };
  736. /* Out-of-class implementation for methods recursing */
  737. inline bool ExtensionSubst::apply (hb_apply_context_t *c) const
  738. {
  739. TRACE_APPLY ();
  740. return get_subtable ().apply (c, get_type ());
  741. }
  742. inline bool ExtensionSubst::sanitize (hb_sanitize_context_t *c)
  743. {
  744. TRACE_SANITIZE ();
  745. if (unlikely (!Extension::sanitize (c))) return false;
  746. unsigned int offset = get_offset ();
  747. if (unlikely (!offset)) return true;
  748. return StructAtOffset<SubstLookupSubTable> (this, offset).sanitize (c, get_type ());
  749. }
  750. inline bool ExtensionSubst::is_reverse (void) const
  751. {
  752. unsigned int type = get_type ();
  753. if (unlikely (type == SubstLookupSubTable::Extension))
  754. return CastR<ExtensionSubst> (get_subtable()).is_reverse ();
  755. return SubstLookup::lookup_type_is_reverse (type);
  756. }
  757. static inline bool substitute_lookup (hb_apply_context_t *c, unsigned int lookup_index)
  758. {
  759. const GSUB &gsub = *(c->layout->face->ot_layout->gsub);
  760. const SubstLookup &l = gsub.get_lookup (lookup_index);
  761. if (unlikely (c->nesting_level_left == 0))
  762. return false;
  763. if (unlikely (c->context_length < 1))
  764. return false;
  765. return l.apply_once (c->layout, c->buffer, c->lookup_mask, c->context_length, c->nesting_level_left - 1);
  766. }
  767. HB_END_DECLS
  768. #endif /* HB_OT_LAYOUT_GSUB_PRIVATE_HH */