/*--------------------------------------------------------------------*//*:Ignore this sentence. Copyright (C) 1999, 2001 SIL International. All rights reserved. Distributable under the terms of either the Common Public License or the GNU Lesser General Public License, as specified in the LICENSING.txt file. File: GrSlotStream.cpp Responsibility: Sharon Correll Last reviewed: Not yet. Description: GrSlotStream class implementation. ----------------------------------------------------------------------------------------------*/ //:>******************************************************************************************** //:> Include files //:>******************************************************************************************** #include "Main.h" #include "GrConstants.h" #ifdef _MSC_VER #pragma hdrstop #endif #undef THIS_FILE DEFINE_THIS_FILE //:End Ignore //:>******************************************************************************************** //:> Forward declarations //:>******************************************************************************************** //:>******************************************************************************************** //:> Local Constants and static variables //:>******************************************************************************************** namespace gr { //:>******************************************************************************************** //:> Methods //:>******************************************************************************************** /*---------------------------------------------------------------------------------------------- Answer true if there is nothing valid available to read from the stream. ----------------------------------------------------------------------------------------------*/ bool GrSlotStream::AtEnd() { Assert(AssertValid()); if (m_islotReprocPos > -1 && m_islotReprocPos < signed(m_vpslotReproc.size())) return false; if (m_islotSegLim > -1 && m_islotReadPos >= m_islotSegLim) // We've determined the segment limit, and we're at or past it. return true; return (m_islotReadPos == m_islotWritePos); } /*---------------------------------------------------------------------------------------------- Answer true if there is nothing at all available to read from the stream. For substitution passes, we need to consider slots that may be beyond what we know will be the official end of the segment. ----------------------------------------------------------------------------------------------*/ bool GrSlotStream::AtEndOfContext() { if (m_fUsedByPosPass) return AtEnd(); if (m_islotReprocPos > -1 && m_islotReprocPos < signed(m_vpslotReproc.size())) return false; return (m_islotReadPos == m_islotWritePos); } /*---------------------------------------------------------------------------------------------- Append a slot to the end of the stream. ----------------------------------------------------------------------------------------------*/ void GrSlotStream::NextPut(GrSlotState * pslot) { Assert(AssertValid()); if (m_islotWritePos < signed(m_vpslot.size())) { m_vpslot[m_islotWritePos] = pslot; } else { m_vpslot.push_back(pslot); m_vislotPrevChunkMap.push_back(-1); m_vislotNextChunkMap.push_back(-1); } if (m_fUsedByPosPass && GotIndexOffset()) pslot->SetPosPassIndex(m_islotWritePos - m_cslotPreSeg, m_fInputToPosPass1); m_islotWritePos++; } /*---------------------------------------------------------------------------------------------- Get the next slot from the stream. ----------------------------------------------------------------------------------------------*/ GrSlotState * GrSlotStream::NextGet() { Assert(AssertValid()); Assert(!AtEndOfContext()); GrSlotState * pslotRet; if (m_islotReprocPos > -1) { if (m_islotReprocPos >= signed(m_vpslotReproc.size())) { // Finished reprocessing. Assert((unsigned)m_islotReprocPos == m_vpslotReproc.size()); m_islotReprocPos = -1; // But leave the m_vpslotReproc array in place so that earlier slots can // be accessed by the rules. pslotRet = m_vpslot[m_islotReadPos]; m_islotReadPos++; } else { // Read from the reprocess buffer. pslotRet = m_vpslotReproc[m_islotReprocPos]; m_islotReprocPos++; } } else { // Read normally. pslotRet = m_vpslot[m_islotReadPos]; m_islotReadPos++; } Assert(!m_fUsedByPosPass || pslotRet->PosPassIndex() != GrSlotAbstract::kNotYetSet); return pslotRet; } /*--------------------------------------------------------------------------------------------- Peek at the next slot from the stream (dislot = 0 means the very next slot). Only used for peeking forward. RuleInputSlot is used for accessing any possible slot in the rule's context. ----------------------------------------------------------------------------------------------*/ GrSlotState * GrSlotStream::Peek(int dislot) { Assert(AssertValid()); Assert(!AtEndOfContext()); Assert(dislot >= 0); GrSlotState * pslotRet; if (m_islotReprocPos > -1) { int cslotReproc = m_vpslotReproc.size() - m_islotReprocPos; // num left in reproc buffer if (dislot >= cslotReproc) { // Not quite enough slots in the reprocess buffer. pslotRet = m_vpslot[m_islotReadPos + dislot - cslotReproc]; } else if (m_islotReprocPos + dislot < 0) { // Reading prior to the reprocess buffer pslotRet = m_vpslot[m_islotReadPos + dislot - cslotReproc]; } else { // Read from the reprocess buffer. pslotRet = m_vpslotReproc[m_islotReprocPos + dislot]; } } else { // Read normally. Assert(m_islotWritePos - m_islotReadPos > dislot); pslotRet = m_vpslot[m_islotReadPos + dislot]; } return pslotRet; } /*--------------------------------------------------------------------------------------------- Treat the stream, which is really the output stream, as an input stream, retrieving a recently output slot. This happens when we are reading before the current stream position when the rule is being matched, or before the original stream position when the rule is being run. @param dislot - how far back to peek before the write position when the rule started; a negative number (NOTE: the current write position is irrelevant) @param fNullOkay - true if it's okay to return NULL in the situation where we're asking for something before the beginning of the stream ----------------------------------------------------------------------------------------------*/ GrSlotState * GrSlotStream::PeekBack(int dislot, bool fNullOkay) { Assert(dislot < 0); if (dislot < m_islotRuleStartWrite * -1) { Assert(fNullOkay); return NULL; } GrSlotState * pslotRet; if (m_islotReprocPos > -1) { if (dislot < m_islotReprocLim - m_islotRuleStartWrite && dislot >= m_islotReprocLim - m_islotRuleStartWrite - signed(m_vpslotReproc.size())) { // Read from the reprocess buffer. // Review: this is weird; can it ever happen? pslotRet = m_vpslotReproc[m_vpslotReproc.size() - (m_islotReprocLim - m_islotRuleStartWrite) + dislot]; } else pslotRet = m_vpslot[m_islotRuleStartWrite + dislot]; } else pslotRet = m_vpslot[m_islotRuleStartWrite + dislot]; return pslotRet; } /*--------------------------------------------------------------------------------------------- Skip the given number of slots in the stream. ENHANCE: implement more efficiently ----------------------------------------------------------------------------------------------*/ void GrSlotStream::Skip(int dislot) { for (int islot = 0; islot < dislot; ++islot) NextGet(); } /*---------------------------------------------------------------------------------------------- Perform assertions to ensure that the stream is in a valid state. ----------------------------------------------------------------------------------------------*/ bool GrSlotStream::AssertValid() { Assert(m_islotWritePos <= signed(m_vpslot.size())); Assert(m_islotReadPos <= m_islotWritePos); // Streams used by the positioning passes (input or output) should not be working // with slots beyond the segment lim Assert(m_islotSegLim == -1 || !m_fUsedByPosPass || m_islotReadPos <= m_islotSegLim); Assert(m_vpslot.size() == m_vislotPrevChunkMap.size()); Assert(m_vpslot.size() == m_vislotNextChunkMap.size()); return true; } /*---------------------------------------------------------------------------------------------- Return the number of slots that have been completely output by the previous pass but not yet processed by the following pass. ("Completely" output means that they do not need to be reprocessed by the same pass, only then are they available as valid input.) Include any slots that are present by virtue of needing to be reprocessed by the corresponding output stream. ----------------------------------------------------------------------------------------------*/ int GrSlotStream::SlotsPending() { Assert(AssertValid()); if (m_islotSegLim > -1) return (m_islotSegLim - m_islotReadPos + SlotsToReprocess()); else return (m_islotWritePos - m_islotReadPos + SlotsToReprocess()); } int GrSlotStream::SlotsPendingInContext() { Assert(AssertValid()); if (m_fUsedByPosPass) return SlotsPending(); else return (m_islotWritePos - m_islotReadPos + SlotsToReprocess()); } int GrSlotStream::TotalSlotsPending() { int cslot = SlotsPendingInContext(); if (m_fUsedByPosPass && m_islotSegLim > -1) { int ctmp = m_islotWritePos - m_islotReadPos + SlotsToReprocess(); if (ctmp > cslot) { int x; x = 3; } cslot = max(cslot, ctmp); } return cslot; } /*---------------------------------------------------------------------------------------------- Return true if this pass is input or output to a positioning pass and we're past the end of the segment. So this stream is virtually finished. ----------------------------------------------------------------------------------------------*/ bool GrSlotStream::PastEndOfPositioning(bool fOutput) { if (m_fUsedByPosPass && m_islotSegLim > -1) { if (fOutput) // this stream is functioning as an output stream return (m_islotWritePos >= m_islotSegLim); else // this stream is functioning as an input stream return (m_islotReadPos >= m_islotSegLim); } return false; } /*---------------------------------------------------------------------------------------------- Mark the stream as fully written, so that the output pass doesn't keep trying to ask for more input. ----------------------------------------------------------------------------------------------*/ void GrSlotStream::MarkFullyWritten() { m_fFullyWritten = true; } /*---------------------------------------------------------------------------------------------- Return the position of the first glyph at or following the given position that is in the top direction, or -1 if we do not have a complete range. @param ptman @param islotStart @nTopDirLevel - top direction: 0 = left-to-right, 1 = right-to-left ----------------------------------------------------------------------------------------------*/ int GrSlotStream::OldDirLevelRange(EngineState * pengst, int islotStart, int nTopDirLevel) { Assert(m_islotReadPos <= islotStart); Assert(islotStart < m_islotWritePos); int islot = islotStart; int nSlotDirLevel = GetSlotDirLevel(pengst, islot, nTopDirLevel, nTopDirLevel, kdircUnknown, kdircNeutral); if (nSlotDirLevel == -1) return -1; while (nSlotDirLevel > nTopDirLevel) { islot++; if (islot >= m_islotWritePos) { if (m_fFullyWritten) return islot; else return -1; } else if (m_islotSegLim > -1 && islot >= m_islotSegLim) { return islot; } nSlotDirLevel = GetSlotDirLevel(pengst, islot, nTopDirLevel, nTopDirLevel, kdircUnknown, kdircNeutral); if (nSlotDirLevel == -1) return -1; } return islot; } /*---------------------------------------------------------------------------------------------- Return the position of the first glyph at or following the given position that is in the top direction, or -1 if we do not have a complete range. @param ptman @param islotStart @nTopDirLevel - top direction: 0 = left-to-right, 1 = right-to-left ----------------------------------------------------------------------------------------------*/ int GrSlotStream::DirLevelRange(EngineState * pengst, int islotStart, int nTopDirLevel, std::vector & vislotStarts, std::vector & vislotStops) { Assert(m_islotReadPos <= islotStart); Assert(islotStart < m_islotWritePos); std::vector vislotStartStack; // indices of starts of ranges to reverse // Stack corresponding to embedding and override markers: std::vector vdircMarkerStack; std::vector vdircOverrideStack; std::vector vnLevelStack; vislotStarts.clear(); vislotStops.clear(); //vislotStartStack.Push(islot); int islotSegLim = (FullyWritten() || m_islotSegLim > -1) ? FinalSegLim() : WritePos() + 100; // arbitrarily large number if (islotStart > islotSegLim) { // Don't do anything fancy with stuff beyond the end of the segment. return 0; } DirCode dircMarker = kdircNeutral; // none so far int nCurrLevel = nTopDirLevel; int nMarkerLevel = nTopDirLevel; //bool fHitMark = false; DirCode dircOverride = kdircNeutral; int islot = islotStart; Assert(islot <= m_islotWritePos); while (true) { GrSlotState * pslot = m_vpslot[islot]; DirCode dirc = pslot->DirProcessed(); bool fIncludeLast = false; int nSlotDirLevel; if (dirc == kdircLRO || dirc == kdircLRE) // steps X1, X3 in the Unicode bidi algorithm { vdircMarkerStack.push_back(dircMarker); vdircOverrideStack.push_back(dircOverride); vnLevelStack.push_back(nCurrLevel); dircMarker = dirc; dircOverride = (dirc == kdircLRO) ? kdircL : kdircNeutral; nSlotDirLevel = (nCurrLevel % 2) ? nCurrLevel + 1 : nCurrLevel + 2; // next highest LTR level nMarkerLevel = nSlotDirLevel; pslot->SetDirProcessed(dircMarker); pslot->SetDirLevel(nMarkerLevel); pslot->MarkDirProcessed(); } else if (dirc == kdircRLO || dirc == kdircRLE) // steps X2, X4 { vdircMarkerStack.push_back(dircMarker); vdircOverrideStack.push_back(dircOverride); vnLevelStack.push_back(nCurrLevel); dircMarker = dirc; dircOverride = (dirc == kdircRLO) ? kdircR : kdircNeutral; nSlotDirLevel = (nCurrLevel % 2) ? nCurrLevel + 2 : nCurrLevel + 1; // next highest RTL level nMarkerLevel = nSlotDirLevel; pslot->SetDirProcessed(dircMarker); pslot->SetDirLevel(nMarkerLevel); pslot->MarkDirProcessed(); } else if (dirc == kdircPDF) { if (dircMarker == kdircNeutral) pslot->SetDirProcessed((nCurrLevel % 2) ? kdircR : kdircL); else pslot->SetDirProcessed(RightToLeftDir(dircMarker) ? kdircPdfR : kdircPdfL); pslot->SetDirLevel(nMarkerLevel); pslot->MarkDirProcessed(); if (vnLevelStack.size()) { dircMarker = vdircMarkerStack.back(); vdircMarkerStack.pop_back(); dircOverride = vdircOverrideStack.back(); vdircOverrideStack.pop_back(); nMarkerLevel = vnLevelStack.back(); vnLevelStack.pop_back(); nSlotDirLevel = nMarkerLevel; fIncludeLast = true; } else // ignore nSlotDirLevel = nCurrLevel; } else { nSlotDirLevel = GetSlotDirLevel(pengst, islot, nTopDirLevel, nMarkerLevel, dircMarker, dircOverride); if (nSlotDirLevel == -1) return -1; // not enough slots } Assert(pengst->WhiteSpaceOnly() || nCurrLevel >= nTopDirLevel); while (nSlotDirLevel > nCurrLevel) { vislotStartStack.push_back(islot); nCurrLevel++; } // otherwise... while (nTopDirLevel <= nCurrLevel && nSlotDirLevel < nCurrLevel) { if (vislotStartStack.size()) { int islotStartTmp = vislotStartStack.back(); vislotStartStack.pop_back(); vislotStarts.push_back(islotStartTmp); vislotStops.push_back(islot - 1); // previous character was the end of the range } else Assert(pengst->WhiteSpaceOnly()); // the space is higher than the segment itself nCurrLevel--; } // In the case of white-space-only segments, the space may be bumped up to a level // higher than the segment itself. Assert(pengst->WhiteSpaceOnly() || vislotStartStack.size() + nTopDirLevel == (unsigned)nCurrLevel); Assert(pengst->WhiteSpaceOnly() || vnLevelStack.size() == vdircMarkerStack.size()); Assert(pengst->WhiteSpaceOnly() || vdircOverrideStack.size() == vdircMarkerStack.size()); if (nCurrLevel <= nTopDirLevel) { if (fIncludeLast) { // Include in the range to reverse the PDF or the char after the LRM/RLM. // (Note: don't use a size_t below: we are decrementing below zero.) for (int i = signed(vislotStops.size()) - 1; i >= 0 && vislotStops[i] == islot-1; i--) { vislotStops[i] = islot; } islot++; } break; // reached end of reversible range } islot++; if (islot >= m_islotWritePos) { if (m_fFullyWritten) break; // end of input else return -1; // not enough slots } else if (islot >= islotSegLim) { break; // end of input } } // Reached the end of a reversable range. while (vislotStartStack.size() > 0) { int islotStartTmp = vislotStartStack.back(); vislotStartStack.pop_back(); vislotStarts.push_back(islotStartTmp); //vislotStops.push_back(min(islot, ReadPos() - 1)); //vislotStops.push_back(min(islot, max(ReadPos() - 1, islotStartTmp))); vislotStops.push_back(min(islot - 1, islotSegLim - 1)); nCurrLevel--; } Assert(pengst->WhiteSpaceOnly() || nCurrLevel == nTopDirLevel); Assert(vislotStarts.size() == vislotStops.size()); // The start- and stop-lists were generated in the order that we hit the stops, in other // words, from inner to outer. Which is the order in which we need to do the reversals, too. return islot; } /*---------------------------------------------------------------------------------------------- Return the direction level of the glyph at the given position, according to the Unicode bidi algorithm. Return -1 if there aren't enough slots in the stream to figure it out. ???? How should this handle the reprocessing sub-stream???? @param ptman @param islot - slot for which information is requested @param nOuterLevel - 0 = left-to-right, 1 = right-to-left @param nCurrLevel @param dircMarker - most recent override or embedding marker, if any (for interpreting PDF codes) @param dircOverride - override imposed by LRO or RLO ----------------------------------------------------------------------------------------------*/ int GrSlotStream::GetSlotDirLevel(EngineState * pengst, int islot, int nOuterLevel, int nCurrLevel, DirCode dircMarker, DirCode dircOverride) { // Assert(m_psstrmReprocess == NULL); GrSlotState * pslot = m_vpslot[islot]; int nDirLevel = pslot->DirLevel(); if (nDirLevel > -1) // already figured out return nDirLevel; if (pengst->WhiteSpaceOnly()) { // White-space-only segments should not have any reordering. nDirLevel = pengst->ParaRightToLeft(); pslot->SetDirLevel(nDirLevel); pslot->MarkDirProcessed(); return nDirLevel; } DirCode dirc = pslot->DirProcessed(); if (!pslot->DirHasBeenProcessed() && (WeakDir(dirc) || NeutralDir(dirc)) ) { // Resolve weak and neutral types; process an entire run of weak types at once, // in several passes. int islotWeakRunLim = islot + 1; while (true) { if (islotWeakRunLim >= m_islotWritePos) { if (!m_fFullyWritten) return -1; // get more slots else break; } else if (m_islotSegLim > -1 && islotWeakRunLim >= SegLimIfKnown()) { break; } if (StrongDir(m_vpslot[islotWeakRunLim]->DirProcessed())) // ignore the line-break; we need the next strong dir after that //// && !m_vpslot[islotWeakRunLim]->IsFinalLineBreak(ptman->LBGlyphID())) { break; } if (m_vpslot[islotWeakRunLim]->DirProcessed() == kdircPDF && dircMarker != kdircUnknown) { // A valid PDF is a strong code. TODO: replace kdircPDF with kdircPdfL/R break; } islotWeakRunLim++; } int islotLp; // X6. Handle directional overrides: LRO, RLO. if (dircOverride != kdircNeutral) { for (islotLp = islot; islotLp < islotWeakRunLim; islotLp++) { m_vpslot[islotLp]->SetDirProcessed(dircOverride); m_vpslot[islotLp]->MarkDirProcessed(); } goto LSetDir; } DirCode dircLp; //DirCode dircTmp; // W1. Non-spacing marks get the type of the previous slot. for (islotLp = islot; islotLp < islotWeakRunLim; islotLp++) { Assert(!m_vpslot[islotLp]->DirHasBeenProcessed()); if (m_vpslot[islotLp]->DirProcessed() == kdircNSM) { #ifdef _DEBUG if (islotLp <= 0) { Warn("Pathological case in bidi algorithm"); } #endif // _DEBUG dircLp = AdjacentNonBndNeutralCode(pengst, islotLp - 1, -1, dircMarker); m_vpslot[islotLp]->SetDirProcessed(dircLp); } } // W2. European numbers are changed to Arabic numbers if the previous strong type // was an Arabic letter. DirCode dircPrevStrong = AdjacentStrongCode(pengst, islot - 1, -1, dircMarker); if (dircPrevStrong == kdircRArab) { for (islotLp = islot; islotLp < islotWeakRunLim; islotLp++) { dircLp = m_vpslot[islotLp]->DirProcessed(); if (dircLp == kdircEuroNum) m_vpslot[islotLp]->SetDirProcessed(kdircArabNum); } } // W4. A single European separator between two Latin numbers changes to a Latin number. // A single common separator between two numbers of the same type changes to that // type. for (islotLp = islot; islotLp < islotWeakRunLim; islotLp++) { dircLp = m_vpslot[islotLp]->DirProcessed(); if ((dircLp == kdircEuroSep || dircLp == kdircComSep) && islotLp + 1 < m_islotWritePos) // don't test last slot in stream { DirCode dircPrev = AdjacentNonBndNeutralCode(pengst, islotLp - 1, -1, dircMarker); DirCode dircNext = AdjacentNonBndNeutralCode(pengst, islotLp + 1, 1, dircMarker); if (dircPrev == dircNext) { if (dircLp == kdircEuroSep && dircPrev == kdircEuroNum) { m_vpslot[islotLp]->SetDirProcessed(kdircEuroNum); } else if (dircLp == kdircComSep && (dircPrev == kdircEuroNum || dircPrev == kdircArabNum)) { m_vpslot[islotLp]->SetDirProcessed(dircPrev); } } } } // W5, W6. A sequence of European number terminators adjacent to Latin numbers changes // to Latin numbers. // This appears to be an ambiguity in the official algorithm: do we search backward // first, or forward? For our purposes, it is easier to search backward first. for (islotLp = islot; islotLp < islotWeakRunLim; islotLp++) { dircLp = m_vpslot[islotLp]->DirProcessed(); if (dircLp == kdircEuroTerm || dircLp == kdircBndNeutral) { if (TerminatorSequence(pengst, islotLp - 1, -1, dircMarker) == kdircEuroNum) dircLp = kdircEuroNum; else { if (TerminatorSequence(pengst, islotLp + 1, 1, dircMarker) == kdircEuroNum) dircLp = kdircEuroNum; // else could possibly be unknown if we are at the end of the stream } } // W6. Separators and terminators change to neutral. (Don't change // boundary neutrals at this point, because that would confuse step Pre-L1.) if (dircLp == kdircEuroSep || dircLp == kdircEuroTerm || dircLp == kdircComSep) { dircLp = kdircNeutral; } m_vpslot[islotLp]->SetDirProcessed(dircLp); } // W7. European numbers are changed to plain left-to-right if the previous strong type // was left-to-right. dircPrevStrong = AdjacentStrongCode(pengst, islot - 1, -1, dircMarker); if (dircPrevStrong == kdircL) { for (islotLp = islot; islotLp < islotWeakRunLim; islotLp++) { dircLp = (m_vpslot[islotLp]->DirProcessed()); if (dircLp == kdircEuroNum) m_vpslot[islotLp]->SetDirProcessed(kdircL); } } // Pre-L1. Make a list of all the trailing whitespace characters that need to take // on the direction of the final line break (ie, the top direction). We can't actually // change them yet, because that would confuse the loop below. But we have to // record them now because they might get changed in the loop below. std::vector vislotTrailingWS; int islotFinalLB = -1; for (islotLp = islotWeakRunLim; islotLp-- > islot; ) { if (m_vpslot[islotLp]->IsFinalLineBreak(pengst->TableManager()->LBGlyphID())) { islotFinalLB = islotLp; for (int islotLp2 = islotLp; islotLp2-- > islot; ) { dircLp = m_vpslot[islotLp2]->DirProcessed(); if (dircLp == kdircWhiteSpace || dircLp == kdircBndNeutral) vislotTrailingWS.push_back(islotLp2); else break; // hit something other than whitespace or neutral } break; } } // N1. A sequence of neutrals takes the direction of the strong surrounding text if // the text on both sides has the same direction. Latin and Arabic numbers are treated // as if they are right-to-left. // (If they are not the same, leave the type as neutral.) for (islotLp = islot; islotLp < islotWeakRunLim; islotLp++) { dircLp = m_vpslot[islotLp]->DirProcessed(); // Change Boundary Neutrals to plain neutrals (must happen after Pre-L1). if (dircLp == kdircBndNeutral) { m_vpslot[islotLp]->SetDirProcessed(kdircNeutral); dircLp = kdircNeutral; } if (NeutralDir(dircLp)) { // First, look for either a strong code or a number. If we get at least // one strong code and a number with the same direction, use that strong // code. DirCode dircPrev = AdjacentStrongCode(pengst, islotLp - 1, -1, dircMarker, true); DirCode dircNext = AdjacentStrongCode(pengst, islotLp + 1, 1, dircMarker, true); if (dircNext == kdircUnknown) return -1; if (dircPrev == kdircUnknown) dircPrev = dircNext; DirCode dircResult = kdircUnknown; if (dircNext == kdircNeutral) {} else if (RightToLeftDir(dircPrev) == RightToLeftDir(dircNext) && (StrongDir(dircNext) || StrongDir(dircPrev))) { // Got a strong matching direction. dircResult = (StrongDir(dircPrev)) ? dircPrev : dircNext; } else if (dircNext == kdircPdfL || dircNext == kdircPdfR) { // A following PDF flag wins, because it represents the edge of a run. dircResult = dircNext; } else if (StrongDir(dircNext) && StrongDir(dircPrev)) { // We must have text in different directions. } else { // Try again, insisting on strong codes, no numbers. if (!StrongDir(dircPrev)) dircPrev = AdjacentStrongCode(pengst, islotLp - 1, -1, dircMarker, false); if (!StrongDir(dircNext)) dircNext = AdjacentStrongCode(pengst, islotLp + 1, 1, dircMarker, false); if (dircPrev == kdircUnknown) dircPrev = dircNext; if (RightToLeftDir(dircPrev) == RightToLeftDir(dircNext) && (StrongDir(dircNext) || StrongDir(dircPrev))) { // Got a strong matching direction. dircResult = (StrongDir(dircPrev)) ? dircPrev : dircNext; } } if (dircResult != kdircUnknown) { dircResult = (RightToLeftDir(dircResult)) ? kdircR : kdircL; m_vpslot[islotLp]->SetDirProcessed(dircResult); } // else leave as neutral } } // L1. Now change trailing neutrals recorded above to the directionality of // the immediately following line-break, if any. if (islotFinalLB != -1) { DirCode dircLB = m_vpslot[islotFinalLB]->DirProcessed(); dircLB = (dircLB == kdircLlb) ? kdircL : kdircR; for (int iislot = 0; iislot < signed(vislotTrailingWS.size()); iislot++) { int islotTmp = vislotTrailingWS[iislot]; m_vpslot[islotTmp]->SetDirProcessed(dircLB); } } // Mark all the slots processed. for (islotLp = islot; islotLp < islotWeakRunLim; islotLp++) m_vpslot[islotLp]->MarkDirProcessed(); } else m_vpslot[islot]->MarkDirProcessed(); LSetDir: // Final resolution. dirc = pslot->DirProcessed(); if (dirc != kdircLlb && dirc != kdircRlb && dircOverride != kdircNeutral) { // Enforce whatever is imposed by the override. m_vpslot[islot]->SetDirProcessed(dircOverride); nDirLevel = nCurrLevel; } else if ((nCurrLevel % 2) == 0) // current level is even (left-to-right) { switch (dirc) { case kdircNeutral: case kdircWhiteSpace: nDirLevel = nCurrLevel; break; case kdircL: nDirLevel = nCurrLevel; break; case kdircLlb: case kdircRlb: // all line-breaks have the direction of the paragraph nDirLevel = nOuterLevel; break; case kdircR: case kdircRArab: nDirLevel = nCurrLevel + 1; break; case kdircEuroNum: case kdircArabNum: nDirLevel = nCurrLevel + 2; break; default: Assert(false); nDirLevel = nCurrLevel; } } else // current level is odd (right-to-left) { switch (dirc) { case kdircNeutral: case kdircWhiteSpace: nDirLevel = nCurrLevel; break; case kdircR: //case kdircRlb: case kdircRArab: nDirLevel = nCurrLevel; break; case kdircL: //case kdircLlb: case kdircEuroNum: case kdircArabNum: nDirLevel = nCurrLevel + 1; break; case kdircLlb: case kdircRlb: // all line-breaks have the direction of the paragraph nDirLevel = nOuterLevel; break; default: Assert(false); nDirLevel = nCurrLevel; } } pslot->SetDirLevel(nDirLevel); return nDirLevel; } /*---------------------------------------------------------------------------------------------- Return the next or previous strong code, kdircNeutral if we don't find one, or kdircUnknown if we need more slots to process. @param islot - index of slot of interest @param nInc - +1 to search forward, -1 to search backward @param fNumbersAreStrong - true if we want to treat numbers as having strong directionality @param dircPDF - how to interpret PDF codes (from corresponding RLO, etc) ----------------------------------------------------------------------------------------------*/ DirCode GrSlotStream::AdjacentStrongCode(EngineState * pengst, int islot, int nInc, DirCode dircPDF, bool fNumbersAreStrong) { if (islot < 0) return pengst->InitialStrongDir(); else if (islot >= m_islotWritePos) { if (m_fFullyWritten || (m_islotSegLim > -1 && islot >= SegLimIfKnown())) return kdircNeutral; else return kdircUnknown; } DirCode dirc = m_vpslot[islot]->DirProcessed(); if (dirc == kdircPDF && StrongDir(dircPDF)) return (RightToLeftDir(dircPDF)) ? kdircPdfR : kdircPdfL; if (StrongDir(dirc)) return dirc; if (fNumbersAreStrong && (dirc == kdircEuroNum || dirc == kdircArabNum)) return dirc; return AdjacentStrongCode(pengst, islot + nInc, nInc, dircPDF, fNumbersAreStrong); } /*---------------------------------------------------------------------------------------------- Return kdircEuroNum if there is a terminator sequence starting or ending with a European number. Return kdircUnknown if we need more slots to process. Return kdircNeutral otherwise. @param islot - index of slot of interest @param nInc - +1 to search forward, -1 to search backward @param dircPDF - how to interpret PDF codes (from corresponding RLO, etc) ----------------------------------------------------------------------------------------------*/ DirCode GrSlotStream::TerminatorSequence(EngineState * pengst, int islot, int nInc, DirCode dircPDF) { if (islot < 0) return pengst->InitialTermDir(); else if (islot >= m_islotWritePos) { if (m_fFullyWritten) return kdircNeutral; else return kdircUnknown; } DirCode dirc = m_vpslot[islot]->DirProcessed(); if (dirc == kdircPDF) return (RightToLeftDir(dircPDF)) ? kdircPdfR : kdircPdfL; else if (dirc == kdircEuroNum) // number found at end of terminator sequence return kdircEuroNum; else if (dirc == kdircEuroTerm) // yes, this slot is a terminator; are there more? return TerminatorSequence(pengst, islot + nInc, nInc, dircPDF); else if (dirc == kdircLlb || dirc == kdircRlb) // line-break slot--ignore and keep going return TerminatorSequence(pengst, islot + nInc, nInc, dircPDF); else if (dirc == kdircBndNeutral) // boundary neutral--ignore and keep going return TerminatorSequence(pengst, islot + nInc, nInc, dircPDF); else // no number found return kdircNeutral; } /*---------------------------------------------------------------------------------------------- Return the previous code that is not a Boundary Neutral. Return Other Neutral if there is no such. @param islot - index of slot of interest @param nInc - +1 to search forward, -1 to search backward @param dircPDF - how to interpret PDF codes (from corresponding RLO, etc) ----------------------------------------------------------------------------------------------*/ DirCode GrSlotStream::AdjacentNonBndNeutralCode(EngineState * pengst, int islot, int nInc, DirCode dircPDF) { Assert(islot < m_islotWritePos); if (islot < 0) return kdircNeutral; // or should we use: ptman->InitialStrongDir() ?? else if (islot >= m_islotWritePos) { if (m_fFullyWritten) return kdircNeutral; else return kdircUnknown; } DirCode dirc = m_vpslot[islot]->DirProcessed(); if (dirc == kdircBndNeutral) return AdjacentNonBndNeutralCode(pengst, islot + nInc, nInc, dircPDF); else if (dirc == kdircPDF) return (RightToLeftDir(dircPDF)) ? kdircPdfR : kdircPdfL; else return dirc; } /*---------------------------------------------------------------------------------------------- Directionality type functions. ----------------------------------------------------------------------------------------------*/ bool StrongDir(DirCode dirc) { return (dirc == kdircL || dirc == kdircR || dirc == kdircRArab || dirc == kdircLRO || dirc == kdircRLO || dirc == kdircLRE || dirc == kdircRLE || dirc == kdircPdfL || dirc == kdircPdfR); } bool WeakDir(DirCode dirc) { return (dirc == kdircEuroNum || dirc == kdircEuroSep || dirc == kdircEuroTerm || dirc == kdircArabNum || dirc == kdircComSep || dirc == kdircBndNeutral || dirc == kdircNSM); } bool NeutralDir(DirCode dirc) { return (dirc == kdircWhiteSpace || dirc == kdircNeutral); } /*---------------------------------------------------------------------------------------------- Special values used by the bidi algorithm. ----------------------------------------------------------------------------------------------*/ bool BidiCode(int nUnicode) { switch (nUnicode) { case knLRM: case knRLM: case knLRO: case knRLO: case knLRE: case knRLE: case knPDF: return true; default: break; } return false; } /*---------------------------------------------------------------------------------------------- Return true if the directionality should be treated as right-to-left. Used for the step of resolving neutrals (N1). ----------------------------------------------------------------------------------------------*/ bool RightToLeftDir(DirCode dirc) { switch (dirc) { case kdircL: case kdircLRO: case kdircLRE: case kdircPdfL: return false; case kdircR: case kdircRArab: case kdircArabNum: case kdircEuroNum: case kdircRLO: case kdircRLE: case kdircPdfR: return true; case kdircNeutral: return false; default: Assert(false); return false; } } /*---------------------------------------------------------------------------------------------- Copy a slot from prevStream, incrementing the positions. ----------------------------------------------------------------------------------------------*/ void GrSlotStream::CopyOneSlotFrom(GrSlotStream * psstrmPrev) { #ifdef _DEBUG gid16 chw; chw = psstrmPrev->Peek()->GlyphID(); #endif // If we are exactly at the segment boundary, pass the information on to this stream. if (psstrmPrev->AtSegMin()) SetSegMinToWritePos(); if (psstrmPrev->AtSegLim()) SetSegLimToWritePos(); NextPut(psstrmPrev->NextGet()); AssertValid(); psstrmPrev->AssertValid(); } /*---------------------------------------------------------------------------------------------- Copy one slot from the input stream to the recipient, which is the output stream. Used when reversing ranges of glyphs; caller is responsible for adjusting the read- and write-positions of the streams. REVIEW: How should this handle the reprocessing sub-stream? Currently this is not a problem because this function is only used by the bidi pass, which never has intra-pass reprocessing. ----------------------------------------------------------------------------------------------*/ void GrSlotStream::SimpleCopyFrom(GrSlotStream * psstrmI, int islotInput, int islotOutput) { if (signed(m_vpslot.size()) < islotOutput + 1) { m_vpslot.resize(islotOutput + 1); m_vislotPrevChunkMap.resize(islotOutput + 1); m_vislotNextChunkMap.resize(islotOutput + 1); } m_vpslot[islotOutput] = psstrmI->m_vpslot[islotInput]; m_vislotPrevChunkMap[islotOutput] = -1; m_vislotNextChunkMap[islotOutput] = -1; Assert(m_fUsedByPosPass); if (m_fUsedByPosPass && GotIndexOffset()) m_vpslot[islotOutput]->SetPosPassIndex(islotOutput - m_cslotPreSeg, m_fInputToPosPass1); } /*---------------------------------------------------------------------------------------------- Return true if there is physical space to add more glyphs, false if the positioning has caused them to use all the alotted space. Recipient is assumed to be output stream from the final pass. @param ptman - table manager, for handling the positioning @param pgg - graphics device @param xsSpaceAllotted - how much space is available @param fWidthIsCharCount - kludge for test procedures @param fIgnoreTrailingWS - true if we are ignoring trailing white-space (currently always true) @param twsh - how the segment is handling trailing white-space @param pxsWidth - return width of stuff so far ----------------------------------------------------------------------------------------------*/ bool GrSlotStream::MoreSpace(GrTableManager * ptman, float xsSpaceAllotted, bool fWidthIsCharCount, bool fIgnoreTrailingWS, TrWsHandling twsh, float * pxsWidth) { Assert(ptman->NumberOfPasses() - 1 == m_ipass); if (fWidthIsCharCount) { // Used in test procedures *pxsWidth = (float)m_islotWritePos; return (m_islotWritePos < xsSpaceAllotted); } else { *pxsWidth = -1; if (!GotIndexOffset()) return true; if (WritePos() <= IndexOffset()) return true; int dislot = MaxClusterSlot(WritePos()); if (dislot == kNegInfinity || dislot > 0) // We're in the middle of processing a cluster--we have to keep processing, // so for now assume there is more space. return true; float xsWidth, xsVisWidth; ptman->CalcPositionsUpTo(m_ipass, NULL, &xsWidth, &xsVisWidth); *pxsWidth = (fIgnoreTrailingWS || twsh == ktwshOnlyWs) ? xsVisWidth : xsWidth; return (*pxsWidth < xsSpaceAllotted); } } /*---------------------------------------------------------------------------------------------- Work backwards from the prevLineBreak position (or if none, the readPos) to find the previous line-break of the given weight or less. Return the position of the inserted line-break slot, or -1 if no appropriate break point was found. Recipient should be the output of the final line break pass, or if none, the output of the glyph-generation pass. @param ptman - table manager, for supplying new slots @param islotPrevBreak - position of previous inserted line break @param fInsertedLB - did we actually insert a line-break glyph before? @param islotStartTry - where to start looking, or -1 for the end of the stream @param lb - (max) break weight to allow @param twsh - how we are handling trailing white-space @param islotMin - first slot that is officially part of the segment (after initial LB) @param plbNextToTry - the best we found ----------------------------------------------------------------------------------------------*/ int GrSlotStream::InsertLineBreak(GrTableManager * ptman, int islotPrevBreak, bool fInsertedLB, int islotStartTry, LineBrk lb, TrWsHandling twsh, int islotMin, LineBrk * plbNextToTry) { EngineState * pengst = ptman->State(); Assert(!fInsertedLB || islotPrevBreak > -1); int ichwSegOffset; int islot; LineBrk lbFound; if (!FindSegmentEnd(ptman, islotStartTry, lb, twsh, islotMin, &islot, &ichwSegOffset, &lbFound, plbNextToTry)) { return -1; // didn't find a legal break } GrSlotState * pslotCopyFeat = pengst->AnAdjacentSlot(m_ipass, islot + 1); Assert(pslotCopyFeat); if (islotPrevBreak > -1 && fInsertedLB) { //GrSlotState * pslotOld = m_vpslot[islotPrevBreak]; m_vpslot.erase(m_vpslot.begin() + islotPrevBreak); m_vislotPrevChunkMap.erase(m_vislotPrevChunkMap.begin() + islotPrevBreak); m_vislotNextChunkMap.erase(m_vislotNextChunkMap.begin() + islotPrevBreak); AdjustPrevStreamNextChunkMap(ptman, islotPrevBreak + 1, -1); } else { m_islotReadPos++; m_islotWritePos++; } GrSlotState * pslotNew; pengst->NewSlot(ptman->LBGlyphID(), pslotCopyFeat, 0, ichwSegOffset, &pslotNew); pslotNew->SetSpecialSlotFlag(kspslLbFinal); pslotNew->SetBreakWeight(lbFound); pslotNew->SetDirectionality(ptman->RightToLeft() ? kdircRlb : kdircLlb); int islotLB = islot + 1; m_vpslot.insert(m_vpslot.begin() + islotLB, pslotNew); // Inserting -1 makes it part of a chunk with the previous char/glyph m_vislotPrevChunkMap.insert(m_vislotPrevChunkMap.begin() + islotLB, -1); m_vislotNextChunkMap.insert(m_vislotNextChunkMap.begin() + islotLB, -1); AdjustPrevStreamNextChunkMap(ptman, islotLB, 1); // We don't need to adjust the following stream because we are going to unwind it anyway. if (m_fUsedByPosPass && GotIndexOffset()) { pslotNew->SetPosPassIndex(islot - m_cslotPreSeg, m_fInputToPosPass1); // Increment the stream index for the following slots. for (int islotTmp = islotLB; islotTmp < ((islotPrevBreak == -1) ? m_islotWritePos : islotPrevBreak + 1); islotTmp++) { SlotAt(islotTmp)->IncPosPassIndex(); } } m_islotSegLim = islotLB + 1; // after the line-break glyph return islotLB; } /*---------------------------------------------------------------------------------------------- Make a segment break that does not correspond to a line break; ie, don't insert a line-break glyph. Work backwards from the prevLineBreak position (or if none, the readPos) to find the previous line-break of the given weight or less. Return the position of the final slot in the segment, or -1 if no appropriate break point was found. Recipient should be the output of the final line break pass, if any, or the glyph-generation pass. @param ptman - table manager, for supplying new slots @param islotPrevBreak - position of previous inserted line break @param fInsertedLB - did we actually insert a line-break glyph previously; currently ignored @param islotStartTry - where to start looking, or -1 for the end of the stream @param lb - (max) break weight to allow @param twsh - how we are handling trailing white-space @param islotMin - first slot that is officially part of the segment (after initial LB) ----------------------------------------------------------------------------------------------*/ int GrSlotStream::MakeSegmentBreak(GrTableManager * ptman, int islotPrevBreak, bool fInsertedLB, int islotStartTry, LineBrk lb, TrWsHandling twsh, int islotMin, LineBrk * plbNextToTry) { int ichwSegOffset; int islot; LineBrk lbFound; if (!FindSegmentEnd(ptman, islotStartTry, lb, twsh, islotMin, &islot, &ichwSegOffset, &lbFound, plbNextToTry)) { return -1; // didn't find a legal break } // Review: do we need to delete any previously-inserted line-break glyph from the stream? // Doesn't seem like this has been a problem so far. m_islotSegLim = islot + 1; // after the final glyph of this segment return islot; } /*---------------------------------------------------------------------------------------------- Work backwards from the prev line break position (or if none, the readPos) to find an earlier line-break of the given weight or less. Recipient should be the output of the final line break pass, if any, or the output of the glyph-generation pass. @param ptman - table manager, for supplying new slots @param islotStartTry - where to start looking, or -1 for the end of the stream @param lb - (max) break weight to allow @param twsh - how we are handling trailing white-space @param islotMin - first slot that is officially part of the segment (after initial LB) @param pislot - slot AFTER which the break will go (not the lim of the segment) @param pichwSegOffset - offset relative to the segment @param plbFound - weight of break to be created @param plbNextToTry - best found, in case we didn't find one at the requested level @return Whether or not an appropriate break was found. ----------------------------------------------------------------------------------------------*/ bool GrSlotStream::FindSegmentEnd(GrTableManager * ptman, int islotStartTry, LineBrk lb, TrWsHandling twsh, int islotMin, int * pislot, int * pichwSegOffset, LineBrk * plbFound, LineBrk * plbNextToTry) { Assert(AssertValid()); Assert(m_ipass == ptman->NumberOfLbPasses()); if (islotStartTry < 0) return false; *pislot = islotStartTry; GrSlotState * pslot = m_vpslot[*pislot]; *plbNextToTry = klbClipBreak; // worst break ptman->State()->SetRemovedTrWhiteSpace(false); while (twsh != ktwshOnlyWs) // in white-space only case, ignore lb values { if (*pislot < islotMin) { return false; } *plbFound = LineBrk(pslot->m_lb); // Sanity check if (abs(*plbFound) > klbClipBreak) { // Assert(false); Warn("Unusually large breakweight"); *plbFound = (LineBrk)((*plbFound < 0) ? -klbClipBreak : klbClipBreak); } *plbNextToTry = (LineBrk)(min(static_cast(*plbNextToTry), abs(*plbFound))); *pichwSegOffset = pslot->SegOffset(); if (int(*plbFound) >= 0 && *plbFound <= lb) break; --(*pislot); if (*pislot < 0) { return false; } pslot = m_vpslot[*pislot]; if (int(*plbFound) <= 0 && ((int)*plbFound * -1) <= lb) { *plbFound = LineBrk(int(*plbFound) * -1); break; } } // Found a good break. if (twsh == ktwshOnlyWs) { // white-space-only segment while (pslot->Directionality() != kdircWhiteSpace) { --(*pislot); if (*pislot < 0) return false; pslot = m_vpslot[*pislot]; *pichwSegOffset = pslot->SegOffset(); } } else if (twsh == ktwshNoWs) { // no trailing white-space: remove it while (pslot->Directionality() == kdircWhiteSpace) { --(*pislot); if (*pislot < 0) return false; pslot = m_vpslot[*pislot]; *pichwSegOffset = pslot->SegOffset(); ptman->State()->SetRemovedTrWhiteSpace(true); } } return true; } /*---------------------------------------------------------------------------------------------- Return true if the stream (which should be the output of the last linebreak pass) has a better break at some earlier point than the one we found. @param islotBreak - position of inserted or final break point in stream, or index of last valid slot ----------------------------------------------------------------------------------------------*/ bool GrSlotStream::HasEarlierBetterBreak(int islotBreak, LineBrk lbFound, gid16 chwLB) { Assert(AssertValid()); int islot = islotBreak; if (SlotAt(islot)->IsFinalLineBreak(chwLB)) islot--; if (lbFound == klbNoBreak) lbFound = (LineBrk)SlotAt(islot)->BreakWeight(); while (islot >= 0) { if (SlotAt(islot)->IsInitialLineBreak(chwLB)) return false; if (SlotAt(islot)->BreakWeight() < lbFound) return true; islot--; } return false; } /*---------------------------------------------------------------------------------------------- Make sure the stream contains a local instance of the given slot, not a copy used by a previous pass. ----------------------------------------------------------------------------------------------*/ void GrSlotStream::EnsureLocalCopy(GrTableManager * ptman, GrSlotState * pslot, GrSlotStream * psstrmIn) { //Assert(m_vpslot[islot + m_cslotPreSeg] == pslot); Assert(pslot->PassModified() <= m_ipass); if (pslot->PassModified() < m_ipass) { int islot = pslot->PosPassIndex(); Assert(SlotAtPosPassIndex(islot) == pslot); GrSlotState * pslotNew; ptman->State()->NewSlotCopy(pslot, m_ipass, &pslotNew); m_vpslot[islot + m_cslotPreSeg] = pslotNew; psstrmIn->ReplaceSlotInReprocessBuffer(pslot, pslotNew); } } /*---------------------------------------------------------------------------------------------- A slot has been replace within the coorsponding output stream. Make sure the reprocess buffer of this stream constains the same slot. ----------------------------------------------------------------------------------------------*/ void GrSlotStream::ReplaceSlotInReprocessBuffer(GrSlotState * pslotOld, GrSlotState * pslotNew) { if (m_islotReprocPos > -1) { for (size_t islot = 0; islot < m_vpslotReproc.size(); islot++) { if (m_vpslotReproc[islot] == pslotOld) m_vpslotReproc[islot] = pslotNew; } } } /*---------------------------------------------------------------------------------------------- Inserting a line break can potentially alter the directionality of preceeding glyphs. Zap the information that has been calculated. @param islotLB - index of the inserted final line-break glyph ----------------------------------------------------------------------------------------------*/ void GrSlotStream::ZapCalculatedDirLevels(int islotLB) { int islot; for (islot = m_islotWritePos; islot-- > islotLB; ) m_vpslot[islot]->ZapDirLevel(); for (islot = islotLB; islot-- > 0; ) { if (StrongDir(m_vpslot[islot]->Directionality())) // We've found a strong direction code; don't need to zap beyond this point. return; m_vpslot[islot]->ZapDirLevel(); } } /*---------------------------------------------------------------------------------------------- Return the slot index of the last slot in the cluster for the just-processed chunk, relative to the last slot in the chunk. Return kNegInfinity if there are not enough slots in the stream to tell. This can happen while we are in the middle of processing a cluster. ----------------------------------------------------------------------------------------------*/ int GrSlotStream::MaxClusterSlot(int islotChunkMin, int islotChunkLim) { Assert(islotChunkLim > islotChunkMin); if (!m_fUsedByPosPass) return 0; // no clusters yet int islotRet = SlotAt(islotChunkLim - 1)->PosPassIndex(); for (int islot = islotChunkMin; islot < islotChunkLim; islot++) { GrSlotState * pslot = SlotAt(islot); if (m_cslotPreSeg == -1) return kNegInfinity; if (!HasSlotAtPosPassIndex(pslot->AttachRootPosPassIndex())) return kNegInfinity; GrSlotState * pslotBase = pslot->Base(this); int dislotOffset = pslotBase->LastLeafOffset(this); if (dislotOffset == kNegInfinity) return kNegInfinity; islotRet = max(islotRet, pslotBase->PosPassIndex() + dislotOffset); } // Make it relative to the last slot in the chunk. islotRet -= SlotAt(islotChunkLim - 1)->PosPassIndex(); return islotRet; } /*---------------------------------------------------------------------------------------------- Return the break weight of the given slot, which should be a line-break. OBSOLETE?? ----------------------------------------------------------------------------------------------*/ LineBrk GrSlotStream::BreakWeightAt(gid16 chwLB, int islot) { GrSlotState * pslot = GetSlotAt(islot); Assert(pslot->IsLineBreak(chwLB)); return LineBrk(pslot->m_lb); } /*---------------------------------------------------------------------------------------------- Append an initial or final line break of the given weight to the stream. @param ptman - table manager, for generating new slot @param lb - break weight for the line-break glyph @param dirc - directionality code for the line-break glyph @param islotLB - where to insert, or -1 if at the end @param fInitial - is this an initial line-break, or a final break? @param ichwSegOffset - offset relative to the beginning of the segment ----------------------------------------------------------------------------------------------*/ void GrSlotStream::AppendLineBreak(GrTableManager * ptman, GrCharStream * pchstrm, LineBrk lb, DirCode dirc, int islotLB, bool fInitial, int ichwSegOffset) { EngineState * pengst = ptman->State(); Assert(AssertValid()); /// Assert(m_islotReprocPos == -1); Assert(islotLB == -1 || fInitial); if (islotLB == -1) islotLB = m_islotWritePos; // int islot = m_islotWritePos; GrSlotState * pslotCopyFeat = pengst->AnAdjacentSlot(m_ipass, islotLB); GrSlotState * pslotNew; if (pslotCopyFeat) pengst->NewSlot(ptman->LBGlyphID(), pslotCopyFeat, 0, ichwSegOffset, &pslotNew); else { GrFeatureValues fval; pchstrm->CurrentFeatures(ptman, &fval); pengst->NewSlot(ptman->LBGlyphID(), fval, 0, ichwSegOffset, -1, &pslotNew); } pslotNew->m_lb = sdata8(lb); pslotNew->SetSpecialSlotFlag(fInitial ? kspslLbInitial : kspslLbFinal); //pslotNew->m_fInitialLB = fInitial; // TODO: remove pslotNew->SetDirectionality(dirc); m_vpslot.insert(m_vpslot.begin() + islotLB, pslotNew); // Make it part of a chunk with previous char/glyph m_vislotPrevChunkMap.insert(m_vislotPrevChunkMap.begin() + islotLB, -1); m_vislotNextChunkMap.insert(m_vislotNextChunkMap.begin() + islotLB, -1); if (m_ipass > 0) AdjustPrevStreamNextChunkMap(ptman, islotLB, 1); m_islotWritePos++; if (m_fUsedByPosPass && GotIndexOffset()) { pslotNew->SetPosPassIndex(islotLB - m_cslotPreSeg, m_fInputToPosPass1); // Increment the stream index for the following slots. for (int islotTmp = islotLB + 1; islotTmp < m_islotWritePos; islotTmp++) { SlotAt(islotTmp)->IncPosPassIndex(); } } if (fInitial) m_islotSegMin = islotLB; // just before the LB else m_islotSegLim = islotLB + 1; // just after the LB } /*---------------------------------------------------------------------------------------------- Return the index of the line break glyph within the specified range, or -1 if none. Recipient is functioning as the output stream. @param chwLB - glyph ID being used for line-break glyphs @param islotMin/Lim - range to search ----------------------------------------------------------------------------------------------*/ int GrSlotStream::FindFinalLineBreak(gid16 chwLB, int islotMin, int islotLim) { for (int islot = islotMin; islot < islotLim; ++islot) { if (m_vpslot[islot]->IsFinalLineBreak(chwLB)) return islot; } return -1; } /*---------------------------------------------------------------------------------------------- A slot has been inserted (nInc == 1) or deleted (nInc == -1). Adjust the next-chunk map of the previous stream to match. ----------------------------------------------------------------------------------------------*/ void GrSlotStream::AdjustPrevStreamNextChunkMap(GrTableManager * ptman, int islotMod, int nInc) { if (m_ipass == 0) return; // no previous stream to adjust int islotTmp = max(islotMod - 5, 0); while (islotTmp > 0 && m_vislotPrevChunkMap[islotTmp] == -1) islotTmp--; ptman->InputStream(m_ipass)->AdjustNextChunkMap( ((islotTmp <= 0) ? 0 : m_vislotPrevChunkMap[islotTmp]), islotMod, nInc); } /*---------------------------------------------------------------------------------------------- An insertion or deletion has occurred at the given location in the following stream (specifically of an line-break glyph). Adjust the next-chunk-map accordingly so the indices still match. ----------------------------------------------------------------------------------------------*/ void GrSlotStream::AdjustNextChunkMap(int islotMin, int islotInsOrDel, int nInc) { for (int islot = islotMin; islot < m_islotWritePos; islot++) { if (m_vislotNextChunkMap[islot] != -1 && m_vislotNextChunkMap[islot] >= islotInsOrDel) m_vislotNextChunkMap[islot] += nInc; } } /*---------------------------------------------------------------------------------------------- Unwind the read position of the stream, so that the following pass will begin reading from the new position. The recipient is functioning as the input stream. ----------------------------------------------------------------------------------------------*/ void GrSlotStream::UnwindInput(int islotNewPos, bool fPreBidiPass) { Assert(m_islotReprocPos == -1); Assert(islotNewPos <= m_islotReadPos); int islot; for (islot = islotNewPos; islot < m_islotReadPos; ++islot) m_vislotNextChunkMap[islot] = -1; m_islotReadPos = islotNewPos; m_islotReadPosMax = m_islotReadPos; Assert(m_islotReadPos <= m_islotWritePos); if (fPreBidiPass) { for (islot = m_islotReadPos; islot < m_islotWritePos; ++islot) SlotAt(islot)->ZapDirLevel(); } } /*---------------------------------------------------------------------------------------------- Unwind the write position of the stream, so that the pass will begin writing at the new position. The recipient is functioning as the output stream ----------------------------------------------------------------------------------------------*/ void GrSlotStream::UnwindOutput(int islotNewPos, bool fOutputOfPosPass) { Assert(islotNewPos <= m_islotWritePos); int islot; for (islot = islotNewPos; islot < m_islotWritePos; ++islot) { m_vislotPrevChunkMap[islot] = -1; if (!fOutputOfPosPass && m_fUsedByPosPass) // This stream is the input to the first positioning pass, ie, the output // of the last sub pass or the bidi pass. Zap the stream indices, // since unwinding this pass could cause them to be invalid due to a later // reordering. SlotAt(islot)->ZapPosPassIndex(); } m_islotWritePos = islotNewPos; m_fFullyWritten = false; if (m_islotSegMin > m_islotWritePos) m_islotSegMin = -1; if (m_islotSegLim > m_islotWritePos) m_islotSegLim = -1; } /*---------------------------------------------------------------------------------------------- We've just created a new chunk, with the recipient as the output stream. The beginning of the chunk is passed as 'islotOutput' and the end of the chunk is assumed to be the write-position of the recipient. Store the mappings into the corresponding input position. @param islotInputMin - beginning of chunk in input stream @param islotOutputMin - beginning of chunk in output stream (recipient) @param islotInputLim - end of chunk in input stream (currently not used) @param fSkipChunkStart - if true, either we have reprocessed part of a previously created chunk, or there was no input consumed; in either case, don't record the beginning of the chunk, just make this chunk be part of the previous @param fBackingUp - this chunk results in the stream position moving backwards, so clear anything we're backing over ----------------------------------------------------------------------------------------------*/ void GrSlotStream::MapInputChunk(int islotInputMin, int islotOutputMin, int islotInputLim, bool fSkipChunkStart, bool fBackingUp) { Assert(AssertValid()); Assert(islotOutputMin >= -1); if (!fSkipChunkStart) { Assert(islotOutputMin >= 0); if (islotOutputMin >= 0) // just in case an invalid value was passed m_vislotPrevChunkMap[islotOutputMin] = islotInputMin; } int islot; for (islot = max(0, islotOutputMin + 1); islot < m_islotWritePos; ++islot) m_vislotPrevChunkMap[islot] = -1; // not a chunk boundary // if (fBackingUp && m_islotWritePos < m_vislotPrevChunkMap.Size()) // m_vislotPrevChunkMap[m_islotWritePos] = -1; if (fBackingUp) for (islot = m_islotWritePos; islot < signed(m_vislotPrevChunkMap.size()); islot++) m_vislotPrevChunkMap[islot] = -1; // m_vislotPrevChunkMap[m_islotWritePos] = islotInputLim; } /*---------------------------------------------------------------------------------------------- Return the length of the final chunk, where the recipient is the input stream. ----------------------------------------------------------------------------------------------*/ int GrSlotStream::LastNextChunkLength() { int cslotRet = 1; for (int islot = m_islotReadPos; islot-- > 0; ) { if (m_vislotNextChunkMap[islot] != -1) return cslotRet; cslotRet++; } return cslotRet + 1; } /*---------------------------------------------------------------------------------------------- We've just created a new chunk, with the recipient as the input stream. The beginning of the chunk is passed as 'islotInputMin' and the end of the chunk is assumed to be the read-position of the recipient. Store the mappings into the corresponding output position. @param islotOutputMin - beginning of chunk in output stream @param islotInputMin - beginning of chunk in input stream (recipient) @param islotOutputLim - end of chunk in output stream (currently not used) @param fSkipChunkStart - if true, either we have reprocessed part of a previously created chunk, or there was no output generated; in either case, don't record the beginning of the chunk, just make this chunk be part of the previous @param fBackingUp - this chunk results in the stream position moving backwards, so clear anything we're backing over ----------------------------------------------------------------------------------------------*/ void GrSlotStream::MapOutputChunk(int islotOutputMin, int islotInputMin, int islotOutputLim, bool fSkipChunkStart, int cslotReprocess, bool fBackingUp) { Assert(AssertValid()); // Note: islotInputMin can be less than -1 if there is a large reprocess buffer near the // beginning of the input stream. In which case, fSkipChunk start should be true and // cslotReprocess large enough to keep any of the stuff below from having any problems. //Assert(islotInputMin >= -1); if (!fSkipChunkStart) { Assert(islotInputMin >= 0); if (islotInputMin >= 0) // just in case an invalid value was passed m_vislotNextChunkMap[islotInputMin] = islotOutputMin; } int islot; for (islot = max(0, islotInputMin + 1 + cslotReprocess); islot < m_islotReadPos; ++islot) m_vislotNextChunkMap[islot] = -1; // not a chunk boundary // if (fBackingUp && m_islotReadPos < m_vislotNextChunkMap.Size()) // m_vislotNextChunkMap[m_islotReadPos] = -1; if (fBackingUp) for (islot = m_islotReadPos; islot < signed(m_vislotNextChunkMap.size()); islot++) m_vislotNextChunkMap[islot] = -1; // m_vislotNextChunkMap[m_islotReadPos] = islotOutputLim; } /*---------------------------------------------------------------------------------------------- Ensure that the chunk maps for a pair of streams match properly. The recipient is the input stream. ----------------------------------------------------------------------------------------------*/ void GrSlotStream::AssertChunkMapsValid(GrSlotStream * psstrmOut) { #ifdef _DEBUG GrSlotStream * psstrmIn = this; Assert(m_ipass == 0 || (m_ipass + 1 == psstrmOut->m_ipass)); int islotMapNext; int islotMapPrev; int islot; for (islot = 0; islot < m_islotReadPos; ++islot) { islotMapNext = psstrmIn->ChunkInNext(islot); Assert(islotMapNext != -2); if (islotMapNext != -1) { islotMapPrev = psstrmOut->ChunkInPrev(islotMapNext); if (islotMapPrev == -2) { Assert(islot == psstrmOut->WritePos()); } else { Assert(islot == islotMapPrev); } } } for (islot = 0; islot < psstrmOut->m_islotWritePos; ++islot) { islotMapPrev = psstrmOut->ChunkInPrev(islot); Assert(islotMapPrev != -2); if (islotMapPrev != -1) { islotMapNext = psstrmIn->ChunkInNext(islotMapPrev); if (islotMapNext == -2) { Assert(islot == psstrmIn->ReadPos()); } else { Assert(islot == islotMapNext); } } } #endif // _DEBUG } /*---------------------------------------------------------------------------------------------- Ensure that corresponding items in the streams of a positioning pass have matching stream indices. The recipient is the output stream. ----------------------------------------------------------------------------------------------*/ void GrSlotStream::AssertStreamIndicesValid(GrSlotStream * psstrmIn) { #ifdef _DEBUG if (!GotIndexOffset()) return; Assert(m_fUsedByPosPass); for (int islot = 0; islot < WritePos(); islot++) { GrSlotState * pslotOut = SlotAt(islot); GrSlotState * pslotIn = psstrmIn->SlotAt(islot + psstrmIn->IndexOffset() - IndexOffset()); Assert(pslotOut->PosPassIndex() == pslotIn->PosPassIndex()); Assert(pslotOut->HasAsPreviousState(pslotIn)); } #endif // _DEBUG } /*---------------------------------------------------------------------------------------------- Ensure that the roots of all attachments made in this chunk are present in the output stream. (Currently the compiler ensures this by making it an error to write rules that don't do this.) ----------------------------------------------------------------------------------------------*/ void GrSlotStream::AssertAttachmentsInOutput(int islotMin, int islotLim) { #ifdef _DEBUG for (int islot = islotMin; islot < islotLim; islot++) { GrSlotState * pslotOut = SlotAt(islot); int dislot = pslotOut->AttachTo(); Assert(islotMin <= islot + dislot); Assert(islot + dislot < islotLim); } #endif // _DEBUG } /*---------------------------------------------------------------------------------------------- Answer the slot index corresponding to the start of the chunk (as mapped to the following stream). ENHANCE: make more robust. ----------------------------------------------------------------------------------------------*/ int GrSlotStream::ChunkInNextMin(int islot) { int islotRet = islot; while (m_vislotNextChunkMap[islotRet] == -1 && islotRet > 0) --islotRet; return islotRet; } /*---------------------------------------------------------------------------------------------- Answer the slot index corresponding to the end of the chunk (as mapped to the following stream). ENHANCE: make more robust. ----------------------------------------------------------------------------------------------*/ int GrSlotStream::ChunkInNextLim(int islot) { int islotRet = islot + 1; if (islotRet == m_islotReadPos) return islot; while (m_vislotNextChunkMap[islotRet] == -1 && islotRet < m_islotReadPos) { ++islotRet; Assert(islotRet < signed(m_vislotNextChunkMap.size())); } return islotRet; } /*---------------------------------------------------------------------------------------------- Skip over the given number slots. This is used to resync the streams when we are restarting a new segment. Specifically the output stream informs the input stream of the number of slots to skip to get the boundary for a chunk that the output finds interesting. ----------------------------------------------------------------------------------------------*/ void GrSlotStream::ResyncSkip(int cslot) { Assert(AssertValid()); m_islotReadPos += cslot; m_cslotSkippedForResync = cslot; // Should never skip past the beginning of the line. Assert(m_islotSegMin == -1 || m_islotReadPos <= m_islotSegMin); } /*---------------------------------------------------------------------------------------------- Record the number of slots in the stream that are previous to the official start of the segment. ----------------------------------------------------------------------------------------------*/ void GrSlotStream::CalcIndexOffset(GrTableManager * ptman) { if (GotIndexOffset()) return; // already figured it if (m_islotSegMin > -1) { m_cslotPreSeg = m_islotSegMin; if (m_fUsedByPosPass) { for (int islotPre = 0; islotPre < WritePos(); islotPre++) { SlotAt(islotPre)->SetPosPassIndex(islotPre - m_cslotPreSeg, m_fInputToPosPass1); } } } // else can't figure it out yet // Old code: #if 0 gid16 chwLB = ptman->LBGlyphID(); for (int islot = 0; islot < WritePos(); islot++) { if (SlotAt(islot)->IsLineBreak(chwLB)) { if (SlotAt(islot)->IsInitialLineBreak(chwLB)) { m_cslotPreSeg = islot; } else { // Otherwise, we hit the final line break, which is kind of strange, because // in this case there is no initial line break, and we should have set // m_cslotPreSeg to 0 immediately when it was initialized. Assert(false); m_cslotPreSeg = 0; } if (m_fUsedByPosPass) { for (int islotPre = 0; islotPre < WritePos(); islotPre++) { SlotAt(islotPre)->SetPosPassIndex(islotPre - m_cslotPreSeg, m_fInputToPosPass1); } } return; } } // No line breaks yet, so no way to tell. #endif } /*---------------------------------------------------------------------------------------------- Set the positions in the streams for the next rule. The recipient is the output stream. If we are moving backwards, copy part of the output stream back to (a temporary buffer in) the input stream, so that we can reprocess it. @param cslotArg - how far to skip forward or back; most commonly zero ----------------------------------------------------------------------------------------------*/ void GrSlotStream::SetPosForNextRule(int cslotArg, GrSlotStream * psstrmIn, bool fOutputOfPosPass) { Assert(AssertValid()); int cslot = cslotArg; // Can't reprocess what has already been read by the next pass. MaxBackup should allow // the flexibility needed, but if not, we fix it here. if (m_islotWritePos + cslot < m_islotReadPos) cslot = m_islotReadPos - m_islotWritePos; Assert(psstrmIn->m_ipass == m_ipass-1); if (cslot >= 0) { // Skipping forward. Assert(cslot <= psstrmIn->SlotsPendingInContext()); for (int islot = 0; islot < cslot; islot++) CopyOneSlotFrom(psstrmIn); } else { // Reprocessing. // Save corresponding positions before doing the back-up. int islotReadPosInSave = psstrmIn->ReadPosForNextGet(); int islotWritePosOutSave = this->WritePos(); std::vector vpslotTmp; int islot; if (psstrmIn->SlotsToReprocess() > 0) { // Stick any slots still to reprocess in a temporary buffer. for (islot = psstrmIn->m_islotReprocPos; islot < signed(psstrmIn->m_vpslotReproc.size()); islot++) { vpslotTmp.push_back(psstrmIn->m_vpslotReproc[islot]); } } psstrmIn->ClearReprocBuffer(); psstrmIn->m_islotReprocLim = psstrmIn->m_islotReadPos; for (islot = cslot; islot < 0; islot++) psstrmIn->m_vpslotReproc.push_back(m_vpslot[m_islotWritePos+islot]); for (islot = 0; islot < signed(vpslotTmp.size()); islot++) psstrmIn->m_vpslotReproc.push_back(vpslotTmp[islot]); psstrmIn->m_islotReprocPos = 0; if (!fOutputOfPosPass && m_fUsedByPosPass) { // Last substitution pass; zap the stream indices, since they may // now be invalid. for (islot = 0; islot < signed(psstrmIn->m_vpslotReproc.size()); islot++) psstrmIn->m_vpslotReproc[islot]->ZapPosPassIndex(); } // If either the min or lim is in the middle of the reprocess buffer, // adjust as necessary to match where it is in the output stream. int islotSegMinIn = psstrmIn->SegMin(); if (islotSegMinIn > -1 && psstrmIn->ReadPosForNextGet() <= islotSegMinIn && islotSegMinIn < islotReadPosInSave) { Assert(this->SegMin() > -1); int dislotIn = islotReadPosInSave - islotSegMinIn; int dislotOut = islotWritePosOutSave - SegMin(); psstrmIn->SetSegMin(islotSegMinIn + dislotIn - dislotOut, true); if (psstrmIn->m_cslotPreSeg > psstrmIn->SegMin()) psstrmIn->m_cslotPreSeg = -1; } int islotSegLimIn = psstrmIn->SegLimIfKnown(); if (islotSegLimIn > -1 && psstrmIn->ReadPosForNextGet() <= islotSegLimIn && islotSegLimIn < islotReadPosInSave) { int islotSegLimOut = this->SegLimIfKnown(); Assert(islotSegLimOut > -1); int dislotIn = islotReadPosInSave - islotSegLimIn; int dislotOut = islotWritePosOutSave - islotSegLimOut; psstrmIn->SetSegLim(islotSegLimIn + dislotIn - dislotOut); } m_islotWritePos += cslot; if (m_islotSegMin >= m_islotWritePos) m_islotSegMin = -1; if (m_islotSegLim >= m_islotWritePos) m_islotSegLim = -1; } } /*---------------------------------------------------------------------------------------------- If a rule was run over a LB glyph, set the appropriate flag in the table manager. CURRENTLY NOT USED ----------------------------------------------------------------------------------------------*/ void GrSlotStream::SetLBContextFlag(GrTableManager * ptman, int islotStart) { gid16 chwLB = ptman->LBGlyphID(); for (int islot = islotStart; islot < WritePos(); islot++) { GrSlotState * pslot = SlotAt(islot); if (pslot->IsInitialLineBreak(chwLB)) ptman->State()->SetStartLineContext(true); if (pslot->IsFinalLineBreak(chwLB)) ptman->State()->SetEndLineContext(true); } } /*---------------------------------------------------------------------------------------------- Return the number of slots that have been output by the current pass but must be reprocessed by this same pass. The recipient is serving as the input stream. ----------------------------------------------------------------------------------------------*/ int GrSlotStream::SlotsToReprocess() { Assert(AssertValid()); if (m_islotReprocPos == -1) return 0; return (m_vpslotReproc.size() - m_islotReprocPos); } /*---------------------------------------------------------------------------------------------- Get the input at the given slot, that is, the original input, ignoring the issue of reprocessing. ----------------------------------------------------------------------------------------------*/ GrSlotState * GrSlotStream::GetSlotAt(int islot) { return m_vpslot[islot]; } /*---------------------------------------------------------------------------------------------- Return the "current" input item from the rule's perspective, ie, the last slot read. So dislotOffset = 0 means not the slot at the read position but one slot earlier. If dislotOffset is less the the original read position that was in effect at the beginning of the rule, we need to read from the output stream, which has the up-to-date values. See the class comment for more details. @param dislotOffset - offset from current stream position @param fNullOkay - true if it's okay to return NULL in the situation where we're asking for something before the beginning of the stream ----------------------------------------------------------------------------------------------*/ GrSlotState * GrSlotStream::RuleInputSlot(int dislotOffset, GrSlotStream * psstrmOutput, bool fNullOkay) { Assert(m_islotRuleStartRead <= m_islotReadPos); if (dislotOffset > 0) return Peek(dislotOffset - 1); int cslotOffsetBack = dislotOffset * -1; if (m_islotReprocLim > -1) { // There is a reprocess buffer. // Position when starting the rule should never be earlier than the // beginning of the reprocess buffer. Assert(m_islotRuleStartRead >= m_islotReprocLim - signed(m_vpslotReproc.size())); // number of items following the reprocess buffer: int cslotPostReproc = m_islotReadPos - m_islotReprocLim; if (cslotOffsetBack >= cslotPostReproc) { // number of items in the reprocess buffer that were valid when the rule // was started: int cslotValidReproc = m_islotReprocLim - m_islotRuleStartRead; if (cslotOffsetBack >= cslotPostReproc + cslotValidReproc) { // Read from the output stream. int dislotTmp = dislotOffset - 1 + cslotPostReproc + cslotValidReproc; Assert(dislotTmp < 0); return psstrmOutput->PeekBack(dislotTmp); } else { if (m_islotReprocPos > -1) { // Current read pos is inside reprocess buffer. Assert(cslotPostReproc == 0); int islotStartReadReprocBuf = m_vpslotReproc.size() - cslotValidReproc; Assert(islotStartReadReprocBuf >= 0); int islotInReprocBuf = m_islotReprocPos - cslotOffsetBack - 1; if (islotInReprocBuf < islotStartReadReprocBuf) { // Return a slot from before the reprocess buffer, from the // output stream (which is serving as our input stream for precontext // items). return psstrmOutput->PeekBack(islotInReprocBuf - islotStartReadReprocBuf); } else if (islotInReprocBuf < 0) { Assert(false); //int islotReprocMin = m_islotReprocLim - m_vpslotReproc.Size(); //return m_vpslot[islotReprocMin + islotInReprocBuf]; } else // Read from the reprocess buffer. return m_vpslotReproc[islotInReprocBuf]; } else // Looking backwards into the reprocess buffer. return m_vpslotReproc[ m_vpslotReproc.size() - cslotOffsetBack + cslotPostReproc - 1]; } } } // There is no reprocess buffer, or it is not a factor. if (m_islotReadPos + dislotOffset - 1 < m_islotRuleStartRead) { // Read from the output stream. return psstrmOutput->PeekBack(m_islotReadPos + dislotOffset - 1 - m_islotRuleStartRead, fNullOkay); } else { // Read normally from the input stream. return m_vpslot[m_islotReadPos - cslotOffsetBack - 1]; } } /*---------------------------------------------------------------------------------------------- Return the "current" output item from the rule's perspective, ie, the last slot written. So dislotOffset = 0 means not the slot at the write position but one slot earlier. ----------------------------------------------------------------------------------------------*/ GrSlotState * GrSlotStream::RuleOutputSlot(int dislotOffset) { return m_vpslot[m_islotWritePos - 1 + dislotOffset]; } /*---------------------------------------------------------------------------------------------- For any slot in the final output that has no associations, set the "before" pointer to the following slot and the "after" pointer to the preceding slot. The idea of this is that it will make it impossible to select a the glyph separately. Note that if the slot is the first on the line, it will not be associated with a slot in the previous segment. @param chwLB - the glyph IB for the line-break glyphs ----------------------------------------------------------------------------------------------*/ void GrSlotStream::SetNeutralAssociations(gid16 chwLB) { for (int islot = 0; islot < m_islotWritePos; ++islot) { GrSlotState * pslot = SlotAt(islot); if (pslot->PassModified() > 0 && (pslot->BeforeAssoc() == kPosInfinity || pslot->AfterAssoc() == kNegInfinity)) { pslot->CleanUpAssocs(); if (pslot->BeforeAssoc() != kPosInfinity && pslot->AfterAssoc() != kNegInfinity) continue; Assert(pslot->BeforeAssoc() == kPosInfinity && pslot->AfterAssoc() == kNegInfinity); GrSlotState * pslotBefore = FindAssociatedSlot(islot, 1, chwLB); GrSlotState * pslotAfter = FindAssociatedSlot(islot, -1, chwLB); if (pslotBefore && pslotAfter) pslot->Associate(pslotBefore, pslotAfter); else if (pslotBefore) pslot->Associate(pslotBefore); else if (pslotAfter) pslot->Associate(pslotAfter); else // Weird, but can happen with an empty segment. Warn("No assocations"); // Assert(pslot->m_vpslotAssoc.Size() > 0); pslot->m_fNeutralAssocs = true; } } } /*---------------------------------------------------------------------------------------------- Find the next or previous slot in the final output that has explicit associations, or return NULL if none. @param islot - starting slot @param nInc - +1 if we want to find the next slot, -1 if we want the previous slot @param chwLB - the glyph IB for the line-break glyphs ----------------------------------------------------------------------------------------------*/ GrSlotState * GrSlotStream::FindAssociatedSlot(int islot, int nInc, gid16 chwLB) { int islotNext = islot + nInc; while (islotNext >= 0 && islotNext < m_islotWritePos) { GrSlotState * pslotRet = SlotAt(islotNext); if (pslotRet->IsLineBreak(chwLB)) { } else if (pslotRet->PassModified() == 0 || (pslotRet->m_vpslotAssoc.size() > 0 && pslotRet->m_fNeutralAssocs == false)) { return pslotRet; } islotNext += nInc; } return NULL; } } // namespace gr