Announcement

Collapse
No announcement yet.

forgiving command line pdf merge or command line pdf correcting tool

Collapse
X
 
  • Filter
  • Time
  • Show
Clear All
new posts

  • forgiving command line pdf merge or command line pdf correcting tool

    After watching a paid for email and attachments-to-pdf tool fail, I've had a go at writing my own.

    I've got so far and have hit soem problems.

    1 the command line pdf merger tools baulk at some of the pdfs whereas the gui tools I have eat them for breakfast.
    2 I wanted to have complete control over the process but, as usual, the process is controlling me
    e.g.

    Code:
    lRes& = ShellExecute(0, "", z_exe, "*.pdf all.pdf", "", %SW_SHOWNORMAL)
    WaitForSingleObject(lRes&,%INFINITE)
    ? "lRes& = "; lRes&
    is printing out the return value far too quickly. As an aside I got error 42 whatever that means.

    so...
    does anyone know of a very tolerant command line pdf merger exe
    or
    a command line tool i can use beforehand to "straighten" the "malformed" pdfs
    and...
    what do I need to do to stop the program until its succeeded or failed with each pdf and make it tell me what the problems are...like what I get when I run the tool from the command line.

    BTW I'm working in PBCC

    Any help much appreciated

  • #2
    Not sure if this is something that would work for what you want to do, but take a look at PDFTK (PDF Toolkit) at https://www.pdflabs.com. It comes in a free version as well as a Pro version ($4).

    Jerry

    Comment


    • #3
      Thank you for the suggestion Jerry.
      I was very hopeful about that. The latest version complains but Pdftkbuilder a gui around an older version does work so I need to play with that.
      Dean

      Comment


      • #4
        re:
        Code:
        lRes& = ShellExecute(0, "", z_exe, "*.pdf all.pdf", "", %SW_SHOWNORMAL)
        WaitForSingleObject(lRes&,%INFINITE)
        ? "lRes& = "; lRes&
        ShellExecute does not return a process handle suitable for use in the WaitForSingleObject function. That is why WFSO returns so quickly ... because it is failing (quickly). .

        You need to check your WinAPI doc for the ShellExecute Function and see what it returns and what that return might mean.

        FWIW, I just posted a real nice function for getting the text from a Windows-generated error code (as you work thru the doc you will find you need it) ... let's see if I can find it (it's not in the source code section)...

        Nah... heck let's post it in Source Code Forum with a meaningful title... heck I am having trouble with the code tags and file won't indent.

        It uses the WinAPI function "FormatMessage() and it has been updated for A) Wide-char sensitivity and B) the extended internet errors; I'll play with posting it later.

        [LATER]

        I got it posted and I also attached the #INCLUDE file here:

        System Error Message Text


        MCM
        Last edited by Michael Mattias; 22 Apr 2021, 10:44 AM. Reason: Add note re posted function SystemErrorMessageText
        Michael Mattias
        Tal Systems (retired)
        Port Washington WI USA
        [email protected]
        http://www.talsystems.com

        Comment


        • #5
          Thank you very much your advice and offer Michael
          codes below 33 are errors for ShellExecute..and above are handles so if that's right 42 is good.
          I've switched to the synchronous version of SHELL for the time being to stop things running away from me.
          Before I did I had lots of fleeting consoles with messages that were too fast to see.
          I'd like to get them under control...in a single console or failing that a log file.

          Comment


          • #6
            codes below 33 are errors for ShellExecute..and above are handles so if that's right 42 is good.
            I did not read the doc that way.

            Return Values

            If the function succeeds, the return value is the instance handle of the application that was run, or the handle of a dynamic data exchange (DDE) server application.


            If the function fails, the return value is an error value that is less than or equal to 32. The following table lists these error values:
            An instance handle is NOT a (required) process handle!

            That's why I (almost) always use ShellExecuteEx.... the SHELLEXECUTEINFO structure returns an "hProcess" automatically!

            MCM
            Michael Mattias
            Tal Systems (retired)
            Port Washington WI USA
            [email protected]
            http://www.talsystems.com

            Comment


            • #7
              heck I am having trouble with the code tags and file won't indent.
              Posted Source Code Losing Indentation/Formatting - PowerBASIC Peer Support Community
              Dale

              Comment


              • #8
                Michael ShellExecuteEx noted for future reference. Thank you.
                Dale...thank you for trying to post

                Comment


                • #9
                  ??? trying ??? I did post. That is the link to Adam's instructions for keeping source code formatting in posts.
                  Dale

                  Comment


                  • #10
                    As long as you are noting ShellExecuteEx for the future, how about some commented code showing how to use it to launch a program and wait until it completes?

                    Win 32: Monitor Process with ShellExecuteEx June 22, 2001

                    As you can see, that's fairly recent code!
                    Michael Mattias
                    Tal Systems (retired)
                    Port Washington WI USA
                    [email protected]
                    http://www.talsystems.com

                    Comment


                    • #11
                      As you can see, that's fairly recent code!
                      Recent?

                      Compared to what??
                      Dale

                      Comment


                      • #12
                        Great code thank you very much.
                        I tried to bend it to pbcc but can't quite see how to specify the exe and file to work on to shellexecuteex. shellexecute fires up the exe but shellexecuteex is baulking, probably quite rightly.

                        Also PBCC seems not to like some of the equates so...I commented them out...for now.

                        Code:
                        #COMPILE EXE
                        '#DIM ALL
                        
                        #INCLUDE "win32api.INC"
                        
                        DECLARE FUNCTION SystemErrorMessageText (BYVAL ECode AS LONG) AS STRING
                        
                        
                        ' TEXT MESSAGES FROM Win32API 'GetLastError' calls
                        FUNCTION SystemErrorMessageText (BYVAL ECode AS LONG) AS STRING
                        LOCAL Buffer AS ASCIIZ * 255
                        FormatMessage %FORMAT_MESSAGE_FROM_SYSTEM, BYVAL %NULL, ECode, %NULL, buffer, SIZEOF(buffer), BYVAL %NULL
                        FUNCTION = FORMAT$(ECode, "#####") & Buffer
                        END FUNCTION
                        
                        
                        'FUNCTION DoTheShellEx (BYVAL hwnd AS LONG, BYVAL ControlID AS LONG) AS LONG
                        FUNCTION DoTheShellEx () AS LONG
                        LOCAL SEI AS ShellExecuteInfo, Stat AS LONG, E AS LONG
                        LOCAL hWaitProcess AS LONG
                        LOCAL lpVerb AS ASCIIZ * 20, lpParameters AS ASCIIZ * 20, lpDirectory AS ASCIIZ * 20, lpFile AS ASCIIZ * %MAX_PATH
                        LOCAL TimeOut2 AS LONG ' "timeout" is another un-hilited "reserved" word which is truly only reserved
                        ' in the context of a TCP or UDP verb and can only be found with "search."
                        ' The error message if you use 'timeout' as a variable name is
                        ' "statement expected," which is a useless error message.
                        SEI.cbSize = SIZEOF(SEI)
                        ' SEI.fmask = %SEE_MASK_NOCLOSEPROCESS
                        ' SEI.hWnd = hWnd
                        
                        'dIM gs AS ASCIZ * 1000
                        'DIM args AS ASCIZ * 1000
                        'gs = $DQ & "C:\Program Files\gs\gs9.54.0\bin\gswin64c.exe" & $DQ
                        
                        gs$ = $DQ & "C:\Program Files\gs\gs9.54.0\bin\gswin64c.exe" & $DQ
                        DIM lpVerb AS ASCIZ PTR
                        lpVerb = STRPTR(gs$)
                        DIM lpFile AS ASCIZ PTR
                        args$ = ""
                        lpFile = STRPTR(args$)
                        
                        SEI.lpVerb = lpVerb 'varptr(gs) 'VARPTR(lpVerb)
                        SEI.LpFile = lpFile 'varptr(args) 'VARPTR(lpFile)
                        SEI.lpParameters = VARPTR(lpParameters)
                        SEI.lpDirectory = %NULL
                        SEI.nShow = %SW_SHOW ' should be zero for a document file PER MSDN DOC.
                        ' MSDN DOC IS WRONG, use SW_SHOW!
                        SEI.hInstApp = 0 ' updated by function
                        SEI.lpIdList = %NULL ' here down to hprocess ignored unless appropriate mask included in fmask
                        SEI.lpClass = %NULL
                        SEI.hkeyClass = %NULL
                        SEI.dwHotKey = %NULL
                        'SEI.item = %NULL
                        SEI.hProcess = 0 ' will be updated by ShellExecuteEx
                        
                        IF ISTRUE ShellExecuteEx(SEI) THEN ' function succeeded and returned
                        hWaitProcess = SEI.hProcess
                        Stat = WaitForSingleObjectEx (hWaitProcess, Timeout2, 0)
                        SELECT CASE Stat
                        CASE -1&
                        E = GetLastError
                        ? "Wait Failed:" & SystemErrorMessageText (E)
                        CASE %WAIT_OBJECT_0
                        ? "Wait returned from " & lpVerb & " on process completion"
                        CASE %WAIT_TIMEOUT
                        ? "Wait returned from " & lpVerb & " on Timeout"
                        CASE ELSE
                        ? "Unexpected Return, code=" & STR$(Stat)
                        END SELECT
                        CloseHandle hWaitProcess
                        ELSE
                        E = GetLastError
                        ? "ShellExecuteEx failed:" '& SystemErrorMessageText(E)
                        END IF
                        END FUNCTION
                        
                        FUNCTION PBMAIN () AS LONG
                        'dim gs as asciz * 1000
                        'dim args as asciz * 1000
                        'gs = $DQ & "C:\Program Files\gs\gs9.54.0\bin\gswin64c.exe" & $DQ
                        'lRes& = ShellExecute(0, "", gs, args, "", %SW_SHOWNORMAL)
                        'the above firesup ghostscript
                        
                        DoTheShellEx()
                        END FUNCTION

                        Comment


                        • #13
                          I've posted this elsewhere here on this forum already, but I guess it's worth repeating: your (console) Windows programs will run in MS' WSL (aka "Linux"). I'm running a Ubuntu Server 18.0 LTS version on it. And it comes with Ghostscript, So here's a very simple way to combine PB + Linux (gs):

                          Code:
                          #Compile Exe ".\LinuxConsoleStdOut.exe"
                          
                          Function PBMain () As Long
                          
                             ' Make sure to surpress the line feed by ending the line with ";"
                             Con.StdOut " -sOutputFile=./MergedDocument.pdf -dBATCH -sDEVICE=pdfwrite -dNOPAUSE -dPDFSETTINGS=/prepress " & Command$;
                          
                          End Function
                          And here's how to call it from Linux:
                          Code:
                          gs $(./LinuxConsoleStdOut.exe "./PdfToMerge1.pdf" "./PdfToMerge2.pdf" "./PdfToMerge3.pdf")
                          This then merges all PDFs passed to the PB program (Command$) into one PDF called MergedDocument.pdf in the current folder.

                          It's also worth pointing out that th Linux installation in WSL has fill access to the Windows filesystem, e.g. a folder C:\Data\MyData is accessible as /mnt/c/Data/MyData from within your Linux installation. Just keep in mind that on Linux file and folder names are case-sensitive.

                          Comment


                          • #14
                            Originally posted by Knuth Konrad View Post
                            I've posted this elsewhere here on this forum already, but I guess it's worth repeating: your (console) Windows programs will run in MS' WSL (aka "Linux"). I'm running a Ubuntu Server 18.0 LTS version on it. And it comes with Ghostscript,
                            I haven't used it for years, but Ghostscript is available for Windows so your solution should work directly in PB without going though MS' WSL
                            https://www.ghostscript.com/download/gsdnld.html

                            I used it for an application called MakePDF 20 years ago. Here's the Ghostscript invocation from that application

                            Code:
                                 OPEN tempfilename FOR OUTPUT AS #1
                                 PRINT #1, " -dCompatibilityLevel#" & level & " -q -dNOPAUSE -dBATCH -sDEVICE#pdfwrite -sOutputFile#" & CHR$(34)&  outfile & CHR$(34) & " -c save pop -f  " & CHR$(34) & infile & CHR$(34)
                                  CLOSE #1
                                  shellstring = gspath & " @" & CHR$(34) &  tempfilename & CHR$(34)
                                  SHELL shellstring,0
                            And just to show that I can post old, irrelevant links too, here's a post about it:
                            https://forum.powerbasic.com/forum/u...236#post105236
                            Don't bother to follow the link to Lexacorp, it re-directs to my current domain that doesn't host any ancient, outdated PB applications

                            Comment


                            • #15
                              Originally posted by Stuart McLachlan View Post

                              I haven't used it for years, but Ghostscript is available for Windows so your solution should work directly in PB without going though MS' WSL
                              https://www.ghostscript.com/download/gsdnld.html

                              I used it for an application called MakePDF 20 years ago. Here's the Ghostscript invocation from that application
                              I know. It's just that the Windows command prompt has certain limitations, which Linux systems typically exceed. In my WSL that's:
                              Code:
                              getconf ARG_MAX [email protected]
                              2097152

                              Comment


                              • #16
                                Originally posted by Knuth Konrad View Post

                                I know. It's just that the Windows command prompt has certain limitations, which Linux systems typically exceed. In my WSL that's:
                                Code:
                                getconf ARG_MAX [email protected]
                                2097152
                                If you need ore than 8191 characters in your command line, you can use the method shown in post#14.
                                i.e. build a temporary file with all the parameters and pass that to Ghostscript.

                                (Does that 8191 character limit also apply to Shell or just to a Command prompt? )

                                Comment


                                • #17
                                  Originally posted by Stuart McLachlan View Post
                                  If you need ore than 8191 characters in your command line, you can use the method shown in post#14.
                                  i.e. build a temporary file with all the parameters and pass that to Ghostscript.
                                  Sure, but using (temporary) files as a means for inter-process communication is something I personally try to avoid, if possible. It introduces (another) possible point of failure, which can be avoided.

                                  Originally posted by Stuart McLachlan View Post
                                  (Does that 8191 character limit also apply to Shell or just to a Command prompt? )
                                  Good question to which I don't know the answer. Assuming that Shell() uses the CreateProcess API, this applies:
                                  The maximum length of this string is 32,767 characters, including the Unicode terminating null character. If lpApplicationName is NULL, the module name portion of lpCommandLine is limited to MAX_PATH characters.
                                  Though by calling cmd.exe via Shell(), I guess we're back to square one, i.e. cmd.exe's limitations cited above.

                                  Comment


                                  • #18
                                    (Does that 8191 character limit also apply to Shell or just to a Command prompt? )
                                    I have used up to 2MB of arguments as the "lpParameters" argument to CreateProcess(). (Only as a test. For production I would pass a memory object name which was a GUIDTXT$() value.

                                    Introducing a command-shell component I know can introduce all kinds of limits on text argument lengths. This is one of the reasons I have avoided using embedded command shells in my applications.

                                    Michael Mattias
                                    Tal Systems (retired)
                                    Port Washington WI USA
                                    [email protected]
                                    http://www.talsystems.com

                                    Comment

                                    Working...
                                    X