1 // -*- related-file-name: "../include/efont/otfgpos.hh" -*-
2 
3 /* otfgpos.{cc,hh} -- OpenType GPOS 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/otfgpos.hh>
20 #include <lcdf/error.hh>
21 #include <lcdf/straccum.hh>
22 #include <errno.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <algorithm>
26 
27 namespace Efont { namespace OpenType {
28 
29 
30 /**************************
31  * Gpos                   *
32  *                        *
33  **************************/
34 
Gpos(const Data & d,ErrorHandler * errh)35 Gpos::Gpos(const Data &d, ErrorHandler *errh)
36 {
37     // Fixed    Version
38     // Offset   ScriptList
39     // Offset   FeatureList
40     // Offset   LookupList
41     if (d.length() == 0)
42         throw BlankTable("GPOS");
43     if (d.u16(0) != 1)
44         throw Format("GPOS");
45     if (_script_list.assign(d.offset_subtable(4), errh) < 0)
46         throw Format("GPOS script list");
47     if (_feature_list.assign(d.offset_subtable(6), errh) < 0)
48         throw Format("GPOS feature list");
49     _lookup_list = d.offset_subtable(8);
50 }
51 
52 int
nlookups() const53 Gpos::nlookups() const
54 {
55     return _lookup_list.u16(0);
56 }
57 
58 GposLookup
lookup(unsigned i) const59 Gpos::lookup(unsigned i) const
60 {
61     if (i >= _lookup_list.u16(0))
62         throw Error("GPOS lookup out of range");
63     else
64         return GposLookup(_lookup_list.offset_subtable(2 + i*2));
65 }
66 
67 
68 /**************************
69  * GposValue              *
70  *                        *
71  **************************/
72 
73 const int GposValue::nibble_bitcount_x2[] = { 0, 2, 2, 4, 2, 4, 4, 6,
74                                               2, 4, 4, 6, 4, 6, 6, 8 };
75 
76 
77 /**************************
78  * GposLookup             *
79  *                        *
80  **************************/
81 
GposLookup(const Data & d)82 GposLookup::GposLookup(const Data &d)
83     : _d(d)
84 {
85     if (_d.length() < 6)
86         throw Format("GPOS Lookup table");
87     _type = _d.u16(0);
88     if (_type == L_EXTENSION && _d.u16(4) != 0) {
89         Data first_subtable = _d.offset_subtable(HEADERSIZE);
90         if (first_subtable.length() < 8 || first_subtable.u16(0) != 1)
91             throw Format("GPOS Extension Lookup table");
92         _type = first_subtable.u16(2);
93     }
94 }
95 
96 Data
subtable(int i) const97 GposLookup::subtable(int i) const
98 {
99     Data subd = _d.offset_subtable(HEADERSIZE + i*RECSIZE);
100     if (_d.u16(0) != L_EXTENSION)
101         return subd;
102     else if (subd.length() >= 8 && subd.u16(0) == 1 && subd.u16(2) == _type)
103         return subd.subtable(subd.u32(4));
104     else
105         return Data();
106 }
107 
108 bool
unparse_automatics(Vector<Positioning> & v,ErrorHandler * errh) const109 GposLookup::unparse_automatics(Vector<Positioning> &v, ErrorHandler *errh) const
110 {
111     int nlookup = _d.u16(4), success = 0;
112     switch (_type) {
113       case L_SINGLE:
114         for (int i = 0; i < nlookup; i++)
115             try {
116                 GposSingle s(subtable(i));
117                 s.unparse(v);
118                 success++;
119             } catch (Error e) {
120                 if (errh)
121                     errh->warning("%s, continuing", e.description.c_str());
122             }
123         return success > 0;
124       case L_PAIR:
125         for (int i = 0; i < nlookup; i++)
126             try {
127                 GposPair p(subtable(i));
128                 p.unparse(v);
129                 success++;
130             } catch (Error e) {
131                 if (errh)
132                     errh->warning("%s, continuing", e.description.c_str());
133             }
134         return success > 0;
135       default:
136         return false;
137     }
138 }
139 
140 
141 /**************************
142  * GposSingle             *
143  *                        *
144  **************************/
145 
GposSingle(const Data & d)146 GposSingle::GposSingle(const Data &d)
147     : _d(d)
148 {
149     if (_d[0] != 0
150         || (_d[1] != 1 && _d[1] != 2))
151         throw Format("GPOS Single Positioning");
152     Coverage coverage(_d.offset_subtable(2));
153     if (!coverage.ok()
154         || (_d[1] == 2 && coverage.size() > _d.u16(6)))
155         throw Format("GPOS Single Positioning coverage");
156 }
157 
158 Coverage
coverage() const159 GposSingle::coverage() const noexcept
160 {
161     return Coverage(_d.offset_subtable(2), 0, false);
162 }
163 
164 void
unparse(Vector<Positioning> & v) const165 GposSingle::unparse(Vector<Positioning> &v) const
166 {
167     if (_d[1] == 1) {
168         int format = _d.u16(4);
169         Data value = _d.subtable(6);
170         for (Coverage::iterator i = coverage().begin(); i; i++)
171             v.push_back(Positioning(Position(*i, format, value)));
172     } else {
173         int format = _d.u16(4);
174         int size = GposValue::size(format);
175         for (Coverage::iterator i = coverage().begin(); i; i++)
176             v.push_back(Positioning(Position(*i, format, _d.subtable(F2_HEADERSIZE + size*i.coverage_index()))));
177     }
178 }
179 
180 
181 /**************************
182  * GposPair               *
183  *                        *
184  **************************/
185 
GposPair(const Data & d)186 GposPair::GposPair(const Data &d)
187     : _d(d)
188 {
189     if (_d[0] != 0
190         || (_d[1] != 1 && _d[1] != 2))
191         throw Format("GPOS Pair Positioning");
192     Coverage coverage(_d.offset_subtable(2));
193     if (!coverage.ok()
194         || (_d[1] == 1 && coverage.size() > _d.u16(8)))
195         throw Format("GPOS Pair Positioning coverage");
196 }
197 
198 Coverage
coverage() const199 GposPair::coverage() const noexcept
200 {
201     return Coverage(_d.offset_subtable(2), 0, false);
202 }
203 
204 void
unparse(Vector<Positioning> & v) const205 GposPair::unparse(Vector<Positioning> &v) const
206 {
207     if (_d[1] == 1) {
208         int format1 = _d.u16(4);
209         int format2 = _d.u16(6);
210         int f2_pos = PAIRVALUE_HEADERSIZE + GposValue::size(format1);
211         int pairvalue_size = f2_pos + GposValue::size(format2);
212         for (Coverage::iterator i = coverage().begin(); i; i++) {
213             Data pairset = _d.offset_subtable(F1_HEADERSIZE + i.coverage_index()*F1_RECSIZE);
214             int npair = pairset.u16(0);
215             for (int j = 0; j < npair; j++) {
216                 Data pair = pairset.subtable(PAIRSET_HEADERSIZE + j*pairvalue_size);
217                 v.push_back(Positioning(Position(*i, format1, pair.subtable(PAIRVALUE_HEADERSIZE)),
218                                         Position(pair.u16(0), format2, pair.subtable(f2_pos))));
219             }
220         }
221     } else {                    // _d[1] == 2
222         int format1 = _d.u16(4);
223         int format2 = _d.u16(6);
224         int f2_pos = GposValue::size(format1);
225         int recsize = f2_pos + GposValue::size(format2);
226         ClassDef class1(_d.offset_subtable(8));
227         ClassDef class2(_d.offset_subtable(10));
228         Coverage coverage = this->coverage();
229         int nclass1 = _d.u16(12);
230         int nclass2 = _d.u16(14);
231         int offset = F2_HEADERSIZE;
232         for (int c1 = 0; c1 < nclass1; c1++)
233             for (int c2 = 0; c2 < nclass2; c2++, offset += recsize) {
234                 Position p1(format1, _d.subtable(offset));
235                 Position p2(format2, _d.subtable(offset + f2_pos));
236                 if (p1 || p2) {
237                     for (ClassDef::class_iterator c1i = class1.begin(c1, coverage); c1i; c1i++)
238                         for (ClassDef::class_iterator c2i = class2.begin(c2); c2i; c2i++)
239                             v.push_back(Positioning(Position(*c1i, p1), Position(*c2i, p2)));
240                 }
241             }
242     }
243 }
244 
245 
246 /**************************
247  * Positioning            *
248  *                        *
249  **************************/
250 
251 static void
unparse_glyphid(StringAccum & sa,Glyph gid,const Vector<PermString> * gns)252 unparse_glyphid(StringAccum &sa, Glyph gid, const Vector<PermString> *gns)
253 {
254     if (!gns)
255         gns = &debug_glyph_names;
256     if (gid && gns && gns->size() > gid && (*gns)[gid])
257         sa << (*gns)[gid];
258     else
259         sa << "g" << gid;
260 }
261 
262 void
unparse(StringAccum & sa,const Vector<PermString> * gns) const263 Position::unparse(StringAccum &sa, const Vector<PermString> *gns) const
264 {
265     unparse_glyphid(sa, g, gns);
266     if (placed())
267         sa << '@' << pdx << ',' << pdy;
268     sa << '+' << adx;
269     if (ady)
270         sa << '/' << ady;
271 }
272 
273 String
unparse(const Vector<PermString> * gns) const274 Position::unparse(const Vector<PermString> *gns) const
275 {
276     StringAccum sa;
277     unparse(sa, gns);
278     return sa.take_string();
279 }
280 
281 bool
context_in(const Coverage & c) const282 Positioning::context_in(const Coverage &c) const
283 {
284     return (c.covers(_left.g) || !_left.g) && (!_right.g || c.covers(_right.g));
285 }
286 
287 bool
context_in(const GlyphSet & gs) const288 Positioning::context_in(const GlyphSet &gs) const
289 {
290     return (gs.covers(_left.g) || !_left.g) && (!_right.g || gs.covers(_right.g));
291 }
292 
293 void
unparse(StringAccum & sa,const Vector<PermString> * gns) const294 Positioning::unparse(StringAccum &sa, const Vector<PermString> *gns) const
295 {
296     if (!*this)
297         sa << "NULL[]";
298     else if (is_single()) {
299         sa << "SINGLE[";
300         _left.unparse(sa, gns);
301         sa << ']';
302     } else if (is_pairkern()) {
303         sa << "KERN[";
304         unparse_glyphid(sa, _left.g, gns);
305         sa << ' ';
306         unparse_glyphid(sa, _right.g, gns);
307         sa << "+" << _left.adx << ']';
308     } else if (is_pair()) {
309         sa << "PAIR[";
310         _left.unparse(sa, gns);
311         sa << ' ';
312         _right.unparse(sa, gns);
313         sa << ']';
314     } else
315         sa << "UNKNOWN[]";
316 }
317 
318 String
unparse(const Vector<PermString> * gns) const319 Positioning::unparse(const Vector<PermString> *gns) const
320 {
321     StringAccum sa;
322     unparse(sa, gns);
323     return sa.take_string();
324 }
325 
326 }}
327 
328 #include <lcdf/vector.cc>
329