Thursday, October 17, 2019

#1 2008-06-07 09:48:43 am

Martin Michel
Administrator
From:: Berlin, Germany
Registered: 2008-03-03
Posts: 701
Website

iconoodle - Convenient ICNS to PNG image conversion

Soon after this article about ICNS to PNG conversion was published by Rob Griffiths on Macworld, a good friend of mine called me to ask if I could write an AppleScript wrapper for the mentioned «sips» command, as he needed some advanced features to simplify his daily work life as a stressed Mac admin.

Yes, he knew that their already existed scripts and apps for this purpose...
Yes, he knew that I was busy with other projects...
Yes, he knew that I simply cannot deny a request from a good friend wink

And so here it is, the result of one week of coding in the evenings after work:



So now you might say: "Hey Martin, what exactly are those advanced features of your 'yet another ICNS to PNG conversion script'?"

OK, here you are:

Cool and convenient features of iconoodle
• You can not only drag & drop ICNS files, but also applications onto iconoodle's icon and it will convert all ICNS files found in the application package folder (cautious clapping)
• You can set a default archive folder for the created PNG files (growing enthusiasm)
• You can execute post action scripts to further process the created PNG files (giggle of the geeks), sample post actions are included with the download
• Preferences panel to adjust the settings (the mob goes wild!)

Requirements
• Mac OS X 10.4.10 (might also run on older systems)

Usage
After you installed iconoodle on your Mac - just drop it anywhere you like, dock, desktop, folder bar, you name it - you can use it in exactly two ways:

1. Drag and drop ICNS files and/or applications bundles onto iconoodle's icon to start the ICNS to PNG conversion process. Or drag a single folder onto iconoodle's icon to set this folder as the default archive folder for created PNG files.

2. Start iconoodle by double-clicking its icon to display the preferences panel. Here you can set/reset the default archive folder and add/remove post action scripts.

Post actions
To further enhance the iconoodle experience, you can also execute various (self-written) post action scripts to process the created PNG files. Two sample post action scripts are included with the download.

The first script, named «openpngs.scpt», will open all created PNG files in the default viewer application. The second script, named «mailpngs.scpt»,  will create a new eMail message in Apple Mail with the created PNG files attached.

Please have a look at the source code of those two AppleScripts to see how easy it is to write your own post action scripts, if you are familiar with AppleScript.

To add or remove post actions, just open the preferences panel and select the corresponding menu items.

FAQ
Q: Who came up with the lame name of the script?
A: I own this book and am a follower. Moreover I am absolutely blown away by this costume.

Applescript:


-- created: October 2007
-- modified:
-- version: 0.7b
-- history:
-- • v0.7b:
-- + first public beta release

-- =====================
-- == CORE PROPERTIES ==
-- =====================

-- what a silly name for a script :)
-- DON'T INSULT OUR GOD, THE FLYING SPAGHETTI MONSTER!!!
property mytitle : "iconoodle"
property domain : ("com.jos." & mytitle)

-- =========================
-- == START MODE HANDLERS ==
-- =========================

on open dropitems
   my main("on open", dropitems)
end open

on run
   my main("on run", missing value)
end run

-- ==================
-- == MAIN ROUTINE ==
-- ==================

on main(startmode, args)
   try
       -- first of all: initializing the preferences system
       my userdefaults's setdomain(domain)
       
       -- user started the script by double-clicking its icon
       -- -> display preferences panel
       if startmode is "on run" then
           my guiloop()
           -- user started the script by dropping items onto its icon
           -- -> process dropped items
       else if startmode is "on open" then
           set dropitems to args
           set dropapps to {}
           set dropicons to {}
           set dropdirs to {}
           -- screening dropped items for relevant item kinds (apps, folders, icns files)
           repeat with dropitem in dropitems
               set dropitem to dropitem as alias
               set iteminfo to info for (dropitem as alias)
               -- searching for dropped applications
               if package folder of iteminfo is true and name extension of iteminfo is "app" then
                   set dropapps to dropapps & dropitem
                   -- searching for dropped icns files
               else if folder of iteminfo is false and name extension of iteminfo is "icns" then
                   set dropicons to dropicons & dropitem
                   -- searching for dropped folders
               else if folder of iteminfo is true and package folder of iteminfo is false then
                   set dropdirs to dropdirs & dropitem
               end if
           end repeat
           
           -- surveying the booty...
           set countdropicons to length of dropicons
           set countdropapps to length of dropapps
           set countdropdirs to length of dropdirs
           
           -- no icns files, noapps, no folders...
           if countdropicons is 0 and countdropapps is 0 and countdropdirs is 0 then
               set infomsg to "Sorry, but we could not detect any processable items." & return & return & my getusageinfo()
               my dspinfomsg(infomsg)
               return
               -- no icns files, no apps, no scripts, but more than 1 folder...
           else if countdropicons is 0 and countdropapps is 0 and countdropdirs is greater than 1 then
               set infomsg to "Sorry, you dropped " & countdropdirs & " folders onto the script." & return & return & "If you want to set the default archive folder, please just drop a single folder onto " & mytitle & "'s icon."
               my dspinfomsg(infomsg)
               return
               -- no icns files, no apps, but 1 folder was dropped onto iconoodle -> archive folder
           else if countdropicons is 0 and countdropapps is 0 and countdropdirs is 1 then
               -- Mac path! ':'
               set archivefolder to (item 1 of dropdirs) as Unicode text
               my userdefaults's setpref("archivefolder", archivefolder)
               set infomsg to "Your default archive folder is now located at:" & return & return & "'" & archivefolder & "'"
               my dspinfomsg(infomsg)
               return
           end if
           
           -- getting the archive folder...
           -- archive folder is a Mac path! ':'
           if my userdefaults's prefsfileexists() is false then
               -- no preferences file yet...so we have to ask the user for the archive folder
               set archivefolder to (my askforarchivefolder() as Unicode text)
           else
               -- preferences file exists and default archive folder is set
               if my userdefaults's haspref("archivefolder") is true then
                   set archivefolder to my userdefaults's getpref("archivefolder")
                   -- preferences file exists but default archive folder is NOT set
               else
                   set archivefolder to (my askforarchivefolder() as Unicode text)
               end if
           end if
           
           -- container for the gathereed icns files
           set icnsfiles to {}
           
           -- dropped icns files are added directly to the list of icns files
           if countdropicons is greater than 0 then
               set icnsfiles to icnsfiles & dropicons
           end if
           
           -- searching the application bundles for icns files
           -- adding found icns files to the list of icns files
           repeat with dropapp in dropapps
               set icnsfiles to icnsfiles & my searchicnsfiles(dropapp)
           end repeat
           
           -- getting filepaths for the new png files, then:
           -- icns > png conversion process
           set converrcounter to 0
           set converrmsgs to ""
           repeat with icnsfile in icnsfiles
               set fileinfo to info for icnsfile
               set filename to (characters 1 through -6 of (name of fileinfo)) as Unicode text
               -- Posix path! '/'
               set pngpath to POSIX path of (my getunusedfilepath(archivefolder, filename, "png"))
               try
                   my icns2png(POSIX path of icnsfile, pngpath)
               on error errmsg number errnum
                   set converrcounter to converrcounter + 1
                   set converrmsgs to converrmsgs & errmsg & " (" & errnum & ")" & return
               end try
           end repeat
           
           set pacterrcounter to 0
           set pacterrmsgs to ""
           -- executing post actions if available
           -- WALLA WALLA! We should check existence of postactions!
           if my userdefaults's prefsfileexists() is true and my userdefaults's haspref("postactions") is true then
               -- Mac paths! ':'
               set postactions to my userdefaults's getpref("postactions")
               set icnspaths to ""
               repeat with icnsfile in icnsfiles
                   set icnspaths to icnspaths & " " & quoted form of (POSIX path of (icnsfile as Unicode text))
               end repeat
               repeat with postaction in postactions
                   try
                       set command to ("osascript " & quoted form of (POSIX path of postaction) & icnspaths) as «class utf8»
                       do shell script command
                   on error errmsg number errnum
                       set pacterrcounter to pacterrcounter + 1
                       set pacterrmsgs to pacterrmsgs & errmsg & " (" & errnum & ")" & return
                   end try
               end repeat
           end if
           
           -- of course: I don't hope for many errors, but in case they occur,
           -- we should inform the user accordingly...
           if converrcounter > 0 or pacterrcounter > 0 then
               set errmsg to "Sorry, some errors occured:" & return & return & "Conversion errors: " & converrcounter & return & "Post action errors: " & pacterrcounter
               set errlog to "Conversion errors:" & return & converrmsgs & return & "Post action errors:" & return & pacterrmsgs
               tell me
                   activate
                   display dialog errmsg buttons {"Show log", "OK"} default button 2 with icon caution with title mytitle
                   set choice to result
                   if button returned of choice is "Show log" then
                       tell application "TextEdit"
                           make new document with properties {text:errlog}
                       end tell
                   end if
               end tell
           end if
       end if
   on error errmsg number errnum
       -- ignoring 'User canceled'-error
       if errnum is not -128 then
           my dsperrmsg(errmsg, errnum)
       end if
   end try
end main

-- ================
-- == ICNS TOOLS ==
-- ================

-- returns icns files found in the application package folder
-- application must be passed as an alias
-- returns the icns files as a list of aliases
on searchicnsfiles(appalias)
   set icnsfiles to {}
   set apppath to (POSIX path of appalias)
   set command to ("find " & quoted form of apppath & " -name '*.icns'") as «class utf8»
   set icnspaths to paragraphs of (do shell script command)
   repeat with icnspath in icnspaths
       set icnsfiles to icnsfiles & (POSIX file icnspath)
   end repeat
   return icnsfiles
end searchicnsfiles

-- converts an icns file to a png file
-- icns source and png destination must be passed as a Posix path
on icns2png(icnspath, pngpath)
   set command to ("sips -s format png " & quoted form of icnspath & " --out " & quoted form of pngpath) as «class utf8»
   do shell script command
end icns2png

-- ========================
-- == GUI CONTROL CENTER ==
-- ========================

-- the main GUI loop displaying the preferences panel
-- and coordinating actions triggered by chosen menu items
on guiloop()
   try
       set menuitem to my dspprefsmenu()
       if menuitem is missing value then
           return
       else
           if menuitem is "Show archive folder" then
               my showarchivefolder()
           else if menuitem is "Set archive folder" then
               my setarchivefolder()
           else if menuitem is "Reset archive folder" then
               my resetarchivefolder()
           else if menuitem is "Add post action(s)" then
               my addpostactions()
           else if menuitem is "Show post action(s)" then
               my showpostactions()
           else if menuitem is "Remove post action(s)" then
               my removepostactions()
           else if menuitem is "eMail feedback" then
               my emailfeedback()
           else if menuitem is "Visit JoS" then
               my visitjoswebsite()
           end if
       end if
       my guiloop()
   on error errmsg number errnum
       if errnum is -128 then
           my guiloop()
       else
           my dsperrmsg(errmsg, errnum)
       end if
   end try
end guiloop

-- displays the preferences menu and returns the chosen menu item
on dspprefsmenu()
   set menuitems to {"== Misc ==", "eMail feedback", "Visit JoS"}
   
   if my userdefaults's prefsfileexists() is false then
       set menuitems to {"== Archive folder ==", "Set archive folder", "", "== Post Actions ==", "Add post action(s)", ""} & menuitems
   else
       if my userdefaults's haspref("postactions") is false then
           set menuitems to {"== Post Actions ==", "Add post action(s)", ""} & menuitems
       else
           set menuitems to {"== Post Actions ==", "Show post action(s)", "Add post action(s)", "Remove post action(s)", ""} & menuitems
       end if
       if my userdefaults's haspref("archivefolder") is false then
           set menuitems to {"== Archive folder ==", "Set archive folder", ""} & menuitems
       else
           set menuitems to {"== Archive folder ==", "Show archive folder", "Set archive folder", "Reset archive folder", ""} & menuitems
       end if
   end if
   
   choose from list menuitems with title mytitle with prompt "Please choose an option:" cancel button name "Quit" OK button name "Select" without multiple selections allowed and empty selection allowed
   set choice to result
   if choice is not false then
       set menuitem to item 1 of choice
       if menuitem begins with "==" or menuitem is "" then
           my dspprefsmenu()
       else
           return menuitem
       end if
   else
       return missing value
   end if
end dspprefsmenu

-- ================================
-- == SUBROUTINES FOR MENU ITEMS ==
-- ================================

-- shows the set archive folder in the Finder
on showarchivefolder()
   set archivefolder to POSIX path of (my userdefaults's getpref("archivefolder"))
   set command to ("open " & quoted form of archivefolder) as «class utf8»
   do shell script command
end showarchivefolder

-- asks for and sets the archive folder
on setarchivefolder()
   set archivefolder to (my askforarchivefolder()) as Unicode text
   my userdefaults's setpref("archivefolder", archivefolder)
end setarchivefolder

-- resets the current archive folder
on resetarchivefolder()
   my userdefaults's rempref("archivefolder")
end resetarchivefolder

-- shows the parent folder of selected post actions in the Finder
on showpostactions()
   set postactions to my dsppostactions()
   if postactions is missing value then
       return
   else
       repeat with postaction in postactions
           set pardirpath to my getpardirpath((postaction as Unicode text))
           set pardirpath to POSIX path of pardirpath
           set command to ("open " & quoted form of pardirpath) as «class utf8»
           do shell script command
       end repeat
   end if
end showpostactions

-- adds new post actions
on addpostactions()
   set scriptfiles to choose file with prompt "Please choose scripts for post actions:" of type {"osas"} with multiple selections allowed without showing package contents and invisibles
   set newpostactions to {}
   repeat with scriptfile in scriptfiles
       set newpostactions to newpostactions & (scriptfile as Unicode text)
   end repeat
   if my userdefaults's haspref("postactions") then
       set existpostactions to my userdefaults's getpref("postactions")
       repeat with newpostaction in newpostactions
           if newpostaction is not in existpostactions then
               set existpostactions to existpostactions & newpostaction
           end if
       end repeat
       my userdefaults's setpref("postactions", existpostactions)
   else
       my userdefaults's setpref("postactions", newpostactions)
   end if
end addpostactions

-- removes post actions
on removepostactions()
   set chosenpostactions to my dsppostactions()
   if chosenpostactions is missing value then
       return
   else
       set existpostactions to my userdefaults's getpref("postactions")
       set remainpostactions to {}
       repeat with existpostaction in existpostactions
           if existpostaction is not in chosenpostactions then
               set remainpostactions to remainpostactions & existpostaction
           end if
       end repeat
       if remainpostactions is {} then
           my userdefaults's rempref("postactions")
       else
           my userdefaults's setpref("postactions", remainpostactions)
       end if
   end if
end removepostactions

-- creates a new email message with my address in the default mail app
on emailfeedback()
   set command to "open 'mailto:martin@joyofscripting.com'" as «class utf8»
   do shell script command
end emailfeedback

-- opens the Joy of Scripting website in the default browser
on visitjoswebsite()
   set command to "open '[url]http://www.joyofscripting.com[/url]'" as «class utf8»
   do shell script command
end visitjoswebsite

-- =============================
-- == MISCELLANEOUS UTILITIES ==
-- =============================

-- displays a list of available post actions
-- returns the file path of the chosen post action
-- returns a Mac path! ':'
on dsppostactions()
   set postactionids to {}
   set postactionnames to {}
   set postactionpaths to {}
   -- FUTURE: the «postactions» should be sorted alphabetically (python script cmdline?)
   set postactions to my userdefaults's getpref("postactions")
   set idcounter to 0
   repeat with postaction in postactions
       set idcounter to idcounter + 1
       set olddelims to AppleScript's text item delimiters
       set AppleScript's text item delimiters to {":"}
       set txtitems to text items of postaction
       set AppleScript's text item delimiters to olddelims
       set postactionname to "[" & idcounter & "] " & last item of txtitems
       set postactionids to postactionids & idcounter
       set postactionnames to postactionnames & postactionname
       set postactionpaths to postactionpaths & postaction
   end repeat
   choose from list postactionnames with title mytitle with prompt "Please choose post actions to process:" OK button name "Select" cancel button name "Cancel" with multiple selections allowed without empty selection allowed
   set choice to result
   if choice is not false then
       set chosenpostactions to {}
       set menuitems to choice
       repeat with menuitem in menuitems
           set rightbracketoffset to offset of "]" in menuitem
           if rightbracketoffset is equal to 3 then
               set menuitemid to (character 2 of menuitem) as integer
           else
               set menuitemid to ((characters 2 through (rightbracketoffset - 1) of menuitem) as Unicode text) as integer
           end if
           repeat with i from 1 to (length of postactionids)
               set postactionid to item i of postactionids
               if menuitemid is equal to postactionid then
                   set postactionpath to item i of postactionpaths
                   set chosenpostactions to chosenpostactions & postactionpath
               end if
           end repeat
       end repeat
       log chosenpostactions
       return chosenpostactions
   else
       return missing value
   end if
end dsppostactions

-- returns the parent folder of an item as a Mac path! ':'
-- expects «itempath» to be a Mac path! ':'
-- origin: [url]http://www.fischer-bayern.de/applescript/html/parent_f.html[/url]
on getpardirpath(itempath)
   set olddelims to AppleScript's text item delimiters
   set AppleScript's text item delimiters to {":"}
   set counttxtitems to (count text items of itempath)
   set lasttxtitem to the last text item of itempath
   if lasttxtitem = "" then
       set counttxtitems to counttxtitems - 2 -- bei Pfad zu einem Ordner
   else
       set counttxtitems to counttxtitems - 1 -- bei Pfad zu einer Datei
   end if
   set pardirpath to text 1 thru text item counttxtitems of itempath & ":"
   set AppleScript's text item delimiters to olddelims
   return pardirpath
end getpardirpath

-- returns an unused file path (Mac path!)
-- expects «folderpath» to be a Mac path! ':'
-- example:
-- folderpath: 'DandyDisk:Users:bender:Desktop:icnsfiles:'
-- filename: 'example'
-- suffix: 'png'
-- -> filepath: 'DandyDisk:Users:bender:Desktop:icnsfiles:example.png'
-- if this filepath already exists:
-- -> filepath: 'DandyDisk:Users:bender:Desktop:icnsfiles:example 1.png'
-- and so on...
on getunusedfilepath(folderpath, filename, suffix)
   set counter to 0
   repeat
       if counter is equal to 0 then
           set filepath to folderpath & filename & "." & suffix
       else
           set filepath to folderpath & filename & " " & counter & "." & suffix
       end if
       try
           set filealias to filepath as alias
       on error
           exit repeat
       end try
       set counter to counter + 1
   end repeat
   return filepath
end getunusedfilepath

-- returns the usage info for iconoodle
on getusageinfo()
   set usageinfo to "To use " & mytitle & ", you can drop ICNS files & application bundles on its icon." & return & return & "If you want to quickly set your default archive folder, you can also drop the folder of your choice onto " & mytitle & "." & return & return & "Start " & mytitle & " by double-clicking its icon to access the preferences panel."
   return usageinfo
end getusageinfo

-- asks the user to provide an archive folder
on askforarchivefolder()
   tell me
       activate
       set archivefolder to choose folder with prompt "Please choose an archive folder for generated PNG files:" without multiple selections allowed, invisibles and showing package contents
   end tell
   return archivefolder
end askforarchivefolder

-- displays a simple info message
on dspinfomsg(infomsg)
   tell me
       activate
       display dialog infomsg buttons {"OK"} default button 1 with title mytitle
   end tell
end dspinfomsg

-- displays an error message
on dsperrmsg(errmsg, errnum)
   tell me
       activate
       display dialog "Sorry, an error occured:" & return & return & errmsg & " (" & errnum & ")" buttons {"OK"} default button 1 with title mytitle with icon stop
   end tell
end dsperrmsg

-- =================================
-- == USER DEFAULTS SCRIPT OBJECT ==
-- =================================

-- WOHOOOO!
script userdefaults
   property domain : missing value
   
   -- well...sets the domain
   on setdomain(thedomain)
       set domain to thedomain
   end setdomain
   
   -- indicates whether the preferences file already exists or not
   on prefsfileexists()
       set prefsfilepath to ((path to preferences folder from user domain) as Unicode text) & domain & ".plist"
       try
           set prefsfilealias to prefsfilepath as alias
           return true
       on error
           return false
       end try
   end prefsfileexists
   
   -- creates a preferences file
   on createprefsfile()
       -- :) yeah, lame hack, very quick and very dirty *g*
       set command to ("defaults write " & domain & " foo foo") as «class utf8»
       do shell script command
   end createprefsfile
   
   -- returns the file path of the preferences file
   -- returns a Mac path! ':'
   on getprefsfilepath()
       set prefsfilepath to (((path to preferences folder from user domain) as Unicode text) & domain & ".plist")
       return prefsfilepath
   end getprefsfilepath
   
   -- indicates whether the given preferences key exists or not
   on haspref(prefkey)
       try
           tell application "System Events"
               set prefvalue to value of property list item prefkey of property list file (my getprefsfilepath())
           end tell
           return true
       on error
           return false
       end try
   end haspref
   
   -- returns the value for the given preferences key
   on getpref(prefkey)
       tell application "System Events"
           set prefvalue to value of property list item prefkey of property list file (my getprefsfilepath())
       end tell
       return prefvalue
   end getpref
   
   -- creates a key/value-pair in the preferences file
   -- creates the preferences file if it does not already exist
   on setpref(prefkey, prefvalue)
       -- if the preferences file does not already exist, we create it
       if not prefsfileexists() then
           my createprefsfile()
       end if
       tell application "System Events"
           try
               -- maybe we are lucky and the property list item already exists
               set value of property list item prefkey of property list file (my getprefsfilepath()) to prefvalue
           on error
               -- no, we are unlucky and now have to create the property list item before manipulating its value
               my createprefkey(prefkey)
               set value of property list item prefkey of property list file (my getprefsfilepath()) to prefvalue
           end try
       end tell
   end setpref
   
   -- removes the given preferences key from the preferences file
   -- yes, also necessary sometimes...
   on rempref(prefkey)
       set command to ("defaults delete " & domain & " " & prefkey) as «class utf8»
       do shell script command
   end rempref
   
   -- creates a preferences key in a preferences file
   -- lame hack, once again, but using "System Events" sometimes just sucks ;)
   on createprefkey(prefkey)
       set command to ("defaults write " & domain & " " & prefkey & " dummyvalue") as «class utf8»
       do shell script command
   end createprefkey
end script


Plonum - Convert images to iWork Numbers documents

Offline

 

Board footer

Powered by FluxBB

RSS (new topics) RSS (active topics)