0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

C# - フォントメトリクスを調査してみたらカオスだった件 - 未解決

Last updated at Posted at 2019-11-02

調査続編(改善版)はこちら → https://qiita.com/kob58im/items/414554ea6f514b11af3a


混乱した結果、記事も空中分解気味になってしまったが、とりあえず投稿してみる。

経緯

下記みたいな感じでStringFormatでセンタリング指定してDrawStringしても縦位置がずれるので、調べてみた。


var sf = new StringFormat();
sf.Alignment = StringAlignment.Center; // 横中央
sf.LineAlignment = StringAlignment.Center; // 縦中央 ・・・ にならない(?)

g.DrawString(text, font, Brushes.Black, new Point(Width/2,Height/2), sf);

gとかqとかの下にはみ出る文字の影響と推測。
参考サイト フォント メトリックを取得する - Microsoft Docs から抜粋:

image.png

Descentの分だけ上に寄っているとすれば、Descentの半分だけY座標を足して描画すれば大体中央に来そう。

謎#1

上記の参考サイトはSizeを使っているが、Heightを使わないと何故か位置が合わない。(?)

descentPixel = font.Size * descent / fontFamily.GetEmHeight(FontStyle.Regular);

いろんなフォントを描画してみる

AscentとHeightの位置に横線を描画してみる。
コード抜粋


    FontFamily ff = font.FontFamily;
    int ascent = ff.GetCellAscent(font.Style);
    int emHeight = ff.GetEmHeight(font.Style);

    // 下記の各Y座標(pixel単位)で横線を引いてみた
    float fontHeight = font.GetHeight(g); // g: 描画に使用するGraphicsクラス
    float ascentHeight  = fontHeight * ascent  / emHeight;

システムのデフォルトフォント

それっぽい(※ドキュメントがよく分からないため根拠がない)

image.png

ビルマ文字(なぜか、はみ出る)

Font.GetHeightがやたら大きい値になったので、縦長の文字を探して色々やってたらビルマ文字1にたどり着いた。なぜかはみ出る。

image.png

ソースコード

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Windows.Forms;

class FontMetricsTest : Form
{
    TrackBar   trackbar;
    PictureBox pct;
    ComboBox   cmbFonts;
    TextBox    txtContent;

    FontMetricsTest()
    {
        Text = "Font metrics test";
        ClientSize = new Size(600, 400);

        cmbFonts = new ComboBox();
        foreach (FontFamily ff in FontFamily.Families) {
            cmbFonts.Items.Add(ff.Name);
        }
        cmbFonts.Text = SystemFonts.DefaultFont.Name;
        cmbFonts.Location = new Point(0,0);
        cmbFonts.Width = 300;
        cmbFonts.DropDownHeight = 500;
        cmbFonts.DropDownStyle = ComboBoxStyle.DropDownList;
        cmbFonts.SelectedIndexChanged += (sender,e)=>{MyRedraw();};
        Controls.Add(cmbFonts);

        trackbar = new TrackBar();
        trackbar.Location = new Point(300,0);
        trackbar.Maximum = 100;
        trackbar.Value   = 50;
        trackbar.Minimum = 1;
        trackbar.TickFrequency = 33;
        trackbar.ValueChanged += (sender,e)=>{MyRedraw();};
        Controls.Add(trackbar);

        txtContent = new TextBox();
        txtContent.Location = new Point(0, 50);
        txtContent.Width = 300;
        txtContent.Text = "g あいう Qiita";
        txtContent.TextChanged += (sender,e)=>{MyRedraw();};
        Controls.Add(txtContent);

        pct = new PictureBox();
        pct.Location = new Point(0, 80);
        pct.Size = new Size(600, 300);
        pct.Image = new Bitmap(600,300);
        Controls.Add(pct);

        MyRedraw();
    }

    void MyRedraw()
    {
        int w = pct.Image.Width;
        int h = pct.Image.Height;

        string fontName = cmbFonts.Text;
        Font font = new Font(fontName, (float)trackbar.Value);
        
        string text = txtContent.Text;
        if (font.Name != fontName) {
            text = "Font unmatch: \"" + font.Name + "\" is loaded.";
        }

        using ( Graphics g = Graphics.FromImage(pct.Image) ) {
            g.Clear(Color.Black);
            
            FontFamily ff = font.FontFamily;

            int ascent = ff.GetCellAscent(font.Style);
            int descent = ff.GetCellDescent(font.Style);
            int emHeight = ff.GetEmHeight(font.Style);
            
            float fontHeight = font.GetHeight(g);
            float ascentHeight  = fontHeight * ascent  / emHeight;
//          float ascentSize    = font.Size  * ascent  / emHeight;

            Console.Write("==== "); Console.Write(font.Name); Console.WriteLine(" ====");
            Console.Write("font.Size: ");  Console.WriteLine(font.Size);
            Console.Write("fontHeight: "); Console.WriteLine(fontHeight);
            Console.Write("ascent: ");     Console.WriteLine(ascent);
            Console.Write("descent: ");    Console.WriteLine(descent);
            Console.Write("sum: ");        Console.WriteLine(ascent+descent);
            Console.Write("emHeight: ");   Console.WriteLine(emHeight);

            Pen pen = new Pen(Color.LightGray, 1.0f);
//          Pen penDash = new Pen(Color.LightGray, 1.0f);
//          penDash.DashStyle = DashStyle.Dash;
            g.DrawLine(pen,     0, fontHeight, w, fontHeight);
            g.DrawLine(pen,     0, ascentHeight, w, ascentHeight);
//          g.DrawLine(penDash, 0, font.Size, w, font.Size);
//          g.DrawLine(penDash, 0, ascentSize, w, ascentSize);

            g.DrawString(text, font, Brushes.White, new PointF(0,0));
        }

        pct.Refresh();
    }
    
    static void DumpSystemFontNames()
    {
        var dict = new Dictionary<string,Font>() {
            {"CaptionFont      " , SystemFonts.CaptionFont     },
            {"DefaultFont      " , SystemFonts.DefaultFont     },
            {"DialogFont       " , SystemFonts.DialogFont      },
            {"IconTitleFont    " , SystemFonts.IconTitleFont   },
            {"MenuFont         " , SystemFonts.MenuFont        },
            {"MessageBoxFont   " , SystemFonts.MessageBoxFont  },
            {"SmallCaptionFont " , SystemFonts.SmallCaptionFont},
            {"StatusFont       " , SystemFonts.StatusFont      }
        };

        foreach (var pair in dict) {
            Console.Write(pair.Key);
            Console.Write(",");
            Console.Write(pair.Value.Size);
            Console.Write(",");
            Console.WriteLine(pair.Value.Name);
        }
    }

    [STAThread]
    static void Main(string[] args)
    {
        // DumpSystemFontNames();
        Application.Run(new FontMetricsTest());
    }
}

結論

色々やってみたが、結局わからん・・・
実験してみるとFont.Size≠描画の高さだし2
Font.Height=行間、とドキュメントからは読み取ったけど、はみ出るケースもあるし3、、

特定の文字列を中央表示したいなら、Graphics.MeasureStringとかを使って、描画する文字列に応じて調整するほうがよいかもしれない。

参考サイト

そのた

調べてる最中にSystem.Windows.MediaのほうのFontFamilyに迷い込んだりしたが別モノと思われる。

  1. 文字はwikipediaからのコピペ。

  2. 【追記】ポイントとピクセルの単位系が違うため。こちらが参考になりそう。

  3. 【追記】コメント頂いている内容の通り、Fontが代用されているため。

0
0
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?