content: ﻿property pScriptTitle : "Skim Notes to Devonthink"
  property pVer : ".022"
  
  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
  
  -- EDIT THE FOLLOWING VALUE TO false IF YOU WISH TO SUPPRESS INDEXING AND LINKING OF THE PDF
  property pblnIndexPDF : true
  
  --  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
  -- ver .003	pShowYPosn and pShowXPosn switches (below) add sortable note coordinates to page number
  -- ver .004	added minor details at foot of note: modification date/time, note type, note position, icon type, user (should be easy to comment out or adjust)
  --			also added new CSS style for these details: .detail {color:#888888; margin-left: 30px; font-family: "lucida grande", "sans-serif"; font-size:8px;}
  -- ver .005  The text of strikethrough notes tagged as <struck>, and given, in the default CSS, the format: {text-decoration:line-through}
  -- ver .006 Corrects the order of the sortable vertical position numbers (it turns out the that 0 is at bottom left)
  -- ver .007 Textless notes (e.g. arrows without attached text) are no longer exported
  -- ver .008	Three styles added to CSS to allow for special styling of Skim 'srike out notes', 'highlight notes', and 'underline notes'
  --			the CSS definitions of struck | highlit | underscored (below) can be adjusted,  
  --			or simply reduced to {} to avoid application of special style
  -- ver .009 Corrected vertical position numbers in source details (to assume y orgin at bottom)
  -- ver .010 Will not create a new group if the PDF open in Skim is the only PDF in the group.
  -- ver .018 Changes the name of the css that it written/read (for compatibility with other scripts)
  -- 		(If you have customized a css file for this script you will need to rename it or copy it to DevnNote.css)
  -- ver 0.22 Temporarily suppressed modification date recording, as the latest Skim version has difficulty with this
  
  ----------------------------------------------------------------------------------------------------------
  -- 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%;}
   .comment {color:#222222; margin-left: 30px; line-height: 140%;}
   .wptag {color:#888888;}
   .detail {color:#888888; margin-left: 30px; font-family: \"lucida grande\", \"sans-serif\"; font-size:8px;}
   struck {text-decoration:line-through}
   highlit {background-color:#FEFC97}
   underscored {text-decoration:underline}
    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).
  
  -- NOTE: CUSTOMIZING NOTE STYLE WITH EXTERNAL CSS FILE
  -- (TO AVOID RE-EDITING AFTER SCRIPT UPDATES)
  --  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 : "DevnNote.css"
  
  property pblnPageFirst : true -- Edit this to give records names which start with a sortable page number like "(@055)" 
  property pShowYPosn : true -- Edit to follow page number with sortable vertical coordinates of note on page (sort top to bottom)
  property pShowXPosn : false -- ditto for horizontal coordinates (sort by left to right posn)
  
  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
  		-- AND THAT AT LEAST ONE PDF IS OPEN
  		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
  	
  	set {oRootGroup, oRootWin} to my GetGroupWin()
  	
  	-- FOR EACH PDF OPEN IN SKIM, CREATE A GROUP IN DT AND EXPORT THE NOTES TO THE GROUP
  	GetSkimNotes(lstSkimDocs, oRootGroup)
  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, oRootGroup)
  	-- 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
  		
  		-- 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"
  			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
  					
  					-- WE WANT A FOLDER WHICH IS EITHER:
  					--	1. EMPTY
  					-- 2.OR CONTAINS ONLY ONE PDF RECORD, WHICH CONTAINS OR INDEXES THE CURRENT PDF
  					
  					-- FIND OR CREATE A DT FOLDER FOR THE NOTES OF THIS DOCUMENT
  					tell application id "DNtp"
  						set oLocn to oRootGroup
  						if (count of children of oRootGroup) > 0 then
  							set lstGroupPDF to (children of oRootGroup where type = PDF document)
  							set lngPDF to count of lstGroupPDF
  							if lngPDF > 0 then
  								if (lngPDF > 1) or ((path of first item of lstGroupPDF) ≠ strPDFPath) then ¬
  									set oLocn to (create location (location of oRootGroup & "/" & name of oRootGroup & "/" & strDocName) in database of oRootGroup)
  							end if
  						end if
  						
  						if pblnIndexPDF then
  							-- 	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 if
  					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 refPage to (a reference to its page)
  						set {lstNoteBounds, lstComment, lstIcon, lstpage, lstPageBounds, lstRTF, lstText, lstType, lstUser} to ¬
  							{bounds, extended text, icon, index of refPage, bounds of refPage, its RTF, its text, its type, user name}
  					end tell
  					
  					-- IF WE ARE GOING TO ADD SORTABLE NOTE COORDINATES TO THE PAGE NUMBER, 
  					-- FIND THE MAXIMA SO THAT WE CAN LEFT-PAD THE DIGIT STRINGS WITH ZEROS, WHERE NEEDED,
  					-- TO ENSURE PROPER SORTING
  					if (pShowYPosn or pShowXPosn) then
  						set {lngYMax, lngXMax} to {0, 0}
  						repeat with i from 1 to lngNotes
  							set {lngX, lngY} to items 1 thru 2 of (item i of lstNoteBounds)
  							if lngX > lngXMax then set lngXMax to lngX
  							if lngY > lngYMax then set lngYMax to lngY
  						end repeat
  						
  						set lngYdigits to length of (lngYMax as string)
  						set lngXdigits to length of (lngXMax as string)
  					end if
  					
  					-- PROCESS NOTES ONE BY ONE
  					set lngDigits to length of (lngPages as string)
  					repeat with i from 1 to lngNotes
  						
  						-- CHECK THAT THERE IS AT LEAST A QUOTATION OR A COMMENT
  						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 strComment to item i of lstComment
  						if strComment is missing value then set strComment to ""
  						
  						-- ONLY EXPORT A NOTE IF THERE IS SOME TEXT
  						if (length of strQuotn > 0) or (length of strComment > 0) then
  							set strType to eType as string
  							
  							-- GET THE OTHER INTERESTING PROPERTIES
  							set lstXYWH to item i of lstNoteBounds
  							
  							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 lngPageHeight to (item 2 of (item i of lstPageBounds))
  							
  							set varUser to (item i of lstUser)
  							if varUser = missing value then set varUser to ""
  							
  							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
  									set strQuotn to "<highlit>" & strQuotn & "<highlit/>"
  								else if eType = strike out note then
  									set strQuotn to "<struck>" & strQuotn & "<struck/>"
  								else if eType = underline note then
  									set strQuotn to "<underscored>" & strQuotn & "</underscored>"
  								end if
  							end if
  							
  							-- ASSEMBLE THE HTML
  							set lngTopY to missing value
  							if pblnPageFirst then
  								set strPrefix to "(@" & my PadNum(lngPage, lngDigits)
  								if pShowYPosn then
  									set lngTopY to (lngPageHeight - (item 2 of lstXYWH))
  									set strPrefix to (strPrefix & tab & my PadNum(lngTopY, lngYdigits) & "y")
  								end if
  								
  								if pShowXPosn then set strPrefix to (strPrefix & tab & my PadNum(item 1 of lstXYWH, lngXdigits) & "x")
  								
  								set strPrefix to strPrefix & ")  "
  								set strRecName to strPrefix & tab & strRecName
  							end if
  							
  							if pblnIndexPDF then
  								set strURL to strUUID & "?page=" & (lngPage - 1) as string
  								set strPDFLink to ("<a href=\"" & strURL & "\">" & strPDFName & "  Page " & lngPage as string) & "</a>"
  							else
  								set strURL to ""
  								set strPDFLink to ("<a href=\"" & strPDFPath & "\">" & strPDFName & "  Page " & lngPage as string) & "</a>"
  							end if
  							
  							set strText to "<p class=\"header\">Quotation:" & "<p class=\"quote\">" & strQuotn & "<p class=\"header\">" & ¬
  								"Comment:" & "<p class=\"comment\">" & strComment & ¬
  								"<p class=\"wptag\">Source:<p class=\"quote\">" & strPDFLink & "<p class=\"detail\">"
  							
  							-- ADD MINOR NOTE DETAILS (comment out and re-order as appropriate)
  							-- 							set strText to strText & (short date string of dteModn) & tab
  							-- 							set strText to strText & (time string of dteModn) & "<br>"
  							set lngTopY to (lngPageHeight - (item 2 of lstXYWH))
  							set strText to strText & strType & tab & "(" & (item 1 of lstXYWH) & "x " & lngTopY & "y)" & "<br>"
  							set strText to strText & strIcon & "<br>"
  							if varUser ≠ "" then set strText to strText & varUser & "<br>"
  							
  							
  							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:strURL} in oLocn
  								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 if
  					end repeat
  				end tell
  			end repeat
  		end tell
  	end if
  	-- NOTIFY COMPLETION BY GROWL
  	Growl(oLocn, 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
  
  ------------------------------------------------------------------------------------------------------------------------
  
  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
  
  
  