Domanda

Sto generando una ricevuta e sto usando l'oggetto Graphics per chiamare il metodo DrawString per stampare il testo richiesto.

graphics.DrawString(string, font, brush, widthOfPage / 2F, yPoint, stringformat);

Funziona benissimo per quello che mi serviva. Ho sempre saputo cosa stavo stampando, quindi potevo tagliare manualmente tutte le stringhe in modo che si adattassero correttamente alla carta per ricevute da 80 mm. Quindi ho dovuto aggiungere un po 'di funzionalità in più per renderlo più flessibile. L'utente potrebbe passare stringhe che verrebbero aggiunte in fondo.

Dato che non sapevo cosa avrebbero messo, ho appena creato la mia funzione di a capo automatico che include un numero di caratteri da avvolgere e la stringa stessa. Per scoprire il numero di personaggi, stavo facendo qualcosa del genere:

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

Ora la larghezza mi sta restituendo 283, che in mm è di circa 72, il che ha senso quando si tengono conto dei margini su carta da 80 mm.

Ma il metodo MeasureString sta restituendo 10.5 su un font Courier New 8pt. Quindi, invece di aggirare ciò che mi aspettavo fosse 36 - 40, ne ottengo 26, con il risultato che 2 righe di testo vengono trasformate in 3-4.

Le unità per PrintableArea.Width sono 1/100 di pollice e PageUnit per l'oggetto grafico è Display (che indica in genere 1 / 100esimo di pollice per le stampanti). Quindi, perché sto solo tornando 26?

È stato utile?

Soluzione

Da WindowsClient.net:

  

GDI + aggiunge una piccola quantità (1/6 em) a ciascuna estremità di ogni stringa visualizzata. Questo 1/6 em consente di glifi con estremità sporgenti (come il corsivo ' f ') e offre inoltre a GDI + un piccolo margine di manovra per aiutare con l'espansione della griglia.

     

L'azione predefinita di DrawString funzionerà contro di te nella visualizzazione di percorsi adiacenti:

     
      
  • Innanzitutto StringFormat predefinito aggiunge un ulteriore 1/6 em a ciascuna estremità di ogni output;
  •   
  • In secondo luogo, quando le larghezze adattate alla griglia sono inferiori a quelle progettate, la stringa può contrarsi fino a un massimo.
  •   
     

Per evitare questi problemi:

     
      
  • Passa sempre MeasureString e DrawString un StringFormat basato sul StringFormat tipografico ( GenericTypographic ).
      Impostare TextRenderingHint su TextRenderingHintAntiAlias ??. Questo metodo di rendering utilizza l'anti-aliasing e il posizionamento dei glifi sub-pixel per evitare la necessità di adattamento alla griglia ed è quindi intrinsecamente indipendente dalla risoluzione.
  •   

Esistono due modi per disegnare il testo in .NET:

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

Dall'eccellente blog (rip) di Michael Kaplan Ordinando tutto , in .NET 1.1 tutto usato GDI + per il rendering del testo. Ma c'erano alcuni problemi:

  
      
  • Ci sono alcuni problemi di prestazioni causati dalla natura alquanto apolide di GDI +, in cui verranno impostati i contesti del dispositivo e ripristinato l'originale dopo ogni chiamata.
  •   
  • I motori di modellazione per il testo internazionale sono stati aggiornati più volte per Windows / Uniscribe e per Avalon (Windows Presentation Foundation), ma non sono stati aggiornati per GDI +, il che fa sì che il supporto del rendering internazionale per nuove lingue non abbia lo stesso livello di qualità.
  •   

Quindi sapevano che volevano cambiare il framework .NET per smettere di usare il sistema di rendering del testo di GDI + e usare GDI . Inizialmente speravano di poter semplicemente cambiare:

graphics.DrawString

per chiamare la vecchia API DrawText invece di GDI +. Ma non sono stati in grado di far corrispondere la disposizione del testo e la spaziatura esattamente come ha fatto GDI +. Quindi furono costretti a mantenere graphics.DrawString per chiamare GDI + (ragioni di compatibilità; le persone che chiamavano graphics.DrawString avrebbero improvvisamente scoperto che il loro testo non andava a finire come abituato a).

È stata creata una nuova classe statica TextRenderer per avvolgere il rendering del testo GDI. Ha due metodi:

TextRenderer.MeasureText
TextRenderer.DrawText
  

Nota: TextRenderer è un wrapper per GDI, mentre graphics.DrawString è ancora un wrapper per GDI +.


Quindi c'era il problema di cosa fare con tutti i controlli .NET esistenti, ad esempio:

  • Label
  • Pulsante
  • TextBox

Volevano cambiarli per usare TextRenderer (cioè GDI), ma dovevano stare attenti. Potrebbero esserci persone che dipendono dai loro controlli disegnando come in .NET 1.1. E così è nato " rendering di testo compatibile " ;.

Per impostazione predefinita, i controlli nell'applicazione si comportano come in .NET 1.1 (sono " compatibili ").

disattivi la modalità di compatibilità chiamando:

Application.SetCompatibleTextRenderingDefault(false);

Questo rende la tua applicazione migliore, più veloce, con un migliore supporto internazionale. Per riassumere:

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

È anche utile annotare il mapping tra GDI + TextRenderingHint e il corrispondente LOGFONT Qualità utilizzato per il disegno di caratteri GDI:

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)

Campioni

Ecco alcuni confronti di GDI + (graphics.DrawString) contro il rendering di testo GDI (TextRenderer.DrawText):

GDI + : TextRenderingHintClearTypeGridFit , GDI : CLEARTYPE_QUALITY :

inserisci qui la descrizione dell'immagine

GDI + : TextRenderingHintAntiAlias ??, GDI : ANTIALIASED_QUALITY :

inserisci qui la descrizione dell'immagine

GDI + : TextRenderingHintAntiAliasGridFit , GDI : non supportato, utilizza ANTIALIASED_QUALITY :

inserisci qui la descrizione dell'immagine

GDI + : TextRenderingHintSingleBitPerPixelGridFit , GDI : PROOF_QUALITY :

inserisci qui la descrizione dell'immagine

GDI + : TextRenderingHintSingleBitPerPixel , GDI : DRAFT_QUALITY :

inserisci qui la descrizione dell'immagine

Trovo strano che DRAFT_QUALITY sia identico a PROOF_QUALITY , che è identico a CLEARTYPE_QUALITY .

Vedi anche

Altri suggerimenti

Courier New Size 11

Quando crei un carattere 'Courier New' con Size = 11 otterrai un output come nell'immagine sopra. Si vede che l'altezza è di 14 pixel senza includere la sottolineatura. La larghezza è esattamente 14 pixel (7 pixel per ogni personaggio).

Quindi questo carattere viene visualizzato con pixel 14x14.

Ma TextRenderer.MeasureText () restituisce invece una larghezza di 21 pixel. Se hai bisogno di valori esatti questo è inutile.

La soluzione è il seguente codice:

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 conterrà la dimensione corretta: 14x14

ATTENZIONE: Questo codice misura correttamente un carattere normale. Se hai bisogno dei valori esatti anche per i caratteri in corsivo (che hanno sempre una sporgenza a destra), dovresti leggere i link menzionati in questo articolo: http://www.codeproject.com/Articles/14915/Width-of-text-in-italic-font

APPENDICE: Per coloro che non hanno mai usato le chiamate API in C # ecco un suggerimento su come creare la classe Win32. Questo non è completo Per maggiori dettagli dai un'occhiata a 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);
}

Ecco una spiegazione che può aiutarti a capire come funziona. e cosa causa gli spazi di più o meno prima e dopo ogni personaggio.

App GDI DrawString Configurator

Screen Capture

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top