【问题标题】:html or pdf printing on server side c#html或pdf在服务器端打印c#
【发布时间】:2012-06-26 13:18:03
【问题描述】:

我知道它是许多论坛和博客中的一个已知主题。我读了很多文章。他们中的许多人都安静地提供信息。但对我来说,完成这项任务似乎需要新的方法。

我正在寻找在服务器端打印 html 的解决方案。但是在使用了许多选项后,我意识到我们

  1. 无法提供打印机名称或
  2. 其打印 html 原始内容,如 txt 文件

后来知道ghostscript (https://stackoverflow.com/a/2600189/1238159) 可以在服务器端静默打印PDF。

还尝试使用水晶报告(但如何动态地将 HTML 内容传递给它,即使它不支持许多标签)、itextsharp、ssrs、pdfsharp 等,但它们都不支持许多 HTMl 标签和 W3C 标准。所以我在某一时刻结束了生成PDF。只有 wkhtmltopdf 非常适合将 html 转换为 pdf。根据我的经验,它支持每个 html 标签,这与其他任何标签都不一样。但是打印 PDf 是我多年以来的问题。

但现在即使使用 GhostScript(我使用的是 9.05 版)也面临问题。使用 localhost 我可以完美地使用它。我可以在服务器端以静默方式打印来自 UI 的任何打印机名称。但使用 IP 地址或机器名称无法正常工作。我什至实施了模拟。即使进程在调用 GhostScript 时挂起。

现在我想弄清楚的是

  1. 是否可以在服务器端打印 html 或 pdf(实际内容)?
  2. 任何开源工具都可以实现这一目标
  3. 我想动态传递的打印机名称

任何线索或解决方法都可能帮助全球数小时的人们。 :)

非常感谢。

问候, 帕万N

在使用了刘的建议之后。我能够在命令提示符下执行此操作(意味着 cmd.exe 在我的帐户下运行)。但我的应用程序将在网络服务下运行。 现在我遇到了一个问题ACCESS Denied

是的。最后我能够开始这个过程。并且能够使用我的域凭据在任务管理器下查看我的 gswin32c.exe 进程。代码如下:

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;
        }
    }
}

但该过程在 localhost:5030 中运行良好,即在我的 Visual Studio 中运行时。但带有 IP 地址或机器名称。它只是挂起并引发此错误

adobe reader、foxit等也发生了同样的事情。

( Process must exit before requested information can be determined. :    at System.Diagnostics.Process.EnsureState(State state)
at System.Diagnostics.Process.get_ExitCode() )

【问题讨论】:

  • 我对“打印服务器端”的意思感到困惑。打印机连接在哪里?您是在谈论从远程 Web 服务器向用户的本地打印机发起打印吗?
  • “打印服务器端”表示我正在尝试在服务器端(即 IIS)打印由 XML+XSL 生成的 HTML 字符串。是的,打印机已连接到 IIS 服务器。它是一个 Intranet 应用程序。

标签: c# wcf printing server-side ghostscript


【解决方案1】:

我一直在做一个正在做这件事的项目。这是一次非常令人沮丧的经历。我发现最可靠的方法是将我的报告导出为 PDF,然后通过 Diagnostics.Process 使用 Foxit Reader(由于安全问题而不是 Adob​​e Reader)来打印文档。

打印机名称可以提供给 Foxit Reader 命令行参数。

我正在使用的环境是 Windows Server 2008 R2 x64 上的 IIS 7 上的 ASP.net 3.5。 我也在使用 Sql Server Reporting Services。

也许这段代码会帮助你:

    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;
    }

我的 web.config 包含:

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

【讨论】:

  • 似乎对我有帮助。我会谷歌它并找到一些与之相关的c#代码。谢谢刘。
  • Dusty Lau,好像我们不能发送要打印的份数。为此,我需要强加一个业务规则:)。循环可能会有所帮助。但它确实会通过假脱机和进程数量影响性能。我提到的链接是 [This(stackoverflow.com/q/4868982/1238159)] 和 [This(stackoverflow.com/q/7383349/1238159)]
  • 我需要同样的东西。我让报表通过发送要生成的副本数来处理页数。
  • 这一切似乎有点老套。但是我很惊讶地发现服务器端打印的支持很少。显然这不是经常做的事情。
  • 是的,尘土飞扬。 Seriusly ...它的支持很少。非常感谢您的支持。并来到你的代码。我通过命令提示符尝试了 "Foxit Reader.exe" /t C:/1.pdf \\ed t-115\HP Officejet 4500 G510>> >>> 它在我的 vista 机器上什么也没做。但在win2003它的打印。我试过你的代码。猜猜假冒的一些问题。它在启动过程时给了我“拒绝访问”(就像我在 GhostScript 中遇到的一样)。在 Win2K3 中它抛出错误,但甚至没有登录我的 txt 文件。我现在一无所知。
【解决方案2】:

很抱歉迟到了。我教过我已经回答了这个问题。 我找到了将html转换为pdf的解决方法。 我正在使用 WKHTMLTOPDF API 将 html 转换为 pdf。与那里的许多商业产品相比,它看起来很棒。能够获得彩色/灰度、边距、索引。还有更多。

这是我关注的链接 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();

同样,我发送给 ghostscript 以获得漂亮的 TIFF 文件用于传真。数据量大时性能也不错。

问候, 帕万N

【讨论】:

  • 对我来说非常有用,甚至可以从 https 链接下载图像并包含它们...调整文档大小以适合页面宽度(其他工具如 CutePDF 打印机没有)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-02-10
  • 1970-01-01
  • 2013-04-04
  • 2011-07-15
  • 1970-01-01
  • 2018-07-08
相关资源
最近更新 更多