調査続編(改善版)はこちら → 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 から抜粋:
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;
システムのデフォルトフォント
それっぽい(※ドキュメントがよく分からないため根拠がない)
ビルマ文字(なぜか、はみ出る)
Font.GetHeight
がやたら大きい値になったので、縦長の文字を探して色々やってたらビルマ文字1にたどり着いた。なぜかはみ出る。
ソースコード
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
とかを使って、描画する文字列に応じて調整するほうがよいかもしれない。
参考サイト
- フォントの作り方(13)〜メトリクスの話〜
- 指定したGraphicsオブジェクトで使用できるフォントを取得する - dobon.net
- フォント メトリックを取得する - Microsoft Docs
そのた
調べてる最中にSystem.Windows.Media
のほうのFontFamily
に迷い込んだりしたが別モノと思われる。