1 // -*- related-file-name: "../include/efont/otfgpos.hh" -*-
2 
3 /* otfgpos.{cc,hh} -- OpenType GPOS table
4  *
5  * Copyright (c) 2003-2012 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) throw (Error)
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) throw (Error)
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) throw (Error)
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 throw ()
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) throw (Error)
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 throw ()
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