Jump to content

DEVONthink ↔ PDF Expert

Recommended Posts


Get either the DEVONthink 3 reference URL from PDF Expert, i.e. the x-devonthink-item page link for the active document opened in PDF Expert, the selection link if you have text selected, or the annotation link if your selection intersects an annotation and checking for annotations is enabled.

  • cmd ⌘ to open the PDF on the same page in DEVONthink 
  • .xdev to enable or disable opening the document in DEVONthink when using the hotkey

Expected result A x-devonthink-item://1D7FA99A-AAFF-4883-9853-F0666A650400?page=6
Expected result B x-devonthink-item://1D7FA99A-AAFF-4883-9853-F0666A650400?page=6&start=66&length=9&search=selection
Expected result C x-devonthink-item://1D7FA99A-AAFF-4883-9853-F0666A650400?page=6&annotation=Squiggly&x=212&y=406

Known issues


  • Fails with documents opened in PDF Expert split view


Download and Preview: https://github.com/zeitlings/alfred-workflows#14-devonthink--pdf-expert



Edited by zeitlings
support for deep linking
Link to comment

Now supports deep linking, which is new to DEVONthink 3.9, i.e. selection links and annotation links.

To get the full annotation link, your text selection just needs to overlap it. I anticipate poor performance for very large PDFs and in cases where the selected text occurs quite often in the document. For these cases, the annotation check can be disabled in the configuration.


Here is the AppleScript, if anyone is interested in it:


  Part of the DEVONthink ↔ PDF Expert workflow for Alfred v2.1.1

  Get the DEVONthink page link, selection link, or annotation link for the page viewed in PDF Expert
   - pete31: https://discourse.devontechnologies.com/t/script-open-devonthink-record-for-pdf-expert-tab/47243
   - Umlaut fix: https://stackoverflow.com/a/23226449/7625403

use AppleScript version "2.4"
use framework "PDFKit"
use scripting additions

property NSURL : a reference to current application's NSURL
property NSString : a reference to current application's NSString
property PDFDocument : a reference to current application's PDFDocument

on run argv
	set checkForAnnotations to (item 1 of argv as integer) as boolean
	tell application "System Events"
		set bufferedClipboard to the clipboard
		activate application "PDF Expert"
		delay 0.5
		tell process "PDF Expert" to keystroke "c" using {command down}
		delay 0.2
		set perhapsSelection to the clipboard as text
		tell process "PDF Expert" to keystroke "g" using {command down, option down} -- go to 
		tell application "System Events" to keystroke "c" using command down
		delay 0.5
		tell application "System Events" to key code 53 -- esc
		set theActiveLabel to the clipboard
			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
			return "Error with code " & (error_number as text) & "	" & (error_message as text)
		end try
	end tell
	set thePath to my recoveredPath(theFileName, PID)
	set theURL to NSURL's fileURLWithPath:(NSString's stringWithString:thePath)
	set pdf to PDFDocument's alloc()'s initWithURL:theURL
	set theCount to pdf's pageCount()
	set thePageIndex to -1
	set cur to 0
	repeat while cur < theCount
		set thePage to pdf's pageAtIndex:cur
		set theLabel to thePage's label()
		if theLabel ≠ missing value then
			if (theLabel as text) is theActiveLabel then
				set thePageIndex to cur
				exit repeat
			end if
		end if
		set cur to cur + 1
	end repeat
	if thePageIndex is equal to -1 then
		set the cliboard to bufferedClipboard
		return "Unable to recover Page Index"
	end if
	set pageLink to my referenceURL(thePath, thePageIndex)
		set thePage to pdf's pageAtIndex:thePageIndex
		-- ------------------------------------- --
		-- Check for annotation intersection --
		-- ------------------------------------- --
		if checkForAnnotations then
			set theSelections to pdf's findString:perhapsSelection withOptions:3
			repeat with thisSelection in theSelections
				if (thisSelection's pages() as list) contains thePage then
					set theBounds to (thisSelection's boundsForPage:thePage)
					set theOrigin to item 1 of theBounds
					set theSize to item 2 of theBounds
					set coorX to item 1 of theOrigin
					set coorY to item 2 of theOrigin
					set width to item 1 of theSize
					set height to item 2 of theSize
					repeat with thisAnnotation in thePage's annotations()
						set annotationBounds to thisAnnotation's |bounds|()
						set annotationOrigin to item 1 of annotationBounds
						set annotationSize to item 2 of annotationBounds
						set annoX to item 1 of annotationOrigin
						set annoY to item 2 of annotationOrigin
						set annoWidth to item 1 of annotationSize
						set annoHeight to item 2 of annotationSize
						set a to (coorX < (annoX + annoWidth))
						set b to ((coorX + width) > annoX)
						set c to (coorY < (annoY + annoHeight))
						set d to ((coorY + height) > annoY)
						set isIntersecting to a and b and c and d
						if isIntersecting then
							set theType to thisAnnotation's type() as text -- possibly nil
							if theType is missing value then
								set theType to "Highlight"
							end if
							set x to (round annoX rounding as taught in school) as text -- ....
							set y to (round annoY rounding as taught in school) as text
							set deepAnnotationLink to (pageLink as text) & "&annotation=" & theType & "&x=" & x & "&y=" & y
							set the clipboard to deepAnnotationLink
							return deepAnnotationLink
						end if
					end repeat
				end if
			end repeat
		end if
		-- ------------------------------ --
		-- Reconstruct page selection --
		-- ------------------------------ --
		set theText to thePage's |string|()
		set theRange to theText's rangeOfString:perhapsSelection
		if theRange's |length|() ≠ 0 then
			set theStart to theRange's location() as text
			set theLength to theRange's |length|() as text
			set theSelection to NSString's stringWithString:perhapsSelection
			set theSearchString to theSelection's stringByAddingPercentEncodingWithAllowedCharacters:(current application's NSCharacterSet's URLQueryAllowedCharacterSet())
			set deepSelectionLink to pageLink & "&start=" & theStart & "&length=" & theLength & "&search=" & theSearchString
			set the clipboard to deepSelectionLink
			return deepSelectionLink
		end if
		set the clipboard to pageLink
		return pageLink
	on error number -2753 -- not defined
		set the cliboard to bufferedClipboard
		return "Document not found in any open Database"
	end try
end run

on recoveredPath(theFileName, PID)
	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
			return thePath
		end if
	end repeat
end recoveredPath

on referenceURL(thePath, thePageIndex)
	tell application id "DNtp"
			set theDatabases to databases
			set theResults to {}
			repeat with thisDatabase in theDatabases
				set dbResults to lookup records with path thePath in thisDatabase
				set theResults to theResults & dbResults
			end repeat
			if theResults = {} then
				set theUUID to uuid of (item 1 of theResults)
				set referenceURL to "x-devonthink-item://" & theUUID & "?page=" & (thePageIndex as text)
				return referenceURL
			end if
		on error error_message number error_number
			return "Error with code " & (error_number as text) & "	" & (error_message as text)
		end try
	end tell
end referenceURL


Edited by zeitlings
Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Create New...