I have been reading that to put or remove the margins of a PDF the following line is used:
PageMargins = new Rotativa.Core.Options.Margins(0,0,0,0)
But, when I enter them I get the following:
'ViewAsPDF' does not contain a definition for 'PageMargins'
Any solution?
This is the complete code:
return new ViewAsPdf("Pdf48", c.cargarDatosPDF(solicitud))
{
CustomSwitches = "--header-html " + _headerUrl + " --header-spacing 0 " +
"--footer-html " + _footerUrl + " --footer-spacing 0",
PageMargins = new Rotativa.Core.Options.Margins(0,0,0,0)
};
Solution:
return new PartialViewAsPdf("Pdf48", c.cargarDatosPDF(solicitud))
{
RotativaOptions = new Rotativa.Core.DriverOptions
{
CustomSwitches = "--header-html " + _headerUrl + " --header-spacing 0 " +
"--footer-html " + _footerUrl + " --footer-spacing 0",
PageMargins = new Rotativa.Core.Options.Margins(0, 0, 0, 0)
}
};
This should help.
var pdfdoc= new Rotativa.ViewAsPdf(){
PageHeight=30.00,
PageWidth=30.00,
PageMargins=new Rotativa.Options.Margins(0,0,0,0)
};
Related
Below is The code after the embed message is posted I would like the bot to delete the command that it was give to post the embed. Also if anyone knows how to add a footer to this embed that would be awesome
if (raid == "gos")
{
if (day == "Sun")
{
var filename = "gos_Sun.png";
var embed = new EmbedBuilder()
{
Title = "Garden of Salvation",
Description = "```" + day + ", " + date + " # " + time + " " + ampm + " " + "\n" + description + "```" + description2,
ImageUrl = $"attachment://{filename}",
}.Build();
SentEmbed = await Context.Channel.SendFileAsync(filename, embed: embed);
await SentEmbed.AddReactionsAsync(myReactions);
}
}
var footer = new EmbedFooterBuilder().WithText("React Below");
if (raid == "gos")
{
if (day == "Sun")
{
var filename = "gos_Sun.png";
var embed = new EmbedBuilder()
{
Title = "Garden of Salvation",
Description = "```" + day + ", " + date + " # " + time + " " + ampm + " " + "\n" + description + "```" + description2,
ImageUrl = $"attachment://{filename}",
}.WithFooter(footer).Build();
First, I apologize for the length of this, but it's all I knew when I started. Now I'm experimenting with the foreach, List<t> and TreeView classes to avoid repetition as recmomended by SO community.
The form will collect information via text boxes, allow attachments of files with file dialogs and collate all info into a neat HTML bodied email. We sell slabs.. and my original code looked a little like this:
private void PrepareReturnEmailTwoSlabs()
{
LoadSettings();
string Fname = Properties.Settings.Default.FabricatorName;
string Facc = Properties.Settings.Default.FabricatorAccountNo;
string Fadd1 = Properties.Settings.Default.FabricatorAddress1;
string Fadd2 = Properties.Settings.Default.FabricatorAddress2;
string Ftown = Properties.Settings.Default.FabricatorTown;
string Fcounty = Properties.Settings.Default.FabricatorCounty;
string Fpostcode = Properties.Settings.Default.FabricatorPostcode;
string Fphoneno = Properties.Settings.Default.FabricatorPhone;
string Femail = Properties.Settings.Default.FabricatorEmail;
string Fclient = Properties.Settings.Default.ClientManagerEmail;
string Fcentre = Properties.Settings.Default.CentreEmail;
string FQt = Properties.Settings.Default.QTEmail;
string Dateofinv = dateTimePicker1.Value.ToShortDateString();
string Inv = textBox13.Text;
string Material1 = textBox14.Text;
string Thick1 = comboBox8.SelectedValue.ToString();
string Batch1 = textBox44.Text;
string Reason1 = comboBox1.SelectedValue.ToString();
string Notes = textBox18.Text;
string Thick2 = comboBox7.SelectedValue.ToString();
string Material2 = textBox15.Text;
string Batch2 = textBox45.Text;
string Reason2 = comboBox2.SelectedValue.ToString();
if (Thick2 == null)
{
Thick2 = "not selected";
}
if (Material2 == null)
{
Material2 = "not selected ";
}
if (Batch2 == null)
{
Batch2 = "not selected ";
}
if (Reason2 == null)
{
Reason2 = "not selected ";
}
GenerateUniqueRefReturn();
//construct email
var message = new MimeMessage();
message.From.Add(new MailboxAddress("************", "***************"));
message.To.Add(new MailboxAddress("**********", "********"));
message.Subject = "Return" + " " + Returnid;
//different message bodies dependant on how many slabs are chosen
TextPart body2 = new TextPart("html")
{
Text = #"Please See Below Information" + "<br/>" +
"<h4>Return ID: " + " " + Returnid + "</h4>" + "<br/>" +
"<b>Fabricator Name:</b>" + " " + Fname + "<br/>" + Environment.NewLine +
"<b>Account Number:</b>" + " " + Facc + "<br/>" + Environment.NewLine +
"<b>Address Line 1:</b>" + " " + Fadd1 + "<br/>" + Environment.NewLine +
"<b>Address Line 2:</b>" + " " + Fadd2 + "<br/>" + Environment.NewLine +
"<b>Town:</b>" + " " + Ftown + "<br/> " + Environment.NewLine +
"<b>County:</b>" + " " + Fcounty + "<br/>" + Environment.NewLine +
"<b>Postcode:</b>" + " " + Fpostcode + "<br/>" + Environment.NewLine +
"<b>Phone:</b>" + " " + Fphoneno + "<br/>" + Environment.NewLine +
"<b>Email:</b>" + " " + Femail + "<br/>" + Environment.NewLine + "<br/>" +
"<br/>" +
"<b>Date Of Invoice: </b>" + " " + DoI + "<br/>" +
"<b>Invoice: </b>" + " " + Inv + "<br/>" +
"<b>Material Information:</b>" + "<br/>" +
//slab 1
"<b>Thickness: </b>" + " " + Thick1 + "mm" + "<br/>" +
"<b>Material Name: </b>" + " " + Material1 + "<br/>" +
"<b>Batch No: </b>" + " " + Batch1 + "<br/>" +
"<b>Reason for Return: </b>" + " " + Reason1 + "<br/>" + "<br/>" +
//slab 2
"<b>Thickness: </b>" + " " + Thick2 + "mm" + "<br/>" +
"<b>Material Name: </b>" + " " + Material2 + "<br/>" +
"<b>Batch No: </b>" + " " + Batch2 + "<br/>" +
"<b>Reason for Return: </b>" + " " + Reason2 + "<br/>" + "<br/>" +
"<br/>" +
"<b>Notes:" + " " + Notes
};
var builder = new BodyBuilder();
//check for return attachment and if found, assign attachment to message via bodybuilder
if (checkBox5.Checked)
{
builder.TextBody = body2.Text;
builder.HtmlBody = body2.Text;
builder.Attachments.Add(ReturnAttachment1);
message.Body = builder.ToMessageBody();
}
if (checkBox7.Checked)
{
builder.TextBody = body2.Text;
builder.HtmlBody = body2.Text;
builder.Attachments.Add(ReturnAttachment1);
builder.Attachments.Add(ReturnAttachment2);
message.Body = builder.ToMessageBody();
}
if (checkBox6.Checked)
{
builder.TextBody = body2.Text;
builder.HtmlBody = body2.Text;
builder.Attachments.Add(ReturnAttachment1);
builder.Attachments.Add(ReturnAttachment2);
builder.Attachments.Add(ReturnAttachment3);
message.Body = builder.ToMessageBody();
}
else
{
message.Body = body2;
}
//Connection to SMTP and Criteria to Send
using (var client = new SmtpClient())
{
// For demo-purposes, accept all SSL certificates (in case the server supports STARTTLS)
client.ServerCertificateValidationCallback = (s, c, h, e) => true;
client.Connect("smtp.gmail.com", 587, false);
// Note: only needed if the SMTP server requires authentication
client.Authenticate("***************#********.com", "*********");
client.Send(message);
client.Disconnect(true);
This code was repeated all the way up to five slabs. So now, I have a class:
{
public string Thickness { get; set; }
public string Material { get; set; }
public string Batch { get; set; }
public Slab(string Thick, string Mat, string Batchno)
{
Thickness = Thick;
Material = Mat;
Batch = Batchno;
}
}
A List that holds this object:
private void button1_Click(object sender, EventArgs e)
{
string t = comboBox1.SelectedValue.ToString();
string m = comboBox2.SelectedValue.ToString();
string b = textBox6.Text;
Slab S = new Slab(t, m, b);
allSlabs.Add(S);
PaintTree();
}
public void PaintTree()
{
int ParentIndex;
TreeSlabs.Nodes.Clear();
foreach (Slab slab in allSlabs)
{
TreeSlabs.BeginUpdate();
TreeSlabs.Nodes.Add("Slab" + " " + (allSlabs.IndexOf(slab) + 1).ToString());
ParentIndex = allSlabs.IndexOf(slab);
TreeSlabs.Nodes[ParentIndex].Nodes.Add("Thickness: " + $"{slab.Thickness}");
TreeSlabs.Nodes[ParentIndex].Nodes.Add("Material: " + $"{slab.Material}");
TreeSlabs.Nodes[ParentIndex].Nodes.Add("Batch: " + $"{slab.Batch}");
TreeSlabs.EndUpdate();
}
}
Now i want to create a foreach.. that iterates through each node.. and collects the info from the parent and its children foreach node, and somehow instantiate new HTML methods for each node..
Foreach Node:
Compose HTML Line - new HTML
Slab 1:
Thickness:
Material
Batch
Slab 2:... etc
If theres 8 nodes, it generates 8 of those bodies. i can think of ways.. but i KNOW they're defintely not the correct ways to go about it based on what ive read and seen out there.
Many Thanks
You don't need to iterate through the tree and try to reclaim the data you put into it from the List of Slabs; it would be simpler to enumerate the List:
By the way, some other tips:
don't beginupdate/endupdate inside the loop, do it outside
the Add method that adds a node to a tree returns the node it added; you don't have to find it again by index, just capture it var justAdded = rootnode.Add("parent node"); and then add your thickness etc to it justAdded.Nodes.Add("thickness..."). The only add command that doesn't return the added mode is the one that takes a treenode type object; it doesn't need to return it because you already have it
consider adding a method to your slab to return some html, to simplify things
you can tidy all that html up with some string interpolation like you did with your $"{slab.thickness}" etc
I am trying to after each 3 loops in my foreach do a page break but can't seem to get it to trigger. Any help is welcome here is my code it prints fine but I'd like 3 GridView rows per page.
int i = 0;
// for each row in Gridview do this part of the PDF print
foreach (GridViewRow row in poGridview.Rows)
{
if ((i++) % 3 == 0)
{
pdfDoc.NewPage();
}
// Declare all values
String itemNum = row.Cells[0].Text;
String house = row.Cells[2].Text;
String staic = row.Cells[4].Text;
String description = row.Cells[1].Text.Replace("&", "");
String qtOrd = row.Cells[5].Text;
String serial = row.Cells[12].Text.Replace(" ", " ");
String dkQty = row.Cells[8].Text;
String stQty = row.Cells[9].Text;
String deptNum = row.Cells[11].Text.Replace(" ", " "); ;
String whBarCode = row.Cells[13].Text.Replace(" ", " ");
String turnA = row.Cells[19].Text;
String turnN = row.Cells[20].Text;
String turnC = row.Cells[21].Text;
String conversion = row.Cells[7].Text;
String unit = row.Cells[6].Text;
decimal ucorq = decimal.Parse(qtOrd) - decimal.Parse(stQty);
// Warehouse barcode
Barcode39 whlsc = new Barcode39();
whlsc.Code = whBarCode.Replace(" ", " ");
// Turn around barcode
Barcode39 turnAr = new Barcode39();
turnAr.Code = (poNum + "$I" + itemNum + "$M");
// Set up lines for paragraph
Paragraph itemLine1 = new Paragraph(#"" + itemNum + " " + house + " " + description, body);
Paragraph itemLine2 = new Paragraph(#" Status -> " + staic + " Ordered: " + qtOrd + " " + unit, body);
Paragraph itemLine3 = new Paragraph(#"STOCKING: " + stQty + " EA CONVERSION FACTOR: " + conversion + " ", body);
Paragraph itemLine4 = new Paragraph(#"Dock: " + dkQty + " Stock: " + stQty + " Open: " + ucorq.ToString(), body);
Paragraph itemLine5 = new Paragraph(#"Serial #: " + serial, body);
Paragraph itemLine6 = new Paragraph(#"VND CATALOG: " + serial + " ", body);
Paragraph itemLine7 = new Paragraph(#"JOB NUMBER: " + " DEFAULT STOCK LOC: ", body);
Paragraph itemLine8 = new Paragraph(#" ", body);
// Select command for the comments for each item
OleDbCommand comtSearch = new OleDbCommand("SELECT CAST(POCOMT.CMNT1 as char(40) ccsid 37) as cmt1, CAST(POCOMT.CMNT2 as char(40) ccsid 37) as cmt2 FROM POCOMT LEFT OUTER JOIN POITEM ON POITEM.ITNBR = POCOMT.ITNBR"
+ $" AND POITEM.ORDNO = POCOMT.ORDNO AND POITEM.HOUSE = POCOMT.HOUSE AND POITEM.LINSQ = POCOMT.LINSQ WHERE POCOMT.ITNBR = '{itemNum}' AND POCOMT.ORDNO = '{poNum}'", cn);
OleDbDataAdapter adp = new OleDbDataAdapter(comtSearch);
DataTable dt = new DataTable();
adp.Fill(dt);
commentGridview.DataSource = dt;
commentGridview.DataBind();
// Add Lines to page
p.Add(itemLine1);
p.Add(new Paragraph("\n"));
p.Add(new Paragraph("\n"));
p.Add(new Paragraph("\n"));
p.Add(new Chunk(turnAr.CreateImageWithBarcode(cb, null, null), 0, 0));
p.Add(new Paragraph("\n"));
p.Add(itemLine2);
p.Add(new Paragraph("\n"));
p.Add(itemLine3);
//p.Add(new Chunk(turnAr.CreateImageWithBarcode(cb, null, null), 0, 0));
p.Add(new Paragraph("\n"));
p.Add(itemLine4);
p.Add(new Paragraph("\n"));
//p.Add(itemLine5);
// loop through the comment fields on each item and print
foreach (GridViewRow cmtRow in commentGridview.Rows)
{
String cmnt1 = cmtRow.Cells[0].Text.Replace(" ", " "); ;
String cmnt2 = cmtRow.Cells[1].Text.Replace(" ", " "); ;
Paragraph cmt1 = new Paragraph(#"" + cmnt1, body);
Paragraph cmt2 = new Paragraph(#"" + cmnt2 + " ", body);
p.Add(cmt1);
p.Add(new Paragraph("\n"));
p.Add(cmt2);
}
p.Add(new Paragraph("\n"));
p.Add(itemLine6);
p.Add(new Paragraph("\n"));
p.Add(itemLine7);
p.Add(new Paragraph("\n"));
p.Add(itemLine8);
p.Add(new Chunk(whlsc.CreateImageWithBarcode(cb, null, null), 0, 0));
p.Add(new Paragraph("\n"));
p.Add(hr);
p.Add(new Paragraph("\n"));
}
p.Add(end);
p.Add(new Paragraph("\n"));
pdfDoc.Add(p);
pdfDoc.NewPage();
}
// Close Document
pdfDoc.Close();
Response.Write(pdfDoc);
Response.ContentType = "Application/pdf";
Response.End();
int i = 0; //initiate variable outside foreach loop
foreach (GridViewRow row in poGridview.Rows)
{
if (i == 3)
{
pdfDoc.NewPage();
i=0; //when i=3, resets to 0 and calls newPage method
}
i++; //+1 to i
}
You're setting i to zero at the beginning of every iteration of the loop. So, it never gets to 3. You probably want something like this:
int i = 0;
foreach( GridViewRow row in poGridview.Rows )
{
if( i % 3 == 0 && i > 0 )
{
pdfDoc.Add(p);
pdfDoc.NewPage();
p = <Create a new p>
}
i++;
}
int i = 0;
foreach(GridViewRow row in poGridview.Rows)
{
if ((i++) % 3 == 0)
{
pdfDoc.NewPage();
}
}
I've got the following bit of code that generates a list I'm trying to process. I need to create multiple files as I go through the list.
Currently I'm getting the following error on the calls to genStream outside of the IF, (in and out of the FOREACH loop), and I'm not sure why:
The name genStream' does not exist in the current context
I'm trying to figure out how I can close the one stream, and open another in the IF statement. I tried to put a .close() before creating the new stream, and it gave me an error for using it before initializing the stream.
Here is my bit of code:
/// <summary>
/// Creates a file for each Genre, and writes movie info to each for the cooresponding movies
/// </summary>
/// <param name="cPath">Path to create HTML files in</param>
/// <param name="mList">List of Movies to generate Genre and Movie info from</param>
public static void WriteGenreHTML(string cPath, List<Movie> mList)
{
int lineID = 0;
string tmpGen = null;
string strHeader, strMovie, strGenre, tmpGenre = null;
// Gets a list of unique Genres from the MovieList
var distinctGenres = from m in mList
from genre in m.Genres
group genre by genre into genres
select genres.First();
// Gets a list of Movies with the associated Genres
var moviesWithGenre = from g in distinctGenres
from m in mList
where m.Genres.Contains(g)
orderby g, m.Title
select new { Genre = g, Movie = m };
// Traverses list of movies creating new HTML Genre files, and writing movie info to the HTML genre files
foreach (var m in moviesWithGenre)
{
// Creates new HTML file if new Genre is detected
if (m.Genre != tmpGen)
{
tmpGen = m.Genre;
// initiates streamwriter for catalog output file
FileStream fs = new FileStream(cPath + Path.DirectorySeparatorChar + m.Genre, FileMode.Create);
StreamWriter genStream = new StreamWriter(fs);
// Generates header info for new file, and new Genre
strHeader = "<style type=\"text/css\">\r\n" + "<!--\r\n" + "tr#odd {\r\n" + " background-color:#e2e2e2;\r\n" + " vertical-align:top;\r\n" + "}\r\n" + "\r\n" + "tr#even {\r\n" + " vertical-align:top;\r\n" + "}\r\n" + "div#title {\r\n" + " font-size:16px;\r\n" + " font-weight:bold;\r\n" + "}\r\n" + "\r\n" + "div#mpaa {\r\n" + " font-size:10px;\r\n" + "}\r\n" + "\r\n" + "div#genre {\r\n" + " font-size:12px;\r\n" + " font-style:italic;\r\n" + "}\r\n" + "\r\n" + "div#plot {\r\n" + " height: 63px;\r\n" + " font-size:12px;\r\n" + " overflow:hidden;\r\n" + "}\r\n" + "\r\n" + "div#genre_heading {\r\n" + " height: 50px;\r\n" + " font-size: 24px;\r\n" + " font-weight: bold;\r\n" + " text-align: center;\r\n" + " text-decoration: underline;\r\n" + "}\r\n" + "-->\r\n" + "</style>\r\n" + "\r\n" + "<html>\r\n" + " <body>\r\n" + " <table>\r\n";
strHeader += " <tr>\r\n" + " <td colspan=2>\r\n" + " <div id=\"genre_heading\">" + m.Genre + "</div>\r\n" + " </td>\r\n" + " </tr>\r\n" + "\r\n";
// Writes header HTML to stream
genStream.WriteLine(strHeader);
Console.WriteLine();
Console.WriteLine("Now Processing " + m.Genre);
}
// Generates the HTML for the Movie
strMovie = lineID == 0 ? " <tr id=\"odd\" style=\"page-break-inside:avoid\">\r\n" : " <tr id=\"even\" style=\"page-break-inside:avoid\">\r\n";
strMovie += " <td>\r\n" + " <img src=\".\\images\\" + m.Movie.ImageFile + "\" width=\"75\" height=\"110\">\r\n" + " </td>\r\n" + " <td>\r\n" + " <div id=\"title\">" + m.Movie.Title + "</div>\r\n" + " <div id=\"mpaa\">" + m.Movie.Certification + " " + m.Movie.MPAA + "</div>\r\n" + " <div id=\"genre\">" + strGenre + "</div>\r\n" + " <div id=\"plot\">" + m.Movie.Plot + "</div>\r\n" + " </td>\r\n" + " </tr>\r\n";
// Writes the HTML to the stream
genStream.WriteLine(strMovie);
lineID = lineID == 0 ? 1 : 0;
}
string closingHTML = " </table>\r\n" + " </body>\r\n" + "</html>";
genStream.WriteLine(closingHTML);
genStream.Close();
}
Side note. I'd GREATLY appreciate it if someone could point me towards something I can use to convert the HTML files into PDFs. I tried EO, and it has a nasty "watermark", and chokes on files over a couple MB (mine are 5-10mb+). I have WkHTMLToSharp, but I am not sure how to use it, and can't find any documentation on how to intitialize/use it.
Thanks as always!
Declare the genStream outside of foreach loop, and then initialize it as needed within the if statement:
StreamWriter genStream;
foreach (var m in moviesWithGenre)
{
// Creates new HTML file if new Genre is detected
if (m.Genre != tmpGen)
{
tmpGen = m.Genre;
// initiates streamwriter for catalog output file
FileStream fs = new FileStream(cPath + Path.DirectorySeparatorChar + m.Genre, FileMode.Create);
// Set genStream to the FileStream
genStream = new StreamWriter(fs);
I am generating HTML files on the fly, and I would like to create a PDF from the final file. I am using the following to generate the HTML file:
public static void WriteHTML(string cFile, List<Movie> mList)
{
int lineID = 0;
string strHeader, strMovie, strGenre, tmpGenre = null;
string strPDF = null;
// initiates streamwriter for catalog output file
FileStream fs = new FileStream(cFile, FileMode.Create);
StreamWriter catalog = new StreamWriter(fs);
strHeader = "<style type=\"text/css\">\r\n" + "<!--\r\n" + "tr#odd {\r\n" + " background-color:#e2e2e2;\r\n" + " vertical-align:top;\r\n" + "}\r\n" + "\r\n" + "tr#even {\r\n" + " vertical-align:top;\r\n" + "}\r\n" + "div#title {\r\n" + " font-size:16px;\r\n" + " font-weight:bold;\r\n" + "}\r\n" + "\r\n" + "div#mpaa {\r\n" + " font-size:10px;\r\n" + "}\r\n" + "\r\n" + "div#genre {\r\n" + " font-size:12px;\r\n" + " font-style:italic;\r\n" + "}\r\n" + "\r\n" + "div#plot {\r\n" + " height: 63px;\r\n" + " font-size:12px;\r\n" + " overflow:hidden;\r\n" + "}\r\n" + "-->\r\n" + "</style>\r\n" + "\r\n" + "<html>\r\n" + " <body>\r\n" + " <table>\r\n";
catalog.WriteLine(strHeader);
strPDF = strHeader;
foreach (Movie m in mList)
{
tmpGenre = null;
strMovie = lineID == 0 ? " <tr id=\"odd\" style=\"page-break-inside:avoid\">\r\n" : " <tr id=\"even\" style=\"page-break-inside:avoid\">\r\n";
catalog.WriteLine(strMovie);
strPDF += strMovie;
foreach (string genre in m.Genres)
tmpGenre += ", " + genre + "";
strGenre = tmpGenre != null ? tmpGenre.Substring(2) : null;
strMovie = " <td>\r\n" + " <img src=\".\\images\\" + m.ImageFile + "\" width=\"75\" height=\"110\">\r\n" + " </td>\r\n" + " <td>\r\n" + " <div id=\"title\">" + m.Title + "</div>\r\n" + " <div id=\"mpaa\">" + m.Certification + " " + m.MPAA + "</div>\r\n" + " <div id=\"genre\">" + strGenre + "</div>\r\n" + " <div id=\"plot\">" + m.Plot + "</div>\r\n" + " </td>\r\n" + " </tr>\r\n";
catalog.WriteLine(strMovie);
strPDF += strMovie;
lineID = lineID == 0 ? 1 : 0;
}
string closingHTML = " </table>\r\n" + " </body>\r\n" + "</html>";
catalog.WriteLine(closingHTML);
strPDF += closingHTML;
WritePDF(strPDF, cFile + ".PDF");
catalog.Close();
}
Once completed, I want to call the following function to generate the PDF file:
public static void WritePDF(string cFile, string pdfFile)
{
WkHtmlToPdfConverter w = new WkHtmlToPdfConverter();
byte[] strHTML = w.Convert(cFile);
File.WriteAllBytes(pdfFile, strHTML);
w.Dispose();
}
I've discovered that the .Convert function will convert HTML code to PDF, not a file. Secondly, when I pass in the HTML code directly, the images are not appearing in the PDF. I know there is an issue with .GIF files, but these are all .JPG files.
I've read a lot about how good wkhtmltopdf is, and the guy who wrote WkHTMLToSharp posted his project all over SO, but I've been disappointed by the lack of documentation for it.
I WANT to be able to pass in a file to convert, change the margins (I know this is possible, I just need to figure out the correct settings), have it convert images correctly, and most importantly, to not break up my items across multiple pages (support "page-break-inside:avoid" or something similar).
I'd love to see how others are using this!
I have coded an example about how to create a PDF from HTML. I just updated it to also print images.
https://github.com/hmadrigal/playground-dotnet/tree/master/MsDotNet.PdfGeneration
(In my blog post I explain most of the project https://hmadrigal.wordpress.com/2015/10/16/creating-pdf-reports-from-html-using-dotliquid-markup-for-templates-and-wkhtmltoxsharp-for-printing-pdf/ )
Pretty much you have two options:
1: Using file:// and the fullpath to the file.
<img alt="profile" src="{{ employee.PorfileFileName | Prepend: "Assets\ProfileImage\" | ToLocalPath }}" />
2: Using URL Data (https://en.wikipedia.org/wiki/Data_URI_scheme)
<img alt="profile" src="data:image/png;base64,{{ employee.PorfileFileName | Prepend: "Assets\ProfileImage\" | ToLocalPath | ToBase64 }}" />
Cheers,
Herb
Use WkHtmlToXSharp.
Download the latest DLL from Github
public static string ConvertHTMLtoPDF(string htmlFullPath, string pageSize, string orientation)
{
string pdfUrl = htmlFullPath.Replace(".html", ".pdf");
try
{
#region USING WkHtmlToXSharp.dll
//IHtmlToPdfConverter converter = new WkHtmlToPdfConverter();
IHtmlToPdfConverter converter = new MultiplexingConverter();
converter.GlobalSettings.Margin.Top = "0cm";
converter.GlobalSettings.Margin.Bottom = "0cm";
converter.GlobalSettings.Margin.Left = "0cm";
converter.GlobalSettings.Margin.Right = "0cm";
converter.GlobalSettings.Orientation = (PdfOrientation)Enum.Parse(typeof(PdfOrientation), orientation);
if (!string.IsNullOrEmpty(pageSize))
converter.GlobalSettings.Size.PageSize = (PdfPageSize)Enum.Parse(typeof(PdfPageSize), pageSize);
converter.ObjectSettings.Page = htmlFullPath;
converter.ObjectSettings.Web.EnablePlugins = true;
converter.ObjectSettings.Web.EnableJavascript = true;
converter.ObjectSettings.Web.Background = true;
converter.ObjectSettings.Web.LoadImages = true;
converter.ObjectSettings.Load.LoadErrorHandling = LoadErrorHandlingType.ignore;
Byte[] bufferPDF = converter.Convert();
System.IO.File.WriteAllBytes(pdfUrl, bufferPDF);
converter.Dispose();
#endregion
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex);
}
return pdfUrl;
}
You can use Spire.Pdf to do so.
This component could convert html to pdf.
PdfDocument pdfdoc = new PdfDocument();
pdfdoc.LoadFromHTML(fileFullName, true, true, true);
//String url = "http://www.e-iceblue.com/";
//pdfdoc.LoadFromHTML(url, false, true, true);
pdfdoc.SaveToFile("FromHTML.pdf");
We're also using wkhtmltopdf and are able to render images correctly. However, by default the rendering of images is disabled.
You have to specify those options on your converter instance:
var wk = _GetConverter()
wk.GlobalSettings.Margin.Top = "20mm";
wk.GlobalSettings.Margin.Bottom = "10mm";
wk.GlobalSettings.Margin.Left = "10mm";
wk.GlobalSettings.Margin.Right = "10mm";
wk.GlobalSettings.Size.PaperSize = PdfPaperSize.A4;
wk.ObjectSettings.Web.PrintMediaType = true;
wk.ObjectSettings.Web.LoadImages = true;
wk.ObjectSettings.Web.EnablePlugins = false;
wk.ObjectSettings.Web.EnableJavascript = true;
result = wk.Convert(htmlContent);