Preview can scale without proportion if you want to use ui scripting.
That all makes sense. Have to look into scripting Preview.
Odd that Image Events lacks that ability.
Thanks a bunch!
Carl
It looks like you can do it with NSImage also from Cocoa-AppleScript.
And possibly with “sips” in shell script.
So far sips looks the most promising. Having a hard time understanding how to make it resize a file
in one directory and save it with a new name in another directory.
Thanks,
Carl
Yes, although it’s a bit more complex than cropping. You basically make a new bitmap of the required size (using one of the longest Cocoa methods there is), and draw from the original bitmap into it. This is untested, but should work:
script scalePic
set {thisFile, exportFile, theWidth, theHeight} to current application's NSApp's passedValue() as list
set theImage to current application's NSImage's alloc()'s initWithContentsOfFile_(thisFile)
set {width:x1, height:y1} to theImage's |size|() -- get size
-- make new bitmap rep
set newRep to current application's NSBitmapImageRep's alloc()'s initWithBitmapDataPlanes_pixelsWide_pixelsHigh_bitsPerSample_samplesPerPixel_hasAlpha_isPlanar_colorSpaceName_bitmapFormat_bytesPerRow_bitsPerPixel_(missing value, theWidth, theHeight, 8, 4, true, false, current application's NSCalibratedRGBColorSpace, current application's NSAlphaFirstBitmapFormat, 0, 32)
current application's NSGraphicsContext's saveGraphicsState() -- save graphics context state
current application's NSGraphicsContext's setCurrentContext_(current application's NSGraphicsContext's graphicsContextWithBitmapImageRep_(newRep)) -- set current graphics context to new image rep
theImage's drawInRect_fromRect_operation_fraction_({origin:{x:0, y:0}, |size|:{width:theWidth, height:theHeight}}, current application's NSZeroRect, current application's NSCompositeSourceOver, 1.0) -- draw from old rep into graphics context
current application's NSGraphicsContext's restoreGraphicsState() -- restore graphics state
set theData to newRep's representationUsingType_properties_(current application's NSJPEGFileType, {NSImageCompressionFactor:0.9})
theData's writeToFile_atomically_(exportFile, true)
end script
set thisFile to POSIX path of "Macintosh HD:Users:TL:desktop:Ski Images:whitefishT.jpg"
set exportFile to POSIX path of "Macintosh HD:Users:TL:desktop:Ski Images:whitefish.jpg"
set theWidth to 460
set theHeight to 300
tell application "ASObjC Runner" to run the script {scalePic} passing {thisFile, exportFile, theWidth, theHeight}
Hi Shane,
I didn’t understand what the representation is about, so I just resized the image which seems to work. Don’t know if something is wrong with this.
script resizePic
set {thisFile, exportFile} to current application's NSApp's passedValue() as list
set theImage to current application's NSImage's alloc()'s initWithContentsOfFile_(thisFile)
set newSize to {width:460, height:300}
theImage's setSize_(newSize)
set theSize to (theImage's |size|()) as record
set theHeight to height of theSize
set theWidth to width of theSize
set newRect to {{x:0, y:0}, {width:theWidth, height:theHeight}}
theImage's lockFocus()
set theRep to current application's NSBitmapImageRep's alloc()'s initWithFocusedViewRect_(newRect)
theImage's unlockFocus()
set theData to theRep's representationUsingType_properties_(current application's NSJPEGFileType, {NSImageCompressionFactor:0.9})
theData's writeToFile_atomically_(exportFile, true)
end script
set thisFile to POSIX path of (choose file)
set exportFile to POSIX path of (choose file name)
tell application "ASObjC Runner" to run the script {resizePic} passing {thisFile, exportFile}
Hope it’s not wasting memory or something.
No, that’s fine. In fact the method I posted is overkill – but you’d use it if, say, you wanted to resize and crop at the same time. (In which case you’d use the rect you want to capture instead of NSZeroRect.)
I think I see it. In my script you can’t crop first then scale.
Thanks,
kel
I believe that will work perfectly. Problem I have is turning up a version of ASObjC Runner that will run in
10.5.8
Many thanks,
Carl
Alas, AppleScriptObjC was introduced in 10.6 – it’s not going to work with anything earlier.
Now I really must get upgraded ![]()
thanks,
Carl
That’s right. The other version is essentially saying take this rectangular part of the original and draw it in another rectangle on the new bitmap, squishing it size and shape. You can use the same technique to blend images, using different compositing operations.
This seems to work, assuming both directories already exist:
property targetW : "460"
property targetH : "300"
set fs to "/Library/Application Support/Perceptive Automation/Images/Sonos/Living Room_art.jpg"
set pngFile to "/Library/Application Support/Perceptive Automation/Indigo 6/IndigoWebServer/images/controls/variables/photo+sonos.png"
do shell script ("sips --setProperty format png --resampleHeightWidth " & targetH & space & targetW & space & quoted form of fs & " --out " & quoted form of pngFile)
Hi Nigel,
Great script. I was a little mixed up at first thinking that the file specification (fs) was the file that is being written to. You were just quoting the original file references.
kel
Hello.
I found This blogpost using Image Magick for resizing and sharpening images. I haven’t come around to use it yet, but here it is in case anybody is interested. You’ll have to install ImageMagick, from Macports, or elsewhere, as Image Magick isn’t included in OS X.
Twelve years later, none of these scripts are working for me and I have hundreds of images to resize.
Basically I want to scale proportionately, any suggestions?
sips is still supposed to work. It definitely works on Sequoia.
This is a script where you specify the larger value of width and height of the image and it will be resized preserving the aspect ratio. The script adds the size information to the file name of the resized image.
use AppleScript version "2.5"
use framework "Foundation"
use scripting additions
property targetWH : "480"
set theImagePath to POSIX path of (choose file of type "public.image")
set theURL to current application's NSURL's fileURLWithPath:theImagePath
set fileExtension to theURL's pathExtension() as text
set fileName to theURL's lastPathComponent() as text
set parentFolder to POSIX path of (theURL's URLByDeletingLastPathComponent() as text)
set destinationPath to parentFolder & text 1 thru -((count fileExtension) + 2) of fileName & "_" & targetWH & "." & fileExtension
do shell script "sips -Z " & targetWH & space & quoted form of theImagePath & " --out " & quoted form of destinationPath
If you’re dealing with files whose dimensions may fall below the destination size…
do shell script "sips -z " & targetWH & space & targetWH & space & quoted form of theImagePath & " --out " & quoted form of destinationPath
Is there an issue with image events? It strikes me as odd to call out to the shell for something that applescript provides natively.
I modified this script slightly. Basically, specify your source and destination folders. It will get all the matching files in the sources and save the scaled copies with a modified name to a destination folder. It should be easy enough to change those factors depending on the required workflow.
Important: As it can grab files from multiple folders, there may be name conflicts when saving. I’ve set it to skip any image that has a name of an already scaled image to prevent any overwriting.
As to the scaling… as written it takes the longest dimension of the image and scales that to 960 pixels. The opposing dimension will scale accordingly. Adjust as required.
use scripting additions
-- set up default location for 'choose folder' command
set defLoc to (path to pictures folder) -- customize as required
tell application "Finder"
-- specify destination folder, create as necessary
try
set repository to ((path to desktop as text) & "repository:") as alias -- destination
on error
set repository to (make new folder at (path to desktop) with properties {name:"repository"}) as alias
end try
-- specify source folders
set sourceFolders to (choose folder default location defLoc with prompt "pick some already" with multiple selections allowed) as alias list
-- set depot to a reference to selection as alias -- finder selection as alternate approach
end tell
-- get jpegs of source folder
tell application "System Events"
-- set sourceList to files of depot whose type identifier is "public.jpeg"
set depot to {}
repeat with folio in sourceFolders
set end of depot to (files of folio whose type identifier is "public.jpeg")
end repeat
end tell
-- flatten to simple list
set depot to flatten(depot)
-- cycle images
repeat with eachImage in depot
-- set up new name and path
tell application "System Events"
set nom to name of eachImage
set ext to name extension of eachImage
set AppleScript's text item delimiters to "." & ext
set baseName to text item 1 of nom
set oldPath to path of eachImage
set AppleScript's text item delimiters to ""
-- append 'scaled' to resulting file name
set newPathName to (repository & baseName & " scaled" & "." & ext) as text
end tell
-- scale images along longest dimension
try -- if file with that name already exists, will skip
alias newPathName
tell application "Image Events"
launch
set orig to open oldPath
scale orig to size 960
save orig in newPathName -- with icon
end tell
end try
end repeat
tell application "Finder"
activate
select repository
end tell
-- flatten depot lists
on flatten(listOfLists)
script o
property fl : {}
on flttn(l)
script p
property lol : l
end script
repeat with i from 1 to (count l)
set v to item i of p's lol
if (v's class is list) then
flttn(v)
else
set end of my fl to v
end if
end repeat
end flttn
end script
tell o
flttn(listOfLists)
return its fl
end tell
end flatten
NB I borrowed Nigel’s recursive list flattener.