4
votes

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() )
2
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
"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

2 Answers

3
votes

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>
1
votes

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