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 = &currentEntry->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;
        }
예제 #2
0
        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
            }
        }
예제 #3
0
        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
            }
        }
예제 #4
0
        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;
        }
예제 #5
0
        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;
        }
예제 #6
0
        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;
            }
        }
예제 #7
0
        /// <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;
                    }
                }
            }
        }
예제 #8
0
 public char* Chars(ScriptParagraph* scriptParagraph, char* charZero)
 {
     return scriptParagraph->Chars(charZero) + CharIndexInParagraph;
 }
예제 #9
0
 public GOFFSET* GlyphOffsets(ScriptParagraph* scriptParagraph)
 {
     return scriptParagraph->GlyphOffsets + GlyphIndexInParagraph;
 }
예제 #10
0
 public int* GlyphAdvanceWidths(ScriptParagraph* scriptParagraph)
 {
     return scriptParagraph->GlyphAdvanceWidths + GlyphIndexInParagraph;
 }
예제 #11
0
 public SCRIPT_VISATTR* GlyphVisualAttributes(ScriptParagraph* scriptParagraph)
 {
     return scriptParagraph->GlyphVisualAttributes + GlyphIndexInParagraph;
 }
예제 #12
0
 public ushort* Glyphs(ScriptParagraph* scriptParagraph)
 {
     return scriptParagraph->Glyphs + GlyphIndexInParagraph;
 }
예제 #13
0
 public SCRIPT_LOGATTR* CharLogicalAttributes(ScriptParagraph* scriptParagraph)
 {
     return scriptParagraph->CharLogicalAttributes + CharIndexInParagraph;
 }
예제 #14
0
 public ushort* CharLogicalClusters(ScriptParagraph* scriptParagraph)
 {
     return scriptParagraph->CharLogicalClusters + CharIndexInParagraph;
 }
예제 #15
0
        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;
        }
예제 #16
0
 private Style GetParagraphStyleAssumingItHasAtLeastOneRun(ScriptParagraph* scriptParagraph)
 {
     return document.LookupStyle(scriptParagraph->ScriptRuns[0].StyleIndex);
 }
예제 #17
0
        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;
                }
            }
        }