Export / Print to PDF in C++

Hi,

I asked this question before, but didnt get any answer so I hope the second try will be successful. I want to export/print layouts automatically (in a loop) to PDFs. With Dale's help I am exploring CRhinoPageView class in order to create the layouts, and that is going fine, but I still dont know how to print them.

My best guesses involve using CRhino(Ui)Dib (somehow) and CRhinoPrintInfo to set the parameters for printing to PDF…but those are just guesses and I don`t know how to combine them…any suggestions?

Thanks!

Milos

CRhinoPrintInfo is the class that you are looking for.

Thanks,

but can you give me couple of more hints…like what functions are used to set PDF as output and what to actually print? I know RhinoDib has WriteToFile or DrawToDC…what is the proper function here? DrawWithGDI?

You need to get a device context (CDC) for a PDF printer driver.
https://msdn.microsoft.com/en-us/library/dbt1s7bw.aspx

Once you have a CDC, you would call UpdateFromPrinterDC to get the print info page sizes all structured correctly. Then you would call DrawWithGDI if you want vector output to the PDF. Otherwise you would draw to a bitmap (CRhinoDib) and then dump that bitmap into a PDF.

Unfortunately this requires quite a bit of code to get set up correctly and I don’t have any samples immediately on hand. An alternate approach could be to use the dashed version of the print command.

How about if I set all the options manually and prepare them for my “batch printing” by printing a test PDF? I guess that information is saved? Then if I called DrawWithGDI() the CDisplayPipelineAttributes should already be set?

P.S. I know that it is out of the Rhino scope, but I cannot find the easiest way to get a CDC for a PDF printer…if you have any tips on how to get that I would appreciate it…is there a way to use one of the Rhino Dialog classes to open some dialog and select the printer, hence getting a CDC?

Thanks a lot!

Milos

To answer my own last question…I think that PrintDlg is the answer…unfortunately I tried it and nothing happens…I start the command and nothing…although theoretically a print dialog should open :

PRINTDLG pd;
HWND hwnd;

// Initialize PRINTDLG
ZeroMemory(&pd, sizeof(pd));
pd.lStructSize = sizeof(pd);
pd.hwndOwner   = hwnd;
pd.hDevMode    = NULL;     // Don't forget to free or store hDevMode.
pd.hDevNames   = NULL;     // Don't forget to free or store hDevNames.
pd.Flags       = PD_USEDEVMODECOPIESANDCOLLATE | PD_RETURNDC; 
pd.nCopies     = 1;
pd.nFromPage   = 0xFFFF; 
pd.nToPage     = 0xFFFF; 
pd.nMinPage    = 1; 
pd.nMaxPage    = 0xFFFF; 

	if (PrintDlg(&pd)==TRUE) 
	{
		// GDI calls to render output. 

		// Delete DC when done.
		DeleteDC(pd.hDC);
	}*/

I tried PrintDlgEx and PageSetupDlg too and nothing happens…is Rhino somehow stopping these dialogs from opening or am I missing something?

I am sorry, but I have to ask again because I want to explore all the possibilities I have, and the one in which I get the printer CDC by calling the PrintDlg looks like the most promising one. So, the question is if you maybe know why doesn`t anything happen after calling PrintDlg (the code is above)? Is the dialog somehow blocked? Like when RunScript cannot be called from a CRhinoCommand derived command? (I tried deriving the command from CRhinoScriptCommand but PrintDlg still does nothing.)

The other possibility was calling EnumPrinters function to find the name of the printer and then CreateDC…but not sure how to do that either, cause EnumPrinters function is not recognized…

The answer “we don`t know”, or “it is too complicated to explain” is also totally acceptable, so I know I have to continue to search on my own…but any hint would help.

Thanks!

Milos

We definitely don’t block the native Win32 printdlg in any way. Core Rhino uses the EnumPrinters and CreateDC technique.

Hi,

I managed to get the printer CDC (using PRINTDLG and PrintDlg()) but no success yet…nothing happens after I call:

CRhinoPrintInfo print;
print.UpdateFromPrinterDC(cdc, true );
print.DrawWithGDI( cdc, pAttributes, true );

If you have any idea at least how can I move from this point on I would appreciate it.

And if I decide to give up (which will be a big disappointment) and try with the dashed version of the print command, how can I “hit Print” after the dialog is open? A simple:

RhinoApp().RunScript( L"_Print _Enter" );

still only opens the dialog…but if I want to automate the process I want the “print” to be hit automatically too…

Thanks!

You probably have to call CDC::StartDoc, StartPage, EndDoc, EndPage around your call to DrawWithGDI

Thanks, we are almost there!

So I got the CDC with PrintDlg(&pd), and I called StartDoc - EndDoc functions around the DrawWithGDI. The result is that I managed to print automatically to the chosen device, BUT it prints only a white A4 paper. At what point am I missing to pass the actual layout (or any CRhinoView) to DrawWithGDI? Because I am obviously not doing it anywhere and I have to give CRhinoPrintInfo somehow the information on what to print. (The DOCINFO part must be wrong…) The code:

CDC cdc;
CRhinoPrintInfo print;
const CDisplayPipelineAttributes* pAttributes = NULL;

PRINTDLG pd = {0};
pd.lStructSize = sizeof( pd );
pd.Flags = PD_RETURNDC;

// Retrieve the printer DC                                             
if (PrintDlg(&pd)==TRUE)                                               
{                                                                      
	CDC *pDC = cdc.FromHandle( pd.hDC );      
	                                                         
	DOCINFO di = {0};                                              
	di.cbSize = sizeof(DOCINFO);                                   
	di.lpszDocName = _T("Scribble Printout");                      
	di.lpszOutput = (LPTSTR) NULL;                                 
	di.lpszDatatype = (LPTSTR) NULL;                               
	di.fwType = 0;                                                 
                                                                           
         	// Begin a print job by calling the StartDoc function.         
         StartDoc(pd.hDC, &di); //  pDC->StartDocW( di.lpszDocName );  
             StartPage( pd.hDC);  //  	pDC->StartPage();              
                                                                           
	    printinfo.UpdateFromPrinterDC( *pDC, true );               
                printinfo.DrawWithGDI( *pDC, pAttributes, true );          
                                                                           
	 EndPage(pd.hDC); // pDC->EndPage();                           
             EndDoc(pd.hDC ); //pDC->EndDoc();                             
			// Delete DC when done.                        
			DeleteDC(pd.hDC);                              
}

Since I am getting no answer I will try to ask more concisely:

When we draw with DrawToDC or RenderToDC we easily tell Rhino what to draw with the use of CRhinoObjectIterator. When I use CRhinoPrintInfo::DrawWithGDI how am I telling Rhino what to draw? I am only giving it the context device. If the answer is that the whole display pipeline gets drawn, then we are back to the old question:

Once I surround my UpdateFromPrinter and DrawWithGDI functions with StartDoc, StartPage, EndPage, EndDoc, they just get ignored. An empty document is generated by the 4 Start/End functions, as if two CRhinoPrintInfo functions don`t exist. And it makes sense that the paper is empty, because (in my bad example above) those functions get only the device context as an input, and for what to print, they get the empty DOCINFO document.

How do I connect the 4 Start/End functions and DrawWithGDI so that it works? I feel that i am only one tip away from solving this…

Thanks!

Milos

Hi Milos,
Sorry for not getting back to you. I believe the missing piece that you need is a call to CRhinoPrintInfo::SetViewport. This is what the print info uses as it’s base for what to print.

Hi Steve,

I have to apologize, but I will not give up until I manage to to this :slight_smile: That being said…

First problem is that I am not sure how to get the CRhinoViewport needed for CRhinoPrintInfo::SetViewport (?) (all examples show only how to get CRhinoView, but not CRhinoViewport - what is their relation?),

Second (main) problem is that CRhinoPrintInfo::DrawWithGDI does nothing…it does not evoke any action. You said to surround it with StartDoc-EndDoc functions. When I do, they (logically) work totally independently of CRhinoPrintInfo and print an empty page, defined by DOCINFO. The main question is how do I connect those two?

I could print with DrawWithGDI and ignore Start/End functions - but this does not work.
I can print with Start/End functions but I am missing a link to my CPrintInfoClass. How do I connect them? How do I pass CRhinoPrintInfo details to DOCINFO or to Start/End functions?

I cannot find the basic information online or in RhinoDocs that can help me understand how this works. You basically only have to tell me what Rhino does after the the user presses “Print” in the Print Setup dialog. Does it call DrawWithGDI? Does it call Start/End functions?

A CRhinoView is the top level window itself. For standard modeling views, a CRhinoView will contain a single CRhinoViewport which covers the entire CRhinoView’s child window area. For layouts, a CRhinoView can contain a single “MainViewport” and multiple CRhinoViewports that represent each detail on the layout. Just use the MainViewport function on a CRhinoView to get the viewport that you want to use.

You absolutely need to tell the CRhinoPrintInfo which CRhinoViewport to print. This is probably why you are getting blank output.

Unfortunately, that is a massive amount of code that spreads across several files. I would have just posted the code if it were easy to do.

OK Steve,

thanks…I`ll try to solve it… just tell me how do I access RhinoView-RhinoViewport, because whenever I call MainViewport, ActiveViewport, or try accessing m_vp directly it tells me it is inaccessible…even though, as I see, these classes are friends. ?

Thanks,
Milos

CRhinoView* view = RhinoApp().ActiveView();
if( nullptr == view )
  return;

CRhinoViewport& vp = view->MainViewport();
CRhinoPrintInfo info;
...
info.SetViewport(vp.ViewportId());

Thanks Steve,

you were right. It actually worked. I will try to continue on my own now as much as I can…I know I am slowly becoming very boring but i might have couple of additional questions along the way. For example, I want to set the printing options now (page layout, etc…) I see two ways of doing this:

  1. The easiest way would be to set everything manually, print something and somehow evoke those last settings for every new printing job. Is this possible? At the moment this doesn`t work… The things I print with DrawWithGDI always have some of their own (default?) settings.

  2. If this is not possible, I have to set everything manually…by setting CRhinoPrintInfo members…in this case the most important question is where the page layout setting is? (Landscape/Portrait, Paper size…)

Thanks!
Milos