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 :
35 :
36 : using namespace graphite2;
37 :
38 :
39 0 : Silf::Silf() throw()
40 : : m_passes(0), m_pseudos(0), m_classOffsets(0), m_classData(0), m_justs(0),
41 : m_numPasses(0), m_sPass(0), m_pPass(0), m_jPass(0), m_bPass(0), m_flags(0),
42 : m_aBreak(0), m_aUser(0), m_iMaxComp(0),
43 0 : m_aLig(0), m_numPseudo(0), m_nClass(0), m_nLinear(0)
44 : {
45 0 : }
46 :
47 0 : Silf::~Silf() throw()
48 : {
49 0 : releaseBuffers();
50 0 : }
51 :
52 0 : void Silf::releaseBuffers() throw()
53 : {
54 0 : delete [] m_passes;
55 0 : delete [] m_pseudos;
56 0 : free(m_classOffsets);
57 0 : free(m_classData);
58 0 : free(m_justs);
59 0 : m_passes= 0;
60 0 : m_pseudos = 0;
61 0 : m_classOffsets = 0;
62 0 : m_classData = 0;
63 0 : m_justs = 0;
64 0 : }
65 :
66 :
67 0 : bool Silf::readGraphite(const void* pSilf, size_t lSilf, const Face& face, uint32 version)
68 : {
69 0 : const byte * p = (byte *)pSilf,
70 0 : * const eSilf = p + lSilf;
71 :
72 0 : if (version >= 0x00030000)
73 : {
74 0 : if (lSilf < 27) { releaseBuffers(); return false; }
75 0 : p += 8;
76 : }
77 0 : else if (lSilf < 19) { releaseBuffers(); return false; }
78 0 : p += 2; // maxGlyphID
79 0 : p += 4; // extra ascent/descent
80 0 : m_numPasses = uint8(*p++);
81 0 : if (m_numPasses > 128)
82 0 : return false;
83 0 : m_passes = new Pass[m_numPasses];
84 0 : m_sPass = uint8(*p++);
85 0 : m_pPass = uint8(*p++);
86 0 : if (m_pPass < m_sPass) {
87 0 : releaseBuffers();
88 0 : return false;
89 : }
90 0 : m_jPass = uint8(*p++);
91 0 : if (m_jPass < m_pPass) {
92 0 : releaseBuffers();
93 0 : return false;
94 : }
95 0 : m_bPass = uint8(*p++); // when do we reorder?
96 0 : if (m_bPass != 0xFF && (m_bPass < m_jPass || m_bPass > m_numPasses)) {
97 0 : releaseBuffers();
98 0 : return false;
99 : }
100 0 : m_flags = uint8(*p++);
101 0 : p += 2; // ignore line end contextuals for now
102 0 : m_aPseudo = uint8(*p++);
103 0 : m_aBreak = uint8(*p++);
104 0 : m_aBidi = uint8(*p++);
105 0 : m_aMirror = uint8(*p++);
106 0 : p += 1; // skip reserved stuff
107 0 : m_numJusts = uint8(*p++);
108 0 : m_justs = gralloc<Justinfo>(m_numJusts);
109 0 : for (uint8 i = 0; i < m_numJusts; i++)
110 : {
111 0 : ::new(m_justs + i) Justinfo(p[0], p[1], p[2], p[3]);
112 0 : p += 8;
113 : }
114 : // p += uint8(*p) * 8 + 1; // ignore justification for now
115 0 : if (p + 9 >= eSilf) { releaseBuffers(); return false; }
116 0 : m_aLig = be::read<uint16>(p);
117 0 : if (m_aLig > 127) {
118 0 : releaseBuffers();
119 0 : return false;
120 : }
121 0 : m_aUser = uint8(*p++);
122 0 : m_iMaxComp = uint8(*p++);
123 0 : p += 5; // skip direction and reserved
124 0 : p += uint8(*p) * 2 + 1; // don't need critical features yet
125 0 : p++; // reserved
126 0 : if (p >= eSilf)
127 : {
128 0 : releaseBuffers();
129 0 : return false;
130 : }
131 0 : p += uint8(*p) * 4 + 1; // skip scripts
132 0 : p += 2; // skip lbGID
133 :
134 0 : if (p + 4 * (m_numPasses + 1) + 6 >= eSilf)
135 : {
136 0 : releaseBuffers();
137 0 : return false;
138 : }
139 0 : const byte * pPasses = p;
140 0 : p += 4 * (m_numPasses + 1);
141 0 : m_numPseudo = be::read<uint16>(p);
142 0 : p += 6;
143 0 : if (p + m_numPseudo * 6 >= eSilf)
144 : {
145 0 : releaseBuffers();
146 0 : return false;
147 : }
148 0 : m_pseudos = new Pseudo[m_numPseudo];
149 0 : for (int i = 0; i < m_numPseudo; i++)
150 : {
151 0 : m_pseudos[i].uid = be::read<uint32>(p);
152 0 : m_pseudos[i].gid = be::read<uint16>(p);
153 : }
154 0 : if (p >= eSilf)
155 : {
156 0 : releaseBuffers();
157 0 : return false;
158 : }
159 :
160 0 : int clen = readClassMap(p, be::peek<uint32>(pPasses) - (p - (byte *)pSilf), version);
161 0 : if (clen < 0) {
162 0 : releaseBuffers();
163 0 : return false;
164 : }
165 0 : p += clen;
166 :
167 0 : for (size_t i = 0; i < m_numPasses; ++i)
168 : {
169 0 : uint32 pOffset = be::read<uint32>(pPasses);
170 0 : uint32 pEnd = be::peek<uint32>(pPasses);
171 0 : if ((uint8 *)pSilf + pEnd > eSilf || pOffset > pEnd)
172 : {
173 0 : releaseBuffers();
174 0 : return false;
175 : }
176 0 : m_passes[i].init(this);
177 0 : if (!m_passes[i].readPass((char *)pSilf + pOffset, pEnd - pOffset, pOffset, face))
178 : {
179 0 : releaseBuffers();
180 0 : return false;
181 : }
182 : }
183 0 : return true;
184 : }
185 :
186 0 : template<typename T> inline uint32 Silf::readClassOffsets(const byte *&p, size_t data_len)
187 : {
188 0 : const T cls_off = 2*sizeof(uint16) + sizeof(T)*(m_nClass+1);
189 0 : const uint32 max_off = (be::peek<T>(p + sizeof(T)*m_nClass) - cls_off)/sizeof(uint16);
190 : // Check that the last+1 offset is less than or equal to the class map length.
191 0 : if (be::peek<T>(p) != cls_off || max_off > (data_len - cls_off)/sizeof(uint16))
192 0 : return 0;
193 :
194 : // Read in all the offsets.
195 0 : m_classOffsets = gralloc<uint32>(m_nClass+1);
196 0 : for (uint32 * o = m_classOffsets, * const o_end = o + m_nClass + 1; o != o_end; ++o)
197 : {
198 0 : *o = (be::read<T>(p) - cls_off)/sizeof(uint16);
199 0 : if (*o > max_off)
200 0 : return 0;
201 : }
202 0 : return max_off;
203 : }
204 :
205 0 : size_t Silf::readClassMap(const byte *p, size_t data_len, uint32 version)
206 : {
207 0 : if (data_len < sizeof(uint16)*2) return -1;
208 :
209 0 : m_nClass = be::read<uint16>(p);
210 0 : m_nLinear = be::read<uint16>(p);
211 :
212 : // Check that numLinear < numClass,
213 : // that there is at least enough data for numClasses offsets.
214 0 : if (m_nLinear > m_nClass
215 : || (m_nClass + 1) * (version >= 0x00040000 ? sizeof(uint32) : sizeof(uint16))> (data_len - 4))
216 0 : return -1;
217 :
218 :
219 : uint32 max_off;
220 0 : if (version >= 0x00040000)
221 0 : max_off = readClassOffsets<uint32>(p, data_len);
222 : else
223 0 : max_off = readClassOffsets<uint16>(p, data_len);
224 :
225 0 : if (max_off == 0) return -1;
226 :
227 : // Check the linear offsets are sane, these must be monotonically increasing.
228 0 : for (const uint32 *o = m_classOffsets, * const o_end = o + m_nLinear; o != o_end; ++o)
229 0 : if (o[0] > o[1])
230 0 : return -1;
231 :
232 : // Fortunately the class data is all uint16s so we can decode these now
233 0 : m_classData = gralloc<uint16>(max_off);
234 0 : for (uint16 *d = m_classData, * const d_end = d + max_off; d != d_end; ++d)
235 0 : *d = be::read<uint16>(p);
236 :
237 : // Check the lookup class invariants for each non-linear class
238 0 : for (const uint32 *o = m_classOffsets + m_nLinear, * const o_end = m_classOffsets + m_nClass; o != o_end; ++o)
239 : {
240 0 : const uint16 * lookup = m_classData + *o;
241 0 : if (lookup[0] == 0 // A LookupClass with no looks is a suspicious thing ...
242 0 : || lookup[0] > (max_off - *o - 4)/2 // numIDs lookup pairs fits within (start of LookupClass' lookups array, max_off]
243 0 : || lookup[3] != lookup[0] - lookup[1]) // rangeShift: numIDs - searchRange
244 0 : return -1;
245 : }
246 :
247 0 : return max_off;
248 : }
249 :
250 0 : uint16 Silf::findPseudo(uint32 uid) const
251 : {
252 0 : for (int i = 0; i < m_numPseudo; i++)
253 0 : if (m_pseudos[i].uid == uid) return m_pseudos[i].gid;
254 0 : return 0;
255 : }
256 :
257 0 : uint16 Silf::findClassIndex(uint16 cid, uint16 gid) const
258 : {
259 0 : if (cid > m_nClass) return -1;
260 :
261 0 : const uint16 * cls = m_classData + m_classOffsets[cid];
262 0 : if (cid < m_nLinear) // output class being used for input, shouldn't happen
263 : {
264 0 : for (unsigned int i = 0, n = m_classOffsets[cid + 1]; i < n; ++i, ++cls)
265 0 : if (*cls == gid) return i;
266 0 : return -1;
267 : }
268 : else
269 : {
270 0 : const uint16 * min = cls + 4, // lookups array
271 0 : * max = min + cls[0]*2; // lookups aray is numIDs (cls[0]) uint16 pairs long
272 0 : do
273 : {
274 0 : const uint16 * p = min + (-2U & ((max-min)/2));
275 0 : if (p[0] > gid) max = p;
276 0 : else min = p;
277 : }
278 : while (max - min > 2);
279 0 : return min[0] == gid ? min[1] : -1;
280 : }
281 : }
282 :
283 0 : uint16 Silf::getClassGlyph(uint16 cid, unsigned int index) const
284 : {
285 0 : if (cid > m_nClass) return 0;
286 :
287 0 : uint32 loc = m_classOffsets[cid];
288 0 : if (cid < m_nLinear)
289 : {
290 0 : if (index < m_classOffsets[cid + 1] - loc)
291 0 : return m_classData[index + loc];
292 : }
293 : else // input class being used for output. Shouldn't happen
294 : {
295 0 : for (unsigned int i = loc + 4; i < m_classOffsets[cid + 1]; i += 2)
296 0 : if (m_classData[i + 1] == index) return m_classData[i];
297 : }
298 0 : return 0;
299 : }
300 :
301 :
302 0 : bool Silf::runGraphite(Segment *seg, uint8 firstPass, uint8 lastPass) const
303 : {
304 0 : assert(seg != 0);
305 0 : SlotMap map(*seg);
306 0 : FiniteStateMachine fsm(map);
307 0 : vm::Machine m(map);
308 0 : unsigned int initSize = seg->slotCount();
309 :
310 0 : if (lastPass == 0)
311 : {
312 0 : if (firstPass == lastPass)
313 0 : return true;
314 0 : lastPass = m_numPasses;
315 : }
316 :
317 : #if !defined GRAPHITE2_NTRACING
318 : if (dbgout)
319 : {
320 : char version[64];
321 : sprintf(version, "%d.%d.%d",
322 : GR2_VERSION_MAJOR, GR2_VERSION_MINOR, GR2_VERSION_BUGFIX);
323 : *dbgout << json::object
324 : << "version" << version
325 : << "passes" << json::array;
326 : }
327 : #endif
328 :
329 0 : for (size_t i = firstPass; i < lastPass; ++i)
330 : {
331 : // bidi and mirroring
332 0 : if (i == m_bPass)
333 : {
334 : #if !defined GRAPHITE2_NTRACING
335 : if (dbgout)
336 : {
337 : *dbgout << json::item << json::object
338 : << "id" << -1
339 : << "slots" << json::array;
340 : seg->positionSlots(0);
341 : for(Slot * s = seg->first(); s; s = s->next())
342 : *dbgout << dslot(seg, s);
343 : *dbgout << json::close
344 : << "rules" << json::array << json::close
345 : << json::close;
346 : }
347 : #endif
348 :
349 0 : if (!(seg->dir() & 2))
350 0 : seg->bidiPass(m_aBidi, seg->dir() & 1, m_aMirror);
351 0 : else if (m_aMirror)
352 : {
353 : Slot * s;
354 0 : for (s = seg->first(); s; s = s->next())
355 : {
356 0 : unsigned short g = seg->glyphAttr(s->gid(), m_aMirror);
357 0 : if (g && (!(seg->dir() & 4) || !seg->glyphAttr(s->gid(), m_aMirror + 1)))
358 0 : s->setGlyph(seg, g);
359 : }
360 : }
361 : }
362 :
363 : #if !defined GRAPHITE2_NTRACING
364 : if (dbgout)
365 : {
366 : *dbgout << json::item << json::object
367 : << "id" << i+1
368 : << "slots" << json::array;
369 : seg->positionSlots(0);
370 : for(Slot * s = seg->first(); s; s = s->next())
371 : *dbgout << dslot(seg, s);
372 : *dbgout << json::close;
373 : }
374 : #endif
375 :
376 : // test whether to reorder, prepare for positioning
377 0 : m_passes[i].runGraphite(m, fsm);
378 : // only subsitution passes can change segment length, cached subsegments are short for their text
379 0 : if (m.status() != vm::Machine::finished
380 0 : || (i < m_pPass && (seg->slotCount() > initSize * MAX_SEG_GROWTH_FACTOR
381 0 : || (seg->slotCount() && seg->slotCount() * MAX_SEG_GROWTH_FACTOR < initSize))))
382 0 : return false;
383 : }
384 : #if !defined GRAPHITE2_NTRACING
385 : if (dbgout)
386 : {
387 : *dbgout << json::item
388 : << json::close // Close up the passes array
389 : << "output" << json::array;
390 : for(Slot * s = seg->first(); s; s = s->next())
391 : *dbgout << dslot(seg, s);
392 : seg->finalise(0); // Call this here to fix up charinfo back indexes.
393 : *dbgout << json::close
394 : << "advance" << seg->advance()
395 : << "chars" << json::array;
396 : for(size_t i = 0, n = seg->charInfoCount(); i != n; ++i)
397 : *dbgout << json::flat << *seg->charinfo(i);
398 : *dbgout << json::close // Close up the chars array
399 : << json::close; // Clsoe up the segment object
400 : }
401 : #endif
402 0 : return true;
403 : }
|