Created
October 18, 2013 19:05
-
-
Save fractaledmind/7046486 to your computer and use it in GitHub Desktop.
This script will (as the title suggests) export all of you Skim notes directly to Evernote with hyperlinks to redirect you to the item in your DEVONthink database. REQUIRED PROGRAMS:
- Skim (pdf viewer and annotator)
- Evernote (cloud based note app)
- DEVONthink (database program)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(* EXPORT ALL SKIM NOTES TO EVERNOTE WITH DEVONTHINK HYPERLINKS | |
-- Stephen Margheim | |
-- 10/18/13 | |
-- open source | |
REQUIRED PROGRAMS: | |
- Skim (pdf viewer and annotator) | |
- Evernote (cloud based note app) | |
- DEVONthink (database program) | |
This script will (as the title suggests) export all of you Skim notes directly to Evernote with hyperlinks to redirect you to the item in your DEVONthink database. It integrates the GENERATE TOP 3 NOTES WITH SYSTEM URL script that will put 3 notes at the top of the PDF with linking information. This means your PDF workflow is now contained in a single script. | |
When launched, the script will ask you a series of questions: | |
* if the current PDF is saved where you want it to finally reside, | |
* what the relation of the Printed page number is relative to Skim's indexed page number, | |
* if the PDF is a Primary Source or a Secondary Source. | |
Your choices will determine (1) the file path used in the custom URL, (2) the key to generating the hyperlinked printed page numbers, (3) the text values for the 6 different highlight colors. | |
Possible alterations include (1) exporting to a web-based site (instead of Evernote), (2) changing the text translation for the various highlight colors (down in the color2text Handler) | |
*) | |
(* PROPERTIES *) | |
property LF : (ASCII character 10) | |
property LF1 : (ASCII character 32) & (ASCII character 32) & (ASCII character 10) | |
property tid : AppleScript's text item delimiters | |
property theHome : path to home folder as string | |
(* THE SCRIPT *) | |
tell application "Skim" | |
(* /// | |
PRE-PROCESSING: | |
Check if annotated on iOS. If yes, convert annotations | |
/// *) | |
tell document 1 | |
if every note = {} then convert notes | |
end tell | |
(* /// | |
PART 1: | |
Dialog Boxes | |
/// *) | |
--get partial file path of PDF | |
set theFile to file of document 1 as string | |
set theFilePath to my replaceString(theFile, theHome, "") | |
set theFilePath_view to my replaceString(theFilePath, ":", "/") | |
--save PDF file in proper place | |
set checkFilePath to display dialog "Is the PDF saved in its final resting place?" & LF & LF & theFilePath_view with title "PDF Annotation Export" buttons {"Yes", "No"} default button {"Yes"} | |
if button returned of checkFilePath is "No" then | |
set theFile to name of document 1 as string | |
set theFolder to choose folder default location (theHome & "Desktop:" as alias) with prompt "Whither save the PDF?" | |
set theFolder to theFolder as string | |
--save as flattened pdf to be viewed by iOS app | |
save front document as "PDF" in (theFolder & theFile) | |
close document 1 | |
open (theFolder & theFile) | |
set theFile to file of document 1 as string | |
end if | |
--get relation of printed page number to Skim's indexed page number | |
set pageRelation to text returned of (display dialog ¬ | |
"Subtract printed page number from Skim's indexed page number" with title "Relation of Printed page # to indexed page #" default answer "") as number | |
--determine type of PDF | |
set theTextStatus to display dialog "Is the PDF a Primary or Secondary text?" buttons {"Primary", "Secondary"} default button {"Secondary"} | |
(* /// | |
PART 2: | |
Generate Top 3 Notes with relevant Meta-Data | |
/// *) | |
--URL encode partial file path | |
set pdfPath to my encode_text(theFilePath, true, false) | |
set pdfPath to my replaceString(pdfPath, ":", "/") | |
--create DEVONthink URL | |
tell application "DEVONthink Pro" | |
try | |
set pdfFile to the content record of the think window 1 | |
set theURL to the reference URL of pdfFile | |
set DevonThinkLink to theURL & "?page=" | |
on error | |
set theGroup to display group selector | |
set theItems to name of every child of theGroup | |
set theTypes to type of every child of theGroup | |
set theURLs to reference URL of every child of theGroup | |
set theItem to choose from list theItems with title "Which DT item?" | |
set theItem to theItem as string | |
set thePos to my list_positions(theItems, theItem, true) as number | |
set theType to item thePos of theTypes | |
set theURL to item thePos of theURLs | |
set DevonThinkLink to theURL & "?page=" | |
if theType is not PDF document then | |
display dialog "You must choose a PDF" | |
set theGroup to display group selector | |
set theItems to name of every child of theGroup | |
set theTypes to type of every child of theGroup | |
set theURLs to reference URL of every child of theGroup | |
set theItem to choose from list theItems with title "Which DT item?" | |
set theItem to theItem as string | |
set theType to my list_positions(theItems, theItem, true) as number | |
set theType to item theType of theTypes | |
set theURL to item thePos of theURLs | |
set DevonThinkLink to theURL & "?page=" | |
end if | |
end try | |
end tell | |
--create the three opening notes | |
set PdfName to name of document 1 of application "Skim" | |
tell page 1 of document 1 | |
make note with properties {type:anchored note, bounds:{579, 842, 595, 826}, text:DevonThinkLink, extended text:PdfName} | |
delay 0.1 | |
make note with properties {type:anchored note, bounds:{579, 826, 595, 810}, text:"[" & PdfName & "](" & DevonThinkLink} | |
delay 0.1 | |
make note with properties {type:anchored note, bounds:{579, 810, 595, 794}, text:pageRelation, extended text:"pr# - sk#"} | |
end tell | |
--save PDF with new notes | |
save front document | |
delay 1 | |
(* /// | |
PART 3: | |
Get all Annotation Information | |
/// *) | |
--get info for all notes | |
set allNotes to every note of document 1 | |
set allNotes_types to every type of notes of document 1 | |
set allNotes_text to text of every note of document 1 | |
set allNotes_pages to index of every page of every note of document 1 | |
--get info for all notes except top 3 | |
set selNotes to items 1 through -4 of allNotes | |
set selNotes_types to items 1 through -4 of allNotes_types | |
set selNotes_text to items 1 through -4 of allNotes_text | |
set selNotes_pages to items 1 through -4 of allNotes_pages | |
--get info from those top 3 notes | |
set pdfTitle to (get extended text of note -3 of page 1 of document 1) as string | |
set PDFLink to (get text of note -3 of page 1 of document 1) as string | |
set Markdownlink to (get text of note -2 of page 1 of document 1) as string | |
set number_of_Note3 to (get text of note -1 of page 1 of document 1) as string | |
--prepare Headers for all of the various note types | |
set notesText_text to "<hr />" & LF & LF & "<h2>The ToC</h2>" & LF1 & LF & LF | |
set notesText_anchor to "<hr />" & LF & LF & "<h2>All of my Text Notes</h2>" & LF1 & LF & LF | |
set notesText_highlight to "<hr />" & LF & LF & "<h2>All of the Highlighted Text </h2>" & LF1 & LF & LF | |
set notesText_underline to "<hr />" & LF & LF & "<h2>All of the Underlined Text</h2>" & LF1 & LF & LF | |
set notesText_strikethru to "<hr />" & LF & LF & "<h2>All of the Strike-Through Text</h2>" & LF1 & LF & LF | |
(* /// | |
PART 4: | |
The Meat-n-Potatoes of the Script | |
/// *) | |
repeat with i from 1 to count of selNotes_types | |
--prepare page # for DEVONthink url | |
set notePage to (item i of selNotes_pages) | |
set theRealPage to (notePage + number_of_Note3) as string | |
set DTpage to notePage - 1 | |
--collect Text Notes | |
if item i of selNotes_types is text note then | |
set noteText to item i of selNotes_text | |
set notesText_text to notesText_text & "<p>" & noteText & " (<a href=\"" & PDFLink & DTpage & "\">" & "p." & theRealPage & "</a>)" & LF & LF | |
--collect Anchor Notes | |
else if item i of selNotes_types is anchored note then | |
set titleText to item i of selNotes_text | |
set noteText to extended text of item i of selNotes | |
--precaution for running script multiple times | |
repeat 1 times | |
if titleText contains "x-devonthink-item://" or noteText contains "pr# - sk#" then exit repeat | |
set notesText_anchor to notesText_anchor & "<p><strong>" & titleText & ":</strong> " & noteText & " (<a href=\"" & PDFLink & DTpage & "\">p." & theRealPage & "</a>)" & LF & LF | |
end repeat | |
--collect Underline Notes | |
else if item i of selNotes_types is underline note then | |
set noteText to item i of selNotes_text | |
set notesText_underline to notesText_underline & "<p>\"" & noteText & "\"" & " (<a href=\"" & PDFLink & DTpage & "\">" & "p." & theRealPage & "</a>)" & LF & LF | |
--collect Strike-Thru Notes | |
else if item i of selNotes_types is strike out note then | |
set noteText to item i of selNotes_text | |
set notesText_strikethru to notesText_strikethru & "<p>\"" & noteText & "\"" & " (<a href=\"" & PDFLink & DTpage & "\">" & "p." & theRealPage & "</a>)" & LF & LF | |
--collect Highlight Notes | |
else if item i of selNotes_types is highlight note then | |
set theColor to color of item i of selNotes | |
set titleText to my color2text(theColor, theTextStatus) | |
set noteText to item i of selNotes_text | |
set notesText_highlight to notesText_highlight & "<p><strong>" & titleText & ":</strong> " & LF1 & "\"" & noteText & "\"" & " (<a href=\"" & PDFLink & DTpage & "\">p." & theRealPage & "</a>)" & LF & LF | |
end if | |
end repeat | |
(* /// | |
PART 5: | |
Extract Summary Annotations | |
/// *) | |
--extract "Summary" notes from the collection of all highlight notes | |
set theSummary to {} | |
set theParas to paragraphs of notesText_highlight | |
repeat with i from 1 to count of theParas | |
if item i of theParas contains "<p><strong>Summary:" then | |
set theTitle to item i of theParas | |
set theText to item (i + 1) of theParas | |
set theSumm to theTitle & LF & theText & LF & LF | |
copy theSumm to the end of theSummary | |
end if | |
end repeat | |
--remove "Summary" highlight notes from collection of highlight notes | |
if (not (theSummary is {})) then | |
set SumText to theSummary as string | |
set SumList to paragraphs of SumText | |
set empty to {""} | |
set FinalSum to my cleanmyList(SumList, empty) | |
set HighList to paragraphs of notesText_highlight | |
set finalHighlights to my cleanmyList(HighList, FinalSum) | |
set AppleScript's text item delimiters to LF | |
set notesText_highlight to (finalHighlights as string) | |
set theSummary to "<hr />" & LF & LF & "<h2>Text Summary</h2>" & LF1 & LF & LF & SumText | |
end if | |
--ALWAYS RETURN TEXT ITEM DELIMITERS TO NORMAL | |
set AppleScript's text item delimiters to tid | |
(* /// | |
PART 6: | |
Remove any Empty Annotation Sections | |
/// *) | |
--remove any note sections that are empty | |
set FinalList to my textCheck(notesText_text, notesText_anchor, notesText_highlight, notesText_underline, notesText_strikethru, theSummary) | |
--prepare the final collection of notes, with MetaData | |
set FinalText to FinalList | |
(* /// | |
PART 7: | |
Create the Evernote Note | |
/// *) | |
tell application "Evernote" | |
--create new "notes" tag if necessary | |
if (not (tag named "notes" exists)) then | |
set theTag to make tag with properties {name:"notes"} | |
else | |
set theTag to tag "notes" | |
end if | |
--create new "PDF Notes" notebook if necessary | |
if (not (notebook named "PDF Notes" exists)) then | |
make notebook with properties {name:"PDF Notes"} | |
end if | |
--create the HTML note | |
set theTitle to "Notes: " & pdfTitle | |
set newNote to create note title theTitle with html FinalText notebook "PDF Notes" | |
assign theTag to newNote | |
--synchronize Evernote | |
repeat until isSynchronizing is false | |
synchronize | |
end repeat | |
repeat until isSynchronizing is false | |
end repeat | |
end tell | |
(* /// | |
PART 8: | |
Display Growl Notifications | |
/// *) | |
--is Growl installed? | |
set Growl_ to my Growl_Check() | |
--notify on Success or Failure | |
if Growl_ is true then | |
tell application "Growl" | |
set allNotificationsFiles to {"Success Notification", "Failure Notification"} | |
set enabledNotificationsFiles to {"Success Notification", "Failure Notification"} | |
register as application ¬ | |
"Skim Annotations to Evernote" all notifications allNotificationsFiles ¬ | |
default notifications enabledNotificationsFiles ¬ | |
icon of application "URL Handler" | |
end tell | |
--on Success | |
tell application "Growl" | |
if newNote exists then | |
notify with name ¬ | |
"Success Notification" title ¬ | |
"Import Success!" description "Successfully Exported All Skim Annotations to Evernote" application name "Skim Annotations to Evernote" icon of application "Custom URL Handler" | |
else | |
notify with name ¬ | |
"Failure Notification" title ¬ | |
"Import Failed" description "The script failed to export all Skim notes to Evernote" application name "Skim Annotations to Evernote" icon of application "Custom URL Handler" | |
end if | |
end tell | |
end if | |
end tell | |
(* HANDLERS *) | |
--URL encode text | |
on encode_text(this_text, encode_URL_A, encode_URL_B) | |
set the standard_characters to "abcdefghijklmnopqrstuvwxyz0123456789" | |
set the URL_A_chars to "$+!'/?;&@=#%><{}[]\"~`^\\|*" | |
set the URL_B_chars to ".-_:" | |
set the acceptable_characters to the standard_characters | |
if encode_URL_A is false then set the acceptable_characters to the acceptable_characters & the URL_A_chars | |
if encode_URL_B is false then set the acceptable_characters to the acceptable_characters & the URL_B_chars | |
set the encoded_text to "" | |
repeat with this_char in this_text | |
if this_char is in the acceptable_characters then | |
set the encoded_text to (the encoded_text & this_char) | |
else | |
set the encoded_text to (the encoded_text & encode_char(this_char)) as string | |
end if | |
end repeat | |
return the encoded_text | |
end encode_text | |
on encode_char(this_char) | |
set the ASCII_num to (the ASCII number this_char) | |
set the hex_list to {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"} | |
set x to item ((ASCII_num div 16) + 1) of the hex_list | |
set y to item ((ASCII_num mod 16) + 1) of the hex_list | |
return ("%" & x & y) as string | |
end encode_char | |
on replaceString(theText, oldString, newString) | |
-- ljr (http://applescript.bratis-lover.net/library/string/) | |
local ASTID, theText, oldString, newString, lst | |
set ASTID to AppleScript's text item delimiters | |
try | |
considering case | |
set AppleScript's text item delimiters to oldString | |
set lst to every text item of theText | |
set AppleScript's text item delimiters to newString | |
set theText to lst as string | |
end considering | |
set AppleScript's text item delimiters to ASTID | |
return theText | |
on error eMsg number eNum | |
set AppleScript's text item delimiters to ASTID | |
error "Can't replaceString: " & eMsg number eNum | |
end try | |
end replaceString | |
on list_positions(this_list, this_item, list_all) | |
set the offset_list to {} | |
repeat with i from 1 to the count of this_list | |
if item i of this_list is this_item then | |
set the end of the offset_list to i | |
if list_all is false then return item 1 of offset_list | |
end if | |
end repeat | |
if list_all is false and offset_list is {} then return 0 | |
return the offset_list | |
end list_positions | |
--convert highlights into text values | |
on color2text(noteColor, theTextStatus) | |
set colorText to {} | |
if theTextStatus is {button returned:"Secondary"} then | |
if noteColor is {64634, 900, 1905, 65535} then | |
set end of colorText to "Disagree" | |
else if noteColor is {64907, 32785, 2154, 65535} then | |
set end of colorText to "Agree" | |
else if noteColor is {65535, 65531, 2689, 65535} then | |
set end of colorText to "Summary" | |
else if noteColor is {8608, 65514, 1753, 65535} then | |
set end of colorText to "Reference" | |
else if noteColor is {8372, 65519, 65472, 65535} then | |
set end of colorText to "Quotable" | |
else if noteColor is {64587, 1044, 65481, 65535} then | |
set end of colorText to "Technique" | |
end if | |
else if theTextStatus is {button returned:"Primary"} then | |
if noteColor is {64634, 900, 1905, 65535} then | |
set end of colorText to "Key Quote" | |
else if noteColor is {64907, 32785, 2154, 65535} then | |
set end of colorText to "Key Theme" | |
else if noteColor is {65535, 65531, 2689, 65535} then | |
set end of colorText to "Summary Passage" | |
else if noteColor is {8608, 65514, 1753, 65535} then | |
set end of colorText to "Key Motif" | |
else if noteColor is {8372, 65519, 65472, 65535} then | |
set end of colorText to "Confusing Passage" | |
else if noteColor is {64587, 1044, 65481, 65535} then | |
set end of colorText to "\"Purple\" Passage" | |
end if | |
end if | |
return colorText | |
end color2text | |
--remove any empty note sections | |
on textCheck(notesText_text, notesText_anchor, notesText_highlight, notesText_underline, notesText_strikethru, theSummary) | |
if notesText_text is "<hr /> | |
<h2>The ToC</h2> | |
" then | |
set notesText_text to "" | |
else | |
set notesText_text to notesText_text & LF & LF | |
end if | |
if notesText_anchor is "<hr /> | |
<h2>All of my Text Notes</h2> | |
" then | |
set notesText_anchor to "" | |
else | |
set notesText_anchor to notesText_anchor & LF & LF | |
end if | |
if notesText_highlight is "<hr /> | |
<h2>All of the Highlighted Text </h2> | |
" then | |
set notesText_highlight to "" | |
else | |
set notesText_highlight to notesText_highlight & LF & LF | |
end if | |
if notesText_underline is "<hr /> | |
<h2>All of the Underlined Text</h2> | |
" then | |
set notesText_underline to "" | |
else | |
set notesText_underline to notesText_underline & LF & LF | |
end if | |
if notesText_strikethru is "<hr /> | |
<h2>All of the Strike-Through Text</h2> | |
" then | |
set notesText_strikethru to "" | |
else | |
set notesText_strikethru to notesText_strikethru & LF & LF | |
end if | |
if theSummary is {} then | |
set theSummary to "" | |
else | |
set theSummary to theSummary & LF & LF | |
end if | |
return notesText_text & theSummary & notesText_anchor & notesText_highlight & notesText_underline & notesText_strikethru | |
end textCheck | |
--remove items from a list | |
on cleanmyList(theList, itemsToDelete) | |
set cleanList to {} | |
repeat with i from 1 to count theList | |
if {theList's item i} is not in itemsToDelete then set cleanList's end to theList's item i | |
end repeat | |
return cleanList | |
end cleanmyList | |
--Check For Growl | |
on Growl_Check() | |
if appIsRunning("Growl") then | |
set growl_Running to true | |
tell application "Growl" | |
set allNotificationsFiles to {"Import To Evernote", "Success Notification", "Failure Notification"} | |
set enabledNotificationsFiles to {"Import To Evernote", "Success Notification", "Failure Notification"} | |
register as application ¬ | |
"Skim Annotations to Evernote" all notifications allNotificationsFiles ¬ | |
default notifications enabledNotificationsFiles ¬ | |
icon of application "Custom URL Handler" | |
end tell | |
return growl_Running | |
else | |
error | |
end if | |
end Growl_Check | |
--App Detect | |
on appIsRunning(appName) | |
tell application "System Events" to (name of processes) contains appName | |
end appIsRunning |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment