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?
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
naTextRenderingHintAntiAlias
. 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
igraphics.DrawString
) - GDI (
TextRenderer.MeasureText
iTextRenderer.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 gdygraphics.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
:
GDI+: TextRenderingHintAntiAlias
, GDI: ANTIALIASED_QUALITY
:
GDI+: TextRenderingHintAntiAliasGridFit
, GDI: nie jest obsługiwane, używa ANTIALIASED_QUALITY :
GDI + : TextRenderingHintSingleBitPerPixelGridFit
, GDI: PROOF_QUALITY
:
GDI+: TextRenderingHintSingleBitPerPixel
, GDI: DRAFT_QUALITY
:
Wydaje mi się dziwne, że DRAFT_QUALITY
jest identyczna z PROOF_QUALITY
, która jest identyczna z CLEARTYPE_QUALITY
.
Zobacz też
- UseCompatibleTextRendering-kompatybilny z whaaaaaat?
- W 2011 roku firma Whidbey wprowadziła do swojej oferty ponad 100 produktów.]}
- MSDN: LOGFONT Struktura
- AppCompat Guy: GDI vs. GDI+ wydajność renderowania tekstu
- GDI + tekst, niezależność rozdzielczości i metody renderowania. Albo-Dlaczego mój tekst wygląda inaczej w GDI + i w GDI?
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
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);
}
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.
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