private void PaintLineForeground(DeviceContext deviceContext, PaintOptions paintOptions, ScriptLine* scriptLine, ScriptParagraph* scriptParagraph, int scriptRunIndex, int scriptRunCount, int* visualToLogicalMap, int xStartPosition, int xEndPosition, Rectangle layoutRect, bool isSelected, bool skipObjects) { int yCurrentBaseline = scriptLine->Y + scriptLine->Ascent; int xCurrentPosition = scriptLine->X; for (int i = 0; i < scriptRunCount; i++) { int logicalScriptRunIndex = visualToLogicalMap[currentLayoutRightToLeft ? scriptRunCount - i - 1 : i]; ScriptRun* scriptRun = scriptParagraph->ScriptRuns + logicalScriptRunIndex + scriptRunIndex; int measuredWidth; switch (scriptRun->RunKind) { case RunKind.Text: { Style style = document.LookupStyle(scriptRun->StyleIndex); ScriptMetrics scriptMetrics = deviceContext.SelectFont(style.Font); int glyphIndexInParagraph, glyphCount; measuredWidth = MeasureTextScriptRun(scriptParagraph, scriptRun, logicalScriptRunIndex == 0 ? scriptLine->TruncatedLeadingCharsCount : 0, logicalScriptRunIndex == scriptRunCount - 1 ? scriptLine->TruncatedTrailingCharsCount : 0, out glyphIndexInParagraph, out glyphCount); if (glyphCount > 0 && xCurrentPosition + measuredWidth > xStartPosition) { int x = currentLayoutRightToLeft ? layoutRect.Right - xCurrentPosition - measuredWidth : layoutRect.Left + xCurrentPosition; int y = layoutRect.Top + yCurrentBaseline - scriptRun->Ascent; deviceContext.SetTextColor(isSelected ? paintOptions.SelectedTextColor : style.Color); ScriptTextOut(deviceContext.HDC, ref scriptMetrics.ScriptCache, x, y, ExtTextOutOptions.NONE, null, &scriptRun->ScriptAnalysis, scriptParagraph->Glyphs + glyphIndexInParagraph, glyphCount, scriptParagraph->GlyphAdvanceWidths + glyphIndexInParagraph, null, scriptParagraph->GlyphOffsets + glyphIndexInParagraph); } break; } case RunKind.Object: { measuredWidth = MeasureObjectScriptRun(scriptRun); if (!skipObjects && xCurrentPosition + measuredWidth > xStartPosition) { int embeddedObjectCharIndex = scriptRun->CharIndexInParagraph + scriptParagraph->CharIndex; EmbeddedObjectHost embeddedObjectHost; if (embeddedObjectHosts.TryGetValue(embeddedObjectCharIndex, out embeddedObjectHost)) { if (embeddedObjectHost.RequiresPaint) { using (Graphics g = Graphics.FromHdc(deviceContext.HDC)) { Rectangle bounds = GetDrawingBoundsOfEmbeddedObject(embeddedObjectHost, layoutRect.Location); embeddedObjectHost.Paint(g, paintOptions, bounds, currentLayoutRightToLeft); } } } } break; } case RunKind.Tab: { measuredWidth = MeasureTabScriptRun(scriptRun, GetParagraphStyleAssumingItHasAtLeastOneRun(scriptParagraph), xCurrentPosition, false); break; } default: throw new NotSupportedException(); } xCurrentPosition += measuredWidth; if (xCurrentPosition >= xEndPosition) break; // can't fit any more runs within the clip rectangle } }
private void PaintLineBackgroundAndExcludeSelectedTextFromClipRegion(DeviceContext deviceContext, PaintOptions paintOptions, ScriptLine* scriptLine, ScriptParagraph* scriptParagraph, int scriptRunIndex, int scriptRunCount, int* visualToLogicalMap, int xStartPosition, int xEndPosition, Rectangle layoutRect, int selectedCharIndex, int selectedCharCount) { IntPtr brush = DeviceContext.GetStockObject(NativeConstants.DC_BRUSH); deviceContext.SetDCBrushColor(paintOptions.SelectedBackgroundColor); int yCurrentBaseline = scriptLine->Y + scriptLine->Ascent; int xCurrentPosition = scriptLine->X; for (int i = 0; i < scriptRunCount; i++) { int logicalScriptRunIndex = visualToLogicalMap[currentLayoutRightToLeft ? scriptRunCount - i - 1 : i]; ScriptRun* scriptRun = scriptParagraph->ScriptRuns + logicalScriptRunIndex + scriptRunIndex; int measuredWidth; switch (scriptRun->RunKind) { case RunKind.Text: { int glyphIndexInParagraph, glyphCount; int truncatedLeadingCharsCount = logicalScriptRunIndex == 0 ? scriptLine->TruncatedLeadingCharsCount : 0; int truncatedTrailingCharsCount = logicalScriptRunIndex == scriptRunCount - 1 ? scriptLine->TruncatedTrailingCharsCount : 0; measuredWidth = MeasureTextScriptRun(scriptParagraph, scriptRun, truncatedLeadingCharsCount, truncatedTrailingCharsCount, out glyphIndexInParagraph, out glyphCount); if (xCurrentPosition + measuredWidth > xStartPosition) { int scriptRunCharIndex = scriptParagraph->CharIndex + scriptRun->CharIndexInParagraph; int leadingCharIndex = scriptRunCharIndex + truncatedLeadingCharsCount; int trailingCharIndex = scriptRunCharIndex + scriptRun->CharCount - truncatedTrailingCharsCount; if (trailingCharIndex >= selectedCharIndex && leadingCharIndex <= selectedCharIndex + selectedCharCount) { int relativePositionOfSelection; if (leadingCharIndex >= selectedCharIndex) { relativePositionOfSelection = 0; } else { relativePositionOfSelection = MeasureTextScriptRun(scriptParagraph, scriptRun, truncatedLeadingCharsCount, truncatedTrailingCharsCount + trailingCharIndex - selectedCharIndex, out glyphIndexInParagraph, out glyphCount); } int measuredWidthOfSelection; if (trailingCharIndex <= selectedCharIndex + selectedCharCount) { measuredWidthOfSelection = measuredWidth - relativePositionOfSelection; } else { measuredWidthOfSelection = MeasureTextScriptRun(scriptParagraph, scriptRun, truncatedLeadingCharsCount + Math.Max(selectedCharIndex - leadingCharIndex, 0), truncatedTrailingCharsCount + trailingCharIndex - selectedCharIndex - selectedCharCount, out glyphIndexInParagraph, out glyphCount); } int x = currentLayoutRightToLeft ? layoutRect.Right - xCurrentPosition - measuredWidth : layoutRect.Left + xCurrentPosition; int y = layoutRect.Top + yCurrentBaseline - scriptRun->Ascent; if (scriptRun->ScriptAnalysis.fRTL) x += measuredWidth - relativePositionOfSelection - measuredWidthOfSelection; else x += relativePositionOfSelection; Rectangle selectedRect = new Rectangle(x, y, measuredWidthOfSelection, scriptRun->Height); deviceContext.FillRect(selectedRect, brush); deviceContext.ExcludeClipRect(selectedRect); } } break; } case RunKind.Object: case RunKind.Tab: { measuredWidth = scriptRun->RunKind == RunKind.Object ? MeasureObjectScriptRun(scriptRun) : MeasureTabScriptRun(scriptRun, GetParagraphStyleAssumingItHasAtLeastOneRun(scriptParagraph), xCurrentPosition, false); if (xCurrentPosition + measuredWidth > xStartPosition) { int leadingCharIndex = scriptParagraph->CharIndex + scriptRun->CharIndexInParagraph; int trailingCharIndex = leadingCharIndex + 1; if (trailingCharIndex >= selectedCharIndex && leadingCharIndex <= selectedCharIndex + selectedCharCount) { int x = currentLayoutRightToLeft ? layoutRect.Right - xCurrentPosition - measuredWidth : layoutRect.Left + xCurrentPosition; int y = layoutRect.Top + yCurrentBaseline - scriptRun->Ascent; Rectangle selectedRect = new Rectangle(x, y, measuredWidth, scriptRun->Height); deviceContext.FillRect(selectedRect, brush); // Don't exclude clip rect for objects and tabs. //deviceContext.ExcludeClipRect(selectedRect); } } break; } default: throw new NotSupportedException(); } xCurrentPosition += measuredWidth; if (xCurrentPosition >= xEndPosition) break; // can't fit any more runs within the clip rectangle } }
private void PaintRegion(DeviceContext deviceContext, Rectangle layoutRect, Rectangle clipRect, IntPtr clipRegion, PaintOptions paintOptions, int selectedCharIndex, int selectedCharCount) { int yStartPosition = clipRect.Top - layoutRect.Top; int yEndPosition = clipRect.Bottom - layoutRect.Top; int firstScriptLineIndex; ScriptLine* scriptLine = GetScriptLineAtYPositionOrNullIfNone(yStartPosition, out firstScriptLineIndex); if (scriptLine == null) return; ScriptLine* endScriptLine = GetScriptLineZero() + scriptLineBuffer.Count; int xStartPosition, xEndPosition; if (currentLayoutRightToLeft) { xStartPosition = layoutRect.Right - clipRect.Right; xEndPosition = layoutRect.Right - clipRect.Left; } else { xStartPosition = clipRect.Left - layoutRect.Left; xEndPosition = clipRect.Right - layoutRect.Left; } // Note: Selection may extend beyond the range of the layout if improper index and count // values were provided. This is ok since we only care about the intersection of the // selected range with actual range of characters on the lines. if (selectedCharCount < 0) { selectedCharIndex += selectedCharCount; selectedCharCount = -selectedCharCount; } deviceContext.SetBkMode(NativeConstants.TRANSPARENT); for (; scriptLine != endScriptLine && scriptLine->Y < yEndPosition; scriptLine++) { int scriptRunCount = scriptLine->ScriptRunCount; if (scriptRunCount != 0) { ScriptParagraph* scriptParagraph = GetScriptParagraph(deviceContext, scriptLine->ParagraphIndex); int scriptRunIndex = scriptLine->ScriptRunIndex; int* visualToLogicalMap = GetTempVisualToLogicalMap(scriptParagraph->ScriptRuns + scriptRunIndex, scriptRunCount); if (selectedCharCount == 0 || selectedCharIndex >= scriptParagraph->CharIndex + scriptParagraph->ScriptRuns[scriptRunIndex + scriptRunCount - 1].EndCharIndexInParagraph - scriptLine->TruncatedTrailingCharsCount || selectedCharIndex + selectedCharCount < scriptParagraph->CharIndex + scriptParagraph->ScriptRuns[scriptRunIndex].CharIndexInParagraph + scriptLine->TruncatedLeadingCharsCount) { deviceContext.SelectClipRegion(clipRegion); // No selection. Just paint the line as usual. PaintLineForeground(deviceContext, paintOptions, scriptLine, scriptParagraph, scriptRunIndex, scriptRunCount, visualToLogicalMap, xStartPosition, xEndPosition, layoutRect, false, false); } else { deviceContext.SelectClipRegion(clipRegion); // Paint background. PaintLineBackgroundAndExcludeSelectedTextFromClipRegion(deviceContext, paintOptions, scriptLine, scriptParagraph, scriptRunIndex, scriptRunCount, visualToLogicalMap, xStartPosition, xEndPosition, layoutRect, selectedCharIndex, selectedCharCount); // Paint all text and objects except in selected areas. PaintLineForeground(deviceContext, paintOptions, scriptLine, scriptParagraph, scriptRunIndex, scriptRunCount, visualToLogicalMap, xStartPosition, xEndPosition, layoutRect, false, false); // Invert the clip region. IntPtr lineRegion = DeviceContext.CreateRectRegion(clipRect); deviceContext.XorClipRegion(lineRegion); DeviceContext.DeleteObject(lineRegion); // Paint all text in selected areas. PaintLineForeground(deviceContext, paintOptions, scriptLine, scriptParagraph, scriptRunIndex, scriptRunCount, visualToLogicalMap, xStartPosition, xEndPosition, layoutRect, true, true); } } } }
/// <summary> /// Analyzes a <see cref="Paragraph" /> to populate a <see cref="ScriptParagraph"/>. /// </summary> private void AnalyzeParagraph(DeviceContext deviceContext, Paragraph* paragraph, ScriptParagraph* scriptParagraph) { int paragraphCharIndex = paragraph->CharIndex; int paragraphCharCount = paragraph->CharCount; scriptParagraph->CharIndex = paragraphCharIndex; scriptParagraph->CharCount = paragraphCharCount; scriptParagraph->ScriptRunCount = 0; scriptParagraph->GlyphCount = 0; Run* runZero = document.GetRunZero(); Run* runs = runZero + paragraph->RunIndex; int runCount = paragraph->RunCount; // Skip paragraphs with no runs (this is the sentinel at end of document) if (runCount == 0) { return; } // Handle paragraphs with exactly one empty run (simple newline) Debug.Assert(paragraphCharCount != 0, "A paragraph with at least one run should always have at least one character."); char* charZero = document.GetCharZero(); char* chars = charZero + paragraphCharIndex; // Step 1. Itemize the script items within the paragraph. // Each script item represents a chunk of text (such as a word) // for Unicode rendering purposes. scriptParagraph->EnsureCharCapacity(paragraphCharCount); SCRIPT_ITEM* tempScriptItems; int tempScriptItemCount; ScriptItemize(chars, paragraphCharCount, currentLayoutRightToLeft, tempScriptItemBuffer, out tempScriptItems, out tempScriptItemCount); // Step 2. Compute logical attributes for characters in the paragraph for word-break purposes. ScriptBreak(scriptParagraph, chars, tempScriptItems, tempScriptItemCount); // Step 3. Split the Runs on SCRIPT_ITEM boundaries to produce ScriptRuns. SplitScriptRuns(scriptParagraph, runs, runCount, tempScriptItems, tempScriptItemCount); // Step 4. Shape and Place glyphs and Measure embedded objects one run at a time. // We do this in linear order because it is simpler and we do not care about // visual order until later when we pack runs into lines and draw them. ScriptRun* scriptRun = scriptParagraph->ScriptRuns; ScriptRun* endScriptRun = scriptRun + scriptParagraph->ScriptRunCount; for (; scriptRun != endScriptRun; scriptRun++) { Style scriptRunStyle = document.LookupStyle(scriptRun->StyleIndex); switch (scriptRun->RunKind) { case RunKind.Text: { // Fill in basic metrics. ScriptMetrics scriptMetrics = deviceContext.SelectFont(scriptRunStyle.Font); scriptRun->Height = scriptMetrics.Height; scriptRun->Descent = scriptMetrics.Descent; scriptRun->TopMargin = 0; scriptRun->BottomMargin = 0; // Set glyphs. ScriptShapeAndPlace(deviceContext.HDC, ref scriptMetrics.ScriptCache, scriptParagraph, scriptRun, chars); break; } case RunKind.Object: { // No glyphs for an embedded object. scriptRun->GlyphCount = 0; scriptRun->GlyphIndexInParagraph = 0; // Fill in object measurements. int charIndex = scriptRun->CharIndexInParagraph + paragraphCharIndex; EmbeddedObjectHost embeddedObjectHost; EmbeddedObjectMeasurements embeddedObjectMeasurements; if (embeddedObjectHosts.TryGetValue(charIndex, out embeddedObjectHost)) embeddedObjectMeasurements = embeddedObjectHost.Measure(); else embeddedObjectMeasurements = EmbeddedObjectHost.CreateDefaultEmbeddedObjectMeasurements(); scriptRun->Height = embeddedObjectMeasurements.Size.Height; scriptRun->Descent = embeddedObjectMeasurements.Descent; scriptRun->TopMargin = embeddedObjectMeasurements.Margin.Top; scriptRun->BottomMargin = embeddedObjectMeasurements.Margin.Bottom; scriptRun->ABC.abcA = embeddedObjectMeasurements.Margin.Left; scriptRun->ABC.abcB = embeddedObjectMeasurements.Size.Width; scriptRun->ABC.abcC = embeddedObjectMeasurements.Margin.Right; // Change the logical attributes of the character so that an object behaves like // a word rather than whitespace when performing word wrap. scriptParagraph->CharLogicalAttributes[scriptRun->CharIndexInParagraph].SetfSoftBreakfCharStopAndfWordStop(); break; } case RunKind.Tab: { // No glyphs for a tab. scriptRun->GlyphCount = 0; scriptRun->GlyphIndexInParagraph = 0; // Use same metrics as inline text. ScriptMetrics scriptMetrics = deviceContext.SelectFont(scriptRunStyle.Font); scriptRun->Height = scriptMetrics.Height; scriptRun->Descent = scriptMetrics.Descent; scriptRun->TopMargin = 0; scriptRun->BottomMargin = 0; scriptRun->ABC.abcA = 0; scriptRun->ABC.abcB = -1; // will be filled in when the TAB is measured scriptRun->ABC.abcC = 0; // Change the logical attributes of the character to make it whitespace since Uniscribe // does not consider a TAB to be whitespace. scriptParagraph->CharLogicalAttributes[scriptRun->CharIndexInParagraph].SetfWhiteSpace(); break; } } } }
/// <summary> /// Gets a fully analyzed <see cref="ScriptParagraph"/> by paragraph index. /// </summary> private ScriptParagraph* GetScriptParagraph(DeviceContext deviceContext, int paragraphIndex) { ScriptParagraph* scriptParagraph; if (!scriptParagraphCache.TryGetScriptParagraph(paragraphIndex, out scriptParagraph)) { Paragraph* paragraph = document.GetParagraphZero() + paragraphIndex; AnalyzeParagraph(deviceContext, paragraph, scriptParagraph); } return scriptParagraph; }
/// <summary> /// Computes the layout of a <see cref="ScriptParagraph"/> and appends one or /// more <see cref="ScriptLine"/>s to the <see cref="scriptLineBuffer"/>. /// </summary> /// <param name="deviceContext">The HDC state.</param> /// <param name="paragraphIndex">The paragraph index to layout.</param> private void AppendScriptLinesForScriptParagraph(DeviceContext deviceContext, int paragraphIndex) { ScriptParagraph* scriptParagraph = GetScriptParagraph(deviceContext, paragraphIndex); int scriptRunCount = scriptParagraph->ScriptRunCount; // Always allocate at least one script line for a paragraph. ScriptLine* scriptLine = AddScriptLine(paragraphIndex, 0, currentLayoutHeight); // Handle paragraph with no runs (sentinel). if (scriptRunCount == 0) { scriptLine->Height = MinimumScriptLineHeight; currentLayoutHeight += MinimumScriptLineHeight; return; } // Apply block-level style information for the paragraph from the first logical run. ScriptRun* scriptRuns = scriptParagraph->ScriptRuns; Style paragraphStyle = GetParagraphStyleAssumingItHasAtLeastOneRun(scriptParagraph); scriptLine->X = paragraphStyle.LeftMargin + paragraphStyle.FirstLineIndent; int maxX = currentLayoutWidth - paragraphStyle.RightMargin - paragraphStyle.LeftMargin; // Loop over all runs in logical order to determine whether everything fits on one line // so we can skip the expensive word wrap calculations. { bool mustLayoutObjects = false; int currentX = scriptLine->X; for (int scriptRunIndex = 0; scriptRunIndex < scriptRunCount; scriptRunIndex++) { ScriptRun* scriptRun = scriptRuns + scriptRunIndex; int measuredWidth; switch (scriptRun->RunKind) { case RunKind.Text: { measuredWidth = scriptRun->ABC.TotalWidth; break; } case RunKind.Object: { measuredWidth = MeasureObjectScriptRun(scriptRun); mustLayoutObjects = true; break; } case RunKind.Tab: { measuredWidth = MeasureTabScriptRun(scriptRun, paragraphStyle, currentX, true); break; } default: throw new NotSupportedException(); } currentX += measuredWidth; if (currentX > maxX && paragraphStyle.WordWrap) goto ComplexLoop; } // Apply pending changes to the script line now that we're done. scriptLine->ScriptRunCount = scriptRunCount; FinishScriptLineLayout(scriptLine, scriptParagraph, mustLayoutObjects); currentLayoutHeight += scriptLine->Height; return; } // Loop over all runs in logical order and pack as many of them as will fit on each line // then wrap the rest. ComplexLoop: { Debug.Assert(scriptParagraph->CharCount != 0, "Script paragraph should not have zero chars because that would have been handled by the simple case."); int currentX = scriptLine->X; int scriptRunIndex = 0; int breakCharIndex = 0; int breakScriptRunIndex = 0; bool mustLayoutObjects = false; while (scriptRunIndex < scriptRunCount) { ScriptRun* firstRunOnLine = scriptRuns + scriptLine->ScriptRunIndex; int firstCharIndexOnLine = firstRunOnLine->CharIndexInParagraph + scriptLine->TruncatedLeadingCharsCount; // Sum glyph widths in logical order until an overflow is discovered. { ScriptRun* scriptRun = scriptRuns + scriptRunIndex; switch (scriptRun->RunKind) { case RunKind.Text: { int firstCharIndex = scriptRun->CharIndexInParagraph; int lastCharIndex = firstCharIndex + scriptRun->CharCount; if (scriptRun == firstRunOnLine) firstCharIndex += scriptLine->TruncatedLeadingCharsCount; bool runRightToLeft = scriptRun->ScriptAnalysis.fRTL; int* glyphAdvanceWidths = scriptRun->GlyphAdvanceWidths(scriptParagraph); int glyphIndex = scriptParagraph->CharLogicalClusters[firstCharIndex]; for (int charIndex = firstCharIndex; charIndex < lastCharIndex; charIndex++) { if (scriptParagraph->CharLogicalAttributes[charIndex].fSoftBreakOrfWhitespace && (charIndex == firstCharIndexOnLine || !scriptParagraph->CharLogicalAttributes[charIndex - 1].fWhiteSpace)) { breakCharIndex = charIndex; breakScriptRunIndex = scriptRunIndex; } int nextCharIndex = charIndex + 1; if (runRightToLeft) { int nextGlyphIndex = nextCharIndex != lastCharIndex ? scriptParagraph->CharLogicalClusters[nextCharIndex] : -1; while (glyphIndex > nextGlyphIndex) currentX += glyphAdvanceWidths[glyphIndex--]; } else { int nextGlyphIndex = nextCharIndex != lastCharIndex ? scriptParagraph->CharLogicalClusters[nextCharIndex] : scriptRun->GlyphCount; while (glyphIndex < nextGlyphIndex) currentX += glyphAdvanceWidths[glyphIndex++]; } if (currentX > maxX && paragraphStyle.WordWrap) goto BreakLine; } break; } case RunKind.Object: { mustLayoutObjects = true; breakCharIndex = scriptRun->CharIndexInParagraph; breakScriptRunIndex = scriptRunIndex; currentX += MeasureObjectScriptRun(scriptRun); if (currentX > maxX && paragraphStyle.WordWrap) goto BreakLine; break; } case RunKind.Tab: { breakCharIndex = scriptRun->CharIndexInParagraph; breakScriptRunIndex = scriptRunIndex; currentX += MeasureTabScriptRun(scriptRun, paragraphStyle, currentX, true); if (currentX > maxX && paragraphStyle.WordWrap) goto BreakLine; break; } } } // No overflow. scriptRunIndex++; continue; BreakLine: // Overflow. { // Introduce a hard break at the current script run when we do not find a useful breakpoint. if (breakCharIndex <= firstCharIndexOnLine) { breakScriptRunIndex = scriptRunIndex; breakCharIndex = scriptRuns[breakScriptRunIndex].CharIndexInParagraph; if (breakCharIndex <= firstCharIndexOnLine) breakCharIndex = firstCharIndexOnLine + 1; // ensure at least one char gets shown } // Fix the breakScriptRun pointer if the breakCharIndex is past the end of the run. // Also advance the index beyond all remaining whitespace on the line so it does not // get pushed into the beginning of the next line. ScriptRun* breakScriptRun = scriptRuns + breakScriptRunIndex; for (; ; ) { int breakCharOffset = breakCharIndex - breakScriptRun->CharIndexInParagraph; if (breakCharOffset == breakScriptRun->CharCount) { breakScriptRun++; breakScriptRunIndex++; } if (breakCharIndex == scriptParagraph->CharCount) goto Finish; // paragraph is completely finished! // Skip past remaining whitespace if run is in same direction as layout. // This will leave the whitespace hanging off the ragged end of the line. if (breakScriptRun->ScriptAnalysis.fRTL != currentLayoutRightToLeft || !scriptParagraph->CharLogicalAttributes[breakCharIndex].fWhiteSpace) break; breakCharIndex += 1; } // Finalize the script line properties. { int breakCharOffset = breakCharIndex - breakScriptRun->CharIndexInParagraph; if (breakCharOffset == 0) { scriptLine->ScriptRunCount = breakScriptRunIndex - scriptLine->ScriptRunIndex; } else { scriptLine->ScriptRunCount = breakScriptRunIndex - scriptLine->ScriptRunIndex + 1; scriptLine->TruncatedTrailingCharsCount = breakScriptRun->CharCount - breakCharOffset; } FinishScriptLineLayout(scriptLine, scriptParagraph, mustLayoutObjects); mustLayoutObjects = false; currentLayoutHeight += scriptLine->Height; // Start a new script line. scriptRunIndex = breakScriptRunIndex; currentX = paragraphStyle.LeftMargin; scriptLine = AddScriptLine(paragraphIndex, scriptRunIndex, currentLayoutHeight); scriptLine->X = currentX; scriptLine->TruncatedLeadingCharsCount = breakCharOffset; } } } Finish: // Finish the last script line. scriptLine->ScriptRunCount = scriptRunCount - scriptLine->ScriptRunIndex; FinishScriptLineLayout(scriptLine, scriptParagraph, mustLayoutObjects); currentLayoutHeight += scriptLine->Height; } }