Silf.cpp (16478B)
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 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 72 Silf::~Silf() throw() 73 { 74 releaseBuffers(); 75 } 76 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 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 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 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 305 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 312 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 338 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 357 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 }