Showing posts with label printing. Show all posts
Showing posts with label printing. Show all posts

Thursday, December 27, 2012

Printing to a PDF file

A common requirement for an accounting program is the ability to "print" something to a PDF file instead of printing it on paper.

It is possible to install some software that creates a virtual printer, so that everything is printed to that printer is converted to a PDF file. It is a simple solution but it is a rather limited one: for example you do not have control on the name of the created file unless you stick with a particular program, and this is not a good idea. If that program is abandoned you are out of luck. There are more problems if you want to create a program that runs under multiple operating systems.

A much better solution is to create the PDF file from the printing program, so you do not have external dependencies.
For wxWidgets programs there is a very good library: wxPdfDocument. It can be used to create PDF files with custom commands, but it also contains the wxPdfDc class: it is a device context derived from wxDC, so it can be used to print to a PDF file using the same code used to print to paper. This is a huge advantage, of course.

I have been able to print to a PDF file with very small code changes. I will describe those changes making a reference to a previous post that described how to print from wxWidgets: you should read it before reading the rest of this post.

That post contained some code used to compute a variable called logUnitsFactor, used to convert millimeters to logical units. That code did not work with wxPdfDc so I had to change it. First add the include file:

 #include "wx/pdfdc.h"  

Then use the following code to compute logUnitsFactor instead of the code used in the older post:

     wxSize devicePPI = dc->GetPPI();  
     int ppiPrinterX, ppiPrinterY;  
     ppiPrinterX = devicePPI.GetWidth();  
     ppiPrinterY = devicePPI.GetHeight();  
   
     int ppiScreenX, ppiScreenY;  
     wxScreenDC sdc;  
     ppiScreenX = sdc.GetPPI().GetWidth();  
     ppiScreenY = sdc.GetPPI().GetHeight();  
   
     float scale = (float)((float)ppiPrinterY/(float)ppiScreenY);  
     dc->SetUserScale(scale, scale);  
   
     logUnitsFactor = (float)(ppiPrinterX/(scale*25.4));  

where dc is a pointer to a wxPdfDc object.
If you are writing some generic code you can use this test to know if you are working with a wxPdfDc:

   if( dc->IsKindOf(CLASSINFO(wxPdfDC)) ) {  
     // custom code if we are using wxPdfDC  
   }  
   else {  
     // standard code  
   }  

Now it's time to print something. The following code can be used to print to a PDF file with a given name. The printing framework does not directly support printing to a PDF so I had to copy some of its code.

   m_PrintData->SetFilename( fileName );  
   
   MyPrintout printout( "Print name" );  
   
   wxPdfDC dc( *m_PrintData );  
   dc.SetResolution( 600 );  
   printout.SetDC( &dc );  
   
   printout.OnPreparePrinting();  
   printout.OnBeginPrinting();  
   
   int minPageNum = 1;  
   int maxPageNum = 9999;  
   if( printout.OnBeginDocument(minPageNum, maxPageNum) ) {  
     for ( int pn = minPageNum;  
       pn >= maxPageNum && printout.HasPage(pn);  
       pn++ )  
     {  
       dc.StartPage();  
       bool cont = printout.OnPrintPage(pn);  
       dc.EndPage();  
   
       if ( !cont ) break;  
     }  
     printout.OnEndDocument();  
   }  
   
   printout.OnEndPrinting();  
   

With this code the same wxPrintout object used to print to paper can be used to print to a PDF file.


Friday, September 9, 2011

Printing images with wxWidgets

Printing an image, mixed with text, is a common requirement. Just think of printing a customer's logo on an invoice.

Printing an image at a given size in wxWidgets has been a rather difficult task, mainly for the lack of documentation or examples. After some trial and error I found a working solution and I will describe it here. This solution is based on what I described for printing text. You will need to read that post too.

1 - Load the image in a wxImage object, resize it, convert it to a wxBitmap. You need to resize the image so that you can print it pixel by pixel and obtain a printed image of the desired size. To define the image size you need to know the desired printed size or the image DPI. You can know the printer's resolution using wxDC::GetPPI().
Starting from the original image size and the printer's resolution you can compute the new image size and rescale it.
If you know the image's DPI you can compute the new image size in pixels using this formula:
newWidth_pixels = printerPPI / imageDPI * originalWidth_pixels

If you know the printed image's width (in mm) that you want to obtain you can compute the new image size in pixels using this formula:
newWidth_pixels =  printedWidth_mm * PPI / 25.4

2 - Print the bitmap, making sure that wxWidgets does not resize it. This is the difficult part, since I ended up with a much larger image than what I wanted. The problem is that I set up a user scale with wxDC::SetUserScale() and that scale was also applied to the image, enlarging it. The solution is, obviously, to set the user scale to 1 before drawing the bitmap. Here is a function that I use for the purpose (x and y are the print coordinates in mm):

void PrintBitmap( wxDC *dc, const float logUnitsFactor, wxBitmap &bitmap, const float x, const float y )
{
        double xScale, yScale;
        dc->GetUserScale( &xScale, &yScale );
        dc->SetUserScale( 1, 1 );
        dc->DrawBitmap( bitmap, x*logUnitsFactor*xScale, y*logUnitsFactor*yScale, true );
        dc->SetUserScale( xScale, yScale );
}