Beispiel #1
0
 /// <summary>
 /// Analyzes a text range for spans where number substitution is applicable, reading attributes from the source and reporting substitutable ranges to the sink callback {{SetNumberSubstitution}}.
 /// </summary>
 /// <param name="analysisSource">The source object to analyze.</param>
 /// <param name="textPosition">The starting position within the source object.</param>
 /// <param name="textLength">The length to analyze.</param>
 /// <param name="analysisSink">A reference to the sink callback object that receives the text analysis.</param>
 /// <returns>
 /// If the method succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code.
 /// </returns>
 /// <unmanaged>HRESULT IDWriteTextAnalyzer::AnalyzeNumberSubstitution([None] IDWriteTextAnalysisSource* analysisSource,[None] int textPosition,[None] int textLength,[None] IDWriteTextAnalysisSink* analysisSink)</unmanaged>
 /// <remarks>
 /// Although the function can handle multiple ranges of differing number substitutions, the text ranges should not arbitrarily split the middle of numbers. Otherwise, it will treat the numbers separately and will not translate any intervening punctuation.
 /// </remarks>
 public void AnalyzeNumberSubstitution(TextAnalysisSource analysisSource, int textPosition, int textLength, TextAnalysisSink analysisSink)
 {
     AnalyzeNumberSubstitution__(TextAnalysisSourceShadow.ToIntPtr(analysisSource), textPosition, textLength, TextAnalysisSinkShadow.ToIntPtr(analysisSink));
 }
 /// <summary>
 /// Return a pointer to the unamanged version of this callback.
 /// </summary>
 /// <param name="callback">The callback.</param>
 /// <returns>A pointer to a shadow c++ callback</returns>
 public static IntPtr ToIntPtr(TextAnalysisSink callback)
 {
     return ToCallbackPtr<TextAnalysisSink>(callback);
 }
Beispiel #3
0
 /// <summary>
 /// Analyzes a text range for potential breakpoint opportunities, reading attributes from the source and reporting breakpoint opportunities to the sink callback {{SetLineBreakpoints}}.
 /// </summary>
 /// <param name="analysisSource">A reference to the source object to analyze.</param>
 /// <param name="textPosition">The starting text position within the source object.</param>
 /// <param name="textLength">The text length to analyze.</param>
 /// <param name="analysisSink">A reference to the  sink callback object that receives the text analysis.</param>
 /// <returns>
 /// If the method succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code.
 /// </returns>
 /// <unmanaged>HRESULT IDWriteTextAnalyzer::AnalyzeLineBreakpoints([None] IDWriteTextAnalysisSource* analysisSource,[None] int textPosition,[None] int textLength,[None] IDWriteTextAnalysisSink* analysisSink)</unmanaged>
 /// <remarks>
 /// Although the function can handle multiple paragraphs, the text range should not arbitrarily split the middle of paragraphs, unless the specified text span is considered a whole unit. Otherwise, the returned properties for the first and last characters will inappropriately allow breaks.
 /// </remarks>
 public void AnalyzeLineBreakpoints(TextAnalysisSource analysisSource, int textPosition, int textLength, TextAnalysisSink analysisSink)
 {
     AnalyzeLineBreakpoints__(TextAnalysisSourceShadow.ToIntPtr(analysisSource), textPosition, textLength, TextAnalysisSinkShadow.ToIntPtr(analysisSink));
 }
Beispiel #4
0
 /// <summary>
 /// Analyzes a text range for script directionality, reading attributes from the source and reporting levels to the sink callback {{SetBidiLevel}}.
 /// </summary>
 /// <param name="analysisSource">A reference to a source object to analyze.</param>
 /// <param name="textPosition">The starting text position within the source object.</param>
 /// <param name="textLength">The text length to analyze.</param>
 /// <param name="analysisSink">A reference to the sink callback object that receives the text analysis.</param>
 /// <returns>
 /// If the method succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code.
 /// </returns>
 /// <unmanaged>HRESULT IDWriteTextAnalyzer::AnalyzeBidi([None] IDWriteTextAnalysisSource* analysisSource,[None] int textPosition,[None] int textLength,[None] IDWriteTextAnalysisSink* analysisSink)</unmanaged>
 /// <remarks>
 /// While the function can handle multiple paragraphs, the text range should not arbitrarily split the middle of paragraphs. Otherwise, the returned levels may be wrong, because the Bidi algorithm is meant to apply to the paragraph as a whole.
 /// </remarks>
 public void AnalyzeBidi(TextAnalysisSource analysisSource, int textPosition, int textLength, TextAnalysisSink analysisSink)
 {
     AnalyzeBidi__(TextAnalysisSourceShadow.ToIntPtr(analysisSource), textPosition, textLength, TextAnalysisSinkShadow.ToIntPtr(analysisSink));
 }
Beispiel #5
0
        /// <inheritdoc />
        protected override void Draw()
        {
            while (this.pendingActions.TryDequeue(out var action))
            {
                action();
            }

            var args = this.neovimClient.GetScreen();

            if (args == null)
            {
                return;
            }

            this.scriptAnalysesCache.StartNewFrame();
            this.DeviceContext.BeginDraw();
            this.DeviceContext.Clear(Helpers.GetColor(args.BackgroundColor, 0));

            // Paint the background
            for (int i = 0; i < args.Cells.GetLength(0); i++)
            {
                for (int j = 0; j < args.Cells.GetLength(1); j++)
                {
                    if (args.Cells[i, j].BackgroundColor != args.BackgroundColor || args.Cells[i, j].Reverse)
                    {
                        var x = j * this.textParam.CharWidth;
                        var y = i * this.textParam.LineHeight;

                        var rect  = new RawRectangleF(x, y, x + this.textParam.CharWidth, y + this.textParam.LineHeight);
                        int color = args.Cells[i, j].Reverse ? args.Cells[i, j].ForegroundColor : args.Cells[i, j].BackgroundColor;
                        var brush = this.brushCache.GetBrush(this.DeviceContext, color);
                        this.DeviceContext.FillRectangle(rect, brush);
                    }
                }
            }

            // Paint the foreground
            for (int i = 0; i < args.Cells.GetLength(0); i++)
            {
                int j = 0;

                while (j < args.Cells.GetLength(1))
                {
                    // Cells with the same style should be analyzed together.
                    // This prevents the inproper ligature in <html>=
                    // Of course, it relies on enabling the syntax.
                    int  cellRangeStart = j;
                    int  cellRangeEnd   = j;
                    Cell startCell      = args.Cells[i, cellRangeStart];
                    while (true)
                    {
                        if (cellRangeEnd == args.Cells.GetLength(1))
                        {
                            break;
                        }

                        Cell cell = args.Cells[i, cellRangeEnd];
                        if (cell.Character != null &&
                            (cell.ForegroundColor != startCell.ForegroundColor ||
                             cell.BackgroundColor != startCell.BackgroundColor ||
                             cell.SpecialColor != startCell.SpecialColor ||
                             cell.Italic != startCell.Italic ||
                             cell.Bold != startCell.Bold ||
                             cell.Reverse != startCell.Reverse ||
                             cell.Undercurl != startCell.Undercurl ||
                             cell.Underline != startCell.Underline))
                        {
                            break;
                        }

                        cellRangeEnd++;
                    }

                    j = cellRangeEnd;

                    var fontWeight = args.Cells[i, cellRangeStart].Bold ? DWrite.FontWeight.Bold : this.textParam.Weight;
                    var fontStyle  = args.Cells[i, cellRangeStart].Italic ? DWrite.FontStyle.Italic : this.textParam.Style;

                    int cellIndex = cellRangeStart;
                    using (var textSource = new RowTextSource(this.factoryDWrite, args.Cells, i, cellRangeStart, cellRangeEnd))
                    {
                        var scriptAnalyses = this.scriptAnalysesCache.GetOrAddAnalysisResult(
                            textSource.GetTextAtPosition(0),
                            (_) =>
                        {
                            using (var textSink = new TextAnalysisSink())
                            {
                                this.textAnalyzer.AnalyzeScript(textSource, 0, textSource.Length, textSink);
                                return(textSink.ScriptAnalyses);
                            }
                        });

                        // The result of AalyzeScript may cut the text into several ranges,
                        // and in each range the text's scripts are different.
                        foreach (var(codePointStart, codePointLength, scriptAnalysis) in scriptAnalyses)
                        {
                            var     glyphBufferLength = (codePointLength * 3 / 2) + 16;
                            var     clusterMap        = new short[codePointLength];
                            var     textProperties    = new DWrite.ShapingTextProperties[codePointLength];
                            short[] indices;
                            DWrite.ShapingGlyphProperties[] shapingProperties;
                            var fontFace = this.fontCache.GetPrimaryFontFace(fontWeight, fontStyle);
                            int actualGlyphCount;

                            // We don't know how many glyphs the text have. TextLength * 3 / 2 + 16
                            // is an empirical estimation suggested by MSDN. So using a loop to detect
                            // the actual glyph count.
                            while (true)
                            {
                                indices           = new short[glyphBufferLength];
                                shapingProperties = new DWrite.ShapingGlyphProperties[glyphBufferLength];
                                try
                                {
                                    var str = textSource.GetSubString(codePointStart, codePointLength);

                                    DWrite.FontFeature[][] fontFeatures = null;
                                    int[] featureLength = null;

                                    if (!this.EnableLigature)
                                    {
                                        fontFeatures = new DWrite.FontFeature[][]
                                        {
                                            new DWrite.FontFeature[]
                                            {
                                                new DWrite.FontFeature(DWrite.FontFeatureTag.StandardLigatures, 0),
                                            },
                                        };

                                        featureLength = new int[]
                                        {
                                            str.Length,
                                        };
                                    }

                                    this.textAnalyzer.GetGlyphs(
                                        str,
                                        str.Length,
                                        fontFace,
                                        false,
                                        false,
                                        scriptAnalysis,
                                        null,
                                        null,
                                        fontFeatures,
                                        featureLength,
                                        glyphBufferLength,
                                        clusterMap,
                                        textProperties,
                                        indices,
                                        shapingProperties,
                                        out actualGlyphCount);
                                    break;
                                }
                                catch (SharpDX.SharpDXException e)
                                {
                                    const int ERROR_INSUFFICIENT_BUFFER = 122;
                                    if (e.ResultCode == SharpDX.Result.GetResultFromWin32Error(ERROR_INSUFFICIENT_BUFFER))
                                    {
                                        glyphBufferLength *= 2;
                                    }
                                }
                            }

                            for (int codePointIndex = 0, glyphIndex = 0; codePointIndex < codePointLength;)
                            {
                                // var fontWeight = args.Screen[i, cellIndex].Bold ? DWrite.FontWeight.Bold : DWrite.FontWeight.Normal;
                                // var fontStyle = args.Screen[i, cellIndex].Italic ? DWrite.FontStyle.Italic : DWrite.FontStyle.Normal;
                                var     foregroundColor = args.Cells[i, cellIndex].Reverse ? args.Cells[i, cellIndex].BackgroundColor : args.Cells[i, cellIndex].ForegroundColor;
                                var     foregroundBrush = this.brushCache.GetBrush(this.DeviceContext, foregroundColor);
                                var     fontFace2       = fontFace;
                                short[] indices2;

                                int cellWidth      = 0;
                                int codePointCount = 0;
                                int glyphCount     = 0;

                                if (indices[glyphIndex] == 0)
                                {
                                    // If the primary font doesn't have the glyph, get a font from system font fallback.
                                    // Ligatures for fallback fonts are not supported yet.
                                    int codePoint = args.Cells[i, cellIndex].Character.Value;
                                    fontFace2  = this.fontCache.GetFontFace(codePoint, fontWeight, fontStyle);
                                    indices2   = fontFace2.GetGlyphIndices(new int[] { codePoint });
                                    glyphCount = indices2.Length;

                                    // NativeInterop.Methods.wcwidth(textSource.GetCodePoint(codePointStart + codePointIndex));
                                    cellWidth      = this.GetCharWidth(args.Cells, i, cellIndex);
                                    codePointCount = 1;
                                }
                                else
                                {
                                    // The cluster map stores the information about the codepoint-glyph mapping.
                                    // If several codepoints share the same glyph (e.g. ligature), then they will
                                    // have the same value in clusterMap.
                                    // If one code point has several corresponding glyphs, then for the next codepoint,
                                    // the value in clusterMap will bump higher.
                                    // Example:  CodePointLength = 5, GlyphCount = 5, clusterMap = [0, 1, 1, 2, 4] means:
                                    // Codepoint Index    Glyph Index
                                    //       0   -----------   0
                                    //       1   -----------   1
                                    //       2   ----------/
                                    //       3   -----------   2
                                    //           \----------   3
                                    //       4   -----------   4
                                    var cluster     = clusterMap[codePointIndex];
                                    int nextCluster = cluster;
                                    while (true)
                                    {
                                        if (codePointIndex + codePointCount == clusterMap.Length)
                                        {
                                            nextCluster++;
                                            break;
                                        }

                                        nextCluster = clusterMap[codePointIndex + codePointCount];
                                        if (cluster != nextCluster)
                                        {
                                            break;
                                        }

                                        // NativeInterop.Methods.wcwidth(textSource.GetCodePoint(codePointStart + codePointIndex + codePointCount));
                                        cellWidth += this.GetCharWidth(args.Cells, i, cellIndex + cellWidth);
                                        codePointCount++;
                                    }

                                    glyphCount = nextCluster - cluster;
                                    indices2   = new short[glyphCount];
                                    for (int c = 0; c < glyphCount; c++)
                                    {
                                        indices2[c] = indices[glyphIndex];
                                    }
                                }

                                using (var glyphrun = new DWrite.GlyphRun
                                {
                                    FontFace = fontFace2,
                                    Advances = null,
                                    BidiLevel = 0,
                                    FontSize = this.textParam.DipSize,
                                    Indices = indices2,
                                    IsSideways = false,
                                    Offsets = null,
                                })
                                {
                                    var origin = new RawVector2(this.textParam.CharWidth * cellIndex, this.textParam.LineHeight * (i + 0.8f));
                                    this.DeviceContext.DrawGlyphRun(origin, glyphrun, foregroundBrush, D2D.MeasuringMode.GdiNatural);
                                    glyphrun.FontFace = null;
                                }

                                cellIndex      += cellWidth;
                                codePointIndex += codePointCount;
                                glyphIndex     += glyphCount;
                            }
                        }
                    }
                }
            }

            this.DeviceContext.EndDraw();
            this.DrawCursor(args);
        }
Beispiel #6
0
 /// <summary>
 /// Return a pointer to the unmanaged version of this callback.
 /// </summary>
 /// <param name="callback">The callback.</param>
 /// <returns>A pointer to a shadow c++ callback</returns>
 public static IntPtr ToIntPtr(TextAnalysisSink callback)
 {
     return(ToCallbackPtr <TextAnalysisSink>(callback));
 }
Beispiel #7
0
 /// <summary>
 /// Analyzes a text range for spans where number substitution is applicable, reading attributes from the source and reporting substitutable ranges to the sink callback {{SetNumberSubstitution}}.
 /// </summary>
 /// <param name="analysisSource">The source object to analyze.</param>
 /// <param name="textPosition">The starting position within the source object.</param>
 /// <param name="textLength">The length to analyze.</param>
 /// <param name="analysisSink">A reference to the sink callback object that receives the text analysis.</param>
 /// <returns>
 /// If the method succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code.
 /// </returns>
 /// <unmanaged>HRESULT IDWriteTextAnalyzer::AnalyzeNumberSubstitution([None] IDWriteTextAnalysisSource* analysisSource,[None] int textPosition,[None] int textLength,[None] IDWriteTextAnalysisSink* analysisSink)</unmanaged>
 /// <remarks>
 /// Although the function can handle multiple ranges of differing number substitutions, the text ranges should not arbitrarily split the middle of numbers. Otherwise, it will treat the numbers separately and will not translate any intervening punctuation.
 /// </remarks>
 public void AnalyzeNumberSubstitution(TextAnalysisSource analysisSource, int textPosition, int textLength, TextAnalysisSink analysisSink)
 {
     AnalyzeNumberSubstitution__(TextAnalysisSourceShadow.ToIntPtr(analysisSource), textPosition, textLength, TextAnalysisSinkShadow.ToIntPtr(analysisSink));
 }
Beispiel #8
0
 /// <summary>
 /// Analyzes a text range for script directionality, reading attributes from the source and reporting levels to the sink callback {{SetBidiLevel}}.
 /// </summary>
 /// <param name="analysisSource">A reference to a source object to analyze.</param>
 /// <param name="textPosition">The starting text position within the source object.</param>
 /// <param name="textLength">The text length to analyze.</param>
 /// <param name="analysisSink">A reference to the sink callback object that receives the text analysis.</param>
 /// <returns>
 /// If the method succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code.
 /// </returns>
 /// <unmanaged>HRESULT IDWriteTextAnalyzer::AnalyzeBidi([None] IDWriteTextAnalysisSource* analysisSource,[None] int textPosition,[None] int textLength,[None] IDWriteTextAnalysisSink* analysisSink)</unmanaged>
 /// <remarks>
 /// While the function can handle multiple paragraphs, the text range should not arbitrarily split the middle of paragraphs. Otherwise, the returned levels may be wrong, because the Bidi algorithm is meant to apply to the paragraph as a whole.
 /// </remarks>
 public void AnalyzeBidi(TextAnalysisSource analysisSource, int textPosition, int textLength, TextAnalysisSink analysisSink)
 {
     AnalyzeBidi__(TextAnalysisSourceShadow.ToIntPtr(analysisSource), textPosition, textLength, TextAnalysisSinkShadow.ToIntPtr(analysisSink));
 }
Beispiel #9
0
 /// <summary>
 /// Analyzes a text range for potential breakpoint opportunities, reading attributes from the source and reporting breakpoint opportunities to the sink callback {{SetLineBreakpoints}}.
 /// </summary>
 /// <param name="analysisSource">A reference to the source object to analyze.</param>
 /// <param name="textPosition">The starting text position within the source object.</param>
 /// <param name="textLength">The text length to analyze.</param>
 /// <param name="analysisSink">A reference to the  sink callback object that receives the text analysis.</param>
 /// <returns>
 /// If the method succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code.
 /// </returns>
 /// <unmanaged>HRESULT IDWriteTextAnalyzer::AnalyzeLineBreakpoints([None] IDWriteTextAnalysisSource* analysisSource,[None] int textPosition,[None] int textLength,[None] IDWriteTextAnalysisSink* analysisSink)</unmanaged>
 /// <remarks>
 /// Although the function can handle multiple paragraphs, the text range should not arbitrarily split the middle of paragraphs, unless the specified text span is considered a whole unit. Otherwise, the returned properties for the first and last characters will inappropriately allow breaks.
 /// </remarks>
 public void AnalyzeLineBreakpoints(TextAnalysisSource analysisSource, int textPosition, int textLength, TextAnalysisSink analysisSink)
 {
     AnalyzeLineBreakpoints__(TextAnalysisSourceShadow.ToIntPtr(analysisSource), textPosition, textLength, TextAnalysisSinkShadow.ToIntPtr(analysisSink));
 }