Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/vba/14.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何";“展平”;PDF格式;“Microsoft打印到PDF”;在VBA中使用Win32 API调用?_Vba_Pdf_Winapi_Printing_Flatten Pdf - Fatal编程技术网

如何";“展平”;PDF格式;“Microsoft打印到PDF”;在VBA中使用Win32 API调用?

如何";“展平”;PDF格式;“Microsoft打印到PDF”;在VBA中使用Win32 API调用?,vba,pdf,winapi,printing,flatten-pdf,Vba,Pdf,Winapi,Printing,Flatten Pdf,我试图让我的Excel VBA代码读取交互式PDF(即带有下拉列表、日历等的PDF)。我通常通过在Word中打开PDF文件来阅读它们;但是,Word在呈现文件时会删除所有交互控件(例如,日历控件连同其值一起被完全删除) 我知道我可以通过在AdobeAcrobatReader(或任何浏览器)中打开PDF并将其打印为PDF(从而用选定值替换所有控件)来“展平”PDF。问题是如何在VBA中以编程方式实现这一点。我知道关于这个话题,在S.O.上有成千上万的问题;但是,所有答案要么需要安装Adobe Ac

我试图让我的Excel VBA代码读取交互式PDF(即带有下拉列表、日历等的PDF)。我通常通过在Word中打开PDF文件来阅读它们;但是,Word在呈现文件时会删除所有交互控件(例如,日历控件连同其值一起被完全删除)

我知道我可以通过在AdobeAcrobatReader(或任何浏览器)中打开PDF并将其打印为PDF(从而用选定值替换所有控件)来“展平”PDF。问题是如何在VBA中以编程方式实现这一点。我知道关于这个话题,在S.O.上有成千上万的问题;但是,所有答案要么需要安装Adobe Acrobat Pro,使用第三方安装的应用程序(例如CutePDF),将文件提交到在线API,要么最常见的是使用SendKeys或Win32 API调用与出现的“另存为”对话框交互。我正在编写的代码将分发给多个用户;因此,任何附加的应用程序安装都是不可能的(AcrobatPro和其他第三方转换工具)。文件包含专有数据;因此,一个在线API也出现了。就SendKeys或SendMessage而言……坦白说,一定有更好的方法

我尝试过很多方法,包括Chrome的无头“打印到pdf”;但它似乎只接受HTML文件(我甚至尝试将PDF嵌入HTML文件,但仍然不起作用)。 我得到的最接近的方法是使用下面的代码将数据直接发送到“MicrosoftPrinttoPDF”驱动程序(它基于所描述的过程)。代码成功创建了一个PDF文件…但它是一个零字节文件。“WritePrinter”函数似乎没有正确接受从输入文件读取的数据(尽管没有发生错误)。尽管“WritePrinter”函数的第二个参数声明“pBuf”参数应该是字节数组,但询问的人传递了一个字符串并使其工作(理解他们没有试图打印到GDI打印机)。如果我将字节数组转换为字符串,甚至使用FSO的“ReadAll”方法读取输入文件的内容,我的代码仍然会生成一个空白PDF文件。 “WritePrinter”函数的返回值是“1”,而“pcWrite”输出的值是正确的字节数,这也没有什么价值……只是生成的PDF文件的文件大小为0字节

那么,有人能想出如何让“WritePrinter”函数接受从输入文件读取的数据吗

顺便说一句,这对任何能够理解这一点的人来说都是一颗巨大的金星,因为根据我的研究,互联网正在寻求一种不使用Acrobat Pro或与“另存为”对话框交互的方法

更新#1:我在网上发现了一些帖子,其中用户遇到了“Microsoft打印到PDF”驱动程序手动生成空白文件的问题。显然,罪魁祸首是输入文件名中的特殊字符。我想清楚地表明,我不相信这是我的问题,因为我可以手动打印成PDF格式,只是不能通过本文中的代码。(另外,正如前面所说,输入文件路径/名称和输出文件路径/名称都不包含特殊字符)。此外,我不认为打印驱动程序的具体安装有问题(如某些帖子所建议的),因为我的代码在3台不同的计算机上创建了0字节文件,3台不同的Windows操作系统版本(18363.1082、18363.1139和19041.572)

更新#2:在我对这个问题的持续研究中,我在MSDN的Visual Studio帮助论坛上发现了这个问题。我知道这是给C的,但其中一位出资人表示:

因此,您应该将托管字节数组转换为非托管数组,然后调用该方法

他提供的C#代码使用“Marshal.allocTaskMem”和“Marshal.Copy”函数“将托管字节数组复制到非托管数组”。我不熟悉术语“托管”或“非托管”字节数组,因此我将继续对它们进行一些研究。 是否有人有使用Excel(VB6)中VBA的“Marshal.allocTaskMem”和“Marshal.Copy”函数的经验

更新#3:我注意到我编写的代码只会将XPS文件打印为PDF格式。我已经手动将我原来的交互式PDF转换为XPS,并确认我的代码在将XPS转换为PDF时运行良好。 这让我回到了第一步:如何在不使用任何第三方应用程序或在线转换器的情况下以编程方式阅读交互式PDF? 有人有什么想法吗


Type DOCINFO
   lpszDocName As String
   lpszOutput As String
   lpszDatatype As String
End Type

Private Declare PtrSafe Function OpenPrinter Lib "winspool.drv" Alias "OpenPrinterA" (ByVal pPrinterName As String, phPrinter As Long, ByVal pDefault As Long) As Long
Private Declare PtrSafe Function StartDocPrinter Lib "winspool.drv" Alias "StartDocPrinterA" (ByVal hPrinter As Long, ByVal Level As Long, pDocInfo As DOCINFO) As Long
Private Declare PtrSafe Function StartPagePrinter Lib "winspool.drv" (ByVal hPrinter As Long) As Long
Private Declare PtrSafe Function WritePrinter Lib "winspool.drv" (ByVal hPrinter As Long, pBuf As Any, ByVal cdBuf As Long, pcWritten As Long) As Long
Private Declare PtrSafe Function EndPagePrinter Lib "winspool.drv" (ByVal hPrinter As Long) As Long
Private Declare PtrSafe Function EndDocPrinter Lib "winspool.drv" (ByVal hPrinter As Long) As Long
Private Declare PtrSafe Function ClosePrinter Lib "winspool.drv" (ByVal hPrinter As Long) As Long

Public Sub Print_File_To_PDF(strInputFile As String, strOutputPDF As String)
   Dim udtDocInfo As DOCINFO
   Dim lngPrinterCheck As Long, lngFileNumber As Long, lngPrinterHandle As Long, lngPrinterOutput as Long
   Dim arrBytes() As Byte

   'Get handle of printer
   lngPrinterCheck = OpenPrinter("Microsoft Print To PDF", lngPrinterHandle, 0)
   If lngPrinterCheck = 0 Then
       Exit Sub
   End If

   'Define document info
   With udtDocInfo
       .lpszDocName = CreateObject("Scripting.FileSystemObject").GetBaseName(strOutputPDF)
       .lpszOutput = strOutputPDF
       .lpszDatatype = "RAW"
   End With

   'Read file into byte array
   lngFileNumber = FreeFile
   Open strInputFile For Binary Access Read As lngFileNumber
   ReDim arrBytes(LOF(lngFileNumber))
   Get lngFileNumber, , arrBytes()
   Close lngFileNumber

   'Print byte array to PDF file
   Call StartDocPrinter(lngPrinterHandle, 1, udtDocInfo)
   Call StartPagePrinter(lngPrinterHandle)
   Call WritePrinter(lngPrinterHandle, arrBytes(1), UBound(arrBytes), lngPrinterOutput)
   Call EndPagePrinter(lngPrinterHandle)
   Call EndDocPrinter(lngPrinterHandle)
   Call ClosePrinter(lngPrinterHandle)
End Sub

WritePrinter()
的返回值是多少?您能否成功打印其他类型的文件?WritePrint(以及所有其他Win32 API函数)的返回值为>=1。我用Word文件(.docx)和txt文件进行了尝试,结果相同。我还尝试将udtDocInfo的“lpszDatatype”属性的值从“RAW”更改为我应该提到的另一件事是WritePrinter函数的pcWrited输出确实返回了正确的字节数…但是由于某些原因,函数实际写入的PDF文件的文件大小为0。因此,总而言之:WritePrinter的返回值为“1”(表示成功),pcWrite输出的字节数正确,但生成的PDF文件大小为0字节。
WritePrinter
调用看起来很笨拙。最后一个参数应该是有效的指针。未理解值
0
;尽管这没什么区别。我编辑了我的帖子,使其具有一个有效指针作为“WritePrinter”函数的最后一个参数。新添加的“lngPrinterOutput”指针的值是正确的字节数;但是,生成的PDF文件仍然是