/// <summary> /// Emulate what a real renderer will do, assuming character widths are as determined by CharWidth, /// and only space is a valid line break. /// Including width of white space: /// Basically, if the white space is known to end the line, we don't count its width. /// - If we stopped mid-line because of a hard break, ichLim{backtracking} != source length, etc., /// we will try to put more on line, so must include space width. /// - If we stop at the very end of the whole paragraph, don't include it? But then how can IP be after /// final space? /// - If we know next stuff must go on another line leave out its width. /// </summary> public void FindBreakPoint(IVwGraphics vg, IVwTextSource source, IVwJustifier justifier, int ichMin, int ichLim, int ichLimBacktrack, bool fNeedFinalBreak, bool fStartLine, int dxMaxWidth, LgLineBreak lbPref, LgLineBreak lbMax, LgTrailingWsHandling twsh, bool fParaRightToLeft, out ILgSegment segRet, out int dichLimSeg, out int dxWidth, out LgEndSegmentType est, ILgSegment segPrev) { // Default return values in case we cannot make a segment. segRet = null; dichLimSeg = 0; dxWidth = 0; est = LgEndSegmentType.kestNothingFit; int width = 0; int lastSpace = -1; string input; int cchRead = ichLim - ichMin; if (fNeedFinalBreak) { cchRead++; } using (ArrayPtr ptr = new ArrayPtr((cchRead) * 2 + 2)) { source.Fetch(ichMin, ichMin + cchRead, ptr.IntPtr); input = MarshalEx.NativeToString(ptr, cchRead, true); } int widthAtLastSpace = 0; int ichLimWs = GetLimInSameWs(ichMin, source, ichLimBacktrack); FakeSegment result = null; // limit in input of characters we may include in segment. int limit = Math.Min(ichLimBacktrack, ichLimWs) - ichMin; int limWholeInput = ichLim - ichMin; int ichInput = 0; if (twsh == LgTrailingWsHandling.ktwshOnlyWs) { result = MakeWhiteSpaceOnlySegment(input, width, ichInput, limit); } else // mixed allowed or no-white-space { // Advance past what will fit, keeping track of the last good break position (white space). for (; ichInput < limit; ichInput++) { if (input[ichInput] == ' ') { lastSpace = ichInput; widthAtLastSpace = width; } var charWidth = CharWidth(input[ichInput]); if (width + charWidth > dxMaxWidth) { break; } width += charWidth; } Debug.Assert(width <= dxMaxWidth); // loop never allows it to exceed max if (ichInput < input.Length && input[ichInput] == ' ') { // good break exactly at limit lastSpace = ichInput; widthAtLastSpace = width; } if (ichInput == limit && ichLimBacktrack >= ichLimWs) // all the text in our WS fit; also handles special case of zero-char range { // everything we were allowed to include fit; if it wasn't absolutely everything caller needs to decide // about break. if (twsh == LgTrailingWsHandling.ktwshAll) { result = new FakeSegment(input.Substring(0, limit), width, Ws, ichInput == limWholeInput ? LgEndSegmentType.kestNoMore : LgEndSegmentType.kestWsBreak); } else { // must be no-trailing-white-space (all WS is handled above). strip off any trailing spaces. while (ichInput > 0 && input[ichInput - 1] == ' ') { ichInput--; width -= CharWidth(' '); } if (ichInput == limit) // no trailing ws removed (also handles empty run) { result = new FakeSegment(input.Substring(0, limit), width, Ws, ichInput == limWholeInput ? LgEndSegmentType.kestNoMore : LgEndSegmentType.kestWsBreak); // all fit } else if (ichInput > 0) // got some text, stripped some white space { result = new FakeSegment(input.Substring(0, ichInput), width, Ws, LgEndSegmentType.kestMoreWhtsp); } else { // we're not done with the whole input, but no non-white at start return; } } } else // some stuff we wanted to put on line didn't fit; if (lastSpace >= 0) // there's a good break we can use { int end = lastSpace; if (twsh == LgTrailingWsHandling.ktwshAll) { // include trailing spaces without increasing width; they 'disappear' into line break. while (end < limit && input[end] == ' ') { end++; } } result = new FakeSegment(input.Substring(0, end), widthAtLastSpace, Ws, LgEndSegmentType.kestMoreLines); } else if (lbMax == LgLineBreak.klbWordBreak) { // we needed a word break and didn't get one. return; } else { // remaining possibility is that we need to make some sort of segment with at least one character. // (if we don't have any i = limit = 0 and we take another branch). if (ichInput == 0) { width += CharWidth(input[ichInput]); ichInput++; // must have at least one character } result = new FakeSegment(input.Substring(ichInput), width, Ws, LgEndSegmentType.kestMoreLines); } } if (result == null) { return; } segRet = result; dichLimSeg = result.Length; dxWidth = result.Width; est = result.EndSegType; result.Height = SegmentHeight; result.Ascent = SegmentAscent; // DirectionDepth is usually 0 for LTR text in an LTR paragraph. // For RTL text, however, it is always 1. // And for UPSTREAM LTR text (in an RTL paragraph) it has to be 2. if (RightToLeft) { result.DirectionDepth = 1; } else if (fParaRightToLeft) { result.DirectionDepth = 2; } }