예제 #1
0
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);
}
예제 #2
0
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);
    }
}
예제 #3
0
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());
}
예제 #4
0
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()];
    }
}
예제 #5
0
// 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;
}
예제 #6
0
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

}
예제 #7
0
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);
    }
}
예제 #8
0
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;
}
예제 #9
0
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;
}
예제 #10
0
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;
}
예제 #11
0
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;
}
예제 #12
0
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;
}
예제 #13
0
inline
Slot * output_slot(const SlotMap &  slots, const int n)
{
	Slot * s = slots[slots.context() + n - 1];
	return s ? s->next() : slots.segment.first();
}
예제 #14
0
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;
}
예제 #15
0
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;
}
예제 #16
0
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;
}