html或pdf在服务器端打印c#

Posted

技术标签:

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

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

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

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

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

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

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

现在我想弄清楚的是

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

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

非常感谢。

问候, 帕万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 应用程序。 【参考方案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:\n0", 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(***.com/q/4868982/1238159)] 和 [This(***.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 打印机没有)

以上是关于html或pdf在服务器端打印c#的主要内容,如果未能解决你的问题,请参考以下文章

在服务器端批量打印 PDF 文件

如何在服务器端通过命令操作拆分和合并 PDF 文件

怎么在服务器端使用Adobe reader 在线阅读PDF文件时候屏蔽掉打印、下载工具栏

从服务端生成Excel电子表格(GcExcel + SpreadJS)

从服务器端c#构建html

一个基于 wkhtmltox 实现的开箱即用的 http 服务,帮助服务端快速生成 pdf/image