65.9K
CodeProject 正在变化。 阅读更多。
Home

ASP.NET PDF 查看器用户控件,无需在客户端或服务器上安装 Acrobat Reader

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.77/5 (71投票s)

2013年9月19日

GPL3

3分钟阅读

viewsIcon

1036280

downloadIcon

18223

ASP.NET PDF 文档查看器控件,无需安装任何 Acrobat 产品

PDFViewASP

引言

本文讨论了如何创建一个 ASP.NET PDF 查看器用户控件,该控件不依赖于安装 Acrobat 软件。

基本概念

  1. 获取需要查看的 PDF 文档的页面计数,以定义您的页码边界 (PdfLibNet - XPDF) 
  2. 将 PDF 文档(按需特定页面)转换为光栅图像格式 (PdfLibNet - XPDF) 
  3. 将当前页面转换为 PNG 文件
  4. 在网页上的图像中显示 PNG 文件 

创建或从其他地方添加了几个实用程序类,这些类公开了各种辅助库所需的功能。

  1. AFPDFLibUtil.vb(包含创建书签 HTML、搜索、获取页面计数、将 PDF 转换为 PNG 的方法)
  2. ImageUtil.vb(包含图像操作方法,例如调整大小、旋转、转换等)
  3. ASPPDFLib.vb(包含调用特定技术的通用包装函数)
  4. PDFViewer.ascx.vb(包含 PDF 查看器用户控件的后台代码)
  5. PDFViewer.ascx(包含 PDF 查看器用户控件的客户端 HTML/JavaScript)

使用代码 

ASP 服务器配置要求

  • 您必须授予 ASPNET 用户(IISUSR 或 ASPNET 或网络服务用户)修改(读/写)/PDF/render 目录的权限。
  • 您必须授予 ASPNET 用户(IISUSR 或 ASPNET 或网络服务用户)读取和执行 /bin 目录的权限。
  • DLL PDFLibNet.dll 必须对页面可用。根据您的操作系统,您可能需要使用 GAC 注册它,以使其对应用程序可用。
  • PDFLibNet.dll 和 PDFLibCmdLine.exe 必须使用相同的架构(x86 或 x64)进行编译 
  • 如果使用 x86,您必须设置 AppPool 的高级设置以允许执行 32 位应用程序(如果您使用的是 64 位操作系统)。 

此项目包含 3 个 DLL,它们都必须位于同一目录下

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

要在网页上放置 PDF 查看器用户控件:  

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

设置 FileName 属性以在后台代码中查看 PDF 文件

      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

此解决方案的本质部分是从 PDF 文件中提取要查看的当前页面。由于我们使用的是 ASP.NET,我选择实现一个基于文件的解决方案,以避免在尝试为多个客户端保留 PDF 字节流时出现内存管理问题。我选择使用 PDFLibNet 从 PDF 中提取页面,并将其作为 PNG 图像存储到文件系统中。我选择了 PNG,因为它使用 ZIP 压缩,这会产生无损压缩图像和较小的文件大小。

    '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

在 PDFViewer 代码中,指定了一个页码,并且

  • 从 PDF 文件中加载页面并将其转换为 PNG 文件。
  • 我们将 PNG 文件名添加到 ASP.NET 缓存中,有效期为 5 分钟,以确保我们不会在服务器上留下渲染后的图像。
  • 使用 PNG 文件的路径更新 ImageUrl

PDFViewer.ascx.vb

 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

    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

关注点

由于其他人慷慨地自由分发的各种开源库,这个项目才得以实现。 我要感谢 PDFLibNet 的开发者 Antonio Sandoval 和 Foo Labs (XPDF) 他们的努力。

历史

  • 1.0 - 初始版本
  • 1.1 - 增加了搜索功能,减少了 DLL 依赖项,使 PDF 子系统仅使用 XPDF
  • 1.2 - 替换了 PDFLibNet.dll 以修复不正确的配置错误 0x800736B1
  • 1.3 - 优化搜索例程
  • 1.4 - 移除过时的指向旧内容链接
  • 1.5 - 更新权限信息
© . All rights reserved.