Let me start by saying if I'm making this overly complicated, please, please offer suggestions on how to simplify!! I'm trying to create some logic that will let users export the contents on the screen to a PDF file. The contents contains multiple highcharts graphs, so I need for the html to be rendered completely to the page so I can grab the svg information to pass into the PDF file. Since the page has been rendered completely, the only way I know to pass the rendered html back to the server is through Web Services. Web Services is calling a .cs file where the PDF functionality is located. I can set a break point in the .cs file at the end of the pdf function, and it is hitting it. The problem is, it's returning an error of "200 OK parsererror SyntaxError: Unexpected token %".
Now, I create a dummy html string in my C# codebehind and call the .cs file from the codebehind, and it creates a PDF file with no issues at all. So my question is, why would the class work when being called from a codebehind, but not from Web Services? The only thing I can think of, is that it has something to do with a postback? My code is below:
JavaScript that is calling the Web Service..I can step all the way through the web service and class functions being called, they are making it to the end, but it's returning to the error function with 200 OK parsererror SyntaxError: Unexpected token %
function checkSplash() {
timesChecked++;
if (loadImages >= document.getElementsByName('img').length || timesChecked * checkInterval >= maxLoadTime) {
clearInterval(intervalID);
if (document.getElementById("exportDiv") != null) {
var mydata = {
src: document.getElementById("export_pdf").innerHTML
}
$.ajax({
type: "post",
contentType: "application/json; charset=UTF-8",
url: "../WebServices/print.asmx/export",
data: JSON.stringify(mydata),
dataType: "json",
success: function (response) {
alert("here");
location.reload(true);
},
error: function (xhr, textStatus, errorThrown) {
alert(xhr.status + ' ' + xhr.statusText + ' ' + textStatus + ' ' + errorThrown);
}
})
}
}
}
Web Service being called...this function is identical the a test function I created in the codebehind. The codebehind test function works and creates the PDF document with no issues. But when this gets called from the javascript, it returns the error
using DMC.Classes;
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Web;
using System.Web.Services;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace DMC.WebServices
{
[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[System.ComponentModel.ToolboxItem(false)]
[System.Web.Script.Services.ScriptService]
public class print : System.Web.Services.WebService
{
[WebMethod]
public bool export(string src)
{
string fileName = null;
export dmc = new export();
fileName = " Behavior Statistics YTD ";
dmc.exportPDF(fileName, "Portrait", src);
return true;
}
}
}
Finally, the .cs methods being used:
using iTextSharp.text;
using iTextSharp.text.html.simpleparser;
using iTextSharp.text.pdf;
using NReco.PdfGenerator;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.IO;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
namespace DMC.Classes
{
public class export
{
public void exportPDF(string fileName, string Orientation, string html)
{
HtmlToPdfConverter pdf = new HtmlToPdfConverter();
html = html.Replace("\n", "");
html = html.Replace("\t", "");
html = html.Replace("\r", "");
html = html.Replace("\"", "'");
switch (Orientation)
{
case "Portrait":
pdf.Orientation = PageOrientation.Portrait;
break;
case "Landscape":
pdf.Orientation = PageOrientation.Landscape;
break;
default:
pdf.Orientation = PageOrientation.Default;
break;
}
pdf.Margins.Top = 25;
pdf.PageFooterHtml = createPDFFooter();
var pdfBytes = pdf.GeneratePdf(createPDFScript() + html + "</body></html>");
HttpContext.Current.Response.ContentType = "application/pdf";
HttpContext.Current.Response.ContentEncoding = System.Text.Encoding.UTF8;
HttpContext.Current.Response.AddHeader("content-disposition", "attachment; filename=" + fileName + ".pdf");
HttpContext.Current.Response.BinaryWrite(pdfBytes);
HttpContext.Current.Response.Flush();
HttpContext.Current.Response.End();
}
private string createPDFScript()
{
return "<html><head><style>td,th{line-height:20px;} tr { page-break-inside: avoid }</style><script>function subst() {var vars={};var x=document.location.search.substring(1).split('&');for(var i in x) {var z=x[i].split('=',2);vars[z[0]] = unescape(z[1]);}" +
"var x=['frompage','topage','page','webpage','section','subsection','subsubsection'];for(var i in x) {var y = document.getElementsByClassName(x[i]);" +
"for(var j=0; j<y.length; ++j) y[j].textContent = vars[x[i]];}}</script></head><body onload=\"subst()\">";
}
private string createPDFFooter()
{
return "<div><table style='font-family:Tahoma; font-size:9px; width:100%'><tr><td style='text-align:left'>Research Dept|RR:mm:jpg</td><td style='text-align:right'>Page <span class=\"page\"></span> of <span class=\"topage\"></span></td></div>";
}
}
}
In your exportPDF function, add a try-catch to trap the Response.End() exception and ignore it:
Referencing
HttpContext
from within the PDF creator class really is not a good idea as it violates the principal of Separation of Concerns. A better way would be for yourexportPDF
method to return thepdfBytes
array and have the caller can handle it appropriately.So your
exportPDF
method ends up looking like this:And your web method looks like this:
One thing that isn't clear to me is how you are planning to handle the response in the JavaScript. What this returns is the PDF file content. What is your JavaScript going to do with that?