Created
August 7, 2015 15:28
-
-
Save bnickel/5f24e6df2e290bef0ddb to your computer and use it in GitHub Desktop.
Walgreens photo uploaded would only let me crop, but I had a bunch of square photos, so...
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/swift | |
// Because sometimes you don't want to crop. | |
// USAGE: aspect-grow RATIO FILES... | |
// aspect-grow 4:6 *.jpg | |
// | |
// Places all files in an output directory relative to their location, so | |
// aspect-grow 4:6 *.jpg creates a folder "output" in this directory but | |
// aspect-grow 4:6 a/b.jpg and b/c.jpg creates folders a/output and b/output. | |
import Cocoa | |
struct AspectRatio : CustomStringConvertible { | |
var width:CGFloat | |
var height:CGFloat | |
init?(width:CGFloat, height:CGFloat) { | |
if width <= 0 || height <= 0 { | |
return nil | |
} | |
self.width = width | |
self.height = height | |
} | |
init?(_ string:String) { | |
let parts = string.componentsSeparatedByString(":") | |
if parts.count != 2 { | |
return nil | |
} | |
guard let width = Double(parts[0]), height = Double(parts[1]) else { | |
return nil | |
} | |
self.init(width: CGFloat(width), height: CGFloat(height)) | |
} | |
var description:String { | |
return "\(width):\(height)" | |
} | |
func rotated() -> AspectRatio { | |
return AspectRatio(width: height, height: width)! | |
} | |
} | |
extension CGSize { | |
mutating func grow(aspectRatio:AspectRatio) { | |
if width < height * aspectRatio.width / aspectRatio.height { | |
width = height * aspectRatio.width / aspectRatio.height | |
} else { | |
height = width * aspectRatio.height / aspectRatio.width | |
} | |
} | |
func grown(aspectRatio:AspectRatio) -> CGSize { | |
var size = self | |
size.grow(aspectRatio) | |
return size | |
} | |
var diagonal:CGFloat { | |
return sqrt(width * width + height * height) | |
} | |
func rotated() -> CGSize { | |
return CGSize(width: height, height: width) | |
} | |
} | |
func bestSizeForImageAtPath(path:String, aspectRatio:AspectRatio) -> CGSize? { | |
guard let size = NSImage(contentsOfFile: path)?.size else { | |
return nil | |
} | |
var normalSize = size.grown(aspectRatio) | |
var rotatedSize = size.grown(aspectRatio.rotated()) | |
if normalSize.diagonal < rotatedSize.diagonal { | |
return normalSize | |
} else { | |
return rotatedSize | |
} | |
} | |
func resizeImageAtPath(path:String, aspectRatio:AspectRatio) { | |
guard let newSize = bestSizeForImageAtPath(path, aspectRatio: aspectRatio) else { | |
preconditionFailure("Could not determine aspect ratio for file at \(path)") | |
} | |
do { | |
try NSFileManager().createDirectoryAtPath(path.stringByDeletingLastPathComponent.stringByAppendingPathComponent("output"), withIntermediateDirectories: false, attributes: nil) | |
} catch { | |
} | |
let task = NSTask() | |
task.launchPath = "/usr/local/bin/convert" | |
task.arguments = [ | |
"-size", "\(Int(newSize.width))x\(Int(newSize.height))", | |
"xc:white", | |
path, | |
"-gravity", "center", | |
"-composite", | |
path.stringByDeletingLastPathComponent.stringByAppendingPathComponent("output").stringByAppendingPathComponent(path.lastPathComponent) | |
] | |
task.launch() | |
task.waitUntilExit() | |
if task.terminationStatus != 0 { | |
preconditionFailure("ImageMagick failed with arguments: \(task.arguments!)") | |
} | |
} | |
let args = Process.arguments | |
precondition(args.count >= 3, "USAGE: aspect-grow RATIO FILES...") | |
guard let aspectRatio = AspectRatio(args[1]) else { | |
preconditionFailure("Aspect ratio must be of format A:B, e.g. 4:6") | |
} | |
for path in args[2..<args.endIndex] { | |
resizeImageAtPath(path, aspectRatio: aspectRatio) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment