Search code examples
asp.net-mvcasp.net-mvc-3itexthtml-to-pdf

iTextSharp MVC View to PDF


I'm having a little trouble with my TextReader when trying to parse the html string I want to convert to PDF when using iTextSharp.

Function ViewDeliveryNote(ByVal id As Integer) As FileStreamResult
        'Memory buffer
        Dim ms As MemoryStream = New MemoryStream()

        'the document
        Dim document As Document = New Document(PageSize.A4)

        'the pdf writer
        PdfWriter.GetInstance(document, ms)

        Dim wc As WebClient = New WebClient
        Dim htmlText As String = wc.DownloadString("http://localhost:59800/Warehouse/DeliveryNote/" & id) 'Change to live URL
        Dim worker As html.simpleparser.HTMLWorker = New html.simpleparser.HTMLWorker(document)
        Dim reader As TextReader = New StringReader(htmlText)

        document.Open()

        worker.Open()
        worker.StartDocument()
        worker.Parse(reader)
        worker.EndDocument()
        worker.Close()

        document.Close()

        'ready the file stream
        Response.ContentType = "application/pdf"
        Response.AddHeader("content-disposition", "attachment;filename=DeliveryNote.pdf")
        Response.Buffer = True
        Response.Clear()
        Response.OutputStream.Write(ms.GetBuffer(), 0, ms.GetBuffer.Length)
        Response.OutputStream.Flush()
        Response.End()

        Return New FileStreamResult(Response.OutputStream, "application/pdf")
 End Function

The line it stops on is worker.Parse(reader) with the error Object reference not set to an instance of an object even though StringReader(htmlText) has successfully read the HTML page.

I'm not sure what I'm doing wrong or what I'm missing at the moment so I would be grateful for any assistance.

UPDATE I just tried Dim reader As New StringReader(htmlText) instead but to no avail. Although htmlText still definitely contains a value, but the object thinks that it doesn't.


Solution

  • I would definitely write a custom action result for this to avoid polluting my controller. Also all those undisposed disposable resources in your code should be taken care of:

    Public Class PdfResult
        Inherits ActionResult
    
        Private ReadOnly _id As Integer
    
        Public Sub New(ByVal id As Integer)
            _id = id
        End Sub
    
        Public Overrides Sub ExecuteResult(context As ControllerContext)
            If context Is Nothing Then
                Throw New ArgumentNullException("context")
            End If
    
            Dim response = context.HttpContext.Response
            response.Buffer = True
            response.ContentType = "application/pdf"
            response.AddHeader("Content-Disposition", "attachment; filename=DeliveryNote.pdf")
    
            Using client = New WebClient()
                Dim htmlText As String = client.DownloadString("http://localhost:59800/Warehouse/DeliveryNote/" & _id) 'Change to live URL
                Dim doc = New Document(PageSize.A4)
                PdfWriter.GetInstance(doc, response.OutputStream)
                Dim worker = New HTMLWorker(doc)
                doc.Open()
                worker.Open()
                Using reader = New StringReader(htmlText)
                    worker.Parse(reader)
                End Using
                doc.Close()
            End Using
        End Sub
    End Class
    

    and then simply:

    Function ViewDeliveryNote(ByVal id As Integer) As ActionResult
        Return New PdfResult(id)
    End Function
    

    You should also make sure that the server has access to the desired url. Don't forget that his request will execute in the context of the Network Account which might not have the same privileges as normal accounts.