添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
PLEASE NOTE: The printing subsystem has been optimized. Please find details here: http://forum.patagames.com/posts/m431-Print-functionality-in-Pdfium--NET-SDK#post431

Question:
I'm currently working on a C# application that uses PDF render features for my employer. Pdfium.Net does an excellent job of displaying the PDFs but the app must be able to print them as well. Anybody know a way to print the current PDF document through this API? I've checked in the likely places and I haven't found anything.

Answer:
To print a PDF document, you can use standard .Net Framework, like shown in the code below:

Code:

public void PrintDocument(PdfDocument pdf)
	//.Net Framework class from System.Drawing.Printing namespace
	PrintDocument pd = new PrintDocument();
	int pageForPrint = 0;
	pd.PrintPage += (s, e) =>
		//set paper size and orientation
		e.PageSettings.PaperSize.RawKind = (int)PaperKind.A4;
		e.PageSettings.Landscape = false;
		//Calculate actual size in pixels that depends on current printer's DPI 
		int actualWidth = (int)(e.Graphics.DpiX * 210 / 25.4f); //210x297 - size of  paper (A4) in millimeters
		int actualHeight = (int)(e.Graphics.DpiY * 297 / 25.4f); //25.4f - millimeters per inch
		//Render to PdfBitmap using page's Render method with FPDF_PRINTING flag
		pdf.Pages[pageForPrint].Render
			(e.Graphics,
			actualWidth,
			actualHeight,
			Patagames.Pdf.Enums.PageRotate.Normal, Patagames.Pdf.Enums.RenderFlags.FPDF_PRINTING);
		//Print next page
		if (pageForPrint < pdf.Pages.Count - 1)
			pageForPrint++;
			e.HasMorePages = true;
	//start printing routine
	pd.Print();

Edited by user Sunday, July 17, 2016 9:50:17 AM(UTC) | Reason: Not specified

The code above illustrates the basic idea. Usually in real applications it a bit more complicated.

Here's an code for printing, which is used in PdfViewer User Control (printing from tool strip).

Code:

private PageRotate PageRotation(PdfPage pdfPage)
	int rot = pdfPage.Rotation - pdfPage.OriginalRotation;
	if (rot < 0)
		rot = 4 + rot;
	return (PageRotate)rot;
private SizeF GetRenderSize(Size pageSize, Size fitSize)
	double w, h;
	w = pageSize.Width;
	h = pageSize.Height;
	double nh = fitSize.Height;
	double nw = w * nh / h;
	if (nw > fitSize.Width)
		nw = fitSize.Width;
		nh = h * nw / w;
	return new SizeF((float)nw, (float)nh);
/// <summary>
/// Occurs when the Print button is clicked
/// </summary>
/// <param name="item">The item that has been clicked</param>
protected virtual void OnPrintClick(ToolStripButton item)
	//Set up PrintDocument object
	PrintDocument pd = new PrintDocument();
	pd.PrinterSettings.MinimumPage = 1;
	pd.PrinterSettings.MaximumPage = PdfViewer.Document.Pages.Count;
	pd.PrinterSettings.FromPage = pd.PrinterSettings.MinimumPage;
	pd.PrinterSettings.ToPage = pd.PrinterSettings.MaximumPage;
	int pageForPrint = 0;
	pd.BeginPrint += (s, e) => 
		//Calculate range of pages for print
		switch(pd.PrinterSettings.PrintRange)
			case PrintRange.Selection:
			case PrintRange.CurrentPage: //Curent page
				pd.PrinterSettings.FromPage = PdfViewer.Document.Pages.CurrentIndex+1;
				pd.PrinterSettings.ToPage = PdfViewer.Document.Pages.CurrentIndex+1;
				break;
			case PrintRange.SomePages: //The range specified by the user
				break;
			default: //All pages
				pd.PrinterSettings.FromPage = pd.PrinterSettings.MinimumPage;
				pd.PrinterSettings.ToPage = pd.PrinterSettings.MaximumPage;
				break;
		pageForPrint = pd.PrinterSettings.FromPage-1;
	pd.QueryPageSettings += (s, e) =>
		//Set the paper orientation to Landscape if the page is rotated
		e.PageSettings.Landscape = pd.PrinterSettings.DefaultPageSettings.Landscape;
		if ((PdfViewer.Document.Pages[pageForPrint].Rotation == PageRotate.Rotate270
			|| PdfViewer.Document.Pages[pageForPrint].Rotation == PageRotate.Rotate90
			e.PageSettings.Landscape = true;
	pd.PrintPage += (s, e) =>
		//Calculate the size of the printable area in pixels
		var fitSize = new Size(
			(int)(e.Graphics.DpiX * e.PageSettings.PrintableArea.Width / 100),
			(int)(e.Graphics.DpiY * e.PageSettings.PrintableArea.Height / 100)
		//Get page's size
		var pageSize = new Size(
			(int)PdfViewer.Document.Pages[pageForPrint].Width / 72.0f * e.Graphics.DpiX, 
			(int)PdfViewer.Document.Pages[pageForPrint].Height / 72.0f * e.Graphics.DpiY);
		//If page was rotated in original file, then we need to "rotate the paper in printer". 
		//For that just swap the width and height of the paper.
		if (PdfViewer.Document.Pages[pageForPrint].OriginalRotation == PageRotate.Rotate270
			|| PdfViewer.Document.Pages[pageForPrint].OriginalRotation == PageRotate.Rotate90)
			fitSize = new Size(fitSize.Height, fitSize.Width);
		//Calculate the page's size fitted to the paper's size.  
		var rSize = GetRenderSize(pageSize, fitSize);
		using (PdfBitmap bmp = new PdfBitmap((int)rSize.Width, (int)rSize.Height, true))
			//Render to PdfBitmap using page's Render method with FPDF_PRINTING flag.
			PdfViewer.Document.Pages[pageForPrint].Render
				(bmp,
				(int)rSize.Width,
				(int)rSize.Height,
				PageRotate.Normal,
				RenderFlags.FPDF_PRINTING | RenderFlags.FPDF_ANNOT);
			//Rotates the PdfBitmap image depending on the orientation of the page
			if (PageRotation(PdfViewer.Document.Pages[pageForPrint]) == PageRotate.Rotate270)
				bmp.Image.RotateFlip(System.Drawing.RotateFlipType.Rotate270FlipNone);
			else if (PageRotation(PdfViewer.Document.Pages[pageForPrint]) == PageRotate.Rotate180)
				bmp.Image.RotateFlip(System.Drawing.RotateFlipType.Rotate180FlipNone);
			else if (PageRotation(PdfViewer.Document.Pages[pageForPrint]) == PageRotate.Rotate90)
				bmp.Image.RotateFlip(System.Drawing.RotateFlipType.Rotate90FlipNone);
			//Set DPI of the image same as the printer's DPI
			(bmp.Image as Bitmap).SetResolution(e.Graphics.DpiX, e.Graphics.DpiY);
			//Draw rendered image to printer's graphics surface
			e.Graphics.DrawImageUnscaled(bmp.Image, 0, 0);
		//Print next page
		if (pageForPrint < pd.PrinterSettings.ToPage-1)
			pageForPrint++;
			e.HasMorePages = true;
	//Show standard print dilog
	var dlg = new PrintDialog();
	dlg.AllowCurrentPage = true;
	dlg.AllowSomePages = true;
	dlg.UseEXDialog = true;
	dlg.Document = pd;
	if(dlg.ShowDialog()== DialogResult.OK)
		pd.Print();

Edited by user Tuesday, April 19, 2016 5:04:55 PM(UTC) | Reason: Not specified

These methods can be found in source code:

Code:

private PageRotate PageRotation(PdfPage pdfPage)
	int rot = pdfPage.Rotation - pdfPage.OriginalRotation;
	if (rot < 0)
		rot = 4 + rot;
	return (PageRotate)rot;
private SizeF GetRenderSize(Size pageSize, Size fitSize)
	double w, h;
	w = pageSize.Width;
	h = pageSize.Height;
	double nh = fitSize.Height;
	double nw = w * nh / h;
	if (nw > fitSize.Width)
		nw = fitSize.Width;
		nh = h * nw / w;
	return new SizeF((float)nw, (float)nh);

Edited by user Wednesday, February 17, 2016 12:35:14 AM(UTC) | Reason: Not specified

I need a VB.Net version of this solution and there is a particular bit of this code that I don't know how to convert from c#.

specifically what does pd.PrintPage += (s, e) => {...} do and what would be the Vb.net equivalent. I notice that pd has not PrintPage method, it looks to me like you are adding an event listener? Can someone give me a quick VB.Net version of this?

Thanks for your help!
Originally Posted by: Guest Go to Quoted Post

specifically what does pd.PrintPage += (s, e) => {...} do and what would be the Vb.net equivalent.

I guess a VB.NET equivalent is a folowing:

Code:

Dim pd as PrintDocument = New PrintDocument();
Dim pageForPrint as Integer = 0;
AddHandler pd.PrintPage, Sub(s, e)
End Sub
'start printing routine
pd.Print();

Edited by user Sunday, March 20, 2016 11:14:09 AM(UTC) | Reason: Not specified

Yes that did the trick thanks.

I'm trying to print using the code example at the top of this thread so that I don't have to implement the PDFViewer control. I don't want to display the PDF I just want to print it. After reviewing the code, testing it, and reviewing the SDK Reference, I have some questions about the page sizes and the Render method of the Page class.

There doesn't seem to be any property in the Page class indicating the Page Layout e.g. Portrait/Landscape. Which is odd because the PDF must contain that information on a page by page basis. The rotation property also does not indicate page layout. So I'm having to use some math to compare the printer paper size to the Page.Height and Page.Width. Now the reference says those are in Points (1/72 inch) so I can do the math. But it seems like it should easier.

Please advise. Thanks



The printing subsystem has been optimized.
Now it printing faster and consumes much less memory.

To print the document to the default printer, you can write the following:
Code:

var doc = PdfDocument.Load("c:\test.pdf");
var printDoc = new PdfPrintDocument(doc);
printDoc.Print();


The code above shows the standard printing dialog with printing progress. If you want to suppress it modify code like shown below:
Code:

var doc = PdfDocument.Load("c:\test.pdf");
var printDoc = new PdfPrintDocument(doc);
PrintController printController = new StandardPrintController();
printDoc.PrintController = printController;
printDoc.Print();



Because the PdfPrintDocument is derived from standard PrintDocument class you can use standard Microsoft Windows print dialog box ( PrinterDialog ) that configures a PrintDocument according to user input and then prints a document.

Please look at this code:

Code:

public void OnPrintClick()
	if (PdfViewer.Document.FormFill != null)
		PdfViewer.Document.FormFill.ForceToKillFocus();
	//create an instance of PrintDocument class
	var printDoc = new PdfPrintDocument(PdfViewer.Document);
	//Create a standard print dialog box
	var dlg = new PrintDialog();
	dlg.AllowCurrentPage = true;
	dlg.AllowSomePages = true;
	dlg.UseEXDialog = true;
	//sets the PrintDocument used to obtain PrinterSettings.
	dlg.Document = printDoc;
	//show PrinterDialog and print pdf document
	if (dlg.ShowDialog() == DialogResult.OK)
		printDoc.Print();

Edited by user Sunday, July 17, 2016 9:47:33 AM(UTC) | Reason: Not specified

Do you mean you need to print a four page on one sheet?
The most pronters has this functionality in they settings which available through PrintDialog window. You can combine this option with the Collate option to achieve the desired result.
Unfortunately there is no a simple way to do that if a printer's drivers does not support this feature.
In this case you need manually render pages, combine and print them self as an image.
Hi. I am using the following vb.net code

Code:

Dim doc As PdfDocument = PdfDocument.Load(strPDF)
Dim printDoc As PdfPrintDocument = New PdfPrintDocument(doc)
printDoc.Print()


and line 2 gives me: Error BC30389 'PdfiumViewer.PdfPrintDocument' is not accessible in this context because it is 'Friend'.

There is an Imports PDFiumViewer at the top of the module

Where am I going wrong?
I have managed to solve the issue. Originally I had downloaded the NuGet package for PDFium viewer but not the full PDFium toolkit. I had a previous reference to PDFWriter.dll that also has a PDFPrintDocument object. Even qualifying the reference as PDFiumViewer.PDFPrintDocument didn't stop the 'Friend' error. After installing the full PDFium toolkit package and removing the PDFiumViewer qualification in line 2, I still got an error but now it was ambiguous name error saying that PdfDocument could be from PDFiumViewer, PDFWriter or Patagames.PDF.Net so I qualified the code as follows

Code:

PdfCommon.Initialize("")
Dim doc As Patagames.Pdf.Net.PdfDocument = Patagames.Pdf.Net.PdfDocument.Load(strPDF)
Dim printDoc As New PdfPrintDocument(doc)
Dim PrintController As PrintController = New StandardPrintController()
printDoc.PrintController = PrintController
printDoc.Print()


And now it works.
Does this mean that the viewer does not function as a stand-alone component without the full toolkit?

Edited by moderator Monday, March 30, 2020 10:37:31 PM(UTC) | Reason: Not specified

Important Information: The Patagames Software Support Forum uses cookies. By continuing to browse this site, you are agreeing to our use of cookies. More Details Close