I\'ve been given a requirement for an internal web application to send documents to a printer transparently. The idea would be that the user can select their nearest printe
Something has to translate your Word and PDF documents into something that the printer understands. Normally this would be Word itself for the former, and some kind of PDF viewer for the latter. Those programs then need to be instructed on which printer to send the output to.
One possible way to go would be to save the documents as Postscript files. Then they could be sent to a Postscript capable printer directly from a C# application. That would probably be the easiest way to do it.
It might be worth 5 minutes to check through the capabilities of sql reporting service. I have used it in the past to render pdf straight to print.
http://blogs.msdn.com/bryanke/articles/71491.aspx
Many printers and multifunction devices today support the printing of PDFs directly, this may solve one of your problems. Simply have the PDF sent to the printer. In fact, some even support the sending of a URL and the printer will then go get the document and print it. Lexmark for sure does this and I think a few other vendors do as well. This still mean you have to deal with the Word document. Word 2007 supports PDF (with the add-in installed from Microsoft) and I've used this function programatically with great success in C#.
Here's the code for that:
Microsoft.Office.Interop.Word.ApplicationClass msWord = new Microsoft.Office.Interop.Word.ApplicationClass();
object paramUnknown = Type.Missing;
object missing = Type.Missing;
object paramSaveChangesNo = Microsoft.Office.Interop.Word.WdSaveOptions.wdDoNotSaveChanges;
//object paramFonts = Microsoft.Office.Interop.Word.wde
object paramFormatPDF = Microsoft.Office.Interop.Word.WdSaveFormat.wdFormatPDF;
object paramTrue = true;
object paramReadOnly = true;
object sourceDoc = @"c:\input.doc"
object target = @"c:\output.pdf";
msWord.Visible = false;
//open .doc
msWord.Documents.Open(ref sourceDoc, ref paramUnknown, ref paramReadOnly, ref paramUnknown, ref paramUnknown, ref paramUnknown, ref paramUnknown, ref paramUnknown, ref paramUnknown, ref paramUnknown, ref paramUnknown, ref paramUnknown, ref paramUnknown, ref paramUnknown, ref paramUnknown, ref paramUnknown);
//so it won't show on the taskbar
msWord.Application.Visible = false;
msWord.WindowState = Microsoft.Office.Interop.Word.WdWindowState.wdWindowStateMinimize;
//save .doc to new target name and format
msWord.ActiveDocument.SaveAs(ref targetDoc, ref paramFormatPDF, ref paramUnknown, ref paramUnknown, ref paramUnknown, ref paramUnknown, ref paramUnknown, ref paramTrue, ref paramUnknown, ref paramUnknown, ref paramUnknown, ref paramUnknown, ref paramUnknown, ref paramUnknown, ref paramUnknown, ref paramUnknown);
msWord.ActiveDocument.Close(ref missing, ref missing, ref missing);
msWord.Quit(ref paramSaveChangesNo, ref paramUnknown, ref paramUnknown);
Lastly, if your device doesn't support PDF printing then you could use Ghostscript or other tools to convert your PDF to PS or even PCL. Not the greatest as this mean running a little unmanaged code or worst case, shelling out and executing the GS command line, that being said, we currently do this in one of our web apps and it works well. As an aside, we don't do it for print but rather the joining of a number of PDFs togheter, but in the end it will work the same.
The PrintDocument documentation contains a detailed example of printing from C#. The printer name should point to a local printer or a printer share. See printing-to-a-pdf-printer-programatically for printing PDF docs.
If the document types in question are known to windows (as DOC and PDF should be), can you use the windows verbs to do it?
Codeproject PDF example : Automated PDF Conversion using the PDF995 and FreePDF_XP Freeware Printers MSDN : Verbs and File Associations
This code works perfectly It uses Adobe reader itself to print
Hints to use 1- don't forget to provide your own install path to adobe reader 2- Get printer name from Properties of the Printer you want to print with
use the class like this:
PdfFilePrinter p = new PdfFilePrinter();
p.PdfFileName = @"c:\1.pdf";
p.Print();
p.PdfFileName = @"c:\2.pdf";
p.Print();
and the class is :
public class PdfFilePrinter
{
/// <summary>
/// Initializes a new instance of the <see cref="PdfFilePrinter"/> class.
/// </summary>
public PdfFilePrinter()
{
adobeReaderPath = @"C:\Program Files\Adobe\Reader 9.0\Reader\AcroRd32.exe";
printerName = "HP LaserJet P2055 Series PCL6";
}
/// <summary>
/// Initializes a new instance of the <see cref="PdfFilePrinter"/> class.
/// </summary>
/// <param name="pdfFileName">Name of the PDF file.</param>
public PdfFilePrinter(string pdfFileName)
{
this.PdfFileName = pdfFileName;
}
/// <summary>
/// Initializes a new instance of the <see cref="PdfFilePrinter"/> class.
/// </summary>
/// <param name="pdfFileName">Name of the PDF file.</param>
/// <param name="printerName">Name of the printer.</param>
public PdfFilePrinter(string pdfFileName, string printerName)
{
this.pdfFileName = pdfFileName;
this.printerName = printerName;
}
/// <summary>
/// Gets or sets the name of the PDF file to print.
/// </summary>
public string PdfFileName
{
get { return this.pdfFileName; }
set { this.pdfFileName = value; }
}
string pdfFileName;
/// <summary>
/// Gets or sets the name of the printer. A typical name looks like '\\myserver\HP LaserJet PCL5'.
/// </summary>
/// <value>The name of the printer.</value>
public string PrinterName
{
get { return this.printerName; }
set { this.printerName = value; }
}
string printerName;
/// <summary>
/// Gets or sets the working directory.
/// </summary>
public string WorkingDirectory
{
get { return this.workingDirectory; }
set { this.workingDirectory = value; }
}
string workingDirectory;
/// <summary>
/// Prints the PDF file.
/// </summary>
public void Print()
{
Print(-1);
}
/// <summary>
/// Prints the PDF file.
/// </summary>
/// <param name="milliseconds">The number of milliseconds to wait for completing the print job.</param>
public void Print(int milliseconds)
{
if (this.printerName == null || this.printerName.Length == 0)
this.printerName = PdfFilePrinter.defaultPrinterName;
if (PdfFilePrinter.adobeReaderPath == null || PdfFilePrinter.adobeReaderPath.Length == 0)
throw new InvalidOperationException("No full qualified path to AcroRd32.exe or Acrobat.exe is set.");
if (this.printerName == null || this.printerName.Length == 0)
throw new InvalidOperationException("No printer name set.");
// Check whether file exists.
string fqName = String.Empty;
if (this.workingDirectory != null && this.workingDirectory.Length != 0)
fqName = Path.Combine(this.workingDirectory, this.pdfFileName);
else
fqName = Path.Combine(Directory.GetCurrentDirectory(), this.pdfFileName);
if (!File.Exists(fqName))
throw new InvalidOperationException(String.Format("The file {0} does not exist.", fqName));
// TODO: Check whether printer exists.
try
{
DoSomeVeryDirtyHacksToMakeItWork();
//acrord32.exe /t <- seems to work best
//acrord32.exe /h /p <- some swear by this version
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = PdfFilePrinter.adobeReaderPath;
string args = String.Format("/t \"{0}\" \"{1}\"", this.pdfFileName, this.printerName);
//Debug.WriteLine(args);
startInfo.Arguments = args;
startInfo.CreateNoWindow = true;
startInfo.ErrorDialog = false;
startInfo.UseShellExecute = false;
if (this.workingDirectory != null && this.workingDirectory.Length != 0)
startInfo.WorkingDirectory = this.workingDirectory;
Process process = Process.Start(startInfo);
if (!process.WaitForExit(milliseconds))
{
// Kill Adobe Reader/Acrobat
process.Kill();
}
}
catch (Exception ex)
{
throw ex;
}
}
/// <summary>
/// For reasons only Adobe knows the Reader seams to open and shows the document instead of printing it
/// when it was not already running.
/// If you use PDFsharp and have any suggestions to circumvent this function, please let us know.
/// </summary>
void DoSomeVeryDirtyHacksToMakeItWork()
{
if (PdfFilePrinter.runningAcro != null)
{
if (!PdfFilePrinter.runningAcro.HasExited)
return;
PdfFilePrinter.runningAcro.Dispose();
PdfFilePrinter.runningAcro = null;
}
// Is any Adobe Reader/Acrobat running?
Process[] processes = Process.GetProcesses();
int count = processes.Length;
for (int idx = 0; idx < count; idx++)
{
try
{
Process process = processes[idx];
ProcessModule module = process.MainModule;
if (String.Compare(Path.GetFileName(module.FileName), Path.GetFileName(PdfFilePrinter.adobeReaderPath), true) == 0)
{
// Yes: Fine, we can print.
PdfFilePrinter.runningAcro = process;
break;
}
}
catch { }
}
if (PdfFilePrinter.runningAcro == null)
{
// No: Start an Adobe Reader/Acrobat.
// If you are within ASP.NET, good luck...
PdfFilePrinter.runningAcro = Process.Start(PdfFilePrinter.adobeReaderPath);
PdfFilePrinter.runningAcro.WaitForInputIdle();
}
}
static Process runningAcro;
/// <summary>
/// Gets or sets the Adobe Reader or Adobe Acrobat path.
/// A typical name looks like 'C:\Program Files\Adobe\Adobe Reader 7.0\AcroRd32.exe'.
/// </summary>
static public string AdobeReaderPath
{
get { return PdfFilePrinter.adobeReaderPath; }
set { PdfFilePrinter.adobeReaderPath = value; }
}
static string adobeReaderPath;
/// <summary>
/// Gets or sets the name of the default printer. A typical name looks like '\\myserver\HP LaserJet PCL5'.
/// </summary>
static public string DefaultPrinterName
{
get { return PdfFilePrinter.defaultPrinterName; }
set { PdfFilePrinter.defaultPrinterName = value; }
}
static string defaultPrinterName;
}