1 // -*- related-file-name: "../include/efont/otfgsub.hh" -*-
2 
3 /* otfgsub.{cc,hh} -- OpenType GSUB table
4  *
5  * Copyright (c) 2003-2014 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) throw ()
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) throw ()
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) throw ()
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) throw ()
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) throw ()
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) throw ()
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) throw ()
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) throw ()
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) throw ()
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) throw (Error)
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) throw (Error)
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) throw (Error)
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 throw ()
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) throw (Error)
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 throw ()
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) throw (Error)
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 throw ()
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) throw (Error)
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 throw ()
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) throw (Error)
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 throw ()
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             Glyph* left_begin = s.left_glyphptr();
1283             if (gsub.chaincontext_reverse_backtrack()) {
1284                 for (int i = 0; i != nbacktrack; ++i)
1285                     s.left_glyphptr()[i] = _d.u16(sr_offset + 2 + i*2);
1286             } else {
1287                 for (int i = nbacktrack - 1; i != -1; --i)
1288                     s.left_glyphptr()[nbacktrack - 1 - i] = _d.u16(sr_offset + 2 + i*2);
1289             }
1290             Glyph* in_begin = s.in_glyphptr();
1291             Glyph* out_begin = s.out_glyphptr();
1292             in_begin[0] = out_begin[0] = *i0iter;
1293             for (int i = 1; i != ninput; ++i)
1294                 in_begin[i] = out_begin[i] = _d.u16(input_offset + 2 + (i-1)*2);
1295             for (int i = 0; i != ninput; ++i)
1296                 if (!limit.covers(in_begin[i]))
1297                     goto skip;
1298             for (int i = 0; i != nlookahead; ++i)
1299                 s.right_glyphptr()[i] = _d.u16(lookahead_offset + 2 + i*2);
1300 
1301             // now, apply referred lookups to the resulting substitution array
1302             GsubContext::f1_unparse(_d, nsubst, subtab_offset, gsub, v, s);
1303         skip: ;
1304         }
1305     }
1306 
1307     return true;
1308 }
1309 
1310 bool
f3_unparse(const Gsub & gsub,Vector<Substitution> & v,const Coverage & limit) const1311 GsubChainContext::f3_unparse(const Gsub &gsub, Vector<Substitution> &v, const Coverage &limit) const
1312 {
1313     int nbacktrack = _d.u16(2);
1314     int input_offset = F3_HSIZE + nbacktrack*2;
1315     int ninput = _d.u16(input_offset);
1316     int lookahead_offset = input_offset + F3_INPUT_HSIZE + ninput*2;
1317     int nlookahead = _d.u16(lookahead_offset);
1318     int subst_offset = lookahead_offset + F3_LOOKAHEAD_HSIZE + nlookahead*2;
1319     int nsubst = _d.u16(subst_offset);
1320 
1321     Vector<Coverage> backtrackc;
1322     Vector<Coverage> lookaheadc;
1323     if (gsub.chaincontext_reverse_backtrack()) {
1324 	for (int i = 0; i < nbacktrack; i++)
1325 	    backtrackc.push_back(Coverage(_d.offset_subtable(F3_HSIZE + i*2)) & limit);
1326     } else {
1327 	for (int i = nbacktrack - 1; i >= 0; i--)
1328 	    backtrackc.push_back(Coverage(_d.offset_subtable(F3_HSIZE + i*2)) & limit);
1329     }
1330     for (int i = 0; i < nlookahead; i++)
1331 	lookaheadc.push_back(Coverage(_d.offset_subtable(lookahead_offset + F3_LOOKAHEAD_HSIZE + i*2)) & limit);
1332 
1333     // give up if would generate too many substitutions
1334     double n = 1;
1335     for (int i = 0; i < nbacktrack; ++i)
1336 	n *= backtrackc[i].size();
1337     for (int i = 0; i < nlookahead; ++i)
1338 	n *= lookaheadc[i].size();
1339     for (int i = 0; i < ninput; ++i)
1340 	n *= (Coverage(_d.offset_subtable(input_offset + F3_INPUT_HSIZE + i*2)) & limit).size();
1341     if (n > 1000000)		// arbitrary cutoff
1342 	return false;
1343 
1344     Vector<Coverage::iterator> backtracki;
1345     Vector<Coverage::iterator> lookaheadi;
1346     for (int i = 0; i < nbacktrack; i++)
1347 	backtracki.push_back(backtrackc[i].begin());
1348     for (int i = 0; i < nlookahead; i++)
1349 	lookaheadi.push_back(lookaheadc[i].begin());
1350 
1351     bool any = false;
1352 
1353     while (1) {
1354 
1355 	// run GsubContext
1356 	Substitution s(nbacktrack, 0, 0, nlookahead);
1357 	Glyph *left_begin = s.left_glyphptr();
1358 	for (int i = 0; i < nbacktrack; i++)
1359 	    left_begin[i] = *backtracki[i];
1360 	Glyph *right_begin = s.right_glyphptr();
1361 	for (int i = 0; i < nlookahead; i++)
1362 	    right_begin[i] = *lookaheadi[i];
1363 
1364 	any |= GsubContext::f3_unparse(_d, ninput, input_offset + F3_INPUT_HSIZE, limit, nsubst, subst_offset + F3_SUBST_HSIZE, gsub, v, s);
1365 
1366 	// step iterators
1367 	for (int i = nlookahead - 1; i >= 0; i--) {
1368 	    lookaheadi[i]++;
1369 	    if (lookaheadi[i])
1370 		goto next;
1371 	    lookaheadi[i] = lookaheadc[i].begin();
1372 	}
1373 	for (int i = nbacktrack - 1; i >= 0; i--) {
1374 	    backtracki[i]++;
1375 	    if (backtracki[i])
1376 		goto next;
1377 	    backtracki[i] = backtrackc[i].begin();
1378 	}
1379 	break;
1380 
1381       next: ;
1382     }
1383 
1384     return any;
1385 }
1386 
1387 bool
unparse(const Gsub & gsub,Vector<Substitution> & v,const Coverage & limit) const1388 GsubChainContext::unparse(const Gsub &gsub, Vector<Substitution> &v, const Coverage &limit) const
1389 {
1390     if (_d.u16(0) == 1)
1391         return f1_unparse(gsub, v, limit);
1392     else if (_d.u16(0) == 3)
1393         return f3_unparse(gsub, v, limit);
1394     else
1395 	return false;
1396 }
1397 
1398 
1399 }}
1400