Warum ist Graphics.MeasureString () eine höher als erwartete Anzahl der Rückkehr?
-
05-07-2019 - |
Frage
Ich bin Erzeugung eine Quittung und bin mit der Graphics-Objekt der DrawString Methode aufzurufen den gewünschten Text zu drucken.
graphics.DrawString(string, font, brush, widthOfPage / 2F, yPoint, stringformat);
Dies funktioniert gut für das, was ich tun musste. Ich wusste immer, was ich den Ausdruck, so konnte ich keine Saiten manuell trimmen, damit es ordnungsgemäß auf 80mm Bonpapier passen würde. Dann hatte ich ein Extra an Funktionalität hinzuzufügen, die diese flexibler machen würde. Der Benutzer könnte in Strings übergeben, die an der Unterseite hinzugefügt werden würde.
Da ich nicht wusste, was sie setzen werde wurde, habe ich nur meine eigene Zeilenumbruch-Funktion, die auf und die Zeichenfolge selbst wickeln in einer Reihe von Zeichen erfolgt. Um die Anzahl der Zeichen, um herauszufinden, tat ich so etwas wie folgt aus:
float width = document.DefaultPageSettings.PrintableArea.Width;
int max = (int)(width / graphics.MeasureString("a", font).Width);
Nun ist die Breite zurückkehrt mir 283, die in mm ist etwa 72, was Sinn macht, wenn Sie sich für die Margen auf 80 mm Papier berücksichtigen.
Aber die MeasureString Methode 10.5 auf einem Courier New 8pt Schriftart zurück. Anstatt also um das, was von immer ich erwartet hatte 36 zu sein - 40, ich bin 26 bekommen, in zwei Zeilen Text resultierenden in 3-4 gedreht wird
.Die Einheiten für PrintableArea.Width sind 1 / 100stel eines Zolls und der Pageunit für das Grafik-Objekt ist Display (die besagt, ist in der Regel 1 / 100stel eines Zolls für Drucker). Also warum bin ich nur 26 zurück zu bekommen?
Lösung
Von WindowsClient.net:
GDI + fügt eine kleine Menge (1/6 em) an jedes Ende jeden angezeigten Zeichenfolge. Diese 1/6 em ermöglicht Glyphen mit überhängenden Enden (wie kursiv ‚ f ‘), und gibt auch GDI + eine kleine Menge Spielraum mit Rasteranpassung Expansion zu helfen.
Die Standardaktion von
DrawString
wird gegen Sie arbeitet benachbarte Läufe bei der Anzeige:
- Zum einen die Standard-String fügt eine zusätzliche 1/6 em an jedem Ende eines jeden Ausgang;
- Zweitens, wenn Einhängeraster Breiten geringer sind als entworfen, wird die Zeichenfolge darf ein em schrumpfen um bis.
Um diese Probleme zu vermeiden:
- Geben Sie stets
MeasureString
undDrawString
einen String basierend auf dem typographischen String (GenericTypographic
).
Stellen Sie die GrafikTextRenderingHint
aufTextRenderingHintAntiAlias
. Diese Rendering-Methode verwendet Anti-Aliasing und Subpixel-Glyphe Positionierung die Notwendigkeit für netz Einbau zu vermeiden, und ist somit von Natur aus unabhängig von der Auflösung.
Es gibt zwei Möglichkeiten von Text in .NET Zeichnung:
- GDI + (
graphics.MeasureString
undgraphics.DrawString
) - GDI (
TextRenderer.MeasureText
undTextRenderer.DrawText
)
Von Michael Kaplan (rip) ausgezeichnetem Blog Sortierung It All Out In .NET 1.1 alles verwendet GDI + für Text-Rendering. Aber es gab einige Probleme:
- Es gibt einige Performance-Probleme durch die etwas staatenlos Art von GDI + verursacht, in dem Gerätekontexte eingestellt werden würde und dann das Original nach jedem Gespräch wieder hergestellt.
- Die Formmaschinen für den internationalen Text haben viele Male für Windows / Uniscribe und für Avalon (Windows Presentation Foundation) aktualisiert worden, aber noch nicht für GDI + aktualisiert worden ist, die für neue Sprachen internationale Rendering Unterstützung bewirkt, nicht das gleiche Niveau haben, Qualität.
So sie wussten, sie wollten das .NET Framework ändern mit GDI zu stoppen + 's Text-Rendering-System, und verwenden Sie GDI . Zunächst hoffte, dass sie sie könnten einfach ändern:
graphics.DrawString
die alte DrawText
API anstelle von GDI + nennen. Aber sie konnten nicht den Text-Wrapping und Abstand Spiel machen genau wie das, was GDI + tat. So waren sie gezwungen, graphics.DrawString
zu halten GDI + zu nennen (Kompatiblität Gründe, die Leute, die graphics.DrawString
riefen würde plötzlich feststellen, dass ihr Text nicht wickeln, wie es verwendet wird).
Eine neue statische TextRenderer
Klasse wurde erstellt GDI Text-Rendering zu wickeln. Es verfügt über zwei Methoden:
TextRenderer.MeasureText
TextRenderer.DrawText
. Hinweis:
TextRenderer
ist ein Wrapper um GDI, währendgraphics.DrawString
noch ein Wrapper um GDI + ist
Dann war da noch die Frage, was mit allen vorhandenen .NET-Steuerelemente zu tun, z.
-
Label
-
Button
-
TextBox
Sie wollten sie wechseln TextRenderer
(das heißt GDI) zu verwenden, aber sie musste vorsichtig sein. Es könnte Leute geben, die Zeichnung auf ihren Kontrollen angewiesen, wie sie in tat .NET 1.1. Und so wurde geboren " kompatibel Text-Rendering ".
Mit dem Standard-Steuerelemente in der Anwendung verhalten sich wie sie in tat .NET 1.1 (sie sind " kompatibel ").
Sie deaktivieren Kompatibilitätsmodus durch den Aufruf:
Application.SetCompatibleTextRenderingDefault(false);
Das macht Ihre Anwendung besser, schneller und mit besserer internationalen Unterstützung. Fazit:
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
Es ist auch nützlich, um die Zuordnung zwischen GDI + TextRenderingHint
und dem entsprechenden LOGFONT
Qualität für GDI Schriftzeichen verwendet:
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)
Samples
Hier einige Vergleiche von GDI + (Graphics.DrawString) Verse GDI (TextRenderer.DrawText) Text-Rendering:
GDI + : TextRenderingHintClearTypeGridFit
, GDI : CLEARTYPE_QUALITY
:
GDI + : TextRenderingHintAntiAlias
, GDI : ANTIALIASED_QUALITY
:
GDI + : TextRenderingHintAntiAliasGridFit
, GDI : nicht unterstützt wird, verwendet ANTIALIASED_QUALITY :
GDI + : TextRenderingHintSingleBitPerPixelGridFit
, GDI : PROOF_QUALITY
:
GDI + : TextRenderingHintSingleBitPerPixel
, GDI : DRAFT_QUALITY
:
Ich finde es seltsam, dass DRAFT_QUALITY
identisch mit PROOF_QUALITY
, die CLEARTYPE_QUALITY
identisch ist.
Siehe auch
- UseCompatibleTextRendering - Kompatibel mit whaaaaaat
- sortieren sie alle aus: Ein kurzer Blick auf Whidbey der TextRenderer-
- MSDN: LOGFONT Struktur
- AppCompat Guy: GDI vs. GDI + Text Rendering-Leistung
- GDI + Text, Auflösungsunabhängigkeit und Renderingmethoden. Oder? - Warum wird mein Text anders aussehen in GDI + und in GDI
Andere Tipps
Wenn Sie eine Schriftart ‚Courier New‘ mit Größe erstellen = 11 Sie werden über eine Ausgabe wie in dem Bild bekommen. Sie sehen, dass die Höhe 14 Pixel ist die unterstrichenen nicht enthält. Die Breite beträgt genau 14 Pixel (Bildpunkt 7 für jedes Zeichen).
Also diese Schrift macht 14x14 Pixel.
Aber TextRenderer.MeasureText()
gibt eine Breite von 21 Pixeln statt. Wenn Sie genaue Werte brauchen dies ist nutzlos.
Die Lösung ist der folgende Code:
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 wird die richtige Größe enthalten: 14x14
Wichtig: Dieser Code Maßnahmen richtig ein normaler Schrift. Wenn Sie die genauen Werte auch für kursiv müssen (das hat immer einen Überhang auf dem rechten Seite) können Sie über die Links lesen sollen, die in diesem Artikel erwähnt werden: http://www.codeproject.com/Articles/14915/Width-of-text-in-italic-font
ANHANG: Für diejenigen, die nie API-Aufrufe in C # hier einen Hinweis, wie Sie erstellen die Klasse Win32 verwendet haben. Dies ist nicht vollständig. Weitere Einzelheiten finden Sie unter 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);
}
Hier ist eine Erklärung, die Sie verstehen helfen, wie es funktioniert. und was bewirkt, dass die Räume von mehr oder weniger vor und nach jedem Zeichen.