/// <summary> /// Shorten the current line. Currently this is only called when the last thing on the line /// is a string box. If possible, replace its segment with a shorter one. If this is not possible, /// and it is not the first thing on the line, remove it altogether. If it IS the first thing on /// the line, do nothing. We'll live with the bad break, because there is nothing better we know /// how to do. /// Returns true if successful, which means FinalizeLine should be called again. False if /// unsuccessful, to prevent infinite loop. /// </summary> private bool Backtrack() { var lastBox = m_lines.Last().LastBox as StringBox; while (lastBox != null) { // We want to call FindLineBreak again with the same arguments we used to create the segment // of lastBox, except that ichLimBacktrack should prevent including the last character. // (We expect that is is NOT a white-space-only segment, since we were not able to break after it.) int ichMin = lastBox.IchMin; int ichLim = ichMin + lastBox.Segment.get_Lim(ichMin); //int dxWidthLast = lastBox.Segment.get_Width(ichMin, m_layoutInfo.VwGraphics); int ichLimBacktrack = ichLim - 1; // skip back one if we're splitting a surrogate. if (ichLimBacktrack > 0) { string charsAtLim = Fetch(ichLimBacktrack - 1, ichLimBacktrack + 1); if (Surrogates.IsTrailSurrogate(charsAtLim[1]) && Surrogates.IsLeadSurrogate(charsAtLim[0])) { ichLimBacktrack--; } } int runIndex = m_renderRunIndex; // what would have beem m_renderRunIndex when making lastBox's segment. while (runIndex > 0 && ichMin < m_renderRuns[runIndex].RenderStart) { runIndex--; } var renderer = m_layoutInfo.GetRenderer(m_renderRuns[runIndex].Ws); int ichRunLim = GetLimitOfRunWithSameRenderer(renderer, runIndex); ILgSegment seg; int dichLim, dxWidth; LgEndSegmentType est; bool mustGetSomeSeg = m_currentLine.FirstBox == lastBox; var twsh = LgTrailingWsHandling.ktwshAll; bool runRtl = m_layoutInfo.RendererFactory.RightToLeft(m_renderRuns[runIndex].Ws); bool paraRtl = m_para.Style.RightToLeft; if (runRtl != paraRtl) { twsh = LgTrailingWsHandling.ktwshNoWs; } var spaceLeftOnCurrentLine = m_spaceLeftOnCurrentLine + lastBox.Width; renderer.FindBreakPoint(m_layoutInfo.VwGraphics, m_para.Source, null, ichMin, ichRunLim, ichLimBacktrack, false, mustGetSomeSeg, spaceLeftOnCurrentLine, LgLineBreak.klbWordBreak, mustGetSomeSeg ? LgLineBreak.klbClipBreak : LgLineBreak.klbWordBreak, twsh, false, out seg, out dichLim, out dxWidth, out est, null); if (seg == null) { // can't make any shorter segment here. // Can we backtrack further by getting rid of this box altogether? Box boxBefore = m_lines.Last().BoxBefore(lastBox); if (boxBefore is StringBox) { lastBox = (StringBox)boxBefore; continue; } // Currently backtracking should not need to remove a non-string box, because we always allow // breaking after them. So if boxBefore is not a string box, we can break after it. m_currentLine.RemoveFrom(lastBox); m_ichRendered = ichMin; m_spaceLeftOnCurrentLine = spaceLeftOnCurrentLine; m_renderRunIndex = AdvanceRenderRunIndexToIch(m_ichRendered, m_renderRunIndex); return(true); } m_currentLine.RemoveFrom(lastBox); AddBoxToLine(seg, ichMin, dichLim, est, spaceLeftOnCurrentLine); ILgSegment whiteSpaceSeg; if (twsh == LgTrailingWsHandling.ktwshNoWs) { // Add a white space segment if possible. renderer.FindBreakPoint(m_layoutInfo.VwGraphics, m_para.Source, null, m_ichRendered, ichRunLim, ichRunLim, false, true, m_spaceLeftOnCurrentLine, LgLineBreak.klbWordBreak, LgLineBreak.klbWordBreak, LgTrailingWsHandling.ktwshOnlyWs, false, out whiteSpaceSeg, out dichLim, out dxWidth, out est, null); if (seg != null) { AddBoxToLine(whiteSpaceSeg, m_ichRendered, dichLim, est, m_spaceLeftOnCurrentLine); } } m_renderRunIndex = AdvanceRenderRunIndexToIch(m_ichRendered, m_renderRunIndex); return(true); } return(false); // no better break available, use the line as it is. }