Hi am using Wkhtmltopdf to create PDF from Html page using c#, Asp.net. PDF are created smoothly, but when I add images to HTML it does not get displayed on PDF.
<div id="Image" class="right" style="background:#333;height:15%;width:25%;">
<img width="100%" height="100%" src="../TempLogo/chafa.jpg" />
I have used "file:///E:/AMS/AMS/AMS.WEB/TempLogo/chafa.jpg" & "src="localhost:5822/AMS.WEB/TempLogo/chafa.jpg"; also but it does not work. I also tried to change Version from 0.11 to 0.9 but it also not succeed
public static string HtmlToPdf(string pdfOutputLocation, string outputFilenamePrefix, string[] urls,
string[] options = null,
// string pdfHtmlToPdfExePath = "C:\\Program Files (x86)\\wkhtmltopdf\\wkhtmltopdf.exe")
string pdfHtmlToPdfExePath = "C:\\Program Files\\wkhtmltopdf\\wkhtmltopdf.exe")
{
string urlsSeparatedBySpaces = string.Empty;
try
{
//Determine inputs
if ((urls == null) || (urls.Length == 0))
throw new Exception("No input URLs provided for HtmlToPdf");
else
urlsSeparatedBySpaces = String.Join(" ", urls); //Concatenate URLs
string outputFolder = pdfOutputLocation;
string outputFilename = outputFilenamePrefix + "_" + DateTime.Now.ToString("yyyy-MM-dd-hh-mm-ss-fff") + ".PDF"; // assemble destination PDF file name
var p = new System.Diagnostics.Process()
{
StartInfo =
{
FileName = pdfHtmlToPdfExePath,
Arguments = ((options == null) ? "" : String.Join(" ", options)) + " " + urlsSeparatedBySpaces + " " + outputFilename,
UseShellExecute = false, // needs to be false in order to redirect output
RedirectStandardOutput = true,
RedirectStandardError = true,
RedirectStandardInput = true, // redirect all 3, as it should be all 3 or none
WorkingDirectory = HttpContext.Current.Server.MapPath(outputFolder),
CreateNoWindow = true
}
};
p.Start();
// read the output here...
var output = p.StandardOutput.ReadToEnd();
var errorOutput = p.StandardError.ReadToEnd();
// ...then wait n milliseconds for exit (as after exit, it can't read the output)
p.WaitForExit(60000);
// read the exit code, close process
int returnCode = p.ExitCode;
p.Close();
// if 0 or 2, it worked so return path of pdf
if ((returnCode == 0) || (returnCode == 2))
return outputFolder + outputFilename;
else
throw new Exception(errorOutput);
}
catch (Exception exc)
{
throw new Exception("Problem generating PDF from HTML, URLs: " + urlsSeparatedBySpaces + ", outputFilename: " + outputFilenamePrefix, exc);
}
}
Related
My program allow user to insert bunch of images and one audio file, then can generate them into a video file. My program also allow user to provide duration for each images.
I am able to adding audio background into video output, but when video length is more longer than audio length, audio is not looping.
Also when audio length is more longer than video length, the video output will show blank till the end of audio.
Create Video
public bool CreateVideo(string name, List<string> imgs)
{
Bitmap bitmap = (Bitmap)null;
try
{
if (!Directory.Exists(this.outputFolder + "\\temp"))
Directory.CreateDirectory(this.outputFolder + "\\temp");
int num = this.rand.Next(this.delayMin, this.delayMax + 1);
List<string> stringList = new List<string>((IEnumerable<string>)imgs);
if (this.randomizeImages)
this.Shuffle((IList)stringList);
if (!string.IsNullOrEmpty(this.waterImage) && File.Exists(this.waterImage))
bitmap = (Bitmap)Image.FromFile(this.waterImage, true);
if (!string.IsNullOrEmpty(this.introImage))
{
int index = stringList.IndexOf(this.introImage);
if (index > 0)
{
string str = stringList[index];
stringList[index] = stringList[0];
stringList[0] = str;
}
}
for (int position = 0; position < stringList.Count; ++position)
this.ApplyWatermarkImage(stringList[position], position, (Image)bitmap);
if (bitmap != null)
bitmap.Dispose();
string str1 = "";
if (!string.IsNullOrEmpty(this.audioFile))
{
str1 = " -i \"" + this.audioFile + "\" -ar 22050 -ab 64k ";
if (this.GetAudioLength(this.audioFile) > num * stringList.Count)
str1 += "-shortest ";
}
string str2 = this.outputFolder + "\\" + name + ".avi";
ProcessStartInfo processStartInfo = new ProcessStartInfo(Environment.CurrentDirectory + "\\sm\\ffmpeg.exe", str1 + "-fflags +genpts -r 1/" + (object)num + " -b 32K -f image2 -i \"" + this.outputFolder + "\\temp\\%3d.jpg\" -vcodec mjpeg -s " + this.width.ToString() + "x" + this.height.ToString() + " -y -r 30 \"" + str2 + "\"");
Process process = new Process();
processStartInfo.WorkingDirectory = Directory.GetCurrentDirectory();
processStartInfo.CreateNoWindow = true;
processStartInfo.UseShellExecute = false;
processStartInfo.RedirectStandardError = true;
process.StartInfo = processStartInfo;
process.Start();
StreamReader standardError = process.StandardError;
Trace.WriteLine(standardError.ReadToEnd());
standardError.Close();
process.WaitForExit();
return process.ExitCode != 1 && process.ExitCode != -1;
}
catch (Exception ex)
{
Trace.WriteLine("CreateVideo: " + ex.Message);
return false;
}
finally
{
if (bitmap != null)
bitmap.Dispose();
try
{
if (Directory.Exists(this.outputFolder + "\\temp"))
Directory.Delete(this.outputFolder + "\\temp", true);
}
catch
{
}
}
}
Get Audio Length
public int GetAudioLength(string path)
{
string output = this.GetOutput(path);
int num = 0;
if (output == "" || output.Contains("Invalid data found when processing input") || output.Contains("could not find codec parameters"))
return num;
string str;
try
{
str = Regex.Match(output, "(?:Duration\\:)(.*?)(?:,)", RegexOptions.Singleline).Groups[1].Value.Trim();
}
catch (Exception ex)
{
str = "";
}
if (str != "")
{
try
{
num = int.Parse(str.Split(':')[0]) * 3600;
num += int.Parse(str.Split(':')[1]) * 60;
num += (int)double.Parse(str.Split(':')[2]);
}
catch (Exception ex)
{
}
}
return num;
}
Get Output
private string GetOutput(string video_file)
{
try
{
return Process.Start(new ProcessStartInfo()
{
FileName = Environment.CurrentDirectory + "\\sm\\ffmpeg.exe",
Arguments = "-i \"" + video_file + "\"",
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true,
WindowStyle = ProcessWindowStyle.Hidden
}).StandardError.ReadToEnd();
}
catch (Exception ex)
{
return "";
}
}
I've looked around but couldn't find a solution. Any help will be greatly appreciated.
how can i generate hocr using the tesseract wrapper here
currently i need to dynamically add the location of the tessdata to the environment variables and run my code
System.Diagnostics.Process pProcess = new System.Diagnostics.Process();
pProcess.StartInfo.FileName = System.IO.Path.GetDirectoryName(Environment.GetCommandLineArgs()[0]) + #"\tesseract-3.05.00dev-win32-vc19\tesseract.exe";
string inputImg = #"00067.jpg";
string hocrLocation = #"00067";
string argsPdf = "\"" + inputImg + "\"" + " " + "\"" + hocrLocation + "\"" + " hocr ";
Console.WriteLine(argsPdf);
pProcess.StartInfo.Arguments = argsPdf;
pProcess.StartInfo.CreateNoWindow = false;
pProcess.StartInfo.UseShellExecute = false;
pProcess.StartInfo.RedirectStandardOutput = true;
pProcess.Start();
string strOutput = pProcess.StandardOutput.ReadToEnd();
Console.WriteLine("OUtput: " + strOutput);
pProcess.WaitForExit();
and then i found the tesseract wrapper. how can i generate an hocr file using the wrapper? i cant find an example how to do it.
this is the current code(from the example ) im using but how to output an hocr file?
var testImagePath = "./phototest.tif";
if (args.Length > 0)
{
testImagePath = args[0];
}
try
{
using (var engine = new TesseractEngine(#"./tessdata", "eng", EngineMode.Default))
{
using (var img = Pix.LoadFromFile(testImagePath))
{
using (var page = engine.Process(img))
{
}
}
}
}
catch (Exception e)
{
Trace.TraceError(e.ToString());
Console.WriteLine("Unexpected Error: " + e.Message);
Console.WriteLine("Details: ");
Console.WriteLine(e.ToString());
}
string hocrText = page.GetHOCRText(pageNum - 1);
Back few days i'm trying to make a web application that convert url to pdf. Finally i have done this with the help of wkhtmltopdf.exe
My class code is given below
public class PDFGenerator
{
public static string HtmlToPdf(string pdfOutputLocation, string outputFilenamePrefix, string[] urls,
string[] options = null,
string pdfHtmlToPdfExePath = "C:\\Program Files\\wkhtmltopdf\\bin\\wkhtmltopdf.exe")
{
string urlsSeparatedBySpaces = string.Empty;
try
{
//Determine inputs
if ((urls == null) || (urls.Length == 0))
throw new Exception("No input URLs provided for HtmlToPdf");
else
urlsSeparatedBySpaces = String.Join(" ", urls); //Concatenate URLs
string outputFolder = pdfOutputLocation;
string outputFilename = outputFilenamePrefix + "_" + DateTime.Now.ToString("yyyy-MM-dd-hh-mm-ss-fff") + ".PDF"; // assemble destination PDF file name
var p = new System.Diagnostics.Process()
{
StartInfo =
{
FileName = pdfHtmlToPdfExePath,
Arguments = ((options == null) ? "" : String.Join(" ", options)) + " " + urlsSeparatedBySpaces + " " + outputFilename,
UseShellExecute = false, // needs to be false in order to redirect output
RedirectStandardOutput = true,
RedirectStandardError = true,
RedirectStandardInput = true, // redirect all 3, as it should be all 3 or none
WorkingDirectory = HttpContext.Current.Server.MapPath(outputFolder)
}
};
p.Start();
// read the output here...
var output = p.StandardOutput.ReadToEnd();
var errorOutput = p.StandardError.ReadToEnd();
// ...then wait n milliseconds for exit (as after exit, it can't read the output)
p.WaitForExit(60000);
// read the exit code, close process
int returnCode = p.ExitCode;
p.Close();
// if 0 or 2, it worked so return path of pdf
if ((returnCode == 0) || (returnCode == 2))
return outputFolder + outputFilename;
else
throw new Exception(errorOutput);
}
catch (Exception exc)
{
throw new Exception("Problem generating PDF from HTML, URLs: " + urlsSeparatedBySpaces + ", outputFilename: " + outputFilenamePrefix, exc);
}
}
}
and my button click event code is given below
//Create PDF from a single URL
var pdfUrl = PdfGenerator.PDFGenerator.HtmlToPdf(pdfOutputLocation: "~/PDFs/",
outputFilenamePrefix: "GeneratedPDF",
urls: new string[] { "http://news.bbc.co.uk" });
//Create PDF from multiple URLs
pdfUrl = PdfGenerator.PDFGenerator.HtmlToPdf(pdfOutputLocation: "~/PDFs/",
outputFilenamePrefix: "GeneratedPDF",
urls: new string[] { "http://www.google.co.uk", "http://news.bbc.co.uk" });
all code is working properly and saving pdf in my PDFs code. But i have 2 questions:
This code is not working when i host my website because C:\\Program Files\\wkhtmltopdf\\bin\\wkhtmltopdf.exe executing error. Is any dll library avail for wkhtmltopdf?
When i run this code pdf saved in silent mode. I want to know that is this possible to save pdf with save dialog or ask user to folder location where they want to save pdf?
Sorry for my bad english.
Place your "wkhtmltopdf.exe" in your project folder, so that it can be easily accessible to your application. There might be some access related issues if you have your .exe file in 'C' folder. Try this, it worked for me.
I've got a .aspx page with some C# code in the code behind file.
I want to execute a command in the command prompt and save the output to a .txt file. This works and a new .txt file is created.
Then I want to read the first line from the .txt file and store the string in a variable.
My current code is below, however, it throws the "The file could not be read." error message in my code. Why is this and how can I resolve this?
UPDATE:
So I've modified my code from my original post and added a check to see if the 'archivo_resultado' exists before reading from it. However, debugview outputs
"archivo_resultado doesn't exist."
so of course attempting to read it would throw an error.
What's the most efficient way to execute the following command in command prompt:
'ejecutable_CheckMac + " " + archivo_temporal'
and store the output string in a variable and to a file (archivo_resultado) ?
code:
string ejecutable_CheckMac = "C:\\inetpub\\wwwroot\\cgi-bin\\tbk_check_mac.exe";
var archivo_temporal = "C:\inetpub\wwwroot\cgi-bin\log\DatosParaCheckMac_100942.txt";
var archivo_resultado = "C:\inetpub\wwwroot\cgi-bin\log\ResultadoCheckMac_100942.txt";
System.Diagnostics.Debugger.Log(0, null, "Declare cmd variable.");
string cmd = ejecutable_CheckMac + " " + archivo_temporal + " > " + archivo_resultado;
System.Diagnostics.Debugger.Log(0, null, "cmd: " + cmd);
System.Diagnostics.Debugger.Log(0, null, "Start cmd execution.");
System.Diagnostics.Process process = new System.Diagnostics.Process();
var startInfo = new System.Diagnostics.ProcessStartInfo("cmd", "/c " + cmd);
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
startInfo.RedirectStandardOutput = true;
System.Diagnostics.Process.Start(startInfo);
if (File.Exists(archivo_resultado))
{
System.Diagnostics.Debugger.Log(0, null, "archivo_resultado exists.");
}
else
{
System.Diagnostics.Debugger.Log(0, null, "archivo_resultado doesn't exist.");
}
string returnedLine = "";
try
{
System.Diagnostics.Debugger.Log(0, null, "Start - StreamReader to read archivo_resultado: " + archivo_resultado);
using (StreamReader sr = new StreamReader(archivo_resultado))
{
returnedLine = sr.ReadLine() ?? "";
Console.WriteLine(returnedLine);
System.Diagnostics.Debugger.Log(0, null, "archivo_resultado: " + returnedLine);
}
System.Diagnostics.Debugger.Log(0, null, "Finished - StreamReader to read archivo_resultado: " + archivo_resultado);
}
catch (Exception Ex2)
{
System.Diagnostics.Debugger.Log(0, null, "The file could not be read.");
Console.WriteLine(Ex2.Message);
}
string checkMacOutcome = "";
var psi = new System.Diagnostics.ProcessStartInfo(ejecutable_CheckMac, archivo_temporal);
psi.UseShellExecute = false;
psi.CreateNoWindow = true;
psi.RedirectStandardOutput = true;
using (var proc = System.Diagnostics.Process.Start(psi))
{
using (StreamReader sr = proc.StandardOutput)
{
checkMacOutcome = sr.ReadToEnd();
}
}
StreamWriter writetext = new StreamWriter(archivo_resultado);
writetext.WriteLine(checkMacOutcome);
writetext.Close();
I want to get the video file duration in string using C#. I searched the internet and all i get is:
ffmpeg -i inputfile.avi
And every1 say that parse the output for duration.
Here is my code which is
string filargs = "-y -i " + inputavi + " -ar 22050 " + outputflv;
Process proc;
proc = new Process();
proc.StartInfo.FileName = spath;
proc.StartInfo.Arguments = filargs;
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.CreateNoWindow = false;
proc.StartInfo.RedirectStandardOutput = false;
try
{
proc.Start();
}
catch (Exception ex)
{
Response.Write(ex.Message);
}
try
{
proc.WaitForExit(50 * 1000);
}
catch (Exception ex)
{ }
finally
{
proc.Close();
}
Now please tell me how can i save the output string and parse it for the video duration.
Thanks and regards,
There is another Option to get Video Length ,by using Media Info DLL
Using Ffmpeg :
proc.StartInfo.RedirectErrorOutput = true;
string message = proc.ErrorOutput.ReadToEnd();
Filtering shouldn't be an issue ,so do it you're self.
PS : using ffmpeg you should not read the StandardOutput but ErrorOutput i dont know why ,but it work's only like that.
FFmpeg is a little bit of an adventure to parse. But in any case, here's what you need to know.
First, FFmpeg doesn't play well with RedirectOutput options
What you'll need to do is instead of launching ffmpeg directly, launch cmd.exe, passing in ffmpeg as an argument, and redirecting the output to a "monitor file" through a command line output like so... note that in the while (!proc.HasExited) loop you can read this file for real-time FFmpeg status, or just read it at the end if this is a quick operation.
FileInfo monitorFile = new FileInfo(Path.Combine(ffMpegExe.Directory.FullName, "FFMpegMonitor_" + Guid.NewGuid().ToString() + ".txt"));
string ffmpegpath = Environment.SystemDirectory + "\\cmd.exe";
string ffmpegargs = "/C " + ffMpegExe.FullName + " " + encodeArgs + " 2>" + monitorFile.FullName;
string fullTestCmd = ffmpegpath + " " + ffmpegargs;
ProcessStartInfo psi = new ProcessStartInfo(ffmpegpath, ffmpegargs);
psi.WorkingDirectory = ffMpegExe.Directory.FullName;
psi.CreateNoWindow = true;
psi.UseShellExecute = false;
psi.Verb = "runas";
var proc = Process.Start(psi);
while (!proc.HasExited)
{
System.Threading.Thread.Sleep(1000);
}
string encodeLog = System.IO.File.ReadAllText(monitorFile.FullName);
Great, now you've got the log of what FFmpeg just spit out. Now to get the duration. The duration line will look something like this:
Duration: 00:10:53.79, start: 0.000000, bitrate: 9963 kb/s
Clean up the results into a List<string>:
var encodingLines = encodeLog.Split(System.Environment.NewLine[0]).Where(line => string.IsNullOrWhiteSpace(line) == false && string.IsNullOrEmpty(line.Trim()) == false).Select(s => s.Trim()).ToList();
... then loop through them looking for Duration.
foreach (var line in encodingLines)
{
// Duration: 00:10:53.79, start: 0.000000, bitrate: 9963 kb/s
if (line.StartsWith("Duration"))
{
var duration = ParseDurationLine(line);
}
}
Here's some code that can do the parse for you:
private TimeSpan ParseDurationLine(string line)
{
var itemsOfData = line.Split(" "[0], "="[0]).Where(s => string.IsNullOrEmpty(s) == false).Select(s => s.Trim().Replace("=", string.Empty).Replace(",", string.Empty)).ToList();
string duration = GetValueFromItemData(itemsOfData, "Duration:");
return TimeSpan.Parse(duration);
}
private string GetValueFromItemData(List<string> items, string targetKey)
{
var key = items.FirstOrDefault(i => i.ToUpper() == targetKey.ToUpper());
if (key == null) { return null; }
var idx = items.IndexOf(key);
var valueIdx = idx + 1;
if (valueIdx >= items.Count)
{
return null;
}
return items[valueIdx];
}
Just check it out::
//Create varriables
string ffMPEG = System.IO.Path.Combine(Application.StartupPath, "ffMPEG.exe");
system.Diagnostics.Process mProcess = null;
System.IO.StreamReader SROutput = null;
string outPut = "";
string filepath = "D:\\source.mp4";
string param = string.Format("-i \"{0}\"", filepath);
System.Diagnostics.ProcessStartInfo oInfo = null;
System.Text.RegularExpressions.Regex re = null;
System.Text.RegularExpressions.Match m = null;
TimeSpan Duration = null;
//Get ready with ProcessStartInfo
oInfo = new System.Diagnostics.ProcessStartInfo(ffMPEG, param);
oInfo.CreateNoWindow = true;
//ffMPEG uses StandardError for its output.
oInfo.RedirectStandardError = true;
oInfo.WindowStyle = ProcessWindowStyle.Hidden;
oInfo.UseShellExecute = false;
// Lets start the process
mProcess = System.Diagnostics.Process.Start(oInfo);
// Divert output
SROutput = mProcess.StandardError;
// Read all
outPut = SROutput.ReadToEnd();
// Please donot forget to call WaitForExit() after calling SROutput.ReadToEnd
mProcess.WaitForExit();
mProcess.Close();
mProcess.Dispose();
SROutput.Close();
SROutput.Dispose();
//get duration
re = new System.Text.RegularExpressions.Regex("[D|d]uration:.((\\d|:|\\.)*)");
m = re.Match(outPut);
if (m.Success) {
//Means the output has cantained the string "Duration"
string temp = m.Groups(1).Value;
string[] timepieces = temp.Split(new char[] {':', '.'});
if (timepieces.Length == 4) {
// Store duration
Duration = new TimeSpan(0, Convert.ToInt16(timepieces[0]), Convert.ToInt16(timepieces[1]), Convert.ToInt16(timepieces[2]), Convert.ToInt16(timepieces[3]));
}
}
With thanks,
Gouranga Das.