Is it possible to change the icon of a Mac folder using a set of Ruby commands? I believe that OSX requires a .icon file to be present within the modified folder, perhaps there is a specific way of converting a jpg or png to the .icon criteria?
-- Edit (Working solution. Requires ImageMagick and OSXUtils)
* note, for my application I intended to set folder icons. It is entirely possible this could work for files as well.
def set_icon image, folder
# Convert to absolute paths and setup
image = File.expand_path image
folder = File.expand_path folder
dim = 512
thumb = folder + '/'+ 'thumb.png' # PNG supports transparency
icon = folder + '/'+ 'icon.icns'
# Convert original to thumbnail
system "convert '#{ image }' -quiet -thumbnail '#{dim}x#{dim}>' \
-background none -gravity center -extent #{dim}x#{dim} '#{ thumb }'"
# Set icon format. Causes 'libpng warning: Ignoring attempt to set cHRM RGB triangle with zero area'
system "sips -s format icns '#{ thumb }' --out '#{ icon }'"
# Set the icon
system "seticon -d '#{ icon }' '#{ folder }'"
# Cleanup
FileUtils.rm thumb
FileUtils.rm icon
end
It's been years since I played with them, but .icon files' format were documented by Apple's documentation and Wikepedia.
If I remember right, the name has a trailing "\r" to make it harder to type, but that's easily handled from code.
You should be able to use the normal File.rename method to move an .icon file into a folder, and the Finder should do the right thing.
Looking at your code, I'd do some things differently:
require 'fileutils'
def set_icon image, folder
# Convert to absolute paths and setup
image = File.expand_path image
folder = File.expand_path folder
temp = File.join(folder, 'temp2' + File.extname(image))
# Copy image
FileUtils.cp(image, temp)
# Take an image and make the image its own icon
system "sips -Z 512 -i #{ temp }"
# Extract the icon to its own resource file
system "DeRez -only icns #{ temp } > tmpicns.rsrc"
# Append a resource to the folder you want to icon-ize
system "Rez tmpicns.rsrc -o $'#{ folder }/Icon\r'"
# Use the resource to set the icon.
system "SetFile -a C #{ folder }"
end
Rather than rely on sprintf or % ("format") to build the strings, instead use simple interpolation. sprintf strings are great when you need to force column widths and coerce values into a different representation, but they're overkill when you're inserting a single value that isn't formatted.
sips has this option, which looks promising, but it's not documented well in the man page:
-i
--addIcon
Add a Finder icon to image file.
Also, Stack Overflow's sibling site "Ask Different" has "Why setting image as its own icon with sips result a blurred icon? Are there any alternatives?", "How do I set an icon for a directory via the CLI? and "Changing a file or folder icon using the Terminal" which look useful.
Related
This is a pseudo question to share my own trick and script below.
The point is to be able to display image pixel for pixel on Retina displays. This is mainly useful for high resolution image and/or for developers working on HDPI version of images.
The solution works well only if the display setting is set to 2:1 ratio in the OS X preferences. Beware, late 2016 MacBook Pro default setting is not set to 2:1 by default. You should set it on the medium setting to get it right.
Finder : the simple trick is to give a name ending with #2x (before the extension): my_image#2x.jpg . Then when using Quick Look feature the image is pixel-wise. As this naming scheme is recommended for retina images, both normal and HDPI images display at same size, as expected, the retina being sharper.
Preview : In preview, the DPI resolution of an image is interpreted as normal if it is set to 72dpi. By setting it to 144, you get the right display ratio. One can achieve the same effect at 72dpi by changing the display scale to 50%, but the scale setting does not stick to the image file while the DPI setting does. Change it through the Tools->Size menu item.
Here below is a small applescript to automate 144dpi setting from the Finder.
tell application "Finder"
repeat with item_cour in selection as list
if word 1 of (the kind of item_cour as text) is "Image" then
set path_cour to POSIX path of (item_cour as text)
do shell script "p_cour='" & path_cour & "';
tags=$(xattr -px com.apple.metadata:_kMDItemUserTags \"$p_cour\");
f_info=$(xattr -px com.apple.FinderInfo \"$p_cour\");
sips -s dpiHeight 144 -s dpiWidth 144 \"$p_cour\";
xattr -wx com.apple.FinderInfo '$f_info' \"$p_cour\";
xattr -wx com.apple.metadata:_kMDItemUserTags \"$tags\" \"$p_cour\" "
-- do shell script "convert \"" & path_cour & "\" -set units PixelsPerInch -density 144 \"" & path_cour & "\""
end if
end repeat
end tell
Since the sips command does not preserve tags, the script includes 4 lines to get and set them back to the file after it has been modified, using the xattr command.
To install the script: open the script editor, create a new document, paste the code and save it into the ~/Library/Scripts/Finder folder.
Be sure to check the Show the Script Menu option in Script editor preference.
To use the script: select image file(s) in Finder and activate the script from the menu.
I want to clean up my Photos library and especially move all my iPhone screenshots to a new album, that way I can easily look through them and delete what I want.
Now for this I first wanted to make a smart album in Photos, but it seems that Photos can't filter on the dimensions of an image, so I started up the AppleScript Editor :).
I created the following script, which works on a "Test Album" I created:
tell application "Photos"
set source_album_name to "Test Album"
set target_album_name to "Screenshots"
set target_width to 640
set target_height to 1136
if not (exists container named target_album_name) then
make new album named target_album_name
end if
set target_album to container target_album_name
set source_album to container source_album_name
set imageList to {}
repeat with photo in media items in source_album
if width of photo = target_width and height of photo = target_height then
set the end of imageList to photo
end if
end repeat
add imageList to target_album
end tell
This script loops through an Album named Test Album and compares the height and width to the dimensions of an iPhone 5S. When they match, it adds the photo to the Screenshots library. No problems there.
Now I want to run this script on my entire photo collection, so I changed the line repeat with photo in media items in source_album to repeat with photo in every media item.
This generates an error once it is past the first item (Photos got an error: Can’t get item 2 of every media item. Invalid index).
After that I changed the code to:
set all_images to every media item
repeat with photo in all_images
but after loading for a while, the script exits with code -10000, probably because of the amount of photos in that library (27.000).
Is there some way to page through a set like this?
EDIT: Changing the set line to contain a better query has the same effect, resulting in an AppleEvent handler failed with error number -10000.
set all_images to every media item whose width = target_width and height = target_height
The -10000 error is due to a bug in Photos 1.0. I've filed this with Apple as radar 20626449 (on OpenRadar at http://openradar.appspot.com/radar?id=6090497735000064). The error is intermittent, but the more photos there are in the library, the more likely it is to occur on any given scripting command.
There is no totally reliable way around the error, but one thing that seems to help is if the "All Photos" album is selected in Photos before starting your script. Unfortunately the Photos AppleScript suite doesn't have the ability to select an album, so you'll need to do this manually before starting the script.
Not sure if you mind dropping into the shell in Terminal, or if you can make that work with iPhoto, but if that is ok, this may get you started on finding all files in your HOME directory and below, that are PNGs and that match your sizes...
#!/bin/bash
isIphone() {
# Get width using sips, don't bother getting height if not 640 wide
w=$(sips -g pixelWidth "$1" | awk 'END{print $2}')
[ "$w" != "640" ] && return
# Get height using sips, just return if not iPhone size
h=$(sips -g pixelHeight "$1" | awk 'END{print $2}')
[ $h != "1136" ] && return
# Output image size and name
echo $w,$h, $1
}
# Go through all PNG files in HOME directory listing iPhone-sized ones
find ~ -iname "*.png" -type f -print0 | while read -d $'\0' -r file ; do isIphone "$file"; done
Output
640,1136, ./iPhone/2013 09 iPhone/IMG_0269.PNG
640,1136, ./iPhone/2013 09 iPhone/IMG_0363.PNG
640,1136, ./iPhone/2013 09 iPhone/IMG_0376.PNG
I have six folders like this >> Images
and each folder contains some images. I know how to read images in matlab BUT my question is how I can traverse through these folders and read images in abc.m file (this file is shown in this image)
So basically you want to read images in different folders without putting all of the images into one folder and using imread()? Because you could just copy all of the images (and name them in a way that lets you know which folder they came from) into a your MATLAB working directory and then load them that way.
Use the cd command to change directories (like in *nix) and then load/read the images as you traverse through each folder. You might need absolute path names.
The easiest way is certainly a right clic on the forlder in matlab and "Add to Path" >> "Selected Folders and Subfolders"
Then you can just get images with imread without specifying the path.
if you know the path to the image containing directory, you can use dir on it to list all the files (and directories) in it. Filter the files with the image extension you want and voila, you have an array with all the images in the directory you specified:
dirname = 'images';
ext = '.jpg';
sDir= dir( fullfile(dirname ,['*' ext]) );;
sDir([sDir.isdir])=[]; % remove directories
% following is obsolete because wildcarded dir ^^
b=arrayfun(#(x) strcmpi(x.name(end-length(ext)+1:end),ext),sDir); % filter on extension
sFiles = sDir(b);
You probably want to prefix the name of each file with the directory before using:
sFileName(ii) = fullfile(dirname, sFiles(ii));
You can process this resulting files as you want. Loading all the files for example:
for ii=1:numel(sFiles)
data{i}=imread(sFiles(ii).name)
end
If you also want to recurse the subdirectories, I suggest you take a look at:
How to get all files under a specific directory in MATLAB?
or other solutions on the FEX:
http://www.mathworks.com/matlabcentral/fileexchange/8682-dirr-find-files-recursively-filtering-name-date-or-bytes
http://www.mathworks.com/matlabcentral/fileexchange/15505-recursive-dir
EDIT: added Amro's suggestion of wildcarding the dir call
How to set icon for bundle which is not an app? I tried using CFBundleIconFile, but it doesn't work (though if I just change bundle extension to .app, icon is changed to desired one). Is there another key, or the only way is to set icon for directory? If so, is there already some script to do this from command line (Xcode run script)?
If you need to do it from CLI... It's a bit more involved...
First, you need to add a CFBundleIconFile string to your bundle's
YourThing.bundle/Contents/Info.plist
Here's where the developer gets to
specify a custom icon for the bundle.
This key contains the name of a file
in the bundle's Resources folder that
holds the icons. TextEdit keeps its
icon in a file called Edit.icns file,
but there's no rule about what the
name of the file must be.
That said, you either need an ICNS file, or can follow these instructions from this Utility (which includes its source code) that generates ICNS's from image files via the command line..
$ ./makeicns
Usage: makeicns [k1=v1] [k2=v2] ...
Keys and values include:
512: Name of input image for 512x512 variant of icon
256: Name of input image for 256x256 variant of icon
128: Name of input image for 128x128 variant of icon
32: Name of input image for 32x32 variant of icon
16: Name of input image for 16x16 variant of icon
in: Name of input image for all variants not having an explicit name
out: Name of output file, defaults to first nonempty input name,
but with icns extension
Examples:
makeicns -512 image.png -32 image.png
Creates image.icns with only a 512x512 and a 32x32 variant.
makeicns -in myfile.jpg -32 otherfile.png -out outfile.icns
Creates outfile.icns with sizes 512,
256, 128, and 16 containing data
from myfile.jpg and with size 32 containing data from otherfile.png.
Answer from similar (duplicate) question:
[[NSWorkspace sharedWorkspace]
setIcon:(NSImage *)image
forFile:(NSString *)bundlePath
options:0];
I have an application that I've bundled into a Mac OS X app bundle. Everything is working fine, but I want to change its icon from the default. How do I set its icon? Thanks.
in your info.plist add
<key>CFBundleIconFile</key>
<string>iconfile</string>
with icon file iconfile.icns in your Resources directory
I made a small script that takes a big image and resizes it to all expected icon sizes for Mac OS, including the double ones for retina displays. It takes the original png file, which I expect to be as big as the maximum size, if not bigger, to make sure they are rendered at maximum quality.
It resizes and copies them to a icon set, and uses the Mac OS's 'iconutil' tool to join them into a .icns file.
For this script to run, you need your original icon file to be a png, and you have your bundle in more or less working order. You only need to touch the first three lines.
export PROJECT=Myproject
export ICONDIR=$PROJECT.app/Contents/Resources/$PROJECT.iconset
export ORIGICON=Mybigfile.png
mkdir $ICONDIR
# Normal screen icons
for SIZE in 16 32 64 128 256 512; do
sips -z $SIZE $SIZE $ORIGICON --out $ICONDIR/icon_${SIZE}x${SIZE}.png ;
done
# Retina display icons
for SIZE in 32 64 256 512; do
sips -z $SIZE $SIZE $ORIGICON --out $ICONDIR/icon_$(expr $SIZE / 2)x$(expr $SIZE / 2)x2.png ;
done
# Make a multi-resolution Icon
iconutil -c icns -o $PROJECT.app/Contents/Resources/$PROJECT.icns $ICONDIR
rm -rf $ICONDIR #it is useless now
If you came here because you have a single app and want to change the image on your computer only (not sure how it works for sharing), there are much easier ways. In particular, here are two options I have used:
If you want to copy an existing icon:
Select the source item and press Cmd-I (Apple-I)
Select the item you want to change and press Cmd-I (Apple-I)
Drag the icon from the source to the top left icon of the one you want to change (the example image shows the target icon: it is the 'folder' icon to the left of the words "bird_id 2"):
Create a .icns file from any image. If you use MacPorts, I recommend instead using the port makeicns - see below for more info. You can alternatively do this using an app such as http://www.img2icnsapp.com/ as recommended at https://discussions.apple.com/thread/2773825.
makeicns v1.4.10 (284bd686824f)
Usage: makeicns [k1=v1] [k2=v2] ...
Keys and values include:
512: Name of input image for 512x512 variant of icon
256: Name of input image for 256x256 variant of icon
128: Name of input image for 128x128 variant of icon
32: Name of input image for 32x32 variant of icon
16: Name of input image for 16x16 variant of icon
in: Name of input image for all variants not having an explicit name
out: Name of output file, defaults to first nonempty input name,
but with icns extension
align: [center, left, right, top, bottom] {First letter suffices!}
Examples:
makeicns -512 image.png -32 image.png
Creates image.icns with only a 512x512 and a 32x32 variant.
makeicns -in myfile.jpg -32 otherfile.png -out outfile.icns
Creates outfile.icns with sizes 512, 256, 128, and 16 containing data
from myfile.jpg and with size 32 containing data from otherfile.png.