public static GlyphRun Create(string text, Typeface typeface, double emSize, Vector centerOffset) { GlyphTypeface glyphTypeface; if (!typeface.TryGetGlyphTypeface(out glyphTypeface)) { throw new ArgumentException(string.Format("{0}: no GlyphTypeface found", typeface.FontFamily)); } var glyphIndices = new ushort[text.Length]; var advanceWidths = new double[text.Length]; for (int i = 0; i < text.Length; i++) { var glyphIndex = glyphTypeface.CharacterToGlyphMap[text[i]]; glyphIndices[i] = glyphIndex; advanceWidths[i] = glyphTypeface.AdvanceWidths[glyphIndex] * emSize; } var glyphRun = new GlyphRun(glyphTypeface, 0, false, emSize, glyphIndices, new Point(), advanceWidths, null, null, null, null, null, null); var bbox = glyphRun.ComputeInkBoundingBox(); var baselineOrigin = new Point(centerOffset.X - bbox.X - bbox.Width / 2d, centerOffset.Y - bbox.Y - bbox.Height / 2d); return(new GlyphRun(glyphTypeface, 0, false, emSize, glyphIndices, baselineOrigin, advanceWidths, null, null, null, null, null, null)); }
/// <summary> /// Draw a GlyphRun. /// </summary> public void DrawGlyphRun(Brush foreground, GlyphRun glyphRun) { if (glyphRun != null) { foreground = ReduceBrush(foreground, glyphRun.ComputeInkBoundingBox()); // foreground may be null, but glyphrun may still be a hyperlink _dc.DrawGlyphRun(foreground, glyphRun); } }
/// <summary> /// gets a bounding box holding the overall glyph run /// </summary> /// <param name="glyphRun"></param> /// <param name="origin">where the run will be centered</param> /// <returns></returns> public static Rect GetBoundingBox(this GlyphRun glyphRun, Point origin) { Rect rect = glyphRun.ComputeInkBoundingBox(); Matrix mat = new Matrix(); mat.Translate(origin.X, origin.Y); rect.Transform(mat); return(rect); }
/// <summary> /// 获取<see cref="GlyphRun"/>的Bounds /// </summary> /// <param name="run"></param> /// <returns></returns> public static Rect GetBounds(this GlyphRun run) { var box = run.ComputeInkBoundingBox(); //相对于run.BuildGeometry().Bounds方法,run.ComputeInkBoundingBox()会多出一个厚度为1的框框,所以要减去 if (box.Width >= 2 && box.Height >= 2) { box.Inflate(-1, -1); } return(box); }
public static void DrawGlyphRun(this DrawingContext drawingContext, Brush foreground, GlyphRun glyphRun, Point position, HorizontalAlignment horizontalAlignment, VerticalAlignment verticalAlignment) { var boundingBox = glyphRun.ComputeInkBoundingBox(); switch (horizontalAlignment) { case HorizontalAlignment.Center: position.X -= boundingBox.Width / 2d; break; case HorizontalAlignment.Right: position.X -= boundingBox.Width; break; default: break; } switch (verticalAlignment) { case VerticalAlignment.Center: position.Y -= boundingBox.Height / 2d; break; case VerticalAlignment.Bottom: position.Y -= boundingBox.Height; break; default: break; } drawingContext.PushTransform(new TranslateTransform(position.X - boundingBox.X, position.Y - boundingBox.Y)); drawingContext.DrawGlyphRun(foreground, glyphRun); drawingContext.Pop(); }
/// <summary> /// Draw all formatted glyphruns /// </summary> /// <returns>drawing bounding box</returns> public Rect Draw( DrawingContext drawingContext, Point currentOrigin ) { Rect inkBoundingBox = Rect.Empty; Debug.Assert(_glyphs != null); foreach (Glyphs glyphs in _glyphs) { GlyphRun glyphRun = glyphs.CreateGlyphRun(currentOrigin, _rightToLeft); Rect boundingBox; if (glyphRun != null) { boundingBox = glyphRun.ComputeInkBoundingBox(); if (drawingContext != null) { // Emit glyph run background. glyphRun.EmitBackground(drawingContext, glyphs.BackgroundBrush); drawingContext.PushGuidelineY1(currentOrigin.Y); try { drawingContext.DrawGlyphRun(glyphs.ForegroundBrush, glyphRun); } finally { drawingContext.Pop(); } } } else { boundingBox = Rect.Empty; } if (!boundingBox.IsEmpty) { // glyph run's ink bounding box is relative to its origin boundingBox.X += glyphRun.BaselineOrigin.X; boundingBox.Y += glyphRun.BaselineOrigin.Y; } // accumulate overall ink bounding box inkBoundingBox.Union(boundingBox); if (_rightToLeft) { currentOrigin.X -= glyphs.Width; } else { currentOrigin.X += glyphs.Width; } } return(inkBoundingBox); }
private void Test2() { //Glyph系クラス GlyphRun glyphRun; GlyphRunDrawing glyphRunDrawing; Glyphs glyphs; GlyphTypeface glyphTypeface; FontFamily fontFamily = new FontFamily("Meiryo UI"); var typefaces = fontFamily.GetTypefaces(); Uri myFontUri = null; foreach (var face in typefaces) { face.TryGetGlyphTypeface(out GlyphTypeface gType); myFontUri = gType.FontUri; break; } glyphs = new(); glyphs.FontUri = myFontUri; glyphs.FontRenderingEmSize = 100; //glyphs.StyleSimulations = StyleSimulations.BoldItalicSimulation; glyphs.UnicodeString = "(ゆっくり)"; glyphs.Fill = Brushes.MediumOrchid; //glyphs.BidiLevel = 1; //glyphs.IsSideways = true; GlyphRun gRun = glyphs.ToGlyphRun(); var chars = gRun.Characters; var clustre = gRun.ClusterMap; var box = gRun.ComputeAlignmentBox(); var inkBox = gRun.ComputeInkBoundingBox(); var dfName = gRun.DeviceFontName; var inside = gRun.GetCaretCharacterHitFromDistance(100, out bool isindside); var hit = gRun.GetNextCaretCharacterHit(new System.Windows.Media.TextFormatting.CharacterHit()); var gInd = gRun.GlyphIndices; var gOffset = gRun.GlyphOffsets; var gTypeface = gRun.GlyphTypeface; MyGrid.Children.Add(glyphs); DrawingVisual dv = new(); //dv.Offset = new Vector(0, -100); using (var dc = dv.RenderOpen()) { //dc.DrawRectangle(Brushes.MediumBlue, null, new Rect(0, 0, 500, 500)); //dc.DrawRectangle(Brushes.MediumBlue, null, new Rect(0, 0, box.Width, box.Height)); dc.DrawGlyphRun(Brushes.MediumAquamarine, gRun); } Rect r3 = dv.Drawing.Bounds; Rect r = dv.ContentBounds; Rect r2 = dv.DescendantBounds; var geo = gRun.BuildGeometry(); Rect rectGeo1 = geo.Bounds; Rect rectGeo2 = geo.GetRenderBounds(null); RenderTargetBitmap bitmap = new((int)box.Width, (int)box.Height, 96, 96, PixelFormats.Pbgra32); bitmap.Render(dv); glyphRunDrawing = new(Brushes.MediumAquamarine, gRun); MyGrid.Background = new DrawingBrush(glyphRunDrawing); //Glyphs //FontUri フォント //FontRenderingEmSize フォントサイズみたいなもの //StyleSimulations 太字と斜体の指定 //BidiLevel 文字を並べる向きの指定、0or偶数で左から、奇数で右からになる //ToGlyphRun() GlyphRun作成 //GlyphRun クラス (System.Windows.Media) | Microsoft Docs //https://docs.microsoft.com/ja-jp/dotnet/api/system.windows.media.glyphrun?view=net-6.0 // 同じ描画スタイルが設定され、サイズ、フォント、およびフォントの書体が同じである一連のグリフを表します。 //GlyphRunDrawingと組み合わせて使う //プロパティ //AdvanceWidths //グリフ インデックスに対応するアドバンス幅を表す Double 値の一覧を取得または設定します。 //BaselineOrigin Point //GlyphRun のベースライン原点を取得または設定します。 //BidiLevel Int32 //GlyphRun の双方向の入れ子レベルを取得または設定します。 //CaretStops bool //GlyphRun を表す Unicode で UTF16 コード ポイント毎にキャレット ストップがあるかどうかを決定する Boolean 値の一覧を取得または設定します。 //Characters List<Char> //GlyphRun の Unicode を表す UTF16 コード ポイントの一覧を取得または設定します。 //ClusterMap List<Uint16> //GlyphRun の文字をグリフ インデックスにマップする UInt16 値の一覧を取得または設定します。 //DeviceFontName string //GlyphRun が最適化される対象の、デバイス固有のフォントを取得または設定します。 //FontRenderingEmSize double //GlyphRun のレンダリングに使用する全角サイズを取得または設定します。 //GlyphIndices list<Uint16> //描画物理フォントのグリフ インデックスを表す UInt16 値の配列を取得または設定します。 //GlyphOffsets List<point> //GlyphRun のグリフのオフセットを表す Point 値の配列を取得または設定します。 //GlyphTypeface GlyphTypeface //GlyphTypeface の GlyphRun を取得または設定します。 //IsHitTestable bool //GlyphRun 内に有効なキャレット文字ヒットがあるかどうかを示す値を取得します。 //IsSideways bool //グリフを回転するかどうかを示す値を取得または設定します。 //Language XmlLanguage //XmlLanguage の GlyphRun を取得または設定します。 //PixelsPerDip Single //テキストを表示する PixelsPerDip を取得または設定します。 //メソッド //BuildGeometry // GlyphRunのジオメトリを取得します。 //ComputeAlignmentBox Rect // GlyphRunの配置ボックスを取得します。 //ComputeInkBoundingBox Rect // GlyphRunのインク境界ボックスを取得します。 //GetCaretCharacterHitFromDistance(Double, Boolean) CharacterHitグリフラン内でヒットした文字に関する情報を表します。 // GlyphRunのキャレットの文字ヒットを表すCharacterHit値を取得します。 //GetDistanceFromCaretCharacterHit(CharacterHit) double // GlyphRunの前縁から、指定された文字ヒットを含むキャレットストップの前縁または後縁までのオフセットを取得します。 //GetNextCaretCharacterHit(CharacterHit) CharacterHit // GlyphRunで論理方向にヒットした次の有効なキャレット文字を取得します。 //GetPreviousCaretCharacterHit(CharacterHit) CharacterHit // GlyphRunで論理方向にヒットした前の有効なキャレット文字を取得します。 }
/// <summary> /// Draw glyphrun /// </summary> /// <param name="drawingContext">The drawing context to draw into </param> /// <param name="foregroundBrush"> /// The foreground brush of the glyphrun. Pass in "null" to draw the /// glyph run with the foreground in TextRunProperties. /// </param> /// <param name="glyphRun">The GlyphRun to be drawn </param> /// <returns>bounding rectangle of drawn glyphrun</returns> /// <Remarks> /// TextEffect drawing code may use a different foreground brush for the text. /// </Remarks> internal Rect DrawGlyphRun( DrawingContext drawingContext, Brush foregroundBrush, GlyphRun glyphRun ) { Debug.Assert(_shapeable != null); Rect inkBoundingBox = glyphRun.ComputeInkBoundingBox(); if (!inkBoundingBox.IsEmpty) { // glyph run's ink bounding box is relative to its origin inkBoundingBox.X += glyphRun.BaselineOrigin.X; inkBoundingBox.Y += glyphRun.BaselineOrigin.Y; } if (drawingContext != null) { int pushCount = 0; // the number of push we do try { if (_textEffects != null) { // we need to push in the same order as they are set for (int i = 0; i < _textEffects.Count; i++) { // get the text effect by its index TextEffect textEffect = _textEffects[i]; if (textEffect.Transform != null && textEffect.Transform != Transform.Identity) { drawingContext.PushTransform(textEffect.Transform); pushCount++; } if (textEffect.Clip != null) { drawingContext.PushClip(textEffect.Clip); pushCount++; } if (textEffect.Foreground != null) { // remember the out-most non-null brush // this brush will be used to draw the glyph run foregroundBrush = textEffect.Foreground; } } } _shapeable.Draw(drawingContext, foregroundBrush, glyphRun); } finally { for (int i = 0; i < pushCount; i++) { drawingContext.Pop(); } } } return(inkBoundingBox); }
private void InitializeMetrics() { Contract.Requires(_typeface != null, "Typeface cannot be null."); Contract.Requires(_mapping != null, "Mapping cannot be null."); Contract.Requires(_transform != null, "Transform must be initialized first."); ushort glyph = _mapping.Glyph.GetValueOrDefault(); checked { _emHeight = (ushort)Round(_typeface.Height * _emSize); _emBaseline = (short)Round(_typeface.Baseline * _emSize); } // System.Diagnostics.Debug.WriteLine("{2}: {0} {1}", // _typeface.LeftSideBearings[glyph] * _emSize, // _typeface.RightSideBearings[glyph] * _emSize, (char)(int)_mapping.Character); if (_antialiasLevel == AntialiasingLevel.None) { _emSideBearing = new Int32Thickness( Round(_typeface.LeftSideBearings[glyph] * _emSize), Round(_typeface.TopSideBearings[glyph] * _emSize), Round(_typeface.RightSideBearings[glyph] * _emSize), Round(_typeface.BottomSideBearings[glyph] * _emSize) ); } else { _emSideBearing = new Int32Thickness( Floor(_typeface.LeftSideBearings[glyph] * _emSize), Floor(_typeface.TopSideBearings[glyph] * _emSize), Floor(_typeface.RightSideBearings[glyph] * _emSize), Floor(_typeface.BottomSideBearings[glyph] * _emSize) ); } Rect advance = new Rect(new Size(_typeface.AdvanceWidths[glyph], _typeface.AdvanceHeights[glyph])); Rect advanceTransformed = _transform.TransformBounds(advance); _emAdvance = new Int32Vector( Round(advanceTransformed.Width * _emSize), Round(advanceTransformed.Height * _emSize) ); if (!_transform.Value.IsIdentity) { Rect boundBox = _run.ComputeInkBoundingBox(); Rect advanceBox = boundBox.Add(_emSideBearing); Rect boundBoxTransformed = _transform.TransformBounds(boundBox); Rect advanceBoxTransformed = _transform.TransformBounds(advanceBox); Thickness bearingTransformed = boundBox.Subtract(advanceBox); _emSideBearing = new Int32Thickness( Round(bearingTransformed.Left), Round(bearingTransformed.Top), Round(bearingTransformed.Right), Round(bearingTransformed.Bottom) ); } }