How to get wkhtmltopdf logging info in C#? - c#

I'm using wkhtmltopdf to convert some html to pdf. I think I'm getting some javascrpot errors and I'd like to get access to some debug\logging from wkhtmltopdf. Does anyone know how to get the logging info?
var wkhtmlDir = Settings.HtmlToPdfFolderPath;
var wkhtml = Settings.HtmlToPdfExePath;
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.WorkingDirectory = wkhtmlDir;
string switches = "";
switches += "--print-media-type --redirect-delay 800 ";
//switches += "--margin-top 10mm --margin-bottom 10mm --margin-right 10mm --margin-left 10mm ";
switches += "--page-size Letter ";
p.StartInfo.Arguments = switches + " " + url
p.Start();
//read output
byte[] buffer = new byte[32768];
byte[] file;
using (var ms = new MemoryStream())
{
while (true)
{
int read = p.StandardOutput.BaseStream.Read(buffer, 0, buffer.Length);
if (read <= 0)
{
break;
}
ms.Write(buffer, 0, read);
}
file = ms.ToArray();
}
// wait or exit
p.WaitForExit(60000);
// read the exit code, close process
int returnCode = p.ExitCode;
p.Close();
//return returnCode == 0 ? file : null;
return file;

You can read standard error and standard output:
String output =string.Format("STD: {0}\nErr: {1}", p.StandardOutput.ReadToEnd(),
p.StandardError.ReadToEnd());
Is that what you are looking for?

Related

Printing PDF using ReportViewer from C# windows

Here is my creating PDF code, which will open the PDF document .
public void createPDF(string Reportpath, ReportViewer RV)
{
Warning[] warnings;
string[] streamids;
string mimeType = string.Empty;
string encoding = string.Empty;
string extension = string.Empty;
byte[] bytes = RV.LocalReport.Render("pdf", null, out mimeType, out encoding, out extension, out streamids, out warnings);
try
{
FileStream fs = new FileStream(Reportpath, FileMode.Create);
Thread.Sleep(1000);
fs.Write(bytes, 0, bytes.Length);
fs.Close();
Thread.Sleep(1000);
System.Diagnostics.Process.Start(Reportpath);
}
catch (Exception ex)
{
MessageBox.Show("Report could not be created...\n" + ex.Message);
}
}
instead of opening i need to print the pdf directly using reportviewer or any other way to print that pdf document??.
I think this MSDN article gives a good solution to your problem
got some idea from the following link
http://www.codeproject.com/Tips/598424/How-to-Silently-Print-PDFs-using-Adobe-Reader-and
send the pdf to adobe reader to print....
public static Boolean PrintPDFs(string pdfFileName)
{
try
{
Process proc = new Process();
proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
proc.StartInfo.Verb = "print";
//Define location of adobe reader/command line
//switches to launch adobe in "print" mode
proc.StartInfo.FileName =
#"C:\Program Files (x86)\Adobe\Reader 11.0\Reader\AcroRd32.exe";
proc.StartInfo.Arguments = String.Format(#"/p /h {0}", pdfFileName);
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.CreateNoWindow = true;
proc.Start();
proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
if (proc.HasExited == false)
{
proc.WaitForExit(10000);
}
proc.EnableRaisingEvents = true;
proc.Close();
KillAdobe("AcroRd32");
return true;
}
catch
{
return false;
}
}

Read a byte array from a redirected process

I am using the process object in c#
I am also using FFMPEG.
I am trying to read the bytes from the redirected output. i know the data is an image but when I use the following code I do not get an image byte array.
this is my code:
var process = new Process();
process.StartInfo.FileName = #"C:\bin\ffmpeg.exe";
process.StartInfo.Arguments = #" -i rtsp://admin:admin#192.168.0.8:554/video_1 -an -f image2 -s 360x240 -vframes 1 -";
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.UseShellExecute = false;
process.Start();
var output = process.StandardOutput.ReadToEnd();
byte[] bytes = Encoding.ASCII.GetBytes(output);
The 1st bytes are not the header of a jpeg?
I think treating the output as a text stream is not the right thing to do here. Something like this worked for me, just directly read the data off the output pipe, it doesn't need conversion.
var process = new Process();
process.StartInfo.FileName = #"C:\bin\ffmpeg.exe";
// take frame at 17 seconds
process.StartInfo.Arguments = #" -i c:\temp\input.mp4 -an -f image2 -vframes 1 -ss 00:00:17 pipe:1";
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.UseShellExecute = false;
process.Start();
FileStream baseStream = process.StandardOutput.BaseStream as FileStream;
byte[] imageBytes = null;
int lastRead = 0;
using (MemoryStream ms = new MemoryStream())
{
byte[] buffer = new byte[4096];
do
{
lastRead = baseStream.Read(buffer, 0, buffer.Length);
ms.Write(buffer, 0, lastRead);
} while (lastRead > 0);
imageBytes = ms.ToArray();
}
using (FileStream s = new FileStream(#"c:\temp\singleFrame.jpeg", FileMode.Create))
{
s.Write(imageBytes, 0, imageBytes.Length);
}
Console.ReadKey();

C# FFmpeg rtsp byte stream

How can I open a constant byte stream that I can read while it's playing? I am able to get a single image with a resolution of 100:75, but when I bump up the resolution the process never finishes.
I need help improving the performance this method, I'm sure it's possible but I know very little about FFmpeg.
I'd like to be able to:
List item
Get a full res image
Read while the process is running
Constantly read byte output from a live stream
This is how I get the single image, it's running on a background thread.
string stream = "-i " + url + " -vf scale=100:75 -an -vframes 1 -r 1 -f image2pipe pipe:bmp";
var process = new Process();
process.StartInfo.FileName = ffmpeg.exe;
process.StartInfo.Arguments = stream;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.CreateNoWindow = true;
process.Start();
process.WaitForExit();
Stream outputReader = process.StandardOutput.BaseStream;
byte[] buffer = new byte[16*1024];
using (MemoryStream ms = new MemoryStream())
{
int read;
while ((read = outputReader.Read(buffer, 0, buffer.Length)) > 0)
{
ms.Write(buffer, 0, read);
}
BitmapImage bitmapImage = new BitmapImage();
bitmapImage.BeginInit();
bitmapImage.StreamSource = new MemoryStream(ms.ToArray());
bitmapImage.EndInit();
bitmapImage.Freeze();
Image = bitmapImage;
}

Save to local drive

I had done a task in which the csv files can be selected using calendar and download form FTP.
At present I had predefined the local drive path to save the csv files.
I need to show pop for save as then the user has to save to any location as they need.
private void Download()
{
System.DateTime myDate = new System.DateTime();
myDate = caldownload.SelectedDate;
System.DateTime myDate1 = new System.DateTime();
myDate1 = Calendar1.SelectedDate;
string sdate;
string edate;
TextBox1.Text = myDate.ToString("yyyy-MM-dd");
TextBox2.Text = myDate1.ToString("yyyy-MM-dd");
DateTime d1 = myDate.Date;
DateTime d2 = myDate1.Date;
TimeSpan sum = d2 - d1;
int NrOfDays = sum.Days;
NrOfDays++;
int k = NrOfDays;
string[] fileName = new string[k];
//increment for days
int s = myDate.Day;
FtpWebRequest reqFTP;
try
{
if (NrOfDays<=7)
{
for (k = 0; k <= NrOfDays; k++)
{
fname[i] = myDate.ToString("yyyy-MM-dd");
fileName[k] = myDate.ToString("yyyy-MM-dd");
//files to save local drive
FileStream outputStream = new FileStream("F:\\ch" + "\\" + fname[i]+".csv", FileMode.Create);
reqFTP = (FtpWebRequest)FtpWebRequest.Create(new Uri("ftp://" + "192.162.1.152" + "//" + "[100]" + "//" + fileName[k] + ".csv"));
reqFTP.Method = WebRequestMethods.Ftp.ListDirectory;
reqFTP.Method = WebRequestMethods.Ftp.DownloadFile;
reqFTP.UseBinary = true;
reqFTP.Credentials = new NetworkCredential("", "");
FtpWebResponse response = (FtpWebResponse)reqFTP.GetResponse();
Stream ftpStream = response.GetResponseStream();
long cl = response.ContentLength;
int bufferSize = 2048;
int readCount;
byte[] buffer = new byte[bufferSize];
readCount = ftpStream.Read(buffer, 0, bufferSize);
while (readCount > 0)
{
outputStream.Write(buffer, 0, readCount);
readCount = ftpStream.Read(buffer, 0, bufferSize);
}
myDate = myDate.AddDays(1);
ftpStream.Close();
outputStream.Close();
response.Close();
}
}
else
{
ScriptManager.RegisterStartupScript(this, this.GetType(), "message", "alert('You are allowed to download files for maximum of 7 days .');location.href = 'Default.aspx';", true);
}
}
catch (Exception ex)
{ }
}
You can add a SaveFileDialog to retrieve the name of the file from the user.
For example:
string sFileNameToSaveAs = "";
using (var dialog = new SaveFileDialog())
{
dialog.AddExtension = true;
dialog.Filter = "CSV Files (*.csv) | *.csv";
dialog.Title = "Select the file name to save as";
dialog.InitialDirectory = "C:\\";
dialog.CheckPathExists = true;
dialog.DefaultExt = ".csv";
dialog.ValidateNames = true;
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
sFileNameToSaveAs = dialog.FileName;
}
}
if (!string.IsNullOrEmpty(sFileNameToSaveAs))
{
FileStream outputStream = new FileStream(sFileNameToSaveAs, FileMode.Create);
// The rest of your retrieval code goes here
}

Process hangs when trying to read bytes from buffer to int. Wkhtmltopdf

My method creates PDF out of HTML content with Wkhtmltopdf program with Process. I can't find a reason why it hangs. When there is a big string with HTML, i suppose it hangs when there are 30 pages or more. But everything works fine if there are less pages. Wkhtmltopdf.exe process i can see at the Task Manager, it does not exit forever. When i stop my MVC project with hanged Wkhtmltopdf, it creates normally PDF like it would wait for something.. Manually, of course, Wkhtmltopdf creates everything without any problem.
This is not duplicate of this post. Here i have issues when trying to read bytes into int.
public IActionResult createPdf()
{
string html = "content";
Process p;
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = "...\\wkhtmltopdf.exe";
psi.WorkingDirectory = "...\\wkhtmltopdf\\bin";
psi.UseShellExecute = false;
psi.CreateNoWindow = true;
psi.RedirectStandardInput = true;
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
psi.StandardOutputEncoding = System.Text.Encoding.UTF8;
psi.Arguments = "-O landscape --footer-left qwe --footer-center [page]/[topage] --footer-right --footer-font-size 9 --no-stop-slow-scripts --zoom 0.8 --dpi 300 - - ";
p = Process.Start(psi);
byte[] pdf = null;
try
{
// Get PDF as bytes without temp files
using(StreamWriter stdin = new StreamWriter(p.StandardInput.BaseStream, Encoding.UTF8))
{
stdin.AutoFlush = true;
stdin.Write(html);
}
byte[] buffer = new byte[32768];
using(var ms = new MemoryStream())
{
while(true)
{
// HANGS HERE!!!
int read = p.StandardOutput.BaseStream.Read(buffer, 0, buffer.Length);
if(read <= 0)
{
break;
}
ms.Write(buffer, 0, read);
}
pdf = ms.ToArray();
}
}
...
}
I managed to solve this problem through merging separated pdf files into one using PdfSharp, instead of passing concatenated content to wkhtmltopdf as one. It lasts a little bit longer, but at least it works. Except of PdfSharp library (if you use MVC as me, then PdfSharp.Core to exclude version warnings), probably you would need to install System.Text.Encoding.CodePages to exclude encoding error that PdfSharp may cause. My method does not use temp files and works only with bytes, at the end it sends created pdf to a browser.
using PdfSharp.Pdf;
using PdfSharp.Pdf.IO;
public IActionResult createPdf()
{
string html = "";
byte[] pdf = null;
using(PdfDocument doc = new PdfDocument())
{
for(int i = 0; i < files.Length; i++)
{
html = "html content";
Process p;
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = "...\\wkhtmltopdf.exe";
psi.WorkingDirectory = "...\\wkhtmltopdf\\bin";
psi.UseShellExecute = false;
psi.CreateNoWindow = true;
psi.RedirectStandardInput = true;
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
psi.StandardOutputEncoding = System.Text.Encoding.UTF8;
psi.Arguments = "-O landscape --footer-left qwe --footer-center [page]/[topage] --footer-right --footer-font-size 9 --no-stop-slow-scripts --zoom 0.8 --dpi 300 - - ";
p = Process.Start(psi);
using(StreamWriter stdin = new StreamWriter(p.StandardInput.BaseStream, Encoding.UTF8))
{
stdin.AutoFlush = true;
stdin.Write(html);
}
byte[] buffer = new byte[32768];
byte[] currentPdf = null;
using(var ms = new MemoryStream())
{
while(true)
{
int read = p.StandardOutput.BaseStream.Read(buffer, 0, buffer.Length);
if(read <= 0)
{
break;
}
ms.Write(buffer, 0, read);
}
currentPdf = ms.ToArray();
}
p.StandardOutput.Close();
p.WaitForExit(10000);
p.Close();
MemoryStream currentPDF = new MemoryStream(currentPdf);
// Merge separated pdfs into one
// Solves encoding errors
System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance);
using(PdfDocument pdfDoc = PdfReader.Open(currentPDF, PdfDocumentOpenMode.Import))
{
for(int j = 0; j < pdfDoc.PageCount; j++)
{
doc.AddPage(pdfDoc.Pages[j]);
}
}
currentPDF.Close();
}
// Get merged pdfs as bytes
MemoryStream rms = new MemoryStream();
doc.Save(rms, false);
pdf = rms.ToArray();
rms.Close();
}
MemoryStream PDF = new MemoryStream(pdf);
// Return PDF to browser
return new FileStreamResult(PDF, "application/x-msdownload")
{
FileDownloadName = "mergedPdfs.pdf"
};
}

Categories