summaryrefslogtreecommitdiff
path: root/Build/source/libs/graphite/src/segment/Segment.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'Build/source/libs/graphite/src/segment/Segment.cpp')
-rw-r--r--Build/source/libs/graphite/src/segment/Segment.cpp3547
1 files changed, 3547 insertions, 0 deletions
diff --git a/Build/source/libs/graphite/src/segment/Segment.cpp b/Build/source/libs/graphite/src/segment/Segment.cpp
new file mode 100644
index 00000000000..b936b037a64
--- /dev/null
+++ b/Build/source/libs/graphite/src/segment/Segment.cpp
@@ -0,0 +1,3547 @@
+/*--------------------------------------------------------------------*//*: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: Segment.cpp
+Responsibility: Sharon Correll
+Last reviewed: Not yet.
+
+Description:
+ Implements a Graphite segment--a range of rendered text in one writing system, that can be
+ rendered with a single font, and that fits on a single line.
+-------------------------------------------------------------------------------*//*:End Ignore*/
+
+//:>********************************************************************************************
+//:> Include files
+//:>********************************************************************************************
+#include "Main.h"
+#ifdef _MSC_VER
+#pragma hdrstop
+#endif
+// any other headers (not precompiled)
+#include <math.h>
+#ifndef _WIN32
+#include <stdlib.h>
+#endif
+
+#undef THIS_FILE
+DEFINE_THIS_FILE
+
+//:>********************************************************************************************
+//:> Forward declarations
+//:>********************************************************************************************
+
+//:>********************************************************************************************
+//:> Local Constants and static variables
+//:>********************************************************************************************
+#if 0 // TODO remove
+namespace
+{
+
+static bool g_fDrawing;
+
+} // namespace
+#endif
+
+
+static int g_csegTotal = 0;
+
+namespace gr
+{
+
+//:>********************************************************************************************
+//:> Segment methods
+//:>********************************************************************************************
+
+
+int Segment::GetSegmentCount() /// TEMP
+{
+ return g_csegTotal;
+}
+
+
+/*----------------------------------------------------------------------------------------------
+ Constructor: basic initialization.
+----------------------------------------------------------------------------------------------*/
+Segment::Segment() :
+ m_pgts(NULL),
+ m_pfont(NULL),
+ m_preneng(NULL),
+ m_fErroneous(false),
+ m_pgjus(NULL),
+ m_prgbNextSegDat(NULL),
+ m_cbNextSegDat(0),
+ m_prgInitDat(NULL),
+ m_cbInitDat(0),
+// m_fUseSepBase(0),
+ //m_psegAltEndLine(NULL),
+ m_prgisloutBefore(NULL),
+ m_prgisloutAfter(NULL),
+ m_prgpvisloutAssocs(NULL),
+ m_prgisloutLigature(NULL),
+ m_prgiComponent(NULL),
+// m_psstrm(NULL),
+ m_cslout(0),
+ m_prgslout(NULL),
+ m_prgnSlotVarLenBuf(NULL),
+// m_cnUserDefn(0),
+ m_cnCompPerLig(0),
+// m_cnFeat(0),
+ m_cginf(0),
+ m_prgginf(NULL)
+{
+ m_cref = 1;
+
+ g_csegTotal++;
+
+// m_stuFaceName.erase();
+// m_stuBaseFaceName.erase();
+ m_vnSkipOffsets.clear();
+}
+
+/*----------------------------------------------------------------------------------------------
+ Reference counting.
+----------------------------------------------------------------------------------------------*/
+
+long Segment::IncRefCount(void)
+{
+ AssertPtr(this);
+ return InterlockedIncrement(&m_cref);
+}
+
+long Segment::DecRefCount(void)
+{
+ AssertPtr(this);
+ long cref = InterlockedDecrement(&m_cref);
+ if (cref == 0) {
+ m_cref = 1;
+ delete this;
+ }
+ return cref;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Constructor to fit as much of the text on the line as possible,
+ finding a reasonable break point if necessary.
+----------------------------------------------------------------------------------------------*/
+LineFillSegment::LineFillSegment(
+ Font * pfont,
+ ITextSource * pts,
+ LayoutEnvironment * playoutArg,
+ toffset ichStart,
+ toffset ichStop,
+ float xsMaxWidth,
+ bool fBacktracking)
+ : Segment()
+{
+ if (!pfont)
+ throw;
+ if (!pts)
+ throw;
+
+ LayoutEnvironment layoutDefault;
+ LayoutEnvironment * playout = (!playoutArg) ? &layoutDefault : playoutArg;
+
+ pfont->RenderLineFillSegment(this, pts, *playout, ichStart, ichStop, xsMaxWidth,
+ fBacktracking);
+}
+
+/*----------------------------------------------------------------------------------------------
+ Constructor to create a segment representing the given range of characters.
+----------------------------------------------------------------------------------------------*/
+RangeSegment::RangeSegment(
+ Font * pfont,
+ ITextSource * pts,
+ LayoutEnvironment * playoutArg,
+ toffset ichStart,
+ toffset ichStop,
+ Segment * psegInitLike)
+ : Segment()
+{
+ if (!pfont)
+ throw;
+ if (!pts)
+ throw;
+
+ LayoutEnvironment layoutDefault;
+ LayoutEnvironment * playout = (!playoutArg) ? &layoutDefault : playoutArg;
+ playout->setSegmentForInit(psegInitLike);
+
+ pfont->RenderRangeSegment(this, pts, *playout, ichStart, ichStop);
+
+ playout->setSegmentForInit(NULL);
+}
+
+/*----------------------------------------------------------------------------------------------
+ Constructor to create a justified segment. Private.
+----------------------------------------------------------------------------------------------*/
+JustSegmentAux::JustSegmentAux(
+ Font * pfont,
+ ITextSource * pts,
+ LayoutEnvironment * playoutArg,
+ toffset ichStart,
+ toffset ichStop,
+ float xsNaturalWidth,
+ float xsJustifiedWidth,
+ Segment * psegInitLike)
+ : Segment()
+{
+ if (!pfont)
+ throw;
+ if (!pts)
+ throw;
+
+ LayoutEnvironment layoutDefault;
+ LayoutEnvironment * playout = (!playoutArg) ? &layoutDefault : playoutArg;
+ playout->setSegmentForInit(psegInitLike);
+
+ pfont->RenderJustifiedSegment(this, pts, *playout, ichStart, ichStop,
+ xsNaturalWidth, xsJustifiedWidth);
+
+ playout->setSegmentForInit(NULL);
+}
+
+//Segment::Segment(ITextSource * pgts, int ichwMin, int ichwLim,
+// LineBrk lbStart, LineBrk lbEnd,
+// bool fStartLine, bool fEndLine, bool fWsRtl)
+//{
+// m_cref = 1;
+// Initialize(pgts, ichwMin, ichwLim, lbStart, lbEnd, fStartLine, fEndLine, fWsRtl);
+//}
+
+void Segment::Initialize(ITextSource * pgts, int ichwMin, int ichwLim,
+ LineBrk lbStart, LineBrk lbEnd, SegEnd est,
+ bool fStartLine, bool fEndLine, bool fWsRtl)
+{
+ AssertPtrN(pgts);
+ Assert(ichwMin >= 0);
+ Assert(ichwLim >= 0);
+ Assert(ichwMin <= ichwLim);
+
+ //pgts->TextSrcObject(&m_pgts); // create a permanent wrapper, if necessary
+ // m_pgts->AddRef(); no, smart pointer
+ m_pgts = pgts;
+
+ m_ichwMin = ichwMin;
+ m_dichwLim = ichwLim - ichwMin;
+ m_lbStart = lbStart;
+ m_lbEnd = lbEnd;
+ m_est = est;
+ m_dxsStretch = 0;
+ m_dxsWidth = -1;
+ m_dysAscent = -1; // to properly init, ComputeDimensions must be called
+ m_dysHeight = -1;
+ m_dysXAscent = -1;
+ m_dysXDescent = -1;
+ m_dysAscentOverhang = -1;
+ m_dysDescentOverhang = -1;
+ m_dxsLeftOverhang = -1;
+ m_dxsRightOverhang = -1;
+ m_dysOffset = 0;
+ m_fStartLine = fStartLine;
+ m_fEndLine = fEndLine;
+ m_fWsRtl = fWsRtl;
+
+ m_dxsVisibleWidth = -1;
+ m_dxsTotalWidth = -1;
+
+// m_psstrm = NULL;
+ m_prgslout = NULL;
+ m_prgnSlotVarLenBuf = NULL;
+ m_prgisloutBefore = NULL;
+ m_prgisloutAfter = NULL;
+ m_prgpvisloutAssocs = NULL;
+ m_prgisloutLigature = NULL;
+ m_prgiComponent = NULL;
+
+ m_prgbNextSegDat = NULL;
+ m_cbNextSegDat = 0;
+
+ //m_psegAltEndLine = NULL;
+
+// m_stuFaceName.erase();
+// m_stuBaseFaceName.erase();
+// m_fUseSepBase = false;
+
+// m_psegPrev = NULL; // obsolete
+// m_psegNext = NULL;
+
+ //InitializePlatform();
+}
+
+/*----------------------------------------------------------------------------------------------
+ Destructor.
+----------------------------------------------------------------------------------------------*/
+Segment::~Segment()
+{
+ g_csegTotal--;
+
+ DestroyContents();
+}
+
+void Segment::DestroyContents()
+{
+ delete m_pfont;
+ m_pfont = NULL;
+
+ //if (m_pgts)
+ // // A specially-created wrapper object has to be deleted.
+ // m_pgts->DeleteTextSrcPtr();
+ m_pgts = NULL;
+
+ //if (m_pgjus)
+ // // A specially-created wrapper object has to be deleted.
+ // m_pgjus->DeleteJustifierPtr();
+ m_pgjus = NULL;
+
+ //if (m_psegAltEndLine)
+ //{
+ // if (m_psegAltEndLine->m_psegAltEndLine == this)
+ // m_psegAltEndLine->m_psegAltEndLine = NULL;
+ // delete m_psegAltEndLine;
+ //}
+
+ //for (int iginf = 0; iginf < m_cginf; iginf++) // DELETE
+ // delete m_prgginf[iginf].components;
+
+ Assert((m_prgslout && m_prgnSlotVarLenBuf) || (!m_prgslout && !m_prgnSlotVarLenBuf));
+ delete[] m_prgslout;
+ delete[] m_prgnSlotVarLenBuf;
+ delete[] m_prgisloutBefore;
+ delete[] m_prgisloutAfter;
+ for (int ichw = 0; ichw < m_ichwAssocsLim - m_ichwAssocsMin; ichw++)
+ {
+ if (m_prgpvisloutAssocs && m_prgpvisloutAssocs[ichw])
+ delete m_prgpvisloutAssocs[ichw];
+ }
+ delete[] m_prgpvisloutAssocs;
+ delete[] m_prgisloutLigature;
+ delete[] m_prgiComponent;
+ delete[] m_prgbNextSegDat;
+ delete[] m_prgInitDat;
+ delete[] m_prgginf;
+ m_cginf = 0;
+
+ //DestroyContentsPlatform();
+
+ // ReleaseObj(m_pgts); automatic because smart pointer
+}
+
+/*----------------------------------------------------------------------------------------------
+ Factory method to create a new version of an existing segment with different
+ line-boundary flags.
+----------------------------------------------------------------------------------------------*/
+Segment * Segment::LineContextSegment(Segment & seg, bool fStartLine, bool fEndLine)
+{
+ if (!seg.hasLineBoundaryContext())
+ {
+ // Make an identical segment and just change the flags.
+ Segment * psegNew = new Segment(seg);
+ psegNew->InitLineContextSegment(fStartLine, fEndLine);
+ return psegNew;
+ }
+ else
+ {
+ // Rerun the processing.
+ LayoutEnvironment layout = seg.Layout();
+ layout.setStartOfLine(fStartLine);
+ layout.setEndOfLine(fEndLine);
+ ITextSource & gts = seg.getText();
+ Font & font = seg.getFont();
+ return new RangeSegment(&font, &gts, &layout,
+ seg.startCharacter(), seg.stopCharacter(), &seg);
+ }
+}
+
+void Segment::InitLineContextSegment(bool fStartLine, bool fEndLine)
+{
+ // For RTL end-of-line segments, trailing white space hangs off the left of
+ // the origin of the segment. So when changing that flag
+ // shift appropriately.
+ bool fShift = ((m_nDirDepth % 2) && (m_fEndLine != fEndLine));
+
+ m_fStartLine = fStartLine;
+ m_fEndLine = fEndLine;
+
+ m_layout.setStartOfLine(fStartLine);
+ m_layout.setEndOfLine(fEndLine);
+
+ if (fShift)
+ {
+ (m_fEndLine) ?
+ ShiftGlyphs(m_dxsVisibleWidth - m_dxsTotalWidth) : // shift left
+ ShiftGlyphs(m_dxsTotalWidth - m_dxsVisibleWidth); // shift right
+ }
+
+ m_dxsWidth = -1;
+}
+
+/*----------------------------------------------------------------------------------------------
+ A factory method to create a justified version of an existing segment.
+----------------------------------------------------------------------------------------------*/
+Segment * Segment::JustifiedSegment(Segment & seg, float xsNewWidth)
+{
+ LayoutEnvironment layout(seg.Layout());
+ ITextSource & gts = seg.getText();
+
+ // Why do we have to do this?
+ layout.setJustifier(seg.Justifier());
+ Font & font = seg.getFont();
+ return new JustSegmentAux(&font, &gts, &layout,
+ seg.startCharacter(), seg.stopCharacter(),
+ seg.advanceWidth(), xsNewWidth, &seg);
+}
+
+/*----------------------------------------------------------------------------------------------
+ A factory method to create a new version of an existing trailing white-space segment
+ with a different direction.
+----------------------------------------------------------------------------------------------*/
+Segment * Segment::WhiteSpaceSegment(Segment & seg, int nNewDepth)
+{
+ Segment * psegNew = new Segment(seg);
+ psegNew->InitWhiteSpaceSegment(nNewDepth);
+ return psegNew;
+}
+
+void Segment::InitWhiteSpaceSegment(int nNewDepth)
+{
+ if (nNewDepth == m_nDirDepth)
+ {
+ return;
+ }
+ else if ((nNewDepth % 2) == (m_nDirDepth % 2))
+ {
+ // Same direction, not much to change.
+ m_nDirDepth = nNewDepth;
+ return;
+ }
+ else if (m_twsh != ktwshOnlyWs)
+ return; // couldn't change it, oh, well
+
+ // Otherwise, do the hard stuff: reverse the positions of the glyphs.
+ for (int islout = 0; islout < m_cslout; islout++)
+ {
+ OutputSlot(islout)->ShiftForDirDepthChange(m_dxsTotalWidth);
+ }
+ m_nDirDepth = nNewDepth;
+
+ //SetDirectionDepthPlatform(nNewDepth);
+}
+
+/*----------------------------------------------------------------------------------------------
+ Basic copy method.
+----------------------------------------------------------------------------------------------*/
+Segment::Segment(Segment & seg)
+{
+ int islout;
+
+ m_pgts = seg.m_pgts;
+ m_dichwLim = seg.m_dichwLim;
+ m_ichwMin = seg.m_ichwMin;
+ m_pfont = seg.m_pfont->copyThis();
+ m_preneng = seg.m_preneng;
+ m_fErroneous = seg.m_fErroneous;
+ m_pgjus = seg.m_pgjus;
+ m_fWsRtl = seg.m_fWsRtl;
+ m_fParaRtl = seg.m_fParaRtl;
+ m_twsh = seg.m_twsh;
+ m_nDirDepth = seg.m_nDirDepth;
+ m_cbNextSegDat = seg.m_cbNextSegDat;
+ m_prgbNextSegDat = new byte[m_cbNextSegDat];
+ std::copy(seg.m_prgbNextSegDat, seg.m_prgbNextSegDat + m_cbNextSegDat, m_prgbNextSegDat);
+// m_psegPrev = seg.m_psegPrev;
+// m_psegNext = seg.m_psegNext;
+// m_stuFaceName = seg.m_stuFaceName;
+// m_stuBaseFaceName = seg.m_stuBaseFaceName;
+// m_fUseSepBase = seg.m_fUseSepBase;
+// m_pixHeight = seg.m_pixHeight;
+// m_fBold = seg.m_fBold;
+// m_fItalic = seg.m_fItalic;
+ m_lbStart = seg.m_lbStart;
+ m_lbEnd = seg.m_lbEnd;
+ m_fStartLine = seg.m_fStartLine;
+ m_fEndLine = seg.m_fEndLine;
+ m_est = seg.m_est;
+ m_mFontEmUnits = seg.m_mFontEmUnits;
+ m_dysFontAscent = seg.m_dysFontAscent;
+ m_dysFontDescent = seg.m_dysFontDescent;
+ m_xysEmSquare = seg.m_xysEmSquare;
+// m_xsDPI = seg.m_xsDPI;
+// m_ysDPI = seg.m_ysDPI;
+ m_dxsStretch = seg.m_dxsStretch;
+ m_dxsWidth = seg.m_dxsWidth;
+ m_dysHeight = seg.m_dysHeight;
+ m_dysAscent = seg.m_dysAscent;
+ m_dysXAscent = seg.m_dysXAscent;
+ m_dysXDescent = seg.m_dysXDescent;
+ m_dysAscentOverhang = seg.m_dysAscentOverhang;
+ m_dysDescentOverhang = seg.m_dysDescentOverhang;
+ m_dxsLeftOverhang = seg.m_dxsLeftOverhang;
+ m_dxsRightOverhang = seg.m_dxsRightOverhang;
+ m_dxsVisibleWidth = seg.m_dxsVisibleWidth;
+ m_dxsTotalWidth = seg.m_dxsTotalWidth;
+ m_isloutVisLim = seg.m_isloutVisLim;
+ m_dysOffset = seg.m_dysOffset;
+
+ m_ichwAssocsMin = seg.m_ichwAssocsMin;
+ m_ichwAssocsLim = seg.m_ichwAssocsLim;
+
+ int dichw = m_ichwAssocsLim - m_ichwAssocsMin;
+ m_prgisloutBefore = new int[dichw];
+ std::copy(seg.m_prgisloutBefore, seg.m_prgisloutBefore + dichw, m_prgisloutBefore);
+ m_prgisloutAfter = new int[dichw];
+ std::copy(seg.m_prgisloutAfter, seg.m_prgisloutAfter + dichw, m_prgisloutAfter);
+ m_prgpvisloutAssocs = new std::vector<int> * [dichw];
+ for (int ivislout = 0; ivislout < dichw; ivislout++)
+ {
+ std::vector<int> * pvislout = seg.m_prgpvisloutAssocs[ivislout];
+ if (pvislout)
+ {
+ std::vector<int> * pvisloutNew = new std::vector<int>;
+ m_prgpvisloutAssocs[ivislout] = pvisloutNew;
+ *pvisloutNew = *pvislout; // copy the vector
+ }
+ }
+ m_prgisloutLigature = new int[dichw];
+ std::copy(seg.m_prgisloutLigature, seg.m_prgisloutLigature + dichw, m_prgisloutLigature);
+ m_prgiComponent = new sdata8[dichw];
+ std::copy(seg.m_prgiComponent, seg.m_prgiComponent + dichw, m_prgiComponent);
+
+ m_cslout = seg.m_cslout;
+ int cnExtraPerSlot = (m_cslout > 0) ? seg.m_prgslout[0].CExtraSpaceSlout() : 0;
+ m_prgnSlotVarLenBuf = new u_intslot[m_cslout * cnExtraPerSlot];
+ m_prgslout = new GrSlotOutput[m_cslout];
+ for (islout = 0; islout < m_cslout; islout++)
+ {
+ GrSlotOutput * psloutOrig = seg.m_prgslout + islout;
+ GrSlotOutput * psloutThis = m_prgslout + islout;
+ psloutThis->ExactCopyFrom(psloutOrig,
+ m_prgnSlotVarLenBuf + (islout * cnExtraPerSlot), // var-length buffer location
+ cnExtraPerSlot);
+ }
+
+// m_cnUserDefn = seg.m_cnUserDefn;
+ m_cnCompPerLig = seg.m_cnCompPerLig;
+// m_cnFeat = seg.m_cnFeat;
+ m_cslotRestartBackup = seg.m_cslotRestartBackup;
+ std::copy(seg.m_prgnSlotVarLenBuf, seg.m_prgnSlotVarLenBuf +
+ (m_cslout * cnExtraPerSlot), m_prgnSlotVarLenBuf);
+
+ m_cginf = seg.m_cginf;
+ m_isloutGinf0 = seg.m_isloutGinf0;
+ m_prgginf = new GlyphInfo[m_cginf];
+ std::copy(seg.m_prgginf, seg.m_prgginf + m_cginf, m_prgginf);
+ for (int iginf = 0; iginf < m_cginf; iginf++)
+ {
+ m_prgginf[iginf].m_pseg = this;
+ m_prgginf[iginf].m_pslout = m_prgslout + m_prgginf[iginf].m_islout;
+ }
+
+ // Platform-specific; remove eventually:
+ //m_cgstrm = seg.m_cgstrm;
+ //m_prggstrm = new GlyphStrm[m_cgstrm];
+ //for (int igstrm = 0; igstrm < m_cgstrm; igstrm++)
+ //{
+ // m_prggstrm[igstrm].gsk.ys = seg.m_prggstrm[igstrm].gsk.ys;
+ // m_prggstrm[igstrm].gsk.clrFore = seg.m_prggstrm[igstrm].gsk.clrFore;
+ // m_prggstrm[igstrm].gsk.clrBack = seg.m_prggstrm[igstrm].gsk.clrBack;
+ // m_prggstrm[igstrm].vchwGlyphId = seg.m_prggstrm[igstrm].vchwGlyphId;
+ // m_prggstrm[igstrm].vdxs = seg.m_prggstrm[igstrm].vdxs;
+ // m_prggstrm[igstrm].vigbb = seg.m_prggstrm[igstrm].vigbb;
+ // m_prggstrm[igstrm].xsStart = seg.m_prggstrm[igstrm].xsStart;
+ // m_prggstrm[igstrm].xsStartInt = seg.m_prggstrm[igstrm].xsStartInt;
+ //}
+ //m_cgbb = seg.m_cgbb;
+ //m_prggbb = new GlyphBb[m_cgbb];
+ //memcpy(m_prggbb, seg.m_prggbb, m_cgbb * sizeof(GlyphBb));
+ //-------------------------------------
+
+ m_vnSkipOffsets = seg.m_vnSkipOffsets;
+ m_dircPrevStrong = seg.m_dircPrevStrong;
+ m_dircPrevTerm = seg.m_dircPrevTerm;
+ m_cbInitDat = seg.m_cbInitDat;
+ m_prgInitDat = new byte[m_cbInitDat];
+ std::copy(seg.m_prgInitDat, seg.m_prgInitDat + m_cbInitDat, m_prgInitDat);
+}
+
+/*----------------------------------------------------------------------------------------------
+ Swap the guts of the two segments.
+ OBSOLETE
+----------------------------------------------------------------------------------------------*/
+void Segment::SwapWith(Segment * pgrseg)
+{
+ int crefThis = m_cref;
+ int crefOther = pgrseg->m_cref;
+
+ std::swap(*this, *pgrseg);
+
+ m_cref = crefThis;
+ pgrseg->m_cref = crefOther;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Delete the pointer to the alternate-end-of-line segment.
+----------------------------------------------------------------------------------------------*/
+void Segment::ClearAltEndLineSeg()
+{
+ //if (m_psegAltEndLine)
+ //{
+ // if (m_psegAltEndLine->m_psegAltEndLine == this)
+ // m_psegAltEndLine->m_psegAltEndLine = NULL; // so we don't get into a loop
+ // delete m_psegAltEndLine;
+ // m_psegAltEndLine = NULL;
+ //}
+}
+
+
+//:>********************************************************************************************
+//:> Interface methods
+//:>********************************************************************************************
+
+/*----------------------------------------------------------------------------------------------
+ Return the font that was used to create this segment.
+ Note that in some implementations, the font may be invalid; eg, on Windows the font
+ may not contain a valid device context.
+----------------------------------------------------------------------------------------------*/
+Font & Segment::getFont()
+{
+ return *m_pfont;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Return the text-source that the segment is representing.
+----------------------------------------------------------------------------------------------*/
+ITextSource & Segment::getText()
+{
+ return *m_pgts;
+}
+
+/*----------------------------------------------------------------------------------------------
+ The distance the drawing point should advance after drawing this segment.
+ Always positive, even for RtoL segments.
+----------------------------------------------------------------------------------------------*/
+float Segment::advanceWidth()
+{
+ if (m_dxsWidth < 0)
+ {
+ ////SetUpGraphics(ichwBase, pgg, true);
+ ComputeDimensions();
+ }
+ return m_dxsWidth;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Compute the rectangle in destination coords which contains all the pixels drawn by
+ this segment. This should be a sufficient rectangle to invalidate if the segment is
+ about to be discarded.
+----------------------------------------------------------------------------------------------*/
+Rect Segment::boundingRect()
+{
+ if (m_dxsWidth < 0)
+ {
+ ////SetUpGraphics(ichwBase, pgg, true);
+ ComputeDimensions();
+ }
+
+ Rect rectRet;
+ rectRet.top = m_dysAscent + m_dysAscentOverhang;
+ rectRet.bottom = (m_dysAscent - m_dysHeight) - m_dysDescentOverhang;
+ rectRet.left = m_dxsLeftOverhang;
+ rectRet.right = m_dxsVisibleWidth + m_dxsRightOverhang;
+
+ return rectRet;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Answer whether the primary direction of the segment is right-to-left. This is based
+ on the direction of the writing system, except for white-space-only segments, in
+ which case it is based on the main paragraph direction.
+----------------------------------------------------------------------------------------------*/
+bool Segment::rightToLeft()
+{
+ if (m_twsh == ktwshOnlyWs)
+ {
+ // White-space-only segment: use main paragraph direction.
+ return (m_nDirDepth % 2);
+ }
+
+ GrEngine * pgreng = EngineImpl();
+ if (pgreng)
+ {
+ return pgreng->RightToLeft();
+ }
+ return (m_pgts->getRightToLeft(m_ichwMin));
+}
+
+/*----------------------------------------------------------------------------------------------
+ Get the depth of direction embedding used by this segment. It is presumably the same
+ for all runs of the segment, otherwise, some of them would use a different writing
+ system and therefore be part of a different segment. So just use the first.
+----------------------------------------------------------------------------------------------*/
+int Segment::directionDepth(bool * pfWeak)
+{
+ if (pfWeak)
+ *pfWeak = (m_twsh == ktwshOnlyWs);
+
+// *pnDepth = m_pgts->getDirectionDepth(m_ichwLim);
+// Assert(*pnDepth == m_nDirDepth);
+
+ return m_nDirDepth;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Change the direction of the segment. This is needed specifically for white-space-only
+ segments, which are initially created to be in the direction of the paragraph, but then
+ later are discovered to not be line-end after all, and need to be changed to use the
+ directionality of the writing system.
+
+ @return kresFail for segments that do not have weak directionality and therefore
+ cannot be changed.
+----------------------------------------------------------------------------------------------*/
+bool Segment::setDirectionDepth(int nNewDepth)
+{
+ Assert(false);
+
+ if (nNewDepth == m_nDirDepth)
+ {
+ return true;
+ }
+ else if ((nNewDepth % 2) == (m_nDirDepth % 2))
+ {
+ // Same direction, not much to change.
+ m_nDirDepth = nNewDepth;
+ return true;
+ }
+ else if (m_twsh != ktwshOnlyWs)
+ return false;
+
+ // Otherwise, do the hard stuff: reverse the positions of the glyphs.
+ for (int islout = 0; islout < m_cslout; islout++)
+ {
+ OutputSlot(islout)->ShiftForDirDepthChange(m_dxsTotalWidth);
+ }
+
+ //SetDirectionDepthPlatform(nNewDepth);
+
+ return true;
+
+}
+
+/*----------------------------------------------------------------------------------------------
+ The logical range of characters covered by the segment, relative to the beginning of
+ the segment. The stop character is the index of the first character beyond the end
+ of the segment.
+
+ These values should be exact at a writing system or string boundary,
+ but may be somewhat fuzzy at a line-break, since characters may be re-ordered
+ across such boundaries. The renderer is free to apply any definition it likes of
+ where a line-break occurs. This should always be the same value obtained from the
+ renderer as pdichLimSeg.
+----------------------------------------------------------------------------------------------*/
+int Segment::startCharacter()
+{
+ return m_ichwMin;
+}
+
+int Segment::stopCharacter()
+{
+ return m_ichwMin + m_dichwLim;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Returns an indication of why the segment ended.
+----------------------------------------------------------------------------------------------*/
+bool Segment::startOfLine()
+{
+ return m_fStartLine;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Returns an indication of why the segment ended.
+----------------------------------------------------------------------------------------------*/
+bool Segment::endOfLine()
+{
+ return m_fEndLine;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Returns true if this segment has something special happening at line boundaries.
+----------------------------------------------------------------------------------------------*/
+bool Segment::hasLineBoundaryContext()
+{
+ return (EngineImpl()->LineBreakFlag());
+}
+
+/*----------------------------------------------------------------------------------------------
+ Returns an indication of why the segment ended.
+----------------------------------------------------------------------------------------------*/
+SegEnd Segment::segmentTermination()
+{
+ return m_est;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Indicates the last character of interest to the segment, relative to the beginning
+ of the segment. The meaning of this is that no behavior of this segment will be
+ affected if characters beyond that change. This does not necessarily mean that
+ a different line break could not have been obtained by the renderer if characters
+ beyond that change, just that a segment with the boundaries of this one would
+ not behave differently.
+----------------------------------------------------------------------------------------------*/
+//GrResult Segment::get_LimInterest(int ichwBase, int * pdichw)
+//{
+// ChkGrOutPtr(pdichw);
+//
+// GrEngine * pgreng = EngineImpl();
+// Assert(pgreng);
+// *pdichw = m_dichwLim + pgreng->PostXlbContext();
+// ReturnResult(kresOk);
+//}
+
+/*----------------------------------------------------------------------------------------------
+ Changes the end-of-line status of the segment. This is used after making the last segment
+ of a string if we want to attempt to put some more text after it (e.g., in another
+ writing system), or if we have reordered due to bidirectionality.
+
+ ENHANCE: the current version handles switching back and forth when we are trying to
+ decide whether we can put more stuff on a line. So it assumes we are at the end of
+ the contextual run (ie, the next batch of stuff will be in a different ows).
+ This will not correctly handle the way this function could or possibly
+ should be used for bidirectional reordering.
+----------------------------------------------------------------------------------------------*/
+/*
+GrResult Segment::changeLineEnds(bool fNewStart, bool fNewEnd)
+{
+ if (m_fStartLine != fNewStart || m_fEndLine != fNewEnd)
+ {
+ if (EngineImpl()->LineBreakFlag())
+ {
+ if (!m_psegAltEndLine)
+ {
+ int dichwLimSeg, dichwContext;
+ float dxWidth;
+ SegEnd est;
+ int cbNextSegDat;
+ byte rgbNextSegDat[256];
+ OLECHAR rgchErrMsg[256];
+ Segment * pseg;
+
+ EngineImpl()->FindBreakPointAux(m_pfont, m_pgts, m_pgjus,
+ m_ichwMin, m_ichwMin + m_dichwLim, m_ichwMin + m_dichwLim,
+ false, fNewStart, fNewEnd, kPosInfFloat, false,
+ klbNoBreak, klbNoBreak, m_twsh, m_fParaRtl,
+ &pseg,
+ &dichwLimSeg, &dxWidth, &est,
+ m_cbInitDat, m_prgInitDat, 256, rgbNextSegDat, &cbNextSegDat,
+ &dichwContext,
+ NULL,
+ rgchErrMsg, 256);
+ // TODO: do something with the error message. This shouldn't really happen,
+ // since to get here we already created a segment with this font.
+ if (!pseg)
+ {
+ // Because we provide INT_MAX as the amount of space, there should
+ // always be enough room to generate the alternate segment.
+ Assert(false);
+ THROW(kresUnexpected);
+ }
+ Assert(cbNextSegDat == 0);
+ m_psegAltEndLine = pseg;
+ }
+ Assert(m_psegAltEndLine->m_fEndLine == fNewEnd);
+ Segment * pseg = m_psegAltEndLine; // save the value
+ SwapWith(m_psegAltEndLine);
+ m_psegAltEndLine = pseg; // reset it after swap
+ pseg->m_psegAltEndLine = this;
+ }
+ else
+ {
+ bool fShift = ((m_nDirDepth % 2) && (m_fEndLine != fNewEnd));
+ m_fStartLine = fNewStart;
+ m_fEndLine = fNewEnd;
+ if (fShift)
+ {
+ (m_fEndLine) ?
+ ShiftGlyphs(m_dxsVisibleWidth - m_dxsTotalWidth) : // shift left
+ ShiftGlyphs(m_dxsTotalWidth - m_dxsVisibleWidth); // shift right
+ }
+ m_dxsWidth = -1;
+ }
+ }
+
+ ReturnResult(kresOk);
+}
+*/
+
+/*----------------------------------------------------------------------------------------------
+ Get the type of break that occurs at the logical start of the segment.
+----------------------------------------------------------------------------------------------*/
+LineBrk Segment::startBreakWeight()
+{
+ return m_lbStart;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Get the type of break that occurs at the logical end of the segment.
+----------------------------------------------------------------------------------------------*/
+LineBrk Segment::endBreakWeight()
+{
+ return m_lbEnd;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Read the amount of stretch, that is, the difference between the actual and natural width.
+ If the segment has been shrunk, returns a negative number.
+ TODO: implement properly.
+----------------------------------------------------------------------------------------------*/
+float Segment::stretch()
+{
+ return (float)m_dxsStretch;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Return the maximum stretch possible.
+ TODO: implement properly.
+----------------------------------------------------------------------------------------------*/
+float Segment::maxStretch()
+{
+ return 1000;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Return the maximum shrink possible.
+ TODO: implement properly.
+----------------------------------------------------------------------------------------------*/
+float Segment::maxShrink()
+{
+ return 0;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Return iterators representing the complete range of glyphs in the segment.
+----------------------------------------------------------------------------------------------*/
+std::pair<GlyphIterator, GlyphIterator> Segment::glyphs()
+{
+ return std::make_pair(GlyphIterator(*this, 0), GlyphIterator(*this, m_cginf));
+}
+
+/*----------------------------------------------------------------------------------------------
+ Return iterators representing the set of glyphs for the given character.
+----------------------------------------------------------------------------------------------*/
+std::pair<GlyphSetIterator, GlyphSetIterator> Segment::charToGlyphs(toffset ich)
+{
+ std::vector<int> vislout = UnderlyingToLogicalAssocs(ich);
+
+ if (vislout.size() == 0)
+ return std::make_pair(GlyphSetIterator(), GlyphSetIterator());
+ else
+ {
+ // Note that BOTH instances of GlyphSetIterator must use the same underlying vector,
+ // so that comparisons make sense.
+ RcVector * qvislout = new RcVector(vislout);
+ return std::make_pair(
+ GlyphSetIterator(*this, 0, qvislout),
+ GlyphSetIterator(*this, vislout.size(), qvislout));
+ }
+}
+
+/*----------------------------------------------------------------------------------------------
+ Return the rendered glyphs and their x- and y-coordinates, in the order generated by
+ the final positioning pass, relative to the top-left of the segment. They are always
+ returned in (roughly) left-to-right order. Note: any of the arrays may be null.
+----------------------------------------------------------------------------------------------*/
+/*
+GrResult Segment::GetGlyphsAndPositions(
+ Rect rs, Rect rd, int cchMax, int * pcchRet,
+ utf16 * prgchGlyphs, float * prgxd, float * prgyd, float * prgdxdAdv)
+{
+ ChkGrOutPtr(pcchRet);
+ if (prgchGlyphs)
+ ChkGrArrayArg(prgchGlyphs, cchMax);
+ if (prgxd)
+ ChkGrArrayArg(prgxd, cchMax);
+ if (prgyd)
+ ChkGrArrayArg(prgyd, cchMax);
+ if (prgdxdAdv)
+ ChkGrArrayArg(prgdxdAdv, cchMax);
+
+ GrResult res;
+
+ //SetUpGraphics(ichwBase, pgg, true);
+
+ Assert(m_dxsWidth >= 0);
+ Assert(m_dysAscent >= 0);
+
+ *pcchRet = m_cginf;
+ if (cchMax < m_cginf)
+ {
+ res = (cchMax == 0) ? kresFalse : kresInvalidArg;
+ //RestoreFont(pgg);
+ ReturnResult(res);
+ }
+
+ if (m_dxsWidth < 0)
+ ComputeDimensions();
+
+ float dysFontAscent = m_dysFontAscent;
+ //////res = GetFontAscentSourceUnits(pgg, &dysFontAscent);
+ //////if (ResultFailed(res))
+ //////{
+ ////// RestoreFont(pgg);
+ ////// ReturnResult(res);
+ //////}
+
+ float xs = 0;
+ // Top of text that sits on the baseline, relative to top of segment:
+ float ys = m_dysAscent - dysFontAscent;
+
+ float * prgxdLocal;
+ if (prgxd == NULL)
+ prgxdLocal = new float[cchMax];
+ else
+ prgxdLocal = prgxd;
+
+ res = GetGlyphsAndPositionsPlatform(xs, ys, rs, rd,
+ prgchGlyphs, prgxdLocal, prgyd, prgdxdAdv);
+
+ if (!prgxd)
+ delete[] prgxdLocal;
+
+ //RestoreFont(pgg);
+
+ ReturnResult(res);
+}
+*/
+
+/*----------------------------------------------------------------------------------------------
+ For debugging. Return the characters in this segment.
+ OBSOLETE
+----------------------------------------------------------------------------------------------*/
+GrResult Segment::GetCharData(int cchMax, utf16 * prgch, int * pcchRet)
+{
+ ChkGrArgPtr(prgch);
+ ChkGrOutPtr(pcchRet);
+
+ int ichLimTmp = m_ichwMin + min(m_dichwLim, cchMax);
+ m_pgts->fetch(m_ichwMin, ichLimTmp - m_ichwMin, prgch);
+ *pcchRet = ichLimTmp - m_ichwMin;
+ ReturnResult(kresOk);
+}
+
+/*----------------------------------------------------------------------------------------------
+ Return either the first or last character that the the glyph corresponds to. The
+ index of the glyph matches what was returned by GetGlyphsAndPositions.
+ OBSOLETE
+----------------------------------------------------------------------------------------------*/
+GrResult Segment::GlyphToChar(int iginf, bool fFirst, int * pich)
+{
+ Assert(m_dxsWidth >= 0);
+ Assert(m_dysAscent >= 0);
+
+ *pich = PhysicalSurfaceToUnderlying(iginf, fFirst);
+ ReturnResult(kresOk);
+}
+
+/*----------------------------------------------------------------------------------------------
+ Return all the characters that the glyph corresponds to. Most commonly this will
+ be only one char, but ligatures, for instance, will have several. The index of the
+ glyph matches what was returned by GetGlyphsAndPositions.
+ OBSOLETE
+----------------------------------------------------------------------------------------------*/
+GrResult Segment::GlyphToAllChars(int iginf,
+ int cichMax, int * prgich, int * pcichRet)
+{
+ Assert(m_dxsWidth >= 0);
+ Assert(m_dysAscent >= 0);
+
+ std::vector<int> vich;
+ int ichFirst = PhysicalSurfaceToUnderlying(iginf, true);
+ int ichLast = PhysicalSurfaceToUnderlying(iginf, false);
+ Assert(ichFirst <= ichLast);
+
+ if (ichFirst < ichLast)
+ {
+ // Loop over the range of characters that might be associated with this glyph
+ // and add any that actually are.
+ // TODO: we should probably use a method that gets us all the physical associations,
+ // but this should get us 99.5% of the way there for now.
+ for (int ich = ichFirst; ich <= ichLast; ich++)
+ {
+ int iginfFirst = UnderlyingToPhysicalSurface(ich, true);
+ int iginfLast = UnderlyingToPhysicalSurface(ich, false);
+ if (iginfFirst == iginf || iginfLast == iginf)
+ vich.push_back(ich);
+ }
+ }
+ else
+ vich.push_back(ichFirst);
+
+ *pcichRet = vich.size();
+ if (cichMax < signed(vich.size()))
+ {
+ GrResult res = (cichMax == 0 ? kresFalse : kresInvalidArg);
+ ReturnResult(res);
+ }
+ int * pi = prgich;
+ for (size_t i = 0; i < vich.size(); i++)
+ *pi++ = vich[i];
+
+ ReturnResult(kresOk);
+}
+
+/*----------------------------------------------------------------------------------------------
+ Return either the first or last glyph that corresponds to the character. The
+ index of the glyph matches what was returned by GetGlyphsAndPositions.
+ OBSOLETE
+----------------------------------------------------------------------------------------------*/
+GrResult Segment::CharToGlyph(int ich, bool fFirst, int * pigbb)
+{
+ Assert(m_dxsWidth >= 0);
+ Assert(m_dysAscent >= 0);
+
+ *pigbb = UnderlyingToPhysicalSurface(ich, fFirst);
+ ReturnResult(kresOk);
+}
+
+
+//:>********************************************************************************************
+//:> Non-FieldWorks interface methods
+//:>********************************************************************************************
+
+/*----------------------------------------------------------------------------------------------
+ Return the breakweight for the given character. Returns the best value if the character is
+ mapped to glyphs with different values.
+----------------------------------------------------------------------------------------------*/
+LineBrk Segment::getBreakWeight(int ich, bool fBreakBefore)
+{
+ LineBrk lbFirst, lbLast;
+
+ int isloutFirst = UnderlyingToLogicalSurface(ich, true);
+ if (isloutFirst == kPosInfinity || isloutFirst == kNegInfinity)
+ return klbClipBreak;
+ GrSlotOutput * psloutFirst = m_prgslout + isloutFirst;
+ bool fNotFirst = false;
+ if ((psloutFirst->NumberOfComponents() > 0)
+ && psloutFirst->UnderlyingComponent(0) != ich)
+ {
+ // Not the first component of a ligature.
+ lbFirst = (LineBrk) ((int)klbClipBreak * -1);
+ fNotFirst = true;
+ }
+ else
+ {
+ lbFirst = (LineBrk) psloutFirst->BreakWeight();
+ }
+
+ int isloutLast = UnderlyingToLogicalSurface(ich, false);
+ if (isloutLast == kPosInfinity || isloutLast == kNegInfinity)
+ return klbClipBreak;
+ GrSlotOutput * psloutLast = m_prgslout + isloutLast;
+ bool fNotLast = false;
+ if ((psloutLast->NumberOfComponents() > 0)
+ && psloutLast->UnderlyingComponent(psloutLast->NumberOfComponents() - 1) != ich)
+ {
+ // Not the last component of a ligature.
+ lbLast = klbClipBreak;
+ fNotLast = true;
+ }
+ else
+ {
+ lbLast = (LineBrk) psloutLast->BreakWeight();
+ }
+
+ LineBrk lbRet;
+ if (fNotLast && fNotFirst)
+ // middle of a ligature
+ lbRet = klbClipBreak;
+ else if (fNotLast)
+ lbRet = (fBreakBefore) ? lbFirst : klbClipBreak;
+ else if (fNotFirst)
+ lbRet = (fBreakBefore) ? klbClipBreak : lbLast;
+ else
+ lbRet = (fBreakBefore) ? lbFirst : lbLast;
+
+ return lbRet;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Return the width of the range of characters. Line-boundary contextualization is handled
+ by the measure attributes on the glyphs. The width returned is equivalent to the sum of
+ the widths of any line-segments that would be needed to underline all the characters.
+
+ Since this method is intended to be used on "measured" segments that include the entire
+ paragraph, it is not very smart about handling cross-line-boundary contextualization.
+ It basically ignores any glyphs that are rendered by this segment but not officially
+ part of the segment.
+----------------------------------------------------------------------------------------------*/
+float Segment::getRangeWidth(int ichMin, int ichLim,
+ bool fStartLine, bool fEndLine, bool fSkipSpace)
+{
+ if (m_dxsWidth < 0)
+ {
+ ////SetUpGraphics(ichwBase, pgg, true);
+ ComputeDimensions();
+ }
+
+ Assert(m_dxsWidth >= 0);
+ Assert(m_dysAscent >= 0);
+
+ //float xsSegRight = m_dxsTotalWidth;
+
+ int ichMinRange = min(ichMin, ichLim);
+ int ichLimRange = max(ichMin, ichLim);
+
+ //int ichMinSeg = max(ichMinRange, m_ichwMin + m_ichwAssocsMin);
+ //int ichLimSeg = min(ichLimRange, m_ichwMin + m_ichwAssocsLim);
+ int ichMinSeg = max(ichMinRange, m_ichwMin);
+ int ichLimSeg = min(ichLimRange, m_ichwMin + m_dichwLim);
+
+ if (ichLimSeg < m_ichwMin) // not + m_ichwAssocsMin
+ return 0;
+ if (ichMinSeg >= m_ichwMin + m_dichwLim) // not m_ichwAssocsLim
+ return 0;
+
+ if (fSkipSpace)
+ {
+ int islout = UnderlyingToLogicalSurface(ichLimSeg - 1, true);
+ GrSlotOutput * pslout = (islout == kNegInfinity || islout == kPosInfinity) ?
+ NULL :
+ OutputSlot(islout);
+ while (pslout && pslout->IsSpace())
+ {
+ ichLimSeg--;
+ islout = UnderlyingToLogicalSurface(ichLimSeg - 1, true);
+// pslout = OutputSlot(islout);
+ pslout = (islout == kNegInfinity || islout == kPosInfinity) ?
+ NULL : OutputSlot(islout);
+ }
+ }
+
+ float xsWidth = 0;
+ SegmentNonPainter segp(this); // doesn't really need to paint
+ float rgxdLefts[100];
+ float rgxdRights[100];
+ size_t cxd = 0;
+ if (ichMinRange < ichLimRange)
+ cxd = segp.getUnderlinePlacement(ichMinSeg, ichLimSeg, fSkipSpace,
+ 100, rgxdLefts, rgxdRights, NULL);
+ for (size_t i = 0; i < cxd; i++)
+ xsWidth += rgxdRights[i] - rgxdLefts[i];
+
+ // Add in the line-boundary contextualization.
+ int isloutFirst = UnderlyingToLogicalSurface(ichMin, true);
+ int isloutLast = UnderlyingToLogicalSurface(ichLim - 1, false);
+ int mSol = 0;
+ int mEol = 0;
+ if (0 <= isloutFirst && isloutFirst < m_cslout)
+ mSol = m_prgslout[isloutFirst].MeasureSol();
+ if (0 <= isloutLast && isloutLast < m_cslout)
+ mEol = m_prgslout[isloutLast].MeasureEol();
+ float dxsSol = GrEngine::GrIFIMulDiv(mSol, m_xysEmSquare, m_mFontEmUnits);
+ float dxsEol = GrEngine::GrIFIMulDiv(mEol, m_xysEmSquare, m_mFontEmUnits);
+ xsWidth += dxsSol;
+ xsWidth += dxsEol;
+
+ //RestoreFont(pgg);
+
+ return xsWidth;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Return the next reasonable breakpoint after ichStart resulting in range that fits in
+ the specified width.
+----------------------------------------------------------------------------------------------*/
+int Segment::findNextBreakPoint(int ichStart,
+ LineBrk lbPref, LineBrk lbWorst, float dxMaxWidth,
+ float * pdxBreakWidth, bool fStartLine, bool fEndLine)
+{
+ ChkGrOutPtr(pdxBreakWidth);
+
+ int ichBreak;
+
+ // First make a rough estimation of how much will fit, using a binary chop approach.
+ int iginfStart = UnderlyingToPhysicalSurface(ichStart, !m_fWsRtl);
+ int iginfEnd = UnderlyingToPhysicalSurface(m_ichwMin + m_dichwLim - 1, m_fWsRtl);
+ int iginfLeft = min(iginfStart, iginfEnd);
+ int iginfRight= max(iginfStart, iginfEnd);
+
+ int iginfMin = iginfLeft;
+ int iginfLim = iginfRight;
+ int ichFit;
+ if (m_fWsRtl) // right-to-left
+ {
+ float xRight = GlyphLeftEdge(iginfRight + 1);
+ float xLeftMost = xRight - dxMaxWidth;
+ while (GlyphLeftEdge(iginfLim) < xLeftMost)
+ {
+ if (iginfLim - iginfMin <= 1)
+ break;
+ int iginfMid = (iginfLim + iginfMin) >> 1; // divide by 2
+ if (GlyphLeftEdge(iginfMid) < xLeftMost)
+ iginfLim = iginfMid;
+ else
+ iginfMin = iginfMid;
+ }
+ ichFit = PhysicalSurfaceToUnderlying(iginfLim, false);
+ }
+ else // left-to-right
+ {
+ float xLeft = GlyphLeftEdge(iginfLeft);
+ float xRightMost = dxMaxWidth - xLeft; // QUESTION: shouldn't this be +, not -?
+ iginfLim++;
+ while (GlyphLeftEdge(iginfLim) > xRightMost)
+ {
+ if (iginfLim - iginfMin <= 1)
+ break;
+ int iginfMid = (iginfLim + iginfMin) >> 1; // divide by 2
+ if (GlyphLeftEdge(iginfMid) > xRightMost)
+ iginfLim = iginfMid;
+ else
+ iginfMin = iginfMid;
+ }
+ Assert(iginfLim <= m_cginf);
+ if (iginfLim >= m_cginf) iginfLim = m_cginf - 1;
+ ichFit = PhysicalSurfaceToUnderlying(iginfLim, false);
+ }
+
+ // Breaking after ichFit should pretty much fit in the space. But make sure.
+ float dxWidthFit;
+ while (ichFit > ichStart)
+ {
+ dxWidthFit = getRangeWidth(ichStart, ichFit, fStartLine, fEndLine);
+ if (dxWidthFit <= dxMaxWidth)
+ break;
+ ichFit--;
+ }
+
+ if (ichFit <= ichStart)
+ {
+ // Nothing will fit.
+ return ichStart - 1;
+ }
+
+ // Now look for a good break. First look forward until the range doesn't fit anymore.
+ int ichTry = ichFit;
+ int nExtra = 2;
+ LineBrk lbBest = klbClipBreak;
+ int ichBestBreak = -1;
+ while (nExtra > 0 && ichTry < m_ichwMin + m_dichwLim)
+ {
+ float dxWTmp;
+ dxWTmp = getRangeWidth(ichStart, ichTry, fStartLine, fEndLine);
+ if (dxWTmp <= dxMaxWidth)
+ {
+ LineBrk lbTmp;
+ lbTmp = getBreakWeight(ichTry - 1, false);
+ if (lbTmp > 0 && lbTmp < lbWorst && max(lbTmp, lbPref) <= lbBest)
+ {
+ ichBestBreak = ichTry;
+ lbBest = max(lbTmp, lbPref); // any value better than lbPref is insignificant
+ }
+ else
+ {
+ lbTmp = getBreakWeight(ichTry - 1, true);
+ if (lbTmp < 0)
+ {
+ lbTmp = LineBrk(max(lbTmp * -1L, long(lbPref)));
+ if (lbTmp < lbWorst && lbTmp <= lbBest)
+ {
+ ichBestBreak = ichTry - 1;
+ lbBest = lbTmp;
+ }
+ }
+ }
+ }
+ else
+ {
+ // Didn't fit. But it is theoretically possible that actually adding another
+ // character will! Try a few more.
+ nExtra--;
+ }
+ ichTry++;
+ }
+
+ if (ichTry >= m_ichwMin + m_dichwLim)
+ {
+ // Break at the end of the segment.
+ ichBreak = m_ichwMin + m_dichwLim;
+ *pdxBreakWidth = getRangeWidth(ichStart, m_dichwLim, fStartLine, fEndLine);
+ return ichBreak;
+ }
+
+ if (ichBestBreak > -1 && lbBest <= lbPref)
+ {
+ // Found a reasonable break by looking forward; return it.
+ ichBreak = ichBestBreak;// + m_ichwMin;
+ *pdxBreakWidth = getRangeWidth(ichStart, ichBestBreak, fStartLine, fEndLine);
+ return ichBreak;
+ }
+
+ // Didn't find a really good break point looking forward from our rough guess.
+ // Now look backward.
+
+ ichTry = ichFit;
+ while (ichTry > ichStart)
+ {
+ LineBrk lbTmp;
+ lbTmp = getBreakWeight(ichTry - 1, false);
+ if (lbTmp > 0 && lbTmp < lbWorst && max(lbTmp, lbPref) < lbBest)
+ {
+ ichBestBreak = ichTry;
+ lbBest = max(lbTmp, lbPref); // any value better than lbPref is insignificant
+ }
+ else
+ {
+ lbTmp = getBreakWeight(ichTry - 1, true);
+ if (lbTmp < 0)
+ {
+ lbTmp = LineBrk(max(lbTmp * -1L, long(lbPref)));
+ if (lbTmp < lbWorst && lbTmp < lbBest)
+ {
+ ichBestBreak = ichTry - 1;
+ lbBest = lbTmp;
+ }
+ }
+ }
+ if (lbBest <= lbPref)
+ break;
+
+ ichTry--;
+ }
+
+ if (ichBestBreak > -1 && ichBestBreak > ichStart)
+ {
+ // Found a reasonable break by looking backward; return it.
+ ichBreak = ichBestBreak;// + m_ichwMin;
+ *pdxBreakWidth = getRangeWidth(ichStart, ichBestBreak, fStartLine, fEndLine);
+ return ichBreak;
+ }
+
+ // No reasonable break.
+ return ichStart - 1;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Private; used by findNextBreakPoint().
+----------------------------------------------------------------------------------------------*/
+float Segment::GlyphLeftEdge(int iginf)
+{
+ if (iginf >= m_cginf)
+ return m_prgginf[m_cginf - 1].origin() + m_prgginf[m_cginf - 1].advanceWidth();
+// return m_prgginf[m_cginf].origin() + m_prgginf[m_cginf].advanceWidth();
+ else
+ return m_prgginf[iginf].origin();
+}
+
+
+//:>********************************************************************************************
+//:> Other methods
+//:>********************************************************************************************
+
+/*----------------------------------------------------------------------------------------------
+ Get the embedded string. For temporary use; AddRef is not automatically called.
+ Do not release unless you AddRef.
+----------------------------------------------------------------------------------------------*/
+ITextSource * Segment::GetString()
+{
+ return m_pgts;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Compute the ascent, height, width, and overhangs of the segment.
+ The ascent is the ascent of the font (distance from the baseline to the top)
+ plus any extra ascent height specified by the font.
+ The descent is distance from the baseline to the bottom of font, plus any extra descent
+ specified by the font (ExtraDescent).
+ The ascent overhang is the distance from the top of the font to the top of the highest
+ glyph, or zero if all the glyphs fit within the ascent.
+ The descent overhang is the distance from the bottom of the font to the bottom of the lowest
+ glyph, or zero if all the glyphs fit within the descent.
+ The height is the sum of the ascent and descent.
+----------------------------------------------------------------------------------------------*/
+void Segment::ComputeDimensions()
+{
+ if (m_fEndLine)
+ m_dxsWidth = m_dxsVisibleWidth; // don't include trailing white space.
+ else
+ m_dxsWidth = m_dxsTotalWidth;
+
+ if (m_dxsWidth == -1)
+ {
+ for (int iginf = 0; iginf < m_cginf; iginf++)
+ {
+ GlyphInfo * pginf = m_prgginf + iginf;
+ if (pginf->isSpace())
+ continue;
+ m_dxsWidth = max(m_dxsWidth, (pginf->origin() + pginf->advanceWidth()));
+ }
+ }
+
+ int dysNegFontDescent = (int)m_dysFontDescent * -1;
+
+ // if there were no glyphs in the segment then there is sometimes no EngineImpl
+ if (!EngineImpl())
+ {
+ Assert(m_cslout == 0);
+ Assert(m_cginf == 0);
+ m_dxsWidth = 0;
+ m_dysAscent = 0; // max(0.0, m_dysFontAscent);
+ m_dysHeight = 0; // max(0.0, m_dysAscent - dysNegFontDescent);
+ m_dysAscentOverhang = 0;
+ m_dysDescentOverhang = 0;
+ m_dxsLeftOverhang = 0;
+ m_dxsRightOverhang = 0;
+ return;
+ }
+
+ // Calculate the extra ascent and descent.
+ int mXAscent = EngineImpl()->ExtraAscent();
+ int mXDescent = EngineImpl()->ExtraDescent();
+
+ m_dysXAscent = GrEngine::GrIFIMulDiv(mXAscent, m_xysEmSquare, m_mFontEmUnits);
+ m_dysXDescent = GrEngine::GrIFIMulDiv(mXDescent, m_xysEmSquare, m_mFontEmUnits);
+
+ m_dysAscent = m_dysFontAscent + m_dysXAscent;
+ float dysNegDescent = dysNegFontDescent - m_dysXDescent; // dysNegDescent < 0
+ m_dysHeight = m_dysAscent - dysNegDescent;
+
+ // Calculate the overhangs.
+ float dysVisAscent = m_dysAscent;
+ float dysNegVisDescent = dysNegDescent;
+ float dxsVisLeft = 0;
+ float dxsVisRight = m_dxsTotalWidth;
+ ComputeOverhangs(&dysVisAscent, &dysNegVisDescent, &dxsVisLeft, &dxsVisRight);
+ m_dysAscentOverhang = max(float(0), (dysVisAscent - m_dysAscent));
+ m_dysDescentOverhang = max(float(0), (dysNegDescent - dysNegVisDescent));
+ m_dxsLeftOverhang = min(float(0), dxsVisLeft);
+ m_dxsRightOverhang = max(float(0), dxsVisRight - m_dxsTotalWidth);
+
+ m_dysOffset = EngineImpl()->VerticalOffset();
+}
+
+/*----------------------------------------------------------------------------------------------
+ Compute the visible ascent and descent, based on how much the bounding box of any
+ glyph may extend beyond the ascent or descent of the font.
+ Also compute the left- and right-overhangs.
+----------------------------------------------------------------------------------------------*/
+void Segment::ComputeOverhangs(float * pdysVisAscent, float * pdysNegVisDescent,
+ float * pdxsVisLeft, float * pdxsVisRight)
+{
+ for (int iginf = 0; iginf < m_cginf; iginf++)
+ {
+ *pdysVisAscent = max(*pdysVisAscent, m_prgginf[iginf].bb().top);
+ *pdysNegVisDescent = min(*pdysNegVisDescent, m_prgginf[iginf].bb().bottom);
+
+ *pdxsVisLeft = min(*pdxsVisLeft, m_prgginf[iginf].bb().left);
+ *pdxsVisRight = max(*pdxsVisRight, m_prgginf[iginf].bb().right);
+ }
+}
+
+/*----------------------------------------------------------------------------------------------
+ Create the arrays to store the final output and association information.
+----------------------------------------------------------------------------------------------*/
+void Segment::SetUpOutputArrays(Font * pfont, GrTableManager * ptman,
+ GrSlotStream * psstrmFinal,
+ int cchwInThisSeg, int csloutSurface, gid16 chwLB,
+ TrWsHandling twsh, bool fParaRtl, int nDirDepth, bool fEmpty)
+{
+ m_mFontEmUnits = EngineImpl()->GetFontEmUnits();
+
+ pfont->getFontMetrics(&m_dysFontAscent, &m_dysFontDescent, &m_xysEmSquare);
+// m_xsDPI = (float)pfont->getDPIx();
+// m_ysDPI = (float)pfont->getDPIy();
+
+ // Note that storing both of these values is redundant; they should be the same.
+// Assert(m_xysEmSquare == m_pixHeight);
+
+ m_twsh = twsh;
+ m_fParaRtl = fParaRtl;
+ if (m_twsh == ktwshOnlyWs)
+ m_nDirDepth = (int)fParaRtl;
+ else if (fParaRtl && nDirDepth == 0)
+ m_nDirDepth = 2;
+ else
+ m_nDirDepth = nDirDepth;
+
+ // Create association mappings and set up final versions of slots.
+
+ Assert((psstrmFinal == NULL) == fEmpty);
+
+ m_ichwAssocsMin = 0;
+ m_ichwAssocsLim = cchwInThisSeg;
+
+ m_prgisloutBefore = new int[cchwInThisSeg];
+
+ m_prgisloutAfter = new int[cchwInThisSeg];
+
+ m_prgpvisloutAssocs = new std::vector<int> * [cchwInThisSeg];
+
+ m_prgisloutLigature = new int[cchwInThisSeg];
+
+ m_prgiComponent = new sdata8[cchwInThisSeg];
+
+ int cslot = 0;
+ //m_psstrm = psstrmFinal; // TODO: make a local variable
+ if (psstrmFinal)
+ cslot = psstrmFinal->FinalSegLim();
+ else
+ Assert(fEmpty);
+
+ float xsMin = 0;
+ int islot;
+ int islotMin = (psstrmFinal) ? psstrmFinal->IndexOffset() : 0;
+ for (islot = islotMin; islot < cslot; islot++)
+ xsMin = min(xsMin, psstrmFinal->SlotAt(islot)->XPosition());
+
+ // For right-to-left segments, the draw origin is at the left side of the visible
+ // portion of the text. So if necessary, scoot everything left so that the invisible
+ // trailing white-space is to the left of the draw position.
+ Assert(m_dxsTotalWidth > -1);
+ Assert(m_dxsVisibleWidth > -1);
+ float dxsInvisible = (m_fEndLine) ? m_dxsTotalWidth - m_dxsVisibleWidth : 0;
+ for (islot = islotMin; ((m_nDirDepth % 2) && (islot < cslot)); islot++)
+ {
+ GrSlotState * pslot = psstrmFinal->SlotAt(islot);
+ // int islout = islot - islotMin;
+ if (pslot->GlyphID() == chwLB)
+ continue; // skip over linebreak markers
+ if (m_nDirDepth % 2) // redundant test
+ {
+ // RTL will have descending neg values, adjust to be positive. Also make sure
+ // invisible trailing white space is to the left of the draw origin.
+ // Review: will changing this value have side affects?
+ pslot->SetXPos(pslot->XPosition() - xsMin - dxsInvisible);
+ }
+ }
+
+ Assert(kPosInfinity > 0);
+ Assert(kNegInfinity < 0);
+ for (int ichw = 0; ichw < cchwInThisSeg; ++ichw)
+ {
+ m_prgisloutBefore[ichw] = kPosInfinity;
+ m_prgisloutAfter[ichw] = kNegInfinity;
+ m_prgpvisloutAssocs[ichw] = new std::vector<int>;
+ m_prgisloutLigature[ichw] = kNegInfinity;
+ m_prgiComponent[ichw] = 0;
+ }
+
+ m_cslout = csloutSurface;
+ if (ptman->NumUserDefn() > 0 && ptman->NumCompPerLig() > 0)
+ {
+ int x; x = 3;
+ }
+
+// m_cnUserDefn = ptman->NumUserDefn();
+ m_cnCompPerLig = ptman->NumCompPerLig();
+// m_cnFeat = ptman->NumFeat();
+ // Normal buffers, plus underlying indices of ligature components, plus
+ // map from used components to defined components.
+// int cnExtraPerSlot = m_cnUserDefn + (m_cnCompPerLig * 2) + m_cnFeat + (m_cnCompPerLig * 2);
+
+ // We don't need to store the user-defined slot attributes or the features in the segment itself.
+ // What we need is: (1) component.ref attr settings, (2) slot-attribute indices,
+ // (3) underlying indicates of ligature components, and (4) map from used components
+ // to defined components
+ int cnExtraPerSlot = m_cnCompPerLig * 4;
+ m_prgslout = new GrSlotOutput[m_cslout];
+ m_prgnSlotVarLenBuf = new u_intslot[m_cslout * cnExtraPerSlot];
+
+ m_isloutVisLim = 0;
+ if (psstrmFinal)
+ {
+ for (islot = islotMin; islot < psstrmFinal->FinalSegLim(); ++islot)
+ {
+ int isloutRel = islot - islotMin;
+
+ GrSlotState * pslot = psstrmFinal->SlotAt(islot);
+ pslot->SetPosPassIndex(isloutRel, false);
+ pslot->EnsureCacheForOutput(ptman);
+ GrSlotOutput * pslout = OutputSlot(isloutRel);
+ pslout->SetBufferPtr(m_prgnSlotVarLenBuf + (isloutRel * cnExtraPerSlot));
+ pslout->InitializeOutputFrom(pslot);
+ pslout->SetBeforeAssoc(pslot->BeforeAssoc());
+ pslout->SetAfterAssoc(pslot->AfterAssoc());
+ if (pslot->HasComponents())
+ pslot->SetComponentRefsFor(pslout);
+ Assert(pslot->HasComponents() == (pslout->NumberOfComponents() > 0));
+ //if (pslot->HasClusterMembers())
+ //{
+ //Assert(pslot->HasClusterMembers() || pslot->ClusterRootOffset() == 0);
+ pslout->SetClusterXOffset(pslot->ClusterRootOffset() * -1);
+ pslout->SetClusterAdvance(pslot->ClusterAdvWidthFrom(0));
+
+ //}
+
+ if (!ptman->IsWhiteSpace(pslot))
+ m_isloutVisLim = islot + 1;
+ }
+ }
+
+ // Set up the clusters.
+ for (islot = islotMin; islot < m_cslout; islot++)
+ {
+ GrSlotState * pslot = psstrmFinal->SlotAt(islot);
+ int isloutAdj = islot - islotMin;
+ int isloutBaseIndex = pslot->Base(psstrmFinal)->PosPassIndex();
+ if (!pslot->IsBase())
+ {
+ OutputSlot(isloutBaseIndex)->AddClusterMember(isloutBaseIndex, pslot->PosPassIndex());
+ OutputSlot(isloutAdj)->SetClusterBase(isloutBaseIndex);
+ }
+ else if (pslot->HasClusterMembers() && pslot->IsBase())
+ {
+ Assert(isloutBaseIndex == islot - islotMin);
+ OutputSlot(isloutAdj)->SetClusterBase(isloutBaseIndex);
+ }
+ }
+
+ ///AssertValidClusters(psstrmFinal);
+
+#ifdef OLD_TEST_STUFF
+ if (ptman->GlyphTable() == NULL)
+ return; // test procedures
+#endif // OLD_TEST_STUFF
+
+ // CalcPositionsUpTo() called on final pass already from Table Mgr
+
+ // Final output for draw routines.
+
+ SetUpGlyphInfo(ptman, psstrmFinal, chwLB, nDirDepth, islotMin, cslot);
+
+ //SetUpOutputArraysPlatform(ptman, chwLB, nDirDepth, islotMin, cslot);
+
+}
+
+/*----------------------------------------------------------------------------------------------
+ Set up the data structures that represent the actual rendered glyphs for the new segment.
+----------------------------------------------------------------------------------------------*/
+void Segment::SetUpGlyphInfo(GrTableManager * ptman, GrSlotStream * psstrmFinal,
+ gid16 chwLB, int nDirDepth, int islotMin, int cslot)
+{
+ //int paraDirLevel = (ptman->State()->ParaRightToLeft()) ? 1 : 0;
+
+ m_cginf = 0;
+
+ int islot;
+ for (islot = islotMin; islot < cslot; islot++)
+ {
+ if (psstrmFinal->SlotAt(islot)->GlyphID() != chwLB)
+ {
+ m_cginf++;
+ }
+ }
+
+ // For right-to-left segments, the draw origin will be at the left side of the visible
+ // portion of the text. So if necessary, scoot everything left so that the invisible
+ // trailing white-space is to the left of the draw position.
+ Assert(m_dxsTotalWidth > -1);
+ Assert(m_dxsVisibleWidth > -1);
+ //float dxsInvisible = (m_fEndLine) ? m_dxsTotalWidth - m_dxsVisibleWidth : 0;
+
+ m_prgginf = new GlyphInfo [m_cginf];
+
+ m_isloutGinf0 = -1;
+ int iginf = 0;
+ for (int islot = islotMin; islot < cslot; islot++)
+ {
+ GrSlotState * pslot = psstrmFinal->SlotAt(islot);
+
+ if (pslot->GlyphID() == chwLB)
+ {
+ continue; // skip over linebreak markers
+ }
+
+ int islout = islot - islotMin;
+ GrSlotOutput * pslout = OutputSlot(islout);
+
+ if (m_isloutGinf0 == -1)
+ m_isloutGinf0 = islout;
+
+ m_prgginf[iginf].m_pslout = pslout;
+ m_prgginf[iginf].m_islout = islout;
+ m_prgginf[iginf].m_pseg = this;
+
+ // Fill in stuff in the output slot that is needed by the GlyphInfo object.
+ pslout->m_xsAdvanceX = pslot->GlyphMetricLogUnits(ptman, kgmetAdvWidth);
+ //pslout->m_ysAdvanceY = pslot->GlyphMetricLogUnits(ptman, kgmetAdvHeight);
+ //pslout->m_rectBB.top
+ // = pslot->YPosition() + pslot->GlyphMetricLogUnits(ptman, kgmetBbTop);
+ //pslout->m_rectBB.bottom
+ // = pslot->YPosition() + pslot->GlyphMetricLogUnits(ptman, kgmetBbBottom);
+ //pslout->m_rectBB.left
+ // = pslot->XPosition() + pslot->GlyphMetricLogUnits(ptman, kgmetBbLeft);
+ //if (pslot->IsSpace(ptman))
+ // pslout->m_rectBB.right
+ // = pslot->XPosition() + pslot->GlyphMetricLogUnits(ptman, kgmetAdvWidth);
+ //else
+ // pslout->m_rectBB.right
+ // = pslot->XPosition() + pslot->GlyphMetricLogUnits(ptman, kgmetBbRight);
+
+ iginf++;
+ }
+
+ if (cslot - islotMin == 0)
+ m_isloutGinf0 = 0;
+ if (m_isloutGinf0 == -1)
+ {
+ Assert(m_cginf == 0);
+ m_isloutGinf0 = ((OutputSlot(0)->IsInitialLineBreak()) ? 1 : 0);
+ }
+
+ Assert(m_isloutGinf0 == 0 || m_isloutGinf0 == 1);
+}
+
+/*
+// OLD VERSION
+void Segment::SetUpGlyphInfo(GrTableManager * ptman, GrSlotStream * psstrmFinal, gid16 chwLB, int nDirDepth,
+ int islotMin, int cslot)
+{
+ int paraDirLevel = (ptman->State()->ParaRightToLeft()) ? 1 : 0;
+
+ m_cginf = 0;
+ int islot;
+ for (islot = islotMin; islot < cslot; islot++)
+ {
+ if (psstrmFinal->SlotAt(islot)->GlyphID() != chwLB)
+ m_cginf++;
+ }
+
+ // For right-to-left segments, the draw origin will be at the left side of the visible
+ // portion of the text. So if necessary, scoot everything left so that the invisible
+ // trailing white-space is to the left of the draw position.
+ Assert(m_dxsTotalWidth > -1);
+ Assert(m_dxsVisibleWidth > -1);
+ float dxsInvisible = (m_fEndLine) ? m_dxsTotalWidth - m_dxsVisibleWidth : 0;
+
+ m_prgginf = new GlyphInfo [m_cginf];
+
+ // Fill in glyph information structures.
+ m_isloutGinf0 = -1;
+ for (islot = islotMin; islot < cslot; islot++)
+ {
+ GrSlotState * pslot = psstrmFinal->SlotAt(islot);
+
+ int islout = islot - islotMin;
+ if (pslot->GlyphID() == chwLB)
+ {
+ continue; // skip over linebreak markers
+ }
+ if (m_isloutGinf0 == -1)
+ m_isloutGinf0 = islout;
+
+ GrSlotOutput * pslout = OutputSlot(islout);
+
+ int iginf = islout - m_isloutGinf0;
+ GlyphInfo & ginf = m_prgginf[iginf];
+
+ ginf.glyphID = pslot->ActualGlyphForOutput(ptman);
+ ginf.pseudoGlyphID = pslot->GlyphID();
+ if (ginf.glyphID == ginf.pseudoGlyphID)
+ ginf.pseudoGlyphID = 0;
+
+ ginf.isSpace = pslot->IsSpace(ptman);
+
+ ginf.origin = pslot->XPosition();
+ ginf.yOffset = pslot->YPosition();
+
+ ginf.bbLeft = pslot->XPosition() + pslot->GlyphMetricLogUnits(ptman, kgmetBbLeft);
+ ginf.bbTop = pslot->YPosition() + pslot->GlyphMetricLogUnits(ptman, kgmetBbTop);
+ if (ginf.isSpace)
+ {
+ ginf.bbRight = pslot->XPosition() + pslot->GlyphMetricLogUnits(ptman, kgmetAdvWidth);
+ }
+ else
+ {
+ ginf.bbRight = pslot->XPosition() + pslot->GlyphMetricLogUnits(ptman, kgmetBbRight);
+ }
+ ginf.bbBottom = pslot->YPosition() + pslot->GlyphMetricLogUnits(ptman, kgmetBbBottom);
+
+ ginf.advanceWidth = pslot->GlyphMetricLogUnits(ptman, kgmetAdvWidth);
+ ginf.advanceHeight = 0;
+
+ if (pslot->AttachTo() == 0) // not attached
+ ginf.attachedTo = -1;
+ else
+ ginf.attachedTo = pslot->AttachTo() + islot;
+
+ ginf.directionality = byte(pslot->Directionality());
+ if (pslot->DirLevel() == -1)
+ ginf.directionLevel = paraDirLevel;
+ else
+ ginf.directionLevel = byte(pslot->DirLevel());
+
+ //ginf.firstChar = LogicalSurfaceToUnderlying(islout, true);
+ //ginf.lastChar = LogicalSurfaceToUnderlying(islout, false);
+
+ if (pslot->HasComponents())
+ {
+ SetUpLigInfo(ptman, ginf, pslout);
+ }
+ else
+ {
+ ginf.numberOfComponents = 0;
+ ginf.components = NULL;
+ }
+
+ ginf.insertBefore = pslot->InsertBefore();
+
+ ginf.maxStretch[0] = ptman->EmToLogUnits(pslot->JStretch());
+ ginf.maxShrink[0] = ptman->EmToLogUnits(pslot->JShrink());
+ ginf.stretchStep[0]= ptman->EmToLogUnits(pslot->JStep());
+ ginf.justWidth[0] = ptman->EmToLogUnits(pslot->JWidth());
+ ginf.justWeight[0] = pslot->JWeight();
+
+ ginf.measureStartOfLine = pslout->MeasureSolLogUnits();
+ ginf.measureEndOfLine = pslout->MeasureEolLogUnits();
+ }
+}
+*/
+
+/*----------------------------------------------------------------------------------------------
+ Set up the data structures that represent the components of a ligature glyph.
+----------------------------------------------------------------------------------------------*/
+/*
+void Segment::SetUpLigInfo(GrTableManager * ptman, GlyphInfo & ginf, GrSlotOutput * pslout)
+{
+ GrGlyphTable * pgtbl = ptman->GlyphTable();
+
+ int ccomp = pslout->NumberOfComponents();
+ ginf.numberOfComponents = ccomp;
+ ginf.components = new ComponentBox[ccomp];
+ for (int icomp = 0; icomp < ccomp; icomp++)
+ {
+ ComponentBox & cb = ginf.components[icomp];
+
+ // TODO: Rework ComponentIndexForGlyph to take slati rather than iForGlyph.
+ float xsLeft, xsRight, ysTop, ysBottom;
+ int slati = pslout->ComponentId(icomp);
+ int iForGlyph = pgtbl->ComponentIndexForGlyph(pslout->GlyphID(), slati);
+ pgtbl->ComponentBoxLogUnits(m_xysEmSquare, pslout->GlyphID(), iForGlyph,
+ m_mFontEmUnits, m_dysAscent,
+ &xsLeft, &ysTop, &xsRight, &ysBottom, false);
+
+ cb.firstChar = pslout->UnderlyingComponent(icomp);
+ cb.lastChar = cb.firstChar; // TODO: is this supported?
+ cb.left = xsLeft;
+ cb.right = xsRight;
+ cb.top = ysTop;
+ cb.bottom = ysBottom;
+ }
+}
+*/
+
+
+// --------- OBSOLETE - replaced by GrTableManager::AdjustAssocsForOverlaps() ---------------
+#if 0
+
+/*----------------------------------------------------------------------------------------------
+ Now that this segment is finished and has its associations set, handle any overlaps in
+ the associations between the previous segment and this one.
+
+ Review: Think about whether we need to distinguish between before==kPosInfinity meaning
+ that the glyph is in the next segment vs. there is no association at all.
+ OBSOLETE
+----------------------------------------------------------------------------------------------*/
+void Segment::AdjustForOverlapsWithPrevSeg()
+{
+ if (m_psegPrev)
+ {
+ AdjustBeforeArrayFromPrevSeg();
+ m_psegPrev->AdjustAfterArrayFromNextSeg();
+ }
+}
+
+/*----------------------------------------------------------------------------------------------
+ For any characters that are officially in this segment but are rendered in
+ the previous segment, initialize this segment's before array appropriately. Specifically,
+ set this segment's before value to something invalid--negative infinity, which will
+ never be overwritten.
+ OBSOLETE - replaced by AdjustAfterArrayFromNextSeg
+----------------------------------------------------------------------------------------------*/
+void Segment::InitBeforeArrayFromPrevSeg()
+{
+ Assert(m_psegPrev);
+
+ int ichwPrevMin = m_psegPrev->m_ichwMin;
+ int ichwPrevLim = m_psegPrev->m_ichwLim;
+// int ichwPrevContextLim =
+// m_psegPrev->m_ichwAssocsLim - m_psegPrev->m_ichwAssocsMin + m_psegPrev->m_ichwMin;
+ int cchwPrevAssocsMin = m_psegPrev->m_ichwAssocsMin;
+ int cchwPrevAssocsLim = m_psegPrev->m_ichwAssocsLim;
+
+ // Loop over associations appended to the end of the previous segment;
+ // ichwPrev and ichwThis are relative to the start of their respective segments.
+ for (int ichwPrev = ichwPrevLim - ichwPrevMin; ichwPrev < cchwPrevAssocsLim; ++ichwPrev)
+ {
+ int ichwThis = ichwPrev - (ichwPrevLim - ichwPrevMin); // relative to start of this seg
+ if (m_psegPrev->m_prgisloutBefore[ichwPrev - cchwPrevAssocsMin] < kPosInfinity)
+ // The "before" value is in the previous segment, so make this segment's
+ // before value invalid.
+ m_prgisloutBefore[ichwThis - m_ichwAssocsMin] = kNegInfinity;
+ }
+}
+
+/*----------------------------------------------------------------------------------------------
+ For any associations that overlap between the previous segment and this, adjust
+ this segment's before array appropriately. Specifically, for any characters that
+ are rendered in both, set this segment's before value to something invalid.
+ OBSOLETE
+----------------------------------------------------------------------------------------------*/
+void Segment::AdjustBeforeArrayFromPrevSeg()
+{
+ Assert(m_psegPrev);
+
+ int ichwPrevMin = m_psegPrev->m_ichwMin;
+ int ichwPrevLim = m_psegPrev->m_ichwLim;
+ int ichwPrevAssocsMin = m_psegPrev->m_ichwAssocsMin;
+ int ichwPrevAssocsLim = m_psegPrev->m_ichwAssocsLim;
+
+ // Loop over associations appended to the end of the previous segment
+ // (ie, rendered in the previous segment but officially in this one);
+ // ichwPrev and ichwThis are relative to the start of their respective segments.
+ for (int ichwPrev = ichwPrevLim - ichwPrevMin; ichwPrev < ichwPrevAssocsLim; ++ichwPrev)
+ {
+ int ichwThis = ichwPrev - (ichwPrevLim - ichwPrevMin); // relative to start of this seg
+ if (m_psegPrev->m_prgisloutBefore[ichwPrev - ichwPrevAssocsMin] < kPosInfinity)
+ // The "before" value is in the previous segment, so make this segment's
+ // before value invalid.
+ m_prgisloutBefore[ichwThis - m_ichwAssocsMin] = kNegInfinity;
+ }
+ // Loop over associations prepended to the start of this segment
+ // (ie, rendered in this segment but officially in the previous one);
+ // ichwPrev and ichwThis are relative to the start of their respective segments.
+ for (int ichwThis = m_ichwAssocsMin; ichwThis < 0; ++ichwThis)
+ {
+ int ichwPrev = ichwThis + (ichwPrevLim - ichwPrevMin);
+ if (m_psegPrev->m_prgisloutBefore[ichwPrev - ichwPrevAssocsMin] < kPosInfinity)
+ // The "before" value is in the previous segment, so make this segment's
+ // before value invalid.
+ m_prgisloutBefore[ichwThis - m_ichwAssocsMin] = kNegInfinity;
+ }
+}
+
+/*----------------------------------------------------------------------------------------------
+ For any associations that overlap between this segment and the next, adjust
+ this segment's after array appropriately. Specifically, for any characters that
+ are rendered in both, set this segment's after value to something invalid.
+ OBSOLETE
+----------------------------------------------------------------------------------------------*/
+void Segment::AdjustAfterArrayFromNextSeg()
+{
+ Assert(m_psegNext);
+
+ int ichwNextMin = m_psegNext->m_ichwMin;
+ int ichwNextContextMin = ichwNextMin + m_psegNext->m_ichwAssocsMin;
+ int ichwNextAssocsMin = m_psegNext->m_ichwAssocsMin;
+
+ // Loop over associations prepended to the beginning of the following segment
+ // (ie, rendered in the next segment but officially in this one);
+ // ichwThis and ichwNext are relative to the start of their respective segments.
+ for (int ichwNext = cchwNextAssocsMin; ichwNext < 0; ++ichwNext)
+ {
+ int ichwThis = ichwNext + (m_ichwLim - m_ichwMin);
+ if (m_psegNext->m_prgisloutAfter[ichwNext - ichwNextAssocsMin] > kNegInfinity)
+ // The "after" value is in the next segment, so make this segment's
+ // after value invalid.
+ m_prgisloutAfter[ichwThis - m_ichwAssocsMin] = kPosInfinity;
+ }
+
+ // Loop over associations appended to the end of this segment
+ // (ie, rendered in this segment but officially in the next one);
+ // ichwThis and ichwNext are relative to the start of their respective segments.
+ for (int ichwThis = m_ichwLim - m_ichwMin; ichwThis < m_cchwAssocsLim; ++ichwThis)
+ {
+ int ichwNext = ichwThis - (m_ichwLim - m_ichwMin);
+ if (m_psegNext->m_prgisloutAfter[ichwNext - ichwNextAssocsMin] > kNegInfinity)
+ // The "after" value is in the next segment, so make this segment's
+ // after value invalid.
+ m_prgisloutAfter[ichwThis - m_ichwAssocsMin] = kPosInfinity;
+ }
+}
+
+#endif // -------------------------- END OF OBSOLETE CODE ----------------------------------
+
+
+/*----------------------------------------------------------------------------------------------
+ Generate a list of all the glyphs that are attached to the base with the given index.
+ Note that we want to generate glyph indices, not slot indices.
+
+ @param disloutCluster - indicates how far on either side of the base to look.
+----------------------------------------------------------------------------------------------*/
+void Segment::ClusterMembersForGlyph(int isloutBase, int disloutCluster,
+ std::vector<int> & visloutRet)
+{
+ for (int islout = max(0, isloutBase - disloutCluster);
+ islout < min(m_cslout, isloutBase + disloutCluster + 1);
+ islout++)
+ {
+ if (islout == isloutBase)
+ continue; // don't include the base itself
+ GrSlotOutput * pslout = m_prgslout + islout;
+ if (pslout->ClusterBase() == isloutBase)
+ {
+ visloutRet.push_back(islout);
+ }
+ }
+}
+
+/*----------------------------------------------------------------------------------------------
+ Record an underlying-to-surface association mapping, based on the fact that there is
+ a corresponding surface-to-underlying association in the streams.
+
+ @param ichwUnder - character index relative to the official beginning of the segment
+ @param islotSurface - surface glyph it maps to
+----------------------------------------------------------------------------------------------*/
+void Segment::RecordSurfaceAssoc(int ichwUnder, int islotSurface, int nDir)
+{
+ // If we are recording an association across the line boundary, make sure there
+ // is space for it.
+ EnsureSpaceAtLineBoundaries(ichwUnder);
+
+ // For left-to-right chars, the before-value is the minimum of the previous
+ // and new values, and the after-value is the maximum.
+ if (nDir % 2 == 0)
+ {
+ // left-to-right
+ m_prgisloutBefore[ichwUnder - m_ichwAssocsMin] =
+ min(m_prgisloutBefore[ichwUnder - m_ichwAssocsMin], islotSurface);
+ m_prgisloutAfter[ ichwUnder - m_ichwAssocsMin] =
+ max(m_prgisloutAfter[ ichwUnder - m_ichwAssocsMin], islotSurface);
+ }
+ else
+ {
+ // right-to-left: the before-value is the max and the after-value is the min
+ m_prgisloutBefore[ichwUnder - m_ichwAssocsMin] =
+ max(m_prgisloutBefore[ichwUnder - m_ichwAssocsMin], islotSurface);
+ m_prgisloutAfter[ ichwUnder - m_ichwAssocsMin] =
+ min(m_prgisloutAfter[ ichwUnder - m_ichwAssocsMin], islotSurface);
+ }
+
+ m_prgpvisloutAssocs[ichwUnder - m_ichwAssocsMin]->push_back(islotSurface);
+}
+
+/*----------------------------------------------------------------------------------------------
+ Record an underlying-to-surface ligature mapping, based on the fact that there is
+ a component.ref attribute in the surface stream. Note that if there was a previous
+ ligature value there, it will be overwritten.
+
+ @param ichwUnder - character index relative to the official beginning of the segment
+ @param islotSurface - surface glyph it maps to
+ @param iComponent - which component this glyph represents
+----------------------------------------------------------------------------------------------*/
+void Segment::RecordLigature(int ichwUnder, int islotSurface, int iComponent)
+{
+ // If we are recording a mapping across the line boundary, make sure there
+ // is space for it.
+ EnsureSpaceAtLineBoundaries(ichwUnder);
+
+ Assert(m_prgisloutLigature[ichwUnder - m_ichwAssocsMin] == kNegInfinity);
+
+ m_prgisloutLigature[ichwUnder - m_ichwAssocsMin] = islotSurface;
+ Assert(iComponent < kMaxComponentsPerGlyph);
+ m_prgiComponent[ ichwUnder - m_ichwAssocsMin] = sdata8(iComponent);
+}
+
+/*----------------------------------------------------------------------------------------------
+ The given slot is one that is rendered in the previous segment.
+ If it is associated with a character officially in this segment, mark the
+ before-association kNegInfinity.
+
+ @param ichwUnder - character index relative to the official beginning of the segment
+ @param islot - processed glyph it maps to
+----------------------------------------------------------------------------------------------*/
+void Segment::MarkSlotInPrevSeg(int ichwUnder, int islot)
+{
+ if (ichwUnder >= m_ichwAssocsMin)
+ m_prgisloutBefore[ichwUnder - m_ichwAssocsMin] = kNegInfinity;
+}
+
+/*----------------------------------------------------------------------------------------------
+ The given slot is one that is rendered in the following segment.
+ If it is associated with a character officially in this segment, mark the
+ after-association kPosInfinity.
+
+ @param ichwUnder - character index relative to the official beginning of the segment
+ @param islot - processed glyph it maps to
+----------------------------------------------------------------------------------------------*/
+void Segment::MarkSlotInNextSeg(int ichwUnder, int islot)
+{
+ if (ichwUnder < m_ichwAssocsLim)
+ m_prgisloutAfter[ichwUnder - m_ichwAssocsMin] = kPosInfinity;
+}
+
+/*----------------------------------------------------------------------------------------------
+ If we are recording an association across the line boundary (in either direction),
+ make sure there is space for it. This involves adding space to the beginning or
+ the end of the association arrays, and adjusting the min and lim indicators.
+----------------------------------------------------------------------------------------------*/
+void Segment::EnsureSpaceAtLineBoundaries(int ichwUnder)
+{
+ int cchwNewMin = min(ichwUnder, m_ichwAssocsMin);
+ int cchwNewLim = max(ichwUnder+1, m_ichwAssocsLim);
+ if (cchwNewMin < m_ichwAssocsMin || cchwNewLim > m_ichwAssocsLim)
+ {
+ // Make space either at the beginning or the end of the arrays.
+ int cchwPreAdd = m_ichwAssocsMin - cchwNewMin;
+ int cchwPostAdd = cchwNewLim - m_ichwAssocsLim;
+
+ int * prgisloutTmp = m_prgisloutBefore;
+ m_prgisloutBefore = new int[cchwNewLim - cchwNewMin];
+ std::copy(prgisloutTmp, prgisloutTmp + (m_ichwAssocsLim - m_ichwAssocsMin),
+ m_prgisloutBefore + cchwPreAdd);
+ delete[] prgisloutTmp;
+
+ prgisloutTmp = m_prgisloutAfter;
+ m_prgisloutAfter = new int[cchwNewLim - cchwNewMin];
+ std::copy(prgisloutTmp, prgisloutTmp + (m_ichwAssocsLim - m_ichwAssocsMin),
+ m_prgisloutAfter + cchwPreAdd);
+ delete[] prgisloutTmp;
+
+ std::vector<int> ** ppvisloutTmp = m_prgpvisloutAssocs;
+ m_prgpvisloutAssocs = new std::vector<int> * [cchwNewLim - cchwNewMin];
+ std::swap_ranges(m_prgpvisloutAssocs + cchwPreAdd,
+ m_prgpvisloutAssocs + cchwPreAdd +
+ (m_ichwAssocsLim - m_ichwAssocsMin), ppvisloutTmp);
+ delete[] ppvisloutTmp;
+
+ prgisloutTmp = m_prgisloutLigature;
+ m_prgisloutLigature = new int[cchwNewLim - cchwNewMin];
+ std::copy(prgisloutTmp, prgisloutTmp + (m_ichwAssocsLim - m_ichwAssocsMin),
+ m_prgisloutLigature + cchwPreAdd);
+ delete[] prgisloutTmp;
+
+ sdata8 * prgiCompTmp = m_prgiComponent;
+ m_prgiComponent = new sdata8[cchwNewLim - cchwNewMin];
+ std::copy(prgiCompTmp, prgiCompTmp + (m_ichwAssocsLim - m_ichwAssocsMin),
+ m_prgiComponent + cchwPreAdd);
+ delete[] prgiCompTmp;
+
+ // Initialize new slots.
+ int i;
+ for (i = 0; i < cchwPreAdd; ++i)
+ {
+ m_prgisloutBefore[i] = kPosInfinity;
+ m_prgisloutAfter[i] = kNegInfinity;
+ m_prgpvisloutAssocs[i] = new std::vector<int>;
+ m_prgisloutLigature[i] = kNegInfinity;
+ m_prgiComponent[i] = 0;
+ }
+ for (i = m_ichwAssocsLim - m_ichwAssocsMin + cchwPreAdd;
+ i < m_ichwAssocsLim - m_ichwAssocsMin + cchwPreAdd + cchwPostAdd;
+ ++i)
+ {
+ m_prgisloutBefore[i] = kPosInfinity;
+ m_prgisloutAfter[i] = kNegInfinity;
+ m_prgpvisloutAssocs[i] = new std::vector<int>;
+ m_prgisloutLigature[i] = kNegInfinity;
+ m_prgiComponent[i] = 0;
+ }
+ m_ichwAssocsMin = cchwNewMin;
+ m_ichwAssocsLim = cchwNewLim;
+ }
+}
+
+/*----------------------------------------------------------------------------------------------
+ Return the given output slot.
+----------------------------------------------------------------------------------------------*/
+GrSlotOutput * Segment::OutputSlot(int islout)
+{
+ return m_prgslout + islout;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Set the rendering engine to the current one.
+----------------------------------------------------------------------------------------------*/
+void Segment::SetEngine(GrEngine * pgreng)
+{
+ //if (m_preneng)
+ // m_preneng->DecRefCount();
+ m_preneng = pgreng;
+ //if (m_preneng)
+ // m_preneng->IncRefCount();
+}
+
+/*----------------------------------------------------------------------------------------------
+ Set the font.
+----------------------------------------------------------------------------------------------*/
+void Segment::SetFont(Font * pfont)
+{
+ m_pfont = pfont->copyThis();
+
+// m_fBold = pfont->bold();
+// m_fItalic = pfont->italic();
+ // Note that we store the character height (which does not include the internal leading),
+ // not the actual font height, ie, ascent + descent. This is what is used in the LOGFONT.
+ pfont->getFontMetrics(NULL, NULL, &m_xysEmSquare); // m_xysEmSquare = m_pixHeight
+}
+
+/*----------------------------------------------------------------------------------------------
+ Switch the font out.
+ ??? Do we need to change the member variables too?
+----------------------------------------------------------------------------------------------*/
+void Segment::SwitchFont(Font * pfont)
+{
+ m_pfont = pfont;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Return the engine implementation if it is a proper GrEngine, or NULL otherwise.
+----------------------------------------------------------------------------------------------*/
+GrEngine * Segment::EngineImpl()
+{
+ return m_preneng;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Set the justification agent, in case this segment will ever need to be stretched
+ or shrunk.
+----------------------------------------------------------------------------------------------*/
+void Segment::SetJustifier(IGrJustifier * pgjus)
+{
+ m_pgjus = pgjus;
+ //if (pgjus)
+ // pgjus->JustifierObject(&m_pgjus);
+ //else
+ // m_pgjus = NULL;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Set the graphics object to use the base font.
+----------------------------------------------------------------------------------------------*/
+//void Segment::SetToBaseFont(IGrGraphics * pgg)
+//{
+// if (m_fUseSepBase)
+// EngineImpl()->SwitchGraphicsFont(pgg, true);
+//}
+
+/*----------------------------------------------------------------------------------------------
+ Restore the graphics object to use the font that it originally had when passed in to
+ the interface method.
+----------------------------------------------------------------------------------------------*/
+//void Segment::RestoreFont(IGrGraphics * pgg)
+//{
+// if (m_fUseSepBase)
+// EngineImpl()->SwitchGraphicsFont(pgg, false);
+//}
+
+/*----------------------------------------------------------------------------------------------
+ Given a logical surface location, return the underlying position (relative to the
+ beginning of the string). NOTE that we are returning the index of the closest charater
+ (plus an indication of which side the click was on), not a position between characters.
+
+ @param islout - output slot
+ @param xsOffset - x coordinate of physical click, relative to left of glyph;
+ kPosInfinity means all the way to the right;
+ kNegInfinity means all the way to the left
+ @param ysOffset - y coordinate of physical click, relative to TOP of segment (NOT
+ upwards from baseline); kPosInfinity means all the way at
+ the top; kNegInfinity means all the way at the bottom
+ @param dxsGlyphWidth - width of glyph clicked on, or 0 if irrelevant
+ @param dysGlyphHeight - actually line height -- CURRENTLY NOT USED
+ @param pfAfter - return true if they clicked on trailing side; possibly NULL
+----------------------------------------------------------------------------------------------*/
+int Segment::LogicalSurfaceToUnderlying(int islout, float xsOffset, float ysClick,
+ float dxsGlyphWidth, float dysGlyphHeight, bool * pfAfter)
+{
+ Assert(islout >= 0);
+ Assert(islout < m_cslout);
+
+ GrSlotOutput * pslout = OutputSlot(islout);
+ bool fGlyphRtl = SlotIsRightToLeft(pslout);
+
+ int dichw;
+ bool fAfter, fRight;
+
+ float ysForGlyph = pslout->YPosition();
+
+ float dysFontAscent = m_dysFontAscent;
+ ////GrResult res = GetFontAscentSourceUnits(pgg, &dysFontAscent);
+ ////if (ResultFailed(res))
+ //// THROW(WARN(res));
+ dysFontAscent += m_dysXAscent;
+
+ float dysOffset = ysClick - (m_dysAscent - dysFontAscent); // relative to top of 0-baseline text
+
+ // If the slot has components, see if the offset falls within one of them.
+
+ if (pslout->NumberOfComponents() > 0)
+ {
+ GrEngine * pgreng = EngineImpl();
+ if (!pgreng)
+ goto LNotLigature;
+
+ GrGlyphTable * pgtbl = pgreng->GlyphTable();
+
+ for (int icomp = 0; icomp < pslout->NumberOfComponents(); icomp++)
+ {
+ float xsLeft, xsRight, ysTop, ysBottom;
+
+ // TODO: Rework ComponentIndexForGlyph to take slati rather than iForGlyph.
+ int slati = pslout->ComponentId(icomp);
+ int iForGlyph = pgtbl->ComponentIndexForGlyph(pslout->GlyphID(), slati);
+ if (!pgtbl->ComponentBoxLogUnits(m_xysEmSquare, pslout->GlyphID(), iForGlyph,
+ m_mFontEmUnits, m_dysAscent,
+ &xsLeft, &ysTop, &xsRight, &ysBottom))
+ {
+ continue; // component not defined
+ }
+ Assert(xsLeft <= xsRight);
+ Assert(ysTop <= ysBottom);
+ if (xsOffset < xsLeft || xsOffset > xsRight)
+ continue;
+ if ((dysOffset - ysForGlyph) < ysTop || (dysOffset - ysForGlyph) > ysBottom)
+ continue;
+
+ // Click was within the component's box.
+
+ dichw = pslout->UnderlyingComponent(icomp);
+ Assert(m_ichwMin + dichw >= 0);
+ Assert(dichw < m_dichwLim);
+
+ fRight = (xsOffset - xsLeft > xsRight - xsOffset);
+ fAfter = (fGlyphRtl) ? !fRight : fRight;
+ if (pfAfter)
+ *pfAfter = fAfter;
+
+ Assert(GrCharStream::AtUnicodeCharBoundary(m_pgts, m_ichwMin + dichw));
+
+ return m_ichwMin + dichw;
+ }
+ }
+
+LNotLigature:
+
+ // No relevant ligature component.
+ // If the x-offset is less than half way across,
+ // answer the before-association; otherwise answer the after-association.
+
+ if (xsOffset == kPosInfinity)
+ fRight = true;
+ else if (xsOffset == kNegInfinity)
+ fRight = false;
+ else
+ fRight = (xsOffset > (dxsGlyphWidth / 2));
+
+ fAfter = (fGlyphRtl) ? !fRight : fRight;
+
+ int diCharAdj = 0;
+ if (fAfter)
+ {
+ dichw = pslout->AfterAssoc();
+ if (pfAfter)
+ *pfAfter = true;
+ // The following should not be necessary because of the way associations are set up:
+ //while (!GrCharStream::AtUnicodeCharBoundary(m_pgts, ichwBase + dichw + diCharAdj))
+ // diCharAdj++;
+ }
+ else
+ {
+ dichw = pslout->BeforeAssoc();
+ if (pfAfter)
+ *pfAfter = false;
+ // The following should not be necessary because of the way associations are set up:
+ //while (!GrCharStream::AtUnicodeCharBoundary(m_pgts, ichwBase + dichw + diCharAdj))
+ // diCharAdj--;
+ }
+
+ if (dichw == kNegInfinity || dichw == kPosInfinity)
+ return dichw;
+
+ // This should be true because of the way the associations are set up:
+ Assert(GrCharStream::AtUnicodeCharBoundary(m_pgts, m_ichwMin + dichw)); // + diCharAdj));
+
+ return m_ichwMin + dichw + diCharAdj;
+}
+
+/*--------------------------------------------------------------------------------------------*/
+
+int Segment::LogicalSurfaceToUnderlying(int islout, bool fBefore)
+{
+ if (fBefore)
+ return LogicalSurfaceToUnderlying(islout, kNegInfFloat, kNegInfFloat);
+ else
+ return LogicalSurfaceToUnderlying(islout, kPosInfFloat, kPosInfFloat);
+}
+
+/*----------------------------------------------------------------------------------------------
+ Given a physical surface location (an index into the glyph boxes),
+ return the underlying position, relative to the beginning of the string.
+
+ @param igbb - index of glyph
+ @param xsOffset - relative to left side of glyph
+ @param ysOffset - downwards from top of segment (not upwards from baseline)
+----------------------------------------------------------------------------------------------*/
+int Segment::PhysicalSurfaceToUnderlying(
+ int iginf, float xsOffset, float ysOffset,
+ float dxsGlyphWidth, float dysGlyphHeight, bool * pfAfter)
+{
+ int islout = iginf + m_isloutGinf0;
+ return LogicalSurfaceToUnderlying(islout, xsOffset, ysOffset,
+ dxsGlyphWidth, dysGlyphHeight, pfAfter);
+}
+
+/*--------------------------------------------------------------------------------------------*/
+
+int Segment::PhysicalSurfaceToUnderlying(int iginf, bool fBefore)
+{
+ int islout = iginf + m_isloutGinf0;
+ bool fGlyphRtl = SlotIsRightToLeft(OutputSlot(islout));
+ if (fBefore == fGlyphRtl)
+ return PhysicalSurfaceToUnderlying(iginf, kPosInfFloat, kPosInfFloat);
+ else
+ return PhysicalSurfaceToUnderlying(iginf, kNegInfFloat, kNegInfFloat);
+}
+
+/*----------------------------------------------------------------------------------------------
+ Given an underlying position (relative to the beginning of the string),
+ return the logical surface location (ignoring right-to-left).
+ NOTE that ichw indicates a CHARACTER, not a location between characters. So ichw = 3,
+ fBefore = false indicates a selection between characters 3 and 4.
+ For surrogates, it indicates the FIRST of the pair of 16-bit chars.
+----------------------------------------------------------------------------------------------*/
+int Segment::UnderlyingToLogicalSurface(int ichw, bool fBefore)
+{
+ int ichwSegOffset = ichw - m_ichwMin;
+
+ if (ichwSegOffset < m_ichwAssocsMin)
+ return kNegInfinity; // probably rendered in previous segment
+
+
+ // if the buffers aren't even allocated, then it probably means there
+ // wasn't room for any glyphs, so the following segment is most likely
+// else if (ichwSegOffset >= m_ichwAssocsLim)
+ else if (ichwSegOffset >= m_ichwAssocsLim || !m_prgisloutBefore || !m_prgisloutAfter)
+ return kPosInfinity; // probably rendered in following segment
+
+ else if (fBefore)
+ {
+ int isloutRet;
+ int ichw = ichwSegOffset;
+ // If no association has been made, loop forward to the next slot
+ // we are before. As a last resort, answer kPosInfinity, meaning we
+ // aren't before anything.
+ do
+ {
+ isloutRet = m_prgisloutBefore[ichw - m_ichwAssocsMin];
+ do { ++ichw; }
+ while (!GrCharStream::AtUnicodeCharBoundary(m_pgts, ichw));
+ } while (isloutRet == kPosInfinity && ichw < m_ichwAssocsLim);
+ return isloutRet;
+ }
+ else
+ {
+ int isloutRet;
+ int ichw = ichwSegOffset;
+ // If no association has been made, loop backward to the previous slot
+ // we are after. As a last resort, answer kNegInfinity, meaning we
+ // aren't after anything.
+ do
+ {
+ isloutRet = m_prgisloutAfter[ichw - m_ichwAssocsMin];
+ do { --ichw; }
+ while (!GrCharStream::AtUnicodeCharBoundary(m_pgts, ichw));
+ } while (isloutRet == kNegInfinity && ichw >= 0);
+ return isloutRet;
+ }
+ Assert(false); // should never reach here
+}
+
+/*----------------------------------------------------------------------------------------------
+ Given an underlying position (relative to the beginning of the string),
+ return the physical surface location, the index into m_prgginf.
+----------------------------------------------------------------------------------------------*/
+int Segment::UnderlyingToPhysicalSurface(int ichw, bool fBefore)
+{
+ int islout = UnderlyingToLogicalSurface(ichw, fBefore);
+ return LogicalToPhysicalSurface(islout);
+}
+
+/*----------------------------------------------------------------------------------------------
+ Given a logical position (index into output slots), return the physical position,
+ the index into the list of glyphs (m_prgginf).
+----------------------------------------------------------------------------------------------*/
+int Segment::LogicalToPhysicalSurface(int islout)
+{
+ if (islout == kNegInfinity || islout == kPosInfinity)
+ return islout;
+
+ Assert(m_prgginf[islout - m_isloutGinf0].logicalIndex() == (unsigned)(islout - m_isloutGinf0));
+
+ return islout - m_isloutGinf0;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Given an underlying position (relative to the beginning of the string),
+ return the physical surface locations of all the associated surface glyphs.
+ OBSOLETE
+----------------------------------------------------------------------------------------------*/
+#if 0
+void Segment::UnderlyingToPhysicalAssocs(int ichw, std::vector<int> & viginf)
+{
+ int ichwSegOffset = ichw - m_ichwMin;
+
+ if (ichwSegOffset < m_ichwAssocsMin)
+ return; // possibly rendered in previous segment
+
+ else if (ichwSegOffset >= m_ichwAssocsLim)
+ return; // possibly rendered in following segment
+
+ else
+ {
+ std::vector<int> * pvisloutTmp = &(m_prgvisloutAssocs[ichwSegOffset - m_ichwAssocsMin]);
+
+ for (size_t i = 0; i < pvisloutTmp->size(); i++)
+ {
+ int islout = (*pvisloutTmp)[i];
+ Assert(islout != kNegInfinity);
+ Assert(islout != kPosInfinity);
+ viginf.push_back(LogicalToPhysicalSurface(islout));
+ }
+ }
+}
+#endif
+
+/*----------------------------------------------------------------------------------------------
+ Given an underlying character position (relative to the beginning of the string),
+ return a pointer to the vector containing the logical surface locations of
+ all the associated surface glyphs.
+
+ Returns an empty vector (not something containing infinities) if the character is
+ "invisible."
+----------------------------------------------------------------------------------------------*/
+std::vector<int> Segment::UnderlyingToLogicalAssocs(int ichw)
+{
+ std::vector<int> vnEmpty;
+ vnEmpty.clear();
+ Assert(vnEmpty.size() == 0);
+
+ int ichwSegOffset = ichw - m_ichwMin;
+
+ if (ichwSegOffset < m_ichwAssocsMin)
+ return vnEmpty; // probably rendered in previous segment
+
+ else if (ichwSegOffset >= m_ichwAssocsLim)
+ return vnEmpty; // probably rendered in following segment
+
+ else if (m_prgpvisloutAssocs[ichwSegOffset - m_ichwAssocsMin] == NULL)
+ {
+ // Create a vector using the before and after values.
+ std::vector<int> visloutRet;
+ int isloutBefore = m_prgisloutBefore[ichwSegOffset - m_ichwAssocsMin];
+ int isloutAfter = m_prgisloutAfter[ichwSegOffset - m_ichwAssocsMin];
+ if (isloutBefore != kPosInfinity && isloutBefore != kNegInfinity)
+ visloutRet.push_back(isloutBefore);
+ if (isloutAfter != kPosInfinity && isloutAfter != kNegInfinity && isloutBefore != isloutAfter)
+ {
+ visloutRet.push_back(isloutAfter);
+ }
+ return visloutRet;
+ }
+ else
+ {
+ std::vector<int> * pvisloutRet = m_prgpvisloutAssocs[ichwSegOffset - m_ichwAssocsMin];
+ return *pvisloutRet;
+ }
+}
+
+/*----------------------------------------------------------------------------------------------
+ Given an underlying character position (relative to the beginning of the string),
+ return an index of a slot in this segment that can used to test things like
+ InsertBefore. Don't return kNegInfinity or kPosInfinity except as a last resort.
+----------------------------------------------------------------------------------------------*/
+int Segment::UnderlyingToLogicalInThisSeg(int ichw)
+{
+ int isloutTest = kNegInfinity;
+ std::vector<int> vislout = UnderlyingToLogicalAssocs(ichw);
+ for (size_t iislout = 0; iislout < vislout.size(); iislout++)
+ {
+ isloutTest = vislout[iislout];
+ if (isloutTest != kNegInfinity && isloutTest != kPosInfinity)
+ return isloutTest;
+ }
+ return isloutTest;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Return true if the two characters map to exactly the same set of glyphs.
+----------------------------------------------------------------------------------------------*/
+bool Segment::SameSurfaceGlyphs(int ichw1, int ichw2)
+{
+ std::vector<int> vislout1 = UnderlyingToLogicalAssocs(ichw1);
+ std::vector<int> vislout2 = UnderlyingToLogicalAssocs(ichw2);
+
+ bool fRet = true;
+ if (vislout1.size() == 0 || vislout2.size() == 0)
+ fRet = false;
+ else if (vislout1.size() != vislout2.size())
+ fRet = false;
+ else
+ {
+ for (size_t islout = 0; islout < vislout1.size(); islout++)
+ {
+ if (vislout1[islout] != vislout2[islout])
+ {
+ fRet = false;
+ break;
+ }
+ }
+ }
+ return fRet;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Return the direction level of the given character.
+----------------------------------------------------------------------------------------------*/
+int Segment::DirLevelOfChar(int ichw, bool fBefore)
+{
+ if (m_twsh == ktwshOnlyWs)
+ return m_nDirDepth;
+ int islot = UnderlyingToLogicalSurface(ichw, fBefore);
+ if (islot == kNegInfinity || islot == kPosInfinity)
+ return 0;
+ int nDir = OutputSlot(islot)->DirLevel();
+ if (nDir == -1)
+ // Not calculated: assume to be the top direction level.
+ nDir = TopDirLevel();
+
+ return nDir;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Return whether the given character is right-to-left.
+----------------------------------------------------------------------------------------------*/
+int Segment::CharIsRightToLeft(int ichw, bool fBefore)
+{
+ int nDir = DirLevelOfChar(ichw, fBefore);
+ return ((nDir % 2) != 0);
+}
+
+/*----------------------------------------------------------------------------------------------
+ Return whether the given slot is right-to-left.
+----------------------------------------------------------------------------------------------*/
+int Segment::SlotIsRightToLeft(GrSlotOutput * pslout)
+{
+ if (m_twsh == ktwshOnlyWs)
+ return (m_nDirDepth % 2);
+ int nDir = pslout->DirLevel();
+ if (nDir == -1)
+ nDir = TopDirLevel();
+ return (nDir % 2);
+}
+
+/*----------------------------------------------------------------------------------------------
+ Check to make sure that the attachments are valid. Specifically, for every glyph that
+ is part of a cluster, all glyphs between that glyph and its root must (ultimately) have
+ that root glyph as one of its roots.
+ OBSOLETE
+----------------------------------------------------------------------------------------------*/
+void Segment::AssertValidClusters(GrSlotStream * psstrm)
+{
+#ifdef _DEBUG
+ for (int islot = 0; islot < psstrm->WritePos(); islot++)
+ {
+ GrSlotState * pslotThis = psstrm->SlotAt(islot);
+ GrSlotState * pslotRoot = pslotThis->AttachRoot(psstrm);
+ if (!pslotRoot)
+ continue;
+ Assert(pslotRoot != pslotThis);
+
+ int inc = (pslotThis->PosPassIndex() > pslotRoot->PosPassIndex()) ? -1 : 1;
+
+ for (int islot2 = pslotThis->PosPassIndex() + inc;
+ islot2 != pslotRoot->PosPassIndex();
+ islot2 += inc)
+ {
+ GrSlotState * pslotMid = psstrm->SlotAt(islot2);
+ Assert(pslotMid->HasAsRoot(psstrm, pslotRoot));
+ }
+ }
+#endif // _DEBUG
+}
+
+/*----------------------------------------------------------------------------------------------
+ Return information about (editing) clusters as appropriate for Uniscribe, ie, consistent
+ with the ScriptShape funciton.
+ A cluster includes all attachments, all clumps of characters created by insert = false,
+ and clumps of reordered glyphs.
+
+ prgiginfFirstOfCluster returns the first glyph of the cluster for each CHARACTER
+ (glyph indices returned are zero-based and refer to physical order--left-to-right, etc.).
+ pfClusterStart returns a flag for each GLYPH indicating whether it is the first (left-most)
+ of a cluster.
+ Both arrays are optional.
+ CORRECTION: for now we use strictly logical order. It's not clear whether physical
+ order is needed for right-to-left text or not. See note below.
+
+ TODO: convert from GlyphInfo objects which are RTL.
+----------------------------------------------------------------------------------------------*/
+GrResult Segment::getUniscribeClusters(
+ int * prgiginfFirstOfCluster, int cchMax, int * pcch,
+ bool * pfClusterStart, int cfMax, int * pcf)
+{
+ ChkGrOutPtr(pcch);
+ if (prgiginfFirstOfCluster)
+ ChkGrArrayArg(prgiginfFirstOfCluster, cchMax);
+ ChkGrOutPtr(pcf);
+ if (pfClusterStart)
+ ChkGrArrayArg(pfClusterStart, cfMax);
+
+ GrResult res = kresOk;
+
+ //SetUpGraphics(ichwBase, pgg, true);
+
+ if (m_dxsWidth == -1)
+ ComputeDimensions();
+
+ Assert(m_dxsWidth >= 0);
+ Assert(m_dysAscent >= 0);
+
+ if (pcch)
+ *pcch = m_dichwLim;
+ if (pcf)
+ *pcf = m_cginf;
+ if (cchMax < m_dichwLim)
+ {
+ if (cchMax == 0 && cfMax == 0)
+ res = kresFalse; // just asking for size information
+ else if (prgiginfFirstOfCluster || cchMax > 0)
+ res = kresInvalidArg; // not enough space
+ }
+ if (cfMax < m_cginf)
+ {
+ if (cchMax == 0 && cfMax == 0)
+ res = kresFalse;
+ else if (pfClusterStart || cfMax > 0)
+ res = kresInvalidArg;
+ }
+ if (res != kresOk)
+ {
+ //RestoreFont(pgg);
+ ReturnResult(res);
+ }
+
+ // Generate a pair of arrays, indicating the first and last (logical) slots
+ // of the cluster for each character. Initially clusters are based just on
+ // character-to-glyph associations, but they expand as we process
+ // insert=false settings, attachments, and reordering.
+
+ std::vector<int> visloutBefore;
+ std::vector<int> visloutAfter;
+ visloutBefore.resize(m_dichwLim);
+ visloutAfter.resize(m_dichwLim);
+
+ int ich;
+ for (ich = 0; ich < m_dichwLim; ich++)
+ {
+ visloutBefore[ich] = m_prgisloutBefore[ich - m_ichwAssocsMin];
+ visloutAfter[ich] = m_prgisloutAfter[ich - m_ichwAssocsMin];
+ }
+
+ if (m_dichwLim == 1)
+ {
+ // Only one character, therefore only one cluster.
+ visloutBefore[0] = m_isloutGinf0;
+ visloutAfter[0] = max(m_cginf - 1, 0) + m_isloutGinf0;
+ }
+ else
+ {
+ // Make all glyphs attached to each other part of a cluster, or glyphs where
+ // insertion is not allowed.
+ for (ich = 0; ich < m_dichwLim; ich++)
+ {
+ std::vector<int> vislout = UnderlyingToLogicalAssocs(ich + m_ichwMin);
+
+ if (vislout.size() == 0)
+ {
+ // No glyphs represent this character. Merge it with the previous
+ // character.
+ visloutBefore[ich] = (ich == 0) ? visloutBefore[ich + 1] : visloutBefore[ich - 1];
+ visloutAfter[ich] = (ich == 0) ? visloutAfter[ich + 1] : visloutAfter[ich - 1];
+ continue;
+ }
+
+ size_t iislout;
+ for (iislout = 0; iislout < vislout.size(); iislout++)
+ {
+ int islout = vislout[iislout];
+ if (!m_prgslout[islout].InsertBefore())
+ {
+ // This glyph does not allow insertion before it; make its character part
+ // of the same cluster as the previous character.
+ MergeUniscribeCluster(visloutBefore, visloutAfter, ich - 1, ich);
+/*
++ // Keith Stribley's fix:
++ // NOTE: this is NOT always the same as the previous character, so
++ // check whether the next character actually has a glyph before this
++ // one
++ if ((ich == m_dichwLim - 1) ||
++ (std::min(visloutBefore[ich + 1], visloutAfter[ich + 1]) >
++ std::max(visloutBefore[ich], visloutAfter[ich])))
++ {
++ MergeUniscribeCluster(visloutBefore, visloutAfter, ich - 1, ich);
++ }
++ else
++ MergeUniscribeCluster(visloutBefore, visloutAfter, ich, ich + 1);
+*/
+ }
+ else if (m_prgslout[islout].ClusterBase() != -1
+ && m_prgslout[islout].ClusterBase() != islout)
+ {
+ // This glyph is attached to something; make them part of the same cluster.
+ int isloutBase = m_prgslout[islout].ClusterBase();
+ int ichBase = LogicalSurfaceToUnderlying(isloutBase, false) - m_ichwMin;
+ MergeUniscribeCluster(visloutBefore, visloutAfter,
+ ich, ichBase);
+ int ichBase1 = LogicalSurfaceToUnderlying(isloutBase, false) - m_ichwMin;
+ if (ichBase1 != ichBase)
+ MergeUniscribeCluster(visloutBefore, visloutAfter,
+ ich, ichBase1);
+ }
+ }
+ }
+
+ // Handle reordering. Whenever the islout values get out of order, we need to
+ // merge into a single cluster.
+ for (ich = 0; ich < m_dichwLim - 1; ich++)
+ {
+ Assert(visloutBefore[ich] <= visloutAfter[ich]);
+ Assert(visloutBefore[ich] != kPosInfinity);
+ Assert(visloutBefore[ich] != kNegInfinity);
+ Assert(visloutAfter[ich] != kPosInfinity);
+ Assert(visloutAfter[ich] != kNegInfinity);
+ const int b1 = visloutBefore[ich];
+ const int a1 = visloutAfter[ich];
+ const int b2 = visloutBefore[ich + 1];
+ const int a2 = visloutAfter[ich + 1];
+ if (b1 == b2 && a1 == a2)
+ continue; // already a cluster
+
+ if (std::min(b2, a2) <= std::max(b1, a1))
+ {
+ MergeUniscribeCluster(visloutBefore, visloutAfter, ich, ich + 1);
+ }
+ }
+ }
+
+ if (pfClusterStart)
+ {
+ // Initialize.
+ int iginf;
+ for (iginf = 0; iginf < m_cginf; iginf++)
+ pfClusterStart[iginf] = false;
+ }
+
+ // To skip line-break slots; remember that the number of output slots equals the
+ // number of gbbs plus initial and/or final line-break slots if any.
+ // In this case the beginning of the first cluster indicates the first real glyph.
+ int isloutFirstReal = (m_dichwLim) ? ((m_fWsRtl)? visloutAfter[0] : visloutBefore[0]) : 0;
+
+ // Kludge to make assertions below easier:
+ if (m_dichwLim > 0)
+ {
+ visloutBefore.push_back(visloutAfter.back() + 1);
+ visloutAfter.push_back(visloutAfter.back() + 1);
+ }
+
+ // Convert (logical) slots to actual glyphs.
+ for (ich = 0; ich < m_dichwLim; ich++)
+ {
+ // NOTE: to treat the first of the cluster as the left-most, use visloutAfter for
+ // right-to-left situations.
+
+ if (prgiginfFirstOfCluster)
+ prgiginfFirstOfCluster[ich]
+ = LogicalToPhysicalSurface((m_fWsRtl) ? visloutAfter[ich] : visloutBefore[ich]);
+ int clusterStartPos = ((m_fWsRtl) ? visloutAfter[ich] : visloutBefore[ich]) - isloutFirstReal;
+ if (pfClusterStart)
+ pfClusterStart[clusterStartPos] = true;
+
+ Assert((visloutBefore[ich] == visloutBefore[ich + 1] &&
+ visloutAfter[ich] == visloutAfter[ich + 1])
+ || (visloutBefore[ich] <= visloutAfter[ich] &&
+ visloutAfter[ich] < visloutBefore[ich + 1]));
+ }
+
+ //RestoreFont(pgg);
+
+ return kresOk;
+}
+
+
+// suppress GCC 4.3 warning for optimized min()/max() when called with (ich, ich+1) or similar
+#pragma GCC diagnostic ignored "-Wstrict-overflow"
+
+/*----------------------------------------------------------------------------------------------
+ Merge the given characters into the same Uniscribe cluster. This means merging any
+ intervening characters as well.
+----------------------------------------------------------------------------------------------*/
+void Segment::MergeUniscribeCluster(
+ std::vector<int> & visloutBefore, std::vector<int> & visloutAfter,
+ int ich1, int ich2)
+{
+ int ichStartOrig = min(ich1, ich2);
+ int ichStopOrig = max(ich1, ich2);
+
+ int isloutMin = visloutBefore[ichStopOrig];
+ int isloutMax = visloutAfter[ichStartOrig];
+
+ int ichStart = ichStopOrig;
+ int ichStop = ichStartOrig;
+ // Find the beginning of the cluster.
+ while (ichStart > 0 &&
+ (ichStart > ichStartOrig
+ || visloutBefore[ichStart - 1] >= visloutBefore[ichStopOrig]
+ || visloutBefore[ichStart - 1] == visloutBefore[ichStart]))
+ {
+ ichStart--;
+ isloutMin = std::min(isloutMin, visloutBefore[ichStart]);
+ isloutMax = std::max(isloutMax, visloutAfter[ichStart]);
+ }
+ // Find end of cluster.
+ while (ichStop < m_dichwLim - 1 &&
+ (ichStop < ichStopOrig
+ || visloutAfter[ichStop + 1] <= visloutAfter[ichStartOrig]
+ || visloutAfter[ichStop + 1] == visloutAfter[ichStop]))
+ {
+ ichStop++;
+ isloutMin = std::min(isloutMin, visloutBefore[ichStop]);
+ isloutMax = std::max(isloutMax, visloutAfter[ichStop]);
+ }
+
+ int ich;
+ for (ich = ichStart; ich <= ichStop; ich++)
+ {
+ visloutBefore[ich] = isloutMin;
+ visloutAfter[ich] = isloutMax;
+ }
+}
+
+/*----------------------------------------------------------------------------------------------
+ Return information about the positioning of glyphs as appropriate for Uniscribe.
+ The advance widths returned are the natural ones (what would be applied if no smart
+ rendering were happening), and the x-offsets are the difference between the
+ natural location of the glyph and what is produced by Graphite. The y-offset
+ is the same as GetGlyphsAndPositions.
+
+ This is an experimental method. Its behavior may change signficantly in the future.
+----------------------------------------------------------------------------------------------*/
+/*
+GrResult Segment::GetUniscribePositions(
+ Rect rs, Rect rd, int cgidMax, int *pcgidRet,
+ float * prgxd, float * prgyd, float * prgdxdAdv)
+{
+ ChkGrOutPtr(pcgidRet);
+ ChkGrArrayArg(prgxd, cgidMax);
+ ChkGrArrayArg(prgyd, cgidMax);
+ ChkGrArrayArg(prgdxdAdv, cgidMax);
+
+ GrResult res;
+
+ //SetUpGraphics(ichBase, pgg, true);
+
+ Assert(m_dxsWidth >= 0);
+ Assert(m_dysAscent >= 0);
+
+ *pcgidRet = m_cginf;
+ if (cgidMax < m_cginf)
+ {
+ res = (cgidMax == 0) ? kresFalse : kresInvalidArg;
+ //RestoreFont(pgg);
+ ReturnResult(res);
+ }
+
+ if (m_dxsWidth < 0)
+ ComputeDimensions();
+
+ float * rgxdTmp = new float[cgidMax];
+ float * rgdxdAdvTmp = new float[cgidMax];
+ res = GetGlyphsAndPositions(rs, rd, cgidMax, pcgidRet,
+ NULL, rgxdTmp, prgyd, rgdxdAdvTmp);
+ if (ResultFailed(res))
+ {
+ delete[] rgxdTmp;
+ delete[] rgdxdAdvTmp;
+ //RestoreFont(pgg);
+ ReturnResult(res);
+ }
+
+ float xdLeft = SegmentPainter::ScaleX(0, rs, rd);
+ float xdXPosNatural = xdLeft;
+ for (int iginf = 0; iginf < *pcgidRet; iginf++)
+ {
+ int islout = iginf + m_isloutGinf0;
+ GrSlotOutput * pslout = OutputSlot(islout);
+ float dxsAdvNatural = pslout->AdvanceXMetric();
+ float dxdAdvNatural = SegmentPainter::ScaleX(dxsAdvNatural, rs, rd) - xdLeft;
+ prgdxdAdv[iginf] = dxdAdvNatural;
+ prgxd[iginf] = rgxdTmp[iginf] - xdXPosNatural;
+
+ xdXPosNatural += dxdAdvNatural;
+ }
+
+ delete[] rgxdTmp;
+ delete[] rgdxdAdvTmp;
+
+ //RestoreFont(pgg);
+
+ ReturnResult(res);
+
+}
+*/
+
+/*----------------------------------------------------------------------------------------------
+ Return information about the positioning of glyphs as appropriate for Uniscribe.
+ The advance widths returned are the natural ones (what would be applied if no smart
+ rendering were happening), and the x-offsets are the difference between the
+ natural location of the glyph and what is produced by Graphite. The y-offset
+ is the same as GetGlyphsAndPositions.
+
+ This is an experimental method. Its behavior may change signficantly in the future.
+----------------------------------------------------------------------------------------------*/
+/*
+GrResult Segment::GetUniscribeGlyphsAndPositions(
+ Rect rs, Rect rd, int cgidMax, int *pcgidRet, utf16 * prgchGlyphs,
+ float * prgxd, float * prgyd, float * prgdxdAdv)
+{
+ ChkGrOutPtr(pcgidRet);
+ ChkGrArrayArg(prgxd, cgidMax);
+ ChkGrArrayArg(prgyd, cgidMax);
+ ChkGrArrayArg(prgdxdAdv, cgidMax);
+
+ GrResult res;
+
+ //SetUpGraphics(ichBase, pgg, true);
+
+ Assert(m_dxsWidth >= 0);
+ Assert(m_dysAscent >= 0);
+
+ *pcgidRet = m_cginf;
+ if (cgidMax < m_cginf)
+ {
+ res = (cgidMax == 0) ? kresFalse : kresInvalidArg;
+ //RestoreFont(pgg);
+ ReturnResult(res);
+ }
+
+ if (m_dxsWidth < 0)
+ ComputeDimensions();
+
+ float * rgxdTmp = new float[cgidMax];
+ float * rgdxdAdvTmp = new float[cgidMax];
+ res = GetGlyphsAndPositions(rs, rd, cgidMax, pcgidRet,
+ prgchGlyphs, rgxdTmp, prgyd, rgdxdAdvTmp);
+ if (ResultFailed(res))
+ {
+ delete[] rgxdTmp;
+ delete[] rgdxdAdvTmp;
+ //RestoreFont(pgg);
+ ReturnResult(res);
+ }
+
+ float xdLeft = SegmentPainter::ScaleX(0, rs, rd);
+ float xdXPosNatural = xdLeft;
+ for (int iginf = 0; iginf < *pcgidRet; iginf++)
+ {
+ int islout = iginf + m_isloutGinf0;
+ GrSlotOutput * pslout = OutputSlot(islout);
+ float dxsAdvNatural = pslout->AdvanceXMetric();
+ float dxdAdvNatural = SegmentPainter::ScaleX(dxsAdvNatural, rs, rd) - xdLeft;
+ prgdxdAdv[iginf] = dxdAdvNatural;
+ prgxd[iginf] = rgxdTmp[iginf] - xdXPosNatural;
+
+ xdXPosNatural += dxdAdvNatural;
+ }
+
+ delete[] rgxdTmp;
+ delete[] rgdxdAdvTmp;
+
+ //RestoreFont(pgg);
+
+ ReturnResult(res);
+}
+*/
+
+/*----------------------------------------------------------------------------------------------
+ Return the visual box for the given ligature component. Return (0,0,0,0) if the component
+ is not defined.
+----------------------------------------------------------------------------------------------*/
+Rect Segment::ComponentRect(GrSlotOutput * pslout, int icomp)
+{
+ Rect rectRet;
+ rectRet.top = rectRet.bottom = rectRet.left = rectRet.right = 0;
+ if (icomp < 0 || icomp > pslout->NumberOfComponents())
+ return rectRet;
+
+ GrEngine * pgreng = EngineImpl();
+ if (!pgreng)
+ return rectRet;
+ GrGlyphTable * pgtbl = pgreng->GlyphTable();
+ if (!pgtbl)
+ return rectRet;
+
+ float xsLeft, xsRight, ysTop, ysBottom;
+
+ // TODO: Rework ComponentIndexForGlyph to take slati rather than iForGlyph.
+ int slati = pslout->ComponentId(icomp);
+ int iForGlyph = pgtbl->ComponentIndexForGlyph(pslout->GlyphID(), slati);
+ if (!pgtbl->ComponentBoxLogUnits(m_xysEmSquare, pslout->GlyphID(), iForGlyph,
+ m_mFontEmUnits, m_dysAscent,
+ &xsLeft, &ysTop, &xsRight, &ysBottom,
+ false)) // origin is baseline
+ {
+ return rectRet;
+ }
+ rectRet.top = ysTop;
+ rectRet.bottom = ysBottom;
+ rectRet.left = xsLeft;
+ rectRet.right = xsRight;
+
+ return rectRet;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Shift the glyphs physically by the given amount. This happens when a right-to-left
+ segment is changed from being end-of-line to not, so we have to account for the fact
+ that the trailing white-space was previous invisible but is now visible.
+----------------------------------------------------------------------------------------------*/
+void Segment::ShiftGlyphs(float dxsShift)
+{
+ for (int islout = 0; islout < m_cslout; islout++)
+ {
+ GrSlotOutput * pslout = OutputSlot(islout);
+ pslout->AdjustPosXBy(dxsShift);
+ }
+
+ //ShiftGlyphsPlatform(dxsShift);
+}
+
+/*----------------------------------------------------------------------------------------------
+ Find the glyph that best matches the given click location.
+
+ @param xsClick - relative to start of segment
+ @param ysClick - relative to segment baseline
+----------------------------------------------------------------------------------------------*/
+int Segment::GlyphHit(float xsClick, float ysClick)
+{
+ float xsLeft = kPosInfFloat;
+ float xsRight = kNegInfFloat;
+
+ int iginf;
+ for (iginf = 0; iginf < m_cginf; iginf++)
+ {
+ GlyphInfo * pginf = m_prgginf + iginf;
+ xsLeft = min(xsLeft, pginf->origin());
+ xsRight = max(xsRight, pginf->origin() + pginf->advanceWidth());
+ }
+
+ if (xsClick < xsLeft)
+ return m_fWsRtl ? m_cginf - 1 : 0;
+ else if (xsClick > xsRight)
+ return m_fWsRtl ? 0 : m_cginf - 1;
+
+ int iginfHit;
+ std::vector<int> viginfNear;
+ std::vector<int> viginfHit;
+ std::vector<int> viginfInside;
+
+ // Find glyph which might contain click point.
+
+ for (iginfHit = m_cginf; iginfHit-- > 0; )
+ {
+ if (m_fWsRtl)
+ {
+ if (m_prgginf[iginfHit].bb().right >= xsClick)
+ break;
+ }
+ else
+ {
+ if (m_prgginf[iginfHit].bb().left <= xsClick)
+ break;
+ }
+ }
+ if (iginfHit < 0) // click within lsb of first glyph
+ {
+ return 0;
+ }
+
+ // Find all BB that OVERLAP the click point (bb.left < XClick < bb.right).
+ for (iginf = iginfHit; iginf >= 0; iginf--)
+ {
+ Rect rectBB = m_prgginf[iginf].bb();
+ if (rectBB.left <= xsClick && xsClick <= rectBB.right)
+ {
+ viginfNear.push_back(iginf);
+ }
+ }
+
+ // Find all BB that CONTAIN the click point (considering the click's Y coor too).
+ int iiginf;
+ for (iiginf = 0; iiginf < (int)viginfNear.size(); iiginf++)
+ {
+ Rect rectBB = m_prgginf[viginfNear[iiginf]].bb();
+ if (rectBB.bottom <= ysClick && ysClick <= rectBB.top)
+ {
+ viginfHit.push_back(viginfNear[iiginf]);
+ }
+ }
+
+ if (viginfNear.size() > 2 && viginfHit.size() == 0)
+ {
+ // No hit along y-axis; find the closest thing.
+ float dy = float(10000000.0);
+ for (int iiginf2 = 0; iiginf2 < (int)viginfNear.size(); iiginf2++)
+ {
+ Rect rectBB = m_prgginf[viginfNear[iiginf2]].bb();
+ if (fabsf(rectBB.top - ysClick) < dy)
+ {
+ dy = fabsf(rectBB.top - ysClick);
+ viginfHit.clear();
+ viginfHit.push_back(viginfNear[iiginf2]);
+ }
+ if (fabsf(rectBB.bottom - ysClick) < dy)
+ {
+ dy = fabsf(rectBB.bottom - ysClick);
+ viginfHit.clear();
+ viginfHit.push_back(viginfNear[iiginf2]);
+ }
+ }
+ }
+
+ // Find all BB for which the click is within their advance width, if any.
+ for (iiginf = 0; iiginf < (int)viginfHit.size(); iiginf++)
+ {
+ //Rect rectBB = m_prgginf[viginfHit[iiginf]].bb(); -- ?? this tests the bounding box again :-/
+ //if (rectBB.left <= xsClick && xsClick <= rectBB.right)
+ gr::GlyphInfo * pginf = m_prgginf + viginfHit[iiginf];
+ if (pginf->advanceWidth() == 0 // advance width is irrelevant
+ || (pginf->origin() < xsClick && xsClick <= pginf->origin() + pginf->advanceWidth()))
+ {
+ viginfInside.push_back(viginfHit[iiginf]);
+ }
+ }
+
+ // If no BB coincides in any way with the click point, take the BB whose leading edge
+ // is closest to the click point.
+ // There could be more than one BB with the same leading x coor.
+ if (viginfHit.size() == 0 && viginfNear.size() == 0 && viginfInside.size() == 0)
+ {
+ float xsLeading;
+ if (iginfHit + 1 == m_cginf)
+ {
+ viginfNear.push_back(iginfHit);
+ }
+ else if (!m_fWsRtl)
+ {
+ // Left-to-right:
+ xsLeading = m_prgginf[iginfHit + 1].bb().left;
+ for (iginf = iginfHit + 1; iginf < m_cginf; iginf++)
+ {
+ if (m_prgginf[iginf].bb().left == xsLeading)
+ viginfNear.push_back(iginf);
+ }
+ }
+ else
+ {
+ // Right-to-left:
+ xsLeading = m_prgginf[iginfHit].bb().right;
+ for (iginf = iginfHit; iginf < m_cginf; iginf++)
+ {
+ if (m_prgginf[iginf].bb().right == xsLeading)
+ viginfNear.push_back(iginf);
+ }
+ }
+ }
+
+ Assert(viginfNear.size());
+
+ int iginfLoc;
+
+ // Select the BB based on its leading edge, offset from baseline, position in final pass.
+ if (viginfInside.size())
+ iginfLoc = SelectBb(viginfInside, m_fWsRtl);
+ else if (viginfHit.size())
+ iginfLoc = SelectBb(viginfHit, m_fWsRtl);
+ else
+ iginfLoc = SelectBb(viginfNear, m_fWsRtl);
+
+ return iginfLoc;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Select the bounding box we will use for a mouse hit.
+ First select any glyph with a significantly smaller bounding box.
+ Then, select the BB with the left-most (LTR) or right-most (RTL) edge.
+ If more than one BB has same edge of interest, select the one that is closest to the
+ the baseline.
+ If that is a tie, select the one with the lowest islout.
+
+ @param vigbb - list of indexes into m_prggbb
+ @param fRTL - writing system direction
+----------------------------------------------------------------------------------------------*/
+int Segment::SelectBb(std::vector<int> & viginf, bool fRTL)
+{
+ if (!viginf.size())
+ {
+ Assert(false);
+ return -1;
+ }
+
+ if (viginf.size() == 1)
+ return viginf[0];
+
+ // Find a glyph that has a signficantly smaller bounding box.
+ // The idea is that it is going to be harder to hit than the bigger glyph.
+ float smallestArea = float(1000000000.0);
+ float largestArea = 0.0;
+ size_t iiginfSmallest = 0;
+ size_t iiginf;
+ for (iiginf = 0; iiginf < viginf.size(); iiginf++)
+ {
+ gr::GlyphInfo * pginf = m_prgginf + viginf[iiginf];
+ Rect rectBB = pginf->bb();
+ float thisArea = (rectBB.right - rectBB.left) * (rectBB.top - rectBB.bottom);
+ if (smallestArea > thisArea)
+ {
+ smallestArea = thisArea;
+ iiginfSmallest = iiginf;
+ }
+ largestArea = max(largestArea, thisArea);
+ }
+ if (smallestArea * 2.0 < largestArea)
+ return viginf[iiginfSmallest];
+
+ // Find appropriate x coor of leading edge.
+ float xsLeading;
+ if (!fRTL)
+ xsLeading = m_prgginf[viginf[0]].bb().left;
+ else
+ xsLeading = m_prgginf[viginf[0]].bb().right;
+
+ for (iiginf = 1; iiginf < viginf.size(); iiginf++)
+ {
+ Rect rectBB = m_prgginf[viginf[iiginf]].bb();
+ if (!fRTL)
+ xsLeading = min(xsLeading, rectBB.left);
+ else
+ xsLeading = max(xsLeading, rectBB.right);
+ }
+
+ // Find BBs with the leading edge.
+ std::vector<int> viginfSameEdge;
+ for (iiginf = 0; iiginf < viginf.size(); iiginf++)
+ {
+ Rect rectBB = m_prgginf[viginf[iiginf]].bb();
+ if (!fRTL)
+ {
+ if (rectBB.left == xsLeading)
+ viginfSameEdge.push_back(viginf[iiginf]);
+ }
+ else
+ {
+ if (rectBB.right == xsLeading)
+ viginfSameEdge.push_back(viginf[iiginf]);
+ }
+ }
+
+ if (viginfSameEdge.size() == 1)
+ return viginfSameEdge[0];
+
+ // Find minimum distance from baseline.
+ float ysMin = fabsf(m_prgginf[viginfSameEdge[0]].yOffset());
+ for (iiginf = 1; iiginf < viginfSameEdge.size(); iiginf++)
+ {
+ float ysThis = fabsf(m_prgginf[viginfSameEdge[iiginf]].yOffset());
+ ysMin = min(ysMin, ysThis);
+ }
+
+ // Find BBs with minimum distance from baseline.
+ std::vector<int> viginfSameY;
+ for (iiginf = 0; iiginf < viginfSameEdge.size(); iiginf++)
+ {
+ if (fabsf(m_prgginf[viginfSameEdge[iiginf]].yOffset()) == ysMin)
+ viginfSameY.push_back(viginfSameEdge[iiginf]);
+ }
+
+ if (viginfSameY.size() == 1)
+ return viginfSameY[0];
+
+ // Find minimum index of current candidates.
+ int islout = m_prgginf[viginfSameY[0]].logicalIndex();
+ for (iiginf = 1; iiginf < viginfSameY.size(); iiginf++)
+ {
+ int isloutThis = m_prgginf[viginfSameY[iiginf]].logicalIndex();
+ islout = min(islout, isloutThis);
+ }
+
+ // Find BB with minimum islout - islout should be unique for all glyphs.
+ for (iiginf = 0; iiginf < viginfSameY.size(); iiginf++)
+ {
+ if (m_prgginf[viginfSameY[iiginf]].logicalIndex() == (unsigned)islout)
+ return viginfSameY[iiginf];
+ }
+
+ Assert(false);
+ return -1; // needed to stop compiler warning
+}
+
+/*----------------------------------------------------------------------------------------------
+ Return the right-most glyph on the physical surface. If there are ties, it will
+ prefer the one that is logically closest to the edge.
+----------------------------------------------------------------------------------------------*/
+int Segment::RightMostGlyph()
+{
+ float xsRight = kNegInfFloat;
+ int iginfRet = -1;
+ for (int iginf = 0; iginf < m_cginf; iginf++)
+ {
+ float xsThis = m_prgginf[iginf].bb().right;
+ // For RTL, we prefer the glyph that is logically first; for LTR we prefer logically last.
+ if (xsThis > xsRight || (!m_fWsRtl && xsRight == xsThis))
+ {
+ xsRight = xsThis;
+ iginfRet = iginf;
+ }
+ }
+ return iginfRet;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Return the left-most glyph on the physical surface. If there are ties, it will
+ prefer the one that is logically closest to the edge.
+----------------------------------------------------------------------------------------------*/
+int Segment::LeftMostGlyph()
+{
+ float xsLeft = kPosInfFloat;
+ int iginfRet = -1;
+ for (int iginf = 0; iginf < m_cginf; iginf++)
+ {
+ float xsThis = m_prgginf[iginf].bb().left;
+ // For RTL, we prefer the glyph that is logically last; for LTR we prefer logically first.
+ if (xsThis < xsLeft || (m_fWsRtl && xsLeft == xsThis))
+ {
+ xsLeft = xsThis;
+ iginfRet = iginf;
+ }
+ }
+ return iginfRet;
+}
+
+/*----------------------------------------------------------------------------------------------
+ Convert from em-units to logical units.
+----------------------------------------------------------------------------------------------*/
+float Segment::EmToLogUnits(int m)
+{
+ return GrEngine::GrIFIMulDiv(m, m_xysEmSquare, m_mFontEmUnits);
+}
+
+/*----------------------------------------------------------------------------------------------
+ Return the line-break pseudo glyph ID.
+----------------------------------------------------------------------------------------------*/
+gid16 Segment::LBGlyphID()
+{
+ return m_preneng->LBGlyphID();
+}
+
+} // namespace gr