void Pass::runGraphite(Machine & m, FiniteStateMachine & fsm) const { Slot *s = m.slotMap().segment.first(); if (!s || !testPassConstraint(m)) return; Slot *currHigh = s->next(); #if !defined GRAPHITE2_NTRACING if (fsm.dbgout) *fsm.dbgout << "rules" << json::array; json::closer rules_array_closer(fsm.dbgout); #endif m.slotMap().highwater(currHigh); int lc = m_iMaxLoop; do { findNDoRule(s, m, fsm); if (s && (m.slotMap().highpassed() || s == m.slotMap().highwater() || --lc == 0)) { if (!lc) { // if (dbgout) *dbgout << json::item << json::flat << rule_event(-1, s, 1); s = m.slotMap().highwater(); } lc = m_iMaxLoop; if (s) m.slotMap().highwater(s->next()); } } while (s); }
void Segment::associateChars(int offset, int numChars) { int i = 0, j = 0; CharInfo *c, *cend; for (c = m_charinfo + offset, cend = m_charinfo + offset + numChars; c != cend; ++c) { c->before(-1); c->after(-1); } for (Slot * s = m_first; s; s->index(i++), s = s->next()) { j = s->before(); if (j < 0) continue; for (const int after = s->after(); j <= after; ++j) { c = charinfo(j); if (c->before() == -1 || i < c->before()) c->before(i); if (c->after() < i) c->after(i); } } for (Slot *s = m_first; s; s = s->next()) { int a; for (a = s->after() + 1; a < offset + numChars && charinfo(a)->after() < 0; ++a) { charinfo(a)->after(s->index()); } --a; s->after(a); for (a = s->before() - 1; a >= offset && charinfo(a)->before() < 0; --a) { charinfo(a)->before(s->index()); } ++a; s->before(a); } }
inline Slot * input_slot(const SlotMap & slots, const int n) { Slot * s = slots[slots.context() + n]; if (!s->isCopied()) return s; return s->prev() ? s->prev()->next() : (s->next() ? s->next()->prev() : slots.segment.last()); }
void Segment::splice(size_t offset, size_t length, Slot * const startSlot, Slot * endSlot, const Slot * srcSlot, const size_t numGlyphs) { size_t numChars = length; extendLength(numGlyphs - length); // remove any extra if (numGlyphs < length) { Slot * end = endSlot->next(); do { endSlot = endSlot->prev(); freeSlot(endSlot->next()); } while (numGlyphs < --length); endSlot->next(end); if (end) end->prev(endSlot); } else { // insert extra slots if needed while (numGlyphs > length) { Slot * extra = newSlot(); if (!extra) return; extra->prev(endSlot); extra->next(endSlot->next()); endSlot->next(extra); if (extra->next()) extra->next()->prev(extra); if (m_last == endSlot) m_last = extra; endSlot = extra; ++length; } } endSlot = endSlot->next(); assert(numGlyphs == length); assert(offset + numChars <= m_numCharinfo); Slot * indexmap[eMaxSpliceSize*3]; assert(numGlyphs < sizeof indexmap/sizeof *indexmap); Slot * slot = startSlot; for (uint16 i=0; i < numGlyphs; slot = slot->next(), ++i) indexmap[i] = slot; for (slot = startSlot; slot != endSlot; slot = slot->next(), srcSlot = srcSlot->next()) { slot->set(*srcSlot, offset, m_silf->numUser(), m_silf->numJustLevels(), numChars); if (srcSlot->attachedTo()) slot->attachTo(indexmap[srcSlot->attachedTo()->index()]); if (srcSlot->nextSibling()) slot->m_sibling = indexmap[srcSlot->nextSibling()->index()]; if (srcSlot->firstChild()) slot->m_child = indexmap[srcSlot->firstChild()->index()]; } }
// reverse the slots but keep diacritics in their same position after their bases void Segment::reverseSlots() { m_dir = m_dir ^ 64; // invert the reverse flag if (m_first == m_last) return; // skip 0 or 1 glyph runs Slot *t = 0; Slot *curr = m_first; Slot *tlast; Slot *tfirst; Slot *out = 0; while (curr && getSlotBidiClass(curr) == 16) curr = curr->next(); if (!curr) return; tfirst = curr->prev(); tlast = curr; while (curr) { if (getSlotBidiClass(curr) == 16) { Slot *d = curr->next(); while (d && getSlotBidiClass(d) == 16) d = d->next(); d = d ? d->prev() : m_last; Slot *p = out->next(); // one after the diacritics. out can't be null if (p) p->prev(d); else tlast = d; t = d->next(); d->next(p); curr->prev(out); out->next(curr); } else // will always fire first time round the loop { if (out) out->prev(curr); t = curr->next(); curr->next(out); out = curr; } curr = t; } out->prev(tfirst); if (tfirst) tfirst->next(out); else m_first = out; m_last = tlast; }
void Pass::dumpRuleEventOutput(const FiniteStateMachine & fsm, const Rule & r, Slot * const last_slot) const { *fsm.dbgout << json::item << json::flat << json::object << "id" << &r - m_rules << "failed" << false << "input" << json::flat << json::object << "start" << objectid(dslot(&fsm.slots.segment, input_slot(fsm.slots, 0))) << "length" << r.sort - r.preContext << json::close // close "input" << json::close // close Rule object << json::close // close considered array << "output" << json::object << "range" << json::flat << json::object << "start" << objectid(dslot(&fsm.slots.segment, input_slot(fsm.slots, 0))) << "end" << objectid(dslot(&fsm.slots.segment, last_slot)) << json::close // close "input" << "slots" << json::array; const Position rsb_prepos = last_slot ? last_slot->origin() : fsm.slots.segment.advance(); fsm.slots.segment.positionSlots(0); for(Slot * slot = output_slot(fsm.slots, 0); slot != last_slot; slot = slot->next()) *fsm.dbgout << dslot(&fsm.slots.segment, slot); *fsm.dbgout << json::close // close "slots" << "postshift" << (last_slot ? last_slot->origin() : fsm.slots.segment.advance()) - rsb_prepos << json::close; // close "output" object }
void Segment::doMirror(uint16 aMirror) { Slot * s; for (s = m_first; s; s = s->next()) { unsigned short g = glyphAttr(s->gid(), aMirror); if (g && (!(dir() & 4) || !glyphAttr(s->gid(), aMirror + 1))) s->setGlyph(this, g); } }
bool Segment::initCollisions() { m_collisions = grzeroalloc<SlotCollision>(slotCount()); if (!m_collisions) return false; for (Slot *p = m_first; p; p = p->next()) if (p->index() < slotCount()) ::new (collisionInfo(p)) SlotCollision(this, p); else return false; return true; }
bool Face::runGraphite(Segment *seg, const Silf *aSilf) const { #if !defined GRAPHITE2_NTRACING json * dbgout = logger(); if (dbgout) { *dbgout << json::object << "id" << objectid(seg) << "passes" << json::array; } #endif // if ((seg->dir() & 1) != aSilf->dir()) // seg->reverseSlots(); if ((seg->dir() & 3) == 3 && aSilf->bidiPass() == 0xFF) seg->doMirror(aSilf->aMirror()); bool res = aSilf->runGraphite(seg, 0, aSilf->positionPass(), true); if (res) { seg->associateChars(0, seg->charInfoCount()); if (aSilf->flags() & 0x20) res &= seg->initCollisions(); res &= aSilf->runGraphite(seg, aSilf->positionPass(), aSilf->numPasses(), false); } #if !defined GRAPHITE2_NTRACING if (dbgout) { seg->positionSlots(0, 0, 0, aSilf->dir()); *dbgout << json::item << json::close // Close up the passes array << "output" << json::array; for(Slot * s = seg->first(); s; s = s->next()) *dbgout << dslot(seg, s); seg->finalise(0); // Call this here to fix up charinfo back indexes. *dbgout << json::close << "advance" << seg->advance() << "chars" << json::array; for(size_t i = 0, n = seg->charInfoCount(); i != n; ++i) *dbgout << json::flat << *seg->charinfo(i); *dbgout << json::close // Close up the chars array << json::close; // Close up the segment object } #endif return res; }
bool Face::runGraphite(Segment *seg, const Silf *aSilf) const { #if !defined GRAPHITE2_NTRACING json * dbgout = logger(); if (dbgout) { *dbgout << json::object << "id" << objectid(seg) << "passes" << json::array; } #endif bool res = aSilf->runGraphite(seg, 0, aSilf->justificationPass(), true); if (res) res = aSilf->runGraphite(seg, aSilf->positionPass(), aSilf->numPasses(), false); #if !defined GRAPHITE2_NTRACING if (dbgout) { *dbgout << json::item << json::close // Close up the passes array << "output" << json::array; for(Slot * s = seg->first(); s; s = s->next()) *dbgout << dslot(seg, s); seg->finalise(0); // Call this here to fix up charinfo back indexes. *dbgout << json::close << "advance" << seg->advance() << "chars" << json::array; for(size_t i = 0, n = seg->charInfoCount(); i != n; ++i) *dbgout << json::flat << *seg->charinfo(i); *dbgout << json::close // Close up the chars array << json::close; // Close up the segment object } #endif return res; }
Slot *Segment::newSlot() { if (!m_freeSlots) { // check that the segment doesn't grow indefinintely if (m_numGlyphs > m_numCharinfo * MAX_SEG_GROWTH_FACTOR) return NULL; int numUser = m_silf->numUser(); #if !defined GRAPHITE2_NTRACING if (m_face->logger()) ++numUser; #endif Slot *newSlots = grzeroalloc<Slot>(m_bufSize); int16 *newAttrs = grzeroalloc<int16>(m_bufSize * numUser); if (!newSlots || !newAttrs) { free(newSlots); free(newAttrs); return NULL; } for (size_t i = 0; i < m_bufSize; i++) { ::new (newSlots + i) Slot(newAttrs + i * numUser); newSlots[i].next(newSlots + i + 1); } newSlots[m_bufSize - 1].next(NULL); newSlots[0].next(NULL); m_slots.push_back(newSlots); m_userAttrs.push_back(newAttrs); m_freeSlots = (m_bufSize > 1)? newSlots + 1 : NULL; return newSlots; } Slot *res = m_freeSlots; m_freeSlots = m_freeSlots->next(); res->next(NULL); return res; }
bool CachedFace::runGraphite(Segment *seg, const Silf *pSilf) const { assert(pSilf); pSilf->runGraphite(seg, 0, pSilf->substitutionPass()); unsigned int silfIndex = 0; for (; silfIndex < m_numSilf && &(m_silfs[silfIndex]) != pSilf; ++silfIndex); if (silfIndex == m_numSilf) return false; SegCache * const segCache = m_cacheStore->getOrCreate(silfIndex, seg->getFeatures(0)); if (!segCache) return false; assert(m_cacheStore); // find where the segment can be broken Slot * subSegStartSlot = seg->first(); Slot * subSegEndSlot = subSegStartSlot; uint16 cmapGlyphs[eMaxSpliceSize]; int subSegStart = 0; for (unsigned int i = 0; i < seg->charInfoCount(); ++i) { const unsigned int length = i - subSegStart + 1; if (length < eMaxSpliceSize) cmapGlyphs[length-1] = subSegEndSlot->gid(); else return false; const bool spaceOnly = m_cacheStore->isSpaceGlyph(subSegEndSlot->gid()); // at this stage the character to slot mapping is still 1 to 1 const int breakWeight = seg->charinfo(i)->breakWeight(), nextBreakWeight = (i + 1 < seg->charInfoCount())? seg->charinfo(i+1)->breakWeight() : 0; const uint8 f = seg->charinfo(i)->flags(); if (((spaceOnly || (breakWeight > 0 && breakWeight <= gr_breakWord) || i + 1 == seg->charInfoCount() || ((nextBreakWeight < 0 && nextBreakWeight >= gr_breakBeforeWord) || (subSegEndSlot->next() && m_cacheStore->isSpaceGlyph(subSegEndSlot->next()->gid())))) && f != 1) || f == 2) { // record the next slot before any splicing Slot * nextSlot = subSegEndSlot->next(); // spaces should be left untouched by graphite rules in any sane font if (!spaceOnly) { // found a break position, check for a cache of the sub sequence const SegCacheEntry * entry = segCache->find(cmapGlyphs, length); // TODO disable cache for words at start/end of line with contextuals if (!entry) { SegmentScopeState scopeState = seg->setScope(subSegStartSlot, subSegEndSlot, length); pSilf->runGraphite(seg, pSilf->substitutionPass(), pSilf->numPasses()); if (length < eMaxSpliceSize) { seg->associateChars(); entry = segCache->cache(m_cacheStore, cmapGlyphs, length, seg, subSegStart); } seg->removeScope(scopeState); } else seg->splice(subSegStart, length, subSegStartSlot, subSegEndSlot, entry->first(), entry->glyphLength()); } subSegStartSlot = subSegEndSlot = nextSlot; subSegStart = i + 1; } else { subSegEndSlot = subSegEndSlot->next(); } } return true; }
inline Slot * output_slot(const SlotMap & slots, const int n) { Slot * s = slots[slots.context() + n - 1]; return s ? s->next() : slots.segment.first(); }
bool Silf::runGraphite(Segment *seg, uint8 firstPass, uint8 lastPass) const { assert(seg != 0); SlotMap map(*seg); FiniteStateMachine fsm(map, seg->getFace()->logger()); vm::Machine m(map); unsigned int initSize = seg->slotCount(); #if !defined GRAPHITE2_NTRACING json * const dbgout = seg->getFace()->logger(); #endif if (lastPass == 0) { if (firstPass == lastPass) return true; lastPass = m_numPasses; } for (size_t i = firstPass; i < lastPass; ++i) { // bidi and mirroring if (i == m_bPass) { #if !defined GRAPHITE2_NTRACING if (dbgout) { *dbgout << json::item << json::object << "id" << -1 << "slots" << json::array; seg->positionSlots(0); for(Slot * s = seg->first(); s; s = s->next()) *dbgout << dslot(seg, s); *dbgout << json::close << "rules" << json::array << json::close << json::close; } #endif if (!(seg->dir() & 2)) seg->bidiPass(m_aBidi, seg->dir() & 1, m_aMirror); else if (m_aMirror) { Slot * s; for (s = seg->first(); s; s = s->next()) { unsigned short g = seg->glyphAttr(s->gid(), m_aMirror); if (g && (!(seg->dir() & 4) || !seg->glyphAttr(s->gid(), m_aMirror + 1))) s->setGlyph(seg, g); } } } #if !defined GRAPHITE2_NTRACING if (dbgout) { *dbgout << json::item << json::object << "id" << i+1 << "slots" << json::array; seg->positionSlots(0); for(Slot * s = seg->first(); s; s = s->next()) *dbgout << dslot(seg, s); *dbgout << json::close; } #endif // test whether to reorder, prepare for positioning m_passes[i].runGraphite(m, fsm); // only subsitution passes can change segment length, cached subsegments are short for their text if (m.status() != vm::Machine::finished || (i < m_pPass && (seg->slotCount() > initSize * MAX_SEG_GROWTH_FACTOR || (seg->slotCount() && seg->slotCount() * MAX_SEG_GROWTH_FACTOR < initSize)))) return false; } return true; }
bool Silf::runGraphite(Segment *seg, uint8 firstPass, uint8 lastPass, int dobidi) const { assert(seg != 0); unsigned int maxSize = seg->slotCount() * MAX_SEG_GROWTH_FACTOR; SlotMap map(*seg, m_dir, maxSize); FiniteStateMachine fsm(map, seg->getFace()->logger()); vm::Machine m(map); uint8 lbidi = m_bPass; #if !defined GRAPHITE2_NTRACING json * const dbgout = seg->getFace()->logger(); #endif if (lastPass == 0) { if (firstPass == lastPass && lbidi == 0xFF) return true; lastPass = m_numPasses; } if ((firstPass < lbidi || (dobidi && firstPass == lbidi)) && (lastPass >= lbidi || (dobidi && lastPass + 1 == lbidi))) lastPass++; else lbidi = 0xFF; for (size_t i = firstPass; i < lastPass; ++i) { // bidi and mirroring if (i == lbidi) { #if !defined GRAPHITE2_NTRACING if (dbgout) { *dbgout << json::item << json::object << "id" << -1 << "slots" << json::array; seg->positionSlots(0, 0, 0, m_dir); for(Slot * s = seg->first(); s; s = s->next()) *dbgout << dslot(seg, s); *dbgout << json::close << "rules" << json::array << json::close << json::close; } #endif if (seg->currdir() != (m_dir & 1)) seg->reverseSlots(); if (m_aMirror && (seg->dir() & 3) == 3) seg->doMirror(m_aMirror); --i; lbidi = lastPass; --lastPass; continue; } #if !defined GRAPHITE2_NTRACING if (dbgout) { *dbgout << json::item << json::object << "id" << i+1 << "slots" << json::array; seg->positionSlots(0, 0, 0, m_dir); for(Slot * s = seg->first(); s; s = s->next()) *dbgout << dslot(seg, s); *dbgout << json::close; } #endif // test whether to reorder, prepare for positioning bool reverse = (lbidi == 0xFF) && (seg->currdir() != ((m_dir & 1) ^ m_passes[i].reverseDir())); if ((i >= 32 || (seg->passBits() & (1 << i)) == 0 || m_passes[i].collisionLoops()) && !m_passes[i].runGraphite(m, fsm, reverse)) return false; // only subsitution passes can change segment length, cached subsegments are short for their text if (m.status() != vm::Machine::finished || (seg->slotCount() && seg->slotCount() > maxSize)) return false; } return true; }
bool Silf::runGraphite(Segment *seg, uint8 firstPass, uint8 lastPass) const { assert(seg != 0); SlotMap map(*seg); FiniteStateMachine fsm(map); vm::Machine m(map); unsigned int initSize = seg->slotCount(); if (lastPass == 0) { if (firstPass == lastPass) return true; lastPass = m_numPasses; } #if !defined GRAPHITE2_NTRACING if (dbgout) { char version[64]; sprintf(version, "%d.%d.%d", GR2_VERSION_MAJOR, GR2_VERSION_MINOR, GR2_VERSION_BUGFIX); *dbgout << json::object << "version" << version << "passes" << json::array; } #endif for (size_t i = firstPass; i < lastPass; ++i) { // bidi and mirroring if (i == m_bPass) { #if !defined GRAPHITE2_NTRACING if (dbgout) { *dbgout << json::item << json::object << "id" << -1 << "slots" << json::array; seg->positionSlots(0); for(Slot * s = seg->first(); s; s = s->next()) *dbgout << dslot(seg, s); *dbgout << json::close << "rules" << json::array << json::close << json::close; } #endif if (!(seg->dir() & 2)) seg->bidiPass(m_aBidi, seg->dir() & 1, m_aMirror); else if (m_aMirror) { Slot * s; for (s = seg->first(); s; s = s->next()) { unsigned short g = seg->glyphAttr(s->gid(), m_aMirror); if (g && (!(seg->dir() & 4) || !seg->glyphAttr(s->gid(), m_aMirror + 1))) s->setGlyph(seg, g); } } } #if !defined GRAPHITE2_NTRACING if (dbgout) { *dbgout << json::item << json::object << "id" << i+1 << "slots" << json::array; seg->positionSlots(0); for(Slot * s = seg->first(); s; s = s->next()) *dbgout << dslot(seg, s); *dbgout << json::close; } #endif // test whether to reorder, prepare for positioning m_passes[i].runGraphite(m, fsm); // only subsitution passes can change segment length, cached subsegments are short for their text if (m.status() != vm::Machine::finished || (i < m_pPass && (seg->slotCount() > initSize * MAX_SEG_GROWTH_FACTOR || (seg->slotCount() && seg->slotCount() * MAX_SEG_GROWTH_FACTOR < initSize)))) return false; } #if !defined GRAPHITE2_NTRACING if (dbgout) { *dbgout << json::item << json::close // Close up the passes array << "output" << json::array; for(Slot * s = seg->first(); s; s = s->next()) *dbgout << dslot(seg, s); seg->finalise(0); // Call this here to fix up charinfo back indexes. *dbgout << json::close << "advance" << seg->advance() << "chars" << json::array; for(size_t i = 0, n = seg->charInfoCount(); i != n; ++i) *dbgout << json::flat << *seg->charinfo(i); *dbgout << json::close // Close up the chars array << json::close; // Clsoe up the segment object } #endif return true; }