8

我正在尝试使用从 C# Web 应用程序调用的 wkhtmltopdf.exe 将 HTML 文档呈现为 PDF。

HTML 文档需要在每个页面上重复出现页脚和页眉,这可以通过 wkhtmltopdf 指定--header-html <a path>为参数来实现。

但是,页脚是从 Razor 视图动态呈现的,我宁愿不必将其存储在磁盘上的临时文件中并使用该路径,但我想使用已经在内存中呈现的 HTML。通过写入StandardInput流,文档本身可以做到这一点,如下所示:

var wkhtml = ConfigurationManager.AppSettings["WkHtmlToPdfPath"];
var p = new Process();

p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.UseShellExecute = false;
p.StartInfo.FileName = wkhtml;

p.StartInfo.Arguments = "-q -n --disable-smart-shrinking - -";
p.Start();

var stdin = p.StandardInput;
stdin.AutoFlush = true;
stdin.Write(template);
stdin.Dispose();

是否可以对页眉和页脚 HTML 执行相同的操作,即直接将其传递给内联而不必求助于临时文件?

我试过了:

stdin.Write(string.Format("--footer-html {0} ", footer));

但当然,它只是将其视为文档的一部分,而不是页脚。

我也想动态渲染页脚和页眉的主要原因(主要)是由另一个问题引起的。虽然拥有动态页眉和页脚会很好,但这主要是为了解决我必须使用绝对路径(即:C:\templates\images\logo.png)链接到图像的问题,因为相对路径(即: images/logo.png)在您使用标准输入并仅传入 HTML 字符串 blob 时不起作用,因此我需要在运行时通过 Razor 插入绝对路径。

对于这个问题,我尝试设置进程的工作目录以匹配相对路径,但无济于事:

p.StartInfo.WorkingDirectory = @"C:\templates";

如果我能解决这个问题,那也将解决 90% 的问题。

4

1 回答 1

3

请注意,如果您解决了这个 JulianR,我也会假设您在 MVC 中(?)如果没有,您可以忽略下面的一些初始代码,但我有类似的情况,由于安全,我需要将输出直接流式传输到 wkhtmltopdf并登录网站的各个部分。

首先,在控制器中,您可以使用任何适用的母版页(本身可能使用页眉和页脚)拉入显示所需的视图:

var view = ViewEngines.Engines.FindView(ControllerContext, myViewName, myMasterPageLayout);

然后,您可以使用任何必要的 ViewData、Tempdata 等获取此视图的当前信息,并将其存储在字符串中(内容如下):

string content;
ViewData.Model = model;
using (var writer = new System.IO.StringWriter())
{
    var context = new ViewContext(ControllerContext, view.View, ViewData, TempData, writer);
    view.View.Render(context, writer);
    writer.Flush();
    content = writer.ToString();
    writer.Close();
}

在这个阶段,如果需要,您实际上可以修改字符串中的输出 html - 例如,将任何本地路径更改为完整路径

随着您输出 HTML,您只需传递到 wkhtmltopdf:

var p = new Process();
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.UseShellExecute = false;
//Other parameters as required
byte[] file;
try
{
    p.Start();
    byte[] buffer = new byte[32768];

    using (System.IO.StreamWriter stdin = p.StandardInput)
    {
        stdin.AutoFlush = true;
        stdin.Write(content);
    }


    using (MemoryStream ms = new MemoryStream())
    {
        ms.Position = 0;
        p.StandardOutput.BaseStream.CopyTo(ms);
        file = ms.ToArray();
    }

    p.StandardOutput.Close();
    // wait or exit
    p.WaitForExit(60000);

    // read the exit code, close process
    int returnCode = p.ExitCode;

}

然后你有一个字节数组,其中包含整个页面的 PDF 内容。

于 2013-09-12T12:59:46.517 回答