0

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.

  • AppleScript by itself can’t do anything like that. You will get a lot more precision (and it would probably be easier) using an application such as Gimp to create the icon, but various [NSImage](https://developer.apple.com/documentation/appkit/nsimage?language=objc) methods can be used via some **AppleScriptObjC** to overlay images - is that the part you are looking for? – red_menace Jan 05 '23 at 22:39
  • Basically you're asking two things: 1) to create a unique image, and 2) attach that image to the folder as an icon. There are plenty of answers to each of those questions on the web, the issue is putting them together. – Ted Wrigley Jan 06 '23 at 17:04
  • So, as a concrete example... you want to take the icon from **Notes** app and reduce its size and composite it onto a folder icon and then set that as the icon for a folder on your Desktop? – Mark Setchell Jan 07 '23 at 02:05
  • @MarkSetchell Yes, exactly, that’s what I’m trying to do – Phedinopsis brazzae Jan 07 '23 at 06:09
  • @red_menace That’s where things get fuzzy for me. It seems like NSImage might be relevant, but it’s currently well beyond my ability to parse that enough to determine the answer. Can’t even begin to comment on AppleScriptObjC. To answer your first question, I’m trying to make use of a pre-existing app icon or, if need be, a pre-existing image file (preferably the former in most cases, though). – Phedinopsis brazzae Jan 07 '23 at 06:21

2 Answers2

1

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...

Mark Setchell
  • 191,897
  • 31
  • 273
  • 432
  • This is a great start, so thank you! I will look into this some more based on what you’ve written and post an update if I’m able to figure out any more. There may be some functionality from the Foundation and/or AppKit framework that would fill in the gaps. I remember seeing some image-related functions in there somewhere. – Phedinopsis brazzae Jan 07 '23 at 06:13
  • Not sure if this works similarly in a modern OS but you may be able to use this to get the icon resource: `tell application "Notes" to path to resource "AppIcon.icns"` – Mockman Jan 13 '23 at 02:15
1

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
red_menace
  • 3,162
  • 2
  • 10
  • 18