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