I am trying to learn, if it's possible to create an application in C# which needs to meet requirements:
Can load a template PDF file, and draw some text on top of it (positioned by pixels for example)
Save this file in a specific directory
And most important... Application should be able to print such PDF file. And i don't mean print it on the screen, i mean 'silently' send on the printer.
I've heard, that two first points can be accomplished by iTextSharp/iTextPdf. But does it allow me to simply send this file to the printer? I was looking for a solution for a while now.
User should pick default printer once in options, and all he should do is click "print" after generating such PDF file. I would love the solution that doesn't open Adobe Reader in the background.
Are there maybe some alternatives?
I know its possible to send images to printer like that, without running any other program in the background. But I need it to work for PDFs.
private void SendToPrinter(string path)
{
PrinterSettings defaultprinter = new PrinterSettings();
PrintDocument pd = new PrintDocument();
pd.PrinterSettings.PrinterName = defaultprinter.PrinterName;
pd.PrintPage += (sender, e) => PrintPage(e, path);
pd.Print();
}
private void PrintPage(PrintPageEventArgs e, string path)
{
Bitmap bitma = new Bitmap(776, 538, PixelFormat.Format32bppArgb);
System.Drawing.Image dest = bitma;
Graphics graph = Graphics.FromImage(dest);
graph.Clear(Color.FromArgb(255, 255, 255));
Bitmap z = new Bitmap(path);
System.Drawing.Image img = z;
graph.DrawImage(img, new System.Drawing.Point(120, 350));
e.Graphics.DrawImage(dest, new System.Drawing.Point(0, 0));
}
PS. Yes, I've tried searching. Google, stackoverflow, seen even few similar questions, but didn't find one answering all my questions.
If you look at the different Questions I put myself here in SO You'll see that using GhostScript in order to print pdf files it's a great tool. You can find the Questions here, here, here and here.
Between the 4 of them you'll find all the information you need :)
Related
I've been attempting to find an easy solution to exporting a Canvas in my WPF Application to a PDF Document.
So far, the best solution has been to use the PrintDialog and set it up to automatically use the Microsoft Print the PDF 'printer'. The only problem I have had with this is that although the PrintDialog is skipped, there is a FileDialog to choose where the file should be saved.
Sadly, this is a deal-breaker because I would like to run this over a large number of canvases with automatically generated PDF names (well, programitically provided anyway).
Other solutions I have looked at include:
Using PrintDocument, but from my experimentation I would have to manually iterate through all my Canveses children and manually invoke the correct Draw method (of which a lot of my custom elements with transformation would be rather time consuming to do)
Exporting as a PNG image and then embedding that in a PDF. Although this works, TextBlocks within my canvas are no longer text. So this isn't an ideal situation.
Using the 3rd party library PDFSharp has the same downfall as the PrintDocument. A lot of custom logic for each element.
With PDFSharp. I did find a method fir generating the XGraphics from a Canvas but no way of then consuming that object to make a PDF Page
So does anybody know how I can skip or automate the PDF PrintDialog, or consume PDFSharp XGraphics to make
A page. Or any other ideas for directions to take this besides writing a whole library to convert each of my Canvas elements to PDF elements.
If you look at the output port of a recent windows installation of Microsoft Print To PDF
You may note it is set to PORTPROMP: and that is exactly what causes the request for a filename.
You might note lower down, I have several ports set to a filename, and the fourth one down is called "My Print to PDF"
So very last century methodology; when I print with a duplicate printer but give it a different name I can use different page ratios etc., without altering the built in standard one. The output for a file will naturally be built:-
A) Exactly in one repeatable location, that I can file monitor and rename it, based on the source calling the print sequence, such that if it is my current default printer I can right click files to print to a known \folder\file.pdf
B) The same port can be used via certain /pt (printto) command combinations to output, not just to that default port location, but to a given folder\name such as
"%ProgramFiles%\Windows NT\Accessories\WORDPAD.EXE" /pt listIN.doc "My Print to PDF" "My Print to PDF" "listOUT.pdf"
Other drivers usually charge for the convenience of WPF programmable renaming, but I will leave you that PrintVisual challenge for another of your three wishes.
MS suggest XPS is best But then they would be promoting it as a PDF competitor.
It does not need to be Doc[X]2PDF it could be [O]XPS2PDF or aPNG2PDF or many pages TIFF2PDF etc. etc. Any of those are Native to Win 10 also other 3rd party apps such as [Free]Office with a PrintTo verb will do XLS[X]2PDF. Imagination becomes pagination.
I had a great success in generating PDFs using PDFSharp in combination with SkiaSharp (for more advanced graphics).
Let me begin from the very end:
you save the PdfDocument object in the following way:
PdfDocument yourDocument = ...;
string filename = #"your\file\path\document.pdf"
yourDocument.Save(filename);
creating the PdfDocument with a page can be achieved the following way (adjust the parameters to fit your needs):
PdfDocument yourDocument = new PdfDocument();
yourDocument.PageLayout = PdfPageLayout.SinglePage;
yourDocument.Info.Title = "Your document title";
PdfPage yourPage = yourDocument.AddPage();
yourDocument.Orientation = PageOrientation.Landscape;
yourDocument.Size = PageSize.A4;
the PdfPage object's content (as an example I'm putting a string and an image) is filled in the following way:
using (XGraphics gfx = XGraphics.FromPdfPage(yourPage))
{
XFont yourFont = new XFont("Helvetica", 20, XFontStyle.Bold);
gfx.DrawString(
"Your string in the page",
yourFont,
XBrushes.Black,
new XRect(0, XUnit.FromMillimeter(10), page.Width, yourFont.GetHeight()),
XStringFormats.Center);
using (Stream s = new FileStream(#"path\to\your\image.png", FileMode.Open))
{
XImage image = XImage.FromStream(s);
var imageRect = new XRect()
{
Location = new XPoint() { X = XUnit.FromMillimeter(42), Y = XUnit.FromMillimeter(42) },
Size = new XSize() { Width = XUnit.FromMillimeter(42), Height = XUnit.FromMillimeter(42.0 * image.PixelHeight / image.PixelWidth) }
};
gfx.DrawImage(image, imageRect);
}
}
Of course, the font objects can be created as static members of your class.
And this is, in short to answer your question, how you consume the XGraphics object to create a PDF page.
Let me know if you need more assistance.
I have an A3-sized print document that contains images customer provide and it will fill up the document through calculation (There could sometimes be more than 300 images at once being computed to printDocument). The problem I am facing now is that when it is sent to the printer the document is too big for the printer memory to handle. Is there a way to let printer to print page as soon as it is sent rather than the whole document? My colleague suggest to break those pages to a different document. Is that possible?
I have scour through the documentation and there seems to be no way for the printDocument or printerController to talk with the printer to start printing page as soon as it receives.
On my test run I have a job of 360 images stuffed into 28 pages and the document spool data went up to 2.71GB
Screenshot of the print queue
private void PrintPageEventHandler(object sender, PrintPageEventArgs e)
{
//set some settings
//loop until the page has been filled up by images
while(counter < maxImageAllowedPerPage)
{
e.Graphics.DrawImage(image, currentPoint.X + posX, currentPoint.Y +
posY, newWidth, newHeight);
}
e.Graphics.Dispose();
e.HasMorePages = (PrintedImageCount != TotalImageCount);
}
Ok based on the Microsoft docs for PrintDocument, you should just need to move your loop.
So something like
while(counter < maxImageAllowedPerPage)
{
PrintDocument pd = new PrintDocument();
pd.PrintPage += new PrintPageEventHandler(this.PrintPageEventHandler);
pd.Print();
}
Not sure how your determining which image's to print, but you'll need to do that in this loop too, probably before the final Print(), as that is what will fire the Event Handler. Possibly you'll want two collections of 'images', your complete collection of images, and a collection that is filled for each page, so you'll fill that second collection in the loop above and the PrintPage EV will read from that collection.
Oh and HasMorePages will always be false now.
I ended up with something similar to #cjb110's answer
//initialize the print docuemnts with all settings required
var printDocument = new PrintDocument();
printDocument.PrintedImageCount = 0;
printDocument.TotalImageCount = 150;
while(printDocument.PrintedImageCount != printDocument.TotalImageCount){
printDocument.Print();
}
As of current the printer is able to handle based on my test print 30 document being sent to the printer without crashing, and my client will monitor on their own to see how many document can the printer accept until it crash to see if I need to implement a limitation to prevent too many docuemnt being sent at once.
Thanks to all that have suggest different kind of solutions.
After struggling whole day, I identified the issue but this didn't solve my problem.
On short:
I need to open a PDF, convert to BW (grayscale), search some words and insert some notes nearby found words. At a first look it seems easy but I discovered how hard PDF files are processed (having no "words" concepts and so on).
Now the first task, converting to grayscale just drove me crazy. I didn't find a working solution either commercial or free. I came up with this solution:
open the PDF
print with windows drivers, some free PDF printers
This is quite ugly since I will force the C# users to install such 3'rd party SW but.. that is fpr the moment. I tested FreePDF, CutePDF and PDFCreator. All of them are working "stand alone" as expected.
Now when I tried to print from C#, obviously, I don't want the print dialog, just select BW option and print (aka. convert)
The following code just uses a PDF library, shown for clarity only.
Aspose.Pdf.Facades.PdfViewer viewer = new Aspose.Pdf.Facades.PdfViewer();
viewer.BindPdf(txtPDF.Text);
viewer.PrintAsGrayscale = true;
//viewer.RenderingOptions = new RenderingOptions { UseNewImagingEngine = true };
//Set attributes for printing
//viewer.AutoResize = true; //Print the file with adjusted size
//viewer.AutoRotate = true; //Print the file with adjusted rotation
viewer.PrintPageDialog = true; //Do not produce the page number dialog when printing
////PrinterJob printJob = PrinterJob.getPrinterJob();
//Create objects for printer and page settings and PrintDocument
System.Drawing.Printing.PrinterSettings ps = new System.Drawing.Printing.PrinterSettings();
System.Drawing.Printing.PageSettings pgs = new System.Drawing.Printing.PageSettings();
//System.Drawing.Printing.PrintDocument prtdoc = new System.Drawing.Printing.PrintDocument();
//prtdoc.PrinterSettings = ps;
//Set printer name
//ps.PrinterName = prtdoc.PrinterSettings.PrinterName;
ps.PrinterName = "CutePDF Writer";
ps.PrintToFile = true;
ps.PrintFileName = #"test.pdf";
//
//ps.
//Set PageSize (if required)
//pgs.PaperSize = new System.Drawing.Printing.PaperSize("A4", 827, 1169);
//Set PageMargins (if required)
//pgs.Margins = new System.Drawing.Printing.Margins(0, 0, 0, 0);
//Print document using printer and page settings
viewer.PrintDocumentWithSettings(ps);
//viewer.PrintDocument();
//Close the PDF file after priting
What I discovered and seems to be little explained, is that if you select
ps.PrintToFile = true;
no matter C# PDF library or PDF printer driver, Windows will just skip the PDF drivers and instead of PDF files will output PS (postscript) ones which obviously, will not be recognized by Adobe Reader.
Now the question (and I am positive that others who may want to print PDFs from C# may be encountered) is how to print to CutePDF for example and still suppress any filename dialog?
In other words, just print silently with programmatically selected filename from C# application. Or somehow convince "print to file" to go through PDF driver, not Windows default PS driver.
Thanks very much for any hints.
I solved conversion to grayscale with a commercial component with this post and I also posted there my complete solution, in care anyone will struggle like me.
Converting PDF to Grayscale pdf using ABC PDF
If I have an existing PDF that has a graphic on it, and I simple want a user to be able to click a point on the pdf, and drop a letter at the point the click, like A... B... etc. I'm thinking that ITextSharp could handle something like this, but frankly, I'm not sure how to accomplish it. Can you offer some guidance?
Here's a C# example using the commercial Quick PDF Library.
using System;
using System.IO;
using System.Drawing;
using System.Windows.Forms;
using QuickPDFAX0714;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
private string LicenseKey = " your key here ";
private string OriginalFileName = "D:\\QuickPDFLibrary\\hello1.pdf";
private string NewFileName = "D:\\QuickPDFLibrary\\hello2.pdf";
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
ShowPDF(OriginalFileName);
}
private void ShowPDF(string fileName)
{
PDFLibrary qp = new PDFLibrary();
qp.UnlockKey(LicenseKey);
qp.LoadFromFile(fileName);
// Fit width of PDF to width of picture box
int dpi = Convert.ToInt32((pictureBox1.Width * 72) / qp.PageWidth());
byte[] bmpData = (byte[])qp.RenderPageToVariant(dpi, 1, 0);
MemoryStream ms = new MemoryStream(bmpData);
Bitmap bmp = new Bitmap(ms);
pictureBox1.Image = bmp;
ms.Dispose();
}
private void pictureBox1_MouseClick(object sender, MouseEventArgs e)
{
PDFLibrary qp = new PDFLibrary();
qp.UnlockKey(LicenseKey);
qp.LoadFromFile(OriginalFileName);
// Calculate co-ordinates, width of PDF fitted to width of PictureBox
double xpos = ((double)e.X / (double)pictureBox1.Width) * qp.PageWidth();
double ypos = qp.PageHeight() - ((double)e.Y / (double)pictureBox1.Width) * qp.PageWidth();
qp.SetTextSize(24);
qp.SetTextColor(1, 0, 0);
qp.DrawText(xpos, ypos, "A");
qp.SaveToFile(NewFileName);
ShowPDF(NewFileName);
}
}
}
Wouldn't you probably want to load the PDF, turn it into something that you can render in your app (ie, some editable document object), make changes, and then write it back out to PDF?
Granted, that's probably a lot of work for what you are trying to accomplish, but most PDF tools I am aware of don't actually edit the .PDF directly.
iTextSharp may have the ability to load .PDF files into some sort of document tree that you can then manipulate. I would start by seeing if it does such a thing, and then try to change some of the nodes, or perhaps add nodes corresponding to your annotations.
It may not be possible to see your changes reflected in real time, so you probably will want to "fake" the changes by layering them over your render of the .PDF file as simple lable objects. That's probably the easiest way to start.
No, itextsharp can not do that, since itextsharp can manipulate pdf-files, but there's no way of rendering a pdf in itextsharp.
You can certainly use ITextSharp to add content to an existing PDF. There are samples on the web that can show you how. (It's been a while since I've done this, and my needs were to add watermark images to existing PDFs.)
ITextSharp doesn't read PDFs well, so you'll probably want to find something else to display the PDF. You may need to overlay a transparent window over whatever displays the PDF so you can catch the click, but that all depends upon the viewer you find.
Bearing in mind that I have only ever used PDFNet SDK for PDF manipulation tasks, I believe everything you are asking about can be achieved with that library, although you may have to deal with some complexity with coordinate conversion from the click to the location in the document, and again to building the matrix that locates the text where you need it on the write.
They do provide a simple WinForms sample PDF viewer application that might serve as a good starting point for your experimentation.
I haven't needed to go this deep into editing, myself. The starting points for documentation are in the FAQ (How to add a watermark to a page) and the knowledge base. The API documentation is also quite extensive and publicly available on the website.
What is the best way to print stuff from c#/.net?
The question is in regard to single pages as well as to reports containing lots of pages.
It would be great to get a list of the most common printing libs containing the main features and gotchas of each of them.
[Update] for standard windows clients (or servers), not for web apps, please.
For reports, I use the RDLC control.
For everything else, I use the inherent printing objects within .NET.
Edit
The inherent printing objects are all found in the System.Drawing.Printing namespace. When you use the PrintDialog or the PrintPreviewDialog in a WinForms (or WPF) application, it is to these objects that you're turning over control.
The fundamental concept is that you're drawing to the printer. The simplest form of this is:
Sub MyMethod()
Dim x as New PrintDocument
AddHandler x.PrintPage, AddressOf printDoc_PrintPage
x.Print
End Sub
Sub printDoc_PrintPage( sender as Object, e as PrintPageEventArgs)
Dim textToPrint as String= ".NET Printing is easy"
dim printFont as new Font("Courier New", 12)
dim leftMargin as int= e.MarginBounds.Left
dim topMargin as int = e.MarginBounds.Top
e.Graphics.DrawString(textToPrint, printFont, Brushes.Black, leftMargin, topMargin)
End Sub
What's happening here is that when my object (x) is sent the print command, it raises the "PRINT PAGE" event (which is designed to print 1 page at a time). This event then uses the Graphics attribute of the PrintPageEventArgs to draw the relevant string directly to the print spooler.
Here's one tutorial, and a quick Google search for ".NET printing tutorial" returns a bit over 200K results.
We used a set of third party DLLs from PDFSharp who in turn use DLLs from MigraDoc. I'm not privy to all the reasons that we went that direction (the decision was made by a senior developer), but I can tell you that:
It seems to be in active
development.
It had most of the
features we needed.
The source code
is available. Although it used some
patterns and conventions that I
hadn't seen before, once I got on to
them, it was fairly easy to make the
changes. I added support for using
the System.Drawing.Image directly
rather than as saving files.
It is
not documented well either
internally or externally.
ยป Sample Code to show basics of Printing in Windows Forms Applications:
using System.Drawing.Printing;
PrintDocument printDoc = new PrintDocument();
printDoc.DefaultPageSettings.Landscape = true;
printDoc.DefaultPageSettings.Margins.Left = 100; //100 = 1 inch = 2.54 cm
printDoc.DocumentName = "My Document Name"; //this can affect name of output PDF file if printer is a PDF printer
//printDoc.PrinterSettings.PrinterName = "CutePDF";
printDoc.PrintPage += new PrintPageEventHandler(printDoc_PrintPage);
PrintDialog printDialog = new PrintDialog();
printDialog.Document = printDoc; //Document property must be set before ShowDialog()
DialogResult dialogResult = printDialog.ShowDialog();
if (dialogResult == DialogResult.OK)
{
printDoc.Print(); //start the print
}
void printDoc_PrintPage(object sender, PrintPageEventArgs e)
{
Graphics g = e.Graphics;
string textToPrint = ".NET Printing is easy";
Font font = new Font("Courier New", 12);
// e.PageBounds is total page size (does not consider margins)
// e.MarginBounds is the portion of page inside margins
int x1 = e.MarginBounds.Left;
int y1 = e.MarginBounds.Top;
int w = e.MarginBounds.Width;
int h = e.MarginBounds.Height;
g.DrawRectangle(Pens.Red, x1, y1, w, h); //draw a rectangle around the margins of the page, also we can use: g.DrawRectangle(Pens.Red, e.MarginBounds)
g.DrawString(textToPrint, font, Brushes.Black, x1, y1);
e.HasMorePages = false; //set to true to continue printing next page
}
Loads of stuff, you say. Hum, seems that you should use a solution with a designer, so you should look into Crystal Reports and RDLC.
There's also the Reporting Services solution, but in that case you would need a server with SQL Server.
Crystal Reports seems to give you more choices, but needs a little more learning than RDLC.
I wouldn't recommend you create those in HTML + CSS, because of the limitations and the extra work you would have to throw at it.
If you can build your output as a FlowDocument, you can turn it into XPS easily to get an "electronic" version, and the print the XPS.
It depends a lot on the requirements of your application.
Even though it isn't the perfect tool (really far from that), Crystal Reports tends to be a good choice.
It gives you the option of getting data directly from a Database or, if you already have a list of objects you want to print, you can pass them to the document and bind the object properties to the labels of the report.
But give us some more information of what you're trying to do, so you can receive better proposals.
Can Print via Adobe Acrobat
I use the standard libraries such as System.Diagnostics.ProcessStartInfo to use Adobe Acrobat to print a pdf. The end user will not have to interact with the Acrobat GUI, though, annoyingly, the following code still pulls it on-screen for a few seconds.
// Sample fileName = System.Environment.GetFolderPath(
// System.Environment.SpecialFolder.CommonApplicationData)
// + #"\MyCompany\MyProject\TestPrint.pdf"
private void SendPrintJob(string fileName)
{
try
{
// Start by finding Acrobat from the Registry.
// This supposedly gets whichever you have of free or paid
string processFilename = Microsoft.Win32.Registry.LocalMachine
.OpenSubKey("Software")
.OpenSubKey("Microsoft")
.OpenSubKey("Windows")
.OpenSubKey("CurrentVersion")
.OpenSubKey("App Paths")
.OpenSubKey("AcroRd32.exe")
.GetValue(String.Empty).ToString();
ProcessStartInfo info = new ProcessStartInfo();
info.Verb = "print";
info.FileName = processFilename;
info.Arguments = String.Format("/p /h {0}", fileName);
info.CreateNoWindow = true;
info.WindowStyle = ProcessWindowStyle.Hidden;
info.UseShellExecute = false;
Process p = new Process();
p.StartInfo = info;
p.Start();
p.WaitForInputIdle();
// Recommended to add a time-out feature. Mine is coded here.
}
catch (Exception e)
{
Console.WriteLine("Error sending print job. " + e.Message);
}
Can Manipulate via PDFSharp/MigraDoc
I did not read document manipulation into the OP, but I see other answers commenting on the fact. A few StackOverflow Q&As from 2008-2012 (including #Robert Gowland from this question) say that PDFSharp / MigraDoc have poor documentation.
In 2018, I have found it to be an easy learning curve, with many samples at the homepage. I read this question this morning to figure out how to print a graph, and now have a button to screen-shot my application and print.
You will want to go to NuGet package manager for PDFsharp-MigraDocs (or PDFsharp-MigraDocs-WPF, or PDFsharp-MigraDocs-GDI). MigraDocs is the high-level component that can create documents from elements, without care of if they are pdf or image or what have you. PDFSharp is the component that helps to, i.e., rearrange documents, put multiple documents on a page, and break apart content from one to two pages.