macosapplescriptfindericns

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.


Solution

  • 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