0

I am looking for a command-line tool on macOS that can write an icon file for a specified document or file to disk. qlmanage can either create a thumbnail, or a preview, but in my case I would like to get the icon file.

The man page mentions the icon mode, but I can't find any combination to make it work. Does anyone know how this works or has an alternative?

% qlmanage -h
Usage: qlmanage [OPTIONS] path...
    [...]
    -i      Compute thumbnail in icon mode

Thanks!

Daniel Stephens
  • 2,371
  • 8
  • 34
  • 86

1 Answers1

1

I'm not aware of any utility which does this, but you can easily write your own with the help of https://github.com/apple/swift-argument-parser package and NSWorkspace:

The following sample code utilizes the first method.

Sample project

  • Xcode - New project
  • Select macOS & Command line utility
  • Add https://github.com/apple/swift-argument-parser Swift package
  • Copy & paste the code below
import AppKit
import ArgumentParser

final class StderrOutputStream: TextOutputStream {
    func write(_ string: String) {
        FileHandle.standardError.write(Data(string.utf8))
    }
}

struct Icon: ParsableCommand {
    static var configuration = CommandConfiguration(
        commandName: "icon",
        abstract: "Get the workspace icon and save it as a PNG image.",
        version: "0.0.1"
    )
    
    @Argument(help: "Path to a file.")
    var input: String
    
    @Argument(help: "Output file.")
    var output: String
    
    @Option(name: .shortAndLong, help: "Icon size.")
    var size: Int = 512
    
    mutating func run() throws {
        if !FileManager.default.fileExists(atPath: input) {
            var stderr = StderrOutputStream()
            print("File not found: \(input)", to: &stderr)
            throw ExitCode(-1)
        }
        
        // Get NSImage with multiple representations (sizes)
        let icon = NSWorkspace.shared.icon(forFile: input)
        
        // Propose some size to get as accurate as possible representation size
        var proposedRect = NSRect(x: 0, y: 0, width: size, height: size)
        
        guard let cgRef = icon.cgImage(forProposedRect: &proposedRect, context: nil, hints: nil) else {
            var stderr = StderrOutputStream()
            print("Internal error: unable to get CGImage from NSImage", to: &stderr)
            throw ExitCode(-2)
        }
        
        do {
            // Get PNG representation
            let data = NSBitmapImageRep(cgImage: cgRef).representation(using: .png, properties: [:])
            // Save it to a file
            try data?.write(to: URL(fileURLWithPath: output), options: .atomic)
        }
        catch {
            var stderr = StderrOutputStream()
            print("Failed to save icon: \(error)", to: &stderr)
            throw ExitCode(-3)
        }
        
        print("Icon (\(size)x\(size)) saved to: \(output)")
    }
}

Icon.main()
zrzka
  • 20,249
  • 5
  • 47
  • 73