Gambar teks dengan benar menggunakan Jalur Grafik

Oct 30 2018

Seperti yang Anda lihat pada gambar di bawah ini, teks pada kotak gambar berbeda dari yang ada di kotak teks. Ini berfungsi dengan baik jika saya menggunakan Graphics.DrawString()tetapi ketika saya menggunakan Jalur Grafik, itu terpotong dan tidak menampilkan keseluruhan teks. Menurut Anda, apa yang salah dalam kode saya?

Ini kode saya:

public LayerClass DrawString(LayerClass.Type _text, string text, RectangleF rect, Font _fontStyle, PaintEventArgs e)
{
    using (StringFormat string_format = new StringFormat())
    {
        rect.Size = e.Graphics.MeasureString(text, _fontStyle);
        rect.Location = new PointF(Shape.center.X - (rect.Width / 2), Shape.center.Y - (rect.Height / 2));
        if(isOutlinedOrSolid)
        {
            using (GraphicsPath path = new GraphicsPath())
            {
                path.AddString(text, _fontStyle.FontFamily, (int)_fontStyle.Style, rect.Size.Height, rect, string_format);
                e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
                e.Graphics.CompositingQuality = CompositingQuality.HighQuality;
                e.Graphics.CompositingMode = CompositingMode.SourceOver;
                e.Graphics.DrawPath(new Pen(Color.Red, 1), path);
            }
        }
        else
        {
            e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
            e.Graphics.CompositingQuality = CompositingQuality.HighQuality;
            e.Graphics.CompositingMode = CompositingMode.SourceOver;
            e.Graphics.DrawString(text,_fontStyle,Brushes.Red, rect.Location);
        }
    }

    this._Text = text;
    this._TextRect = rect;
    return new LayerClass(_text, this, text, rect);
}

Jawaban

5 roozbehS Oct 30 2018 at 16:45

Sepertinya Anda memberikan ukuran yang salah untuk ukuran font di tempat pertama dan kemudian menambahkan ketebalan ekstra ke kuas. Coba ini sebagai gantinya:

using (GraphicsPath path = new GraphicsPath())
{
    path.AddString(
        text,                         
        _fontStyle.FontFamily,      
        (int)_fontStyle.Style,      
        e.Graphics.DpiY * fontSize / 72f,       // em size
        new Point(0, 0),                       // location where to draw text
        string_format);          

    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
    e.Graphics.CompositingQuality = CompositingQuality.HighQuality;
    e.Graphics.CompositingMode = CompositingMode.SourceOver;
    e.Graphics.DrawPath(new Pen(Color.Red), path);
}
5 Jimi Oct 31 2018 at 07:23

Kelas GraphicsPath menghitung ukuran objek Teks dengan cara yang berbeda (seperti yang sudah dicatat di komentar). Ukuran Teks dihitung menggunakan Emsukuran persegi panjang pembatas.
An Emadalah ukuran tipografi yang independen dari konteks perangkat tujuan.
Ini mengacu pada persegi panjang yang ditempati oleh huruf terlebar dari sebuah font, biasanya huruf "M" (diucapkan em ).

EmUkuran tujuan dapat dihitung dengan 2 cara berbeda: yang satu menyertakan a Font descent, yang lain tidak menyertakannya.

float EMSize = (Font.SizeInPoints * [FontFamily].GetCellAscent([FontStyle]) 
                                  / [FontFamily].GetEmHeight([FontStyle])

atau

float EMSize = (Font.SizeInPoints * ([FontFamily].GetCellAscent([FontStyle] + 
                                     [FontFamily.GetCellDescent([FontStyle])) 
                                  / [FontFamily].GetEmHeight([FontStyle])

Lihat Dokumen tentang:
FontFamily.GetEmHeight , FontFamily.GetCellAscent dan FontFamily.GetCellDescent

Saya memasukkan di sini gambar yang dapat Anda temukan di Dokumen.

Lihat informasi umum yang terkandung di sini:
Menggunakan Font dan Teks (MSDN)

Dokumen ini melaporkan secara spesifik tentang cara menerjemahkan Poin, Piksel, dan Em:
Cara: Mendapatkan Metrik Font (MSDN)


Saya berasumsi Anda sudah memiliki objek kelas yang berisi / mereferensikan pengaturan Font yang berasal dari kontrol UI dan penyesuaian yang diperlukan.
Saya menambahkan satu di sini dengan beberapa properti yang berisi subset dari pengaturan tersebut, terkait dengan pertanyaan.

Kelas ini melakukan beberapa perhitungan berdasarkan ukuran Font yang dipilih oleh pengguna.
Ukuran Font biasanya direferensikan dalam Poin. Ukuran ini kemudian diubah dalam Piksel, menggunakan DPIresolusi layar saat ini (atau diubah dalam Poin dari dimensi Piksel). Setiap ukuran juga dikonversi Ems, yang berguna jika Anda harus menggunakan GraphicsPathuntuk menggambar Teks.

The Emsukuran dihitung mempertimbangkan baik Ascentdan Descentdari Font.
The GraphicsPathkelas bekerja lebih baik dengan ukuran ini, karena teks campuran dapat memiliki keduanya bagian dan jika tidak, bagian yang = 0.

Untuk menghitung kotak kontainer dari Teks yang digambar dengan Font dan ukuran Font tertentu, gunakan metode GraphicsPath.GetBounds () :
( [Canvas]adalah Kontrol yang menyediakan objek Paintacara e.Graphics)

using (var path = new GraphicsPath())
using (var format = new StringFormat(StringFormatFlags.NoClip | StringFormatFlags.NoWrap))
{
    format.Alignment = [StringAlignment.Center/Near/Far]; //As selected
    format.LineAlignment = [StringAlignment.Center/Near/Far]; //As selected
    //Add the Text to the GraphicsPath
    path.AddString(fontObject.Text, fontObject.FontFamily, 
                   (int)fontObject.FontStyle, fontObject.SizeInEms, 
                   [Canvas].ClientRectangle, format);
    //Ems size (bounding rectangle)
    RectangleF TextBounds = path.GetBounds(null, fontObject.Outline);
    //Location of the Text
    fontObject.Location = TextBounds.Location;
}

Gambarkan Teks pada [Canvas]konteks Perangkat:

private void Canvas_Paint(object sender, PaintEventArgs e)
{
    using (var path = new GraphicsPath())
    using (var format = new StringFormat(StringFormatFlags.NoClip | StringFormatFlags.NoWrap))
    {
        format.Alignment = [StringAlignment.Center/Near/Far]; //As selected
        format.LineAlignment = [StringAlignment.Center/Near/Far]; //As selected

        path.AddString(fontObject.Text, fontObject.FontFamily, (int)fontObject.FontStyle, fontObject.SizeInEms, Canvas.ClientRectangle, format);

        e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
        e.Graphics.TextRenderingHint = TextRenderingHint.ClearTypeGridFit;
        // The composition properties are useful when drawing on a composited surface
        // when we simply draw on a Control's surface, these are useless
        e.Graphics.CompositingMode = CompositingMode.SourceOver;
        e.Graphics.CompositingQuality = CompositingQuality.HighQuality;

        if (fontObject.Outlined) { 
            e.Graphics.DrawPath(fontObject.Outline, path);
        }
        using(var brush = new SolidBrush(fontObject.FillColor)) {
            e.Graphics.FillPath(brush, path);
        }
    }
}

Efek visual menggunakan kelas ini dan metode yang sebenarnya:

Objek kelas, untuk digunakan sebagai referensi:

public class FontObject
{
    private float currentScreenDPI = 0.0F;
    private float m_SizeInPoints = 0.0F;
    private float m_SizeInPixels = 0.0F;
    public FontObject() 
        : this(string.Empty, FontFamily.GenericSansSerif, FontStyle.Regular, 18F) { }
    public FontObject(string text, Font font) 
        : this(text, font.FontFamily, font.Style, font.SizeInPoints) { }
    public FontObject(string text, FontFamily fontFamily, FontStyle fontStyle, float FontSize)
    {
        if (FontSize < 3) FontSize = 3;
        using (Graphics g = Graphics.FromHwndInternal(IntPtr.Zero)) {
            this.currentScreenDPI = g.DpiY; 
        }
        this.Text = text;
        this.FontFamily = fontFamily;
        this.SizeInPoints = FontSize;
        this.FillColor = Color.Black;
        this.Outline = new Pen(Color.Black, 1);
        this.Outlined = false;
    }

    public string Text { get; set; }
    public FontStyle FontStyle { get; set; }
    public FontFamily FontFamily { get; set; }
    public Color FillColor { get; set; }
    public Pen Outline { get; set; }
    public bool Outlined { get; set; }
    public float SizeInPoints {
        get => this.m_SizeInPoints;
        set {  this.m_SizeInPoints = value;
               this.m_SizeInPixels = (value * 72F) / this.currentScreenDPI;
               this.SizeInEms = GetEmSize();
        }
    }
    public float SizeInPixels {
        get => this.m_SizeInPixels;
        set {  this.m_SizeInPixels = value;
               this.m_SizeInPoints = (value * this.currentScreenDPI) / 72F;
               this.SizeInEms = GetEmSize();
        }
    }

    public float SizeInEms { get; private set; }
    public PointF Location { get; set; }
    public RectangleF DrawingBox { get; set; }

    private float GetEmSize()
    {
        return (this.m_SizeInPoints * 
               (this.FontFamily.GetCellAscent(this.FontStyle) +
                this.FontFamily.GetCellDescent(this.FontStyle))) /
                this.FontFamily.GetEmHeight(this.FontStyle);
    }
}

Edit :

ComboBox dengan keluarga font.

Buat ComboBox kustom dan atur DrawMode = OwnerDrawVariable:

string[] FontList = FontFamily.Families.Where(f => f.IsStyleAvailable(FontStyle.Regular)).Select(f => f.Name).ToArray();

cboFontFamily.DrawMode = DrawMode.OwnerDrawVariable;
cboFontFamily.AutoCompleteMode = AutoCompleteMode.SuggestAppend;
cboFontFamily.AutoCompleteSource = AutoCompleteSource.CustomSource;
cboFontFamily.AutoCompleteCustomSource.AddRange(FontList);
cboFontFamily.DisplayMember = "Name";
cboFontFamily.Items.AddRange(FontList);
cboFontFamily.Text = "Arial";

Penangan acara:

private void cboFontFamily_DrawItem(object sender, DrawItemEventArgs e)
{
    if ((Items.Count == 0) || e.Index < 0) return;
    e.DrawBackground();
   
    var flags = TextFormatFlags.Left | TextFormatFlags.VerticalCenter;
    using (var family = new FontFamily(cboFontFamily.GetItemText(cboFontFamily.Items[e.Index])))
    using (var font = new Font(family, 10F, FontStyle.Regular, GraphicsUnit.Point)) {
        TextRenderer.DrawText(e.Graphics, family.Name, font, e.Bounds, cboFontFamily.ForeColor, flags);
    }
    e.DrawFocusRectangle();
}

private void cboFontFamily_MeasureItem(object sender, MeasureItemEventArgs e)
{
    e.ItemHeight = (int)this.Font.Height + 4;
}

private void cboFontFamily_SelectionChangeCommitted(object sender, EventArgs e)
{
    fontObject.FontFamily = new FontFamily(cboFontFamily.GetItemText(cboFontFamily.SelectedItem));
    Canvas.Invalidate();
}