Printing

From WxWiki
Jump to navigation Jump to search
The printable version is no longer supported and may have rendering errors. Please update your browser bookmarks and please use the default browser print function instead.
  • For simple uses, wxHtmlEasyPrinting will get you started quickly.
  • If you need more complex printing with drawing, here's a small code snippet to quickly get you started with the basics of printing in wxWidgets.

On wxGTK, make sure to build wxWidgets with libgtkprint (formerly libgnomeprint) support enabled. This provides a much more suitable printing solution.

On wxMac, upgrade to wxOSX 2.9.x+ if possible, since many printing bugs/glitches were fixed there compared to 2.8

#include <iostream>
#include "wx/wx.h"
#include "wx/print.h"
#include "wx/printdlg.h"
#include <cmath>

static const int brush_size = 3;

/**
  * Shows a basic example of how to print stuff in wx.
  */
class QuickPrint : public wxPrintout
{
    wxPageSetupDialogData m_page_setup;
    
    /** the type of paper (letter, A4, etc...) */
    wxPaperSize m_paper_type;
    
    /** horizontal or vertical */
    int m_orient;
    
    // Or, if you use wxWidgets 2.9+ :
    // wxPrintOrientation m_orient;

    /** number of pages we want to print. here it's static, but in a real example you will often
      * want to calculate dynamically once you know the size of the printable area from page setup
      */
    int m_page_amount;
        
    /** margins, in millimeters */
    int m_margin_left, m_margin_right, m_margin_top, m_margin_bottom;
    
    /** we'll use this to determine the coordinate system; it describes the number of units per
      * centimeter (i.e. how fine the coordinate system is)
      */
    float m_units_per_cm;
    
    /** will contain the dimensions of the coordinate system, once it's calculated.
      * in the printing callback, you can then draw from (0, 0) to (m_coord_system_width, m_coord_system_height),
      * which will be the area covering the paper minus the margins
      */
    int m_coord_system_width, m_coord_system_height;
    
public:
    
    /**
      * @param page_amount    number of pages we want to print. Here it's static because it's just a test, but in
      *                       real code you will often want to calculate dynamically once you know the size of the
      *                       printable area from page setup
      * @param title          name of the print job / of the printed document
      * @param units_per_cem  we'll use this to determine the coordinate system; it describes the number of units
      *                       per centimeter (i.e. how fine the coordinate system is)
      */
    QuickPrint(int page_amount, wxString title, float units_per_cm) : wxPrintout( title )
    {
        m_page_amount = page_amount;
        
        m_orient = wxPORTRAIT; // wxPORTRAIT, wxLANDSCAPE
        m_paper_type = wxPAPER_LETTER;
        m_margin_left   = 16;
        m_margin_right  = 16;
        m_margin_top    = 32;
        m_margin_bottom = 32;
        
        m_units_per_cm   = units_per_cm;
    }
    
    
    /** shows the page setup dialog, OR sets up defaults */
    bool performPageSetup(const bool showPageSetupDialog)
    {
        // don't show page setup dialog, use default values
        wxPrintData printdata;
        printdata.SetPrintMode( wxPRINT_MODE_PRINTER );
        printdata.SetOrientation( m_orient );
        printdata.SetNoCopies(1);
        printdata.SetPaperId( m_paper_type ); 
        
        m_page_setup = wxPageSetupDialogData(printdata);
        m_page_setup.SetMarginTopLeft    (wxPoint(m_margin_left,  m_margin_top));
        m_page_setup.SetMarginBottomRight(wxPoint(m_margin_right, m_margin_bottom)); 
        
        if (showPageSetupDialog)
        {
            wxPageSetupDialog dialog( NULL, &m_page_setup );
            if (dialog.ShowModal() == wxID_OK)
            {
                
                m_page_setup = dialog.GetPageSetupData();
                m_orient = m_page_setup.GetPrintData().GetOrientation();
                m_paper_type = m_page_setup.GetPrintData().GetPaperId();
                
                wxPoint marginTopLeft = m_page_setup.GetMarginTopLeft();
                wxPoint marginBottomRight = m_page_setup.GetMarginBottomRight();
                m_margin_left   = marginTopLeft.x;
                m_margin_right  = marginBottomRight.x;
                m_margin_top    = marginTopLeft.y;
                m_margin_bottom = marginBottomRight.y;
            }
            else
            {
                std::cout << "user canceled at first dialog" << std::endl;
                return false;
            }
        }
        return true;
    }
    
    /** Called when printing starts */
    void OnBeginPrinting()
    {
        // set-up coordinate system however we want, we'll use it when drawing
        
        // take paper size and margin sizes into account when setting up coordinate system
        // so that units are "square" (1 unit x is a wide as 1 unit y is high)
        // (actually, if we don't make it square, on some platforms wx will even resize your
        //  margins to make it so)
        wxSize paperSize = m_page_setup.GetPaperSize();  // in millimeters
        
        // still in millimeters
        float large_side = std::max(paperSize.GetWidth(), paperSize.GetHeight());
        float small_side = std::min(paperSize.GetWidth(), paperSize.GetHeight());
        
        float large_side_cm = large_side / 10.0f;  // in centimeters
        float small_side_cm = small_side / 10.0f;  // in centimeters
        
        if (m_orient == wxPORTRAIT)
        {
            float ratio = float(large_side - m_margin_top  - m_margin_bottom) /
                          float(small_side - m_margin_left - m_margin_right);
            
            m_coord_system_width  = (int)((small_side_cm - m_margin_left/10.f -
                                           m_margin_right/10.0f)*m_units_per_cm);
            m_coord_system_height = m_coord_system_width*ratio;
        }
        else
        {
            float ratio = float(large_side - m_margin_left - m_margin_right) /
                          float(small_side - m_margin_top  - m_margin_bottom);
            
            m_coord_system_height = (int)((small_side_cm - m_margin_top/10.0f -
                                           m_margin_bottom/10.0f)* m_units_per_cm);
            m_coord_system_width  = m_coord_system_height*ratio;
            
        }

    }
    
    /** returns the data obtained from the page setup dialog (or the defaults,
     * if dialog was not shown) */
    wxPrintData getPrintData()
    {
        return m_page_setup.GetPrintData();
    }
    
    /** Called when starting to print a document */
    bool OnBeginDocument(int startPage, int endPage)
    {
        std::cout << "beginning to print document, from page " << startPage
                  << " to " << endPage << std::endl;
        return wxPrintout::OnBeginDocument(startPage, endPage);
    }
    
    /** wx will call this to know how many pages can be printed */
    void GetPageInfo(int *minPage, int *maxPage, int *pageSelFrom, int *pageSelTo)
    {
        *minPage = 1;
        *maxPage = m_page_amount;
        
        *pageSelFrom = 1;
        *pageSelTo = m_page_amount;
    }
    
    /** called by wx to know what pages this document has */
    bool HasPage(int pageNum)
    {
        // wx will call this to know how many pages can be printed
        return pageNum >= 1 && pageNum <= m_page_amount;
    }
    
    
    /** called by wx everytime it's time to render a specific page onto the
     * printing device context */
    bool OnPrintPage(int pageNum)
    {
        std::cout << "printing page " << pageNum << std::endl;
        
        // ---- setup DC with coordinate system ----
        FitThisSizeToPageMargins(wxSize(m_coord_system_width, m_coord_system_height), m_page_setup);
        
        wxRect fitRect = GetLogicalPageMarginsRect(m_page_setup);
        
        wxCoord xoff = (fitRect.width - m_coord_system_width) / 2;
        wxCoord yoff = (fitRect.height - m_coord_system_height) / 2;
        OffsetLogicalOrigin(xoff, yoff);
        
        wxDC* ptr = GetDC();
        if (ptr==NULL || !ptr->IsOk())
        {
            std::cout << "DC is not Ok, interrupting printing" << std::endl;
            return false;
        }
        wxDC& dc = *ptr;
        
        // ---- A couple helper variables to help us during draw within paper area -----
        const int x0 = 0;
        const int y0 = 0;
        const int width = m_coord_system_width;
        const int height = m_coord_system_height;
        const int x1 = x0 + width;
        const int y1 = y0 + height;
        
        const int center_x = x0 + width/2;
        const int center_y = y0 + height/2;
        
        std::cout << "printable area : (" << x0 << ", " << y0 << ") to ("
        << x1 << ", " << y1 << ")" << std::endl;
        
        // ---- Draw to the print DC ----
        dc.Clear();
        
        dc.SetPen(  wxPen( wxColour(0,0,0), brush_size ) );
        dc.SetBrush( *wxTRANSPARENT_BRUSH );
        
        // draw a rectangle to show its bounds.
        dc.DrawRectangle(x0, y0, width, height);
        
        // draw wxWidgets logo
        dc.SetBrush( *wxRED_BRUSH );
        dc.DrawRectangle(center_x-45-38, center_y, 76, 76);
        dc.SetBrush( *wxBLUE_BRUSH );
        dc.DrawRectangle(center_x-38, center_y-45, 76, 76);
        dc.SetBrush( wxBrush( wxColor(255,244,0) ) );
        dc.DrawRectangle(center_x+45-38, center_y-10, 76, 76);
        
        // draw page number label
        wxString label( wxT("This is page #") );
        label << pageNum;
        dc.SetTextBackground( wxColour(255,255,0) );
        dc.SetTextForeground( wxColour(0,0,0) );
        dc.DrawText( label, x0 + width/5, y0 + height - 50 );
        
        return true;
    }  
    
    /** Called when printing is done. I have nothing to do in this case
     * in this example. */
    void OnEndPrinting()
    {
    }
};

// -------------------------------------------------------------------------------------
//                                HOW TO INVOKE THIS TEST
// -------------------------------------------------------------------------------------

class MyApp: public wxApp
{
public:
    virtual bool OnInit();
};


IMPLEMENT_APP(MyApp)


bool MyApp::OnInit()
{
    QuickPrint*  myprint = new QuickPrint( 5 /* 5 pages */,
                                           wxT("wxPrint test"),
                                           30 /* 30 units per centimeter */ );
    if (!myprint->performPageSetup(true))
    {
        // user cancelled
        exit(0);
    }
    
    wxPrintDialogData data(myprint->getPrintData());
    wxPrinter printer(&data);
    const bool success = printer.Print(NULL, myprint, true /* show dialog */);
    
    delete myprint;
    
    if (!success)
    {
        std::cerr << "Failed!!\n";
        return false;
    }
    exit(0);
    return true;
}


The question is often asked : how can I print a component from my frame? Sorry, there's no automagical way to do this. Use the HTML easy printing class mentioned above, or another third-party class supporting printing, or draw what you want on the DC.