How to Get Number of Pages of Open Foxit PDF Document Topic is solved

Get help with using AutoHotkey (v1.1 and older) and its commands and hotkeys
AlFlo
Posts: 360
Joined: 29 Nov 2021, 21:46

How to Get Number of Pages of Open Foxit PDF Document

Post by AlFlo » 04 May 2022, 00:44

I have to work with a large number of .pdf documents using Foxit Editor (previously called Foxit Phantom). Often times, the numbering in the bottom left page navigation box is hinky. For example, the numbering in the document as shown in the page navigation box on the bottom left might go from "16/16" to "1", depending on how the document was set up.

Does anyone know how to have AHK determine the total number of pages in an open .pdf document? In the above example, the correct answer would give the result of "17" total pages, since that is the actual total number of pages in the document.

I have tried to adapt this script, but am not getting anywhere: viewtopic.php?t=89193
AHKStudent
Posts: 1472
Joined: 05 May 2018, 12:23

Re: How to Get Number of Pages of Open Foxit PDF Document

Post by AHKStudent » 04 May 2022, 01:04

if you open those pdf's in notepad and do edit find Pages/Count do you see number of pages?
User avatar
Frosti
Posts: 426
Joined: 27 Oct 2017, 14:30
Contact:

Re: How to Get Number of Pages of Open Foxit PDF Document

Post by Frosti » 04 May 2022, 02:33

Oh, I see you tried one of my functions. These were written for FoxitReader. Unless I've stupidly misunderstood something in a quick read of your post, you want the total number of pages in the displayed document.
I can suggest a function. The function still results from my attempts to disassemble PDF files natively using Autohotkey and to extract data. Since PDF files can contain all kinds of content. Among other things, also compressed, encoded and compressed again content. I then decided on a built-in automatic fallback solution via the commandline tool "qpdf". qpdf you can get here.

I would say that it is anything but a slim command line tool yet.
qpdf-10.6.3-bin-msvc32.zip 9.25 MB
qpdf-10.6.3-bin-msvc64.zip 9.73 MB

Code: Select all


PDFGetPages(pdfFilePath , qpdfPath:="")                           	{               	;-- returns count of pages

	; last change 25.11.2021

	; PDF's are written in ANSI
		If !(fobj := FileOpen(pdfFilePath, "r", "CP0"))
			return 0

	; search string(s) that contain/s/ing page data
		while !fobj.AtEof {
			line := fobj.ReadLine()
			If RegExMatch(line, "i)\/(Count|Pages)\s+(?<ages>\d+)\/", p)
				break
			else If RegExMatch(line, "Length\s+(?<seek>\d+)", file)
				fobj.seek(fileseek, 1)
		}
		fobj.Close()

	; sometimes it finds more pages than money on Elon Musk's stock portfolio - so qpdf will be used as fallback
		If RegExMatch(pages, "\d+")
			If (pages > 0 && pages < 10000)
				return pages

	; #### if there's no match, sometimes the PDF XREF table is encoded/compressed - qpdf will be used
		If qpdfPath {
			qpdfPages := StdoutToVar(qpdfPath "\qpdf.exe --show-npages " q pdfFilePath q)
			If RegExMatch(qpdfPages, "\d+", qpages)
				return qpages
		}

return 0
}


StdoutToVar(sCmd, sEncoding:="UTF-8", sDir:="", ByRef nExitCode:=0) {                               	;-- cmdline Ausgabe in einen String umleiten

    DllCall( "CreatePipe"					, "PtrP"	,hStdOutRd, "PtrP",hStdOutWr, "Ptr",0, "UInt",0 )
    DllCall( "SetHandleInformation"	, "Ptr"	,hStdOutWr, "UInt"	,1, "UInt",1)

               VarSetCapacity( pi, (A_PtrSize == 4) ? 16 : 24,  0 )
    siSz := VarSetCapacity( si, (A_PtrSize == 4) ? 68 : 104, 0 )
    NumPut( siSz,         si,  0,                                      		"UInt" )
    NumPut( 0x100,     si,  (A_PtrSize == 4) ? 44 : 60, 	"UInt" )
    NumPut( hStdOutWr, si,  (A_PtrSize == 4) ? 60 : 88, 	"Ptr"  )
    NumPut( hStdOutWr, si,  (A_PtrSize == 4) ? 64 : 96, 	"Ptr"  )

    If (!DllCall( "CreateProcess", "Ptr",0, "Ptr",&sCmd, "Ptr",0, "Ptr",0, "Int",True, "UInt",0x08000000, "Ptr",0, "Ptr",sDir?&sDir:0, "Ptr",&si, "Ptr",&pi ))
        Return ""
      , DllCall( "CloseHandle", "Ptr",hStdOutWr )
      , DllCall( "CloseHandle", "Ptr",hStdOutRd )

    DllCall( "CloseHandle", "Ptr",hStdOutWr ) ; The write pipe must be closed before reading the stdout.
    While ( 1 )  { ; Before reading, we check if the pipe has been written to, so we avoid freezings.

        If (!DllCall( "PeekNamedPipe", "Ptr",hStdOutRd, "Ptr",0, "UInt",0, "Ptr",0, "UIntP",nTot, "Ptr",0 ))
            Break

        If ( !nTot ) { ; If the pipe buffer is empty, sleep and continue checking.
            Sleep, 100
            Continue
        } ; Pipe buffer is not empty, so we can read it.

        VarSetCapacity(sTemp, nTot+1)
        DllCall( "ReadFile", "Ptr",hStdOutRd, "Ptr",&sTemp, "UInt",nTot, "PtrP",nSize, "Ptr",0 )
        sOutput .= StrGet(&sTemp, nSize, sEncoding)

    }

    ; * SKAN has managed the exit code through SetLastError.
    DllCall( "GetExitCodeProcess"	, "Ptr",NumGet(pi,0), "UIntP",nExitCode	)
    DllCall( "CloseHandle"       	, "Ptr",NumGet(pi,0)                           	)
    DllCall( "CloseHandle"       	, "Ptr",NumGet(pi,A_PtrSize)               	)
    DllCall( "CloseHandle"       	, "Ptr",hStdOutRd                               	)

Return sOutput
}

However, if you want to know the exact page you are on in the document at the moment, then you will have to help yourself by reading out the surface information from the FoxitEditor.
Since I don't use the Foxit Editor, I can't really help you with reading out the controls.
AlFlo
Posts: 360
Joined: 29 Nov 2021, 21:46

Re: How to Get Number of Pages of Open Foxit PDF Document

Post by AlFlo » 04 May 2022, 11:18

AHKStudent wrote:
04 May 2022, 01:04
if you open those pdf's in notepad and do edit find Pages/Count do you see number of pages?
AHKStudent, opening in notepad gives me an inaccurate page count (my test .pdf is 11 pages, but Notepad thought it was 38 pages).
AlFlo
Posts: 360
Joined: 29 Nov 2021, 21:46

Re: How to Get Number of Pages of Open Foxit PDF Document

Post by AlFlo » 04 May 2022, 11:19

BoBo: Thank you, that does look promising. Unfortunately, I don't know any C programming.
AHKStudent
Posts: 1472
Joined: 05 May 2018, 12:23

Re: How to Get Number of Pages of Open Foxit PDF Document

Post by AHKStudent » 04 May 2022, 11:25

AlFlo wrote:
04 May 2022, 11:18
AHKStudent wrote:
04 May 2022, 01:04
if you open those pdf's in notepad and do edit find Pages/Count do you see number of pages?
AHKStudent, opening in notepad gives me an inaccurate page count (my test .pdf is 11 pages, but Notepad thought it was 38 pages).
keep on looking in the txt file you should see the correct number of pages
AlFlo
Posts: 360
Joined: 29 Nov 2021, 21:46

Re: How to Get Number of Pages of Open Foxit PDF Document

Post by AlFlo » 04 May 2022, 12:11

Frosti wrote:
04 May 2022, 02:33
Oh, I see you tried one of my functions. These were written for FoxitReader. Unless I've stupidly misunderstood something in a quick read of your post, you want the total number of pages in the displayed document.
I can suggest a function.
Thanks, Frosti. Please excuse my ignorance, but is my assumption correct that I can't use your AHK script until I've installed qpdf? And that I can't install/use qpdf unless I have a C++ compiler?

If so, this looks really good, but is above my pay grade (I'm a newbie in all things AHK, let alone C++).
RickC
Posts: 302
Joined: 27 Oct 2013, 08:32

Re: How to Get Number of Pages of Open Foxit PDF Document

Post by RickC » 04 May 2022, 20:26

I haven't yet found a way using AHK but I've been using a PowerShell script with a free commandline utility called PDFINFO to find the page count of PDF files in a folder.

For example, scanning a folder of 60 PDF files took just 3 seconds to return a list of each PDF file with their page count. PDFINFO is a component of XPDF. Download the ZIP file from https://dl.xpdfreader.com/xpdf-tools-win-4.04.zip and unzip it. I found it easiest to just copy and paste .\xpdf-tools-win-4.04\xpdf-tools-win-4.04\bin64\pdfinfo.exe into C:\Windows so I didn't have to bother with PATH statements.

The PowerShell script is:

Code: Select all

$folder = 'C:\PDF_TEST'
$Total = $Files = 0

foreach($File in (Get-ChildItem -Path $Folder -Filter *.pdf)){
    $Pages = (pdfinfo $File.FullName | Select-String -Pattern '(?<=Pages:\s*)\d+').Matches.Value
    $Total += $Pages
    $Files++
    [PSCustomObject]@{
        PdfFile = $File.Name
        Pages   = $Pages
    }
}
"`nTotalNumber of pages: {0} in {1} files" -f $Total,$Files
I'll warn you now... on folders with large nos. of PDF files there's sometimes an issue with the REGEX expression in line 5 but I have zero REGEX skills/knowledge to know what is wrong or how to fix. The point is... the PS script still just works. Here's a screenshot of just a few files so I can show both script and output:

Image

I know it's not what you wanted but hope an alternative method is useful.
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: How to Get Number of Pages of Open Foxit PDF Document  Topic is solved

Post by malcev » 04 May 2022, 20:47

Code: Select all

pdfPath := "G:\7585637.pdf"
VarSetCapacity(GUID, 16)
DllCall("ole32\CLSIDFromString", "wstr", IID_RandomAccessStream := "{905A0FE1-BC53-11DF-8C49-001E4FC686DA}", "ptr", &GUID)
DllCall("ShCore\CreateRandomAccessStreamOnFile", "wstr", pdfPath, "uint", Read := 0, "ptr", &GUID, "ptr*", IRandomAccessStream)
CreateClass("Windows.Data.Pdf.PdfDocument", IPdfDocumentStatics := "{433A0B5F-C007-4788-90F2-08143D922599}", PdfDocumentStatics)
DllCall(NumGet(NumGet(PdfDocumentStatics+0)+8*A_PtrSize), "ptr", PdfDocumentStatics, "ptr", IRandomAccessStream, "ptr*", PdfDocument)   ; LoadFromStreamAsync
WaitForAsync(PdfDocument)
DllCall(NumGet(NumGet(PdfDocument+0)+7*A_PtrSize), "ptr", PdfDocument, "uint*", PageCount)   ; get_PageCount
ObjReleaseClose(IRandomAccessStream)
ObjReleaseClose(PdfDocumentStatics)
ObjReleaseClose(PdfDocument)
msgbox % PageCount
return



CreateClass(string, interface := "", ByRef Class := "")
{
   CreateHString(string, hString)
   if (interface = "")
      result := DllCall("Combase.dll\RoActivateInstance", "ptr", hString, "ptr*", Class, "uint")
   else
   {
      VarSetCapacity(GUID, 16)
      DllCall("ole32\CLSIDFromString", "wstr", interface, "ptr", &GUID)
      result := DllCall("Combase.dll\RoGetActivationFactory", "ptr", hString, "ptr", &GUID, "ptr*", Class, "uint")
   }
   if (result != 0)
   {
      if (result = 0x80004002)
         msgbox No such interface supported
      else if (result = 0x80040154)
         msgbox Class not registered
      else
         msgbox error: %result%
      ExitApp
   }
   DeleteHString(hString)
}

CreateHString(string, ByRef hString)
{
    DllCall("Combase.dll\WindowsCreateString", "wstr", string, "uint", StrLen(string), "ptr*", hString)
}

DeleteHString(hString)
{
   DllCall("Combase.dll\WindowsDeleteString", "ptr", hString)
}

WaitForAsync(ByRef Object)
{
   AsyncInfo := ComObjQuery(Object, IAsyncInfo := "{00000036-0000-0000-C000-000000000046}")
   loop
   {
      DllCall(NumGet(NumGet(AsyncInfo+0)+7*A_PtrSize), "ptr", AsyncInfo, "uint*", status)   ; IAsyncInfo.Status
      if (status != 0)
      {
         if (status != 1)
         {
            DllCall(NumGet(NumGet(AsyncInfo+0)+8*A_PtrSize), "ptr", AsyncInfo, "uint*", ErrorCode)   ; IAsyncInfo.ErrorCode
            msgbox AsyncInfo status error: %ErrorCode%
            ExitApp
         }
         ObjRelease(AsyncInfo)
         break
      }
      sleep 10
   }
   DllCall(NumGet(NumGet(Object+0)+8*A_PtrSize), "ptr", Object, "ptr*", ObjectResult)   ; GetResults
   ObjReleaseClose(Object)
   Object := ObjectResult
}

ObjReleaseClose(ByRef Object)
{
   if Object
   {
      if (Close := ComObjQuery(Object, IClosable := "{30D5A829-7FA4-4026-83BB-D75BAE4EA99E}"))
      {
         DllCall(NumGet(NumGet(Close+0)+6*A_PtrSize), "ptr", Close)   ; Close
         ObjRelease(Close)
      }
      ObjRelease(Object)
   }
   Object := ""
}
AlFlo
Posts: 360
Joined: 29 Nov 2021, 21:46

Re: How to Get Number of Pages of Open Foxit PDF Document

Post by AlFlo » 04 May 2022, 20:56

malcev wrote:
04 May 2022, 20:47

Code: Select all

pdfPath := "G:\7585637.pdf"
VarSetCapacity(GUID, 16)
DllCall("ole32\CLSIDFromString", "wstr", IID_RandomAccessStream := "{905A0FE1-BC53-11DF-8C49-001E4FC686DA}", "ptr", &GUID)
DllCall("ShCore\CreateRandomAccessStreamOnFile", "wstr", pdfPath, "uint", Read := 0, "ptr", &GUID, "ptr*", IRandomAccessStream)
CreateClass("Windows.Data.Pdf.PdfDocument", IPdfDocumentStatics := "{433A0B5F-C007-4788-90F2-08143D922599}", PdfDocumentStatics)
DllCall(NumGet(NumGet(PdfDocumentStatics+0)+8*A_PtrSize), "ptr", PdfDocumentStatics, "ptr", IRandomAccessStream, "ptr*", PdfDocument)   ; LoadFromStreamAsync
WaitForAsync(PdfDocument)
DllCall(NumGet(NumGet(PdfDocument+0)+7*A_PtrSize), "ptr", PdfDocument, "uint*", PageCount)   ; get_PageCount
ObjReleaseClose(IRandomAccessStream)
ObjReleaseClose(PdfDocumentStatics)
ObjReleaseClose(PdfDocument)
msgbox % PageCount
return



CreateClass(string, interface := "", ByRef Class := "")
{
   CreateHString(string, hString)
   if (interface = "")
      result := DllCall("Combase.dll\RoActivateInstance", "ptr", hString, "ptr*", Class, "uint")
   else
   {
      VarSetCapacity(GUID, 16)
      DllCall("ole32\CLSIDFromString", "wstr", interface, "ptr", &GUID)
      result := DllCall("Combase.dll\RoGetActivationFactory", "ptr", hString, "ptr", &GUID, "ptr*", Class, "uint")
   }
   if (result != 0)
   {
      if (result = 0x80004002)
         msgbox No such interface supported
      else if (result = 0x80040154)
         msgbox Class not registered
      else
         msgbox error: %result%
      ExitApp
   }
   DeleteHString(hString)
}

CreateHString(string, ByRef hString)
{
    DllCall("Combase.dll\WindowsCreateString", "wstr", string, "uint", StrLen(string), "ptr*", hString)
}

DeleteHString(hString)
{
   DllCall("Combase.dll\WindowsDeleteString", "ptr", hString)
}

WaitForAsync(ByRef Object)
{
   AsyncInfo := ComObjQuery(Object, IAsyncInfo := "{00000036-0000-0000-C000-000000000046}")
   loop
   {
      DllCall(NumGet(NumGet(AsyncInfo+0)+7*A_PtrSize), "ptr", AsyncInfo, "uint*", status)   ; IAsyncInfo.Status
      if (status != 0)
      {
         if (status != 1)
         {
            DllCall(NumGet(NumGet(AsyncInfo+0)+8*A_PtrSize), "ptr", AsyncInfo, "uint*", ErrorCode)   ; IAsyncInfo.ErrorCode
            msgbox AsyncInfo status error: %ErrorCode%
            ExitApp
         }
         ObjRelease(AsyncInfo)
         break
      }
      sleep 10
   }
   DllCall(NumGet(NumGet(Object+0)+8*A_PtrSize), "ptr", Object, "ptr*", ObjectResult)   ; GetResults
   ObjReleaseClose(Object)
   Object := ObjectResult
}

ObjReleaseClose(ByRef Object)
{
   if Object
   {
      if (Close := ComObjQuery(Object, IClosable := "{30D5A829-7FA4-4026-83BB-D75BAE4EA99E}"))
      {
         DllCall(NumGet(NumGet(Close+0)+6*A_PtrSize), "ptr", Close)   ; Close
         ObjRelease(Close)
      }
      ObjRelease(Object)
   }
   Object := ""
}
THANKS, MALCEV! THIS SEEMS TO WORK LIKE A ROCK STAR!
RickC
Posts: 302
Joined: 27 Oct 2013, 08:32

Re: How to Get Number of Pages of Open Foxit PDF Document

Post by RickC » 04 May 2022, 21:00

Excellent solution, @malcev
User avatar
JoeWinograd
Posts: 2213
Joined: 10 Feb 2014, 20:00
Location: U.S. Central Time Zone

Re: How to Get Number of Pages of Open Foxit PDF Document

Post by JoeWinograd » 04 May 2022, 21:11

Hi @malcev,
Great stuff! Tested here on W10 and W11...worked perfectly! What is the oldest version of Windows that it will work on? Thanks, Joe
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: How to Get Number of Pages of Open Foxit PDF Document

Post by malcev » 04 May 2022, 21:19

Windows requirements
Device family: Windows 10 (introduced in 10.0.10240.0)
https://docs.microsoft.com/en-us/uwp/api/windows.data.pdf.pdfdocument?view=winrt-22000
AHKStudent
Posts: 1472
Joined: 05 May 2018, 12:23

Re: How to Get Number of Pages of Open Foxit PDF Document

Post by AHKStudent » 04 May 2022, 22:13

malcev wrote:
04 May 2022, 20:47

Code: Select all

pdfPath := "G:\7585637.pdf"
VarSetCapacity(GUID, 16)
DllCall("ole32\CLSIDFromString", "wstr", IID_RandomAccessStream := "{905A0FE1-BC53-11DF-8C49-001E4FC686DA}", "ptr", &GUID)
DllCall("ShCore\CreateRandomAccessStreamOnFile", "wstr", pdfPath, "uint", Read := 0, "ptr", &GUID, "ptr*", IRandomAccessStream)
CreateClass("Windows.Data.Pdf.PdfDocument", IPdfDocumentStatics := "{433A0B5F-C007-4788-90F2-08143D922599}", PdfDocumentStatics)
DllCall(NumGet(NumGet(PdfDocumentStatics+0)+8*A_PtrSize), "ptr", PdfDocumentStatics, "ptr", IRandomAccessStream, "ptr*", PdfDocument)   ; LoadFromStreamAsync
WaitForAsync(PdfDocument)
DllCall(NumGet(NumGet(PdfDocument+0)+7*A_PtrSize), "ptr", PdfDocument, "uint*", PageCount)   ; get_PageCount
ObjReleaseClose(IRandomAccessStream)
ObjReleaseClose(PdfDocumentStatics)
ObjReleaseClose(PdfDocument)
msgbox % PageCount
return



CreateClass(string, interface := "", ByRef Class := "")
{
   CreateHString(string, hString)
   if (interface = "")
      result := DllCall("Combase.dll\RoActivateInstance", "ptr", hString, "ptr*", Class, "uint")
   else
   {
      VarSetCapacity(GUID, 16)
      DllCall("ole32\CLSIDFromString", "wstr", interface, "ptr", &GUID)
      result := DllCall("Combase.dll\RoGetActivationFactory", "ptr", hString, "ptr", &GUID, "ptr*", Class, "uint")
   }
   if (result != 0)
   {
      if (result = 0x80004002)
         msgbox No such interface supported
      else if (result = 0x80040154)
         msgbox Class not registered
      else
         msgbox error: %result%
      ExitApp
   }
   DeleteHString(hString)
}

CreateHString(string, ByRef hString)
{
    DllCall("Combase.dll\WindowsCreateString", "wstr", string, "uint", StrLen(string), "ptr*", hString)
}

DeleteHString(hString)
{
   DllCall("Combase.dll\WindowsDeleteString", "ptr", hString)
}

WaitForAsync(ByRef Object)
{
   AsyncInfo := ComObjQuery(Object, IAsyncInfo := "{00000036-0000-0000-C000-000000000046}")
   loop
   {
      DllCall(NumGet(NumGet(AsyncInfo+0)+7*A_PtrSize), "ptr", AsyncInfo, "uint*", status)   ; IAsyncInfo.Status
      if (status != 0)
      {
         if (status != 1)
         {
            DllCall(NumGet(NumGet(AsyncInfo+0)+8*A_PtrSize), "ptr", AsyncInfo, "uint*", ErrorCode)   ; IAsyncInfo.ErrorCode
            msgbox AsyncInfo status error: %ErrorCode%
            ExitApp
         }
         ObjRelease(AsyncInfo)
         break
      }
      sleep 10
   }
   DllCall(NumGet(NumGet(Object+0)+8*A_PtrSize), "ptr", Object, "ptr*", ObjectResult)   ; GetResults
   ObjReleaseClose(Object)
   Object := ObjectResult
}

ObjReleaseClose(ByRef Object)
{
   if Object
   {
      if (Close := ComObjQuery(Object, IClosable := "{30D5A829-7FA4-4026-83BB-D75BAE4EA99E}"))
      {
         DllCall(NumGet(NumGet(Close+0)+6*A_PtrSize), "ptr", Close)   ; Close
         ObjRelease(Close)
      }
      ObjRelease(Object)
   }
   Object := ""
}
AMAZINGG! Tried on 558 page book worked :thumbup: :thumbup:
User avatar
JoeWinograd
Posts: 2213
Joined: 10 Feb 2014, 20:00
Location: U.S. Central Time Zone

Re: How to Get Number of Pages of Open Foxit PDF Document

Post by JoeWinograd » 04 May 2022, 22:27

malcev wrote:Windows requirements
Device family: Windows 10 (introduced in 10.0.10240.0)
Thanks!
BoBo
Posts: 6564
Joined: 13 May 2014, 17:15

Re: How to Get Number of Pages of Open Foxit PDF Document

Post by BoBo » 05 May 2022, 03:57

@malcev - :thumbup:

Q: is it possible to configure your script so that it returns all available property values of 'windows.data.pdf. ...' on the fly within an object?
... creating "Magic Malcev's PDF Tool" :shifty: :shh:
malcev
Posts: 1769
Joined: 12 Aug 2014, 12:37

Re: How to Get Number of Pages of Open Foxit PDF Document

Post by malcev » 05 May 2022, 16:00

I think it is possible, but I do not think that all this properties are really needed.
Also as I understand in future versions of autohotkey We can call uwp api natively.
viewtopic.php?f=81&t=95945
BoBo
Posts: 6564
Joined: 13 May 2014, 17:15

Re: How to Get Number of Pages of Open Foxit PDF Document

Post by BoBo » 05 May 2022, 16:34

@malcev - Thx for your statement. Much appreciated :thumbup:
Post Reply

Return to “Ask for Help (v1)”