/// <summary> /// Analyzes the text using each of the analyzers and returns /// their results as a series of runs. /// </summary> public void GenerateResults(TextAnalyzer textAnalyzer, out Run[] runs, out LineBreakpoint[] breakpoints) { // Initially start out with one result that covers the entire range. // This result will be subdivided by the analysis processes. LinkedRun initialRun = new LinkedRun() { nextRunIndex = 0, textStart = 0, textLength = text_.Length, bidiLevel = (readingDirection_ == ReadingDirection.RightToLeft) ? 1 : 0 }; runs_ = new List<LinkedRun>(); runs_.Add(initialRun); breakpoints_ = new List<LineBreakpoint>(); textAnalyzer.AnalyzeLineBreakpoints(this, 0, text_.Length, this); textAnalyzer.AnalyzeBidi(this, 0, text_.Length, this); textAnalyzer.AnalyzeScript(this, 0, text_.Length, this); textAnalyzer.AnalyzeNumberSubstitution(this, 0, text_.Length, this); //Call each of the analyzers in sequence, recording their results. breakpoints = new LineBreakpoint[breakpoints_.Count]; breakpoints_.CopyTo(breakpoints); // Resequence the resulting runs in order before returning to caller. runs = new Run[runs_.Count]; int nextRunIndex = 0; for (int i = 0; i < runs_.Count; i++) { runs[i] = runs_[nextRunIndex].AsRun; nextRunIndex = runs_[nextRunIndex].nextRunIndex; } }
/// <summary> /// This constructor initializes a new instance of this class. /// </summary> /// <param name="factory"> This parameter references the DirectWrite factory. </param> public Analyzer(Factory factory) { Contract.Requires(factory != null); _Characters = new CharacterProperties[0]; _TextAnalyzer = new TextAnalyzer(factory); }
public TextAnalyzer(FontDevice fontDevice, TextTextShaper textShaper) { Contract.Requires(fontDevice != null); _TextAnalyzer = new SharpDX.DirectWrite.TextAnalyzer(fontDevice.Factory); _TextShaper = textShaper; }
/// <summary> /// Initializes a new instance of the <see cref="NeovimControl"/> class. /// </summary> /// <param name="parent">The parent control.</param> /// <param name="neovimClient">the neovim client.</param> public NeovimControl(IElement parent, NeovimClient.NeovimClient neovimClient) : base(parent) { this.neovimClient = neovimClient; this.neovimClient.Redraw += this.Invalidate; this.neovimClient.FontChanged += this.OnFontChanged; this.textAnalyzer = new DWrite.TextAnalyzer(this.factoryDWrite); this.cursorEffects = new CursorEffects(this.DeviceContext); this.brushCache = new BrushCache(); this.scriptAnalysesCache = new ScriptAnalysesCache(); this.fontCache = new FontCache(this.factoryDWrite); this.textParam = new TextLayoutParameters( this.factoryDWrite, "Consolas", 11, false, false, false, false, this.Factory.DesktopDpi); }
protected override void Dispose(bool disposing) { if(disposing) { _TextAnalyzer.Dispose(); _TextAnalyzer = null; } base.Dispose(disposing); }
protected void ShapeGlyphRun(TextAnalyzer textAnalyzer, int runIndex, ref int glyphStart) { // Shapes a single run of text into glyphs. // Alternately, you could iteratively interleave shaping and line // breaking to reduce the number glyphs held onto at once. It's simpler // for this demostration to just do shaping and line breaking as two // separate processes, but realize that this does have the consequence that // certain advanced fonts containing line specific features (like Gabriola) // will shape as if the line is not broken. Run run = runs_[runIndex]; int textStart = run.textStart; int textLength = run.textLength; int maxGlyphCount = glyphIndices_.Length - glyphStart; int actualGlyphCount = 0; run.glyphStart = glyphStart; run.glyphCount = 0; if (textLength == 0) return;// Nothing to do.. // Allocate space for shaping to fill with glyphs and other information, // with about as many glyphs as there are text characters. We'll actually // need more glyphs than codepoints if they are decomposed into separate // glyphs, or fewer glyphs than codepoints if multiple are substituted // into a single glyph. In any case, the shaping process will need some // room to apply those rules to even make that determintation. if (textLength > maxGlyphCount) { maxGlyphCount = EstimateGlyphCount(textLength); int totalGlyphsArrayCount = glyphStart + maxGlyphCount; short[] Resized_glyphIndices_ = new short[totalGlyphsArrayCount]; glyphIndices_.CopyTo(Resized_glyphIndices_, 0); glyphIndices_ = Resized_glyphIndices_; } ShapingTextProperties[] textProps = new ShapingTextProperties[textLength]; ShapingGlyphProperties[] glyphProps = new ShapingGlyphProperties[maxGlyphCount]; // Get the glyphs from the text, retrying if needed. int tries = 0; while (tries < 2)// We'll give it two chances. { short[] call_glyphClusters_ = new short[glyphClusters_.Length - textStart]; short[] call_glyphIndices_ = new short[glyphIndices_.Length - glyphStart]; bool isDone = false; try { textAnalyzer.GetGlyphs( text_.Substring(textStart, textLength), textLength, fontFace_, run.isSideways, (run.bidiLevel % 2 == 1), run.script, localName_, run.isNumberSubstituted ? numberSubstitution_ : null, null, null, maxGlyphCount, call_glyphClusters_, textProps, call_glyphIndices_, glyphProps, out actualGlyphCount); Array.Copy(call_glyphClusters_, 0, glyphClusters_, textStart, call_glyphClusters_.Length); Array.Copy(call_glyphIndices_, 0, glyphIndices_, glyphStart, call_glyphIndices_.Length); isDone = true; } finally { tries++; // Try again using a larger buffer. maxGlyphCount = EstimateGlyphCount(maxGlyphCount); int totalGlyphsArrayCount = glyphStart + maxGlyphCount; glyphProps = new ShapingGlyphProperties[maxGlyphCount]; glyphIndices_ = new short[totalGlyphsArrayCount]; } if (isDone) break; } // Get the placement of the all the glyphs. if (glyphAdvances_.Length < glyphStart + actualGlyphCount) { float[] Resized_glyphAdvances_ = new float[glyphStart + actualGlyphCount]; glyphAdvances_.CopyTo(Resized_glyphAdvances_, 0); glyphAdvances_ = Resized_glyphAdvances_; } if (glyphOffsets_.Length < glyphStart + actualGlyphCount) { GlyphOffset[] Resized_glyphOffsets_ = new GlyphOffset[glyphStart + actualGlyphCount]; glyphOffsets_.CopyTo(Resized_glyphOffsets_, 0); glyphOffsets_ = Resized_glyphOffsets_; } short[] call2_glyphClusters_ = new short[glyphClusters_.Length - textStart]; Array.Copy(glyphClusters_, textStart, call2_glyphClusters_, 0, call2_glyphClusters_.Length); short[] call2_glyphIndices_ = new short[glyphIndices_.Length - glyphStart]; Array.Copy(glyphIndices_, glyphStart, call2_glyphIndices_, 0, call2_glyphIndices_.Length); float[] call2_glyphAdvances_ = new float[glyphAdvances_.Length - glyphStart]; Array.Copy(glyphAdvances_, glyphStart, call2_glyphAdvances_, 0, call2_glyphAdvances_.Length); GlyphOffset[] call2_glyphOffsets_ = new GlyphOffset[glyphOffsets_.Length - glyphStart]; Array.Copy(glyphOffsets_, glyphStart, call2_glyphOffsets_, 0, call2_glyphOffsets_.Length); textAnalyzer.GetGlyphPlacements( text_.Substring(textStart, textLength), call2_glyphClusters_, textProps, textLength, call2_glyphIndices_, glyphProps, actualGlyphCount, fontFace_, fontEmSize_, run.isSideways, run.bidiLevel % 2 == 1, run.script, localName_, null, null, call2_glyphAdvances_, call2_glyphOffsets_); //call2_glyphClusters_.CopyTo(glyphClusters_, textStart); call2_glyphAdvances_.CopyTo(glyphAdvances_, glyphStart); call2_glyphOffsets_.CopyTo(glyphOffsets_, glyphStart); // Certain fonts, like Batang, contain glyphs for hidden control // and formatting characters. So we'll want to explicitly force their // advance to zero. if (run.script.Shapes == ScriptShapes.NoVisual) { for (int i = glyphStart; i < glyphStart + actualGlyphCount; i++) glyphAdvances_[i] = 0; } // Set the final glyph count of this run and advance the starting glyph. run.glyphCount = actualGlyphCount; runs_[runIndex] = run; glyphStart += actualGlyphCount; }
protected void ShapeGlyphRuns(TextAnalyzer textAnalyzer) { // Shapes all the glyph runs in the layout. // Estimate the maximum number of glyph indices needed to hold a string. int estimatedGlyphCount = EstimateGlyphCount(text_.Length); glyphIndices_ = new short[estimatedGlyphCount]; glyphOffsets_ = new GlyphOffset[estimatedGlyphCount]; glyphAdvances_ = new float[estimatedGlyphCount]; glyphClusters_ = new short[text_.Length]; int glyphStart = 0; // Shape each run separately. This is needed whenever script, locale, // or reading direction changes. for (int runIndex = 0; runIndex < runs_.Length; runIndex++) { ShapeGlyphRun(textAnalyzer, runIndex, ref glyphStart); } short[] resized_glyphIndices_ = new short[glyphStart]; Array.Copy(glyphIndices_, 0, resized_glyphIndices_, 0, glyphStart); glyphIndices_ = resized_glyphIndices_; GlyphOffset[] resized_glyphOffsets_ = new GlyphOffset[glyphStart]; Array.Copy(glyphOffsets_, 0, resized_glyphOffsets_, 0, glyphStart); glyphOffsets_ = resized_glyphOffsets_; float[] resized_glyphAdvances_ = new float[glyphStart]; Array.Copy(glyphAdvances_, 0, resized_glyphAdvances_, 0, glyphStart); glyphAdvances_ = resized_glyphAdvances_; }
public void AnalyzeText(String text) { // Perform analysis on the given text, converting text to glyphs. // Analyzes the given text and keeps the results for later reflow. isTextAnalysisComplete_ = false; // Need a font face to determine metrics. if (fontFace_ == null) throw new Exception("FlowLayout: Need a font face to determine metrics."); text_ = text; // Query for the text analyzer's interface. TextAnalyzer textAnalyzer = new TextAnalyzer(dwriteFactory_); // Record the analyzer's results. TextAnalysis textAnalysis = new TextAnalysis(text_, localName_, readingDirection_, numberSubstitution_); textAnalysis.GenerateResults(textAnalyzer, out runs_, out breakpoints_); // Convert the entire text to glyphs. ShapeGlyphRuns(textAnalyzer); isTextAnalysisComplete_ = true; textAnalyzer.Dispose(); }