Line data Source code
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 :
41 0 : 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 0 : m_gEndLine(0)
68 : {
69 0 : memset(&m_silfinfo, 0, sizeof m_silfinfo);
70 0 : }
71 :
72 0 : Silf::~Silf() throw()
73 : {
74 0 : releaseBuffers();
75 0 : }
76 :
77 0 : void Silf::releaseBuffers() throw()
78 : {
79 0 : delete [] m_passes;
80 0 : delete [] m_pseudos;
81 0 : free(m_classOffsets);
82 0 : free(m_classData);
83 0 : free(m_justs);
84 0 : m_passes= 0;
85 0 : m_pseudos = 0;
86 0 : m_classOffsets = 0;
87 0 : m_classData = 0;
88 0 : m_justs = 0;
89 0 : }
90 :
91 :
92 0 : bool Silf::readGraphite(const byte * const silf_start, size_t lSilf, Face& face, uint32 version)
93 : {
94 0 : const byte * p = silf_start,
95 0 : * const silf_end = p + lSilf;
96 0 : Error e;
97 :
98 0 : if (e.test(version >= 0x00060000, E_BADSILFVERSION))
99 : {
100 0 : releaseBuffers(); return face.error(e);
101 : }
102 0 : if (version >= 0x00030000)
103 : {
104 0 : if (e.test(lSilf < 28, E_BADSIZE)) { releaseBuffers(); return face.error(e); }
105 0 : be::skip<int32>(p); // ruleVersion
106 0 : be::skip<uint16>(p,2); // passOffset & pseudosOffset
107 : }
108 0 : else if (e.test(lSilf < 20, E_BADSIZE)) { releaseBuffers(); return face.error(e); }
109 0 : const uint16 maxGlyph = be::read<uint16>(p);
110 0 : m_silfinfo.extra_ascent = be::read<uint16>(p);
111 0 : m_silfinfo.extra_descent = be::read<uint16>(p);
112 0 : m_numPasses = be::read<uint8>(p);
113 0 : m_sPass = be::read<uint8>(p);
114 0 : m_pPass = be::read<uint8>(p);
115 0 : m_jPass = be::read<uint8>(p);
116 0 : m_bPass = be::read<uint8>(p);
117 0 : m_flags = be::read<uint8>(p);
118 0 : be::skip<uint8>(p,2); // max{Pre,Post}Context.
119 0 : m_aPseudo = be::read<uint8>(p);
120 0 : m_aBreak = be::read<uint8>(p);
121 0 : m_aBidi = be::read<uint8>(p);
122 0 : m_aMirror = be::read<uint8>(p);
123 0 : m_aPassBits = be::read<uint8>(p);
124 :
125 : // Read Justification levels.
126 0 : m_numJusts = be::read<uint8>(p);
127 0 : if (e.test(maxGlyph >= face.glyphs().numGlyphs(), E_BADMAXGLYPH)
128 0 : || e.test(p + m_numJusts * 8 >= silf_end, E_BADNUMJUSTS))
129 : {
130 0 : releaseBuffers(); return face.error(e);
131 : }
132 :
133 0 : if (m_numJusts)
134 : {
135 0 : m_justs = gralloc<Justinfo>(m_numJusts);
136 0 : if (e.test(!m_justs, E_OUTOFMEM)) return face.error(e);
137 :
138 0 : for (uint8 i = 0; i < m_numJusts; i++)
139 : {
140 0 : ::new(m_justs + i) Justinfo(p[0], p[1], p[2], p[3]);
141 0 : be::skip<byte>(p,8);
142 : }
143 : }
144 :
145 0 : if (e.test(p + sizeof(uint16) + sizeof(uint8)*8 >= silf_end, E_BADENDJUSTS)) { releaseBuffers(); return face.error(e); }
146 0 : m_aLig = be::read<uint16>(p);
147 0 : m_aUser = be::read<uint8>(p);
148 0 : m_iMaxComp = be::read<uint8>(p);
149 0 : m_dir = be::read<uint8>(p) - 1;
150 0 : m_aCollision = be::read<uint8>(p);
151 0 : be::skip<byte>(p,3);
152 0 : be::skip<uint16>(p, be::read<uint8>(p)); // don't need critical features yet
153 0 : be::skip<byte>(p); // reserved
154 0 : if (e.test(p >= silf_end, E_BADCRITFEATURES)) { releaseBuffers(); return face.error(e); }
155 0 : be::skip<uint32>(p, be::read<uint8>(p)); // don't use scriptTag array.
156 0 : if (e.test(p + sizeof(uint16) + sizeof(uint32) >= silf_end, E_BADSCRIPTTAGS)) { releaseBuffers(); return face.error(e); }
157 0 : m_gEndLine = be::read<uint16>(p); // lbGID
158 0 : const byte * o_passes = p;
159 0 : uint32 passes_start = be::read<uint32>(p);
160 :
161 0 : const size_t num_attrs = face.glyphs().numAttrs();
162 0 : if (e.test(m_aPseudo >= num_attrs, E_BADAPSEUDO)
163 0 : || e.test(m_aBreak >= num_attrs, E_BADABREAK)
164 0 : || e.test(m_aBidi >= num_attrs, E_BADABIDI)
165 0 : || e.test(m_aMirror>= num_attrs, E_BADAMIRROR)
166 0 : || e.test(m_aCollision && m_aCollision >= num_attrs - 5, E_BADACOLLISION)
167 0 : || e.test(m_numPasses > 128, E_BADNUMPASSES) || e.test(passes_start >= lSilf, E_BADPASSESSTART)
168 0 : || 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 0 : || e.test(m_jPass < m_pPass, E_BADJPASSBOUND) || e.test(m_jPass > m_numPasses, E_BADJPASS)
170 0 : || e.test((m_bPass != 0xFF && (m_bPass < m_jPass || m_bPass > m_numPasses)), E_BADBPASS)
171 0 : || e.test(m_aLig > 127, E_BADALIG))
172 : {
173 0 : releaseBuffers();
174 0 : return face.error(e);
175 : }
176 0 : be::skip<uint32>(p, m_numPasses);
177 0 : if (e.test(unsigned(p - silf_start) + sizeof(uint16) >= passes_start, E_BADPASSESSTART)) { releaseBuffers(); return face.error(e); }
178 0 : m_numPseudo = be::read<uint16>(p);
179 0 : be::skip<uint16>(p, 3); // searchPseudo, pseudoSelector, pseudoShift
180 0 : m_pseudos = new Pseudo[m_numPseudo];
181 0 : if (e.test(unsigned(p - silf_start) + m_numPseudo*(sizeof(uint32) + sizeof(uint16)) >= passes_start, E_BADNUMPSEUDO)
182 0 : || e.test(!m_pseudos, E_OUTOFMEM))
183 : {
184 0 : releaseBuffers(); return face.error(e);
185 : }
186 0 : for (int i = 0; i < m_numPseudo; i++)
187 : {
188 0 : m_pseudos[i].uid = be::read<uint32>(p);
189 0 : m_pseudos[i].gid = be::read<uint16>(p);
190 : }
191 :
192 0 : const size_t clen = readClassMap(p, passes_start + silf_start - p, version, e);
193 0 : m_passes = new Pass[m_numPasses];
194 0 : if (e || e.test(clen > unsigned(passes_start + silf_start - p), E_BADPASSESSTART)
195 0 : || e.test(!m_passes, E_OUTOFMEM))
196 0 : { releaseBuffers(); return face.error(e); }
197 :
198 0 : for (size_t i = 0; i < m_numPasses; ++i)
199 : {
200 0 : uint32 pass_start = be::read<uint32>(o_passes);
201 0 : uint32 pass_end = be::peek<uint32>(o_passes);
202 0 : face.error_context((face.error_context() & 0xFF00) + EC_ASILF + (i << 16));
203 0 : if (e.test(pass_start > pass_end, E_BADPASSSTART)
204 0 : || e.test(pass_start < passes_start, E_BADPASSSTART)
205 0 : || e.test(pass_end > lSilf, E_BADPASSEND)) {
206 0 : releaseBuffers(); return face.error(e);
207 : }
208 :
209 0 : enum passtype pt = PASS_TYPE_UNKNOWN;
210 0 : if (i >= m_jPass) pt = PASS_TYPE_JUSTIFICATION;
211 0 : else if (i >= m_pPass) pt = PASS_TYPE_POSITIONING;
212 0 : else if (i >= m_sPass) pt = PASS_TYPE_SUBSTITUTE;
213 0 : else pt = PASS_TYPE_LINEBREAK;
214 :
215 0 : m_passes[i].init(this);
216 0 : if (!m_passes[i].readPass(silf_start + pass_start, pass_end - pass_start, pass_start, face, pt,
217 : version, e))
218 : {
219 0 : releaseBuffers();
220 0 : return false;
221 : }
222 : }
223 :
224 : // fill in gr_faceinfo
225 0 : m_silfinfo.upem = face.glyphs().unitsPerEm();
226 0 : m_silfinfo.has_bidi_pass = (m_bPass != 0xFF);
227 0 : m_silfinfo.justifies = (m_numJusts != 0) || (m_jPass < m_pPass);
228 0 : m_silfinfo.line_ends = (m_flags & 1);
229 0 : m_silfinfo.space_contextuals = gr_faceinfo::gr_space_contextuals((m_flags >> 2) & 0x7);
230 0 : return true;
231 : }
232 :
233 0 : template<typename T> inline uint32 Silf::readClassOffsets(const byte *&p, size_t data_len, Error &e)
234 : {
235 0 : const T cls_off = 2*sizeof(uint16) + sizeof(T)*(m_nClass+1);
236 0 : const size_t 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 0 : if (e.test(be::peek<T>(p) != cls_off, E_MISALIGNEDCLASSES)
239 0 : || e.test(max_off > (data_len - cls_off)/sizeof(uint16), E_HIGHCLASSOFFSET))
240 0 : return ERROROFFSET;
241 :
242 : // Read in all the offsets.
243 0 : m_classOffsets = gralloc<uint32>(m_nClass+1);
244 0 : if (e.test(!m_classOffsets, E_OUTOFMEM)) return ERROROFFSET;
245 0 : for (uint32 * o = m_classOffsets, * const o_end = o + m_nClass + 1; o != o_end; ++o)
246 : {
247 0 : *o = (be::read<T>(p) - cls_off)/sizeof(uint16);
248 0 : if (e.test(*o > max_off, E_HIGHCLASSOFFSET))
249 0 : return ERROROFFSET;
250 : }
251 0 : return max_off;
252 : }
253 :
254 0 : size_t Silf::readClassMap(const byte *p, size_t data_len, uint32 version, Error &e)
255 : {
256 0 : if (e.test(data_len < sizeof(uint16)*2, E_BADCLASSSIZE)) return ERROROFFSET;
257 :
258 0 : m_nClass = be::read<uint16>(p);
259 0 : m_nLinear = be::read<uint16>(p);
260 :
261 : // Check that numLinear < numClass,
262 : // that there is at least enough data for numClasses offsets.
263 0 : if (e.test(m_nLinear > m_nClass, E_TOOMANYLINEAR)
264 0 : || e.test((m_nClass + 1) * (version >= 0x00040000 ? sizeof(uint32) : sizeof(uint16)) > (data_len - 4), E_CLASSESTOOBIG))
265 0 : return ERROROFFSET;
266 :
267 : uint32 max_off;
268 0 : if (version >= 0x00040000)
269 0 : max_off = readClassOffsets<uint32>(p, data_len, e);
270 : else
271 0 : max_off = readClassOffsets<uint16>(p, data_len, e);
272 :
273 0 : if (max_off == ERROROFFSET) return ERROROFFSET;
274 :
275 0 : if (e.test((int)max_off < m_nLinear + (m_nClass - m_nLinear) * 6, E_CLASSESTOOBIG))
276 0 : return ERROROFFSET;
277 :
278 : // Check the linear offsets are sane, these must be monotonically increasing.
279 0 : for (const uint32 *o = m_classOffsets, * const o_end = o + m_nLinear; o != o_end; ++o)
280 0 : if (e.test(o[0] > o[1], E_BADCLASSOFFSET))
281 0 : return ERROROFFSET;
282 :
283 : // Fortunately the class data is all uint16s so we can decode these now
284 0 : m_classData = gralloc<uint16>(max_off);
285 0 : if (e.test(!m_classData, E_OUTOFMEM)) return ERROROFFSET;
286 0 : for (uint16 *d = m_classData, * const d_end = d + max_off; d != d_end; ++d)
287 0 : *d = be::read<uint16>(p);
288 :
289 : // Check the lookup class invariants for each non-linear class
290 0 : for (const uint32 *o = m_classOffsets + m_nLinear, * const o_end = m_classOffsets + m_nClass; o != o_end; ++o)
291 : {
292 0 : const uint16 * lookup = m_classData + *o;
293 0 : if (e.test(*o + 4 > max_off, E_HIGHCLASSOFFSET) // LookupClass doesn't stretch over max_off
294 0 : || e.test(lookup[0] == 0 // A LookupClass with no looks is a suspicious thing ...
295 0 : || lookup[0] * 2 + *o + 4 > max_off // numIDs lookup pairs fits within (start of LookupClass' lookups array, max_off]
296 0 : || lookup[3] + lookup[1] != lookup[0], E_BADCLASSLOOKUPINFO) // rangeShift: numIDs - searchRange
297 0 : || e.test(((o[1] - *o) & 1) != 0, ERROROFFSET)) // glyphs are in pairs so difference must be even.
298 0 : return ERROROFFSET;
299 : }
300 :
301 0 : return max_off;
302 : }
303 :
304 0 : uint16 Silf::findPseudo(uint32 uid) const
305 : {
306 0 : for (int i = 0; i < m_numPseudo; i++)
307 0 : if (m_pseudos[i].uid == uid) return m_pseudos[i].gid;
308 0 : return 0;
309 : }
310 :
311 0 : uint16 Silf::findClassIndex(uint16 cid, uint16 gid) const
312 : {
313 0 : if (cid > m_nClass) return -1;
314 :
315 0 : const uint16 * cls = m_classData + m_classOffsets[cid];
316 0 : if (cid < m_nLinear) // output class being used for input, shouldn't happen
317 : {
318 0 : for (unsigned int i = 0, n = m_classOffsets[cid + 1] - m_classOffsets[cid]; i < n; ++i, ++cls)
319 0 : if (*cls == gid) return i;
320 0 : return -1;
321 : }
322 : else
323 : {
324 0 : const uint16 * min = cls + 4, // lookups array
325 0 : * max = min + cls[0]*2; // lookups aray is numIDs (cls[0]) uint16 pairs long
326 0 : do
327 : {
328 0 : const uint16 * p = min + (-2 & ((max-min)/2));
329 0 : if (p[0] > gid) max = p;
330 0 : else min = p;
331 : }
332 0 : while (max - min > 2);
333 0 : return min[0] == gid ? min[1] : -1;
334 : }
335 : }
336 :
337 0 : uint16 Silf::getClassGlyph(uint16 cid, unsigned int index) const
338 : {
339 0 : if (cid > m_nClass) return 0;
340 :
341 0 : uint32 loc = m_classOffsets[cid];
342 0 : if (cid < m_nLinear)
343 : {
344 0 : if (index < m_classOffsets[cid + 1] - loc)
345 0 : return m_classData[index + loc];
346 : }
347 : else // input class being used for output. Shouldn't happen
348 : {
349 0 : for (unsigned int i = loc + 4; i < m_classOffsets[cid + 1]; i += 2)
350 0 : if (m_classData[i + 1] == index) return m_classData[i];
351 : }
352 0 : return 0;
353 : }
354 :
355 :
356 0 : bool Silf::runGraphite(Segment *seg, uint8 firstPass, uint8 lastPass, int dobidi) const
357 : {
358 0 : assert(seg != 0);
359 0 : unsigned int maxSize = seg->slotCount() * MAX_SEG_GROWTH_FACTOR;
360 0 : SlotMap map(*seg, m_dir, maxSize);
361 0 : FiniteStateMachine fsm(map, seg->getFace()->logger());
362 0 : vm::Machine m(map);
363 0 : uint8 lbidi = m_bPass;
364 : #if !defined GRAPHITE2_NTRACING
365 : json * const dbgout = seg->getFace()->logger();
366 : #endif
367 :
368 0 : if (lastPass == 0)
369 : {
370 0 : if (firstPass == lastPass && lbidi == 0xFF)
371 0 : return true;
372 0 : lastPass = m_numPasses;
373 : }
374 0 : if ((firstPass < lbidi || (dobidi && firstPass == lbidi)) && (lastPass >= lbidi || (dobidi && lastPass + 1 == lbidi)))
375 0 : lastPass++;
376 : else
377 0 : lbidi = 0xFF;
378 :
379 0 : for (size_t i = firstPass; i < lastPass; ++i)
380 : {
381 : // bidi and mirroring
382 0 : if (i == lbidi)
383 : {
384 : #if !defined GRAPHITE2_NTRACING
385 : if (dbgout)
386 : {
387 : *dbgout << json::item << json::object
388 : // << "pindex" << i // for debugging
389 : << "id" << -1
390 : << "slotsdir" << (seg->currdir() ? "rtl" : "ltr")
391 : << "passdir" << (m_dir & 1 ? "rtl" : "ltr")
392 : << "slots" << json::array;
393 : seg->positionSlots(0, 0, 0, seg->currdir());
394 : for(Slot * s = seg->first(); s; s = s->next())
395 : *dbgout << dslot(seg, s);
396 : *dbgout << json::close
397 : << "rules" << json::array << json::close
398 : << json::close;
399 : }
400 : #endif
401 0 : if (seg->currdir() != (m_dir & 1))
402 0 : seg->reverseSlots();
403 0 : if (m_aMirror && (seg->dir() & 3) == 3)
404 0 : seg->doMirror(m_aMirror);
405 0 : --i;
406 0 : lbidi = lastPass;
407 0 : --lastPass;
408 0 : continue;
409 : }
410 :
411 : #if !defined GRAPHITE2_NTRACING
412 : if (dbgout)
413 : {
414 : *dbgout << json::item << json::object
415 : // << "pindex" << i // for debugging
416 : << "id" << i+1
417 : << "slotsdir" << (seg->currdir() ? "rtl" : "ltr")
418 : << "passdir" << ((m_dir & 1) ^ m_passes[i].reverseDir() ? "rtl" : "ltr")
419 : << "slots" << json::array;
420 : seg->positionSlots(0, 0, 0, seg->currdir());
421 : for(Slot * s = seg->first(); s; s = s->next())
422 : *dbgout << dslot(seg, s);
423 : *dbgout << json::close;
424 : }
425 : #endif
426 :
427 : // test whether to reorder, prepare for positioning
428 0 : bool reverse = (lbidi == 0xFF) && (seg->currdir() != ((m_dir & 1) ^ m_passes[i].reverseDir()));
429 0 : if ((i >= 32 || (seg->passBits() & (1 << i)) == 0 || m_passes[i].collisionLoops())
430 0 : && !m_passes[i].runGraphite(m, fsm, reverse))
431 0 : return false;
432 : // only subsitution passes can change segment length, cached subsegments are short for their text
433 0 : if (m.status() != vm::Machine::finished
434 0 : || (seg->slotCount() && seg->slotCount() > maxSize))
435 0 : return false;
436 : }
437 0 : return true;
438 : }
|