none
LOGFONT構造体の横幅を取得する方法 RRS feed

  • 質問

  • いつも大変お世話になっています。

    開発環境:Win7 x86 Visual Studio 2013

    文字列をFormに描画する際、文字の横幅を通常のx%に変更したいと考えています。

    条件として、C++でTextOut()を使った描画と結果を同じにしたいのですが、

    下記①のように、いったん通常サイズで描画した後、そのイメージをストレッチする方法では、

    若干違い生じますし、斜体では、最初のMeasureTextで、文字列幅が必要な長さより短くなり、

    後ろが切れてしまいます。

    ①ストレッチ

    // 対象文字サイズ取得
    nopadSize = TextRenderer.MeasureText(g, charEnum.Current.ToString(), font
            , new Size(GXSIZE, GXSIZE), TextFormatFlags.NoPadding | TextFormatFlags.NoPrefix);
    
    // 対象文字を描画
    Bitmap imgChar = new Bitmap(nopadSize.Width, nopadSize.Height);
    Graphics cg = Graphics.FromImage(imgChar);
    TextRenderer.DrawText(cg, charEnum.Current.ToString(), font, new Point(0, 0), color
                        , TextFormatFlags.NoPadding | TextFormatFlags.NoPrefix);
    
    // 文字列描画領域に横倍分ストレッチして描画
    int stretchWidth = nopadSize.Width * wbai / 100;
    sg.DrawImage(imgChar, new Rectangle(posX, 0, stretchWidth, nopadSize.Height)
                        , new Rectangle(0, 0, nopadSize.Width, nopadSize.Height)
                        , GraphicsUnit.Pixel);
    

    そこで、C++(下記②//*****部分)と同じ方法でフォントを作成しようと考えたのですが(下記③)、
    VisualStyleRenderer.GetTextMetrics()の使い方がわかりません。

    ご指導、どうぞよろしくお願いいたします。

    ②c++ //*****部分

    void LogFontSet(HDC hdc,PLOGFONT plf, PTCHAR facename, WORD style, WORD fsize, WORD wbai, char hou)
    {
    	long	l;
    	int		Height,Width;
    
    	_tcscpy_s(plf->lfFaceName,facename);
    
    	SetMapMode(hdc,MM_TEXT);
    	FONTENUMPROC FontEnumProc=(FONTENUMPROC)EnumFontFamProc;
    	EnumFontFamilies(hdc,plf->lfFaceName,FontEnumProc,(LPARAM)plf);
    
    	Height=MulDiv(fsize,96,72);
    	plf->lfHeight=-Height;
    
    	if (style>=2)	plf->lfWeight=FW_BOLD;
    	else			plf->lfWeight=FW_NORMAL;
    	if (style&1)	plf->lfItalic=1;
    	if (hou>=0)
    	{
    		plf->lfEscapement=plf->lfOrientation=(int)hou*900;
    	}
    	plf->lfQuality=NONANTIALIASED_QUALITY;
    
    	HFONT		hFont;
    	TEXTMETRIC  tm;
    
    
    //********** GetTextMetricsで文字幅を取得し、x%倍(wbai)して、LOGFONT構造体に戻す
    	hFont=(HFONT)SelectObject(hdc,CreateFontIndirect(plf));
    	GetTextMetrics(hdc,&tm);
    	DeleteObject(SelectObject(hdc,hFont));
    
    	l=(long)tm.tmAveCharWidth*(long)wbai/100L;
    	Width=(int)l;
    	plf->lfWidth=-Width;
    }
    

    ③C# //********部分にどうコーディングすればよいか?

    Font CreateFont(string fontname, int angleInDegrees)
    {
        //            LogFont logf = new Microsoft.WindowsCE.Forms.LogFont();
        LOGFONT logf = new LOGFONT();
    
        // Create graphics object for the form, and obtain
        // the current DPI value at design time. In this case,
        // only the vertical resolution is petinent, so the DpiY
        // property is used. 
    
        Graphics g = this.CreateGraphics();
        // Scale an 18-point font for current screen vertical DPI.
        logf.lfHeight = (int)(-50f * g.DpiY / curDPI);
    
        // Convert specified rotation angle to tenths of degrees.  
        logf.lfEscapement = angleInDegrees * 10;
    
        // Orientation is the same as Escapement in mobile platforms.
        logf.lfOrientation = logf.lfEscapement;
    
        logf.lfFaceName = fontname;
    
        // Set LogFont enumerations.
        logf.lfCharSet = define.DEFAULT_CHARSET;
        logf.lfOutPrecision = define.OUT_DEFAULT_PRECIS;
        logf.lfClipPrecision = define.CLIP_DEFAULT_PRECIS;
        logf.lfQuality = define.CLEARTYPE_QUALITY;
        logf.lfPitchAndFamily = define.DEFAULT_PITCH;
    
    //***************** ここで"lfWidth"を変更したい
    
        // Explicitly dispose any drawing objects created.
        g.Dispose();
    
        return Font.FromLogFont(logf);
    }
    



    kizakura_ui

    2015年11月13日 4:32

回答

  • コンパイルを通るだけは書きましたが動作は保証しなかったので。

    GDI+におけるFontオブジェクトは、LOGFONT::lfWidthは無視されるみたいですね。

    Fontオブジェクトを使うのは諦めて、(Ext)TextOutで出力してください。

    • 回答としてマーク kizakura_ui 2015年11月13日 11:35
    2015年11月13日 10:11

すべての返信

  • VisualStyleRenderer自体は、VisualStyleElementクラス以下にツリー状に山ほど定義されている適切なVisualStyleElementを与えればnewできます。

    // この辺の仕組みは詳しくないので、どのVisualStyleElementを与えるのが妥当かは知りません。

    VisualStyleRenderer::GetTextMetricsは引数にIDeviceContextを要求し、Graphics型はこのインターフェイスを実装しているため直接渡すことが可能です。

    しかし、Windows APIのHDCとは異なり、Graphics自体はフォント情報を持たず、描画の度にFontオブジェクトを受け取って処理するために、そのままではGetTextMetricsにこちら側が意図するフォント情報を与えることができません。

    そうするには、Graphics::GetHdc()でHDCを取ってきて、SelectObject関数でフォントを選択する必要があります。選択後はGraphics::ReleaseHdc()で一旦解放してからGetTextMetricsにGraphicsを与えればいいです。

    2015年11月13日 5:30
  • ご指導、ありがとうございます。

    ご指摘いただいたことがよく理解できなかったのですが、

    とりあえず、下記の”//******************** 追加部分”を追加したところ、

    textMetric.tmAveCharWidthを取得することができました。

    ただ、logf.lfWidthに取得値の3倍の値をセットしても、横長な文字列にはなりませんでした。

    また、追加部分の処理をコメントにして、

    logf.lfWidth = logf.lfHeight;

    もやってみましたが、反映されませんでした。

    お忙しいところ、大変申し訳ありませんが、具体的なコーディング方法をお教えいただけないでしょうか?

    よろしくお願いいたします。

     

    [DllImport("Gdi32.dll", EntryPoint = "CreateFontIndirect")]
    static extern IntPtr CreateFontIndirect(LOGFONT logf);
    
    [DllImport("Gdi32.dll", CharSet = CharSet.Auto)]
    static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
    
    [DllImport("Gdi32.dll", CharSet = CharSet.Auto)]
    static extern bool GetTextMetrics(IntPtr hdc, out TEXTMETRIC lptm);
    
    [DllImport("Gdi32.dll", CharSet = CharSet.Auto)]
    static extern bool DeleteObject(IntPtr hgdiobj);
    
    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
    internal struct TEXTMETRIC
    {
        public int tmHeight;
        public int tmAscent;
        public int tmDescent;
        public int tmInternalLeading;
        public int tmExternalLeading;
        public int tmAveCharWidth;
        public int tmMaxCharWidth;
        public int tmWeight;
        public int tmOverhang;
        public int tmDigitizedAspectX;
        public int tmDigitizedAspectY;
        public char tmFirstChar;
        public char tmLastChar;
        public char tmDefaultChar;
        public char tmBreakChar;
        public byte tmItalic;
        public byte tmUnderlined;
        public byte tmStruckOut;
        public byte tmPitchAndFamily;
        public byte tmCharSet;
    }
    
    // Method to create a rotated font using a LOGFONT structure.
    Font CreateRotatedFont(string fontname, int angleInDegrees)
    {
        //            LogFont logf = new Microsoft.WindowsCE.Forms.LogFont();
        LOGFONT logf = new LOGFONT();
    
        // Create graphics object for the form, and obtain
        // the current DPI value at design time. In this case,
        // only the vertical resolution is petinent, so the DpiY
        // property is used. 
    
        Graphics g = this.CreateGraphics();
        // Scale an 18-point font for current screen vertical DPI.
        logf.lfHeight = (int)(-50f * g.DpiY / curDPI);
    
        // Convert specified rotation angle to tenths of degrees.  
        logf.lfEscapement = angleInDegrees * 10;
    
        // Orientation is the same as Escapement in mobile platforms.
        logf.lfOrientation = logf.lfEscapement;
    
        logf.lfFaceName = fontname;
    
        // Set LogFont enumerations.
        logf.lfCharSet = define.DEFAULT_CHARSET;
        logf.lfOutPrecision = define.OUT_DEFAULT_PRECIS;
        logf.lfClipPrecision = define.CLIP_DEFAULT_PRECIS;
        logf.lfQuality = define.CLEARTYPE_QUALITY;
        logf.lfPitchAndFamily = define.DEFAULT_PITCH;
        logf.lfItalic = 1;
    
    
    //******************** 追加部分
        TEXTMETRIC textMetric; 
        IntPtr hFont = CreateFontIndirect(logf);
        try
        {
            IntPtr hDC = g.GetHdc();
            SelectObject(hDC, hFont);
            bool result = GetTextMetrics(hDC, out textMetric);
        }
        finally
        {
            DeleteObject(hFont);
            g.ReleaseHdc();
        }
    
        int Width = textMetric.tmAveCharWidth * 300 / 100;
        logf.lfWidth = -Width;
    //******************** 追加部分
    
    
        // Explicitly dispose any drawing objects created.
        g.Dispose();
    
        return Font.FromLogFont(logf);
    }
    


    kizakura_ui

    2015年11月13日 8:32
  • コンパイルを通るだけは書きましたが動作は保証しなかったので。

    GDI+におけるFontオブジェクトは、LOGFONT::lfWidthは無視されるみたいですね。

    Fontオブジェクトを使うのは諦めて、(Ext)TextOutで出力してください。

    • 回答としてマーク kizakura_ui 2015年11月13日 11:35
    2015年11月13日 10:11
  • お時間とってくださりありがとうございました。

    >Fontオブジェクトを使うのは諦めて、(Ext)TextOutで出力してください。

    それでやってみます。

    今後ともご指導よろしくお願いいたします。


    kizakura_ui

    2015年11月13日 11:36