利用AdobeReader的API将PDF转换成BMP
2015-12-05 13:30:17 | 来源:玩转帮会 | 投稿:佚名 | 编辑:小柯

原标题:利用AdobeReader的API将PDF转换成BMP

本文由玩赚乐(www.banghui.org)– 小峰原创翻译,转载请看清文末的转载要求,欢迎加入技术翻译小组!

从.pdf文件中提取页面,并保存为bitmaps

预告源代码 – 12KB

介绍

在我的一个项目中,我需要提取.pdf文件的页面。图像需要尽可能地完美,即最好是原始的分辨率(DPI)。很多软件都声称它们可以从PDF文件中提取图像,我试了几个。但是当涉及到保存原始DPI图像的时候,效果都不怎么好。有人可能会认为原始大小和Adobe Reader中的100%缩放级别视图是一样的,但往往并非如此。并且当图像被保存为,例如.jpg格式的时候,它会更加失真。所以我决定自己写一个。

背景

我想找一个免费的解决方案。而Adobe Reader就是免费的,并配备了一个可以嵌入到VB6的ActiveX控件。然而,它现有的功能非常有限(相对于附带Adobe Pro的ActiveX)。所以提取页面很难,因此我不得不求助于API调用来找到(子)窗口,发送消息。还有一件事是,我想隐藏ActiveXReader窗口,因为它的选择和取消选择以及定期调整大小看上去不美观。要发送击键和鼠标点击到一个隐藏的窗口,或让它重新绘制,需要额外编码。

使用代码

代码涉及多个主题:

  • 通过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)。

译文链接:
英文原文:PDF to BMP using Adobe Reader and API Functions
翻译作者:玩赚乐(www.banghui.org)– 小峰
[转载必须在正文中标注并保留原文链接、译文链接和译者等信息。]

tags:

上一篇  下一篇

相关:

眼睛总发痒,到底是为什么?

一觉起来,发现眼睛又红又肿、又痒又疼,我们的第一反应是去看医生。事实上,许多眼睛症状其实自己在家就能

如何诊断Java中的内存泄露

每次我怀疑有内存泄漏时,我都要翻箱倒柜找这些命令。所以,这里总结一下以备后用:首先,我用下面的命令监

蒋介石也炒过股?

根据周枕琴(曾任国民政府军政部军需署长)孙子周宏涛的说法,辛亥革命之后,因为革命经费十分困难,孙中山

FE华人杰2016夏季新品以“自由行走”为主题发布会于12月7-9日隆重召开


自由行走,在时光的流转中审视当下与未来,这是华人杰的宗旨。它希望让女性在繁琐的环境中解脱,更

在这个满是辐射的世界里,儿子失踪老婆死,我却天天捡垃圾

【多图慎点】1.钻石城的旅馆里有这么一个夹食物机好多人说这个是摆设,根本夹不出来然后我幸运 10 的角色执

有些手机倒过来,能看到文字的彩边

这可能是次像素渲染(subpixel rendering)的效果。换言之,文字的「彩边」是故意的。先从计算机图形显示的

PHP代码规范的10个好习惯

  PHP被称为 dirty but quick 的编程语言。尽管在其它编程语言使用者看来,PHP 程序并不简洁美观,但是我

提升运营效率的10款工具

决定运营人员工作效率的因素有很多:能力、经验、直属领导决策力等,但其实还有一个很重要的因素会决定你的

陈坤演唱电影《寻龙诀》主题曲 MV曝光

电影《寻龙诀》主题曲MV曝光,陈坤酷帅登场。(取自新浪娱乐) 陈坤表示,演唱《寻龙诀》主题曲时,自己

2016年度中国-美大地区国际科技合作项目战略评议会专家公示

兹定于2015年12月7日召开2015年度中国-美大地区国际科技合作项目战略评议会,现对参加评议的专家名单进行公

站长推荐: