Click here to Skip to main content
15,881,559 members
Articles / Desktop Programming / MFC

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

Rate me:
Please Sign up or sign in to vote.
4.75/5 (13 votes)
15 Sep 2011CPOL5 min read 76.7K   10K   59   10
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:

  1. Data set Input - This is done by using a object DataSetObj which gets the input data in the form of cstring from a cedit box. The obtained CString 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 object PlotObj.
  2. 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,and isPrintAsPDFCalled.
  3. 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:

C++
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.

C++
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 Printer
  • PrintAsPDF - 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:

C++
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.

C++
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.

  1. First, we create a new PDF document by using HPDF_New function call.
  2. Then Add a PDF page by using a HPDF_AddPage adds a page to the PDF document.
  3. HPDF_LoadPngImageFromFile loads the Temp.png created earlier into the HPDF_image (image variable in the above code).
  4. HPDF_Page_DrawImage then draws the loaded image into Page.
  5. 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.

C++
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 the Dc has to be printed.
  • Then the code...
    C++
    dev->dmOrientation = DMORIENT_LANDSCAPE;
    
    dev->dmPaperSize = DMPAPER_A4;
  • ...is used to change the device orientation from portrait to landscape mode.

  • 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 passed MemDC is then copied into the printing document by using stretchblt function call.

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

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
India India
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
Questionx64 version requested. Pin
kikiella19-Jan-20 16:08
kikiella19-Jan-20 16:08 
QuestionWonderful Pin
shaima'3-Jan-15 7:41
shaima'3-Jan-15 7:41 
Generalthe images are gray Pin
c_srishti20-Jul-12 9:15
c_srishti20-Jul-12 9:15 
AnswerRe: the images are gray Pin
Moh'd Shakeb Baig24-Aug-12 1:09
Moh'd Shakeb Baig24-Aug-12 1:09 
QuestionHanging issues when using code Pin
Member 83939209-Apr-12 8:16
Member 83939209-Apr-12 8:16 
AnswerRe: Hanging issues when using code Pin
abhishek.biradar17-Apr-12 23:17
abhishek.biradar17-Apr-12 23:17 
AnswerRe: Hanging issues when using code Pin
shaima'3-Jan-15 7:47
shaima'3-Jan-15 7:47 
QuestionOlder project Pin
_Flaviu16-Sep-11 22:42
_Flaviu16-Sep-11 22:42 
AnswerRe: Older project Pin
abhishek.biradar17-Sep-11 22:48
abhishek.biradar17-Sep-11 22:48 
GeneralRe: Older project Pin
_Flaviu18-Sep-11 9:12
_Flaviu18-Sep-11 9:12 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.