2016-04-06 4 views
0

Das Problem, das ich habe, ist, das neue Bitmap korrekt mit der Größe der Druckerausgabe zu synchronisieren, indem DrawImageUnscaled() in PrintPage 's PrintPage Ereignis verwendet wird.Bitmap zu PrinterDocument Seitenauflösung stimmt nicht überein

bekam ich die Idee, ein Bild als eine Seite Sammlung aus den Kommentaren auf einem anderen Dienstposten zu machen, wo ich gefragt, wie mit dem Drucker in einem eher traditionellen Stil arbeiten (NewPage, Zeichnung Artikel vorPrint Aufruf, etc), das existiert nicht im .NET Framework. Bei meinem ersten Versuch, eine Bildsammlung zu verwenden, bemerkte ich, dass sie etwas körnig war, wenn .DrawImage() verwendet wurde, auch nachdem Bitmap Dpi auf die gleiche Dpi wie das Druckerobjekt gesetzt wurde, das ohne das PrinterSettings.CreateMeasurementGraphics() Grafikobjekt durch Versuch und Irrtum gefunden werden konnte (viele Fehler.

Das Ergebnis dieses Bestrebens bisher ist die folgende Klasse (es hat einige 'Test' Code, wo ich herum gespielt habe, aber ich habe das meiste davon aufgeräumt, so dass es hier vorzeigbar ist)

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 
using System.Drawing; 
using System.Drawing.Drawing2D; 
using System.Windows.Forms; 
using System.Windows.Forms.Design; 

using PdfFileWriter; 
using System.Drawing.Printing; 
using System.ComponentModel; 

using System.IO; 

class PDF : PrintDocument { 
    /// <summary> 
    /// Logo to display on invoice 
    /// </summary> 
    public Image Logo { get; set; } 

    /// <summary> 
    /// Current X position on canvas 
    /// </summary> 
    public int X { get; set; } 

    /// <summary> 
    /// Current Y position on canvas 
    /// </summary> 
    public int Y { get; set; } 

    /// <summary> 
    /// Set the folder where backups, downloads, etc will be stored or retrieved from 
    /// </summary> 
    [Editor(typeof(System.Windows.Forms.Design.FolderNameEditor), typeof(System.Drawing.Design.UITypeEditor))] 
    public string Folder { get { return directory; } set { directory=value; } } 

    /// <summary> 
    /// Current font used to print 
    /// </summary> 
    public Font Font { get; set; } 

    /// <summary> 
    /// Current font color 
    /// </summary> 
    public Color ForeColor { get; set; } 

    private int CurrentPagePrinting { get; set; } 

    /// <summary> 
    /// Set printer margins 
    /// </summary> 
    public Margins PrintMargins { 
     get { return DefaultPageSettings.Margins; } 
     set { DefaultPageSettings.Margins = value; } 
    } 

    /// <summary> 
    /// Pages drawn in document 
    /// </summary> 
    private List<Image> Pages; 

    /// <summary> 
    /// The current selected page number. 0 if nothing selected 
    /// </summary> 
    private int CurrentPage; 

    /// <summary> 
    /// The current working directory to save files to 
    /// </summary> 
    private string directory; 

    /// <summary> 
    /// The currently chosen filename 
    /// </summary> 
    private string file; 

    /// <summary> 
    /// Public acceisble object to all paperSizes as set 
    /// </summary> 
    public List<PrintPaperSize> paperSizes { get; private set; } 

    /// <summary> 
    /// Object for holding papersizes 
    /// </summary> 
    public class PrintPaperSize { 
     public string Name { get; set; } 
     public double Height { get; set; } 
     public double Width { get; set; } 

     public PrintPaperSize() { 
      Height = 0; 
      Width = 0; 
      Name = ""; 
     } 
    } 

    /// <summary> 
    /// Current papersize selected. used for some calculations 
    /// </summary> 
    public PrintPaperSize CurrentPaperSize { get; private set; } 

    public PDF() { 
     // set the file name without extension to something safe 
     file = (string)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds.ToString(); 

     // set the save directory to MyDocuments 
     directory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); 
     CurrentPage = 0; 

     // initialize pages array 
     Pages = new List<Image>(); 

     // Set the initial font and color 
     Font = new System.Drawing.Font("Arial", (float)11.25); 
     ForeColor = Color.Black; 

     // set the printer to Microsoft's PDF printer and generate and ensure it will save to a file 
     PrinterSettings = new PrinterSettings() { 
      PrinterName = "Microsoft Print to PDF", 
      PrintToFile = true, 
      PrintFileName = Path.Combine(directory, file + ".pdf"), 
     }; 

     // hide the notice 'printing' while spooling job. 
     PrintController = new StandardPrintController(); 

     // set the printer quality to maximum so we can use this for getting the dpi at this setting 
     DefaultPageSettings.PrinterResolution.Kind = PrinterResolutionKind.High; 

     // store all paper sizes at 1 dpi [ reference: https://social.msdn.microsoft.com/Forums/vstudio/en-US/05169a47-04d5-4890-9b0a-7ad11a6a87f2/need-pixel-width-for-paper-sizes-a4-a5-executive-letter-legal-executive?forum=csharpgeneral ] 
     paperSizes = new List<PrintPaperSize>(); 
     foreach (PaperSize P in PrinterSettings.PaperSizes) { 
      double W=P.Width/100.0; 
      double H=P.Height/100.0; 

      paperSizes.Add(
       new PrintPaperSize() { 
        Height = H, 
        Width = W, 
        Name = P.PaperName 
       } 
      ); 

      if (P.PaperName=="Letter") { 
       CurrentPaperSize = paperSizes[paperSizes.Count-1]; 
      } 
     } 

     // setup the initial page type, orientation, margins, 
     using (Graphics g=PrinterSettings.CreateMeasurementGraphics()) { 
      DefaultPageSettings = new PageSettings(PrinterSettings) { 
       PaperSize=new PaperSize(CurrentPaperSize.Name, (Int32)(CurrentPaperSize.Width*g.DpiX), (Int32)(CurrentPaperSize.Height*g.DpiY)), 
       Landscape = false, 
       Margins = new Margins(left: 10, right: 10, top: 10, bottom: 10), 
       PrinterResolution=new PrinterResolution() { 
        Kind = PrinterResolutionKind.High 
       } 
      }; 
     } 

     // constrain print within margins 
     OriginAtMargins = true; 
    } 


    public void SetPaperSize(PaperKind paperSize) { 
     // TODO: Use Linq on 
    } 

    /// <summary> 
    /// Get specific page 
    /// </summary> 
    /// <param name="page">page number. 1 based array</param> 
    /// <returns></returns> 
    public Image GetPage(int page) { 
     int p = page - 1; 
     if (p<0||p>Pages.Count) { return null; } 
     return Pages[p]; 
    } 

    /// <summary> 
    /// Get the current page 
    /// </summary> 
    /// <returns>Image</returns> 
    public Image GetCurrentPage() { 
     return GetPage(CurrentPage); 
    } 

    /// <summary> 
    /// Before printing starts 
    /// </summary> 
    /// <param name="e">PrintEventArgs</param> 
    protected override void OnBeginPrint(PrintEventArgs e) { 
     CurrentPagePrinting=0; 
     base.OnBeginPrint(e); 
    } 

    /// <summary> 
    /// Print page event 
    /// </summary> 
    /// <param name="e">PrintPageEventArgs</param> 
    protected override void OnPrintPage(PrintPageEventArgs e) { 
     CurrentPagePrinting++; 

     // if page count is max exit print routine 
     if (CurrentPagePrinting>=Pages.Count) { e.HasMorePages=false; base.OnPrintPage(e); return; } 

     // ensure high resolution/clarity of image so text doesn't fuzz 
     e.Graphics.CompositingMode=CompositingMode.SourceOver; 
     e.Graphics.CompositingQuality=CompositingQuality.HighQuality; 

     // Draw image and respect margins (unscaled in addition to the above so text doesn't fuzz) 
     e.Graphics.DrawImageUnscaled(
      Pages[CurrentPagePrinting-1], 
      new Point(
       DefaultPageSettings.Margins.Top, 
       DefaultPageSettings.Margins.Left 
      ) 
     ); 
     base.OnPrintPage(e); 
    } 

    /// <summary> 
    /// After printing has been completed 
    /// </summary> 
    /// <param name="e">PrintEventArgs</param> 
    protected override void OnEndPrint(PrintEventArgs e) { 
     base.OnEndPrint(e); 
    } 

    /// <summary> 
    /// Add a new page to the document 
    /// </summary> 
    public void NewPage() { 
     // Add a new page to the page collection and set it as the current page 

     Bitmap bmp; 
     using(Graphics g = PrinterSettings.CreateMeasurementGraphics()) { 
      float dpiscaleX; 
      float dpiscaleY; 

      // measure default bitmap dpi on this system and use to calculate print dpi 
      using (Bitmap b=new Bitmap(1, 1)) { 
       dpiscaleX = b.HorizontalResolution; 
       dpiscaleY = b.VerticalResolution; 
      }; 

      bmp = new Bitmap( 
       (Int32)(((DefaultPageSettings.PrintableArea.Width-(DefaultPageSettings.Margins.Left+DefaultPageSettings.Margins.Right))/dpiscaleX) * g.DpiX), 
       (Int32)(((DefaultPageSettings.PrintableArea.Height-(DefaultPageSettings.Margins.Top+DefaultPageSettings.Margins.Bottom))/dpiscaleY) * g.DpiY) 
      ); 
      bmp.SetResolution(g.DpiX, g.DpiY); 
     } 
     Pages.Add(bmp); 
     CurrentPage++; 
    } 

    /// <summary> 
    /// Add a new string to the current page 
    /// </summary> 
    /// <param name="text">The string to print</param> 
    /// <param name="align">Optional alignment of the string</param> 
    public void DrawString(string text, System.Windows.TextAlignment align = System.Windows.TextAlignment.Left) { 
     // add string to document 
     using (Graphics g=Graphics.FromImage(Pages[CurrentPage - 1])) { 
      g.CompositingQuality = CompositingQuality.HighQuality; 
      switch (align) { 
       case System.Windows.TextAlignment.Left: 
       case System.Windows.TextAlignment.Justify: 
        g.DrawString(text, Font, new SolidBrush(ForeColor), new PointF(X, Y)); 
        Y+=(Int32)g.MeasureString("X", Font).Height; 
        break; 
       case System.Windows.TextAlignment.Right: 
        g.DrawString(text, Font, new SolidBrush(ForeColor), new PointF(Pages[CurrentPage - 1].Width - g.MeasureString(text, Font).Width, Y)); 
        Y += (Int32)g.MeasureString("X", Font).Height; 
        break; 
       case System.Windows.TextAlignment.Center: 
        g.DrawString(text, Font, new SolidBrush(ForeColor), new PointF((Pages[CurrentPage-1].Width+g.MeasureString(text, Font).Width)/2, Y)); 
        Y+=(Int32)g.MeasureString("X", Font).Height; 
        break; 
      } 
     } 
    } 
} 

Abbildung der PDF-Ausgabe

Demonstration of Dpi

Wie Sie sehen können, geht die Leinwand (Page Eigenschaft in meiner Klasse), außerhalb der Grenzen. Wenn ich das Bild .DrawImage() mit ziehen nur die Skalierung reicht es und es sieht einfach körnig, so muss ich .DrawImageUnscaled()

Die Linien oben verwenden, um das Ergebnis des folgenden Codes:

// Initialize the custom print class 
PDF p = new PDF(); 

// Add a new page to the document 
p.NewPage(); 

// Draw some strings. p.Y value is automatically incremented 
p.DrawString("Hello"); 
p.DrawString("Hello", System.Windows.TextAlignment.Right); 
p.DrawString("Hello", System.Windows.TextAlignment.Center); 
p.DrawString("Hello pure awesomeness"); 

// Uncomment the following and add a picture box to the form 
// pictureBox1.Height = 1100; 
// pictureBox1.Width = 850; 
// pictureBox1.SizeMode = PictureBoxSizeMode.Zoom; 
// pictureBox1.Image = p.GetCurrentPage(); 

// Send all pages to the "printer" 
p.Print(); 

Wenn Sie uncomment die pictureBox1 Linien und p.Print() Kommentar aus, das Ergebnis korrekt ist (wobei das Bild kleiner als die Printdocument-Seite aufgrund der Ränder festgelegt wird für die Seite.

Illustration von PictureBox Ausgabe

im Auge

Demonstration of Dpi in PictureBox

Und wenn Sie den folgenden Code (anstelle des oben pictureBox-Code) verwendet werden, die auf die Dpi Skalierung innerhalb der Klasse funktional äquivalent ist, zeigt alles, was oben richtig in der pictureBox (nur viel größer als meine Die Einstellung 'Hoch' wird auf 600 DPI zurückgesetzt, wobei ein neu erstelltes Bitmap auf 72 DPI gesetzt wird, bevor die Methode SetResolution() auf dem Image aufgerufen wird.

Image img = p.GetCurrentPage(); 

pictureBox1.Height=(Int32)(p.CurrentPaperSize.Height*img.VerticalResolution); 
pictureBox1.Width = (Int32)(p.CurrentPaperSize.Width*img.HorizontalResolution); 
pictureBox1.SizeMode = PictureBoxSizeMode.Zoom; 
pictureBox1.Image = img; 
+0

Verwenden WPF Druck, haben WinForms in der Regel Probleme mit DPI. https://code.msdn.microsoft.com/windowsdesktop/WPF-Printing-Overview-f28c541a#content –

+0

@ VojtěchDohnal - danke, aber das war nicht das Problem. Ich hatte ein Problem damit, wie ich die Ränder berechnete.Die Marge der Druckereinstellungen liegt bei 100stel Zoll, und ich habe diesen Wert nur als "ganze" Marge berücksichtigt. Nun, um ein Ereignis hinzuzufügen, wenn eine neue Seite automatisch hinzugefügt wird, und erlauben Benutzersteuerung, Zeug vor dem Drucken der nächsten Zeile zu drucken (erlauben Sie auch dem Benutzer, die X/Y-Position innerhalb des Ereignisses zu ändern.)/etc) .Post meine Ergebnisse als eine Lösung für andere :) –

+0

@ VojtěchDohnal - Lösung geschrieben :) –

Antwort

0

Die Berechnung für DefaultPageSettings.Margins Messung falsch war als Margins in 100. der von einem Zoll according to this MSDN document vertreten sind. Der richtige Weg für diese anzupassen von 100.

x = DefaultPageSettings.Margins/100 

zu teilen ist nach dem NewPage() Methode des OP ist mit den richtigen Berechnungen neu geschrieben:

/// <summary> 
/// Add a new page to the document 
/// </summary> 
public void NewPage() { 
    // Add a new page to the page collection and set it as the current page 

    Bitmap bmp; 
    using(Graphics g = PrinterSettings.CreateMeasurementGraphics()) { 
     int w=(Int32)(CurrentPaperSize.Width*g.DpiX)-(Int32)(((DefaultPageSettings.Margins.Left+DefaultPageSettings.Margins.Right)/100)*g.DpiX); 
     int h=(Int32)(CurrentPaperSize.Height*g.DpiY)-(Int32)(((DefaultPageSettings.Margins.Top+DefaultPageSettings.Margins.Bottom)/100)*g.DpiY); 
     bmp = new Bitmap(w, h); 
     bmp.SetResolution(g.DpiX, g.DpiY); 
    } 

    // reset X and Y positions 
    Y=0; 
    X=0; 

    // Add new page to the collection 
    Pages.Add(bmp); 
    CurrentPage++; 
} 
Verwandte Themen