1 /* GRAPHITE2 LICENSING
2
3 Copyright 2010, SIL International
4 All rights reserved.
5
6 This library is free software; you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation; either version 2.1 of License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should also have received a copy of the GNU Lesser General Public
17 License along with this library in the file named "LICENSE".
18 If not, write to the Free Software Foundation, 51 Franklin Street,
19 Suite 500, Boston, MA 02110-1335, USA or visit their web page on the
20 internet at http://www.fsf.org/licenses/lgpl.html.
21
22 Alternatively, the contents of this file may be used under the terms of the
23 Mozilla Public License (http://mozilla.org/MPL) or the GNU General Public
24 License, as published by the Free Software Foundation, either version 2
25 of the License or (at your option) any later version.
26 */
27 #include <cstdlib>
28 #include "graphite2/Segment.h"
29 #include "inc/debug.h"
30 #include "inc/Endian.h"
31 #include "inc/Silf.h"
32 #include "inc/Segment.h"
33 #include "inc/Rule.h"
34 #include "inc/Error.h"
35
36
37 using namespace graphite2;
38
39 namespace { static const uint32 ERROROFFSET = 0xFFFFFFFF; }
40
Silf()41 Silf::Silf() throw()
42 : m_passes(0),
43 m_pseudos(0),
44 m_classOffsets(0),
45 m_classData(0),
46 m_justs(0),
47 m_numPasses(0),
48 m_numJusts(0),
49 m_sPass(0),
50 m_pPass(0),
51 m_jPass(0),
52 m_bPass(0),
53 m_flags(0),
54 m_dir(0),
55 m_aPseudo(0),
56 m_aBreak(0),
57 m_aUser(0),
58 m_aBidi(0),
59 m_aMirror(0),
60 m_aPassBits(0),
61 m_iMaxComp(0),
62 m_aCollision(0),
63 m_aLig(0),
64 m_numPseudo(0),
65 m_nClass(0),
66 m_nLinear(0),
67 m_gEndLine(0)
68 {
69 memset(&m_silfinfo, 0, sizeof m_silfinfo);
70 }
71
~Silf()72 Silf::~Silf() throw()
73 {
74 releaseBuffers();
75 }
76
releaseBuffers()77 void Silf::releaseBuffers() throw()
78 {
79 delete [] m_passes;
80 delete [] m_pseudos;
81 free(m_classOffsets);
82 free(m_classData);
83 free(m_justs);
84 m_passes= 0;
85 m_pseudos = 0;
86 m_classOffsets = 0;
87 m_classData = 0;
88 m_justs = 0;
89 }
90
91
readGraphite(const byte * const silf_start,size_t lSilf,Face & face,uint32 version)92 bool Silf::readGraphite(const byte * const silf_start, size_t lSilf, Face& face, uint32 version)
93 {
94 const byte * p = silf_start,
95 * const silf_end = p + lSilf;
96 Error e;
97
98 if (e.test(version >= 0x00060000, E_BADSILFVERSION))
99 {
100 releaseBuffers(); return face.error(e);
101 }
102 if (version >= 0x00030000)
103 {
104 if (e.test(lSilf < 28, E_BADSIZE)) { releaseBuffers(); return face.error(e); }
105 be::skip<int32>(p); // ruleVersion
106 be::skip<uint16>(p,2); // passOffset & pseudosOffset
107 }
108 else if (e.test(lSilf < 20, E_BADSIZE)) { releaseBuffers(); return face.error(e); }
109 const uint16 maxGlyph = be::read<uint16>(p);
110 m_silfinfo.extra_ascent = be::read<uint16>(p);
111 m_silfinfo.extra_descent = be::read<uint16>(p);
112 m_numPasses = be::read<uint8>(p);
113 m_sPass = be::read<uint8>(p);
114 m_pPass = be::read<uint8>(p);
115 m_jPass = be::read<uint8>(p);
116 m_bPass = be::read<uint8>(p);
117 m_flags = be::read<uint8>(p);
118 be::skip<uint8>(p,2); // max{Pre,Post}Context.
119 m_aPseudo = be::read<uint8>(p);
120 m_aBreak = be::read<uint8>(p);
121 m_aBidi = be::read<uint8>(p);
122 m_aMirror = be::read<uint8>(p);
123 m_aPassBits = be::read<uint8>(p);
124
125 // Read Justification levels.
126 m_numJusts = be::read<uint8>(p);
127 if (e.test(maxGlyph >= face.glyphs().numGlyphs(), E_BADMAXGLYPH)
128 || e.test(p + m_numJusts * 8 >= silf_end, E_BADNUMJUSTS))
129 {
130 releaseBuffers(); return face.error(e);
131 }
132
133 if (m_numJusts)
134 {
135 m_justs = gralloc<Justinfo>(m_numJusts);
136 if (e.test(!m_justs, E_OUTOFMEM)) return face.error(e);
137
138 for (uint8 i = 0; i < m_numJusts; i++)
139 {
140 ::new(m_justs + i) Justinfo(p[0], p[1], p[2], p[3]);
141 be::skip<byte>(p,8);
142 }
143 }
144
145 if (e.test(p + sizeof(uint16) + sizeof(uint8)*8 >= silf_end, E_BADENDJUSTS)) { releaseBuffers(); return face.error(e); }
146 m_aLig = be::read<uint16>(p);
147 m_aUser = be::read<uint8>(p);
148 m_iMaxComp = be::read<uint8>(p);
149 m_dir = be::read<uint8>(p) - 1;
150 m_aCollision = be::read<uint8>(p);
151 be::skip<byte>(p,3);
152 be::skip<uint16>(p, be::read<uint8>(p)); // don't need critical features yet
153 be::skip<byte>(p); // reserved
154 if (e.test(p >= silf_end, E_BADCRITFEATURES)) { releaseBuffers(); return face.error(e); }
155 be::skip<uint32>(p, be::read<uint8>(p)); // don't use scriptTag array.
156 if (e.test(p + sizeof(uint16) + sizeof(uint32) >= silf_end, E_BADSCRIPTTAGS)) { releaseBuffers(); return face.error(e); }
157 m_gEndLine = be::read<uint16>(p); // lbGID
158 const byte * o_passes = p;
159 uint32 passes_start = be::read<uint32>(p);
160
161 const size_t num_attrs = face.glyphs().numAttrs();
162 if (e.test(m_aPseudo >= num_attrs, E_BADAPSEUDO)
163 || e.test(m_aBreak >= num_attrs, E_BADABREAK)
164 || e.test(m_aBidi >= num_attrs, E_BADABIDI)
165 || e.test(m_aMirror>= num_attrs, E_BADAMIRROR)
166 || e.test(m_aCollision && m_aCollision >= num_attrs - 5, E_BADACOLLISION)
167 || e.test(m_numPasses > 128, E_BADNUMPASSES) || e.test(passes_start >= lSilf, E_BADPASSESSTART)
168 || e.test(m_pPass < m_sPass, E_BADPASSBOUND) || e.test(m_pPass > m_numPasses, E_BADPPASS) || e.test(m_sPass > m_numPasses, E_BADSPASS)
169 || e.test(m_jPass < m_pPass, E_BADJPASSBOUND) || e.test(m_jPass > m_numPasses, E_BADJPASS)
170 || e.test((m_bPass != 0xFF && (m_bPass < m_jPass || m_bPass > m_numPasses)), E_BADBPASS)
171 || e.test(m_aLig > 127, E_BADALIG))
172 {
173 releaseBuffers();
174 return face.error(e);
175 }
176 be::skip<uint32>(p, m_numPasses);
177 if (e.test(unsigned(p - silf_start) + sizeof(uint16) >= passes_start, E_BADPASSESSTART)) { releaseBuffers(); return face.error(e); }
178 m_numPseudo = be::read<uint16>(p);
179 be::skip<uint16>(p, 3); // searchPseudo, pseudoSelector, pseudoShift
180 m_pseudos = new Pseudo[m_numPseudo];
181 if (e.test(unsigned(p - silf_start) + m_numPseudo*(sizeof(uint32) + sizeof(uint16)) >= passes_start, E_BADNUMPSEUDO)
182 || e.test(!m_pseudos, E_OUTOFMEM))
183 {
184 releaseBuffers(); return face.error(e);
185 }
186 for (int i = 0; i < m_numPseudo; i++)
187 {
188 m_pseudos[i].uid = be::read<uint32>(p);
189 m_pseudos[i].gid = be::read<uint16>(p);
190 }
191
192 const size_t clen = readClassMap(p, passes_start + silf_start - p, version, e);
193 m_passes = new Pass[m_numPasses];
194 if (e || e.test(clen > unsigned(passes_start + silf_start - p), E_BADPASSESSTART)
195 || e.test(!m_passes, E_OUTOFMEM))
196 { releaseBuffers(); return face.error(e); }
197
198 for (size_t i = 0; i < m_numPasses; ++i)
199 {
200 uint32 pass_start = be::read<uint32>(o_passes);
201 uint32 pass_end = be::peek<uint32>(o_passes);
202 face.error_context((face.error_context() & 0xFF00) + EC_ASILF + unsigned(i << 16));
203 if (e.test(pass_start > pass_end, E_BADPASSSTART)
204 || e.test(pass_start < passes_start, E_BADPASSSTART)
205 || e.test(pass_end > lSilf, E_BADPASSEND)) {
206 releaseBuffers(); return face.error(e);
207 }
208
209 enum passtype pt = PASS_TYPE_UNKNOWN;
210 if (i >= m_jPass) pt = PASS_TYPE_JUSTIFICATION;
211 else if (i >= m_pPass) pt = PASS_TYPE_POSITIONING;
212 else if (i >= m_sPass) pt = PASS_TYPE_SUBSTITUTE;
213 else pt = PASS_TYPE_LINEBREAK;
214
215 m_passes[i].init(this);
216 if (!m_passes[i].readPass(silf_start + pass_start, pass_end - pass_start, pass_start, face, pt,
217 version, e))
218 {
219 releaseBuffers();
220 return false;
221 }
222 }
223
224 // fill in gr_faceinfo
225 m_silfinfo.upem = face.glyphs().unitsPerEm();
226 m_silfinfo.has_bidi_pass = (m_bPass != 0xFF);
227 m_silfinfo.justifies = (m_numJusts != 0) || (m_jPass < m_pPass);
228 m_silfinfo.line_ends = (m_flags & 1);
229 m_silfinfo.space_contextuals = gr_faceinfo::gr_space_contextuals((m_flags >> 2) & 0x7);
230 return true;
231 }
232
readClassOffsets(const byte * & p,size_t data_len,Error & e)233 template<typename T> inline uint32 Silf::readClassOffsets(const byte *&p, size_t data_len, Error &e)
234 {
235 const T cls_off = 2*sizeof(uint16) + sizeof(T)*(m_nClass+1);
236 const uint32 max_off = (be::peek<T>(p + sizeof(T)*m_nClass) - cls_off)/sizeof(uint16);
237 // Check that the last+1 offset is less than or equal to the class map length.
238 if (e.test(be::peek<T>(p) != cls_off, E_MISALIGNEDCLASSES)
239 || e.test(max_off > (data_len - cls_off)/sizeof(uint16), E_HIGHCLASSOFFSET))
240 return ERROROFFSET;
241
242 // Read in all the offsets.
243 m_classOffsets = gralloc<uint32>(m_nClass+1);
244 if (e.test(!m_classOffsets, E_OUTOFMEM)) return ERROROFFSET;
245 for (uint32 * o = m_classOffsets, * const o_end = o + m_nClass + 1; o != o_end; ++o)
246 {
247 *o = (be::read<T>(p) - cls_off)/sizeof(uint16);
248 if (e.test(*o > max_off, E_HIGHCLASSOFFSET))
249 return ERROROFFSET;
250 }
251 return max_off;
252 }
253
readClassMap(const byte * p,size_t data_len,uint32 version,Error & e)254 size_t Silf::readClassMap(const byte *p, size_t data_len, uint32 version, Error &e)
255 {
256 if (e.test(data_len < sizeof(uint16)*2, E_BADCLASSSIZE)) return ERROROFFSET;
257
258 m_nClass = be::read<uint16>(p);
259 m_nLinear = be::read<uint16>(p);
260
261 // Check that numLinear < numClass,
262 // that there is at least enough data for numClasses offsets.
263 if (e.test(m_nLinear > m_nClass, E_TOOMANYLINEAR)
264 || e.test((m_nClass + 1) * (version >= 0x00040000 ? sizeof(uint32) : sizeof(uint16)) > (data_len - 4), E_CLASSESTOOBIG))
265 return ERROROFFSET;
266
267 uint32 max_off;
268 if (version >= 0x00040000)
269 max_off = readClassOffsets<uint32>(p, data_len, e);
270 else
271 max_off = readClassOffsets<uint16>(p, data_len, e);
272
273 if (max_off == ERROROFFSET) return ERROROFFSET;
274
275 if (e.test((int)max_off < m_nLinear + (m_nClass - m_nLinear) * 6, E_CLASSESTOOBIG))
276 return ERROROFFSET;
277
278 // Check the linear offsets are sane, these must be monotonically increasing.
279 assert(m_nClass >= m_nLinear);
280 for (const uint32 *o = m_classOffsets, * const o_end = o + m_nLinear; o != o_end; ++o)
281 if (e.test(o[0] > o[1], E_BADCLASSOFFSET))
282 return ERROROFFSET;
283
284 // Fortunately the class data is all uint16s so we can decode these now
285 m_classData = gralloc<uint16>(max_off);
286 if (e.test(!m_classData, E_OUTOFMEM)) return ERROROFFSET;
287 for (uint16 *d = m_classData, * const d_end = d + max_off; d != d_end; ++d)
288 *d = be::read<uint16>(p);
289
290 // Check the lookup class invariants for each non-linear class
291 for (const uint32 *o = m_classOffsets + m_nLinear, * const o_end = m_classOffsets + m_nClass; o != o_end; ++o)
292 {
293 const uint16 * lookup = m_classData + *o;
294 if (e.test(*o + 4 > max_off, E_HIGHCLASSOFFSET) // LookupClass doesn't stretch over max_off
295 || e.test(lookup[0] == 0 // A LookupClass with no looks is a suspicious thing ...
296 || lookup[0] * 2 + *o + 4 > max_off // numIDs lookup pairs fits within (start of LookupClass' lookups array, max_off]
297 || lookup[3] + lookup[1] != lookup[0], E_BADCLASSLOOKUPINFO) // rangeShift: numIDs - searchRange
298 || e.test(((o[1] - *o) & 1) != 0, ERROROFFSET)) // glyphs are in pairs so difference must be even.
299 return ERROROFFSET;
300 }
301
302 return max_off;
303 }
304
findPseudo(uint32 uid) const305 uint16 Silf::findPseudo(uint32 uid) const
306 {
307 for (int i = 0; i < m_numPseudo; i++)
308 if (m_pseudos[i].uid == uid) return m_pseudos[i].gid;
309 return 0;
310 }
311
findClassIndex(uint16 cid,uint16 gid) const312 uint16 Silf::findClassIndex(uint16 cid, uint16 gid) const
313 {
314 if (cid > m_nClass) return -1;
315
316 const uint16 * cls = m_classData + m_classOffsets[cid];
317 if (cid < m_nLinear) // output class being used for input, shouldn't happen
318 {
319 for (unsigned int i = 0, n = m_classOffsets[cid + 1] - m_classOffsets[cid]; i < n; ++i, ++cls)
320 if (*cls == gid) return i;
321 return -1;
322 }
323 else
324 {
325 const uint16 * min = cls + 4, // lookups array
326 * max = min + cls[0]*2; // lookups aray is numIDs (cls[0]) uint16 pairs long
327 do
328 {
329 const uint16 * p = min + (-2 & ((max-min)/2));
330 if (p[0] > gid) max = p;
331 else min = p;
332 }
333 while (max - min > 2);
334 return min[0] == gid ? min[1] : -1;
335 }
336 }
337
getClassGlyph(uint16 cid,unsigned int index) const338 uint16 Silf::getClassGlyph(uint16 cid, unsigned int index) const
339 {
340 if (cid > m_nClass) return 0;
341
342 uint32 loc = m_classOffsets[cid];
343 if (cid < m_nLinear)
344 {
345 if (index < m_classOffsets[cid + 1] - loc)
346 return m_classData[index + loc];
347 }
348 else // input class being used for output. Shouldn't happen
349 {
350 for (unsigned int i = loc + 4; i < m_classOffsets[cid + 1]; i += 2)
351 if (m_classData[i + 1] == index) return m_classData[i];
352 }
353 return 0;
354 }
355
356
runGraphite(Segment * seg,uint8 firstPass,uint8 lastPass,int dobidi) const357 bool Silf::runGraphite(Segment *seg, uint8 firstPass, uint8 lastPass, int dobidi) const
358 {
359 assert(seg != 0);
360 size_t maxSize = seg->slotCount() * MAX_SEG_GROWTH_FACTOR;
361 SlotMap map(*seg, m_dir, maxSize);
362 FiniteStateMachine fsm(map, seg->getFace()->logger());
363 vm::Machine m(map);
364 uint8 lbidi = m_bPass;
365 #if !defined GRAPHITE2_NTRACING
366 json * const dbgout = seg->getFace()->logger();
367 #endif
368
369 if (lastPass == 0)
370 {
371 if (firstPass == lastPass && lbidi == 0xFF)
372 return true;
373 lastPass = m_numPasses;
374 }
375 if ((firstPass < lbidi || (dobidi && firstPass == lbidi)) && (lastPass >= lbidi || (dobidi && lastPass + 1 == lbidi)))
376 lastPass++;
377 else
378 lbidi = 0xFF;
379
380 for (size_t i = firstPass; i < lastPass; ++i)
381 {
382 // bidi and mirroring
383 if (i == lbidi)
384 {
385 #if !defined GRAPHITE2_NTRACING
386 if (dbgout)
387 {
388 *dbgout << json::item << json::object
389 // << "pindex" << i // for debugging
390 << "id" << -1
391 << "slotsdir" << (seg->currdir() ? "rtl" : "ltr")
392 << "passdir" << (m_dir & 1 ? "rtl" : "ltr")
393 << "slots" << json::array;
394 seg->positionSlots(0, 0, 0, seg->currdir());
395 for(Slot * s = seg->first(); s; s = s->next())
396 *dbgout << dslot(seg, s);
397 *dbgout << json::close
398 << "rules" << json::array << json::close
399 << json::close;
400 }
401 #endif
402 if (seg->currdir() != (m_dir & 1))
403 seg->reverseSlots();
404 if (m_aMirror && (seg->dir() & 3) == 3)
405 seg->doMirror(m_aMirror);
406 --i;
407 lbidi = lastPass;
408 --lastPass;
409 continue;
410 }
411
412 #if !defined GRAPHITE2_NTRACING
413 if (dbgout)
414 {
415 *dbgout << json::item << json::object
416 // << "pindex" << i // for debugging
417 << "id" << i+1
418 << "slotsdir" << (seg->currdir() ? "rtl" : "ltr")
419 << "passdir" << ((m_dir & 1) ^ m_passes[i].reverseDir() ? "rtl" : "ltr")
420 << "slots" << json::array;
421 seg->positionSlots(0, 0, 0, seg->currdir());
422 for(Slot * s = seg->first(); s; s = s->next())
423 *dbgout << dslot(seg, s);
424 *dbgout << json::close;
425 }
426 #endif
427
428 // test whether to reorder, prepare for positioning
429 bool reverse = (lbidi == 0xFF) && (seg->currdir() != ((m_dir & 1) ^ m_passes[i].reverseDir()));
430 if ((i >= 32 || (seg->passBits() & (1 << i)) == 0 || m_passes[i].collisionLoops())
431 && !m_passes[i].runGraphite(m, fsm, reverse))
432 return false;
433 // only subsitution passes can change segment length, cached subsegments are short for their text
434 if (m.status() != vm::Machine::finished
435 || (seg->slotCount() && seg->slotCount() > maxSize))
436 return false;
437 }
438 return true;
439 }
440