gfx/graphite2/src/Silf.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial