2012-02-01 11 views
10

Potrzebuję wydrukować cyfry, w których niektóre cyfry w środku są podkreślane przez zwiększenie rozmiaru i wagi czcionki. W poniższym przykładzie podkreślono 456.GDI + rysowanie tekstu o różnych rozmiarach na linii bazowej ma problemy typu off-by-1px.

enter image description here

Czcionka i dwa rozmiary stosowane są konfigurowane przez użytkownika.

Obecny kod wykonuje to za pomocą trzech połączeń z numerem Graphics.DrawString(...).

Problem mam jest to, że w większości czcionek, widzę off-by-1pixel problemy (w stosunku do szarej linii, 456 siedzi dodatkowy piksel wyższa niż pozostałych cyfr):

enter image description here

Dołączyłem kilka zrzutów debugowania (z formuły Boba Powella) dla różnych czcionek na dole mojego postu. Inne techniki dały podobne wyniki.

Aby wydrukować tekst na wspólnej linii bazowej, należy obliczyć przesunięcie linii bazowej dla określonego Font. Próbowałem przy użyciu trzech technik:

pierwsze MSDN Kod: 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) 

Po drugie, kod od: Using GDI+, what's the easiest approach to align text (drawn in several different fonts) along a common baseline?

Wreszcie kod z postu Boba Powella: http://www.bobpowell.net/formattingtext.htm

Oto mój metoda rysowania:

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); 
} 

Gdzie FontWithBaseline prostu kojarzy czcionki z odpowiedniej kalkulacji bazowym:

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. 
    } 
} 

nie eksperymentowali z Graphics.TestRenderingHint jeszcze. Czy to magiczny sos? Czego mi brakuje? Czy istnieje alternatywny interfejs API, z którego mogę skorzystać, gdy nazywam wywołanie, aby pobrać materiały wyjściowe o współrzędnej Y linii bazowej?

enter image description here


Aktualizacja 1

I interpolowane mój kod z @LarsTech. Robił jedno subtelnie inaczej; dodawał 0.5f. Jednak nawet ten wariant nie rozwiązuje problemu. Oto kod:

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; 
} 

Zastanawiam się, czy określenia rozmiaru czcionki korzystając GraphicsUnit.Pixel jest winowajcą. Być może istnieje sposób na znalezienie preferowanych rozmiarów dla jakiejś konkretnej czcionki?


Aktualizacja 2

Próbowałem określić rozmiary czcionek w punktach zamiast pikseli, i to nie w pełni rozwiązać ten problem, albo. Zauważ, że używanie tylko całych rozmiarów punktów nie jest w moim przypadku opcją. Aby sprawdzić, czy jest to możliwe, próbowałem tego na Windows Wordpad. Rozmiary Używając 96 dpi (i 72 punktów na cal z definicji), 17px, 13px przetłumaczyć na 12,75 i 9,75. Oto wynik w porównaniu:

enter image description here

Wskazówki jak mniejsze czcionki są tej samej wysokości na poziomie pikseli. Tak więc Wordpad w jakiś sposób udaje się to zrobić bez zaokrąglania rozmiarów czcionek do wygodnych wartości.

Odpowiedz

5

Nie pokazałeś wystarczającej ilości kodu, aby odtworzyć problem, więc oto przykład działający na przykładzie tego, który podałeś Bob Powell. tylko

kod Demonstracja:

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)); 
} 

ta produkuje:

enter image description here

+0

Dzięki za to. Zaktualizowałem moje pytanie, aby dodać mój kod i uwzględnić Twoje podejście. Dodanie '0,5f' było czymś nowym. Niestety, nadal widzę problem. Spróbuj użyć rozmiarów czcionki 17 pikseli + 13 pikseli; Podejrzewam, że problem też będzie widoczny. –

+0

@DilumRanatunga '0.5f' pochodzi prosto z przykładu Boba Powella (patrz dół tej połączonej strony). Tak, użycie 'GraphicsUnit.Pixel' powoduje przesunięcie go o piksel. Nie wiem, dlaczego, ale zostawienie go znowu przyciągnęło ich do kolejki. Wierzę, że 'GraphicsUnit.Point' jest domyślną wersją, a przy jej używaniu też zadziałało. – LarsTech

+0

@DilumRanatunga Jeśli zmienię kod na GraphicsUnit.Pixel, a następnie użyję "12.75f" i "9.75" jako rozmiaru czcionki, linie zostaną poprawione. Nie * myślę, że * WorkPad robi jakieś zaokrąglenia, myślę, że po prostu pokazuje użytkownikowi całkowitą liczbę dla wygody. Mogę się mylić. Projektant Visual Studio pokazuje '8.25pt' dla domyślnej czcionki' Microsoft Sans Serif', ale jeśli klikniesz okno dialogowe, aby zmienić czcionkę, zobaczysz tylko liczby całkowite w rozwijanym menu. Nie jestem pewien, czy to ci pomoże, czy nie. – LarsTech

2

Może problemem jest to, że jesteś określające rozmiar czcionki w pixels czasie, gdy przesunięcie jest obliczane w points i przekształcony z powrotem w pixels. Może to wprowadzić różnego rodzaju nieścisłości.

Spróbuj określić rozmiary czcionek w points i sprawdź, czy działa.

+0

Zgadzam się, że problem z zaokrąglaniem może być przyczyną. Ale muszę zająć się tym na poziomie programowym. Zobacz moją drugą aktualizację. Windows WordPad udaje, że wszystko działa ... –

+0

Z pewnością próbowałeś ustawić rozmiar czcionki w punktach? Jakie były twoje wyniki? Pytam, ponieważ może to być wskazówka, co się dzieje. – Coincoin

+0

Próbowałem użyć Punktów. W przypadku niektórych rozmiarów czcionek działa. Problem polega na tym, że nie mogę ograniczyć mojego rozwiązania do tych rozmiarów czcionek. Moja kontrola musi obsługiwać określone przez użytkownika rozmiary. Musi również obsługiwać proporcjonalne zmniejszanie rozmiarów, gdy liczba zawiera zbyt wiele cyfr.Gdyby istniał programowy sposób na znalezienie "dobrych" rozmiarów, a takich rozmiarów było wiele, mógłbym przyjąć takie podejście. –

1

Gdy ustawienie Graphics.TextRenderingHint jest ustawione na dopasowanie siatki, rzeczywiste metryki skalowanej czcionki TrueType w pikselach są określane przez GDI + poprzez wyszukiwanie czcionki VDMX table, a nie przez proste skalowanie i zaokrąglanie jej metryk projektowych. Tak właśnie myślałem, przechodząc przez Graphics.DrawString w demontażu.

Powiązane problemy