1 // -*- related-file-name: "../include/efont/otfgsub.hh" -*-
2 
3 /* otfgsub.{cc,hh} -- OpenType GSUB table
4  *
5  * Copyright (c) 2003-2019 Eddie Kohler
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU General Public License as published by the Free
9  * Software Foundation; either version 2 of the License, or (at your option)
10  * any later version. This program is distributed in the hope that it will be
11  * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
13  * Public License for more details.
14  */
15 
16 #ifdef HAVE_CONFIG_H
17 # include <config.h>
18 #endif
19 #include <efont/otfgsub.hh>
20 #include <efont/otfname.hh>
21 #include <lcdf/error.hh>
22 #include <lcdf/straccum.hh>
23 #include <errno.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <algorithm>
27 
28 namespace Efont { namespace OpenType {
29 
30 void
clear(Substitute & s,uint8_t & t)31 Substitution::clear(Substitute &s, uint8_t &t)
32 {
33     switch (t) {
34       case T_GLYPHS:
35         delete[] s.gids;
36         break;
37       case T_COVERAGE:
38         delete s.coverage;
39         break;
40     }
41     t = T_NONE;
42 }
43 
44 void
assign_space(Substitute & s,uint8_t & t,int n)45 Substitution::assign_space(Substitute &s, uint8_t &t, int n)
46 {
47     clear(s, t);
48     if (n == 1)
49         t = T_GLYPH;
50     else if (n > 1) {
51         s.gids = new Glyph[n + 1];
52         s.gids[0] = n;
53         t = T_GLYPHS;
54     }
55 }
56 
57 void
assign(Substitute & s,uint8_t & t,Glyph gid)58 Substitution::assign(Substitute &s, uint8_t &t, Glyph gid)
59 {
60     clear(s, t);
61     s.gid = gid;
62     t = T_GLYPH;
63 }
64 
65 void
assign(Substitute & s,uint8_t & t,int ngids,const Glyph * gids)66 Substitution::assign(Substitute &s, uint8_t &t, int ngids, const Glyph *gids)
67 {
68     clear(s, t);
69     assert(ngids > 0);
70     if (ngids == 1) {
71         s.gid = gids[0];
72         t = T_GLYPH;
73     } else {
74         s.gids = new Glyph[ngids + 1];
75         s.gids[0] = ngids;
76         memcpy(s.gids + 1, gids, ngids * sizeof(Glyph));
77         t = T_GLYPHS;
78     }
79 }
80 
81 void
assign(Substitute & s,uint8_t & t,const Coverage & coverage)82 Substitution::assign(Substitute &s, uint8_t &t, const Coverage &coverage)
83 {
84     clear(s, t);
85     s.coverage = new Coverage(coverage);
86     t = T_COVERAGE;
87 }
88 
89 void
assign(Substitute & s,uint8_t & t,const Substitute & os,uint8_t ot)90 Substitution::assign(Substitute &s, uint8_t &t, const Substitute &os, uint8_t ot)
91 {
92     if (&s == &os)
93         return;
94     switch (ot) {
95       case T_NONE:
96         clear(s, t);
97         break;
98       case T_GLYPH:
99         assign(s, t, os.gid);
100         break;
101       case T_GLYPHS:
102         assign(s, t, os.gids[0], os.gids + 1);
103         break;
104       case T_COVERAGE:
105         assign(s, t, *os.coverage);
106         break;
107       default:
108         assert(0);
109     }
110 }
111 
Substitution(const Substitution & o)112 Substitution::Substitution(const Substitution &o)
113     : _left_is(T_NONE), _in_is(T_NONE), _out_is(T_NONE), _right_is(T_NONE),
114       _alternate(o._alternate)
115 {
116     assign(_left, _left_is, o._left, o._left_is);
117     assign(_in, _in_is, o._in, o._in_is);
118     assign(_out, _out_is, o._out, o._out_is);
119     assign(_right, _right_is, o._right, o._right_is);
120 }
121 
Substitution(Glyph in,Glyph out)122 Substitution::Substitution(Glyph in, Glyph out)
123     : _left_is(T_NONE), _in_is(T_GLYPH), _out_is(T_GLYPH), _right_is(T_NONE)
124 {
125     _in.gid = in;
126     _out.gid = out;
127 }
128 
Substitution(Glyph in,const Vector<Glyph> & out,bool is_alternate)129 Substitution::Substitution(Glyph in, const Vector<Glyph> &out, bool is_alternate)
130     : _left_is(T_NONE), _in_is(T_GLYPH), _out_is(T_NONE), _right_is(T_NONE),
131       _alternate(is_alternate)
132 {
133     assert(out.size() > 0);
134     _in.gid = in;
135     assign(_out, _out_is, out.size(), &out[0]);
136 }
137 
Substitution(Glyph in1,Glyph in2,Glyph out)138 Substitution::Substitution(Glyph in1, Glyph in2, Glyph out)
139     : _left_is(T_NONE), _in_is(T_GLYPHS), _out_is(T_GLYPH), _right_is(T_NONE)
140 {
141     _in.gids = new Glyph[3];
142     _in.gids[0] = 2;
143     _in.gids[1] = in1;
144     _in.gids[2] = in2;
145     _out.gid = out;
146 }
147 
Substitution(const Vector<Glyph> & in,Glyph out)148 Substitution::Substitution(const Vector<Glyph> &in, Glyph out)
149     : _left_is(T_NONE), _in_is(T_NONE), _out_is(T_GLYPH), _right_is(T_NONE)
150 {
151     assert(in.size() > 0);
152     assign(_in, _in_is, in.size(), &in[0]);
153     _out.gid = out;
154 }
155 
Substitution(int nin,const Glyph * in,Glyph out)156 Substitution::Substitution(int nin, const Glyph *in, Glyph out)
157     : _left_is(T_NONE), _in_is(T_NONE), _out_is(T_GLYPH), _right_is(T_NONE)
158 {
159     assert(nin > 0);
160     assign(_in, _in_is, nin, in);
161     _out.gid = out;
162 }
163 
Substitution(int nleft,int nin,int nout,int nright)164 Substitution::Substitution(int nleft, int nin, int nout, int nright)
165     : _left_is(T_NONE), _in_is(T_NONE), _out_is(T_NONE), _right_is(T_NONE)
166 {
167     if (nleft)
168         assign_space(_left, _left_is, nleft);
169     if (nin)
170         assign_space(_in, _in_is, nin);
171     if (nout)
172         assign_space(_out, _out_is, nout);
173     if (nright)
174         assign_space(_right, _right_is, nright);
175 }
176 
~Substitution()177 Substitution::~Substitution()
178 {
179     clear(_left, _left_is);
180     clear(_in, _in_is);
181     clear(_out, _out_is);
182     clear(_right, _right_is);
183 }
184 
185 Substitution &
operator =(const Substitution & o)186 Substitution::operator=(const Substitution &o)
187 {
188     assign(_left, _left_is, o._left, o._left_is);
189     assign(_in, _in_is, o._in, o._in_is);
190     assign(_out, _out_is, o._out, o._out_is);
191     assign(_right, _right_is, o._right, o._right_is);
192     _alternate = o._alternate;
193     return *this;
194 }
195 
196 bool
substitute_in(const Substitute & s,uint8_t t,const Coverage & c)197 Substitution::substitute_in(const Substitute &s, uint8_t t, const Coverage &c)
198 {
199     switch (t) {
200       case T_NONE:
201         return true;
202       case T_GLYPH:
203         return c.covers(s.gid);
204       case T_GLYPHS:
205         for (int i = 1; i <= s.gids[0]; i++)
206             if (!c.covers(s.gids[i]))
207                 return false;
208         return true;
209       case T_COVERAGE:
210         return *s.coverage <= c;
211       default:
212         assert(0);
213         return false;
214     }
215 }
216 
217 bool
substitute_in(const Substitute & s,uint8_t t,const GlyphSet & gs)218 Substitution::substitute_in(const Substitute &s, uint8_t t, const GlyphSet &gs)
219 {
220     switch (t) {
221       case T_NONE:
222         return true;
223       case T_GLYPH:
224         return gs.covers(s.gid);
225       case T_GLYPHS:
226         for (int i = 1; i <= s.gids[0]; i++)
227             if (!gs.covers(s.gids[i]))
228                 return false;
229         return true;
230       case T_COVERAGE:
231         for (Coverage::iterator i = s.coverage->begin(); i; i++)
232             if (!gs.covers(*i))
233                 return false;
234         return true;
235       default:
236         assert(0);
237         return false;
238     }
239 }
240 
241 bool
context_in(const Coverage & c) const242 Substitution::context_in(const Coverage &c) const
243 {
244     return substitute_in(_left, _left_is, c)
245         && substitute_in(_in, _in_is, c)
246         && substitute_in(_right, _right_is, c);
247 }
248 
249 bool
context_in(const GlyphSet & gs) const250 Substitution::context_in(const GlyphSet &gs) const
251 {
252     return substitute_in(_left, _left_is, gs)
253         && substitute_in(_in, _in_is, gs)
254         && substitute_in(_right, _right_is, gs);
255 }
256 
257 Glyph
extract_glyph(const Substitute & s,uint8_t t)258 Substitution::extract_glyph(const Substitute &s, uint8_t t) noexcept
259 {
260     return (t == T_GLYPH ? s.gid : 0);
261 }
262 
263 Glyph
extract_glyph(const Substitute & s,int which,uint8_t t)264 Substitution::extract_glyph(const Substitute &s, int which, uint8_t t) noexcept
265 {
266     switch (t) {
267       case T_GLYPH:
268         return (which == 0 ? s.gid : 0);
269       case T_GLYPHS:
270         return (s.gids[0] > which ? s.gids[which + 1] : 0);
271       case T_COVERAGE:
272         for (Coverage::iterator ci = s.coverage->begin(); ci; ci++, which--)
273             if (which == 0)
274                 return *ci;
275         return 0;
276       default:
277         return 0;
278     }
279 }
280 
281 bool
extract_glyphs(const Substitute & s,uint8_t t,Vector<Glyph> & v,bool coverage_ok)282 Substitution::extract_glyphs(const Substitute &s, uint8_t t, Vector<Glyph> &v, bool coverage_ok) noexcept
283 {
284     switch (t) {
285       case T_GLYPH:
286         v.push_back(s.gid);
287         return true;
288       case T_GLYPHS:
289         for (int i = 1; i <= s.gids[0]; i++)
290             v.push_back(s.gids[i]);
291         return true;
292       case T_COVERAGE:
293         if (coverage_ok) {
294             for (Coverage::iterator i = s.coverage->begin(); i; i++)
295                 v.push_back(*i);
296             return true;
297         } else
298             return false;
299       default:
300         return false;
301     }
302 }
303 
304 Glyph *
extract_glyphptr(const Substitute & s,uint8_t t)305 Substitution::extract_glyphptr(const Substitute &s, uint8_t t) noexcept
306 {
307     switch (t) {
308       case T_GLYPH:
309         return const_cast<Glyph *>(&s.gid);
310       case T_GLYPHS:
311         return &s.gids[1];
312       default:
313         return 0;
314     }
315 }
316 
317 int
extract_nglyphs(const Substitute & s,uint8_t t,bool coverage_ok)318 Substitution::extract_nglyphs(const Substitute &s, uint8_t t, bool coverage_ok) noexcept
319 {
320     switch (t) {
321       case T_GLYPH:
322         return 1;
323       case T_GLYPHS:
324         return s.gids[0];
325       case T_COVERAGE:
326         return (coverage_ok ? 1 : 0);
327       default:
328         return 0;
329     }
330 }
331 
332 bool
matches(const Substitute & s,uint8_t t,int pos,Glyph g)333 Substitution::matches(const Substitute &s, uint8_t t, int pos, Glyph g) noexcept
334 {
335     switch (t) {
336       case T_GLYPH:
337         return (pos == 0 && s.gid == g);
338       case T_GLYPHS:
339         return (pos >= 0 && pos < s.gids[0] && s.gids[1 + pos] == g);
340       case T_COVERAGE:
341         return (pos == 0 && s.coverage->covers(g));
342       default:
343         return false;
344     }
345 }
346 
347 bool
is_noop() const348 Substitution::is_noop() const
349 {
350     return (_in_is == T_GLYPH && _out_is == T_GLYPH && _in.gid == _out.gid)
351         || (_in_is == T_GLYPHS && _out_is == T_GLYPHS
352             && _in.gids[0] == _out.gids[0]
353             && memcmp(_in.gids, _out.gids, (_in.gids[0] + 1) * sizeof(Glyph)) == 0);
354 }
355 
356 bool
all_in_glyphs(Vector<Glyph> & gs) const357 Substitution::all_in_glyphs(Vector<Glyph> &gs) const
358 {
359     bool ok = true;
360     gs.clear();
361     if (_left_is != T_NONE)
362         ok &= extract_glyphs(_left, _left_is, gs, false);
363     ok &= extract_glyphs(_in, _in_is, gs, false);
364     if (_right_is != T_NONE)
365         ok &= extract_glyphs(_right, _right_is, gs, false);
366     return ok;
367 }
368 
369 bool
all_out_glyphs(Vector<Glyph> & v) const370 Substitution::all_out_glyphs(Vector<Glyph> &v) const
371 {
372     bool ok = true;
373     if (_left_is != T_NONE)
374         ok &= extract_glyphs(_left, _left_is, v, false);
375     ok &= extract_glyphs(_out, _out_is, v, false);
376     if (_right_is != T_NONE)
377         ok &= extract_glyphs(_right, _right_is, v, false);
378     return ok;
379 }
380 
381 void
assign_append(Substitute & s,uint8_t & t,const Substitute & ls,uint8_t lt,const Substitute & rs,uint8_t rt)382 Substitution::assign_append(Substitute &s, uint8_t &t, const Substitute &ls, uint8_t lt, const Substitute &rs, uint8_t rt)
383 {
384     if (lt == T_NONE)
385         assign(s, t, rs, rt);
386     else if (rt == T_NONE)
387         assign(s, t, ls, lt);
388     else if (lt != T_COVERAGE && rt != T_COVERAGE) {
389         int nl = extract_nglyphs(ls, lt, false);
390         int nr = extract_nglyphs(rs, rt, false);
391         Glyph *gids = new Glyph[nl + nr + 1];
392         gids[0] = nl + nr;
393         memcpy(&gids[1], extract_glyphptr(ls, lt), nl * sizeof(Glyph));
394         memcpy(&gids[1 + nl], extract_glyphptr(rs, rt), nr * sizeof(Glyph));
395         clear(s, t);
396         s.gids = gids;
397         t = T_GLYPHS;
398     } else
399         throw Error();
400 }
401 
402 void
assign_append(Substitute & s,uint8_t & t,const Substitute & ls,uint8_t lt,Glyph rg)403 Substitution::assign_append(Substitute &s, uint8_t &t, const Substitute &ls, uint8_t lt, Glyph rg)
404 {
405     Substitute rs;
406     rs.gid = rg;
407     assign_append(s, t, ls, lt, rs, T_GLYPH);
408 }
409 
410 Substitution
in_out_append_glyph(Glyph g) const411 Substitution::in_out_append_glyph(Glyph g) const
412 {
413     Substitution s;
414     assign(s._left, s._left_is, _left, _left_is);
415     assign(s._right, s._right_is, _right, _right_is);
416     assign_append(s._in, s._in_is, _in, _in_is, g);
417     assign_append(s._out, s._out_is, _out, _out_is, g);
418     return s;
419 }
420 
421 void
add_outer_left(Glyph g)422 Substitution::add_outer_left(Glyph g)
423 {
424     Substitute ls;
425     ls.gid = g;
426     assign_append(_left, _left_is, ls, T_GLYPH, _left, _left_is);
427 }
428 
429 void
remove_outer_left()430 Substitution::remove_outer_left()
431 {
432     if (_left_is == T_GLYPH)
433         _left_is = T_NONE;
434     else if (_left_is == T_GLYPHS) {
435         if (_left.gids[0] == 2)
436             assign(_left, _left_is, _left.gids[2]);
437         else {
438             _left.gids[0]--;
439             memmove(_left.gids + 1, _left.gids + 2, _left.gids[0] * sizeof(Glyph));
440         }
441     }
442 }
443 
444 void
add_outer_right(Glyph g)445 Substitution::add_outer_right(Glyph g)
446 {
447     assign_append(_right, _right_is, _right, _right_is, g);
448 }
449 
450 void
remove_outer_right()451 Substitution::remove_outer_right()
452 {
453     if (_right_is == T_GLYPH)
454         _right_is = T_NONE;
455     else if (_right_is == T_GLYPHS) {
456         if (_right.gids[0] == 2)
457             assign(_right, _right_is, _right.gids[1]);
458         else
459             _right.gids[0]--;
460     }
461 }
462 
463 bool
out_alter(const Substitution & o,int pos)464 Substitution::out_alter(const Substitution &o, int pos) noexcept
465 {
466     const Glyph *g = out_glyphptr();
467     int ng = out_nglyphs();
468     const Glyph *out_g = o.out_glyphptr();
469     int out_ng = o.out_nglyphs();
470     int in_ng = o.in_nglyphs();
471     if (pos + in_ng > ng || out_ng == 0)
472         return false;
473 
474     // check that input substitution actually matches us
475     for (int i = 0; i < in_ng; i++)
476         if (!o.in_matches(i, g[pos+i]))
477             return false;
478 
479     // actually change output
480     Vector<Glyph> new_g;
481     for (int i = 0; i < pos; i++)
482         new_g.push_back(g[i]);
483     for (int i = 0; i < out_ng; i++)
484         new_g.push_back(out_g[i]);
485     for (int i = pos + in_ng; i < ng; i++)
486         new_g.push_back(g[i]);
487     assign(_out, _out_is, new_g.size(), &new_g[0]);
488 
489     return true;
490 }
491 
492 static void
unparse_glyphid(StringAccum & sa,Glyph gid,const Vector<PermString> * gns)493 unparse_glyphid(StringAccum &sa, Glyph gid, const Vector<PermString> *gns) noexcept
494 {
495     if (gid > 0 && gns && gns->size() > gid && (*gns)[gid])
496         sa << (*gns)[gid];
497     else
498         sa << "g" << gid;
499 }
500 
501 void
unparse_glyphids(StringAccum & sa,const Substitute & s,uint8_t t,const Vector<PermString> * gns)502 Substitution::unparse_glyphids(StringAccum &sa, const Substitute &s, uint8_t t, const Vector<PermString> *gns) noexcept
503 {
504     if (t == T_GLYPH)
505         unparse_glyphid(sa, s.gid, gns);
506     else if (t == T_GLYPHS) {
507         for (int i = 1; i <= s.gids[0]; i++) {
508             if (i != 1)
509                 sa << ' ';
510             unparse_glyphid(sa, s.gids[i], gns);
511         }
512     } else if (t == T_COVERAGE)
513         sa << "<coverage>";
514     else
515         sa << "-";
516 }
517 
518 void
unparse(StringAccum & sa,const Vector<PermString> * gns) const519 Substitution::unparse(StringAccum &sa, const Vector<PermString> *gns) const
520 {
521     if (!*this)
522         sa << "NULL[]";
523     else {
524         if (is_single())
525             sa << "SINGLE[";
526         else if (is_ligature())
527             sa << "LIGATURE[";
528         else if (is_multiple())
529             sa << "MULTIPLE[";
530         else if (is_alternate())
531             sa << "ALTERNATE[";
532         else if (is_simple_context())
533             sa << "SIMPLECONTEXT[";
534         else
535             sa << "UNKNOWN[";
536 
537         if (_left_is != T_NONE) {
538             unparse_glyphids(sa, _left, _left_is, gns);
539             sa << " | ";
540         }
541         unparse_glyphids(sa, _in, _in_is, gns);
542         sa << " => ";
543         unparse_glyphids(sa, _out, _out_is, gns);
544         if (_right_is != T_NONE) {
545             sa << " | ";
546             unparse_glyphids(sa, _right, _right_is, gns);
547         }
548 
549         sa << ']';
550     }
551 }
552 
553 String
unparse(const Vector<PermString> * gns) const554 Substitution::unparse(const Vector<PermString> *gns) const
555 {
556     StringAccum sa;
557     unparse(sa, gns);
558     return sa.take_string();
559 }
560 
561 
562 
563 /**************************
564  * Gsub                   *
565  *                        *
566  **************************/
567 
Gsub(const Data & d,const Font * otf,ErrorHandler * errh)568 Gsub::Gsub(const Data &d, const Font *otf, ErrorHandler *errh)
569     : _chaincontext_reverse_backtrack(false)
570 {
571     // Fixed    Version
572     // Offset   ScriptList
573     // Offset   FeatureList
574     // Offset   LookupList
575     if (d.length() == 0)
576         throw BlankTable("GSUB");
577     if (d.u16(0) != 1)
578         throw Format("GSUB");
579     if (_script_list.assign(d.offset_subtable(4), errh) < 0)
580         throw Format("GSUB script list");
581     if (_feature_list.assign(d.offset_subtable(6), errh) < 0)
582         throw Format("GSUB feature list");
583     _lookup_list = d.offset_subtable(8);
584 
585     if (!otf)
586         return;
587 
588     // Check for "correct" chaining context rules, as suggested by Adobe's
589     // OpenType FDK
590     try {
591         Name nametable(otf->table("name"), ErrorHandler::silent_handler());
592         _chaincontext_reverse_backtrack = nametable.version_chaincontext_reverse_backtrack();
593     } catch (Error) {
594     }
595 }
596 
597 int
nlookups() const598 Gsub::nlookups() const
599 {
600     return _lookup_list.u16(0);
601 }
602 
603 GsubLookup
lookup(unsigned i) const604 Gsub::lookup(unsigned i) const
605 {
606     if (i >= _lookup_list.u16(0))
607         throw Error("GSUB lookup out of range");
608     else
609         return GsubLookup(_lookup_list.offset_subtable(2 + i*2));
610 }
611 
612 
613 /**************************
614  * GsubLookup             *
615  *                        *
616  **************************/
617 
GsubLookup(const Data & d)618 GsubLookup::GsubLookup(const Data &d)
619     : _d(d)
620 {
621     if (_d.length() < 6)
622         throw Format("GSUB Lookup table");
623     _type = _d.u16(0);
624     if (_type == L_EXTENSION && _d.u16(4) != 0) {
625         Data first_subtable = _d.offset_subtable(HEADERSIZE);
626         if (first_subtable.length() < 8 || first_subtable.u16(0) != 1)
627             throw Format("GSUB Extension Lookup table");
628         _type = first_subtable.u16(2);
629     }
630 }
631 
632 Data
subtable(int i) const633 GsubLookup::subtable(int i) const
634 {
635     Data subd = _d.offset_subtable(HEADERSIZE + i*RECSIZE);
636     if (_d.u16(0) != L_EXTENSION)
637         return subd;
638     else if (subd.length() >= 8 && subd.u16(0) == 1 && subd.u16(2) == _type)
639         return subd.subtable(subd.u32(4));
640     else
641         return Data();
642 }
643 
644 void
mark_out_glyphs(const Gsub & gsub,Vector<bool> & gmap) const645 GsubLookup::mark_out_glyphs(const Gsub &gsub, Vector<bool> &gmap) const
646 {
647     int nlookup = _d.u16(4);
648     switch (_type) {
649       case L_SINGLE:
650         for (int i = 0; i < nlookup; i++) {
651             GsubSingle x(subtable(i)); // this pattern makes gcc-3.3.4 happier
652             x.mark_out_glyphs(gmap);
653         }
654         return;
655       case L_MULTIPLE:
656         for (int i = 0; i < nlookup; i++) {
657             GsubMultiple x(subtable(i));
658             x.mark_out_glyphs(gmap);
659         }
660         return;
661       case L_ALTERNATE:
662         for (int i = 0; i < nlookup; i++) {
663             GsubMultiple x(subtable(i));
664             x.mark_out_glyphs(gmap);
665         }
666         return;
667       case L_LIGATURE:
668         for (int i = 0; i < nlookup; i++) {
669             GsubLigature x(subtable(i));
670             x.mark_out_glyphs(gmap);
671         }
672         return;
673     case L_CONTEXT:
674         for (int i = 0; i < nlookup; i++) {
675             GsubContext x(subtable(i));
676             x.mark_out_glyphs(gsub, gmap);
677         }
678         return;
679     case L_CHAIN:
680         for (int i = 0; i < nlookup; i++) {
681             GsubChainContext x(subtable(i));
682             x.mark_out_glyphs(gsub, gmap);
683         }
684         return;
685     }
686 }
687 
688 bool
unparse_automatics(const Gsub & gsub,Vector<Substitution> & v,const Coverage & limit) const689 GsubLookup::unparse_automatics(const Gsub &gsub, Vector<Substitution> &v, const Coverage &limit) const
690 {
691     int nlookup = _d.u16(4);
692     switch (_type) {
693       case L_SINGLE:
694         for (int i = 0; i < nlookup; i++) {
695             GsubSingle x(subtable(i)); // this pattern makes gcc-3.3.4 happier
696             x.unparse(v, limit);
697         }
698         return true;
699       case L_MULTIPLE:
700         for (int i = 0; i < nlookup; i++) {
701             GsubMultiple x(subtable(i));
702             x.unparse(v);
703         }
704         return true;
705       case L_ALTERNATE:
706         for (int i = 0; i < nlookup; i++) {
707             GsubMultiple x(subtable(i));
708             x.unparse(v, true);
709         }
710         return true;
711       case L_LIGATURE:
712         for (int i = 0; i < nlookup; i++) {
713             GsubLigature x(subtable(i));
714             x.unparse(v);
715         }
716         return true;
717       case L_CONTEXT: {
718           bool understood = true;
719           for (int i = 0; i < nlookup; i++) {
720               GsubContext x(subtable(i));
721               understood &= x.unparse(gsub, v, limit);
722           }
723           return understood;
724       }
725       case L_CHAIN: {
726           bool understood = true;
727           for (int i = 0; i < nlookup; i++) {
728               GsubChainContext x(subtable(i));
729               understood &= x.unparse(gsub, v, limit);
730           }
731           return understood;
732       }
733       default:
734         return false;
735     }
736 }
737 
738 bool
apply(const Glyph * g,int pos,int n,Substitution & s) const739 GsubLookup::apply(const Glyph *g, int pos, int n, Substitution &s) const
740 {
741     int nlookup = _d.u16(4);
742     switch (_type) {
743       case L_SINGLE:
744         for (int i = 0; i < nlookup; i++) {
745             GsubSingle x(subtable(i));
746             if (x.apply(g, pos, n, s))
747                 return true;
748         }
749         return false;
750       case L_MULTIPLE:
751         for (int i = 0; i < nlookup; i++) {
752             GsubMultiple x(subtable(i));
753             if (x.apply(g, pos, n, s))
754                 return true;
755         }
756         return false;
757       case L_ALTERNATE:
758         for (int i = 0; i < nlookup; i++) {
759             GsubMultiple x(subtable(i));
760             if (x.apply(g, pos, n, s, true))
761                 return true;
762         }
763         return false;
764       case L_LIGATURE:
765         for (int i = 0; i < nlookup; i++) {
766             GsubLigature x(subtable(i));
767             if (x.apply(g, pos, n, s))
768                 return true;
769         }
770         return false;
771       default:                  // XXX
772         return false;
773     }
774 }
775 
776 
777 /**************************
778  * GsubSingle             *
779  *                        *
780  **************************/
781 
GsubSingle(const Data & d)782 GsubSingle::GsubSingle(const Data &d)
783     : _d(d)
784 {
785     if (_d[0] != 0
786         || (_d[1] != 1 && _d[1] != 2))
787         throw Format("GSUB Single Substitution");
788     Coverage coverage(_d.offset_subtable(2));
789     if (!coverage.ok()
790         || (_d[1] == 2 && coverage.size() > _d.u16(4)))
791         throw Format("GSUB Single Substitution coverage");
792 }
793 
794 Coverage
coverage() const795 GsubSingle::coverage() const noexcept
796 {
797     return Coverage(_d.offset_subtable(2), 0, false);
798 }
799 
800 Glyph
map(Glyph g) const801 GsubSingle::map(Glyph g) const
802 {
803     int ci = coverage().coverage_index(g);
804     if (ci < 0)
805         return g;
806     else if (_d[1] == 1)
807         return g + _d.s16(4);
808     else
809         return _d.u16(HEADERSIZE + FORMAT2_RECSIZE*ci);
810 }
811 
812 void
mark_out_glyphs(Vector<bool> & gmap) const813 GsubSingle::mark_out_glyphs(Vector<bool> &gmap) const
814 {
815     if (_d[1] == 1) {
816         int delta = _d.s16(4);
817         for (Coverage::iterator i = coverage().begin(); i; i++)
818             gmap[*i + delta] = true;
819     } else {
820         for (Coverage::iterator i = coverage().begin(); i; i++)
821             gmap[_d.u16(HEADERSIZE + i.coverage_index()*FORMAT2_RECSIZE)] = true;
822     }
823 }
824 
825 void
unparse(Vector<Substitution> & v,const Coverage & limit) const826 GsubSingle::unparse(Vector<Substitution> &v, const Coverage &limit) const
827 {
828     if (_d[1] == 1) {
829         int delta = _d.s16(4);
830         for (Coverage::iterator it = coverage().begin(); it; ++it)
831             if (limit.covers(*it))
832                 v.push_back(Substitution(*it, *it + delta));
833     } else {
834         for (Coverage::iterator it = coverage().begin(); it; ++it)
835             if (limit.covers(*it))
836                 v.push_back(Substitution(*it, _d.u16(HEADERSIZE + it.coverage_index()*FORMAT2_RECSIZE)));
837     }
838 }
839 
840 bool
apply(const Glyph * g,int pos,int n,Substitution & s) const841 GsubSingle::apply(const Glyph *g, int pos, int n, Substitution &s) const
842 {
843     int ci;
844     if (pos < n && (ci = coverage().coverage_index(g[pos])) >= 0) {
845         if (_d[1] == 1)
846             s = Substitution(g[pos], g[pos] + _d.s16(4));
847         else
848             s = Substitution(g[pos], _d.u16(HEADERSIZE + ci*FORMAT2_RECSIZE));
849         return true;
850     } else
851         return false;
852 }
853 
854 
855 /**************************
856  * GsubMultiple           *
857  *                        *
858  **************************/
859 
GsubMultiple(const Data & d)860 GsubMultiple::GsubMultiple(const Data &d)
861     : _d(d)
862 {
863     if (_d[0] != 0 || _d[1] != 1)
864         throw Format("GSUB Multiple Substitution");
865     Coverage coverage(_d.offset_subtable(2));
866     if (!coverage.ok()
867         || coverage.size() > _d.u16(4))
868         throw Format("GSUB Multiple Substitution coverage");
869 }
870 
871 Coverage
coverage() const872 GsubMultiple::coverage() const noexcept
873 {
874     return Coverage(_d.offset_subtable(2), 0, false);
875 }
876 
877 bool
map(Glyph g,Vector<Glyph> & v) const878 GsubMultiple::map(Glyph g, Vector<Glyph> &v) const
879 {
880     v.clear();
881     int ci = coverage().coverage_index(g);
882     if (ci < 0) {
883         v.push_back(g);
884         return false;
885     } else {
886         Data seq = _d.offset_subtable(HEADERSIZE + ci*RECSIZE);
887         for (int i = 0; i < seq.u16(0); i++)
888             v.push_back(seq.u16(SEQ_HEADERSIZE + i*SEQ_RECSIZE));
889         return true;
890     }
891 }
892 
893 void
mark_out_glyphs(Vector<bool> & gmap) const894 GsubMultiple::mark_out_glyphs(Vector<bool> &gmap) const
895 {
896     for (Coverage::iterator i = coverage().begin(); i; ++i) {
897         Data seq = _d.offset_subtable(HEADERSIZE + i.coverage_index()*RECSIZE);
898         for (int j = 0; j < seq.u16(0); ++j)
899             gmap[seq.u16(SEQ_HEADERSIZE + j*SEQ_RECSIZE)] = true;
900     }
901 }
902 
903 void
unparse(Vector<Substitution> & v,bool is_alternate) const904 GsubMultiple::unparse(Vector<Substitution> &v, bool is_alternate) const
905 {
906     Vector<Glyph> result;
907     for (Coverage::iterator i = coverage().begin(); i; i++) {
908         Data seq = _d.offset_subtable(HEADERSIZE + i.coverage_index()*RECSIZE);
909         result.clear();
910         for (int j = 0; j < seq.u16(0); j++)
911             result.push_back(seq.u16(SEQ_HEADERSIZE + j*SEQ_RECSIZE));
912         v.push_back(Substitution(*i, result, is_alternate));
913     }
914 }
915 
916 bool
apply(const Glyph * g,int pos,int n,Substitution & s,bool is_alternate) const917 GsubMultiple::apply(const Glyph *g, int pos, int n, Substitution &s, bool is_alternate) const
918 {
919     int ci;
920     if (pos < n && (ci = coverage().coverage_index(g[pos])) >= 0) {
921         Vector<Glyph> result;
922         Data seq = _d.offset_subtable(HEADERSIZE + ci*RECSIZE);
923         for (int j = 0; j < seq.u16(0); j++)
924             result.push_back(seq.u16(SEQ_HEADERSIZE + j*SEQ_RECSIZE));
925         s = Substitution(g[pos], result, is_alternate);
926         return true;
927     } else
928         return false;
929 }
930 
931 
932 /**************************
933  * GsubLigature           *
934  *                        *
935  **************************/
936 
GsubLigature(const Data & d)937 GsubLigature::GsubLigature(const Data &d)
938     : _d(d)
939 {
940     if (_d[0] != 0
941         || _d[1] != 1)
942         throw Format("GSUB Ligature Substitution");
943     Coverage coverage(_d.offset_subtable(2));
944     if (!coverage.ok()
945         || coverage.size() > _d.u16(4))
946         throw Format("GSUB Ligature Substitution coverage");
947 }
948 
949 Coverage
coverage() const950 GsubLigature::coverage() const noexcept
951 {
952     return Coverage(_d.offset_subtable(2), 0, false);
953 }
954 
955 bool
map(const Vector<Glyph> & gs,Glyph & result,int & consumed) const956 GsubLigature::map(const Vector<Glyph> &gs, Glyph &result, int &consumed) const
957 {
958     assert(gs.size() > 0);
959     result = gs[0];
960     consumed = 1;
961     int ci = coverage().coverage_index(gs[0]);
962     if (ci < 0)
963         return false;
964     Data ligset = _d.offset_subtable(HEADERSIZE + ci*RECSIZE);
965     int nligset = ligset.u16(0);
966     for (int i = 0; i < nligset; i++) {
967         Data lig = ligset.offset_subtable(SET_HEADERSIZE + i*SET_RECSIZE);
968         int nlig = lig.u16(2);
969         if (nlig > gs.size() - 1)
970             goto bad;
971         for (int j = 0; j < nlig - 1; j++)
972             if (lig.u16(LIG_HEADERSIZE + j*LIG_RECSIZE) != gs[j + 1])
973                 goto bad;
974         result = lig.u16(0);
975         consumed = nlig + 1;
976         return true;
977       bad: ;
978     }
979     return false;
980 }
981 
982 void
mark_out_glyphs(Vector<bool> & gmap) const983 GsubLigature::mark_out_glyphs(Vector<bool> &gmap) const
984 {
985     for (Coverage::iterator i = coverage().begin(); i; i++) {
986         Data ligset = _d.offset_subtable(HEADERSIZE + i.coverage_index()*RECSIZE);
987         int nligset = ligset.u16(0);
988         Vector<Glyph> components(1, *i);
989         for (int j = 0; j < nligset; j++) {
990             Data lig = ligset.offset_subtable(SET_HEADERSIZE + j*SET_RECSIZE);
991             gmap[lig.u16(0)] = true;
992         }
993     }
994 }
995 
996 void
unparse(Vector<Substitution> & v) const997 GsubLigature::unparse(Vector<Substitution> &v) const
998 {
999     for (Coverage::iterator i = coverage().begin(); i; i++) {
1000         Data ligset = _d.offset_subtable(HEADERSIZE + i.coverage_index()*RECSIZE);
1001         int nligset = ligset.u16(0);
1002         Vector<Glyph> components(1, *i);
1003         for (int j = 0; j < nligset; j++) {
1004             Data lig = ligset.offset_subtable(SET_HEADERSIZE + j*SET_RECSIZE);
1005             int nlig = lig.u16(2);
1006             components.resize(1);
1007             for (int k = 0; k < nlig - 1; k++)
1008                 components.push_back(lig.u16(LIG_HEADERSIZE + k*LIG_RECSIZE));
1009             v.push_back(Substitution(components, lig.u16(0)));
1010         }
1011     }
1012 }
1013 
1014 bool
apply(const Glyph * g,int pos,int n,Substitution & s) const1015 GsubLigature::apply(const Glyph *g, int pos, int n, Substitution &s) const
1016 {
1017     int ci;
1018     if (pos < n && (ci = coverage().coverage_index(g[pos])) >= 0) {
1019         Data ligset = _d.offset_subtable(HEADERSIZE + ci*RECSIZE);
1020         int nligset = ligset.u16(0);
1021         for (int j = 0; j < nligset; j++) {
1022             Data lig = ligset.offset_subtable(SET_HEADERSIZE + j*SET_RECSIZE);
1023             int nlig = lig.u16(2);
1024             if (pos + nlig <= n) {
1025                 for (int k = 0; k < nlig - 1; k++)
1026                     if (lig.u16(LIG_HEADERSIZE + k*LIG_RECSIZE) != g[pos + k + 1])
1027                         goto ligature_failed;
1028                 s = Substitution(nlig, &g[pos], lig.u16(0));
1029                 return true;
1030             }
1031           ligature_failed: ;
1032         }
1033     }
1034     return false;
1035 }
1036 
1037 
1038 /**************************
1039  * GsubContext            *
1040  *                        *
1041  **************************/
1042 
GsubContext(const Data & d)1043 GsubContext::GsubContext(const Data &d)
1044     : _d(d)
1045 {
1046     switch (_d.u16(0)) {
1047       case 1:
1048       case 2:
1049         break;
1050       case 3: {
1051           int ninput = _d.u16(2);
1052           if (ninput < 1)
1053               throw Format("GSUB Context Substitution input sequence");
1054           Coverage coverage(_d.offset_subtable(F3_HSIZE));
1055           if (!coverage.ok())
1056               throw Format("GSUB Context Substitution coverage");
1057           break;
1058       }
1059       default:
1060         throw Format("GSUB Context Substitution");
1061     }
1062 }
1063 
1064 Coverage
coverage() const1065 GsubContext::coverage() const noexcept
1066 {
1067     if (_d[1] == 3)
1068         return Coverage(_d.offset_subtable(F3_HSIZE), 0, false);
1069     else
1070         return Coverage();
1071 }
1072 
1073 void
subruleset_mark_out_glyphs(const Data & data,int nsub,int subtab_offset,const Gsub & gsub,Vector<bool> & gmap)1074 GsubContext::subruleset_mark_out_glyphs(const Data &data, int nsub,
1075                                         int subtab_offset, const Gsub &gsub,
1076                                         Vector<bool> &gmap)
1077 {
1078     for (int j = 0; j < nsub; ++j) {
1079         int lookup_index = data.u16(subtab_offset + SUBRECSIZE*j + 2);
1080         gsub.lookup(lookup_index).mark_out_glyphs(gsub, gmap);
1081     }
1082 }
1083 
1084 void
mark_out_glyphs(const Gsub & gsub,Vector<bool> & gmap) const1085 GsubContext::mark_out_glyphs(const Gsub &gsub, Vector<bool> &gmap) const
1086 {
1087     if (_d.u16(0) != 3)         // XXX
1088         return;
1089     int nglyph = _d.u16(2);
1090     int nsubst = _d.u16(4);
1091     subruleset_mark_out_glyphs(_d, nsubst, F3_HSIZE + nglyph*2, gsub, gmap);
1092 }
1093 
1094 bool
f1_unparse(const Data & data,int nsub,int subtab_offset,const Gsub & gsub,Vector<Substitution> & outsubs,Substitution s)1095 GsubContext::f1_unparse(const Data& data,
1096                         int nsub, int subtab_offset,
1097                         const Gsub& gsub, Vector<Substitution>& outsubs,
1098                         Substitution s) {
1099     Substitution subtab_sub;
1100     int napplied = 0;
1101     for (int j = 0; j < nsub; j++) {
1102         int seq_index = data.u16(subtab_offset + SUBRECSIZE*j);
1103         int lookup_index = data.u16(subtab_offset + SUBRECSIZE*j + 2);
1104         // XXX check seq_index against size of output glyphs?
1105         if (gsub.lookup(lookup_index).apply(s.out_glyphptr(), seq_index, s.out_nglyphs(), subtab_sub)) {
1106             napplied++;
1107             s.out_alter(subtab_sub, seq_index);
1108         }
1109     }
1110     outsubs.push_back(s);
1111     return true;
1112 }
1113 
1114 bool
f3_unparse(const Data & data,int nglyph,int glyphtab_offset,const Coverage & limit,int nsub,int subtab_offset,const Gsub & gsub,Vector<Substitution> & outsubs,const Substitution & prototype_sub)1115 GsubContext::f3_unparse(const Data &data,
1116                         int nglyph, int glyphtab_offset, const Coverage &limit,
1117                         int nsub, int subtab_offset,
1118                         const Gsub &gsub, Vector<Substitution> &outsubs,
1119                         const Substitution &prototype_sub)
1120 {
1121     Vector<Substitution> subs;
1122     subs.push_back(prototype_sub);
1123     Vector<Substitution> work_subs;
1124 
1125     // get array of possible substitutions including contexts
1126     for (int i = 0; i < nglyph; i++) {
1127         assert(!work_subs.size());
1128         Coverage c(data.offset_subtable(glyphtab_offset + i*2));
1129         for (Coverage::iterator ci = (c & limit).begin(); ci; ci++)
1130             for (int j = 0; j < subs.size(); j++)
1131                 work_subs.push_back(subs[j].in_out_append_glyph(*ci));
1132         subs.clear();
1133         subs.swap(work_subs);
1134     }
1135 
1136     // now, apply referred lookups to the resulting substitution array
1137     Substitution subtab_sub;
1138     for (int i = 0; i < subs.size(); i++) {
1139         Substitution &s = subs[i];
1140         int napplied = 0;
1141         for (int j = 0; j < nsub; j++) {
1142             int seq_index = data.u16(subtab_offset + SUBRECSIZE*j);
1143             int lookup_index = data.u16(subtab_offset + SUBRECSIZE*j + 2);
1144             // XXX check seq_index against size of output glyphs?
1145             if (gsub.lookup(lookup_index).apply(s.out_glyphptr(), seq_index, s.out_nglyphs(), subtab_sub)) {
1146                 napplied++;
1147                 s.out_alter(subtab_sub, seq_index);
1148             }
1149         }
1150         // 26.Jun.2003 -- always push substitution back, since the no-op might
1151         // override a following substitution
1152         outsubs.push_back(s);
1153     }
1154 
1155     return true;                // XXX
1156 }
1157 
1158 bool
unparse(const Gsub & gsub,Vector<Substitution> & v,const Coverage & limit) const1159 GsubContext::unparse(const Gsub &gsub, Vector<Substitution> &v, const Coverage &limit) const
1160 {
1161     if (_d.u16(0) != 3)         // XXX
1162         return false;
1163     int nglyph = _d.u16(2);
1164     int nsubst = _d.u16(4);
1165     return f3_unparse(_d, nglyph, F3_HSIZE, limit, nsubst, F3_HSIZE + nglyph*2, gsub, v, Substitution());
1166 }
1167 
1168 
1169 /**************************
1170  * GsubChainContext       *
1171  *                        *
1172  **************************/
1173 
GsubChainContext(const Data & d)1174 GsubChainContext::GsubChainContext(const Data &d)
1175     : _d(d)
1176 {
1177     switch (_d.u16(0)) {
1178     case 1: {
1179         Coverage coverage(_d.offset_subtable(2));
1180         if (!coverage.ok()
1181             || coverage.size() != _d.u16(4))
1182             throw Format("ChainContext Substitution coverage");
1183         break;
1184     }
1185       case 2:
1186         break;
1187       case 3: {
1188           int nbacktrack = _d.u16(2);
1189           int input_offset = F3_HSIZE + nbacktrack*2;
1190           int ninput = _d.u16(input_offset);
1191           if (ninput < 1)
1192               throw Format("GSUB ChainContext Substitution input sequence");
1193           Coverage coverage(_d.offset_subtable(input_offset + F3_INPUT_HSIZE));
1194           if (!coverage.ok())
1195               throw Format("GSUB ChainContext Substitution coverage");
1196           break;
1197       }
1198       default:
1199         throw Format("GSUB ChainContext Substitution");
1200     }
1201 }
1202 
1203 Coverage
coverage() const1204 GsubChainContext::coverage() const noexcept
1205 {
1206     switch (_d.u16(0)) {
1207     case 1:
1208         return Coverage(_d.offset_subtable(2), 0, false);
1209     case 3: {
1210         int nbacktrack = _d.u16(2);
1211         int input_offset = F3_HSIZE + nbacktrack*2;
1212         return Coverage(_d.offset_subtable(input_offset + F3_INPUT_HSIZE), 0, false);
1213     }
1214     default:
1215         return Coverage();
1216     }
1217 }
1218 
1219 void
mark_out_glyphs(const Gsub & gsub,Vector<bool> & gmap) const1220 GsubChainContext::mark_out_glyphs(const Gsub &gsub, Vector<bool> &gmap) const
1221 {
1222     switch (_d.u16(0)) {
1223     case 1: {
1224         int nsubruleset = _d.u16(4);
1225         for (int i = 0; i != nsubruleset; ++i) {
1226             int srs_offset = _d.u16(6 + i*2);
1227             int nsubrule = _d.u16(srs_offset);
1228             for (int j = 0; j != nsubrule; ++j) {
1229                 int subrule_offset = srs_offset + _d.u16(srs_offset + 2 + j*2);
1230                 int nbacktrack = _d.u16(subrule_offset);
1231                 int input_offset = subrule_offset + 2 + nbacktrack*2;
1232                 int ninput = _d.u16(input_offset);
1233                 int lookahead_offset = input_offset + 2 + (ninput-1)*2;
1234                 int nlookahead = _d.u16(lookahead_offset);
1235                 int subst_offset = lookahead_offset + 2 + nlookahead*2;
1236                 int nsubst = _d.u16(subst_offset);
1237 
1238                 GsubContext::subruleset_mark_out_glyphs(_d, nsubst, subst_offset + 2, gsub, gmap);
1239             }
1240         }
1241         break;
1242     }
1243     case 3: {
1244         int nbacktrack = _d.u16(2);
1245         int input_offset = F3_HSIZE + nbacktrack*2;
1246         int ninput = _d.u16(input_offset);
1247         int lookahead_offset = input_offset + F3_INPUT_HSIZE + ninput*2;
1248         int nlookahead = _d.u16(lookahead_offset);
1249         int subst_offset = lookahead_offset + F3_LOOKAHEAD_HSIZE + nlookahead*2;
1250         int nsubst = _d.u16(subst_offset);
1251 
1252         GsubContext::subruleset_mark_out_glyphs(_d, nsubst, subst_offset + F3_SUBST_HSIZE, gsub, gmap);
1253         break;
1254     }
1255     default:
1256         return;
1257     }
1258 }
1259 
1260 bool
f1_unparse(const Gsub & gsub,Vector<Substitution> & v,const Coverage & limit) const1261 GsubChainContext::f1_unparse(const Gsub &gsub, Vector<Substitution> &v, const Coverage &limit) const
1262 {
1263     Coverage input0_coverage(_d.offset_subtable(2), 0, false);
1264     Coverage::iterator i0iter = input0_coverage.begin();
1265 
1266     for (int i0index = 0; i0index != input0_coverage.size();
1267          ++i0index, ++i0iter) {
1268         int srs_offset = _d.u16(6 + i0index*2);
1269         int nsubrule = _d.u16(srs_offset);
1270         for (int srindex = 0; srindex != nsubrule; ++srindex) {
1271             int sr_offset = srs_offset + _d.u16(srs_offset + 2 + srindex*2);
1272             int nbacktrack = _d.u16(sr_offset);
1273             int input_offset = sr_offset + 2 + nbacktrack*2;
1274             int ninput = _d.u16(input_offset);
1275             int lookahead_offset = input_offset + 2 + (ninput-1)*2;
1276             int nlookahead = _d.u16(lookahead_offset);
1277             int subst_offset = lookahead_offset + 2 + nlookahead*2;
1278             int nsubst = _d.u16(subst_offset);
1279             int subtab_offset = subst_offset + 2;
1280 
1281             Substitution s(nbacktrack, ninput, ninput, nlookahead);
1282             if (gsub.chaincontext_reverse_backtrack()) {
1283                 for (int i = 0; i != nbacktrack; ++i)
1284                     s.left_glyphptr()[i] = _d.u16(sr_offset + 2 + i*2);
1285             } else {
1286                 for (int i = nbacktrack - 1; i != -1; --i)
1287                     s.left_glyphptr()[nbacktrack - 1 - i] = _d.u16(sr_offset + 2 + i*2);
1288             }
1289             Glyph* in_begin = s.in_glyphptr();
1290             Glyph* out_begin = s.out_glyphptr();
1291             in_begin[0] = out_begin[0] = *i0iter;
1292             for (int i = 1; i != ninput; ++i)
1293                 in_begin[i] = out_begin[i] = _d.u16(input_offset + 2 + (i-1)*2);
1294             for (int i = 0; i != ninput; ++i)
1295                 if (!limit.covers(in_begin[i]))
1296                     goto skip;
1297             for (int i = 0; i != nlookahead; ++i)
1298                 s.right_glyphptr()[i] = _d.u16(lookahead_offset + 2 + i*2);
1299 
1300             // now, apply referred lookups to the resulting substitution array
1301             GsubContext::f1_unparse(_d, nsubst, subtab_offset, gsub, v, s);
1302         skip: ;
1303         }
1304     }
1305 
1306     return true;
1307 }
1308 
1309 bool
f3_unparse(const Gsub & gsub,Vector<Substitution> & v,const Coverage & limit) const1310 GsubChainContext::f3_unparse(const Gsub &gsub, Vector<Substitution> &v, const Coverage &limit) const
1311 {
1312     int nbacktrack = _d.u16(2);
1313     int input_offset = F3_HSIZE + nbacktrack*2;
1314     int ninput = _d.u16(input_offset);
1315     int lookahead_offset = input_offset + F3_INPUT_HSIZE + ninput*2;
1316     int nlookahead = _d.u16(lookahead_offset);
1317     int subst_offset = lookahead_offset + F3_LOOKAHEAD_HSIZE + nlookahead*2;
1318     int nsubst = _d.u16(subst_offset);
1319 
1320     Vector<Coverage> backtrackc;
1321     Vector<Coverage> lookaheadc;
1322     if (gsub.chaincontext_reverse_backtrack()) {
1323         for (int i = 0; i < nbacktrack; i++)
1324             backtrackc.push_back(Coverage(_d.offset_subtable(F3_HSIZE + i*2)) & limit);
1325     } else {
1326         for (int i = nbacktrack - 1; i >= 0; i--)
1327             backtrackc.push_back(Coverage(_d.offset_subtable(F3_HSIZE + i*2)) & limit);
1328     }
1329     for (int i = 0; i < nlookahead; i++)
1330         lookaheadc.push_back(Coverage(_d.offset_subtable(lookahead_offset + F3_LOOKAHEAD_HSIZE + i*2)) & limit);
1331 
1332     // give up if would generate too many substitutions
1333     double n = 1;
1334     for (int i = 0; i < nbacktrack; ++i)
1335         n *= backtrackc[i].size();
1336     for (int i = 0; i < nlookahead; ++i)
1337         n *= lookaheadc[i].size();
1338     for (int i = 0; i < ninput; ++i)
1339         n *= (Coverage(_d.offset_subtable(input_offset + F3_INPUT_HSIZE + i*2)) & limit).size();
1340     if (n > 1000000)            // arbitrary cutoff
1341         return false;
1342 
1343     Vector<Coverage::iterator> backtracki;
1344     Vector<Coverage::iterator> lookaheadi;
1345     for (int i = 0; i < nbacktrack; i++)
1346         backtracki.push_back(backtrackc[i].begin());
1347     for (int i = 0; i < nlookahead; i++)
1348         lookaheadi.push_back(lookaheadc[i].begin());
1349 
1350     bool any = false;
1351 
1352     while (1) {
1353 
1354         // run GsubContext
1355         Substitution s(nbacktrack, 0, 0, nlookahead);
1356         Glyph *left_begin = s.left_glyphptr();
1357         for (int i = 0; i < nbacktrack; i++)
1358             left_begin[i] = *backtracki[i];
1359         Glyph *right_begin = s.right_glyphptr();
1360         for (int i = 0; i < nlookahead; i++)
1361             right_begin[i] = *lookaheadi[i];
1362 
1363         any |= GsubContext::f3_unparse(_d, ninput, input_offset + F3_INPUT_HSIZE, limit, nsubst, subst_offset + F3_SUBST_HSIZE, gsub, v, s);
1364 
1365         // step iterators
1366         for (int i = nlookahead - 1; i >= 0; i--) {
1367             lookaheadi[i]++;
1368             if (lookaheadi[i])
1369                 goto next;
1370             lookaheadi[i] = lookaheadc[i].begin();
1371         }
1372         for (int i = nbacktrack - 1; i >= 0; i--) {
1373             backtracki[i]++;
1374             if (backtracki[i])
1375                 goto next;
1376             backtracki[i] = backtrackc[i].begin();
1377         }
1378         break;
1379 
1380       next: ;
1381     }
1382 
1383     return any;
1384 }
1385 
1386 bool
unparse(const Gsub & gsub,Vector<Substitution> & v,const Coverage & limit) const1387 GsubChainContext::unparse(const Gsub &gsub, Vector<Substitution> &v, const Coverage &limit) const
1388 {
1389     if (_d.u16(0) == 1)
1390         return f1_unparse(gsub, v, limit);
1391     else if (_d.u16(0) == 3)
1392         return f3_unparse(gsub, v, limit);
1393     else
1394         return false;
1395 }
1396 
1397 
1398 }}
1399