I'm trying to publish a cshtml file to PDF but when the PDF renders all the html formatting is lost. I think the problem might be that I need to render view as a string like in this example here Render View As String but I'm not using MVC and I don't understand the process well enough to determine how I can extrapolate from this example. How do I get the view to render so that I don't lose the HTML formatting?
Here's how the code is set up:
public class PrintTemplate<T> : RazorEngine.Templating.TemplateBase<T>
{
public new T Model { get; set; }
public PrintTemplate()
{
//TODO: Add Constructor Logic
}
}
public class ViewPage
{
public string Body { get; set; }
}
public static class PrintPDFBO
{
public static ViewPage PrintPDF(id)
{
var newPrint = new ViewPage();
var pdf = GetDataForPDF(id);
newPrint.Body += RazorEngine.Razor.Parse(PrintPDFUtil.GetPrintTemplate(id), pdf, id.ToString());
newPrint.Body += "</body></html>";
return newPrint;
}
}
protected void btnPrintPDF_OnClick(object sender, EventArgs e)
{
var content = new ViewPage();
content = PrintPDFBO.PrintPDF(id);
title = DateTime.Now + "My Title";
}
UPDATE: I've tried depositing the text from my view into a panel then outputting the panel but
the result is the same, no formatting
protected void PrintablePdf(ViewPage view, string title)
{
Response.Clear();
Response.Buffer = true;
Response.ContentType = "application/pdf";
Response.AddHeader("content-disposition", "attachment;fileName=" + title);
Response.Cache.SetCacheability(HttpCacheability.NoCache);
//StringBuilder sb = new StringBuilder(view.Body);
divPrint.InnerHtml = view.Body.ToString();
StringWriter sw = new StringWriter(sb);
HtmlTextWriter hw = new HtmlTextWriter(sw);
pnlPrint.RenderControl(hw);
StringReader sr = new StringReader(sw.ToString());
Document pdf = new Document(PageSize.A4, 50f, 50f, 50f, 50f);
HTMLWorker htmlparser = new HTMLWorker(pdf);
PdfWriter.GetInstance(pdf, Response.OutputStream);
pdf.Open();
htmlparser.Parse(sr);
pdf.Close();
}
UPDATE for expected output:
Content of the cshtml:
@using Print.DataType
@using Print.Data
@inherits PrintTemplate<PDFPrint>
@*Start*@
<div style="border: 1px solid black; width: 7in; height: 2in;">
<div style="width: 3.5in; height: 2in; padding: 1em; float: left;">
<div>
<div style="float:left; width: 2.5in;">
<div style="border-bottom: 1px solid black; border-right: 1px solid black; height: .3in; padding-top: .25em;">
<span style="font-weight: bold;">OPERATOR</span>
</div>
<div style="border-right: 1px solid black; height: .27in;">
<div style="vertical-align: top;">NAME OF OPERATOR</div>
<div>@Model.Name</div>
</div>
</div>
<div style="float: left; width: 1in;">
<div style="border-bottom: 1px solid black;">
<div style="vertical-align: top;">CARD NO.</div>
<div>@Model.CardNo</div>
</div>
<div style="border-bottom: 1px solid black;">
<div style="vertical-align: top;">DATE ISSUED</div>
<div>@Model.IssueDate.ToShortDateString()</div>
</div>
<div>
<div style="vertical-align: top;">DATE EXPIRES</div>
<div>@Model.Expiration.ToShortDateString()</div>
</div>
</div>
</div>
What I expect to see in the pdf is a division with a solid border, multiple lines each with a border, bolded text in some instances, and multiple inner divisions that have specific widths.
What I get instead is just this, no formatting:
Name Date Time
However, the string of html is intact when it gets to the string builder so Razor is outputting it correctly.
UPDATE - Implementation of New Page:
So I found a post that talked about outputting an asp.net Panel to PDF and one person suggested that the two ways it could work was to make a new page put the content in the panel then try to print to PDF or do it as a stream on the server. So I decided to move my code to a new page so at the very least I could see on a page the output Razor generated from the cshtml page and determine if it was in fact intact which it is, all the border styles and font changes and widths/heights seem to be intact. Then from there I tried to do a normal PDF print of the panel and still lost all formatting once I printed to PDF. The one piece of code I've added is just a function call by the button that calls the PrintablePdf() function and on page load, I've added a line so that when content is populated it is added to the panel like so: divPrint.InnerHtml = content.Body;
UPDATE: (no resolution) Based on the first suggestion below I changed Printable PDF to this: (Correction here I typed StringBuilder when it should have read StringReader)
protected void PrintablePdf(string title, string body)
{
Response.Clear();
Response.Buffer = true;
Response.ContentType = "application/pdf";
Response.AddHeader("content-disposition", "attachment;fileName=" + title);
Response.Cache.SetCacheability(HttpCacheability.NoCache);
Document pdf = new Document(PageSize.A4, 50f, 50f, 50f, 50f);
HTMLWorker htmlparser = new HTMLWorker(pdf);
PdfWriter.GetInstance(pdf, Response.OutputStream);
pdf.Open();
htmlparser.Parse(new StringReader(body));
pdf.Close();
}
FINAL UPDATE RESOLUTION:
In the end, nothing I tried using the CSHTML worked to preserve the layout in the PDf the way I needed it to. I finally had to resort to dynamically creating the PDF's in codebehind using ITextSharp's PdfPTable, PdfPCell and other features to manual build the pdf. I'm not thrilled with the sheer number of nested tables required to pull off the layout I needed and the code looks horrendously complex however I was able to reduce some portions to reusable method calls.