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