2012-02-01 65 views
10

我需要打印數字,其中中間的一些數字通過增加字體大小和重量來強調。在下面的例子中,456被強調。GDI +在基線上繪製不同尺寸的文字有1px的問題

enter image description here

的字體和所使用的兩種尺寸是用戶可配置的。

當前代碼使用三次調用Graphics.DrawString(...)來完成此操作。

我遇到的問題是,與大多數的字體,我看到的off-by-1像素的問題(相對於灰線,該456坐在一個額外的像素高於其他數字):

enter image description here

我附加了一些調試轉儲(鮑勃鮑威爾公式)爲我的文章底部的各種字體。其他技術產生了類似的結果。

爲了在通用基線上打印文本,需要計算特定Font的基線偏移量。

首先,MSDN代碼:http://msdn.microsoft.com/en-us/library/xwf9s90b(v=vs.80).aspx

ascent = fontFamily.GetCellAscent(FontStyle.Regular); 
ascentPixel = font.Size * ascent/fontFamily.GetEmHeight(FontStyle.Regular) 

其次,從代碼:Using GDI+, what's the easiest approach to align text (drawn in several different fonts) along a common baseline?

最後,從鮑勃·鮑威爾郵編:http://www.bobpowell.net/formattingtext.htm

這裏是我的,我用三種技術嘗試繪製方法:

private void DrawOnBaseline(Graphics g, string text, FontWithBaseline fwb, Brush brush, float x, float y) { 
    g.DrawString(text, fwb.Font, brush, x, y - fwb.Baseline, StringFormat.GenericTypographic); 
} 

FontWithBaseline一個簡單的字體具有其各自的基準計算相關聯:

public class FontWithBaseline { 
    private Font m_font; 
    private float m_baseline; 

    public FontWithBaseline(Font font) { 
    m_font = font; 
    m_baseline = CalculateBaseline(font); 
    } 

    public Font Font { get { return m_font; } } 
    public float Baseline { get { return m_baseline; } } 

    private static float CalculateBaseline(Font font) { 
    ... // I've tried the three formulae here. 
    } 
} 

我還沒有嘗試Graphics.TestRenderingHint還。那是不可思議的醬汁? 我錯過了什麼?是否有我可以使用的替代API,在那裏我打電話來向耗材提供基線的Y座標?

enter image description here


更新1

我插我的代碼以@LarsTech。他正在做一個微妙的不同;他正在添加0.5f。但是,即使這個變體也不能解決這個問題。下面的代碼:

protected override void OnPaint(PaintEventArgs e) { 
    base.OnPaint(e); 
    TryLarsTechnique(e); 
} 

private void TryLarsTechnique(PaintEventArgs e) { 
    base.OnPaint(e); 
    Graphics g = e.Graphics; 
    GraphicsContainer c = g.BeginContainer(); 
    g.Clear(Color.White); 
    g.SmoothingMode = SmoothingMode.AntiAlias; 
    g.TextRenderingHint = TextRenderingHint.AntiAlias; 
    Font small = new Font("Arial", 13, FontStyle.Regular, GraphicsUnit.Pixel); 
    Font large = new Font("Arial", 17, FontStyle.Bold, GraphicsUnit.Pixel); 

    int x = 100; 
    int y = 100; 
    x += DrawLars(g, "12.3", small, x, y); 
    x += DrawLars(g, "456", large, x, y); 
    x += DrawLars(g, "8", small, x, y); 
    g.EndContainer(c); 
} 

// returns width of text 
private int DrawLars(Graphics g, string text, Font font, int x, int y) { 
    float offset = font.SizeInPoints/
       font.FontFamily.GetEmHeight(font.Style) * 
       font.FontFamily.GetCellAscent(font.Style); 
    float pixels = g.DpiY/72f * offset; 
    int numTop = y - (int)(pixels + 0.5f);  
    TextRenderer.DrawText(g, text, font, new Point(x, numTop), Color.Black, Color.Empty, TextFormatFlags.NoPadding); 
    return TextRenderer.MeasureText(g, text, font, Size.Empty, TextFormatFlags.NoPadding).Width; 
} 

我想知道,是否指定使用GraphicsUnit.Pixel字體大小是罪魁禍首。也許有一種方法可以找到任何特定字體的首選大小?


更新2

我試過在點而不是像素指定的字體大小,而這並不能完全解決問題,無論是。請注意,在我的情況下,僅使用整點大小不是一種選擇。爲了查看這是否可能,我在Windows寫字板上試了這個。尺寸使用96 dpi(根據定義每英寸72點),17px,13px翻譯爲12.75和9.75。下面是輸出比較:

enter image description here

通知的較小的字體是如何在像素級相同的高度。所以寫字板以某種方式設法得到這個正確的數字,而不需要將字體大小舍入爲方便的值。

回答

5

您還沒有表現出足夠的代碼來重現問題,所以這裏是使用你給鮑勃鮑威爾例如工作的例子。

只是演示代碼:

private void panel1_Paint(object sender, PaintEventArgs e) { 
    e.Graphics.Clear(Color.White); 
    e.Graphics.SmoothingMode = SmoothingMode.AntiAlias; 
    e.Graphics.TextRenderingHint = System.Drawing.Text.TextRenderingHint.AntiAlias; 

    string[] numbers = new string[] { "1", "2", ".", "3", "4", "5", "6", "8" }; 

    int x = 10; 
    int y = 30; 

    foreach (string num in numbers) { 
    Font testFont; 
    if (num == "4" || num == "5" || num == "6") 
     testFont = new Font("Arial", 16, FontStyle.Bold); 
    else 
     testFont = new Font("Arial", 11, FontStyle.Regular); 

    float offset = testFont.SizeInPoints/
        testFont.FontFamily.GetEmHeight(testFont.Style) * 
        testFont.FontFamily.GetCellAscent(testFont.Style); 
    float pixels = e.Graphics.DpiY/72f * offset; 

    int numTop = y - (int)(pixels + 0.5f); 

    TextRenderer.DrawText(e.Graphics, num, testFont, new Point(x, numTop), 
          Color.Black, Color.Empty, TextFormatFlags.NoPadding); 

    x += TextRenderer.MeasureText(e.Graphics, num, testFont, 
            Size.Empty, TextFormatFlags.NoPadding).Width; 
    } 

    e.Graphics.DrawLine(Pens.Red, new Point(5, y + 1), new Point(x + 5, y + 1)); 
} 

這將產生:

enter image description here

+0

感謝。我已更新我的問題以添加我的代碼併合並您的方法。 '0.5f'的加入是新東西。唉,我仍然看到這個問題。嘗試使用17像素+ 13像素的字體大小;我懷疑你也會看到這個問題。 – 2012-02-01 19:14:01

+0

@DilumRanatunga的'0.5f'從鮑勃·鮑威爾的例子就是直(請參閱鏈接頁面的底部)。是的,使用'GraphicsUnit.Pixel'可以使它像素化。不知道爲什麼,但離開它再次拉回來。我相信'GraphicsUnit.Point'是默認的,當使用它時,它也起作用。 – LarsTech 2012-02-01 19:29:05

+0

@DilumRanatunga如果我改變代碼GraphicsUnit.Pixel然後用'12.75f'和'9.75'作爲我的字體大小,它線條正確。我不認爲* WorkPad正在進行四捨五入,我認爲它只是爲了方便而向用戶顯示一個整數。我可能錯了。 Visual Studio設計顯示'默認'Microsoft無Serif'字體8.25pt',但如果你點擊對話框更改字體,你只看到整數在下拉。不知道這是否有助於你。 – LarsTech 2012-02-01 21:06:01

2

也許問題是,你指定你的字體大小pixels而你抵消points計算和轉換回在pixels。這可能會引入各種不精確。

嘗試在points指定的字體大小,看它是否工作。

+0

我同意四捨五入的問題很可能是潛在的原因。但是我需要在程序層面解決這個問題。看到我的第二個更新。 Windows寫字板設法使這一切工作... – 2012-02-01 20:09:43

+0

當然,你嘗試設置點的字體大小?你的結果是什麼?我在問,因爲這可能是發生了什麼事情的線索。 – Coincoin 2012-02-02 16:58:27

+0

我曾嘗試使用Points。對於某些字體大小,它可以工作。問題是我無法將我的解決方案限制爲這些字體大小。我的控件需要支持用戶指定的大小。當數字包含太多數字時,還需要按比例縮小尺寸。如果有一個程序化的方法來找到「好」的尺寸,並且有很多這樣的尺寸,我可以採用這種方法。 – 2012-02-02 17:30:23

1

Graphics.TextRenderingHint設置爲使用網格擬合時,縮放的TrueType字體的實際度量(以像素爲單位)由GDI +通過搜索字體VDMX table確定,而不是簡單地縮放和舍入其設計度量。這就是我在反彙編時通過Graphics.DrawString的步調。