2011-01-10 19 views
22

cómo pasar html como una cadena en lugar de url en wkhtmltopdf usando asp.net, C#?cómo pasar html como una cadena usando wkhtmltopdf?

+0

Probablemente sería más fácil redirigir stdin y pasar la cadena de esa manera. También podría escribir el texto en un archivo (aunque eso no va a escalar muy bien, por supuesto). –

+0

¿Podría mostrar un ejemplo? – Eugene

Respuesta

16

Redirigir STDIN es probablemente la forma más fácil de lograr lo que estás tratando de hacer.

Un método para redirigir STDIN con wkhtmltopdf (en ASP.Net) es el siguiente:

private void WritePDF(string HTML) 
    { 
     string inFileName, 
       outFileName, 
       tempPath; 
     Process p; 
     System.IO.StreamWriter stdin; 
     ProcessStartInfo psi = new ProcessStartInfo(); 

     tempPath = Request.PhysicalApplicationPath + "temp\\"; 
     inFileName = Session.SessionID + ".htm"; 
     outFileName = Session.SessionID + ".pdf"; 

     // run the conversion utility 
     psi.UseShellExecute = false; 
     psi.FileName = "c:\\Program Files (x86)\\wkhtmltopdf\\wkhtmltopdf.exe"; 
     psi.CreateNoWindow = true; 
     psi.RedirectStandardInput = true; 
     psi.RedirectStandardOutput = true; 
     psi.RedirectStandardError = true; 

     // note that we tell wkhtmltopdf to be quiet and not run scripts 
     // NOTE: I couldn't figure out a way to get both stdin and stdout redirected so we have to write to a file and then clean up afterwards 
     psi.Arguments = "-q -n - " + tempPath + outFileName; 

     p = Process.Start(psi); 

     try 
     { 
      stdin = p.StandardInput; 
      stdin.AutoFlush = true; 

      stdin.Write(HTML); 
      stdin.Close(); 

      if (p.WaitForExit(15000)) 
      { 
       // NOTE: the application hangs when we use WriteFile (due to the Delete below?); this works 
       Response.BinaryWrite(System.IO.File.ReadAllBytes(tempPath + outFileName)); 
       //Response.WriteFile(tempPath + outFileName); 
      } 
     } 
     finally 
     { 
      p.Close(); 
      p.Dispose(); 
     } 

     // delete the pdf 
     System.IO.File.Delete(tempPath + outFileName); 
    } 

Tenga en cuenta que el código anterior se supone que hay un directorio temporal disponible en el directorio de la aplicación. Además, debe habilitar explícitamente el acceso de escritura a ese directorio para la cuenta de usuario utilizada al ejecutar el proceso IIS.

+0

No consigo que esto funcione con imágenes y hojas de estilo referenciadas. Es eso posible? Cuando ejecuto la línea de comandos con archivos en el disco, funciona. – smerlung

+0

@smerlung Asegúrese de que la aplicación web tenga acceso a los archivos a los que hace referencia. Cuando se ejecuta desde la línea de comando, se está ejecutando como su inicio de sesión; cuando se ejecuta desde el servidor web, se está ejecutando como el inicio de sesión del servidor web, que puede no tener acceso a los archivos que está utilizando. Además, asegúrese de que la ruta a los archivos también tenga sentido desde esa ubicación. Por ejemplo, si hace referencia a '.. \ styles \ style.css', entonces debe haber un directorio' styles', 'arriba' donde está ejecutando el programa, que contiene el archivo 'style.css'. –

+0

Lo tengo trabajando usando rutas absolutas. Pero eso no es muy bueno. Entiendo lo que dices, pero mi problema es que no entiendo dónde está "funcionando" el proceso. He intentado diferentes opciones para WorkingDirectory pero no pude resolverlo? – smerlung

37

STDIn y STDOut se han redirigido en este ejemplo, por lo que no debería necesitar archivos en absoluto.

public static class Printer 
{ 
    public const string HtmlToPdfExePath = "wkhtmltopdf.exe"; 

    public static bool GeneratePdf(string commandLocation, StreamReader html, Stream pdf, Size pageSize) 
    { 
     Process p; 
     StreamWriter stdin; 
     ProcessStartInfo psi = new ProcessStartInfo(); 

     psi.FileName = Path.Combine(commandLocation, HtmlToPdfExePath); 
     psi.WorkingDirectory = Path.GetDirectoryName(psi.FileName); 

     // run the conversion utility 
     psi.UseShellExecute = false; 
     psi.CreateNoWindow = true; 
     psi.RedirectStandardInput = true; 
     psi.RedirectStandardOutput = true; 
     psi.RedirectStandardError = true; 

     // note: that we tell wkhtmltopdf to be quiet and not run scripts 
     psi.Arguments = "-q -n --disable-smart-shrinking " + (pageSize.IsEmpty? "" : "--page-width " + pageSize.Width + "mm --page-height " + pageSize.Height + "mm") + " - -"; 

     p = Process.Start(psi); 

     try { 
      stdin = p.StandardInput; 
      stdin.AutoFlush = true; 
      stdin.Write(html.ReadToEnd()); 
      stdin.Dispose(); 

      CopyStream(p.StandardOutput.BaseStream, pdf); 
      p.StandardOutput.Close(); 
      pdf.Position = 0; 

      p.WaitForExit(10000); 

      return true; 
     } catch { 
      return false; 

     } finally { 
      p.Dispose(); 
     } 
    } 

    public static void CopyStream(Stream input, Stream output) 
    { 
     byte[] buffer = new byte[32768]; 
     int read; 
     while ((read = input.Read(buffer, 0, buffer.Length)) > 0) { 
      output.Write(buffer, 0, read); 
     } 
    } 
} 
+0

no es necesario ** Cerrar ** y ** Eliminar ** el Proceso, hace lo mismo. Solo deséchelo o póngalo en una construcción en uso. Lo mismo aplica para las transmisiones ... –

+0

Ahora solo deseche se llama –

+0

sí, esto es mejor :) –

3

Sé que esta es una publicación anterior, pero quiero que los futuros desarrolladores tengan esta opción. Tenía la misma necesidad, y la idea de tener que iniciar un proceso en segundo plano solo para obtener un PDF dentro de una aplicación web es terrible.

Aquí hay otra opción: https://github.com/TimothyKhouri/WkHtmlToXDotNet

Es una envoltura alrededor nativa .NET wkhtmltopdf.

Código de ejemplo aquí:

var pdfData = HtmlToXConverter.ConvertToPdf("<h1>COOOL!</h1>"); 

Nota, no es seguro para subprocesos a partir de ahora - Estoy trabajando en eso. Entonces use un monitor o algo así o un candado.

Cuestiones relacionadas