Home > Software engineering >  Print one page of pdf document from VBA using shell
Print one page of pdf document from VBA using shell

Time:09-04

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.

CodePudding user response:

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 enter image description here

    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.

    CodePudding user response:

    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.

    CodePudding user response:

    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 = "C:\Teste VBA Excel\Areas.pdf"
        strPages = "2" 'the page to be printed number
        
        '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

  • Related