Dlaczego grafika.MeasureString () zwraca wyższą niż oczekiwana liczbę?

Generuję Paragon i używam obiektu graficznego do wywołania metody DrawString w celu wydrukowania wymaganego tekstu.

graphics.DrawString(string, font, brush, widthOfPage / 2F, yPoint, stringformat);
To działa dobrze do tego, czego potrzebowałem. Zawsze wiedziałem, co drukuję, więc mogłem ręcznie przyciąć dowolne struny, aby pasowały prawidłowo na papierze paragonowym 80 mm. Następnie musiałem dodać dodatkową funkcjonalność, która uczyniłaby to bardziej elastycznym. Użytkownik może przekazać ciągi, które zostaną dodane do dołu.

Od Nie wiedziałem, co zamierzają umieścić, po prostu stworzyłem własną funkcję zawijania słów, która zajmuje wiele znaków do zawinięcia i sam ciąg znaków. Aby dowiedzieć się, ile znaków, robiłem coś takiego:

float width = document.DefaultPageSettings.PrintableArea.Width;
int max = (int)(width / graphics.MeasureString("a", font).Width);

Teraz szerokość zwraca mi 283, co w mm wynosi około 72, co ma sens, gdy uwzględniasz marginesy na papierze 80mm.

Ale metoda MeasureString zwraca 10.5 na kurierskiej nowej czcionce 8pt. Więc zamiast obejść to, co ja spodziewam się 36-40, ja dostaję 26, w wyniku czego 2 linijki tekstu zamieniają się w 3-4.

Jednostki do wydruku.Szerokość wynosi 1/100 cala, a jednostką PageUnit dla obiektu graficznego jest Display (Co mówi, że zwykle jest to 1/100 cala dla drukarek). Więc dlaczego odzyskam tylko 26?

Author: Brandon, 2009-07-29

3 answers

Od WindowsClient.net:

GDI+ dodaje niewielką ilość (1/6 em) na każdym końcu każdego wyświetlonego ciągu. Ten 1/6 em pozwala na glify z zwisającymi końcami (np. kursywą " f"), a także daje GDI+ niewielką swobodę, aby pomóc w rozbudowie dopasowania siatki.

Domyślna akcja DrawString będzie działać przeciwko Tobie w wyświetlaniu sąsiednich przebiegów:

  • Po pierwsze domyślny StringFormat dodaje dodatkowe 1/6 em na każdym końcu każdego wyjścia;
  • Po drugie, gdy szerokość dopasowana do siatki jest mniejsza niż zaprojektowana, ciąg może się kurczyć maksymalnie o em.

Aby uniknąć tych problemów:

  • zawsze przekazuj MeasureString iDrawString StringFormat oparty na typograficznym StringFormat (GenericTypographic).
    Ustaw grafikę TextRenderingHint na TextRenderingHintAntiAlias. Ta metoda renderowania wykorzystuje antyaliasing i pozycjonowanie glifów subpikselowych, aby uniknąć potrzeby dopasowania siatki, a zatem jest z natury niezależna od rozdzielczości.

Są dwa sposoby rysowania tekstu w. NET:

  • GDI+ (graphics.MeasureString i graphics.DrawString)
  • GDI (TextRenderer.MeasureText i TextRenderer.DrawText)

Z doskonałego bloga Michaela Kaplana (rip) sortowanie wszystkiego, w.NET 1.1 wszystko używane GDI+ do renderowania tekstu. Ale były pewne problemy:

  • istnieją pewne problemy z wydajnością spowodowane nieco bezpaństwową naturą GDI+, gdzie konteksty urządzeń byłyby ustawione, a następnie oryginał przywrócony po każdym połączeniu.
  • silniki kształtowania tekstu międzynarodowego były wielokrotnie aktualizowane Dla Windows/Uniscribe i Avalon (Windows Presentation Foundation), ale nie zostały zaktualizowane dla GDI+, co powoduje, że obsługa renderowania międzynarodowego dla nowych języków nie ma takiego samego poziomu jakości.

Więc wiedzieli, że chcą zmienić. NET framework, aby przestać używać GDI + systemu renderowania tekstu i używać GDI . Na początku mieli nadzieję, że po prostu się zmienią: {]}

graphics.DrawString

Aby wywołać stare DrawText API zamiast GDI+. Ale nie mogli sprawić, że owijanie tekstu i odstępy pasują dokładnie tak, jak robiło to GDI+. Byli więc zmuszeni do utrzymywania graphics.DrawString aby wywoływać GDI+ (powody kompatybilności; ludzie, którzy dzwonili graphics.DrawString nagle odkryli, że ich tekst nie zawija się tak, jak kiedyś).

Nowa klasa statyczna TextRenderer została stworzona do renderowania tekstu GDI. Ma dwie metody:

TextRenderer.MeasureText
TextRenderer.DrawText

Uwaga: TextRenderer jest owijarką wokół GDI, podczas gdy graphics.DrawString jest nadal owijarką wokół GDI+.


W 2007 roku, po raz pierwszy w historii, pojawiła się nowa wersja platformy. NET.]}
  • Label
  • Button
  • TextBox

Chcieli przełączyć je na TextRenderer (tj. GDI), ale musieli być ostrożni. Mogą być ludzie, którzy polegali na rysowaniu kontrolek, tak jak robili to w. NET 1.1. I tak narodził się "zgodne renderowanie tekstu ".

Domyślnie kontrolki w aplikacji zachowują się tak, jak w. NET 1.1 (są "compatible").

Ty wyłączasz tryb zgodności przez wywołanie:

Application.SetCompatibleTextRenderingDefault(false);

To sprawia, że Twoja aplikacja jest lepsza, szybsza, z lepszym międzynarodowym wsparciem. Podsumowując:

SetCompatibleTextRenderingDefault(true)  SetCompatibleTextRenderingDefault(false)
=======================================  ========================================
 default                                  opt-in
 bad                                      good
 the one we don't want to use             the one we want to use
 uses GDI+ for text rendering             uses GDI for text rendering
 graphics.MeasureString                   TextRenderer.MeasureText
 graphics.DrawString                      TextRenderer.DrawText
 Behaves same as 1.1                      Behaves *similar* to 1.1
                                          Looks better
                                          Localizes better
                                          Faster

Warto również zwrócić uwagę na mapowanie pomiędzy GDI + TextRenderingHint a odpowiadającym LOGFONT jakość używana dla czcionki GDI rysunek:

TextRenderingHint           mapped by TextRenderer to LOGFONT quality
========================    =========================================================
ClearTypeGridFit            CLEARTYPE_QUALITY (5) (Windows XP: CLEARTYPE_NATURAL (6))
AntiAliasGridFit            ANTIALIASED_QUALITY (4)
AntiAlias                   ANTIALIASED_QUALITY (4)
SingleBitPerPixelGridFit    PROOF_QUALITY (2)
SingleBitPerPixel           DRAFT_QUALITY (1)
else (e.g.SystemDefault)    DEFAULT_QUALITY (0)

Próbki

Oto kilka porównań GDI+ (grafika.DrawString) wersy GDI (TextRenderer.DrawText) renderowanie tekstu:

GDI+: TextRenderingHintClearTypeGridFit, GDI: CLEARTYPE_QUALITY:

Tutaj wpisz opis obrazka

GDI+: TextRenderingHintAntiAlias, GDI: ANTIALIASED_QUALITY:

Tutaj wpisz opis obrazka

GDI+: TextRenderingHintAntiAliasGridFit, GDI: nie jest obsługiwane, używa ANTIALIASED_QUALITY :

Tutaj wpisz opis obrazka

GDI + : TextRenderingHintSingleBitPerPixelGridFit, GDI: PROOF_QUALITY:

Tutaj wpisz opis obrazka

GDI+: TextRenderingHintSingleBitPerPixel, GDI: DRAFT_QUALITY:

Tutaj wpisz opis obrazka

Wydaje mi się dziwne, że DRAFT_QUALITY jest identyczna z PROOF_QUALITY, która jest identyczna z CLEARTYPE_QUALITY.

Zobacz też

 160
Author: Ian Boyd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-04-11 09:52:39

Kurier Nowy Rozmiar 11

Po utworzeniu czcionki 'Courier New' o rozmiarze = 11 otrzymasz wynik jak na powyższym obrazku. Widać, że wysokość wynosi 14 pikseli, nie wliczając podkreślenia. Szerokość wynosi dokładnie 14 pikseli (7 pikseli dla każdego znaku).

Więc ta czcionka renderuje 14x14 pikseli.

Ale TextRenderer.MeasureText() zwraca zamiast tego Szerokość 21 pikseli. Jeśli potrzebujesz dokładnych wartości, jest to bezużyteczne.

Rozwiązaniem jest następujący kod:

Font i_Courier = new Font("Courier New", 11, GraphicsUnit.Pixel);

Win32.SIZE k_Size;
using (Bitmap i_Bmp = new Bitmap(200, 200, PixelFormat.Format24bppRgb))
{
    using (Graphics i_Graph = Graphics.FromImage(i_Bmp))
    {
        IntPtr h_DC = i_Graph.GetHdc();
        IntPtr h_OldFont = Win32.SelectObject(h_DC, i_Courier.ToHfont());

        Win32.GetTextExtentPoint32(h_DC, "Áp", 2, out k_Size);

        Win32.SelectObject(h_DC, h_OldFont);
        i_Graph.ReleaseHdc();
    }
}

K_Size będzie zawierał prawidłowy rozmiar: 14x14

Ważne: Ten kod mierzy poprawnie zwykłą czcionkę. Jeśli potrzebujesz dokładnych wartości również dla czcionek kursywnych (które zawsze mają zwis po prawej stronie), powinieneś przeczytać linki wymienione w tym artykule: http://www.codeproject.com/Articles/14915/Width-of-text-in-italic-font

"Dodatek": {]} Dla tych, którzy nigdy nie używali wywołań API w C# tutaj podpowiedź jak utworzyć klasę Win32. To nie jest kompletne. Po więcej szczegółów zajrzyj at http://www.pinvoke.net

using System.Runtime.InteropServices;

public class Win32
{       
    [StructLayout(LayoutKind.Sequential)]
    public struct SIZE
    {
        public int cx;
        public int cy;
    }

    [DllImport("Gdi32.dll")]
    public static extern bool GetTextExtentPoint32(IntPtr hdc, string lpString, int cbString, out SIZE lpSize);

    [DllImport("Gdi32.dll")]
    public static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj);
}
 9
Author: Elmue,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-04 22:01:55

Oto Wyjaśnienie, które pomoże Ci zrozumieć, jak to działa. i co powoduje przestrzenie mniej więcej przed i po każdym znaku.

GDI DrawString Configurator App

Screen Capture

 0
Author: Cheva,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-12 08:55:43