简体   繁体   中英

Print one page of pdf document from VBA using shell

I have PDF documents that are mostly multi page but I only want to print page 1 from each document. The following prints the entire pdf document. I have seen reference to a parameter page=1 but do not seem to get that into the right place in the following:-

Shell ("""" & "C:\Program Files (x86)\Adobe\Acrobat Reader DC\Reader\AcroRd32.exe" & """/p /h """ & strPathAndFilename & """")

Can someone please show me how to get the above shell command to just print page 1 of the document. Many thanks.

Using that method it should not in theory be possible, hysterically/historically the Adobe support take on that line is. Unclear if its a breach of EULA to programable aid a Reader user.

...with the free Reader too. Command lines were declared obsolete by Microsoft around 1992...

Thus not a supported Adobe means (just highly popular abuse of Acrobat Reader, including MS commercial printing). There are workarounds.

So to directly answer your question you would need to simply split off page 1 to only send that to Acrobat Reader for blind print the onepage.pdf

There are different methods within Acrobat API via vba to designate only one page is to be converted. but then that's using an expensive method for such a "simple" task.

Thus 3 more common routes to your solution

  1. command line extract page 1 to use as above.
  2. use a page addressable pdf to print solution not abusing Reader.
  3. using VBA or VBS address the default print dialog see https://stackoverflow.com/a/63279528/10802527 "Works for me" (note the different output location with only one page) attribution/kudos to @freesoftwareservers
wscript  "pdf_print_sendkeys.vbs" "C:\Users\WDAGUtilityAccount\Desktop\SandBox\apps\PDF\Adobe\Reader\AcroRd32.exe" "C:\test.pdf" "1"

在此处输入图像描述

For the easier first 2 options there are many good solutions

  1. qpdf is FOSS can easily extract 1 page, but may not send to print.
  2. no good FOSS solution as better ones are naturally commercial

Disclaimer I support SumatraPDF which using just one portable exe (kept as binary is FOSS) can command line print page 1 easily, HOWEVER it will only be print as image and if that's acceptable should serve you well.

Thank you for your response. I could do it using Acrobat Pro but will settle for just the /p parameter and let the user restrict the printed output to the first page.

Please, try the next way. It launch the Adobe Acrobat Reader DC printing window and then finds its control necessary handlers and change/press them according to the printing need. SendKeys does mean a reliable method, if you move the mouse and change the focus from the window where it should act:

  1. Copy the next API declarations on top of a standard module (in the declarations area:
 Private Declare PtrSafe Function FindWindow Lib "user32" Alias "FindWindowA" _
        (ByVal lpClassName As String, ByVal lpWindowName As String) As LongPtr
 Private Declare PtrSafe Function FindWindowEx Lib "user32" Alias "FindWindowExA" (ByVal hwnd1 As LongPtr, _
        ByVal hwnd2 As LongPtr, ByVal lpsz1 As String, ByVal lpsz2 As String) As LongPtr
 Private Declare PtrSafe Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As LongPtr, _
        ByVal wMsg As Long, ByVal wParam As LongPtr, lParam As Any) As Long
 Private Declare PtrSafe Function GetNextWindow Lib "user32.dll" Alias "GetWindow" (ByVal hwnd As LongPtr, _
        ByVal wFlag As Long) As LongPtr

Then copy the next code in the same module:

Sub PrintPdfSpecificPage()
    Dim strPathAndFilename As String, strPages As String
    
    strPathAndFilename = "your pdf file full name" 'use here the real full name
    strPages = "2" 'the page to be printed number
                   'it may be "1,2" or "2-4"...
    
    'launch the Adobe Acrobat DC printing window:
    Shell ("""" & "C:\Program Files (x86)\Adobe\Acrobat Reader DC\Reader\AcroRd32.exe" & """/p """ & strPathAndFilename & """")
    
    'wait for the window to expose its handle and continue the process of the necessary controls identification (their handles):
     Dim prHwnd As LongPtr, grBoxHw1 As LongPtr, grNext4 As LongPtr, grNext20 As LongPtr
     Dim pgHwnd As LongPtr, pgNHwnd As LongPtr, printHwnd As LongPtr, nrPag As String
     Const BM_CLICK = &HF5, WM_SETTEXT = &HC
     Const WM_LBUTTON_DOWN = &H201, WM_LBUTTON_UP = &H202
     
     Do While prHwnd = 0
        prHwnd = FindWindow("#32770", "Print")
        DoEvents
     Loop
     Application.Wait Now + TimeValue("00:00:02") 'wait two seconds for the whole window to expose its controls handles
      grBoxHw1 = FindWindowEx(prHwnd, 0, "GroupBox", vbNullString)     'the first child GroupBox window
        grNext4 = getNextChildX(grBoxHw1, 3, "GroupBox"): Hex (grNext4) 'find the fourth control handle
         pgHwnd = FindWindowEx(grNext4, 0, "Button", "Pa&ges")                'find the radio button accepting pages interval handler
         
          SendMessage pgHwnd, WM_LBUTTON_DOWN, 0&, 0&
          SendMessage pgHwnd, BM_CLICK, 0&, ByVal 0&                               'check the radio button
          
          pgNHwnd = FindWindowEx(grNext4, 0, "RICHEDIT50W", vbNullString) 'find the window where the page number to be dropped
             
         SendMessage pgNHwnd, WM_LBUTTON_DOWN, ByVal 0&, 0& 'without this lines, the page number is changed, but the changing event is not triggered...
         SendMessage pgNHwnd, WM_LBUTTON_UP, ByVal 0&, 0&
             
           SendMessage pgNHwnd, WM_SETTEXT, 0&, ByVal strPages  'place the page to be printed number
             
         SendMessage pgNHwnd, WM_LBUTTON_DOWN, ByVal 0&, 0&
         SendMessage pgNHwnd, WM_LBUTTON_UP, ByVal 0&, 0&
             
          grNext20 = getNextChildX(grBoxHw1, 19, "GroupBox"): Debug.Print 'find the GroupBox where the Print button exists
            printHwnd = FindWindowEx(grNext20, 0&, "Button", "Print")            'find the Print button handle
              
              SendMessage printHwnd, BM_CLICK, 0&, ByVal 0&                          'press/click the Print button:
              
        'close the Acrobat main Window:
        Dim acrobatHwnd As LongPtr
        Const WM_SYSCOMMAND = &H112, SC_CLOSE = &HF060
        
         'wait for the printing Window to be closed, to let Acrobat expose its main window handle:
         Do While acrobatHwnd = 0
                    acrobatHwnd = FindWindow("AcrobatSDIWindow", "Adobe Acrobat Reader DC (32-bit)")
                    DoEvents
        Loop

        SendMessage acrobatHwnd, &H10, 0&, 0& 'close the main Acrobat Reader window
End Sub

Function getNextChildX(parentHwnd As LongPtr, x As Long, strClass As String) As LongPtr
       Dim nextChild As LongPtr, i As Long
       nextChild = FindWindowEx(parentHwnd, 0&, strClass, vbNullString)
        For i = 1 To x
             nextChild = GetNextWindow(nextChild, 2)
        Next i
        getNextChildX = nextChild
End Function

It was a little more complicated than I thought, but worked in my environment...

Please, test it and send some feedback

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM