WkHtmlToPDF — Работа со стандартным вводом и верхними/нижними колонтитулами

Я пытаюсь отобразить HTML-документ в формате PDF с помощью wkhtmltopdf.exe, который я вызываю из веб-приложения C#.

Документ 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, поэтому мне нужно вставить абсолютный путь через Razor во время выполнения.

Для этой проблемы я попытался установить рабочий каталог процесса в соответствии с относительными путями, но безрезультатно:

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

Если бы я мог решить эту проблему, это также решило бы 90% проблемы.


person JulianR    schedule 26.10.2012    source источник
comment
У меня была та же проблема, но поскольку Wkhtmltopdf угрожает заголовкам и нижним колонтитулам отдельно (через внешние пути / URL-адреса), я закончил тем, что определил html-документ, который я передал динамические данные в его URL-адрес. Затем я извлек их с помощью javascript.   -  person Alex    schedule 06.11.2012


Ответы (1)


Обратите внимание, если вы решили этот 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-контент всей страницы.

person SkyBlues87    schedule 12.09.2013
comment
Спасибо за информацию. Я так и не решил эту проблему, а вместо этого использовал отчеты Microsoft RDLC, которые имели ряд преимуществ (простота, встроенная поддержка печати), несмотря на значительную громоздкость при редактировании отчетов. - person JulianR; 12.09.2013