PDF Expert integration scripts

also completely new to this, just interested whether this script can be tweaked to link to specific pages within pdf files within pdf expert as well?

Welcome to the Hook Productivity Forum , @amisean . No that isn’t something we’ve looked into yet. If they add an API for it ( x-callback-url, ,AppleScript, CLI, whatever,) we’d gladly look into it.

awesome! thanks for the reply! going to drop them a message to get going with this👌🏾

2 Likes

I have been able to tweak the scripts to do this as below:

Open Item

set theURL to "$0"
set oldDelimiters to applescript's text item delimiters

set applescript's text item delimiters to "pdfexpert://file:/"
set baseURL to theURL's second text item

set applescript's text item delimiters to "&"
set urlComponents to baseURL's text items

set pageCode to urlComponents's second text item
set applescript's text item delimiters to "="
set pageNo to pageCode's second text item

set openScript to "open -a 'PDF Expert.app' 'file:///" & urlComponents's first item & "'"
do shell script openScript
delay 0.3

tell application "System Events"
	keystroke "g" using {command down, option down}
	delay 0.1
	keystroke pageNo
	keystroke return
end tell

set applescript's text item delimiters to oldDelimiters

Get Name

set oldDelimiters to applescript's text item delimiters
to getPageNo()
	try
		tell application "System Events"
			tell process "PDF Expert"
				set pageText to value of static text 1 of splitter group 1 of window 1
			end tell
		end tell
	
		set AppleScript's text item delimiters to {" of "}
		get pageText's first text item
	end try
	
end getPageNo


tell application "System Events"
	set fileName to name of first window of process "PDF Expert"
end tell

set fullName to fileName & " p. " & getPageNo()

set applescript's text item delimiters to oldDelimiters
get fullName

Get Address

set oldDelimiters to applescript's text item delimiters

to getPageNo()
	try
		tell application "System Events"
			tell process "PDF Expert"
				set pageText to value of static text 1 of splitter group 1 of window 1
			end tell
		end tell
	
		set AppleScript's text item delimiters to {" of "}
		get pageText's first text item
	end try
	
end getPageNo


set encodedPageURLFragment to "&p="&getPageNo()

tell application "System Events"
	tell process "PDF Expert"

		if enabled of menu item "Show in Finder" of menu 1 of menu bar item "File" of menu bar 1 then
			click menu item "Show in Finder" of menu 1 of menu bar item "File" of menu bar 1
			delay 0.1
			tell application "Finder"
				set theItems to selection
				set ext to name extension of item 1 of theItems
				if ext = "hook" or ext = "HOOK" then
					set filepath to item 1 of theItems as alias
					return read POSIX path of filepath
				end if
				set theURL to URL of item 1 of theItems
				close front window
				activate application "PDF Expert"
			end tell
		end if
	end tell
end tell

set finalURL to "pdfexpert:///"&theURL&encodedPageURLFragment


set applescript's text item delimiters to oldDelimiters
get finalURL

Notes:

  1. In PDF Expert’s preferences you will need to set Page Number indicator to Show always.
  2. The URL scheme is pdfexpert://. NB that Readdle has some url schemes albeit they don’t seem to work on the Mac; I chose pdfexpert to avoid conflicting with them.
  3. I strongly suspect the code could be improved, in particular in the Open Item script where I am sure I have not found the most elegant way to handle the URL!
1 Like

Hi Lawyerboy!

I’ve tried your apple script. Unfortunately they don’t seem to work for me. The saving link seems good, but I’m difficulty opening items. Any pointers?

I’m on Monterey btw

Worth checking you’ve set the url scheme?

Otherwise try putting a ‘display dialog openScript’ before the ‘do shell script’ line in Open Item, and seeing if you can execute what pops up in the terminal?

Hi, thanks for the timely reply

I’ve set the url scheme to pdfexpert:// in the text box. I’ve tried to put a display diaglog openScript before the line you mentioned, but nothing showed up

I had to use
set applescript's text item delimiters to "pdfexpert:///file:///"
to make it work. Seems there’re more forward slashes for some reason

I knew I wasn’t happy with the URL-handling code! Also wasn’t very happy with the ‘do shell script’ line with potentially untrusted input. I had a look at the PDFPen Pro scripts and was able to rewrite the script using that code, which I think is a bit neater:

Get Address

use framework "Foundation"
use scripting additions

property NSMutableCharacterSet : a reference to current application's NSMutableCharacterSet

on urlEncode(input)
	tell current application's NSString to set rawUrl to stringWithString_(input)
	set charset to NSMutableCharacterSet's URLQueryAllowedCharacterSet's mutableCopy
	charset's removeCharactersInString:"?"
	set theEncodedURL to rawUrl's stringByAddingPercentEncodingWithAllowedCharacters:charset
	return theEncodedURL as Unicode text
end urlEncode

on getPageNo()
	try
		tell application "System Events"
			tell process "PDF Expert"
				set pageText to value of static text 1 of splitter group 1 of window 1
			end tell
		end tell
		
		set AppleScript's text item delimiters to {" of "}
		return pageText's first text item
	on error
		return ""
	end try
end getPageNo

on getPDFExpertName()
	tell application "System Events"
		return name of first window of process "PDF Expert"
	end tell
end getPDFExpertName

on getPDFExpertFilePath()
	tell application "System Events"
		activate application "PDF Expert"
		tell process "PDF Expert" to set showInFinder to menu item "Show in Finder" of menu 1 of menu bar item "File" of menu bar 1
		set isEnabled to showInFinder's enabled
	end tell
	if isEnabled then
		tell application "System Events" to tell process "PDF Expert" to click showInFinder
		delay 0.1
		tell application "Finder"
			set sel to selection
			set filePath to (item 1 of sel) as string
			close front window
		end tell
	end if
	tell application "System Events" to activate application "PDF Expert"
	
	try
		return filePath
	on error
		return ""
	end try
end getPDFExpertFilePath

on getAddress()
	set filePath to getPDFExpertFilePath()
	set pageNo to getPageNo()
	set title to getPDFExpertName()
	
	if pageNo = "" then
		get "[" & title & "](pdfexpert://" & urlEncode(POSIX path of filePath) & ")"
	else
		get "[" & title & " p. " & pageNo & "](pdfexpert://" & urlEncode(POSIX path of filePath) & "#p=" & pageNo & ")"
	end if
end getAddress

set theLink to getAddress()

Open Item

use framework "Foundation"
use scripting additions

on urlDecode(input)
	tell current application's NSString to set urlString to stringWithString_(input)
	set theDecodedURL to urlString's stringByRemovingPercentEncoding -- 4 is NSUTF8StringEncoding
	return theDecodedURL as Unicode text
end urlDecode

on moveViewToPage(pageNo)
	
	tell application "PDF Expert" to activate
	
	tell application "System Events"
		tell application process "PDF Expert"
			
			set goToPage to menu item "Go to Page..." of menu "Go" of menu bar item "Go" of menu bar 1
			if goToPage's enabled is false then
				key code 53 --esc to remove focus from search box, bookmarks bar etc.
			end if
			if goToPage's enabled then
				click goToPage
				delay 0.1
				keystroke pageNo
				keystroke return
			end if
		end tell
	end tell
end moveViewToPage

on openItem(fullURL)
	
	set AppleScript's text item delimiters to "#"
	set urlPath to text item 1 of fullURL
	if (count of text items of fullURL) is greater than 1 then
		set fragment to text item 2 of fullURL
		set AppleScript's text item delimiters to "&"
		set kvs to text items of fragment
		repeat with kv in kvs
			set AppleScript's text item delimiters to "="
			set k to text item 1 of kv
			set v to text item 2 of kv
			if k = "p" then
				set p to v as number
			end if
		end repeat
	end if

	set fpath to urlDecode(urlPath)
	set text item delimiters to "/"
	set fn to text item -1 of fpath
	set len to length of fpath
	set fpath to text 12 through len of fpath
	
	if application "PDF Expert" is not running then
		tell application "PDF Expert" to open ""
		delay 2
	end if
	
	ignoring application responses
		tell application "PDF Expert" to open fpath
	end ignoring
	
	
	if p is not missing value then
		delay 0.8
		moveViewToPage(p)
	end if
end openItem

openItem("$0")
3 Likes

lol your new script stopped working again. Any idea how I can help to debug? For some reason it runs correctly in script editor but not correctly in hook

Seems have to change the identifier to pdfexpert rather than pdfexpert://

this does the trick for me. thank you so much!

1 Like

Ah, yes, although I think the new version is probably better than the old.

@LucB More generally I tried not specifying an identifier and returning a file:// link, but when I did that the link title was always the filename of the document even if the scripts returned a customised title (either as a separate Get Name script or by returning a markdown link from Get Address). Is that expected behaviour?

It would seem better to use the file:// scheme if possible because that would make the links cross-compatible to some degree with other PDF apps (e.g. the PDFPenPro deep links seem to encode a page number which PDF Expert can be made to open to). However at least for my purposes, I want the link title to have the page number so I can paste it into my notes!

There seems to be an issue on Hook’s side with specifying the name in this case. We will look into that.

I think you mean hook://file/ URLs? That’s what we use for the other PDF apps. We are looking into that.

Yes, this was what I meant!

Hello, Lawyerboy. I’ve tried your code but unfortunately it does not work for me. Is there any chance you might update or give me some advice? (I’m new around here

1 Like

I am working on another version but need to iron out a couple of bugs on my own setup first!

These are the scripts I’m currently using.

Get Address
use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

property NSURLComponents : a reference to current application's NSURLComponents
property NSURLQueryItem : a reference to current application's NSURLQueryItem
property NSMutableDictionary : a reference to current application's NSMutableDictionary
property NSFileManager : a reference to current application's NSFileManager's defaultManager
property NSURL : a reference to current application's NSURL

--Get Address

on getPDFExpertFileURL()
	local fileURL
	set fileURL to missing value
	tell application "System Events"
		activate application "PDF Expert"
		tell process "PDF Expert" to set showInFinder to menu item "Show in Finder" of menu 1 of menu bar item "File" of menu bar 1
		set isEnabled to showInFinder's enabled
	end tell
	if isEnabled then
		tell application "System Events" to tell process "PDF Expert" to click showInFinder
		delay 0.1
		tell application "Finder"
			set openWindows to id of every window
			set fileURL to URL of (selection as alias)
			close (get (every window whose id of it is not in openWindows))
		end tell
	end if
	tell application "System Events" to activate application "PDF Expert"
	return fileURL
end getPDFExpertFileURL

on getPDFExpertName()
	tell application "System Events" to return name of first window of process "PDF Expert"
end getPDFExpertName

on getPageDetails()
	set {pageNo, pageLabel} to {missing value, missing value}
	try --Get the details we need
		tell application "System Events" to tell process "PDF Expert"
			set pageNoText to value of static text 1 of splitter group 1 of window 1
			set pageLabelText to value of static text 2 of splitter group 1 of window 1
		end tell
	end try
	try --Set pageNo
		set AppleScript's text item delimiters to {" of "}
		set pageNo to pageNoText's first text item
	end try
	try --Only try setting pageLabel if the value may make sense
		if (pageLabelText starts with "Back to ") or (pageLabelText starts with "Go to") or (pageLabelText = "") or (pageLabelText is missing value) then error
		set pageLabel to pageLabelText
	on error
		set pageLabel to pageNo
	end try
	return {pageNo, pageLabel}
end getPageDetails

on getAddress()
	set urlBuilder to NSURLComponents's new's initWithString:getPDFExpertFileURL()
	set {urlBuilder's |scheme|, urlBuilder's |host|} to {"pdfexpert", ""}
	
	set title to getPDFExpertName()
	set {pageNo, pageLabel} to getPageDetails()
	
	if pageNo is not missing value then
		set title to title & " p. " & pageLabel
		set urlBuilder's queryItems to {NSURLQueryItem's queryItemWithName:"p" value:pageNo}
		--set urlBuilder's fragment to "page=" & pageNo --necessary for file:/ scheme
	end if
	
	return "[" & title & "](" & (urlBuilder's |string| as string) & ")"
end getAddress

getAddress()
Open Item

use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use scripting additions

property NSURLComponents : a reference to current application's NSURLComponents
property NSURLQueryItem : a reference to current application's NSURLQueryItem
property NSMutableDictionary : a reference to current application's NSMutableDictionary
property NSFileManager : a reference to current application's NSFileManager's defaultManager
property NSURL : a reference to current application's NSURL

on getPDFExpertName()
	tell application "System Events" to return name of first window of process "PDF Expert"
end getPDFExpertName

on replaceText(this_text, search_string, replacement_string)
	set str to current application's NSString's stringWithString:this_text
	return (str's stringByReplacingOccurrencesOfString:search_string withString:replacement_string) as string
end replaceText

on cleanURL(fullURL)
	--Clean up historic bad encodings
	if fullURL contains "%2520" then
		set fullURL to replaceText(fullURL, "%25", "%")
		set fullURL to replaceText(fullURL, "%23", "#")
		set fullURL to replaceText(fullURL, "&#40", "(")
		set fullURL to replaceText(fullURL, "&#41", ")")
	end if
	set fullURL to replaceText(fullURL, ".pdf&", ".pdf?")
	
	set urlObj to NSURLComponents's componentsWithString:fullURL
	
	--Handle Hook file URLs by stripping the '/file:/' component from the start of the path
	set AppleScript's text item delimiters to "file:/"
	get (urlObj's |path| as string)'s text items
	try
		if (urlObj's |path| as string)'s second text item is not "" then set urlObj's |path| to (urlObj's |path| as string)'s second text item
	end try
	
	--Handle old-style paths (which appear with a leading / ...)
	if ((urlObj's |path|) as string) contains ":Users:" then set urlObj's |path| to (POSIX path of (text 2 thru end of (urlObj's |path| as string)))
	
	--Optional: search for files where there is no match
	if not (NSFileManager's fileExistsAtPath:(urlObj's |path|)) then
		set newObj to findItemURL(urlObj)
		if newObj is not missing value then set urlObj's |path| to newObj's |path|
	end if
	
	return urlObj
end cleanURL

to findItemURL(urlObj)
	set {urlObj's |scheme|, urlObj's |host|} to {"file", ""}
	set filename to urlObj's |URL|'s lastPathComponent
	set searchResults to do shell script "mdfind -name " & quoted form of (filename as string)
	set searchResultURLs to {}
	repeat with searchResult in paragraphs of searchResults
		set end of searchResultURLs to (NSURL's fileURLWithPath:searchResult isDirectory:false relativeToURL:(urlObj's |URL|))
	end repeat
	set ourPathComponents to urlObj's |URL|'s pathComponents
	set leadingCandidate to {_URL:missing value, penalty:999999}
	repeat with searchResultURL in searchResultURLs
		set diff to ((searchResultURL's pathComponents)'s differenceFromArray:ourPathComponents)
		set penalty to (count of (diff's insertions)) + (count of (diff's removals))
		if penalty < leadingCandidate's penalty then set leadingCandidate to {_URL:searchResultURL, penalty:penalty}
	end repeat
	return contents of leadingCandidate's _URL
end findItemURL

on getKvs(fromURLComponents)
	if fromURLComponents's query() is missing value then
		set fromURLComponents's query to fromURLComponents's fragment()
		set fromURLComponents's fragment to ""
	end if
	set dict to NSMutableDictionary's new()
	try
		repeat with queryItem in fromURLComponents's queryItems
			(dict's setValue:(queryItem's value as string) forKey:(queryItem's |name|))
		end repeat
	end try
	return dict as record
end getKvs

on moveViewToPage(pageNo)
	set enabledViewElements to missing value
	
	tell application "PDF Expert" to activate
	tell application "System Events"
		tell application process "PDF Expert"
			if role description of value of attribute "AXFocusedUIElement" is "text field" then --Search bar taking focus?
				key code 53
			end if
			tell menu bar 1
				set goToPage to menu item "Go to Page..." of menu "Go"
				
				if goToPage's enabled is not true then
					--Side bar taking focus?
					set enabledViewElements to every UI element of menu "View" whose (value of attribute "AXMenuItemMarkChar" of it) is equal to "✓"
					keystroke "5" using {command down, option down} --faster than click UI element "No Left Panel" of menu "View"
				end if
				
				click goToPage
				delay 0.05
				keystroke pageNo
				keystroke return
				
				--Restore side bar if necessary
				if enabledViewElements is not missing value then
					repeat with element in enabledViewElements
						if element's name is in {"Bookmarks", "Outline", "Annotation Summary", "Thumbnails Panel"} then
							click element
							exit repeat
						end if
					end repeat
				end if
			end tell
		end tell
	end tell
end moveViewToPage

on openItem(fullURL)
	set urlObj to cleanURL(fullURL)
	
	--Test if file exists
	if not (NSFileManager's fileExistsAtPath:(urlObj's |path|)) then
		display dialog "Unable to find file for URL " & fullURL & " looking at path " & urlObj's |path| with title "Hook PDF Expert Integration" buttons {"OK"} default button 1 cancel button 1 giving up after 2
		return urlObj's |path|
	end if
	
	--Convert URL to file URL
	set {urlObj's |scheme|, urlObj's |host|} to {"file", ""}
	set furl to urlObj's |URL| as «class furl»
	
	tell application "PDF Expert" to open location furl
	
	-- Get page number
	set pageRefs to getKvs(urlObj) & {p:missing value, page:missing value} --Neuberg p. 241
	if pageRefs's p is missing value then set pageRefs's p to pageRefs's page
	set p to pageRefs's p
	if p is missing value then return
	--display dialog "Go to page " & p giving up after 1
	
	-- Get file name
	tell application "Finder" to set pdfName to displayed name of (info for furl)
	set AppleScript's text item delimiters to ".pdf"
	set pdfName to pdfName's first text item
	
	-- Move to page
	set counter to 30
	repeat while counter is greater than 0
		if getPDFExpertName() contains pdfName then
			moveViewToPage(p)
			return
		else
			delay 0.1
		end if
		set counter to counter - 1
	end repeat
end openItem

openItem("$0")

5 Likes

Thank you for the script!
I can’t seem to get it to work, can I ask what URL scheme you are using now? Thanks!

1 Like

Hi Steve, sorry to bother you, but is this script still working? I copied it into the hook but I always get “No item or link” feedback, I would like to know if you could help me. Thank you very much! (PS, What is the url scheme you entered? )