Click here to Skip to main content
15,867,453 members
Articles / Web Development / ASP.NET

ASP.NET PDF Viewer User Control Without Acrobat Reader Installed on Client or Server

Rate me:
Please Sign up or sign in to vote.
4.77/5 (72 votes)
19 Sep 2013GPL33 min read 917.2K   18.2K   365   330
ASP.NET PDF document viewer control that does not require any Acrobat product to be installed
PDFViewASP

Introduction

This article discusses how to create an ASP.NET PDF Viewer User Control that is not dependent on Acrobat software being installed.

Fundamental Concepts

  1. Get a page count of the PDF document that needs to be viewed to define your page number boundaries (PdfLibNet - XPDF) 
  2. Convert the PDF document (specific page on demand) to a raster image format (PdfLibNet - XPDF)  
  3. Convert the current page to be viewed into a PNG file
  4. Display the PNG file in an image on a web page 

Several utility classes were created or added from others which expose functionality needed from the various helper libraries.

  1. AFPDFLibUtil.vb (contains methods to create Bookmark HTML, Search, get page count, convert PDF to PNG)
  2. ImageUtil.vb (contains methods for image manipulation such as resize, rotation, conversion, etc.)
  3. ASPPDFLib.vb (contains generic wrapper functions that call specific technologies)
  4. PDFViewer.ascx.vb (contains code behind for the PDF Viewer User Control)
  5. PDFViewer.ascx (contains client side HTML/JavaScript for the PDF Viewer User Control)

Using the Code 

ASP Server Configuration Requirements

  • You must give the ASPNET user (IISUSR or ASPNET or Network services user) permission to modify (read/write) the /PDF and /render directories.
  • You must give the ASPNET user (IISUSR or ASPNET or Network services user) permission to  Read & Execute the /bin diirectory.
  • The DLL PDFLibNet.dll must be available to the page. You might have to register it with the GAC depending on your operating system to make it available to the application.
  • PDFLibNet.dll and PDFLibCmdLine.exe must both be compiled with the same architecture (x86 or x64) 
  • If x86 is used, you must set the advanced settings of the AppPool to allow execution of 32-bit applications if you are on a 64-bit OS.  

This project consists of 3 DLLs that must all be in the same directory:

  • PDFLibNET.dll
  • StatefullScrollPanel.dll
  • PDFViewASP.dll

To place a PDF Viewer User Control on a web page:   

ASP.NET
<uc1:PDFViewer ID="PDFViewer1" runat="server" /> 

Set the FileName property to view the PDF file in the code behind:

VB.NET
Dim pdfFileName As String = Request.MapPath("PDF") & "\" & "myPDF.pdf"
If ImageUtil.IsPDF(pdfFileName) Then
  ErrorLabel.Visible = False
  PDFViewer1.FileName = pdfFileName
Else
  ErrorLabel.Text = "Only PDF files (*.pdf) are allowed to be viewed."
  ErrorLabel.Visible = True
End If

The essential part of this solution is extracting the current page to be viewed from a PDF file. Since we are using ASP.NET, I chose to implement a file based solution to avoid memory management issues when trying to persist PDF byte streams for multiple clients. I chose to extract a page from PDF using PDFLibNet and store it to the File System as a PNG image. I chose PNG since it uses ZIP compression which results in a lossless compressed image and small file size.

VB.NET
  'Modified for ASP usage
  Public Shared Function GetPageFromPDF(ByVal filename As String, _
  ByVal destPath As String, ByRef PageNumber As Integer, _
  Optional ByVal DPI As Integer = RENDER_DPI, _
  Optional ByVal Password As String = "", _
  Optional ByVal searchText As String = "", _
  Optional ByVal searchDir As SearchDirection = 0) As String
  GetPageFromPDF = ""
  Dim pdfDoc As New PDFLibNet.PDFWrapper
  pdfDoc.RenderDPI = 72
  pdfDoc.LoadPDF(filename)
  If Not Nothing Is pdfDoc Then
    pdfDoc.CurrentPage = PageNumber
    pdfDoc.SearchCaseSensitive = False
    Dim searchResults As New List(Of PDFLibNet.PDFSearchResult)
    If searchText <> "" Then
      Dim lFound As Integer = 0
      If searchDir = SearchDirection.FromBeginning Then
        lFound = pdfDoc.FindFirst(searchText, _
          PDFLibNet.PDFSearchOrder.PDFSearchFromdBegin, False, False)
      ElseIf searchDir = SearchDirection.Forwards Then
        lFound = pdfDoc.FindFirst(searchText, _
          PDFLibNet.PDFSearchOrder.PDFSearchFromCurrent, False, False)
      ElseIf searchDir = SearchDirection.Backwards Then
        lFound = pdfDoc.FindFirst(searchText, _
          PDFLibNet.PDFSearchOrder.PDFSearchFromCurrent, True, False)
      End If
      If lFound > 0 Then
        If searchDir = SearchDirection.FromBeginning Then
          PageNumber = pdfDoc.SearchResults(0).Page
          searchResults = GetAllSearchResults(filename, searchText, PageNumber)
        ElseIf searchDir = SearchDirection.Forwards Then
          If pdfDoc.SearchResults(0).Page > PageNumber Then
            PageNumber = pdfDoc.SearchResults(0).Page
            searchResults = GetAllSearchResults(filename, searchText, PageNumber)
          Else
            searchResults = SearchForNextText(filename, searchText, _
                      PageNumber, searchDir)
            If searchResults.Count > 0 Then
              PageNumber = searchResults(0).Page
            End If
          End If
        ElseIf searchDir = SearchDirection.Backwards Then
          If pdfDoc.SearchResults(0).Page < PageNumber Then
            PageNumber = pdfDoc.SearchResults(0).Page
            searchResults = GetAllSearchResults(filename, searchText, PageNumber)
          Else
            searchResults = SearchForNextText(filename, searchText, _
                      PageNumber, searchDir)
            If searchResults.Count > 0 Then
              PageNumber = searchResults(0).Page
            End If
          End If
        End If
      End If
    End If
    Dim outGuid As Guid = Guid.NewGuid()
    Dim output As String = destPath & "\" & outGuid.ToString & ".png"
    Dim pdfPage As PDFLibNet.PDFPage = pdfDoc.Pages(PageNumber)
    Dim bmp As Bitmap = pdfPage.GetBitmap(DPI, True)
    bmp.Save(output, System.Drawing.Imaging.ImageFormat.Png)
    bmp.Dispose()
    GetPageFromPDF = output
    If searchResults.Count > 0 Then
      GetPageFromPDF = HighlightSearchCriteria(output, DPI, searchResults)
    End If
    pdfDoc.Dispose()
  End If
End Function

In the PDFViewer code, a page number is specified and:

  • The page is loaded from the PDF file and converted to a PNG file.
  • We add the PNG file name to the ASP.NET Cache with an expiration of 5 minutes to ensure that we don't leave rendered images lying around on the server.
  • The ImageUrl is updated with the path to the PNG file.

PDFViewer.ascx.vb

VB.NET
Private Sub DisplayCurrentPage(Optional ByVal doSearch As Boolean = False)
  'Set how long to wait before deleting the generated PNG file
  Dim expirationDate As DateTime = Now.AddMinutes(5)
  Dim noSlide As TimeSpan = System.Web.Caching.Cache.NoSlidingExpiration
  Dim callBack As New CacheItemRemovedCallback(AddressOf OnCacheRemove)
  ResizePanels()
  CheckPageBounds()
  UpdatePageLabel()
  InitBookmarks()
  Dim destPath As String = Request.MapPath("render")
  Dim indexNum As Integer = (parameterHash("CurrentPageNumber") - 1)
  Dim numRotation As Integer = parameterHash("RotationPage")(indexNum)
  Dim imageLocation As String
  If doSearch = False Then_
    imageLocation = ASPPDFLib.GetPageFromPDF(parameterHash("PDFFileName"), _
    destPath, parameterHash("CurrentPageNumber"), parameterHash("DPI"), _
    parameterHash("Password"), numRotation)
  Else
    imageLocation = ASPPDFLib.GetPageFromPDF(parameterHash("PDFFileName"), destPath _
                                             , parameterHash("CurrentPageNumber") _
                                             , parameterHash("DPI") _
                                             , parameterHash("Password") _
                                             , numRotation, parameterHash("SearchText") _
                                             , parameterHash("SearchDirection") _
                                             )
    UpdatePageLabel()
  End If
  ImageUtil.DeleteFile(parameterHash("CurrentImageFileName"))
  parameterHash("CurrentImageFileName") = imageLocation
  'Add full filename to the Cache with an expiration
  'When the expiration occurs, it will call OnCacheRemove which will delete the file
  Cache.Insert(New Guid().ToString & "_DeleteFile", imageLocation, _
   Nothing, expirationDate, noSlide, _
   System.Web.Caching.CacheItemPriority.Default, callBack)
  Dim matchString As String = _
   Request.MapPath("").Replace("\", "\\") ' escape backslashes
  CurrentPageImage.ImageUrl = Regex.Replace(imageLocation, matchString & "\\", "~/")
End Sub

Private Sub OnCacheRemove(ByVal key As String, ByVal val As Object, _
   ByVal reason As CacheItemRemovedReason)
  If Regex.IsMatch(key, "DeleteFile") Then
    ImageUtil.DeleteFile(val)
  End If
End Sub

ASPPDFLib.vb

VB.NET
  Public Shared Function GetPageFromPDF(ByVal sourceFileName As String _
                                      , ByVal destFolderPath As String _
                                      , ByRef iPageNumber As Integer _
                                      , Optional ByVal DPI As Integer = 0 _
                                      , Optional ByVal password As String = "" _
                                      , Optional ByVal rotations As Integer = 0 _
                                      , Optional ByVal searchText As String = "" _
                                      , Optional ByVal searchDir As Integer = _
                                          AFPDFLibUtil.SearchDirection.FromBeginning _
                                      ) As String
  GetPageFromPDF = AFPDFLibUtil.GetPageFromPDF(sourceFileName, _
  destFolderPath, iPageNumber, DPI, password, searchText, searchDir)
  ImageUtil.ApplyRotation(GetPageFromPDF, rotations)
End Function

Points of Interest

This project was made possible due to various open source libraries that others were kind enough to distribute freely. I would like to thank the PDFLibNet developer Antonio Sandoval and Foo Labs (XPDF) for their efforts.

History

  • 1.0 - Initial version
  • 1.1 - Added Search capabilities, reduced DLL dependencies, made PDF subsystem use XPDF only
  • 1.2 - Replaced PDFLibNet.dll to fix incorrect configuration error 0x800736B1
  • 1.3 - Optimized search routines
  • 1.4 - Remove outdated links to legacy content
  • 1.5 - Updated permissions information

License

This article, along with any associated source code and files, is licensed under The GNU General Public License (GPLv3)


Written By
Software Developer
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionPDFviewer1 is not declared Pin
Member 148106362-May-20 10:58
Member 148106362-May-20 10:58 
Questionlink to download not working Pin
traci dukes14-Mar-19 10:27
traci dukes14-Mar-19 10:27 
QuestionHelp me, please! Pin
hieulonghai9-Sep-18 5:38
hieulonghai9-Sep-18 5:38 
QuestionPdf with uneven size or broad size not displaying properly Pin
ankitkarmiyal3-Jul-18 0:48
ankitkarmiyal3-Jul-18 0:48 
QuestionHow to display the Bookmark and click event of bookmark. Pin
resac29-Dec-15 20:18
resac29-Dec-15 20:18 
QuestionPDFViewer for ASP.NET for .net framework 4.5+ Pin
Sirisha.BGS24-Sep-15 2:05
Sirisha.BGS24-Sep-15 2:05 
Questionhow i can downlod PDFLibCmdLine.exe? Pin
mohsen_etemadian27-Jun-15 22:04
mohsen_etemadian27-Jun-15 22:04 
QuestionQuality of image is low when run code from windows server 2008 r2 64 bits Pin
rabbwhite14-Jan-15 15:49
rabbwhite14-Jan-15 15:49 
GeneralMy vote of 1 Pin
Sachin Greeshail7-Jan-15 0:53
professionalSachin Greeshail7-Jan-15 0:53 
QuestionHow can I download the code Pin
Bhaskar KV17-Nov-14 0:02
Bhaskar KV17-Nov-14 0:02 
QuestionApplication Pool is getting Stopped When this code is hosted In IIS Pin
Member 110521624-Nov-14 17:37
Member 110521624-Nov-14 17:37 
Questionneed help in converting to C# Pin
Rameshwary17-Oct-14 22:48
Rameshwary17-Oct-14 22:48 
AnswerRe: need help in converting to C# Pin
jomynn17-Oct-14 23:02
jomynn17-Oct-14 23:02 
GeneralRe: need help in converting to C# Pin
Julian Joseph - India17-Apr-15 19:49
Julian Joseph - India17-Apr-15 19:49 
GeneralRe: need help in converting to C# Pin
Member 1160403811-Sep-15 7:00
Member 1160403811-Sep-15 7:00 
AnswerRe: need help in converting to C# Pin
Julian Joseph - India17-Apr-15 19:56
Julian Joseph - India17-Apr-15 19:56 
QuestionPlease help to convert some code to C# Pin
jomynn6-Oct-14 15:45
jomynn6-Oct-14 15:45 
QuestionI am facing an issue of getting blank image while a lot of members view PDF Pin
suniljainmca20087-Sep-14 22:28
suniljainmca20087-Sep-14 22:28 
AnswerRe: I am facing an issue of getting blank image while a lot of members view PDF Pin
Member 110521625-Nov-14 1:36
Member 110521625-Nov-14 1:36 
QuestionHow to download the source code Pin
Tridip Bhattacharjee21-Aug-14 21:37
professionalTridip Bhattacharjee21-Aug-14 21:37 
QuestionCan the user right-click and save as to any type of file (e.g. jpg, png, etc..)? Pin
g00n3r1115-Aug-14 9:34
g00n3r1115-Aug-14 9:34 
AnswerRe: Can the user right-click and save as to any type of file (e.g. jpg, png, etc..)? Pin
g00n3r1122-Aug-14 7:19
g00n3r1122-Aug-14 7:19 
GeneralMy vote of 5 Pin
kentclark51916-Jul-14 8:33
kentclark51916-Jul-14 8:33 
SuggestionPlease post this in c# as well Pin
bhanu prasad nala24-Mar-14 2:44
bhanu prasad nala24-Mar-14 2:44 
GeneralRe: Please post this in c# as well Pin
ZurdoDev4-Apr-14 9:15
professionalZurdoDev4-Apr-14 9:15 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.