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 SetGlyphRun(float x, float y, int glyphCount, short[] glyphIndices, float[] glyphAdvances, GlyphOffset[] glyphOffsets, FontFace fontface, float fontEmSize, int BidiLevel, bool isSideways) { // Append this glyph run to the list. int glyphStart = glyphAdvances_.Count; glyphAdvances_.AddRange(glyphAdvances); glyphIndices_.AddRange(glyphIndices); glyphOffsets_.AddRange(glyphOffsets); glyphRuns_.Add(new CustomGlyphRun(fontface, fontEmSize, x, y, glyphStart, glyphCount, BidiLevel, isSideways)); }
/// ///<summary> /// Save the collection to a xml node /// </summary> /// public bool Save(XmlWriter xml) { if (xml == null) { return(false); } xml.WriteStartElement(Tag); xml.WriteAttributeString("name", Name); if (!string.IsNullOrEmpty(TextureName)) { } else if (!string.IsNullOrEmpty(TTFFileName)) { } else if (!string.IsNullOrEmpty(TileSetName)) { xml.WriteStartElement("tileset"); xml.WriteAttributeString("name", TileSetName); xml.WriteEndElement(); } xml.WriteStartElement("offset"); xml.WriteAttributeString("value", GlyphOffset.ToString()); xml.WriteEndElement(); xml.WriteStartElement("advance"); xml.WriteAttributeString("value", Advance.ToString()); xml.WriteEndElement(); xml.WriteStartElement("interline"); xml.WriteAttributeString("value", Interline.ToString()); xml.WriteEndElement(); xml.WriteEndElement(); return(false); }
internal unsafe void __MarshalFrom(ref __Native @ref) { FontFace = (@ref.FontFace == IntPtr.Zero) ? null : new IDWriteFontFace(@ref.FontFace); if (FontFace != null) { FontFace.AddRef(); } FontEmSize = @ref.FontEmSize; if (@ref.GlyphIndices != IntPtr.Zero) { Indices = new ushort[@ref.GlyphCount]; if (@ref.GlyphCount > 0) { UnsafeUtilities.Read(@ref.GlyphIndices, Indices, @ref.GlyphCount); } } if (@ref.GlyphAdvances != IntPtr.Zero) { Advances = new float[@ref.GlyphCount]; if (@ref.GlyphCount > 0) { UnsafeUtilities.Read(@ref.GlyphAdvances, Advances, @ref.GlyphCount); } } if (@ref.GlyphOffsets != IntPtr.Zero) { Offsets = new GlyphOffset[@ref.GlyphCount]; if (@ref.GlyphCount > 0) { UnsafeUtilities.Read(@ref.GlyphOffsets, Offsets, @ref.GlyphCount); } } IsSideways = @ref.IsSideways; BidiLevel = @ref.BidiLevel; }
protected bool ProduceGlyphRuns(FlowLayoutSink flowSink, ref RectangleF rect, ref ClusterPosition clusterStart, ref ClusterPosition clusterEnd) { // Produce a series of glyph runs from the given range // and send them to the sink. If the entire text fit // into the rect, then we'll only pass on a single glyph // run. //////////////////////////////////////// // Figure out how many runs we cross, because this is the number // of distinct glyph runs we'll need to reorder visually. int runIndexEnd = clusterEnd.runIndex; if (clusterEnd.textPosition > runs_[runIndexEnd].textStart) { ++runIndexEnd; // Only partially cover the run, so round up. } int[] bidiOrdering = new int[100]; int totalRuns = Math.Min(bidiOrdering.Length, runIndexEnd - clusterStart.runIndex); ProduceBidiOrdering(clusterStart.runIndex, totalRuns, bidiOrdering); //////////////////////////////////////// // Ignore any trailing whitespace // Look backward from end until we find non-space. int trailingWsPosition; for (trailingWsPosition = clusterEnd.textPosition; trailingWsPosition > clusterStart.textPosition; --trailingWsPosition) { if (!breakpoints_[trailingWsPosition - 1].IsWhitespace) { break; // Encountered last significant character. } } // Set the glyph run's ending cluster to the last whitespace. ClusterPosition clusterWsEnd = clusterStart; SetClusterPosition(ref clusterWsEnd, trailingWsPosition); //////////////////////////////////////// // Produce justified advances to reduce the jagged edge. List <float> justifiedAdvances; ProduceJustifiedAdvances(ref rect, ref clusterStart, ref clusterWsEnd, out justifiedAdvances); int justificationGlyphStart = GetClusterGlyphStart(ref clusterStart); //////////////////////////////////////// // Determine starting point for alignment. float x = rect.Left; float y = rect.Bottom; FontMetrics fontMetrics = fontFace_.Metrics; float descent = (fontMetrics.Descent * fontEmSize_ / fontMetrics.DesignUnitsPerEm); y -= descent; if (readingDirection_ == ReadingDirection.RightToLeft) { // For RTL, we neeed the run width to adjust the origin // so it starts on the right side. int glyphStart = GetClusterGlyphStart(ref clusterStart); int glyphEnd = GetClusterGlyphStart(ref clusterWsEnd); if (glyphStart < glyphEnd) { float lineWidth = GetClusterRangeWidth( glyphStart - justificationGlyphStart, glyphEnd - justificationGlyphStart, justifiedAdvances.ToArray() ); x = rect.Right - lineWidth; } } //////////////////////////////////////// // Send each glyph run to the sink. for (int i = 0; i < totalRuns; ++i) { Run run = runs_[bidiOrdering[i]]; int glyphStart = run.glyphStart; int glyphEnd = glyphStart + run.glyphCount; // If the run is only partially covered, we'll need to find // the subsection of glyphs that were fit. if (clusterStart.textPosition > run.textStart) { glyphStart = GetClusterGlyphStart(ref clusterStart); } if (clusterWsEnd.textPosition < run.textStart + run.textLength) { glyphEnd = GetClusterGlyphStart(ref clusterWsEnd); } if ((glyphStart >= glyphEnd) || (run.script.Shapes == ScriptShapes.NoVisual)) { // The itemizer told us not to draw this character, // either because it was a formatting, control, or other hidden character. continue; } // The run width is needed to know how far to move forward, // and to flip the origin for right-to-left text. float runWidth = GetClusterRangeWidth( glyphStart - justificationGlyphStart, glyphEnd - justificationGlyphStart, justifiedAdvances.ToArray() ); // Flush this glyph run. //int glyphCount = glyphEnd - glyphStart; int glyphCount = justifiedAdvances.Count; short[] call_glyphIndices_ = new short[glyphCount]; Array.Copy(glyphIndices_, glyphStart, call_glyphIndices_, 0, call_glyphIndices_.Length); float[] call_justifiedAdvances = new float[justifiedAdvances.Count - (glyphStart - justificationGlyphStart)]; justifiedAdvances.CopyTo(glyphStart - justificationGlyphStart, call_justifiedAdvances, 0, call_justifiedAdvances.Length); GlyphOffset[] call_glyphOffsets_ = new GlyphOffset[glyphCount]; Array.Copy(glyphOffsets_, glyphStart, call_glyphOffsets_, 0, call_glyphOffsets_.Length); flowSink.SetGlyphRun( (run.bidiLevel % 2 != 0) ? (x + runWidth) : (x), // origin starts from right if RTL y, glyphCount, call_glyphIndices_, call_justifiedAdvances, call_glyphOffsets_, fontFace_, fontEmSize_, run.bidiLevel, run.isSideways ); x += runWidth; } return(true); }
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; do { short[] call_glyphClusters_ = new short[glyphClusters_.Length - textStart]; short[] call_glyphIndices_ = new short[glyphIndices_.Length - glyphStart]; var result = textAnalyzer.GetGlyphs( text_.Substring(textStart, textLength), textLength, fontFace_, run.isSideways, (run.bidiLevel % 2 == 1), run.script, localName_, run.isNumberSubstituted ? numberSubstitution_ : null, null, null, 0, 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); tries++; //if (result!=SharpDX.Result.OutOfMemory) if (result != SharpDX.Result.Ok) { // Try again using a larger buffer. maxGlyphCount = EstimateGlyphCount(maxGlyphCount); int totalGlyphsArrayCount = glyphStart + maxGlyphCount; glyphProps = new ShapingGlyphProperties[maxGlyphCount]; glyphIndices_ = new short[totalGlyphsArrayCount]; } else { break; } }while (tries < 2);// We'll give it two chances. // 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); var result2 = 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, 0, 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; }
/// <summary> /// Computes the outline of a run of glyphs by calling back to the outline sink interface. /// </summary> /// <param name="emSize">The logical size of the font in DIP units. A DIP ("device-independent pixel") equals 1/96 inch. </param> /// <param name="glyphIndices">An array of glyph indices. The glyphs are in logical order and the advance direction depends on the isRightToLeft parameter. The array must be allocated and be able to contain the number of elements specified by glyphCount. </param> /// <param name="glyphAdvances">An optional array of glyph advances in DIPs. The advance of a glyph is the amount to advance the position (in the direction of the baseline) after drawing the glyph. glyphAdvances contains the number of elements specified by glyphIndices.Length. </param> /// <param name="glyphOffsets">An optional array of glyph offsets, each of which specifies the offset along the baseline and offset perpendicular to the baseline of a glyph relative to the current pen position. glyphOffsets contains the number of elements specified by glyphIndices.Length. </param> /// <param name="isSideways">If TRUE, the ascender of the glyph runs alongside the baseline. If FALSE, the glyph ascender runs perpendicular to the baseline. For example, an English alphabet on a vertical baseline would have isSideways set to FALSE. A client can render a vertical run by setting isSideways to TRUE and rotating the resulting geometry 90 degrees to the right using a transform. The isSideways and isRightToLeft parameters cannot both be true. </param> /// <param name="isRightToLeft">The visual order of the glyphs. If this parameter is FALSE, then glyph advances are from left to right. If TRUE, the advance direction is right to left. By default, the advance direction is left to right. </param> /// <param name="geometrySink">A reference to the interface that is called back to perform outline drawing operations. </param> /// <returns>If the method succeeds, it returns S_OK. Otherwise, it returns an HRESULT error code. </returns> /// <unmanaged>HRESULT IDWriteFontFace::GetGlyphRunOutline([None] float emSize,[In, Buffer] const short* glyphIndices,[In, Buffer, Optional] const float* glyphAdvances,[In, Buffer, Optional] const DWRITE_GLYPH_OFFSET* glyphOffsets,[None] int glyphCount,[None] BOOL isSideways,[None] BOOL isRightToLeft,[None] IDWriteGeometrySink* geometrySink)</unmanaged> public void GetGlyphRunOutline(float emSize, short[] glyphIndices, float[] glyphAdvances, GlyphOffset[] glyphOffsets, bool isSideways, bool isRightToLeft, SimplifiedGeometrySink geometrySink) { IntPtr ptr; if ( geometrySink is GeometrySink) ptr = GeometrySinkShadow.ToIntPtr((GeometrySink)geometrySink); else ptr = SimplifiedGeometrySinkShadow.ToIntPtr(geometrySink); GetGlyphRunOutline_(emSize, glyphIndices, glyphAdvances, glyphOffsets, glyphIndices.Length, isSideways, isRightToLeft, ptr); }
/// <summary> /// Construct a full description of glyph data /// </summary> internal Glyphs( TextShapeableSymbols shapeable, char[] charArray, int[] glyphAdvances, ushort[] clusterMap, ushort[] glyphIndices, GlyphOffset[] glyphOffsets, double scalingFactor ) { _shapeable = shapeable; _charArray = charArray; // create double array for glyph run creation, because Shaping is all done in // ideal units. FormattedTextSymbol is used to draw text collapsing symbols // which usually contains very few glyphs. Using double[] and Point[] directly // is more efficient. _glyphAdvances = new double[glyphAdvances.Length]; double ToReal = 1.0 / scalingFactor; for (int i = 0; i < glyphAdvances.Length; i++) { _glyphAdvances[i] = glyphAdvances[i] * ToReal; _width += _glyphAdvances[i]; } if (glyphIndices != null) { _clusterMap = clusterMap; if (glyphOffsets != null) { _glyphOffsets = new PartialArray<Point>(new Point[glyphOffsets.Length]); for (int i = 0; i < glyphOffsets.Length; i++) { _glyphOffsets[i] = new Point( glyphOffsets[i].du * ToReal, glyphOffsets[i].dv * ToReal ); } } Debug.Assert(glyphAdvances.Length <= glyphIndices.Length); if (glyphAdvances.Length != glyphIndices.Length) { _glyphIndices = new ushort[glyphAdvances.Length]; for (int i = 0; i < glyphAdvances.Length; i++) { _glyphIndices[i] = glyphIndices[i]; } } else { _glyphIndices = glyphIndices; } } }
protected bool ProduceGlyphRuns(FlowLayoutSink flowSink, ref RectangleF rect, ref ClusterPosition clusterStart, ref ClusterPosition clusterEnd) { // Produce a series of glyph runs from the given range // and send them to the sink. If the entire text fit // into the rect, then we'll only pass on a single glyph // run. //////////////////////////////////////// // Figure out how many runs we cross, because this is the number // of distinct glyph runs we'll need to reorder visually. int runIndexEnd = clusterEnd.runIndex; if (clusterEnd.textPosition > runs_[runIndexEnd].textStart) ++runIndexEnd; // Only partially cover the run, so round up. int[] bidiOrdering = new int[100]; int totalRuns = Math.Min(bidiOrdering.Length, runIndexEnd - clusterStart.runIndex); ProduceBidiOrdering(clusterStart.runIndex, totalRuns, bidiOrdering); //////////////////////////////////////// // Ignore any trailing whitespace // Look backward from end until we find non-space. int trailingWsPosition; for (trailingWsPosition = clusterEnd.textPosition; trailingWsPosition > clusterStart.textPosition; --trailingWsPosition) { if (!breakpoints_[trailingWsPosition - 1].IsWhitespace) break; // Encountered last significant character. } // Set the glyph run's ending cluster to the last whitespace. ClusterPosition clusterWsEnd = clusterStart; SetClusterPosition(ref clusterWsEnd, trailingWsPosition); //////////////////////////////////////// // Produce justified advances to reduce the jagged edge. List<float> justifiedAdvances; ProduceJustifiedAdvances(ref rect, ref clusterStart, ref clusterWsEnd, out justifiedAdvances); int justificationGlyphStart = GetClusterGlyphStart(ref clusterStart); //////////////////////////////////////// // Determine starting point for alignment. float x = rect.Left; float y = rect.Bottom; FontMetrics fontMetrics = fontFace_.Metrics; float descent = (fontMetrics.Descent * fontEmSize_ / fontMetrics.DesignUnitsPerEm); y -= descent; if (readingDirection_ == ReadingDirection.RightToLeft) { // For RTL, we neeed the run width to adjust the origin // so it starts on the right side. int glyphStart = GetClusterGlyphStart(ref clusterStart); int glyphEnd = GetClusterGlyphStart(ref clusterWsEnd); if (glyphStart < glyphEnd) { float lineWidth = GetClusterRangeWidth( glyphStart - justificationGlyphStart, glyphEnd - justificationGlyphStart, justifiedAdvances.ToArray() ); x = rect.Right - lineWidth; } } //////////////////////////////////////// // Send each glyph run to the sink. for (int i = 0; i < totalRuns; ++i) { Run run = runs_[bidiOrdering[i]]; int glyphStart = run.glyphStart; int glyphEnd = glyphStart + run.glyphCount; // If the run is only partially covered, we'll need to find // the subsection of glyphs that were fit. if (clusterStart.textPosition > run.textStart) { glyphStart = GetClusterGlyphStart(ref clusterStart); } if (clusterWsEnd.textPosition < run.textStart + run.textLength) { glyphEnd = GetClusterGlyphStart(ref clusterWsEnd); } if ((glyphStart >= glyphEnd) || (run.script.Shapes == ScriptShapes.NoVisual)) { // The itemizer told us not to draw this character, // either because it was a formatting, control, or other hidden character. continue; } // The run width is needed to know how far to move forward, // and to flip the origin for right-to-left text. float runWidth = GetClusterRangeWidth( glyphStart - justificationGlyphStart, glyphEnd - justificationGlyphStart, justifiedAdvances.ToArray() ); // Flush this glyph run. //int glyphCount = glyphEnd - glyphStart; int glyphCount = justifiedAdvances.Count; short[] call_glyphIndices_ = new short[glyphCount]; Array.Copy(glyphIndices_, glyphStart, call_glyphIndices_, 0, call_glyphIndices_.Length); float[] call_justifiedAdvances = new float[justifiedAdvances.Count - (glyphStart - justificationGlyphStart)]; justifiedAdvances.CopyTo(glyphStart - justificationGlyphStart, call_justifiedAdvances, 0, call_justifiedAdvances.Length); GlyphOffset[] call_glyphOffsets_ = new GlyphOffset[glyphCount]; Array.Copy(glyphOffsets_, glyphStart, call_glyphOffsets_, 0, call_glyphOffsets_.Length); flowSink.SetGlyphRun( (run.bidiLevel % 2 != 0) ? (x + runWidth) : (x), // origin starts from right if RTL y, glyphCount, call_glyphIndices_, call_justifiedAdvances, call_glyphOffsets_, fontFace_, fontEmSize_, run.bidiLevel, run.isSideways ); x += runWidth; } return true; }
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_; }
private unsafe GlyphRun ComputeShapedGlyphRun( LSRun lsrun, // ls run TextFormatterImp textFormatterImp, // The TextFormatter Implementation bool originProvided, // flag indicate whether the origin of the run is provided LSPOINT lsrunOrigin, // physical start of the run int charCount, // characters count char *pwchText, // characters for the GlyphRun ushort *puClusterMap, // cluster map int glyphCount, // glyph count ushort *puGlyphs, // glyph indices int *piJustifiedGlyphAdvances, // glyph advances GlyphOffset *piiGlyphOffsets, // glyph offsets bool justify ) { TextMetrics.FullTextLine currentLine = Draw.CurrentLine; Point runOrigin = new Point(); int nominalX = 0; int nominalY = 0; if (originProvided) { if (currentLine.RightToLeft) { lsrunOrigin.x = -lsrunOrigin.x; } if (textFormatterImp.TextFormattingMode == TextFormattingMode.Display && justify) { LSRun.UVToNominalXY( Draw.LineOrigin, Draw.VectorToLineOrigin, currentLine.LSLineUToParagraphU(lsrunOrigin.x), lsrunOrigin.y + lsrun.BaselineMoveOffset, currentLine, out nominalX, out nominalY ); } else { runOrigin = LSRun.UVToXY( Draw.LineOrigin, Draw.VectorToLineOrigin, currentLine.LSLineUToParagraphU(lsrunOrigin.x), lsrunOrigin.y + lsrun.BaselineMoveOffset, currentLine ); } } char[] charString = new char[charCount]; ushort[] clusterMap = new ushort[charCount]; for (int i = 0; i < charCount; i++) { charString[i] = pwchText[i]; clusterMap[i] = puClusterMap[i]; } ushort[] glyphIndices = new ushort[glyphCount]; IList<double> glyphAdvances; IList<Point> glyphOffsets; bool isRightToLeft = (lsrun.BidiLevel & 1) != 0; if (textFormatterImp.TextFormattingMode == TextFormattingMode.Ideal) { glyphAdvances = new ThousandthOfEmRealDoubles(textFormatterImp.IdealToReal(lsrun.EmSize), glyphCount); glyphOffsets = new ThousandthOfEmRealPoints(textFormatterImp.IdealToReal(lsrun.EmSize), glyphCount); for (int i = 0; i < glyphCount; i++) { glyphIndices[i] = puGlyphs[i]; glyphAdvances[i] = textFormatterImp.IdealToReal(piJustifiedGlyphAdvances[i]); glyphOffsets[i] = new Point( textFormatterImp.IdealToReal(piiGlyphOffsets[i].du), textFormatterImp.IdealToReal(piiGlyphOffsets[i].dv) ); } } else { if (justify) { AdjustMetricsForDisplayModeJustifiedText( pwchText, piJustifiedGlyphAdvances, glyphCount, isRightToLeft, nominalX, nominalY, out runOrigin, out glyphAdvances ); } else { glyphAdvances = new List<double>(glyphCount); for (int i = 0; i < glyphCount; i++) { glyphAdvances.Add(textFormatterImp.IdealToReal(piJustifiedGlyphAdvances[i])); } } glyphOffsets = new List<Point>(glyphCount); for (int i = 0; i < glyphCount; i++) { glyphIndices[i] = puGlyphs[i]; glyphOffsets.Add(new Point( textFormatterImp.IdealToReal(piiGlyphOffsets[i].du), textFormatterImp.IdealToReal(piiGlyphOffsets[i].dv) )); } } #if CHECK_GLYPHS if ( lsrun._glyphs != null && glyphCount <= lsrun._glyphs.Length) { for (int i = 0; i < glyphCount; i++) { Debug.Assert(glyphIndices[i] == lsrun._glyphs[i], "Corrupted glyphs"); } } #endif GlyphRun glyphRun = lsrun.Shapeable.ComputeShapedGlyphRun( runOrigin, charString, clusterMap, glyphIndices, glyphAdvances, glyphOffsets, isRightToLeft, false // no sideway support yet ); return glyphRun; }
internal unsafe LsErr EnumText( IntPtr pols, // ls context Plsrun plsrun, // plsrun int cpFirst, // first cp of the ls dnode int dcp, // dcp of the dnode char *pwchText, // characters for glyph run int cchText, // length of characters LsTFlow lstFlow, // flow direction int fReverseOrder, // flag for reverse order enumeration int fGeometryProvided, // flag for providing geometry ref LSPOINT pptStart, // [in] logical start of the run ref LsHeights pheights, // [in] height (iff geometryProvided) int dupRun, // width of the run int glyphBaseRun, // flag for glyph based run int *piCharAdvances, // character advance widths (iff !glyphBaseRun) ushort *puClusterMap, // cluster map (iff glyphBaseRun) ushort *characterProperties, // character properties (iff glyphBaseRun) ushort *puGlyphs, // glyph indices (iff glyphBaseRun) int *piJustifiedGlyphAdvances, // glyph advances (iff glyphBaseRun) GlyphOffset *piiGlyphOffsets, // glyph offsets (iff glyphBaseRun) uint *piGlyphProperties, // glyph properties (iff glyphProperties) int glyphCount // glyph count ) { Debug.Assert(fGeometryProvided == 0, "Line enumeration doesn't need geometry information"); if (cpFirst < 0) { return LsErr.None; } LsErr lserr = LsErr.None; LSRun lsrun = null; try { TextMetrics.FullTextLine currentLine = Draw.CurrentLine; lsrun = currentLine.GetRun(plsrun); GlyphRun glyphRun = null; if (glyphBaseRun != 0) { if (glyphCount > 0) { glyphRun = ComputeShapedGlyphRun( lsrun, currentLine.Formatter, false, // glyph run origin not provided pptStart, cchText, pwchText, puClusterMap, glyphCount, puGlyphs, piJustifiedGlyphAdvances, piiGlyphOffsets, currentLine.IsJustified ); } } else if (cchText > 0) { dupRun = 0; for (int i = 0; i < cchText; i++) { dupRun += piCharAdvances[i]; } glyphRun = ComputeUnshapedGlyphRun( lsrun, lstFlow, currentLine.Formatter, false, // glyph run origin not provided at enumeration pptStart, dupRun, cchText, pwchText, piCharAdvances, currentLine.IsJustified ); } if (glyphRun != null) { IndexedGlyphRuns.Add( new IndexedGlyphRun( currentLine.GetExternalCp(cpFirst), dcp, glyphRun ) ); } } catch (Exception e) { SaveException(e, plsrun, lsrun); lserr = LsErr.ClientAbort; } catch { SaveNonCLSException("EnumText", plsrun, lsrun); lserr = LsErr.ClientAbort; } return lserr; }
internal unsafe LsErr DrawGlyphs( IntPtr pols, // Line Layout context Plsrun plsrun, // plsrun char* pwchText, // character string ushort* puClusterMap, // character-to-cluster map ushort* puCharProperties, // character properties int charCount, // character count ushort* puGlyphs, // glyph indices int* piJustifiedGlyphAdvances, // justified glyph advances int* piGlyphAdvances, // original ideal glyph advances GlyphOffset* piiGlyphOffsets, // glyph offsets uint* piGlyphProperties, // glyph properties LsExpType* plsExpType, // glyph expansion types int glyphCount, // glyph count LsTFlow textFlow, // text flow uint displayMode, // draw transparent or opaque ref LSPOINT ptRun, // [in] display position (at baseline) ref LsHeights lsHeights, // [in] run height metrics int runWidth, // run overall advance width ref LSRECT clippingRect // [in] clipping rectangle if any applied ) { LsErr lserr = LsErr.None; LSRun lsrun = null; try { TextMetrics.FullTextLine currentLine = Draw.CurrentLine; lsrun = currentLine.GetRun(plsrun); Debug.Assert(TextStore.IsContent(plsrun) && lsrun.Shapeable != null); GlyphRun glyphRun = ComputeShapedGlyphRun( lsrun, currentLine.Formatter, true, // origin of the glyph run provided at drawing time ptRun, charCount, pwchText, puClusterMap, glyphCount, puGlyphs, piJustifiedGlyphAdvances, piiGlyphOffsets, currentLine.IsJustified ); if (glyphRun != null) { DrawingContext drawingContext = Draw.DrawingContext; Draw.SetGuidelineY(glyphRun.BaselineOrigin.Y); try { _boundingBox.Union( lsrun.DrawGlyphRun( drawingContext, null, // draw with the run's foreground glyphRun ) ); } finally { Draw.UnsetGuidelineY(); } } } catch (Exception e) { SaveException(e, plsrun, lsrun); lserr = LsErr.ClientAbort; } catch { SaveNonCLSException("DrawGlyphs", plsrun, lsrun); lserr = LsErr.ClientAbort; } return lserr; }
internal unsafe LsErr GetGlyphPositions( IntPtr pols, // Line Layout context IntPtr* plsplsruns, // array of plsruns int* pcchPlsrun, // array of character count per run int plsrunCount, // number of runs LsDevice device, // on reference or presentation device char* pwchText, // character string ushort* puClusterMap, // character-to-glyph cluster map ushort* puCharProperties, // character properties int cchText, // character count ushort* puGlyphs, // glyph indices uint* piGlyphProperties, // glyph properties int glyphCount, // glyph count LsTFlow textFlow, // text flow direction int* piGlyphAdvances, // [out] glyph advances GlyphOffset* piiGlyphOffsets // [out] glyph offsets ) { LsErr lserr = LsErr.None; LSRun lsrunFirst = null; try { LSRun[] lsruns = RemapLSRuns(plsplsruns, plsrunCount); lsrunFirst = lsruns[0]; bool isRightToLeft = ((lsrunFirst.BidiLevel & 1) != 0); GlyphOffset[] glyphOffset; GlyphTypeface glyphTypeface = lsrunFirst.Shapeable.GlyphTypeFace; DWriteFontFeature[][] fontFeatures; uint[] fontFeatureRanges; LSRun.CompileFeatureSet(lsruns, pcchPlsrun, checked((uint)cchText), out fontFeatures, out fontFeatureRanges); FullText.Formatter.TextAnalyzer.GetGlyphPlacements( (ushort*)pwchText, puClusterMap, (ushort*)puCharProperties, (uint)cchText, puGlyphs, piGlyphProperties, (uint)glyphCount, glyphTypeface.FontDWrite, lsrunFirst.Shapeable.EmSize, TextFormatterImp.ToIdeal, false, isRightToLeft, lsrunFirst.RunProp.CultureInfo, fontFeatures, fontFeatureRanges, FullText.TextFormattingMode, lsrunFirst.Shapeable.ItemProps, Util.PixelsPerDip, piGlyphAdvances, out glyphOffset ); for (int i = 0; i < glyphCount; ++i) { piiGlyphOffsets[i].du = glyphOffset[i].du; piiGlyphOffsets[i].dv = glyphOffset[i].dv; } } catch (Exception e) { SaveException(e, (Plsrun)(plsplsruns[0]), lsrunFirst); lserr = LsErr.ClientAbort; } catch { SaveNonCLSException("GetGlyphPositions", (Plsrun)(plsplsruns[0]), lsrunFirst); lserr = LsErr.ClientAbort; } return lserr; }
/// <summary> /// This method retrieves the geometry for a formatted cluster. /// </summary> /// <param name="clusterIndex"> This parameter indicates the formatted cluster index. </param> /// <param name="bidiLevel"> This parameter indicates the bidi level of the cluster. </param> /// <param name="font"> This parameter references the font for the cluster. </param> /// <param name="input"> This parameter references the formatted text output. </param> /// <returns> This method returns the geometry for the formatted cluster if available; otherwise, this method returns <c>null</c> . </returns> public Shape Retrieve( IndexedRange glyphRange, bool isVertical, bool isRightToLeft, FontHandle font, List<Formatting.TextShaper.Glyph> glyphs) { ResizeInternalBuffers(glyphRange.Length); int index = 0; foreach(int itemIndex in glyphRange) { _GlyphAdvances[index] = glyphs[itemIndex].Advance; _GlyphIndices[index] = glyphs[itemIndex].Index; _GlyphOffsets[index] = new GlyphOffset { AdvanceOffset = glyphs[itemIndex].Offset.Width, AscenderOffset = glyphs[itemIndex].Offset.Height }; ++index; } TextGeometryKey key; key.Advances = _GlyphAdvances; key.Indices = _GlyphIndices; key.Offsets = _GlyphOffsets; Dictionary<TextGeometryKey, Shape> cacheForFont; if(_Cache.TryGetValue(font, out cacheForFont)) { Shape result; if(cacheForFont.TryGetValue(key, out result)) { return result; } } else { cacheForFont = new Dictionary<TextGeometryKey, Shape>(); _Cache.Add(font, cacheForFont); } Shape geometry = _Sink.CreateGeometry( key, isRightToLeft, isVertical, font); TextGeometryKey newKey; newKey.Advances = (float[])key.Advances.Clone(); newKey.Indices = (short[])key.Indices.Clone(); newKey.Offsets = (GlyphOffset[])key.Offsets.Clone(); cacheForFont.Add(newKey, geometry); return geometry; }