Phedinopsis brazzae
Phedinopsis brazzae

Reputation: 13

Create scaled, layered folder icons from multiple images via AppleScript?

There are similar answers already on here (like this one), but not with exactly what I want to accomplish. I would like to create an AppleScript that takes a chosen folder and application (or image file), scales the icon of the application/image down to maybe 60% of normal, layers that scaled icon onto the folder icon in a chosen position, and then pastes the resulting layered icon back onto the chosen folder. For example, something like this.

I've been working based on the other solution I linked to, but I don't know much AppleScript, and I don't know enough about Foundation and AppKit to even begin with that. What I've tried so far with plain AppleScript hasn't worked, and I'm not sure I can even accomplish what I want with plain AppleScript (particularly the layering part, I believe). Any help greatly appreciated.

Upvotes: 0

Views: 126

Answers (2)

red_menace
red_menace

Reputation: 3412

In addition to image editing applications such as Gimp, there are a variety of applications available that will overlay an image onto a folder. If you just wanted to use a script to do it yourself, AppleScriptObjC can be used to access methods from various Cocoa classes such as NSImage.

The following script gets a generic folder icon image from the system resources and an overlay image from a file or application icon. The image is then scaled and composited onto the folder image and set as the icon for a folder or saved to a file on the desktop. Note that the Finder mostly creates its own composite icons, so they may be different than the system defaults.

A folder icon can be set programmatically, but I have found that the scale and location offsets of the application icons usually need to be adjusted a little, as the overlay image may have different padding or arrangement within its bounds. An image file can also be dragged to a Finder Get Info window to set the icon, so the default is to save the output image to a file.

use AppleScript version "2.7" -- High Sierra (10.13) or later for newer enumerations
use framework "Foundation"
use scripting additions

property iconResources : POSIX path of (path to library folder from system domain) & "CoreServices/CoreTypes.bundle/Contents/Resources/" -- system icons
property baseSize : 1024 -- base image size (icons are square with maximum size 1024)
property scale : 0.55 -- scale of overlayed image (adjust as desired)
property setIcon : false -- set folder icon or save to file

on run -- overlay an image onto a folder icon
    set baseImage to getBaseImage(iconResources & "GenericFolderIcon.icns") -- or whatever icon image
    baseImage's setSize:{baseSize, baseSize}
    
    set overlayImage to getOverlayImage(choose file with prompt "Choose an application or overlay image:")
    set scaledSize to baseSize * scale
    overlayImage's setSize:{scaledSize, scaledSize}
    set offsetX to (baseSize - scaledSize) / 2 -- center (adjust as needed)
    set offsetY to (baseSize - scaledSize) / 2 - 50 -- shift down from center (adjust as needed)
    
    baseImage's lockFocus() -- set drawing context
    overlayImage's drawAtPoint:{offsetX, offsetY} fromRect:(current application's NSZeroRect) operation:(current application's NSCompositingOperationSourceOver) fraction:1.0
    baseImage's unlockFocus()
    output(baseImage)
end run

to getBaseImage(imagePath)
    set image to readImageFromFile(imagePath)
    if image is missing value then error "Base image was not found."
    return image
end getBaseImage

to getOverlayImage(imageFile)
    tell application "Finder" to if (kind of imageFile is "Application") then
        set image to current application's NSWorkspace's sharedWorkspace's iconForFile:(POSIX path of imageFile)
    else
        set image to my readImageFromFile(POSIX path of imageFile)
    end if
    if image is missing value then error "Overlay image was not found."
    return image
end getOverlayImage

to readImageFromFile(posixFile)
    set image to missing value
    set imageData to current application's NSData's dataWithContentsOfFile:posixFile
    if imageData is not missing value then set image to current application's NSImage's alloc's initWithData:imageData
    return image
end readImageFromFile

to writeImageToFile(image, posixPath)
    set imageData to image's TIFFRepresentation()
    set imageRep to current application's NSBitmapImageRep's imageRepWithData:imageData
    set imageType to imageRep's representationUsingType:(current application's NSBitmapImageFileTypePNG) |properties|:(missing value)
    imageType's writeToFile:posixPath atomically:true
end writeImageToFile

to output(image)
    if setIcon then -- set the icon
        set outputPath to POSIX path of (choose folder with prompt "Choose a folder to set its icon:")
        current application's NSWorkspace's sharedWorkspace's setIcon:image forFile:outputPath options:0
    else -- save to a file
        set outputPath to POSIX path of (((path to desktop folder) as text) & "Composite Folder Icon Image.png") -- or whatever
        writeImageToFile(image, outputPath)
    end if
end output

Upvotes: 1

Mark Setchell
Mark Setchell

Reputation: 207415

I know some, but not all, the aspects of this. Nobody said StackOverflow answers have to be complete so if anyone knows the bits I am missing they are welcome to add a complementary answer or edit mine.

Bear in mind that you can run bash commands, and Python etc, from Applescript easily enough, so although OP asks for Applescript I presume they are happy enough with Applescript that calls bash.

Let's say we want to grab the icon from the Notes app, reduce it, paste it onto the normal folder icon and set this new image to be the icon for a folder called Fred on our desktop. So its icon will be:

enter image description here


So first we need to find the icon for Notes. We can do that by starting Notes and running:

ps -aef | grep Notes
501 22128     1   0  2:42am ??         0:02.06 /System/Applications/Notes.app/Contents/MacOS/Notes

So now we know Notes lives in /System/Applications/Notes.app


Next we want to find its icon, so:

find /System/Applications/Notes.app -name "*.icns"
/System/Applications/Notes.app/Contents/Resources/AppIcon.icns

So now we know where its icons are, and we can extract the icon as a PNG to our Desktop with:

sips -s format png /System/Applications/Notes.app/Contents/Resources/AppIcon.icns --out ~/Desktop/notes.png

That looks like this:

enter image description here

I chose PNG format because, unlike JPEG, it supports transparency.


Now we can resize that and paste it onto the regular folder icon easily enough with PHP+gd (shipped with macOS) or with ImageMagick or with GIMP. I can do that if anyone else can help with the missing aspects.


Next, we can manually set the icon on a folder to the icon of another app and see what that results in. So, find an app in Finder, press I to bring up its info. Select the Fred folder on your desktop and bring up its info the same way. Now click the icon at the top of the first info window to select it, copy with C and then click the icon at the top of the second info window and press V to paste it.

Now go into the folder with the new icon and you will find a file called Icon?:

cd ~/Desktop/Fred
ls
Icon?

We can look inside that with xattr:

xattr Icon*
com.apple.FinderInfo
com.apple.ResourceFork

And we can dump the resource fork with:

xattr -p com.apple.ResourceFork Icon*

I can see there are 8 PNG images in there.

So... the main missing link is how to get the images in that file...

Upvotes: 1

Related Questions