gfx/graphite2/src/Segment.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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 "inc/UtfCodec.h"
michael@0 28 #include <cstring>
michael@0 29 #include <cstdlib>
michael@0 30
michael@0 31 #include "inc/bits.h"
michael@0 32 #include "inc/Segment.h"
michael@0 33 #include "graphite2/Font.h"
michael@0 34 #include "inc/CharInfo.h"
michael@0 35 #include "inc/debug.h"
michael@0 36 #include "inc/Slot.h"
michael@0 37 #include "inc/Main.h"
michael@0 38 #include "inc/CmapCache.h"
michael@0 39 #include "inc/Bidi.h"
michael@0 40 #include "graphite2/Segment.h"
michael@0 41
michael@0 42
michael@0 43 using namespace graphite2;
michael@0 44
michael@0 45 Segment::Segment(unsigned int numchars, const Face* face, uint32 script, int textDir)
michael@0 46 : m_freeSlots(NULL),
michael@0 47 m_freeJustifies(NULL),
michael@0 48 m_charinfo(new CharInfo[numchars]),
michael@0 49 m_face(face),
michael@0 50 m_silf(face->chooseSilf(script)),
michael@0 51 m_first(NULL),
michael@0 52 m_last(NULL),
michael@0 53 m_bufSize(numchars + 10),
michael@0 54 m_numGlyphs(numchars),
michael@0 55 m_numCharinfo(numchars),
michael@0 56 m_passBits(m_silf->aPassBits() ? -1 : 0),
michael@0 57 m_defaultOriginal(0),
michael@0 58 m_dir(textDir)
michael@0 59 {
michael@0 60 freeSlot(newSlot());
michael@0 61 m_bufSize = log_binary(numchars)+1;
michael@0 62 }
michael@0 63
michael@0 64 Segment::~Segment()
michael@0 65 {
michael@0 66 for (SlotRope::iterator i = m_slots.begin(); i != m_slots.end(); ++i)
michael@0 67 free(*i);
michael@0 68 for (AttributeRope::iterator j = m_userAttrs.begin(); j != m_userAttrs.end(); ++j)
michael@0 69 free(*j);
michael@0 70 delete[] m_charinfo;
michael@0 71 }
michael@0 72
michael@0 73 #ifndef GRAPHITE2_NSEGCACHE
michael@0 74 SegmentScopeState Segment::setScope(Slot * firstSlot, Slot * lastSlot, size_t subLength)
michael@0 75 {
michael@0 76 SegmentScopeState state;
michael@0 77 state.numGlyphsOutsideScope = m_numGlyphs - subLength;
michael@0 78 state.realFirstSlot = m_first;
michael@0 79 state.slotBeforeScope = firstSlot->prev();
michael@0 80 state.slotAfterScope = lastSlot->next();
michael@0 81 state.realLastSlot = m_last;
michael@0 82 firstSlot->prev(NULL);
michael@0 83 lastSlot->next(NULL);
michael@0 84 assert(m_defaultOriginal == 0);
michael@0 85 m_defaultOriginal = firstSlot->original();
michael@0 86 m_numGlyphs = subLength;
michael@0 87 m_first = firstSlot;
michael@0 88 m_last = lastSlot;
michael@0 89 return state;
michael@0 90 }
michael@0 91
michael@0 92 void Segment::removeScope(SegmentScopeState & state)
michael@0 93 {
michael@0 94 m_numGlyphs = state.numGlyphsOutsideScope + m_numGlyphs;
michael@0 95 if (state.slotBeforeScope)
michael@0 96 {
michael@0 97 state.slotBeforeScope->next(m_first);
michael@0 98 m_first->prev(state.slotBeforeScope);
michael@0 99 m_first = state.realFirstSlot;
michael@0 100 }
michael@0 101 if (state.slotAfterScope)
michael@0 102 {
michael@0 103 state.slotAfterScope->prev(m_last);
michael@0 104 m_last->next(state.slotAfterScope);
michael@0 105 m_last = state.realLastSlot;
michael@0 106 }
michael@0 107 m_defaultOriginal = 0;
michael@0 108 }
michael@0 109
michael@0 110 #if 0
michael@0 111 void Segment::append(const Segment &other)
michael@0 112 {
michael@0 113 Rect bbox = other.m_bbox + m_advance;
michael@0 114
michael@0 115 m_slots.insert(m_slots.end(), other.m_slots.begin(), other.m_slots.end());
michael@0 116 CharInfo* pNewCharInfo = new CharInfo[m_numCharinfo+other.m_numCharinfo]; //since CharInfo has no constructor, this doesn't do much
michael@0 117 for (unsigned int i=0 ; i<m_numCharinfo ; ++i)
michael@0 118 pNewCharInfo[i] = m_charinfo[i];
michael@0 119 m_last->next(other.m_first);
michael@0 120 other.m_last->prev(m_last);
michael@0 121 m_userAttrs.insert(m_userAttrs.end(), other.m_userAttrs.begin(), other.m_userAttrs.end());
michael@0 122
michael@0 123 delete[] m_charinfo;
michael@0 124 m_charinfo = pNewCharInfo;
michael@0 125 pNewCharInfo += m_numCharinfo ;
michael@0 126 for (unsigned int i=0 ; i<m_numCharinfo ; ++i)
michael@0 127 pNewCharInfo[i] = other.m_charinfo[i];
michael@0 128
michael@0 129 m_numCharinfo += other.m_numCharinfo;
michael@0 130 m_numGlyphs += other.m_numGlyphs;
michael@0 131 m_advance = m_advance + other.m_advance;
michael@0 132 m_bbox = m_bbox.widen(bbox);
michael@0 133 m_passBits &= other.passBits();
michael@0 134 }
michael@0 135 #endif
michael@0 136 #endif // GRAPHITE2_NSEGCACHE
michael@0 137
michael@0 138 void Segment::appendSlot(int id, int cid, int gid, int iFeats, size_t coffset)
michael@0 139 {
michael@0 140 Slot *aSlot = newSlot();
michael@0 141
michael@0 142 if (!aSlot) return;
michael@0 143 m_charinfo[id].init(cid);
michael@0 144 m_charinfo[id].feats(iFeats);
michael@0 145 m_charinfo[id].base(coffset);
michael@0 146 const GlyphFace * theGlyph = m_face->glyphs().glyphSafe(gid);
michael@0 147 m_charinfo[id].breakWeight(theGlyph ? theGlyph->attrs()[m_silf->aBreak()] : 0);
michael@0 148
michael@0 149 aSlot->child(NULL);
michael@0 150 aSlot->setGlyph(this, gid, theGlyph);
michael@0 151 aSlot->originate(id);
michael@0 152 aSlot->before(id);
michael@0 153 aSlot->after(id);
michael@0 154 if (m_last) m_last->next(aSlot);
michael@0 155 aSlot->prev(m_last);
michael@0 156 m_last = aSlot;
michael@0 157 if (!m_first) m_first = aSlot;
michael@0 158 if (theGlyph && m_silf->aPassBits())
michael@0 159 m_passBits &= theGlyph->attrs()[m_silf->aPassBits()]
michael@0 160 | (m_silf->numPasses() > 16 ? (theGlyph->attrs()[m_silf->aPassBits() + 1] << 16) : 0);
michael@0 161 }
michael@0 162
michael@0 163 Slot *Segment::newSlot()
michael@0 164 {
michael@0 165 if (!m_freeSlots)
michael@0 166 {
michael@0 167 int numUser = m_silf->numUser();
michael@0 168 #if !defined GRAPHITE2_NTRACING
michael@0 169 if (m_face->logger()) ++numUser;
michael@0 170 #endif
michael@0 171 Slot *newSlots = grzeroalloc<Slot>(m_bufSize);
michael@0 172 int16 *newAttrs = grzeroalloc<int16>(numUser * m_bufSize);
michael@0 173 if (!newSlots || !newAttrs) return NULL;
michael@0 174 for (size_t i = 0; i < m_bufSize; i++)
michael@0 175 {
michael@0 176 newSlots[i].next(newSlots + i + 1);
michael@0 177 newSlots[i].userAttrs(newAttrs + i * numUser);
michael@0 178 newSlots[i].setBidiClass(-1);
michael@0 179 }
michael@0 180 newSlots[m_bufSize - 1].next(NULL);
michael@0 181 newSlots[0].next(NULL);
michael@0 182 m_slots.push_back(newSlots);
michael@0 183 m_userAttrs.push_back(newAttrs);
michael@0 184 m_freeSlots = (m_bufSize > 1)? newSlots + 1 : NULL;
michael@0 185 return newSlots;
michael@0 186 }
michael@0 187 Slot *res = m_freeSlots;
michael@0 188 m_freeSlots = m_freeSlots->next();
michael@0 189 res->next(NULL);
michael@0 190 return res;
michael@0 191 }
michael@0 192
michael@0 193 void Segment::freeSlot(Slot *aSlot)
michael@0 194 {
michael@0 195 if (m_last == aSlot) m_last = aSlot->prev();
michael@0 196 if (m_first == aSlot) m_first = aSlot->next();
michael@0 197 if (aSlot->attachedTo())
michael@0 198 aSlot->attachedTo()->removeChild(aSlot);
michael@0 199 while (aSlot->firstChild())
michael@0 200 {
michael@0 201 aSlot->firstChild()->attachTo(NULL);
michael@0 202 aSlot->removeChild(aSlot->firstChild());
michael@0 203 }
michael@0 204 // reset the slot incase it is reused
michael@0 205 ::new (aSlot) Slot;
michael@0 206 memset(aSlot->userAttrs(), 0, m_silf->numUser() * sizeof(int16));
michael@0 207 // Update generation counter for debug
michael@0 208 #if !defined GRAPHITE2_NTRACING
michael@0 209 if (m_face->logger())
michael@0 210 ++aSlot->userAttrs()[m_silf->numUser()];
michael@0 211 #endif
michael@0 212 // update next pointer
michael@0 213 if (!m_freeSlots)
michael@0 214 aSlot->next(NULL);
michael@0 215 else
michael@0 216 aSlot->next(m_freeSlots);
michael@0 217 m_freeSlots = aSlot;
michael@0 218 }
michael@0 219
michael@0 220 SlotJustify *Segment::newJustify()
michael@0 221 {
michael@0 222 if (!m_freeJustifies)
michael@0 223 {
michael@0 224 const size_t justSize = SlotJustify::size_of(m_silf->numJustLevels());
michael@0 225 byte *justs = grzeroalloc<byte>(justSize * m_bufSize);
michael@0 226 if (!justs) return NULL;
michael@0 227 for (int i = m_bufSize - 2; i >= 0; --i)
michael@0 228 {
michael@0 229 SlotJustify *p = reinterpret_cast<SlotJustify *>(justs + justSize * i);
michael@0 230 SlotJustify *next = reinterpret_cast<SlotJustify *>(justs + justSize * (i + 1));
michael@0 231 p->next = next;
michael@0 232 }
michael@0 233 m_freeJustifies = (SlotJustify *)justs;
michael@0 234 m_justifies.push_back(m_freeJustifies);
michael@0 235 }
michael@0 236 SlotJustify *res = m_freeJustifies;
michael@0 237 m_freeJustifies = m_freeJustifies->next;
michael@0 238 res->next = NULL;
michael@0 239 return res;
michael@0 240 }
michael@0 241
michael@0 242 void Segment::freeJustify(SlotJustify *aJustify)
michael@0 243 {
michael@0 244 int numJust = m_silf->numJustLevels();
michael@0 245 if (m_silf->numJustLevels() <= 0) numJust = 1;
michael@0 246 aJustify->next = m_freeJustifies;
michael@0 247 memset(aJustify->values, 0, numJust*SlotJustify::NUMJUSTPARAMS*sizeof(int16));
michael@0 248 m_freeJustifies = aJustify;
michael@0 249 }
michael@0 250
michael@0 251 #ifndef GRAPHITE2_NSEGCACHE
michael@0 252 void Segment::splice(size_t offset, size_t length, Slot * const startSlot,
michael@0 253 Slot * endSlot, const Slot * srcSlot,
michael@0 254 const size_t numGlyphs)
michael@0 255 {
michael@0 256 size_t numChars = length;
michael@0 257 extendLength(numGlyphs - length);
michael@0 258 // remove any extra
michael@0 259 if (numGlyphs < length)
michael@0 260 {
michael@0 261 Slot * end = endSlot->next();
michael@0 262 do
michael@0 263 {
michael@0 264 endSlot = endSlot->prev();
michael@0 265 freeSlot(endSlot->next());
michael@0 266 } while (numGlyphs < --length);
michael@0 267 endSlot->next(end);
michael@0 268 if (end)
michael@0 269 end->prev(endSlot);
michael@0 270 }
michael@0 271 else
michael@0 272 {
michael@0 273 // insert extra slots if needed
michael@0 274 while (numGlyphs > length)
michael@0 275 {
michael@0 276 Slot * extra = newSlot();
michael@0 277 if (!extra) return;
michael@0 278 extra->prev(endSlot);
michael@0 279 extra->next(endSlot->next());
michael@0 280 endSlot->next(extra);
michael@0 281 if (extra->next())
michael@0 282 extra->next()->prev(extra);
michael@0 283 if (m_last == endSlot)
michael@0 284 m_last = extra;
michael@0 285 endSlot = extra;
michael@0 286 ++length;
michael@0 287 }
michael@0 288 }
michael@0 289
michael@0 290 endSlot = endSlot->next();
michael@0 291 assert(numGlyphs == length);
michael@0 292 assert(offset + numChars <= m_numCharinfo);
michael@0 293 Slot * indexmap[eMaxSpliceSize*3];
michael@0 294 assert(numGlyphs < sizeof indexmap/sizeof *indexmap);
michael@0 295 Slot * slot = startSlot;
michael@0 296 for (uint16 i=0; i < numGlyphs; slot = slot->next(), ++i)
michael@0 297 indexmap[i] = slot;
michael@0 298
michael@0 299 for (slot = startSlot; slot != endSlot; slot = slot->next(), srcSlot = srcSlot->next())
michael@0 300 {
michael@0 301 slot->set(*srcSlot, offset, m_silf->numUser(), m_silf->numJustLevels(), numChars);
michael@0 302 if (srcSlot->attachedTo()) slot->attachTo(indexmap[srcSlot->attachedTo()->index()]);
michael@0 303 if (srcSlot->nextSibling()) slot->m_sibling = indexmap[srcSlot->nextSibling()->index()];
michael@0 304 if (srcSlot->firstChild()) slot->m_child = indexmap[srcSlot->firstChild()->index()];
michael@0 305 }
michael@0 306 }
michael@0 307 #endif // GRAPHITE2_NSEGCACHE
michael@0 308
michael@0 309 void Segment::linkClusters(Slot *s, Slot * end)
michael@0 310 {
michael@0 311 end = end->next();
michael@0 312
michael@0 313 for (; s != end && !s->isBase(); s = s->next());
michael@0 314 Slot * ls = s;
michael@0 315
michael@0 316 if (m_dir & 1)
michael@0 317 {
michael@0 318 for (; s != end; s = s->next())
michael@0 319 {
michael@0 320 if (!s->isBase()) continue;
michael@0 321
michael@0 322 s->sibling(ls);
michael@0 323 ls = s;
michael@0 324 }
michael@0 325 }
michael@0 326 else
michael@0 327 {
michael@0 328 for (; s != end; s = s->next())
michael@0 329 {
michael@0 330 if (!s->isBase()) continue;
michael@0 331
michael@0 332 ls->sibling(s);
michael@0 333 ls = s;
michael@0 334 }
michael@0 335 }
michael@0 336 }
michael@0 337
michael@0 338 Position Segment::positionSlots(const Font *font, Slot * iStart, Slot * iEnd)
michael@0 339 {
michael@0 340 Position currpos(0., 0.);
michael@0 341 float clusterMin = 0.;
michael@0 342 Rect bbox;
michael@0 343
michael@0 344 if (!iStart) iStart = m_first;
michael@0 345 if (!iEnd) iEnd = m_last;
michael@0 346
michael@0 347 if (m_dir & 1)
michael@0 348 {
michael@0 349 for (Slot * s = iEnd, * const end = iStart->prev(); s && s != end; s = s->prev())
michael@0 350 {
michael@0 351 if (s->isBase())
michael@0 352 currpos = s->finalise(this, font, currpos, bbox, 0, clusterMin = currpos.x);
michael@0 353 }
michael@0 354 }
michael@0 355 else
michael@0 356 {
michael@0 357 for (Slot * s = iStart, * const end = iEnd->next(); s && s != end; s = s->next())
michael@0 358 {
michael@0 359 if (s->isBase())
michael@0 360 currpos = s->finalise(this, font, currpos, bbox, 0, clusterMin = currpos.x);
michael@0 361 }
michael@0 362 }
michael@0 363 return currpos;
michael@0 364 }
michael@0 365
michael@0 366
michael@0 367 void Segment::associateChars(int offset, int numChars)
michael@0 368 {
michael@0 369 int i = 0, j = 0;
michael@0 370 CharInfo *c, *cend;
michael@0 371 for (c = m_charinfo + offset, cend = m_charinfo + offset + numChars; c != cend; ++c)
michael@0 372 {
michael@0 373 c->before(-1);
michael@0 374 c->after(-1);
michael@0 375 }
michael@0 376 for (Slot * s = m_first; s; s->index(i++), s = s->next())
michael@0 377 {
michael@0 378 j = s->before();
michael@0 379 if (j < 0) continue;
michael@0 380
michael@0 381 for (const int after = s->after(); j <= after; ++j)
michael@0 382 {
michael@0 383 c = charinfo(j);
michael@0 384 if (c->before() == -1 || i < c->before()) c->before(i);
michael@0 385 if (c->after() < i) c->after(i);
michael@0 386 }
michael@0 387 }
michael@0 388 for (Slot *s = m_first; s; s = s->next())
michael@0 389 {
michael@0 390 int a;
michael@0 391 for (a = s->after() + 1; a < offset + numChars && charinfo(a)->after() < 0; ++a)
michael@0 392 { charinfo(a)->after(s->index()); }
michael@0 393 --a;
michael@0 394 s->after(a);
michael@0 395
michael@0 396 for (a = s->before() - 1; a >= offset && charinfo(a)->before() < 0; --a)
michael@0 397 { charinfo(a)->before(s->index()); }
michael@0 398 ++a;
michael@0 399 s->before(a);
michael@0 400 }
michael@0 401 }
michael@0 402
michael@0 403
michael@0 404 template <typename utf_iter>
michael@0 405 inline void process_utf_data(Segment & seg, const Face & face, const int fid, utf_iter c, size_t n_chars)
michael@0 406 {
michael@0 407 const Cmap & cmap = face.cmap();
michael@0 408 int slotid = 0;
michael@0 409
michael@0 410 const typename utf_iter::codeunit_type * const base = c;
michael@0 411 for (; n_chars; --n_chars, ++c, ++slotid)
michael@0 412 {
michael@0 413 const uint32 usv = *c;
michael@0 414 uint16 gid = cmap[usv];
michael@0 415 if (!gid) gid = face.findPseudo(usv);
michael@0 416 seg.appendSlot(slotid, usv, gid, fid, c - base);
michael@0 417 }
michael@0 418 }
michael@0 419
michael@0 420
michael@0 421 bool Segment::read_text(const Face *face, const Features* pFeats/*must not be NULL*/, gr_encform enc, const void* pStart, size_t nChars)
michael@0 422 {
michael@0 423 assert(face);
michael@0 424 assert(pFeats);
michael@0 425 if (!m_charinfo) return false;
michael@0 426
michael@0 427 // utf iterator is self recovering so we don't care about the error state of the iterator.
michael@0 428 switch (enc)
michael@0 429 {
michael@0 430 case gr_utf8: process_utf_data(*this, *face, addFeatures(*pFeats), utf8::const_iterator(pStart), nChars); break;
michael@0 431 case gr_utf16: process_utf_data(*this, *face, addFeatures(*pFeats), utf16::const_iterator(pStart), nChars); break;
michael@0 432 case gr_utf32: process_utf_data(*this, *face, addFeatures(*pFeats), utf32::const_iterator(pStart), nChars); break;
michael@0 433 }
michael@0 434 return true;
michael@0 435 }
michael@0 436
michael@0 437 void Segment::prepare_pos(const Font * /*font*/)
michael@0 438 {
michael@0 439 // copy key changeable metrics into slot (if any);
michael@0 440 }
michael@0 441
michael@0 442 Slot *process_bidi(Slot *start, int level, int prelevel, int &nextLevel, int dirover, int isol, int &cisol, int &isolerr, int &embederr, int init, Segment *seg, uint8 aMirror, BracketPairStack &stack);
michael@0 443 void resolveImplicit(Slot *s, Segment *seg, uint8 aMirror);
michael@0 444 void resolveWhitespace(int baseLevel, Slot *s);
michael@0 445 Slot *resolveOrder(Slot * & s, const bool reordered, const int level = 0);
michael@0 446
michael@0 447 void Segment::bidiPass(uint8 aBidi, int paradir, uint8 aMirror)
michael@0 448 {
michael@0 449 if (slotCount() == 0)
michael@0 450 return;
michael@0 451
michael@0 452 Slot *s;
michael@0 453 int baseLevel = paradir ? 1 : 0;
michael@0 454 unsigned int bmask = 0;
michael@0 455 unsigned int ssize = 0;
michael@0 456 for (s = first(); s; s = s->next())
michael@0 457 {
michael@0 458 if (s->getBidiClass() == -1)
michael@0 459 {
michael@0 460 unsigned int bAttr = glyphAttr(s->gid(), aBidi);
michael@0 461 s->setBidiClass((bAttr <= 22) * bAttr);
michael@0 462 }
michael@0 463 bmask |= (1 << s->getBidiClass());
michael@0 464 s->setBidiLevel(baseLevel);
michael@0 465 if (glyphAttr(s->gid(), aMirror) && s->getBidiClass() == 21)
michael@0 466 ++ssize;
michael@0 467 }
michael@0 468
michael@0 469 BracketPairStack bstack(ssize);
michael@0 470 if (bmask & (paradir ? 0x2E7892 : 0x2E789C))
michael@0 471 {
michael@0 472 // O(8N) algorithm, with no working data beyond what is needed for processParens
michael@0 473 int nextLevel = paradir;
michael@0 474 int e, i, c;
michael@0 475 process_bidi(first(), baseLevel, paradir, nextLevel, 0, 0, c = 0, i = 0, e = 0, 1, this, aMirror, bstack);
michael@0 476 resolveImplicit(first(), this, aMirror);
michael@0 477 resolveWhitespace(baseLevel, last());
michael@0 478 s = resolveOrder(s = first(), baseLevel != 0);
michael@0 479 if (s)
michael@0 480 {
michael@0 481 first(s); last(s->prev());
michael@0 482 s->prev()->next(0); s->prev(0);
michael@0 483 }
michael@0 484 }
michael@0 485 else if (!(dir() & 4) && baseLevel && aMirror)
michael@0 486 {
michael@0 487 for (s = first(); s; s = s->next())
michael@0 488 {
michael@0 489 unsigned short g = glyphAttr(s->gid(), aMirror);
michael@0 490 if (g) s->setGlyph(this, g);
michael@0 491 }
michael@0 492 }
michael@0 493 }
michael@0 494

mercurial