4

I know its a known topic in many forums and blogs. I read many articles. And many of them are quiet informative. But for me it seems like it demands new approach to achieve this task.

Am looking for a solution to print a html on server side. But after working with many options i realised that we

  1. cant give printer name or
  2. its printing html raw content like a txt file

Later came to know about ghostscript (https://stackoverflow.com/a/2600189/1238159) can be used to print a PDF on server side silently.

Also tried with crystal report (but how to pass HTML content to it dynamically eventhough it wont support many tags), itextsharp, ssrs, pdfsharp etc etc but none of them were supporting many of the HTMl tags & W3C standards. So i ended at one point to generate PDF. Only wkhtmltopdf is perfect for converting html to pdf. it supports every html tag unlike anyother as of my experience. but printing PDf is the question for me from many years.

But now am facing a problem even with GhostScript (am using ver 9.05). With localhost i can use it perfectly. i can print on server side to what ever printer name coming from UI silently. but with the IP address or machine name its not working. i even implemented Impersonation. Even though the process gets hanged while calling GhostScript.

Now what i want to get clear is

  1. Is it possible to print a html or pdf (actual content) on server side?
  2. Any open source tools are there to achieve this
  3. printer name I would like to pass dynamically

Any clue or workaround might help many hours of people around the globe. :)

Many thanks in advance.

Regards, Pavan N

After using the suggestion by Lau. am able to do it in command prompt (means cmd.exe runs under my account). but my application will run under network service. Now am getting a problem just a kind of this ACCESS Denied

Yeah. Finally i was able to start the process. and am able to see my gswin32c.exe process under task manager with my domain credentials. code is as follows:

public bool PrintVSPDF(string ghostScriptPath, int numberOfCopies, string printerName, string pdfFileName)
{
    Logger.AddToLog("printerName", printerName);
    string impersonationUsername = "";
    string impersonationDomain = "";
    string impersonationPWD = "";

    if (ConfigurationManager.AppSettings["UName"] != null)
    {
        impersonationUsername = Encryption.Decrypt(ConfigurationManager.AppSettings["UName"].ToString(), Encryption.DEFAULT_KEY, Encryption.DEFAULT_SEED);
        impersonationDomain = impersonationUsername.Split('\\').Count() > 1 ? impersonationUsername.Split('\\')[0] : "";
        impersonationUsername = impersonationUsername.Split('\\').Count() > 1 ? impersonationUsername.Split('\\')[1] : impersonationUsername.Split('\\')[0];
    }

    if (ConfigurationManager.AppSettings["PD"] != null)
    {
        impersonationPWD = Encryption.Decrypt(ConfigurationManager.AppSettings["PD"].ToString(), Encryption.DEFAULT_KEY, Encryption.DEFAULT_SEED);
    }

    using (Impersonation imp = new Impersonation(impersonationUsername, impersonationDomain, impersonationPWD))
    {
        ProcessStartInfo startInfo = new ProcessStartInfo();
        startInfo.Arguments = "-dPrinted -dNoCancel -dNOPAUSE -dBATCH -dNumCopies=" + Convert.ToString(numberOfCopies) + "  -sDEVICE=mswinpr2 -sOutputFile=%printer%\"" + printerName + "\" \"" + pdfFileName + "\" ";
        startInfo.FileName = ghostScriptPath;
        startInfo.UseShellExecute = false;
        startInfo.CreateNoWindow = true;
        //startInfo.RedirectStandardInput = true;
        startInfo.RedirectStandardError = true;
        startInfo.RedirectStandardOutput = true;
        startInfo.WindowStyle = ProcessWindowStyle.Hidden;
        startInfo.UserName = impersonationUsername;
        startInfo.Domain = impersonationDomain;
        SecureString ss = new SecureString();
        for (int i = 0; i < impersonationPWD.Length; i++)
        {
            ss.AppendChar(impersonationPWD[i]);
        }
        startInfo.Password = ss;
        Process process = null;
        try
        {
            process = Process.Start(startInfo);
            //Logger.AddToLog("Error VS", process.StandardError.ReadToEnd());
            //Logger.AddToLog("Output VS", process.StandardOutput.ReadToEnd());
            //Logger.AddToLog(process.StartInfo.Arguments.ToString(), "VS Print Arguments");
            //Console.WriteLine(process.StandardError.ReadToEnd() + process.StandardOutput.ReadToEnd());
            //Logger.AddToLog(process.StartInfo.FileName.ToString(), "VS Print file name");
            process.WaitForExit(30000);
            if (process.HasExited == false) 
                process.Kill();
            int exitcode = process.ExitCode;
            process.Close();
            return exitcode == 0;
        }
        catch (Exception ex)
        {
            Logger.AddToLog(ex);
            return false;
        }
    }
}

But the process is working perfectly in localhost:5030 ie., while running from my visual studio. but with IP address or machine name. it just hangs and throws this error

Same thing is happening for adobe reader, foxit, etc etc.

( Process must exit before requested information can be determined. :    at System.Diagnostics.Process.EnsureState(State state)
at System.Diagnostics.Process.get_ExitCode() )
Community
  • 1
  • 1
Pavan N
  • 255
  • 2
  • 6
  • 21
  • I'm confused by what you mean by "printing server side". Where is the printer attached? Are you talking about initiating printing to a user's local printer from the remote web server? – Joe Jun 26 '12 at 13:22
  • "printing server side" means am trying to print a HTML string generated by XML+XSL on server side ie., IIS. And yes printer is connected to IIS server. its an intranet application. – Pavan N Jun 26 '12 at 13:36

2 Answers2

3

I have been working on a project that is doing just this. It has been a very frustrating experience. The most reliable method I have found is to export my reports to PDF and then use Foxit Reader (NOT Adobe Reader due to security problems) via Diagnostics.Process to print the document.

The printer name can be supplied to the Foxit Reader command line args.

The environment I am working with is ASP.net 3.5 on IIS 7 on Windows Server 2008 R2 x64. I am also using Sql Server Reporting Services.

Maybe this code will help you out:

    public FileContentResult GetPOReport(DataTable reportData, int poNumber, string copies, out string fileName, out string downloadPath)
    {
        fileName = "PO_" + poNumber.ToString().Trim() + "_" + DateTime.Now.ToString("yyyy-MM-dd_HH-mm-ss") + ".pdf";
        downloadPath = "/Generated/" + fileName;

        var outputFiles = new Dictionary<string, string>
                              {
                                  {"", Server.MapPath("~" + downloadPath)}
                              };

        if (!string.IsNullOrWhiteSpace(copies))
        {
            var copyList = copies.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);

            foreach (var temp in copyList)
                outputFiles.Add(temp, Server.MapPath("~" + "/Generated/" + temp.Trim() + ".pdf"));
        }

        FileContentResult returnFile = null;

        foreach (var outputFile in outputFiles)
        {
            var file = WriteReportToDisk(reportData, outputFile.Value, outputFile.Key);

            if (file == null)
                continue;

            if (string.IsNullOrWhiteSpace(outputFile.Key))
                returnFile = file;
            else
                PrintReport(outputFile.Value);
        }

        return returnFile;
    }

    public void PrintReport(string filePath)
    {
        try
        {
            if (!ConfigurationManager.AppSettings.AllKeys.Contains("AdobeReaderPath") ||
                !ConfigurationManager.AppSettings.AllKeys.Contains("AdobePrintParameters") ||
                !ConfigurationManager.AppSettings.AllKeys.Contains("PrinterName"))
                return;

            var adobeReaderPath = ConfigurationManager.AppSettings["AdobeReaderPath"].Trim();
            var adobePrintParameters = ConfigurationManager.AppSettings["AdobePrintParameters"].Trim();
            var printerName = ConfigurationManager.AppSettings["PrinterName"].Trim();
            var printProcessDomain = ConfigurationManager.AppSettings["PrintProcessDomain"].Trim();
            var printProcessUserName = ConfigurationManager.AppSettings["PrintProcessUserName"].Trim();
            var printProcessPassword = ConfigurationManager.AppSettings["PrintProcessPassword"].Trim();

            var userPrinter = Entities.UserPrinters.FirstOrDefault(p => p.UserName == User.Identity.Name);

            if (userPrinter != null)
                printerName = userPrinter.PrinterName.Trim();

            using (var process = new Process
            {
                StartInfo =
                    new ProcessStartInfo(
                    adobeReaderPath,
                    string.Format(adobePrintParameters, filePath, printerName)
                    )
            })
            {
                if (!string.IsNullOrWhiteSpace(printProcessUserName))
                {
                    if (!string.IsNullOrWhiteSpace(printProcessDomain))
                        process.StartInfo.Domain = printProcessDomain;

                    process.StartInfo.UserName = printProcessUserName;

                    var securePassword = new SecureString();

                    foreach (var passwordCharacter in printProcessPassword)
                        securePassword.AppendChar(passwordCharacter);

                    process.StartInfo.Password = securePassword;

                    process.StartInfo.UseShellExecute = false;
                    process.StartInfo.CreateNoWindow = true;

                    process.StartInfo.LoadUserProfile = true;
                }

                process.Start();

                process.WaitForExit(30000);
            }
        }
        catch (Exception exception)
        {
            EventLog.WriteEntry("PO Suggestion Viewer", string.Format("PO Suggestion Viewer Error:\n{0}", exception.Message));
            throw;
        }
    }

    public FileContentResult WriteReportToDisk(DataTable reportData, string filePath, string copy)
    {
        var webReport = new WebReport()
        {
            ExportFileName = "PO Report",
            ReportPath = Server.MapPath("~/Reports/PurchaseOrderReport.rdlc")
        };

        if (!string.IsNullOrWhiteSpace(copy))
            webReport.ReportParameters.Add(new ReportParameter("Copy", copy));

        if ((User != null) && (User.Identity != null) && (User.Identity.Name != null))
            webReport.ReportParameters.Add(new ReportParameter("User", User.Identity.Name));

        webReport.ReportDataSources.Add(new ReportDataSource("ReportData", reportData));

        var report = webReport.GetReport();

        Response.AddHeader("content-disposition", string.Format("attachment; filename={0}.{1}", webReport.ExportFileName, webReport.FileNameExtension));
        Response.ContentType = "application/pdf";

        var file = File(report, webReport.MimeType, "POReport");

        System.IO.File.WriteAllBytes(filePath, file.FileContents);

        return file;
    }

My web.config contains:

<appSettings>
    <add key="webpages:Version" value="1.0.0.0" />
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
    <add key="AdobeReaderPath" value="C:\Program Files (x86)\Foxit Software\Foxit Reader\Foxit Re-ader.exe" />
    <add key="AdobePrintParameters" value="-t &quot;{0}&quot; &quot;{1}&quot;" />
    <add key="PrinterName" value="HP_Office" />
    <add key="PrintProcessDomain" value="DOMAIN_NAME" />
    <add key="PrintProcessUserName" value="DOMAIN_USER" />
    <add key="PrintProcessPassword" value="DOMAIN_PASSWORD" />
</appSettings>
Dusty Lau
  • 591
  • 4
  • 18
  • seems like helpful for me. I will google it and find some c# code related to. Thanks lau. – Pavan N Jun 26 '12 at 14:01
  • Dusty Lau, seems like we cant send number of copies to print. for this i need to impose a business rule :). Looping might be helpful. But it literally effects performance with spooling and number of processes. Links i referred are [This(http://stackoverflow.com/q/4868982/1238159)] and [This(http://stackoverflow.com/q/7383349/1238159)] – Pavan N Jun 26 '12 at 14:19
  • 1
    I needed the same thing. I let the report handle the number of pages by sending it the number of copies to generate. – Dusty Lau Jun 26 '12 at 14:27
  • It all seems a bit hacky. But I was very surprised to find how little support there is for server side printing in general. Apparently it is not something done very often. – Dusty Lau Jun 26 '12 at 14:29
  • Yeah Dusty. Seriusly ... its having very little support. And many thanks for your support. And coming to your code. i tried it via command prompt <<<<"Foxit Reader.exe" /t C:/1.pdf \\ed t-115\HP Officejet 4500 G510>>>>> it doing nothing in my vista machine. but in win2003 its printing. And i tried your code. Guess some problem with impersonation. its giving me "Access Denied" while starting process (just like what i faced in GhostScript). In Win2K3 its throwing error but not even logging in my txt files. Am clueless now. – Pavan N Jun 27 '12 at 11:23
  • I got it ,,,, C:\Program Files\Foxit Software\Foxit Reader>"Foxit Reader.exe" /t "c:\1.pdf" "\ \EDT-115\HP Officejet 4500 G510" >>>> It's Case sensitive. i can do this successfully from command line. the only problem is via c# code. its giving "Access Denied". at process.start(); – Pavan N Jun 27 '12 at 12:26
  • surprisingly. WKHTMLTOPDF.exe is still working what ever i do. that process is getting started and working properly. But why GhostScript or Foxit exe's are giving me problems. even if i provide credentials. its throwing access denied... – Pavan N Jun 27 '12 at 14:22
  • Can you try running the app as an admin? Try changing the user for the app pool to a full domain admin. – Dusty Lau Jun 27 '12 at 14:23
  • Yes i tried it. in my Vista machine its always giving me access denied. but in win2k3 its starting with impersonated user. And process gets hanged at there itself. its not at all responding. Just the same is happening with the Ghostscript also. am planning to chk it on win7 & win2k8. Also finding ways to feed HTMl data to Rpt file... – Pavan N Jun 28 '12 at 08:12
  • Have you had a chance to try it on Win7 or 2008? – Dusty Lau Jul 05 '12 at 13:46
  • yeah i did. there also the process gets hanged. few cases am receiving exit code as -1 few cases some garbage value. mostly the error i specified (updated post) above. – Pavan N Jul 05 '12 at 15:40
1

sorry for the late posting. i taught i already answered this Q. I found a work around to convert html to pdf. am using WKHTMLTOPDF API to convert the html to pdf. and it looks awesome compared to many commercial products out there. am able to get coloured/greyscale, margins, indexing. and many more.

here is the link i followed http://code.google.com/p/wkhtmltopdf/

ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = AppDomain.CurrentDomain.BaseDirectory + @"\bin\wkhtmltopdf.exe";
pdfFile = localReportPath + "\\Reports\\Savedfiles\\" + filename + ".pdf";
//Ref: http://madalgo.au.dk/~jakobt/wkhtmltoxdoc/wkhtmltopdf-0.9.9-doc.html
startInfo.Arguments = " --minimum-font-size 16 --margin-left 10mm --margin-right 10mm --zoom 3 " + htmlFile + " " + pdfFile;
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
Process p = Process.Start(startInfo);
p.WaitForExit();
p.Dispose();
p.Close();

and the same i sent for ghostscript to get an beautiful TIFF file for faxing. performance is good with huge data also.

Regards, Pavan N

Pavan N
  • 255
  • 2
  • 6
  • 21
  • 1
    works great for me, even downloads the images from https links and includes them... sizes the document to fit a page width properly (where other tools like CutePDF printer did not) – TCC Sep 27 '13 at 18:12