PageRenderTime 57ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

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

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