Basic principle is that I am trying to take a GridView and turn it into a PDF document. The GridView includes controls like checkboxes and some css classes with some colouring.
I have tried putting this together, but I think I am misunderstanding how something works and now hit a dead end. At present it all compiles and runs, but when you try and open the returned PDF it says it fails to load. The returned file is 1KB in size, so I am guessing that it isn't actually a valid PDF.
I am using iText7 and htmlPDF, with my code looking like this.
Protected Sub ExportToPDF(sender As Object, e As ImageClickEventArgs)
Using sw As New StringWriter()
Using hw As New HtmlTextWriter(sw)
Response.ContentType = "application/pdf"
Response.AddHeader("content-disposition", "attachment;filename=GridViewExport.pdf")
Response.Cache.SetCacheability(HttpCacheability.NoCache)
gvCSC.DataBind()
gvCSC.RenderControl(hw)
Dim pdfWriter As New PdfWriter(Response.OutputStream)
Dim pdfDoc As New PdfDocument(pdfWriter)
Dim sr As New StringReader(sw.ToString)
pdfDoc.SetDefaultPageSize(New PageSize(PageSize.A4.Rotate))
Dim doc As Document = HtmlConverter.ConvertToDocument(sr.ToString, pdfDoc, New ConverterProperties)
Response.End()
pdfDoc.Close()
End Using
End Using
End Sub
I am hoping that I am just being stupid as I've not used iText7 before and it just needs someone to point out the obvious for me. Any help in solving this will be appreciated.
Related
I've seen several questions relating to downloading a PDF from a Web browser using Response, but none seem to fit the mysterious issue I'm having.
I am working on a project that requires the user to be able to click a button (btnPDF) to instantly download a PDF of a Telerik report with a specific "ID" string to the Downloads folder. This process was originally located in an ASPX Page on an IIS separate from where the button is located. When btnPDF was clicked, I used Response.Redirect to download the PDF through that page. The code to download the PDF looked like this:
Response.Clear()
Response.ContentType = result.MimeType 'this is always "application/pdf"
Response.Cache.SetCacheability(HttpCacheability.Private)
Response.Expires = -1
Response.Buffer = True
Response.AddHeader("Content-Disposition", String.Format("{0};FileName={1}", "attachment", fileName))
Response.BinaryWrite(result.DocumentBytes)
Response.End()
Note that result.DocumentBytes is a byte array containing correct bytes for the PDF.
This code worked fine. Now, instead of having the process on a separate Page in a separate project, I need to merge the process onto the same page where btnPDFis located, so that when you click btnPDF, a subroutine is called that performs the same task. I thought this would be very easy, pretty much a copy and paste. With the same code added in a new subroutine, this is what my click event handler "ButtonPDF_Click" now looks like:
Protected Sub ButtonPDF_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles btnPDF.Click
DownloadReportPDF(Me.RadGrid1.SelectedValue.ToString())
Dim strMessage As String = "alert('Printed PDF Sheet.');"
ScriptManager.RegisterStartupScript(Me, Me.GetType, "MyScript", strMessage, True)
End Sub
Protected Sub DownloadReportPDF(ByVal releaseMasterId As String)
'Service call to generate report source
Dim service As New TelerikReportLibrary.ReportServices.PPSReportService
Dim source As Telerik.Reporting.TypeReportSource = service.GetReportSource(releaseMasterId)
'Render PDF and download
Dim reportProcessor As New ReportProcessor()
Dim result As RenderingResult = reportProcessor.RenderReport("PDF", source, Nothing)
Dim fileName As String = result.DocumentName + "_" + releaseMasterId + "." + result.Extension
Response.Clear()
Response.ContentType = result.MimeType 'this is always "application/pdf"
Response.Cache.SetCacheability(HttpCacheability.Private)
Response.Expires = -1
Response.Buffer = True
Response.AddHeader("Content-Disposition", String.Format("{0};FileName={1}", "attachment", fileName))
Response.BinaryWrite(result.DocumentBytes)
Response.End()
End Sub
But the PDF no longer downloads. An accurate byte array is still created, but the Response portion does not result in the PDF being downloaded from the browser. I've found that putting a call to DownloadReportPDF in the Page_Load handler on the same Page successfully generates and downloads a PDF as it did before.
I can't see any reason why this isn't working, but I'm new to ASP, and I'm not great in VB. I've tried using Response.OutputStream, Response.WriteFile, and making use of a MemoryStream, among several other things that I've lost track of. I'm hoping there's something simple, maybe some sort of property of the Page or btnPDF I could be missing. Here is the markup for btnPDF, just in case:
<asp:linkButton ID="btnPDF" CssClass="btn btn-default" runat="server" Width="115px">
<i class="fa fa-file-text" title="Edit"></i> PDF
</asp:linkButton>
What could be causing such a problem? Where should I look at this point?
Let me know if more information is needed.
Thanks,
Shane
EDIT:
I experimented with setting a session variable on btnPDF_Click, and handling the PDF download on postback. Again, a valid byte array was generated, but the HttpResponse did not cause the PDF to download from the browser.
EDIT:
Building on the last edit, this tells me that calling DownloadReportPDF from Page_Load works only when IsPostBack is false. I just tested this thought, and it holds true. In the above code, if I check IsPostBack at the moment I'm trying to download the PDF, it is true. Investigating further.
Alright, I finally found a solution I'm satisfied with (though I still don't understand why I can't download the PDF using Response while IsPostBack is true).
Inspired by this thread, I put the previously posted code in an HttpHandler called PDFDownloadHandler, then used Response.Redirect in the btnPDF_Click event handler to utilize PDFDownloadHandler. This article helped me a lot on that process, as it is something I have not done before.
In case anyone else runs into this problem, here is the new PDFDownloadHandler:
Imports Microsoft.VisualBasic
Imports System.Web
Imports Telerik.Reporting
Imports Telerik.Reporting.Processing
Public Class PDFDownloadHandler
Implements IHttpHandler
Public Sub ProcessRequest(ByVal context As _
System.Web.HttpContext) Implements _
System.Web.IHttpHandler.ProcessRequest
Dim request As HttpRequest = context.Request
Dim response As HttpResponse = context.Response
Dim path As String = request.Path
If path.Contains("pps.pdfdownload") Then
Dim releaseMasterId As String = request.QueryString("ID")
If releaseMasterId IsNot Nothing Then
'Service call to generate report source
Dim service As New TelerikReportLibrary.ReportServices.PPSReportService
Dim source As Telerik.Reporting.TypeReportSource = service.GetReportSource(releaseMasterId)
'Render PDF and save
Dim reportProcessor As New ReportProcessor()
Dim result As RenderingResult = reportProcessor.RenderReport("PDF", source, Nothing)
Dim fileName As String = result.DocumentName + "_" + releaseMasterId + "." + result.Extension
response.Clear()
response.ContentType = result.MimeType
response.Cache.SetCacheability(HttpCacheability.Private)
response.Expires = -1
response.Buffer = True
response.AddHeader("Content-Disposition", String.Format("{0};FileName={1}", "attachment", fileName))
response.BinaryWrite(result.DocumentBytes)
End If
End If
response.End()
End Sub
Public ReadOnly Property IsReusable() As Boolean _
Implements System.Web.IHttpHandler.IsReusable
Get
Return False
End Get
End Property
End Class
Any further insight on why the original technique did not work is greatly appreciated.
I want to export to excel, but I want to choose the excel file that has the customized coloring and modification instead of the default excel page. How can I achieve this on asp.net with vb.
Take a look also on EasyXLS library. For exporting to excel, you can start with this code sample:
http://www.easyxls.com/manual/FAQ/export-to-excel-in-dot-net.html
Try using specific libraries for export to excel in asp.net. For example exist Syncfusion XlsIo, Telerik or spreadsheetgear (This is specializing in excel). In my case I'm using Syncfusion XlsIo and with this customize all the reports in excel, also can use graphics and conditional formats
Public Sub ExportToExcel (dt As DataTable)
If dt.Rows.Count > 0 Then
Dim filename As String = "DownloadMobileNoExcel.xls"
Dim tw As New System.IO.StringWriter()
Dim hw As New System.Web.UI.HtmlTextWriter(tw)
Dim dgGrid As New DataGrid()
dgGrid.DataSource = dt
dgGrid.DataBind()
'Get the HTML for the control.
dgGrid.RenderControl(hw)
'Write the HTML back to the browser.
Response.ContentType = "application/vnd.ms-excel"
Response.AppendHeader("Content-Disposition", (Convert.ToString("attachment; filename=") & filename) + "")
Me.EnableViewState = False
Response.Write(tw.ToString())
Response.End()
End If
End Sub
Okay, I've seen this asked in a couple of spots but never really got an answer that helped or that I understood. I'm just starting back on ASP.net and am using VS 2010 on the 4.0 framework.
I found some code online to allow you to generate an .xls Excel file from a dataset.
The code is below, but when I run this code from a button outside of my ajax update panel it works perfectly. If I execute the code from a button inside the update panel (where I need it to be) then I get the following:
Sys.WebForms.PageRequestManagerParserErrorException The message received from the server could not be parsed.
I have tried the two versions of the header shown and have tried the completerequest() instead of response.end.
Could someone explain to me why this doesn't work in the update panel and how I could make it work in the update panel?
Thanks in advance!
Protected Sub ExportDataSetToExcel(ByVal ds As DataSet, ByVal filename As String)
Dim response As HttpResponse = HttpContext.Current.Response
' first let's clean up the response.object
response.Clear()
response.Charset = ""
' set the response mime type for excel
response.ContentType = "application/vnd.ms-excel"
'response.ContentType = "application/octet-stream"
response.AddHeader("Content-Disposition", "attachment;filename=""" & filename & """")
' create a string writer
Using sw As New StringWriter()
Using htw As New HtmlTextWriter(sw)
' instantiate a datagrid
Dim dg As New DataGrid()
dg.DataSource = ds.Tables(0)
dg.DataBind()
dg.RenderControl(htw)
response.Write(sw.ToString())
'HttpContext.Current.ApplicationInstance.CompleteRequest()
response.End()
End Using
End Using
End Sub
Try this:
response.Flush();
HttpContext.Current.ApplicationInstance.CompleteRequest()
Do not call reposne.End()
check out this sample:
How to download/open the file that I retrive by server path?
it shows how to write a handler to DL a file.
Sorry, I know is a bit late, but found the answer right now.
Try with this answer. I resolve it doing what it is said here and it works perfectly
I just noticed it last night,
Anyway, let's get to the interesting case here.
I have a ButtonField within DataGrid, and if you notice it here...
The Interface of that ButtonField is looks like a LINK.
But if we hover on it, it appeared as Javascript's call.
Here is the Image ScreenShot
Ya, that's the 1st case.
IT IS a javascript's call.
I didnt notice about it lately. (hehehe).
Then, if we click on that... it would call the createPDF() function. The function behind the scene (which I'm using VB.net) is to execute these code;
Protected Sub createPDF()
Dim document As New Document()
Dim mem As LengthFixingStream = New LengthFixingStream()
' instantiate a iTextSharp.text.pdf.Document
'Dim mem As New MemoryStream()
' PDF data will be written here
PdfWriter.GetInstance(document, mem)
' tie a PdfWriter instance to the stream
document.Open()
Dim titleFont = FontFactory.GetFont("Arial", 18, Font.BOLD)
document.Add(New Paragraph("Northwind Traders Receipt", titleFont))
document.Close()
' automatically closes the attached MemoryStream
Dim docData As Byte() = mem.GetBuffer()
' get the generated PDF as raw data
' write the document data to response stream and set appropriate headers:
Response.AppendHeader("Content-Disposition", "attachment; filename=testdoc.pdf")
Response.ContentType = "application/pdf"
Response.BinaryWrite(docData)
Response.[End]()
End Sub
But somehow... this of course would not deliver the PDF into the browser.
BEcause It's called by Javascript, nor the direct as Hyperlink (normally).
Thus, I'm wondering could we get the ASP.net Call new Window,
and then redirect the createPDF() result into it?
Correct me if i'm wrong...
Here is just some mockup so you get the idea. I haven't tested this. Basically you will have to put the above code in a new page...say "receipt.aspx" and execute it on the load event...you will need to setup an id parameter...if data is being pulled from the db to generate the pdf.
on the button click add the following
Dim sb As New System.Text.StringBuilder()
sb.Append("<script language='javascript'>")
sb.Append("window.open('receipt.aspx.htm?id=123'', 'Receipt',")
sb.Append("'width=800, height=800, menubar=yes, resizable=no');<")
sb.Append("/script>")
Dim t As Type = Me.GetType()
If Not ClientScript.IsStartUpScriptRegistered(t, "PopupScript") Then
ClientScript.RegisterStartUpScript(t, "PopupScript", sb.ToString())
End If
Notice the "id=123" querystring value I am passing to receipt.aspx?
You can then call that in the receipt.aspx page like this
Dim id as String = Request.QueryString("id")
CreatePDF(id)
...shoot! Just realized you are using a Grid...the principle remains the same, just wireup the buttons on RowDataBound event.
Protected Sub GridView_RowDataBound(sender As Object, e As GridViewRowEventArgs)
If e.Row.RowType = DataControlRowType.DataRow Then
Dim Id As String = DirectCast(e.Row.Cells(0).FindControl("quotationid"), Label).Text
Dim myButton As New Button
myButton = DirectCast(e.Row.Cells(4).FindControl("btnViewReceipt"), Button)
myButton.Attributes.Add("OnClick", "window.open('receipt.aspx?id=" + id + "','Receipt','scrollbars=yes','width=800,height=800')")
End If
End Sub
I have an ASP.NET page like this:
http://farm4.static.flickr.com/3631/3690714302_c17b259863.jpg
My table is Gridview and some Label, anybody can tell me how to create a button to convert my page to image or PDF file and save it to desktop.
It can be done by code on the backend that renders the html into a memory Graphic object and then serves that resultant bitmap back, or by printing the page (on the server) through a PDF writer and then capturing the result.
Javascript, no.
Or google "online convert html pdf" and use one of those services.
Hai use iTextSharp to convert your webpage to pdf.
It looks very nice.
Just download the dll file and add reference to your application.
Then in button click give the coding as follows.
Response.ClearHeaders()
Response.ContentType = "application/pdf"
Response.AddHeader("content-disposition", attachment; filename="jaison.pdf")
Response.Cache.SetCacheability(HttpCacheability.NoCache)
Dim sw As New StringWriter()
Dim hw As New HtmlTextWriter(sw)
Me.Page.RenderControl(hw)
Dim sr As New StringReader(sw.ToString())
Dim pdfDoc As New Document(iTextSharp.text.PageSize.A2, 10, 10, 10, 10)
Dim htmlparser As New HTMLWorker(pdfDoc)
PdfWriter.GetInstance(pdfDoc, Response.OutputStream)
pdfDoc.Open()
htmlparser.Parse(sr)
pdfDoc.Close()
Response.Write(pdfDoc)
Response.End()
Surely this code will help you.
All the best!