public bool TryGetScriptParagraph(int paragraphIndex, out ScriptParagraph* scriptParagraph) { // Handle wrap-around at 32bits by thrashing all tokens. LruEntry* firstEntry = (LruEntry*)buffer.GetPointer(); LruEntry* endEntry = firstEntry + buffer.Count; if (nextToken == int.MaxValue) { nextToken = 0; for (LruEntry* currentEntry = firstEntry; currentEntry != endEntry; currentEntry++) currentEntry->Token = nextToken++; } // Search for a matching paragraph and return it if found. // Make note of least recently used entry just in case. LruEntry* lruEntry = null; int lruToken = int.MaxValue; for (LruEntry* currentEntry = firstEntry; currentEntry != endEntry; currentEntry++) { if (currentEntry->ParagraphIndex == paragraphIndex) { currentEntry->Token = nextToken++; scriptParagraph = ¤tEntry->Paragraph; return true; } int token = currentEntry->Token; if (token < lruToken) { lruToken = token; lruEntry = currentEntry; } } // Decide whether to allocate a new entry from remaining capacity or replace the least-recently used one. LruEntry* entryToReplace; if (buffer.Count != buffer.Capacity) { entryToReplace = endEntry; entryToReplace->Paragraph.Initialize(); buffer.Count += 1; } else { entryToReplace = lruEntry; } // Return the entry. entryToReplace->Token = nextToken++; entryToReplace->ParagraphIndex = paragraphIndex; scriptParagraph = &entryToReplace->Paragraph; return false; }
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 static void ScriptShapeAndPlace(IntPtr hdc, ref IntPtr scriptCache, ScriptParagraph* scriptParagraph, ScriptRun* scriptRun, char* paragraphChars) { int charIndexInParagraph = scriptRun->CharIndexInParagraph; char* chars = paragraphChars + charIndexInParagraph; int charCount = scriptRun->CharCount; int glyphIndexInParagraph = scriptParagraph->GlyphCount; ushort* charLogicalClusters = scriptParagraph->CharLogicalClusters + charIndexInParagraph; SCRIPT_ANALYSIS* scriptAnalysis = &scriptRun->ScriptAnalysis; int glyphCapacity = charCount * 3 / 2 + 16; // * 1.5 + 16 per Uniscribe recommendations for ScriptShape. int glyphCount; ushort* glyphs; SCRIPT_VISATTR* glyphVisualAttributes; int result; for (; ; ) { scriptParagraph->EnsureGlyphCapacityAndPreserveContents(glyphIndexInParagraph + glyphCapacity); glyphs = scriptParagraph->Glyphs + glyphIndexInParagraph; glyphVisualAttributes = scriptParagraph->GlyphVisualAttributes + glyphIndexInParagraph; result = NativeMethods.ScriptShape(hdc, ref scriptCache, chars, charCount, glyphCapacity, scriptAnalysis, glyphs, charLogicalClusters, glyphVisualAttributes, out glyphCount); if (result == NativeConstants.S_OK) break; if (result == NativeConstants.USP_E_SCRIPT_NOT_IN_FONT) { SCRIPT_ANALYSIS modifiedScriptAnalysis = *scriptAnalysis; modifiedScriptAnalysis.eScript = NativeConstants.SCRIPT_UNDEFINED; result = NativeMethods.ScriptShape(hdc, ref scriptCache, chars, charCount, glyphCapacity, &modifiedScriptAnalysis, glyphs, charLogicalClusters, glyphVisualAttributes, out glyphCount); if (result == NativeConstants.S_OK) break; } if (result != NativeConstants.E_OUTOFMEMORY) Marshal.ThrowExceptionForHR(result); glyphCapacity *= 2; } int* glyphAdvanceWidths = scriptParagraph->GlyphAdvanceWidths + glyphIndexInParagraph; GOFFSET* glyphOffsets = scriptParagraph->GlyphOffsets + glyphIndexInParagraph; result = NativeMethods.ScriptPlace(hdc, ref scriptCache, glyphs, glyphCount, glyphVisualAttributes, scriptAnalysis, glyphAdvanceWidths, glyphOffsets, out scriptRun->ABC); if (result != NativeConstants.S_OK) Marshal.ThrowExceptionForHR(result); scriptRun->GlyphIndexInParagraph = glyphIndexInParagraph; scriptRun->GlyphCount = glyphCount; scriptParagraph->GlyphCount += glyphCount; }
private static void SplitScriptRuns(ScriptParagraph* scriptParagraph, Run* runs, int runCount, SCRIPT_ITEM* scriptItems, int scriptItemCount) { // Reserve space for the largest possible number of script runs. int maxScriptRuns = runCount + scriptItemCount; scriptParagraph->EnsureScriptRunCapacity(maxScriptRuns); // Merge Runs and SCRIPT_ITEMs to create ScriptRuns. Run* run = runs; int runStartCharIndex = 0; Run* endRun = runs + runCount; SCRIPT_ITEM* item = scriptItems; SCRIPT_ITEM* endItem = scriptItems + scriptItemCount; ScriptRun* startScriptRun = scriptParagraph->ScriptRuns; ScriptRun* scriptRun = startScriptRun; int currentCharIndex = 0; for (; ; scriptRun++) { int runEndCharIndex = runStartCharIndex + run->CharCount; int itemEndCharIndex = (item + 1)->iCharPos; if (itemEndCharIndex < runEndCharIndex) { scriptRun->Initialize(run, &item->a, currentCharIndex, itemEndCharIndex - currentCharIndex); currentCharIndex = itemEndCharIndex; item++; } else if (runEndCharIndex < itemEndCharIndex) { scriptRun->Initialize(run, &item->a, currentCharIndex, runEndCharIndex - currentCharIndex); runStartCharIndex = currentCharIndex = runEndCharIndex; run++; } else { scriptRun->Initialize(run, &item->a, currentCharIndex, runEndCharIndex - currentCharIndex); runStartCharIndex = currentCharIndex = runEndCharIndex; run++; item++; // All sequences of Runs and Items should always end on the same char so we only // need to check for termination here. if (run == endRun) { Debug.Assert(item == endItem, "Inconsistent Run and SCRIPT_ITEM sequences."); break; } } } scriptParagraph->ScriptRunCount = (int)(scriptRun - startScriptRun) + 1; }
private static void ScriptBreak(ScriptParagraph* scriptParagraph, char* chars, SCRIPT_ITEM* scriptItems, int scriptItemCount) { SCRIPT_ITEM* scriptItem = scriptItems; SCRIPT_ITEM* endScriptItem = scriptItems + scriptItemCount; SCRIPT_LOGATTR* logicalAttributes = scriptParagraph->CharLogicalAttributes; for (; scriptItem != endScriptItem; scriptItem++) { int scriptItemCharPos = scriptItem->iCharPos; int nextScriptItemCharPos = (scriptItem + 1)->iCharPos; int scriptItemCharCount = nextScriptItemCharPos - scriptItemCharPos; char* scriptItemChars = chars + scriptItemCharPos; int result = NativeMethods.ScriptBreak(scriptItemChars, scriptItemCharCount, &scriptItem->a, logicalAttributes); if (result != 0) Marshal.ThrowExceptionForHR(result); logicalAttributes += scriptItemCharCount; } }
/// <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; } } } }
public char* Chars(ScriptParagraph* scriptParagraph, char* charZero) { return scriptParagraph->Chars(charZero) + CharIndexInParagraph; }
public GOFFSET* GlyphOffsets(ScriptParagraph* scriptParagraph) { return scriptParagraph->GlyphOffsets + GlyphIndexInParagraph; }
public int* GlyphAdvanceWidths(ScriptParagraph* scriptParagraph) { return scriptParagraph->GlyphAdvanceWidths + GlyphIndexInParagraph; }
public SCRIPT_VISATTR* GlyphVisualAttributes(ScriptParagraph* scriptParagraph) { return scriptParagraph->GlyphVisualAttributes + GlyphIndexInParagraph; }
public ushort* Glyphs(ScriptParagraph* scriptParagraph) { return scriptParagraph->Glyphs + GlyphIndexInParagraph; }
public SCRIPT_LOGATTR* CharLogicalAttributes(ScriptParagraph* scriptParagraph) { return scriptParagraph->CharLogicalAttributes + CharIndexInParagraph; }
public ushort* CharLogicalClusters(ScriptParagraph* scriptParagraph) { return scriptParagraph->CharLogicalClusters + CharIndexInParagraph; }
private static int MeasureTextScriptRun(ScriptParagraph* scriptParagraph, ScriptRun* scriptRun, int truncatedLeadingCharsCount, int truncatedTrailingCharsCount, out int glyphIndexInParagraph, out int glyphCount) { if (truncatedLeadingCharsCount != 0 || truncatedTrailingCharsCount != 0) { ushort* logicalClusters = scriptRun->CharLogicalClusters(scriptParagraph); int startGlyphIndexInRun, endGlyphIndexInRun; if (scriptRun->ScriptAnalysis.fRTL) { startGlyphIndexInRun = truncatedTrailingCharsCount != 0 ? logicalClusters[scriptRun->CharCount - truncatedTrailingCharsCount] + 1 : 0; endGlyphIndexInRun = truncatedLeadingCharsCount != 0 ? logicalClusters[truncatedLeadingCharsCount] + 1 : scriptRun->GlyphCount; } else { startGlyphIndexInRun = truncatedLeadingCharsCount != 0 ? logicalClusters[truncatedLeadingCharsCount] : 0; endGlyphIndexInRun = truncatedTrailingCharsCount != 0 ? logicalClusters[scriptRun->CharCount - truncatedTrailingCharsCount] : scriptRun->GlyphCount; } glyphIndexInParagraph = scriptRun->GlyphIndexInParagraph + startGlyphIndexInRun; glyphCount = endGlyphIndexInRun - startGlyphIndexInRun; int* glyphAdvanceWidths = scriptParagraph->GlyphAdvanceWidths + glyphIndexInParagraph; int measuredWidth = 0; for (int j = 0; j < glyphCount; j++) measuredWidth += glyphAdvanceWidths[j]; return measuredWidth; } glyphIndexInParagraph = scriptRun->GlyphIndexInParagraph; glyphCount = scriptRun->GlyphCount; return scriptRun->ABC.TotalWidth; }
private Style GetParagraphStyleAssumingItHasAtLeastOneRun(ScriptParagraph* scriptParagraph) { return document.LookupStyle(scriptParagraph->ScriptRuns[0].StyleIndex); }
private void FinishScriptLineLayout(ScriptLine* scriptLine, ScriptParagraph* scriptParagraph, bool mustLayoutObjects) { int ascent = MinimumScriptLineHeight; int descent = 0; ScriptRun* startScriptRun = scriptParagraph->ScriptRuns + scriptLine->ScriptRunIndex; ScriptRun* endScriptRun = startScriptRun + scriptLine->ScriptRunCount; for (ScriptRun* scriptRun = startScriptRun; scriptRun != endScriptRun; scriptRun++) { int scriptRunDescentAndMargin = scriptRun->Descent + scriptRun->BottomMargin; int scriptRunAscentAndMargin = scriptRun->Ascent + scriptRun->TopMargin; if (scriptRunAscentAndMargin > ascent) ascent = scriptRunAscentAndMargin; if (scriptRunDescentAndMargin > descent) descent = scriptRunDescentAndMargin; } int height = ascent + descent; scriptLine->Height = height; scriptLine->Descent = descent; if (mustLayoutObjects) { int xCurrentPosition = scriptLine->X; int scriptRunIndex = scriptLine->ScriptRunIndex; int scriptRunCount = scriptLine->ScriptRunCount; int* visualToLogicalMap = GetTempVisualToLogicalMap( scriptParagraph->ScriptRuns + scriptRunIndex, scriptRunCount); 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); break; } case RunKind.Object: { measuredWidth = scriptRun->ABC.TotalWidth; EmbeddedObjectHost embeddedObjectHost; int charIndex = scriptRun->CharIndexInParagraph + scriptParagraph->CharIndex; if (embeddedObjectHosts.TryGetValue(charIndex, out embeddedObjectHost)) { embeddedObjectHost.LayoutBounds = new Rectangle( xCurrentPosition + scriptRun->ABC.abcA, scriptLine->Y + scriptLine->Ascent - scriptRun->Ascent, scriptRun->ABC.abcB, scriptRun->Height); pendingEmbeddedObjectHostsToShow.Enqueue(embeddedObjectHost); } break; } case RunKind.Tab: { measuredWidth = scriptRun->ABC.abcB; break; } default: throw new NotSupportedException(); } xCurrentPosition += measuredWidth; } } }