Clickable links

Hello,
googling a little I found on MacScripter this code to make rtf clickable links:


from: http://macscripter.net/viewtopic.php?id=26498
property pTitle : "Make RTF link in clipboard"

set strURL to text returned of (display dialog "Enter a URL:" default answer "http://" with title pTitle)
set strTitle to text returned of (display dialog "Enter a title for the link:" default answer strURL with title pTitle)

if (strURL is not "") and (strTitle is not "") then
	set strHTML to quoted form of ("<font face=\"helvetica\"><a href=\"" & strURL & "\">" & strTitle & "</a></font>")
	do shell script "echo " & strHTML & " | textutil -format html -convert rtf -stdin -stdout | pbcopy -Prefer rtf"
end if

After running the script all you need is to paste the result in an rtf file.
I’m wondering if this could be used in more complex scripts like here: viewtopic.php?f=7&t=12747&p=59735&hilit=academic+workflow#p59735

The first line (‘from:…’) needs commenting.

As it happens, I wrote that snippet and posted it on the OmniGroup and MacScripter forums after experimenting with HTML to RTF conversion in DevonThink (for a Sente to DT script).

In the end I found it simpler, in DT, to bypass textutil, and create formatting and links in RTF by using DT’s built-in convert method.

i.e. start by using simple HTML markup to create the links and formatting required, and then convert the resulting HTML record to RTF.

set oHTML to create record with {type:html, source:strHTML, URL:strURL} in oLocn
set oNewRec to convert record oHTML to rich
delete record oHTML

An exception is HTML which embeds images, e.g. anything like:


<img src="data:image/tiff;base64, ..... >

This can be handled by using textutil to create an RTFD file with images inside the package. Thus something roughly like:

if blnHasImages then -- Create an RTFD file and load it as a record
	do shell script "echo " & quoted form of strHTML & " |  textutil -format html -convert rtfd -stdin -output ~/tmp.rtfd"
	set oNewRec to import "~/tmp.rtfd" to oLocn
	set URL of oNewRec to strURL
	-- do shell script "rm ~/tmp.rtfd"
else -- Use DT's own HTML to RTF conversion
	set oHTML to create record with {type:html, source:strHTML, URL:strURL} in oLocn
	set oNewRec to convert record oHTML to rich
	delete record oHTML
end if

Hi Houthakker,
didn’t notice you were the author of the code!!!
Any way, could you develop a bit more for dummies:

I’m in the following situation:

--set the source for html to some variables and some quoted text:
source should be: pdfText & <a href=pdfDevonThinkLinkWihtoutZero & pageForDevonThinkLink> "p. " & pageofNote </a>

where variables are: pdfText, pdfDevonthinkLingWithoutZero, pageForDevonThinkLink, pageofNote
and text is just "p." (the text of the link)

Thanks Nestor

You will find some illustrative code for doing this in a draft script which I have written for exporting Skim notes to Devonthink.

Brilliant man! That’s what I’ve been looking for since I started using DT for note-taking
:smiley:
Thanks for sharing!

Hi Houthakker,
I was able to make the html in the script I used previously and everything works but playing a little more with your script made me prefer it as it is more complete: now I’m trying (and bothering you) to modify a little aspect:
If I understand, your script get the path to the file, copies it in DT and then makes the link pointing to the new file. In my case the PDF is already in a DTGroup (indexed after the Bookends folder) and I’d like the link to point to that specific PDF. To achieve this the first Skim note is the DTItemLink so what I’m trying to achieve is set the link to the text of this first note and to the page. I made an attempt copying the old script to the one you wrote and get the useful variables from there but something doesn’t work as the link created becomes something like that:

I know I’ a pain in the…but If you could help me I would be very grateful as this is the tool I use 90% of time in DT :confused:
Here’s the code:

property pScriptTitle : "Skim Notes to Devonthink"
property pVer : ".002"

property pblnAvoidDuplication : true -- If true, will not create duplicate records (on a second run) in the target group
property pblnGetKeywordsAndTags : true
property pblnQuickTagsOnly : false -- If true, will only export user-assigned keywords

--  Copyright © 2011, Robin Trew - Houthakker
--  All rights reserved.
-- 	
-- 	Redistribution and use in source and binary forms, with or without modification, 
-- 	are permitted provided that the following conditions are met:
-- 	
-- 		- Redistributions of source code must retain the above copyright notice, 
-- 		  this list of conditions and the following disclaimer.
-- 		- Redistributions in binary form must reproduce the above copyright notice, 
-- 		  this list of conditions and the following disclaimer in the documentation 
-- 		  and/or other materials provided with the distribution.
-- 		
-- 	THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 
-- 	"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 
-- 	THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 
-- 	IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR 
-- 	ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-- 	 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
-- 	 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
-- 	 WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 
-- 	 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

--  RECENT CHANGE LOG

-- ver .001 First rough draft
-- ver .002 Minor corrections to growl message

----------------------------------------------------------------------------------------------------------
-- FUNCTION
-- Copies notes from Skim to the current folder of DEVONthink 2
-- For each document open in Skim, a correspondingly named folder is created 
-- within the currently selected DevonThink folder
-- For each note, an RTF record, with a URL link back to the PDF page is created in the folder

-- USAGE
-- Select a target DevonThink group
-- Open one or more PDFs in Skim
-- Run this script

----------------------------------------------------------------------------------------------------------

property pstrDefaultCSS : "
 .header {color:#888888;}
 .quote {color:#333366; margin-left: 30px; margin-right: 30px; line-height: 140%; text-align: justify;}
 .comment {color:#222222; margin-left: 30px; line-height: 140%;}
 .wptag {color:#888888;}
  p {font-family: monospace; font-size:12px}
  i {font-family: monospace; font-style:italic;}
  b {font-family: monospace; font-weight:bold;}
  u {font-family: monospace}
 "
property pstrActiveCSS : "" -- will be defined at run-time either by pstrDefaultCSS (above),
--									or by the contents of any external pCSSfile (see below).

--  An optional customized css file can be placed in the same folder as this script, 
-- to retain customized formatting if a later version of this script is downloaded.
-- The contents of this file (if its exists) will be used as an alternative to pstrDefaultCSS above,
-- and should define the same set of styles. 
-- If such a file is desired, it should have the name specified in the following property (pCSSFile):
property pCSSFile : "Skim2Devn.css"

property pblnPageFirst : true -- Edit this to give records names which *start* with a sortable page number like "(@055)" 

on run
	-- CHECK THAT DEVONTHINK IS RUNNING
	if application id "DNtp" is not running then
		tell application id "sevs"
			activate
			display dialog "DEVONthink Pro not running ..." & return & return & ¬
				"Start DEVONthink Pro, and run the script again." buttons {"OK"} with title pScriptTitle
		end tell
		return
	end if
	
	-- CHECK THAT SKIM IS RUNNING
	if not IsRunning("SKim") then
		tell application id "sevs"
			activate
			display dialog "Open a PDF in Skim, and run the script again." buttons {"OK"} with title pScriptTitle
		end tell
		return
	else
		tell application id "SKim"
			try
				set lstSkimDocs to documents
			on error
				tell application id "sevs"
					activate
					display dialog "No documents open in Skim ..." & return & return & ¬
						"Open a PDF in Skim, and run the script again." buttons {"OK"} with title pScriptTitle
				end tell
				return
			end try
		end tell
	end if
	
	-- FOR EACH PDF OPEN IN SKIM, CREATE A GROUP IN DT AND EXPORT THE NOTES TO THE GROUP
	GetSkimNotes(lstSkimDocs)
end run

on IsRunning(strCode) -- DNtp / SKim
	tell application id "com.apple.systemevents"
		(count of (processes where creator type = strCode)) > 0
	end tell
end IsRunning

on GetSkimNotes(lstPDF)
	-- QUIT IF NO PDFS SELECTED
	set lngPDF to length of lstPDF
	
	if lngPDF < 1 then
		--tell application id "com.devon-technologies.thinkpro2" to hide progress indicator
		tell application id "sevs"
			activate
			display dialog "No PDFs open in Skim" buttons {"OK"} with title pScriptTitle & " Ver " & pVer
		end tell
		return
	else
		-- Assuming that we are dealing with selected DT records, for the moment
		
		-- OR IF NO GROUP SELECTED, TO ROOT OF CURRENT DATABASE
		-- OR IF NO DATABASE SELECTED, TO GLOBAL INBOX
		set {oGroup, oWin} to my GetGroupWin()
		
		-- If records are to be formatted, see if there is a .css file (named as in pCSSFile above)
		-- in the same folder as this script.
		-- If not, fall back to the default CSS style
		set pstrActiveCSS to my GetCSS()
		if (length of pstrActiveCSS) = 0 then set pstrActiveCSS to pstrDefaultCSS
		
		tell application id "SKim"
			set pdfDevonThinkLink to (get text for note 1 of document 1) as string
			set pdfDevonThinkLinkWihtoutZero to replaceText(pdfDevonThinkLink, "=0", "=") of me
			set allMyNotes to notes of document 1
			set numberOfMyNotes to length of allMyNotes as integer
			repeat with currentNumber from 0 to numberOfMyNotes
				set pdfText to (get text for note currentNumber of document 1) as string
				set pageOfNote to the index of page of note currentNumber of document 1
				set pageForDevonThinkLink to pageOfNote - 1
			end repeat
		end tell
		
		tell application id "SKim"
			repeat with i from 1 to lngPDF
				tell (document i)
					set lngPages to count of pages
					set strPDFPath to POSIX path of (its file as string)
					set strPDFName to its name
					
					-- USE THE FIRST LINE OF TEXT AS THE DEFAULT NAME OF THE DOCUMENT
					set strDocName to ""
					set refParas to (a reference to (paragraphs of its text))
					repeat with oPara in refParas
						set strPara to oPara as string
						if length of strPara > 1 then
							set strDocName to text 1 thru -2 of strPara
							exit repeat
						end if
					end repeat
					
					-- FIND OR CREATE A DT FOLDER FOR THE NOTES OF THIS DOCUMENT
					tell application id "DNtp"
						if name of oGroup ≠ strDocName then
							if (count of parents of oGroup) is 0 then
								set oLocn to (create location strDocName in database of oGroup)
							else
								set oLocn to (create location (location of oGroup & "/" & name of oGroup & "/" & strDocName) in database of oGroup)
							end if
						else
							set oLocn to oGroup
						end if
						
						-- 	FIND OR CREATE A REFERENCE TO THIS DOCUMENT IN THAT FOLDER
						set lstDuplic to (children of oLocn where path is strPDFPath)
						if length of lstDuplic < 1 then
							set oPDFRec to indicate strPDFPath to oLocn
						else
							set oPDFRec to first item of lstDuplic
						end if
						
						set strUUID to reference URL of oPDFRec
					end tell
					
					
					tell (a reference to (notes))
						set lngNotes to count
						if lngNotes < 1 then return
						-- GET WHOLE PROPERTY LISTS TO ECONOMIZE ON APPLE EVENTS
						set {lstBounds, lstComment, lstIcon, lstModn, lstpage, lstRTF, lstText, lstType, lstUser} to ¬
							{bounds, extended text, icon, modification date, index of its page, its RTF, its text, its type, user name}
					end tell
					
					-- PROCESS NOTES ONE BY ONE
					set lngDigits to length of (lngPages as string)
					repeat with i from 1 to lngNotes
						
						-- GET THE INTERESTING PROPERTIES
						set lstXYWH to item i of lstBounds
						set strComment to item i of lstComment
						if strComment is missing value then set strComment to ""
						set strIcon to (item i of lstIcon) as string
						set dteModn to item i of lstModn
						set lngPage to (item i of lstpage)
						set eType to item i of lstType
						if eType = line then -- Line notes fail to return any data to their text method, so we read the rtf
							set refRTF to (a reference to item i of lstRTF)
							tell application id "com.apple.finder"
								set the clipboard to (refRTF as «class RTF »)
								set strQuotn to do shell script "pbpaste -Prefer rtf | textutil -format rtf -convert txt -stdin -stdout"
							end tell
						else
							set strQuotn to item i of lstText
						end if
						set varUser to (item i of lstUser)
						
						set strRecName to my GetFirstLine(strQuotn)
						
						-- BUILD A DT NOTE IN HTML WITH FIELDS MATCHING THE TYPE OF THE DT NOTE
						if eType = anchored note then
							
						else
							if eType = highlight note then
							else if eType = strike out note then
							else if eType = underline note then
							else
								--
							end if
						end if
						
						-- ASSEMBLE THE HTML
						if pblnPageFirst then
							set strPadded to my PadNum(lngPage, lngDigits)
							set strRecName to "(@" & strPadded & ")  " & strRecName
						end if
						
						set strURL to strUUID & "?page=" & (lngPage - 1) as string
						set strText to ("<p class=\"header\">Citazione:" & "<p class=\"quote\">" & strQuotn & "<p class=\"header\">" & ¬
							"Commento:" & "<p class=\"comment\">" & strComment & ¬
							"<p class=\"wptag\">Source:<p class=\"quote\"><a href=\"" & pdfDevonThinkLinkWihtoutZero & pageForDevonThinkLink & "\">" & strPDFName & "  Pag. " & pageOfNote as string) & "</a><p>" --MODIFIED 
						
						set strHTML to "
<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01//EN\" \"http://www.w3.org/TR/html4/strict.dtd\">
<html>
<head>
  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\">
  <meta http-equiv=\"Content-Style-Type\" content=\"text/css\">
  <title></title>
  <style type=\"text/css\">
" & pstrActiveCSS & "
  </style>
</head>
<body>" & strText & "</body>"
						
						-- CREATE THE FORMATTED RTF RECORD
						tell application id "DNtp"
							-- Use DT's own HTML to RTF conversion
							set oHTML to create record with {type:html, source:strHTML, URL:pdfDevonThinkLinkWihtoutZero & pageForDevonThinkLink} in oLocn --MODIFIED URL
							set oNewRec to convert record oHTML to rich
							delete record oHTML
							
							set name of oNewRec to strRecName
							
							if pblnAvoidDuplication then
								-- DISCARD NEW RECORD IF IT DUPLICATES ANOTHER IN THIS GROUP
								set lstDuplics to duplicates of oNewRec
								if lstDuplics ≠ {} then
									set strLocn to location of oNewRec
									repeat with oDuplic in lstDuplics
										if location of oDuplic = strLocn then
											delete record oNewRec
											exit repeat
										end if
									end repeat
								end if
							end if
						end tell
						
					end repeat
					
				end tell
			end repeat
		end tell
	end if
	
	-- NOTIFY COMPLETION BY GROWL
	Growl(oGroup, lngPDF)
	
	-- DISPLAY THE TARGET GROUP (OR DATABASE OR INBOX) IN DT2
	DT2ForeGround()
end GetSkimNotes

on GetFirstLine(strQuotn)
	repeat with oPara in paragraphs of strQuotn
		if length of oPara > 1 then return oPara as string
	end repeat
	return ""
end GetFirstLine

------------------------------------------------------------------------------------------------------------------------
--ADDED
on replaceText(thisText, searchString, replacementString)
	set AppleScript's text item delimiters to the searchString
	set the itemList to every text item of thisText
	set AppleScript's text item delimiters to the replacementString
	set thisText to the itemList as string
	set AppleScript's text item delimiters to {""}
	return thisText
end replaceText


on Growl(oGroup, lngRefs)
	-- If Growl is running, report the notes sent
	tell application id "com.devon-technologies.thinkpro2"
		set {strFolder, strDb} to {name, name of database} of oGroup
		set strTarget to "current folder " & strFolder & return & return & "of DEVONthink database \"" & strDb & "\""
	end tell
	if lngRefs > 0 then
		if lngRefs = 1 then
			set strRefInflection to " PDF"
		else
			set strRefInflection to " PDFs"
		end if
		tell application "System Events"
			set blnGrowlRunning to (count of (every process whose name is "GrowlHelperApp")) > 0
			if blnGrowlRunning then
				-- set strReport to text 1 thru ((length of strReport) - 2) of strReport
				set strReport to "Exported notes for " & (lngRefs as string) & strRefInflection & " to " & strTarget --& return & return & strReport
				tell application "GrowlHelperApp"
					register as application "houthakker scripts" all notifications {"Notes handled"} default notifications {"Notes handled"} icon of application "Sente"
					notify with name "Notes handled" title "Notes processed" application name "houthakker scripts" description strReport
				end tell
			end if
		end tell
	end if
end Growl

on FrontAppName()
	tell application id "com.apple.systemevents"
		set lstApps to name of (application processes where frontmost = true)
		if lstApps = {} then return ""
		first item of lstApps
	end tell
end FrontAppName

on DT2ForeGround()
	tell application id "com.devon-technologies.thinkpro2"
		hide progress indicator
		activate
	end tell
	
	tell application id "com.apple.finder"
		activate front window of application id "com.devon-technologies.thinkpro2"
	end tell
end DT2ForeGround

on GetGroupWin()
	tell application "DEVONthink Pro"
		-- CURRENT GROUP, IF THERE IS ONE
		set oGroup to missing value
		with timeout of 1 second
			try
				set oGroup to current group
			end try
		end timeout
		
		-- ELSE CURRENT DATABASE, IF THERE IS ONE
		try
			oGroup
		on error
			set oGroup to (root of database id 1)
			set oWin to open window for record oGroup
			return {oGroup, oWin}
		end try
		
		if oGroup is missing value then
			set oGroup to (root of database id 1)
			set oWin to open window for record oGroup
			return {oGroup, oWin}
		end if
		
		-- ENSURE THAT A WINDOW IS OPEN FOR THIS GROUP
		set {oDb, strID} to {database, id} of oGroup
		set lstWins to viewer windows where id of its root is strID and name of its root is name of oDb
		if length of lstWins < 1 then
			set oWin to open window for record oGroup
		else
			set oWin to first item of lstWins
		end if
		{oGroup, oWin}
	end tell
end GetGroupWin

on GetCSS() -- Try to get the CSS file from the same folder as the script. If not there, return empty string
	set strcssPath to my GetCSSPath(my pCSSFile)
	set strCSS to ""
	if strcssPath is not "" then
		try
			set strCSS to my readFile(strcssPath)
		end try
	end if
	strCSS
end GetCSS

on GetCSSPath(strcssFile)
	set strFolder to ScriptFolder() as string
	set strcssPath to POSIX path of (strFolder & strcssFile)
	tell application "Finder"
		if not (exists (strcssPath as POSIX file)) then return ""
	end tell
	strcssPath
end GetCSSPath

on ScriptFolder()
	set strPath to (path to me) as string
	set strDelim to text item delimiters
	set text item delimiters to ":"
	set lstParts to text items of strPath
	set lngLast to (length of lstParts) - 1
	set strFolderPath to (items 1 thru lngLast of lstParts) as string
	set text item delimiters to strDelim
	strFolderPath as alias
end ScriptFolder

on readFile(unixPath)
	set foo to (open for access (POSIX file unixPath))
	set txt to (read foo for (get eof foo))
	close access foo
	return txt
end readFile

on PadNum(lngNum, lngDigits)
	set strNum to lngNum as string
	set lngGap to (lngDigits - (length of strNum))
	repeat while lngGap > 0
		set strNum to "0" & strNum
		set lngGap to lngGap - 1
	end repeat
	strNum
end PadNum

I think you may find that Ver 11 now works the way you want.

Select a Devonthink folder which contains only one PDF - it can contain any number of non-PDF files - open that PDF in Skim, and run the script.

(The DT folder can have any name).

Thanks again
so if I replicate a pdf in a new folder, open in skim and run the script a new folder will be created with notes and another copy of the pdf?
If you need a beta-tester here I am :slight_smile:

The new folder containing the replicant which you have opened in Skim will receive the exported notes.

Fantastic!!! :mrgreen: