利用 Adobe Reader 的API將 PDF 轉換成 BMP

2015-12-04    分類:其他、程式設計開發、首頁精華0人評論發表於2015-12-04

本文由碼農網 – 小峰原創翻譯,轉載請看清文末的轉載要求,歡迎參與我們的付費投稿計劃

從.pdf檔案中提取頁面,並儲存為bitmaps

下載原始碼 – 12KB

介紹

在我的一個專案中,我需要提取.pdf檔案的頁面。影像需要儘可能地完美,即最好是原始的解析度(DPI)。很多軟體都聲稱它們可以從PDF檔案中提取影像,我試了幾個。但是當涉及到儲存原始DPI影像的時候,效果都不怎麼好。有人可能會認為原始大小和Adobe Reader中的100%縮放級別檢視是一樣的,但往往並非如此。並且當影像被儲存為,例如.jpg格式的時候,它會更加失真。所以我決定自己寫一個。

背景

我想找一個免費的解決方案。而Adobe Reader就是免費的,並配備了一個可以嵌入到VB6的ActiveX控制元件。然而,它現有的功能非常有限(相對於附帶Adobe Pro的ActiveX)。所以提取頁面很難,因此我不得不求助於API呼叫來找到(子)視窗,傳送訊息。還有一件事是,我想隱藏ActiveX Reader視窗,因為它的選擇和取消選擇以及定期調整大小看上去不美觀。要傳送擊鍵和滑鼠點選到一個隱藏的視窗,或讓它重新繪製,需要額外編碼。

使用程式碼

程式碼涉及多個主題:

  • 通過Classname 和Text找到應用程式中的處理視窗
  • 找到.pdf檔案的頁數
  • 獲取.pdf檔案每個頁面的DPI

從一個.pdf檔案提取頁面的兩種方法:

A. 傳送滑鼠點選到一個隱藏的視窗

  • 用API函式keybd_event模擬Control-C輸入
  • 用API函式從剪貼簿獲取資料

B. 除原件外,獲取另一DPI的兩個方法

  1. 在另一個PictureBox中調整PictureBox.Picture大小為高品質影像
  2. 繪製隱藏視窗的內容到PictureBox

下載原始碼檢視所有這些問題的解釋性註釋。下面的程式碼片段解決了兩個問題:

通過Classname 和Text找到應用程式中的處理視窗:

Private Function FindWindowHandle(ByVal hwnd As Long, _
	SelectClass As String, SelectText As String, bSelect As Boolean) As Long
'' A recursive function to go through all the descendant windows of window with handle hwnd
'' Returns handle for window with Classname = SelectClass and Window Text 
'' that either contains SelectText if bSelect = True; or does not contain SelectText is bSelect = False
'' (There often is more than one window with the same class name)
'' SelectText may be empty ("") and then this function only searches for a Classname
'' Note : hwnd has to be ByVal

   Dim sClass As String, sText As String
   Dim sLen As Long
   Dim ParentHwnd As Long
   Dim FoundHwnd As Long
   FoundHwnd = 0

''Get Class name of window with handle hwnd

   sClass = Space(64)
   sLen = GetClassName(hwnd, sClass, 63)
   sClass = Left(sClass, sLen)
   If StrComp(sClass, SelectClass, 1) = 0 Then
        If SelectText <> "" Then
''Get Window Text of window
               sText = Space(256)
               sLen = SendMessageS(hwnd, WM_GETTEXT, 255, sText)
               sText = Left(sText, sLen)
''If bSelect = True : If the text matches we have found the window
               If bSelect = True Then
                   If InStr(sText, SelectText) > 0 Then
''FoundHwnd is the handle for the window with the required Classname and Text

                      FoundHwnd = hwnd
                   End If
               Else
''If bSelect = False : If the text does not match we have found the window
                   If InStr(sText, SelectText) = 0 Then
                      FoundHwnd = hwnd
                   End If
               End If
        Else
           FoundHwnd = hwnd
        End If
  End If
'' If the window is found, return its handle and exit

  If FoundHwnd <> 0 Then
          FindWindowHandle = FoundHwnd
          Exit Function
  End If

'' If the window is not found, look for the next child window
  ParentHwnd = hwnd 
  hwnd = FindWindowX(hwnd, 0, 0, 0)
  Do While hwnd
''Recursion : this function calls itself to find child windows of the child windows, 
''so all descendants, not just one level of child windows
      FoundHwnd = FindWindowHandle(hwnd, SelectClass, SelectText, bSelect)

      If FoundHwnd <> 0 Then
        Exit Do
      End If
'' FindWindowX is called repeatedly to find the next child window
      hwnd = FindWindowX(ParentHwnd, hwnd, 0, 0)
  Loop

  FindWindowHandle = FoundHwnd

End Function

第二個片段展示瞭如何傳送滑鼠點選到一個隱藏的視窗:

Private Sub SendLeftClick(ByVal hwnd As Long, ByVal hwnd2 As Long, x As Long, y As Long)

''Send Left mouse click to invisible window with handle hwnd and with top-level parent window hwnd2
    Dim position As Long

''Set window as active window
    Call SetActiveWindow(hwnd)
''Calculate lParam to pass the mouses x and y position in the window, (x and y in pixels)
    position = x * &H10000 + y
''The required messages with their wParam and lParam were found by using Spy++

    Call SendMessage(hwnd, WM_MOUSEACTIVATE, ByVal hwnd2, _
    	ByVal CLng(&H2010001)) ''lParam is HTCLIENT(=1, low) and WM_LBUTTONDOWN(= &H201, high)
    Call SendMessage(hwnd, WM_SETCURSOR, ByVal CLng(0), ByVal CLng(&H2010001))
    Call SendMessage(hwnd, WM_LBUTTONDOWN, ByVal CLng(1), ByVal position)
    Call SendMessage(hwnd, WM_LBUTTONUP, ByVal CLng(0), ByVal position)

End Sub

興趣點

這個應用程式是用VB6寫的,比起.NET我更喜歡VB6。而且個人認為,只要有VB6和一些API呼叫,那麼一切都能完成。當然,如果你喜歡其他的程式語言,也可以改寫原始碼,如果你熟悉API的話,這很容易的,因為API函式是這個應用程式的核心。

許可證

這篇文章,以及任何相關的原始碼和檔案,根據 The Code Project Open License (CPOL)。

譯文連結:http://www.codeceo.com/article/adobe-reader-api-pdf-bmp.html
英文原文:PDF to BMP using Adobe Reader and API Functions
翻譯作者:碼農網 – 小峰
轉載必須在正文中標註並保留原文連結、譯文連結和譯者等資訊。]

相關文章