summaryrefslogtreecommitdiff
path: root/Build/source/libs/graphite/src/segment/GrSlotStream.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/libs/graphite/src/segment/GrSlotStream.cpp')
-rw-r--r--Build/source/libs/graphite/src/segment/GrSlotStream.cpp2368
1 files changed, 2368 insertions, 0 deletions
diff --git a/Build/source/libs/graphite/src/segment/GrSlotStream.cpp b/Build/source/libs/graphite/src/segment/GrSlotStream.cpp
new file mode 100644
index 00000000000..db6a5c8af7e
--- /dev/null
+++ b/Build/source/libs/graphite/src/segment/GrSlotStream.cpp
@@ -0,0 +1,2368 @@
+/*--------------------------------------------------------------------*//*: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<int> & vislotStarts, std::vector<int> & vislotStops)
+{
+ Assert(m_islotReadPos <= islotStart);
+ Assert(islotStart < m_islotWritePos);
+
+ std::vector<int> vislotStartStack; // indices of starts of ranges to reverse
+
+ // Stack corresponding to embedding and override markers:
+ std::vector<DirCode> vdircMarkerStack;
+ std::vector<DirCode> vdircOverrideStack;
+ std::vector<int> 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<int> 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<int>(*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<GrSlotState*> 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