X-devonthink-item link from PDF Expert (with page number)

I frequently find myself copying links to specific pages of documents to include them in my notes. This way I can easily access the relevant passages on later review.

Though, I prefer to annotate with PDF Expert. This often entails finding the pdf in dt3, going to the corresponding page and copying the item link in order to get the referencing URL. I’d like to evade those steps.

While working on a solution I came across this thread: Script: Open DEVONthink record for PDF Expert tab. Thanks @pete31 for this! Makes for a feasible result. Here’s my derivative script:

    -- Get the x-devonthink-item URL for the active document's page in PDF Expert
    --
    -- Via "Open DEVONthink record for current PDF Expert tab"
    -- https://discourse.devontechnologies.com/t/script-open-devonthink-record-for-pdf-expert-tab/47243
    -- solution for umlauts ("iconv -t UTF8-MAC") found here: https://stackoverflow.com/questions/23219482/bash-ps-grep-for-process-with-umlaut-os-x/23226449#23226449

    tell application "System Events"
    	activate application "PDF Expert"
    	delay 1
    	tell process "PDF Expert"
    		click menu item "Gehe zu Seite..." of menu 1 of menu bar item "Gehe zu" of menu bar 1
    	end tell
    	tell application "System Events" to keystroke "c" using {command down}
    	delay 1
    	set pageNum to the clipboard
    	tell application "System Events"
    		key code 53
    		-- esc
    	end tell
    	delay 1
    	try
    		tell application process "PDF Expert"
    			tell window 1
    				set theFileName to value of attribute "AXTitle" & ".pdf"
    				if theFileName starts with "* " then set theFileName to characters 3 thru -1 in theFileName as string
    			end tell
    			set PID to unix id
    		end tell
    	on error error_message number error_number
    		if the error_number is not -128 then
    			display alert "System Events" & space & error_number message error_message as warning
    			return
    		end if
    	end try
    end tell

    set theOpenFiles to (do shell script "lsof -p " & PID)
    set convertedOutput to (do shell script "iconv -t UTF8-MAC <<<$" & quoted form of theOpenFiles)
    set theLines to paragraphs of convertedOutput

    repeat with thisLine in theLines
    	if thisLine contains theFileName then
    		set thePath to characters (offset of "/" in thisLine) thru -1 in thisLine as string
    		exit repeat
    	end if
    end repeat

    tell application id "DNtp"
    	try
    		set theDatabases to databases
    		set theResults to {}
    		repeat with thisDatabase in theDatabases
    			set thisDatabasesResults to lookup records with path thePath in thisDatabase
    			set theResults to theResults & thisDatabasesResults
    		end repeat
    		if theResults = {} then
    			display notification "Dokumentpfad nicht in geöffneten Datenbanken enthalten!" with title "Lookup DEVONthink"
    			return
    		else
    			set theUUID to uuid of (item 1 of theResults)
    			set callback to "x-devonthink-item://" & theUUID & "?page=" & pageNum
    			set the clipboard to callback
    			display notification callback with title ("x-devonthink-item URL")
    		end if
    		
    	on error error_message number error_number
    		if the error_number is not -128 then
    			display alert "DEVONthink Pro" & space & error_number message error_message as warning
    			return
    		end if
    	end try
    end tell
2 Likes

Alfred Workflow:

1 Like

I like the idea, but when you get the page with this method via PDF Expert, sometimes you’ll get the printed page number, but not the actual page DT3 needs for the link.

Just now I tested it on a PDF. The printed page number (and the number displayed by PDF Expert and DT3) was 47. But when I use the copy page URL command in DT3, the page number was 57. This because the pdf is not computing the pages numbered i to x.

This here will work with Adobe Acrobat and with Skim.


set thePage to ""
set thePath to ""
set front_app to (path to frontmost application as Unicode text)


if front_app contains "Acrobat" then
	tell application "Adobe Acrobat"
		--    activate
		set theDoc to item 1 of PDF Windows
		set thePage to get page number of theDoc
		set thePage to thePage - 1
		set thePath to file alias of front document
		set thePath to POSIX path of thePath
		
	end tell
end if
if front_app contains "Skim" then
	tell application "Skim"
		--    activate
		set theDoc to front document
		set thePage to get index for current page of theDoc
		set thePage to thePage - 1
		set thePath to the path of theDoc
	end tell
end if
if thePage is not "" then
	tell application id "DNtp"
		try
			set theDatabases to databases
			set theResults to {}
			repeat with thisDatabase in theDatabases
				set thisDatabasesResults to lookup records with path thePath in thisDatabase
				set theResults to theResults & thisDatabasesResults
			end repeat
			if theResults = {} then
				display notification "Document could not be found in open databases" with title "Lookup DEVONthink"
				return
			else
				set theUUID to uuid of (item 1 of theResults)
				set callback to "x-devonthink-item://" & theUUID & "?page=" & thePage
				set the clipboard to callback
				display notification callback with title ("x-devonthink-item URL")
			end if
			
		on error error_message number error_number
			if the error_number is not -128 then
				display alert "DEVONthink Pro" & space & error_number message error_message as warning
				return
			end if
		end try
	end tell
end if

1 Like

Hey Bernardo,

thanks for pointing that out! While for most cases that won’t cause a problem for me, it would be nice to have a workaround. Just in case.

I observed something: Assume you have a document with pages i-v followed by pages 1-10: A pair of two ranges <i-v,1-10>, say the curated page count, at 15 pages total, say the actual page count.

If I run the script on the document viewed in PDF Expert on page 6 (of the curated page count), the reference URL will bring me to page 1 (curated) in dt3 or page 6 (actual) respectively, just as you pointed out (the desired position in the actual page count’s range is 11). Now the observation: If I click on the current page/ page count indicator[^1] in dt3 and enter ‘6’, it brings me to the correct (curated) page. Dt3 handles the alphanumerical mix just fine. If I’m in the range <i-v> the indicator also displays, e.g. “iv”.

So, the record must have the relevant information as one of its properties, right? If we had access to it, then there was a workaround. What we can access however, as far as I can tell, is just this:

page count (integer, r/o) : The page count of a record. Currently only supported by PDF documents.

With information about both sides of the pair we could simply add the left range to the current page position (if >= 1) and had the correct reference URL. Could this be done, developers?

[^1]: Bildschirmfoto 2020-05-14 um 21.05.33

I found a different workaround:

-- Get the x-devonthink-item URL for the active document's page in PDF Expert v.2
--
-- Via "Open DEVONthink record for current PDF Expert tab"
-- https://discourse.devontechnologies.com/t/script-open-devonthink-record-for-pdf-expert-tab/47243
-- solution for umlauts ("iconv -t UTF8-MAC") found here: https://stackoverflow.com/questions/23219482/bash-ps-grep-for-process-with-umlaut-os-x/23226449#23226449

tell application "System Events"
	activate application "PDF Expert"
	delay 1
	tell process "PDF Expert"
		click menu item "Gehe zu Seite..." of menu 1 of menu bar item "Gehe zu" of menu bar 1
	end tell
	tell application "System Events" to keystroke "c" using {command down}
	delay 1
	set pageNum to the clipboard
	tell application "System Events"
		key code 53
		-- esc
	end tell
	delay 1
	
	tell process "PDF Expert"
		click menu item "Letzte Seite" of menu 1 of menu bar item "Gehe zu" of menu bar 1
		delay 1
		click menu item "Gehe zu Seite..." of menu 1 of menu bar item "Gehe zu" of menu bar 1
	end tell
	tell application "System Events" to keystroke "c" using {command down}
	delay 1
	set lastPageCurated to the clipboard
	set the clipboard to pageNum
	tell application "System Events" to keystroke "v" using {command down}
	tell application "System Events"
		key code 36
		-- esc
	end tell
	delay 1
	
	try
		tell application process "PDF Expert"
			tell window 1
				set theFileName to value of attribute "AXTitle" & ".pdf"
				if theFileName starts with "* " then set theFileName to characters 3 thru -1 in theFileName as string
			end tell
			set PID to unix id
		end tell
	on error error_message number error_number
		if the error_number is not -128 then
			display alert "System Events" & space & error_number message error_message as warning
			return
		end if
	end try
end tell

set theOpenFiles to (do shell script "lsof -p " & PID)
set convertedOutput to (do shell script "iconv -t UTF8-MAC <<<$" & quoted form of theOpenFiles)
set theLines to paragraphs of convertedOutput

repeat with thisLine in theLines
	if thisLine contains theFileName then
		set thePath to characters (offset of "/" in thisLine) thru -1 in thisLine as string
		exit repeat
	end if
end repeat

tell application id "DNtp"
	try
		set theDatabases to databases
		set theResults to {}
		repeat with thisDatabase in theDatabases
			set thisDatabasesResults to lookup records with path thePath in thisDatabase
			set theResults to theResults & thisDatabasesResults
		end repeat
		if theResults = {} then
			display notification "Dokumentpfad nicht in geöffneten Datenbanken enthalten!" with title "Lookup DEVONthink"
			return
		else
			set theUUID to uuid of (item 1 of theResults)
			set totalPageCount to page count of (item 1 of theResults)
			
			if lastPageCurated is not equal to totalPageCount then
				set alphaNum to (totalPageCount - lastPageCurated)
				set pageNum to (pageNum + alphaNum)
			end if
			
			set callback to "x-devonthink-item://" & theUUID & "?page=" & (pageNum - 1)
			set the clipboard to callback
			display notification callback with title ("URL copied to clipboard:")
		end if
		
	on error error_message number error_number
		if the error_number is not -128 then
			display alert "DEVONthink Pro" & space & error_number message error_message as warning
			return
		end if
	end try
end tell

Btw. the x-devonthink-item URL is zero-based on purpose, right (and will always stay that way)?

1 Like

Yes, the page parameter in an item link is zero-based.

Updated language agnostic version:

-- Get the x-devonthink-item:// URL for the active document's page viewed in PDF Expert
-- Credit: https://discourse.devontechnologies.com/t/script-open-devonthink-record-for-pdf-expert-tab/47243
-- Solution for umlauts ("iconv -t UTF8-MAC"): https://stackoverflow.com/a/23226449/7625403

tell application "System Events"
	activate application "PDF Expert"
	delay 0.5
	tell process "PDF Expert" to keystroke "g" using {command down, option down} -- open go to page modal
	tell application "System Events" to keystroke "c" using command down
	delay 0.5
	set pageNum to the clipboard
	tell application "System Events" to key code 53 -- esc
	tell application "System Events" to key code 125 using command down -- ⌘↓
	tell process "PDF Expert" to keystroke "g" using {command down, option down} -- open go to page modal
	tell application "System Events" to keystroke "c" using command down -- copy the page number		
	delay 0.5
	set lastPageCurated to the clipboard
	set the clipboard to pageNum
	tell application "System Events" to keystroke "v" using command down -- go back to the original page
	tell application "System Events" to key code 36 -- return / enter
	delay 0.5
	
	try
		tell application process "PDF Expert"
			tell window 1
				set theFileName to value of attribute "AXTitle" & ".pdf"
				if theFileName starts with "* " then set theFileName to characters 3 thru -1 in theFileName as string
			end tell
			set PID to unix id
		end tell
	on error error_message number error_number
		if the error_number is not -128 then
			display alert "System Events" & space & error_number message error_message as warning
			return
		end if
	end try
end tell

set theOpenFiles to (do shell script "lsof -p " & PID)
set convertedOutput to (do shell script "iconv -t UTF8-MAC <<<$" & quoted form of theOpenFiles)
set theLines to paragraphs of convertedOutput

repeat with thisLine in theLines
	if thisLine contains theFileName then
		set thePath to characters (offset of "/" in thisLine) thru -1 in thisLine as string
		exit repeat
	end if
end repeat

tell application id "DNtp"
	try
		set theDatabases to databases
		set theResults to {}
		repeat with thisDatabase in theDatabases
			set thisDatabasesResults to lookup records with path thePath in thisDatabase
			set theResults to theResults & thisDatabasesResults
		end repeat
		if theResults = {} then
			display notification "Document path not found in open databases!" with title "Lookup DEVONthink"
			return
		else
			set theUUID to uuid of (item 1 of theResults)
			set totalPageCount to page count of (item 1 of theResults)
			
			if lastPageCurated is not equal to totalPageCount then
				set alphaNum to (totalPageCount - lastPageCurated)
				set pageNum to (pageNum + alphaNum)
			end if
			
			set callback to "x-devonthink-item://" & theUUID & "?page=" & (pageNum - 1)
			set the clipboard to callback
		end if
		
	on error error_message number error_number
		if the error_number is not -128 then
			display alert "DEVONthink 3" & space & error_number message error_message as warning
			return
		end if
	end try
end tell