Converting Device context to images ( PNG , JPEG , BMP , GIF) , Creating PDF & Printing to the Printer






4.75/5 (12 votes)
In this project, the displayed device context can be converted to images Using Cimage class, Creating PDF using Haru PDF Library and simple code for printing the Device context
Introduction
The source code can be used for creating various formats of images from the device context displayed. The application also uses the Haru PDF library which can be used for creating the PDF output without the need of a virtual printer. And also, this has an option for printing the presently displayed device context to printer as well.
This project uses the CImage
class for creating the images which provides various methods for handling images and creating images in different formats, which is far more easier when compared to creating or converting the device context to images manually. This application also provides the option of creating the PDF files using HARU PDF library.
Using the Code
The source code mainly has three parts:
- Data set Input - This is done by using a object
DataSetObj
which gets the input data in the form ofcstring
from acedit
box. The obtainedCString
is then tokenized, so as to obtain the numeric values of the entered data. The numeric values are then used for plotting of graph from Plotting objectPlotObj
. - Main Screen - The main screen handles the messaging of the action that has to be done by the plotting unit. The messages are sent to the Plotting unit in the form of boolean variables like
isPrintFromPrinterCalled
,isSaveAsImageCalled
,andisPrintAsPDFCalled
. - Plotting unit - This unit handles all the action like plotting, printing, saving as images, saving as PDF, etc.
Input Data set takes the Input numeric values using a Edit box, the values in the edit box are read in the form of cstring
and the cstring
is then tokenized using the following code:
resToken= DataSetString.Tokenize(L"\n ,",curPos);
while (resToken != "")
{
s32Temp = _ttoi(resToken);
if(s32Temp == 0 /*&& resToken.Compare(L"0") == 0*/)
{
PlotObj.PlotPoints.Add(s32Temp);
}
else if(s32Temp != 0)
{
PlotObj.PlotPoints.Add(s32Temp);
}
resToken= DataSetString.Tokenize(L"\n ,",curPos);
}
In the above code, the DataSetString
is a CString
which holds the numeric values of plot points, the tokenize function will break the string
into tokens based on the delimiter as in our case the delimiters are "\n
"," " &",
". The obtained tokens are then converted to integers by using _ttoi
function.
The obtained integer values are then continuously updated into the CArray
.
Hence the numeric values can be separated by new line or space or a comma. If any non-numeric value is added to the edit box, it is taken a zero.
The Plotting unit is a picture control which is converted into a sub static window using the createex
function call. So the Plotting unit is a child window of the main screen.
The following source code is added in the on paint of the plotting unit.
void CPlot::OnPaint()
{
CPaintDC dc(this); // device context for painting
// TODO: Add your message handler code here
// Do not call CDialogEx::OnPaint() for painting messages
GetClientRect(ClientRect);
dc.FillSolidRect(&ClientRect, RGB(255,255,255));
if(PlotPoints.GetCount() > 2){
GetMax();
GetMin();
CString Remove;
DrawLines(dc,ClientRect);
}
else
{
dc.TextOutW(ClientRect.Width()/4,0,L"Please enter
The Integer Data by clicking on the \"enter data set\" button");
}
GetParent()->SendMessage(WM_COMMAND,0,0);
if(isSaveAsImageCalled)
{
isSaveAsImageCalled =false;
SaveAsImage(dc);
}
if(isPrintFromPrinterCalled)
{
isPrintFromPrinterCalled = false;
PrintToPrinter(dc);
}
if(isPrintAsPDFCalled)
{
isPrintAsPDFCalled = false;
PrintAsPDF(dc);
}
In the above code, we first fill the background color with solidfillrect
and passing clientrect
(dimension of the child window) and color as white.
Since at least two points are required for drawing a line, I have added a condition that if number of points are less than 2, no plotting has to be done.
The plotting of graph is done by a function called DrawLines
it just takes the maximum and minimum point from the carray
and marks them as the max peak and min peak of the plotting unit along Y axis. The code handles plotting any number of points along x axis, hence multiple points may be drawn on a single x coordinate.
The following functions:
SaveAsImage
- saves the DC passed to Image.PrintToPrinter
- prints the DC Passed to PrinterPrintAsPDF
- converts the DC to PDF using Haru PDF.
When user click on any buttons in the main screen, the boolean variables are set and the plotting unit is redrawn so that the onpaint
gets called once the current displayed Device context is sent to the function for which boolean variable is set.
If save as image is clicked, the boolean variable IsSaveAsImageCalled
is set. Correspondingly, the following function is called:
void CPlot::SaveAsImage(CDC & dc)
{
CString FileFormatSelection;
FileFormatSelection.Format(_T("Please add extensions at the end of file name
(*.png,*.gif,*.jpeg,*.bmp)|*.png; *.gif; *.jpeg; *.bmp|"));
CFileDialog FileDialog(FALSE,NULL,NULL,OFN_OVERWRITEPROMPT,FileFormatSelection);
FileDialog.DoModal();
if(FileDialog.GetPathName().GetLength() <= 0)
{
return;
}
else
{
CDC MemDC;
MemDC.CreateCompatibleDC(&dc);
CBitmap Bmp;
Bmp.CreateCompatibleBitmap(&dc,ClientRect.Width(),ClientRect.Height());
MemDC.SelectObject(&Bmp);
MemDC.BitBlt(0,0,ClientRect.Width(),
ClientRect.Height(),&dc,0,0,SRCCOPY);
CImage TempImageObj;
TempImageObj.Attach((HBITMAP)Bmp.Detach());
TempImageObj.Save(FileDialog.GetPathName());
}
}
In the above code, the CFileDialog
gets the File Path where the file has to be saved.
Then a Temporary Device Context is created and the passed DC is copied into the Temporary device context by using BitBlt
function call.
Then this temporary DC is copied into the CImage object. The copying of dc
to CImage object is done by using attach
function, which takes the handle to the Bitmap
of the dc
to be copied.
Once the Bitmap
is copied to the CImage object, the save function will save the dc
to an image. The format of the image depends on the extension of the image that is given in the file save dialog. The supported formats include PNG, BMP, GIF and JPEG format.
If save as PDF is selected, the DC is first converted into a temporary PNG image as discussed above and then using Haru PDF, the PNG image is converted to a PDF file.
int CPlot::PrintAsPDF(CDC &dc)
{
CString FileFormatSelection;
FileFormatSelection.Format(_T("supported Formats (*.pdf)|*.pdf|"));
CFileDialog FileDialog(FALSE,NULL,NULL,OFN_OVERWRITEPROMPT,FileFormatSelection);
FileDialog.DoModal();
if(FileDialog.GetPathName().GetLength() <= 0)
{
return 1;
}CDC MemDC;
MemDC.CreateCompatibleDC(&dc);
CBitmap Bmp;
Bmp.CreateCompatibleBitmap(&dc,ClientRect.Width(),ClientRect.Height());
MemDC.SelectObject(&Bmp);
MemDC.BitBlt(0,0,ClientRect.Width(),ClientRect.Height(),&dc,0,0,SRCCOPY);
CImage TempImageObj;
TempImageObj.Attach((HBITMAP)Bmp.Detach());
TempImageObj.Save(L"Temp.png");
HPDF_Doc pdf;
HPDF_Font font;
HPDF_Page page;
char fname[256];
HPDF_Image image;
static int var = 0;
HPDF_REAL x, y;
CStringToChar(FileDialog.GetPathName(),fname);
strcat(fname,".pdf");
pdf = HPDF_New (error_handler, NULL);
if (!pdf) {
AfxMessageBox(L"error: cannot create PdfDoc object 1\n",0,0);
return 0;
}
/* error-handler */
if (setjmp(env)) {
AfxMessageBox(L"error: cannot create PdfDoc object 2\n",0,0);
HPDF_Free (pdf);
return 0 ;
}
HPDF_SetCompressionMode (pdf, HPDF_COMP_ALL);
/* create default-font */
font = HPDF_GetFont (pdf, "Helvetica", NULL);
/* add a new page object. */
page = HPDF_AddPage (pdf);
HPDF_Page_SetWidth (page, ClientRect.Width());
HPDF_Page_SetHeight (page, ClientRect.Height()*2);
image = HPDF_LoadPngImageFromFile (pdf,"Temp.png");
x = 0;
y = 0;
/* Draw image to the canvas. (normal-mode with actual size.)*/
HPDF_Page_DrawImage (page, image, x, y, ClientRect.Width(), ClientRect.Height()*2);
/* save the document to a file */
HPDF_SaveToFile (pdf, fname);
/* clean up */
HPDF_Free (pdf);
CFile::Remove(L"Temp.png"); }
In the above code, the CFileDialog
gets the path where the PDF has to be saved and then creates a temporary image file Temp.png.
- First, we create a new PDF document by using
HPDF_New
function call. - Then Add a PDF page by using a
HPDF_AddPage
adds a page to the PDF document. HPDF_LoadPngImageFromFile
loads the Temp.png created earlier into theHPDF_image
(image variable in the above code).HPDF_Page_DrawImage
then draws the loaded image into Page.HPDF_SaveToFile
saves the PDF into a file name as given by the user.
Then the Temp Image File Temp.png is removed by using CFile::Remove
function call.
The usage of Haru PDF can be found at Haru PDF Functions and Haru PDF examples.
The printing to the device context can be done using the simple source code.
void CPlot::PrintToPrinter(CDC &memDC)
{
CPrintDialog printDlg(FALSE);
printDlg.GetDefaults(); //get the default printer settings
UINT page;
printDlg.m_pd.Flags &= ~PD_RETURNDEFAULT;
LPDEVMODE dev = printDlg.GetDevMode();
GlobalUnlock(dev);
//set to landscape mode.
dev->dmOrientation=DMORIENT_LANDSCAPE;
//set the paper size
dev->dmPaperSize=DMPAPER_A4;
CDC dc;
//attach to the printer device context for printing
if (!dc.Attach(printDlg.GetPrinterDC()))
{
AfxMessageBox(L"NO Printer Found");
return;
}
//change the setting of the printing features.
//the reset is necessary so that the modification for the graph
//are reflected ie change to landscape mode
dc.ResetDC(dev);
dc.m_bPrinting = TRUE;
DOCINFO di;
// Initialise print document details
::ZeroMemory (&di, sizeof (DOCINFO));
di.cbSize = sizeof (DOCINFO);
di.lpszDocName = L"Test Print";
BOOL bPrintingOK = dc.StartDoc(&di); // Begin a new print job
// Get the printing extents
// and store in the m_rectDraw field of a
// CPrintInfo object
CPrintInfo Info;
Info.SetMaxPage(1); // just one page
double f64MaxwInPixels = dc.GetDeviceCaps(HORZRES);
double f64MaxhInPixels = dc.GetDeviceCaps(VERTRES);
double f64MaxwInMillimeters = dc.GetDeviceCaps(HORZSIZE);
double f64MaxhInMillimeters = dc.GetDeviceCaps(VERTSIZE);
double f64OneMM2Pixel_width = f64MaxwInPixels/f64MaxwInMillimeters;
double f64OneMM2Pixel_height = f64MaxhInPixels/f64MaxhInMillimeters;
int __w64 maxw = f64OneMM2Pixel_width*240;
int __w64 maxh = f64MaxhInPixels;
Info.m_rectDraw.SetRect(0, 0, maxw, maxh);
for (page = Info.GetMinPage(); page <=
Info.GetMaxPage() && bPrintingOK; page++)
{
dc.StartPage(); // begin new page
Info.m_nCurPage = page;
memDC.SetMapMode(dc.GetMapMode());
dc.SetStretchBltMode(HALFTONE);
// now stretchblt to maximum width on page
dc.StretchBlt(0, 0, maxw, maxh , &memDC, 0, 0,
ClientRect.Width(), ClientRect.Height(), SRCCOPY);
bPrintingOK = (dc.EndPage() > 0); // end page
}
if (bPrintingOK)
dc.EndDoc(); // end a print job
else
dc.AbortDoc(); // abort job.
}
In the above code, the:
CPrintDialog
is used for creating Print dialog which is used for getting the printer configuration.printDlg.GetDefaults()
gets the default configuration of the printer on which theDc
has to be printed.- Then the code...
dev->dmOrientation = DMORIENT_LANDSCAPE; dev->dmPaperSize = DMPAPER_A4;
- The Printer device context is then attached to a temp Device context (
dc
in code above). dc.ResetDC()
has to be done to reflect the changes for the orientation and the size of paper.dc.startdoc()
begins or creates a printing document. The passedMemDC
is then copied into the printing document by usingstretchblt
function call.
...is used to change the device orientation from portrait to landscape mode.
Once the device context is copied into the printing document, dc.EndDoc()
will trigger the end of printing document modification and send the document to the printer.
In the above code, maxw
and maxh
can be modified so that the output print can be obtained of required scale (like 100 mm or 200 mm, etc.).
History
- 15th September, 2011: Initial post