如何使用C#打印PDF

25
我已经尝试解决这个问题将近两天了。网络上有很多或多或少好的解决方案,但没有一个完美地适合我的任务。
任务:
  • 以编程方式打印PDF文件
  • 使用固定的打印机
  • 不要让用户执行多个按钮点击
  • 做到无声 - 越少越好
  • 在客户端完成
第一种解决方案:

使用Forms.WebBrowser

如果我们安装了Adobe Reader,则可以使用插件在Web浏览器中显示PDF。通过此解决方案,我们可以获得一个漂亮的预览,并且使用webbrowserControlName.Print()触发控件打印其内容。

问题 - 我们仍然需要PrintDialog。


使用启动参数启动AcroRd32.exe

以下CMD命令允许我们使用Adobe Reader打印我们的PDF。

InsertPathTo..\AcroRd32.exe /t "C:\sample.pdf" "\printerNetwork\printerName"

问题 - 我们需要AcroRd32.exe的绝对路径 | 会打开一个Adobe Reader窗口,并且必须在打印任务准备就绪之前保持打开状态。


使用Windows预设

Process process = new Process();

process.StartInfo.FileName = pathToPdf; 
process.StartInfo.Verb = "printto";
process.StartInfo.Arguments = "\"" + printerName + "\""; 
process.Start();

process.WaitForInputIdle();
process.Kill();

问题 - 打印完成后仍会弹出Adobe Reader窗口,但通常会自动关闭。

解决方案 - 说服客户使用Foxit Reader(不要使用最后两行代码)。


将PDF页面转换为Drawing.Image

我不知道如何用代码实现,但是一旦这个工作完成,其余的就轻而易举了。Printing.PrintDocument可以满足所有需求。


有人有什么想法可以从这些PDF中获取一些Drawing.Image,或者另一种方法如何做到呢?

最好的问候, Max


要获取Drawing.Image,您可以查看此示例:https://ghostscriptnet.codeplex.com/SourceControl/latest#Ghostscript.NET/Ghostscript.NET.Samples/Samples/RasterizerSample.cs - HABJAN
有没有解决方案?我正在遇到完全相同的问题。如果有任何建议,将不胜感激。 - csensoft
1
@csensoft 目前还没有符合这个特定需求的解决方案。我选择添加一个额外的服务来处理打印。以下有很多好的答案可能会对您有所帮助。 - Mx.
@Mx. 好的。谢谢。 - csensoft
显示剩余3条评论
12个回答

8
我找到的最灵活、最简单和最优秀的方法是使用GhostScript。它可以通过打印机名称直接打印到Windows打印机。
引用: "C:\Program Files\gs\gs9.07\bin\gswin64c.exe" -dPrinted -dBATCH -dNOPAUSE -sDEVICE=mswinpr2 -dNoCancel -sOutputFile="%printer%打印机名称" "pdfdocument.pdf"
添加这些开关以将文档缩小到A4页面。
引用: -sPAPERSIZE=a4 -dPDFFitPage

1
我建议使用Ghostscript包装器来完成这个任务。类似于:http://ghostscriptnet.codeplex.com - HABJAN
@HABJAN 谢谢您的建议,看起来确实很有用。 - Johan van der Slikke
我使用了Ghostscript.NET(可通过NuGet获得)。当我尝试使用最新版本的ghostscript(9.18)时,我收到了内存访问异常。我卸载了9.18,并发现在Ghostscript.NET的最后一个版本之前的最新版本是9.15。一旦我安装了它,一切似乎都开始工作了。您可以在http://downloads.ghostscript.com/public/获取ghostscript的先前版本。 - Keith
Ghostscript的mswinpr2质量相当差 - 它将输入的PDF文件转换为光栅文件并打印。更好的方法是使用第三方组件或尝试自己实现,特别是PDF / PS已被打印机广泛认可。在这里,我找到了一个简单而不错的命令行工具http://effisoft.pl/rawfileprinter - zuko

4

我尝试了很多方法,最有效的一种是通过命令行启动SumatraPDF:

// Launch SumatraPDF Reader to print
String arguments = "-print-to-default -silent \"" + fileName + "\"";
System.Diagnostics.Process.Start("SumatraPDF.exe", arguments);

这样做有很多优点:

  1. SumatraPDF比Adobe Acrobat Reader快得多。
  2. 用户界面不加载,只需打印即可。
  3. 可以将SumatraPDF作为一个独立的应用程序使用,因此您可以将其与您的应用程序一起使用,以便您可以使用自己的页面。请注意,我没有阅读许可协议;您可能需要自行检查。

经过6年的时间,我猜这仍然是打印PDF文件最干净的免费解决方案。它只有一个便携式exe文件,无需安装。您需要做的就是将此文件复制到exe文件所在的文件夹中。 - Sinan ILYAS
我在2015年从Foxit PDF转换到了Sumatra。有点令人沮丧的是,至今仍然没有更好的选择。 - Ben Curthoys
我在2015年从Foxit PDF转换到了Sumatra。有点令人沮丧的是,至今仍然没有更好的选择。 - undefined

3
如果您可以选择商业库,可以尝试使用Amyuni PDF Creator. Net.使用库进行直接打印:
要打开PDF文件并直接发送到打印机,请使用IacDocument.Print方法。C#代码如下:
// Open PDF document from file<br>
FileStream file1 = new FileStream ("test.pdf", FileMode.Open, FileAccess.Read);
IacDocument doc1 = new IacDocument (null);
doc1.Open (file1, "" );
// print document to a specified printer with no prompt
doc1.Print ("My Laser Printer", false);

导出图片(如有需要,再打印):
选择1:您可以使用IacDocument.ExportToJPeg方法将PDF的所有页面转换为JPG图像,然后使用Drawing.Image打印或显示。

选择2:您可以使用IacDocument.DrawCurrentPage方法将每个页面绘制到位图中,并使用System.Drawing.Graphics.FromImage方法。C#代码应该如下所示:

FileStream myFile = new FileStream ("test.pdf", FileMode.Open, FileAccess.Read);
IacDocument doc = new IacDocument(null);
doc.Open(myFile);
doc.CurrentPage = 1;
Image img = new Bitmap(100,100);
Graphics gph = Graphics.FromImage(img);
IntPtr hdc = gph.GetHDC();
doc.DrawCurrentPage(hdc, false);
gph.ReleaseHdc( hdc );

免责声明:我在Amyuni Technologies工作


2
另一种方法是使用.NET中的spooler函数将预格式化的打印机数据发送到打印机。但不幸的是,您需要使用win32 spooler API进行工作。
您可以查看如何使用Visual C# .NET发送原始数据到打印机 只有当打印机本地支持PDF文档时,才能使用此方法。

我在回答中没有提到的好点子。我会更新答案,但这可能是 OP 需要的吗? - Turbot
1
好的观点,但我不能确定所有打印机都原生支持它。即使目前所有打印机都支持它,如果他们购买另一种没有此功能的产品,就会出现问题。 - Mx.
1
链接现在已经无法访问。 - funkymushroom
重新查看一下,您可能可以访问此处编写的原始打印库。 https://github.com/trehoffman/RawPrint 使用 Window 服务的实现,可能可以使用 RawPrinterHelper.cs - Turbot

1
Process proc = new Process();
proc.StartInfo.FileName = @"C:\Program Files\Adobe\Acrobat 7.0\Reader\AcroRd32.exe";
proc.StartInfo.Arguments = @"/p /h C:\Documents and Settings\brendal\Desktop\Test.pdf";
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.CreateNoWindow = true;
proc.Start();

for (int i = 0; i < 5; i++)
{
    if (!proc.HasExited)
    {
        proc.Refresh();
        Thread.Sleep(2000);
    }
    else
        break;
}
if (!proc.HasExited)
{
    proc.CloseMainWindow();
}

1
我的公司提供Docotic.Pdf库,可以渲染和打印PDF文档。链接后面的文章包含以下主题的详细信息:
  • 直接在Windows Forms或WPF应用程序中打印PDF
  • 通过中间图像打印PDF
  • 在Graphics上呈现PDF
还有示例代码的链接。
我在这家公司工作,请阅读文章并自行尝试建议的解决方案。

0

如果您对商业解决方案感兴趣,并且需要的正是这些解决方案,那么有很多选择。我的公司提供了其中一个选项,即开发人员工具包Debenu Quick PDF Library

以下是代码示例(关键函数为PrintOptionsPrintDocument):

/* Print a document  */

// Load a local sample file from the input folder

DPL.LoadFromFile("Test.pdf", "");

// Configure print options

iPrintOptions = DPL.PrintOptions(0, 0, "Printing Sample")

// Print the current document to the default 
// printing using the options as configured above.
// You can also specify the specific printer.

DPL.PrintDocument(DPL.GetDefaultPrinterName(), 1, 1, iPrintOptions);

0

您可以使用Ghostscript将PDF转换为图像格式。

以下示例将单个PDF转换为一系列PNG文件:

private static void ExecuteGhostscript(string input, string tempDirectory)
{
    // %d will be replaced by ghostscript with a number for each page
    string filename = Path.GetFileNameWithoutExtension(input) + "-%d.png";
    string output = Path.Combine(tempDirectory, filename);

    Process ghostscript = new Process();
    ghostscript.StartInfo.FileName = _pathToGhostscript;
    ghostscript.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;

    ghostscript.StartInfo.Arguments = string.Format(
        "-dSAFER -dBATCH -dNOPAUSE -sDEVICE=png16m -r300 -sOutputFile=\"{0}\" \"{1}\"", output, input);

    ghostscript.Start();
    ghostscript.WaitForExit();
}

如果您更喜欢使用Adobe Reader,您可以隐藏它的窗口:

process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;

0

好的,刚看到这个不行:Adobe的论坛说,新版本的Adobe Reader将不允许您“静默”打印,“出于安全原因”(当然,Adobe)...总会有一个窗口。 - Hinek

0

我知道这个标签有 Windows Forms;然而,由于一般的标题,有些人可能会想知道他们是否可以在 WPF 应用程序中使用该命名空间 -- 他们可以。

以下是代码:

var file = File.ReadAllBytes(pdfFilePath);
var printQueue = LocalPrintServer.GetDefaultPrintQueue();

using (var job = printQueue.AddJob())
using (var stream = job.JobStream)
{
    stream.Write(file, 0, file.Length);
}

现在,这个命名空间必须与 WPF 应用程序一起使用。它与 ASP.NETWindows Service 不兼容。不应该与 Windows Forms 一起使用,因为它有 System.Drawing.Printing。使用上述代码打印 PDF 时,我没有任何问题。

请注意,如果您的打印机不支持 PDF 文件的直接打印,则此方法将无法工作。


1
尝试了一下,看到它在我的打印队列中短暂地出现,然后很快就消失了。也许我的打印机不支持它,但它确实写入了队列! - Caleb W.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接