Created
June 1, 2026 16:17
-
-
Save igm/0fa839653a8f9901bebeda0f3724f984 to your computer and use it in GitHub Desktop.
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
| import Foundation | |
| import CoreGraphics | |
| import ImageIO | |
| import UniformTypeIdentifiers | |
| // Usage: swift pdf2jpeg.swift <input.pdf> <outdir> <dpi> <quality> | |
| let args = CommandLine.arguments | |
| guard args.count >= 3 else { | |
| FileHandle.standardError.write("usage: pdf2jpeg <input.pdf> <outdir> [dpi] [quality]\n".data(using: .utf8)!) | |
| exit(1) | |
| } | |
| let inputPath = args[1] | |
| let outDir = args[2] | |
| let dpi: CGFloat = args.count > 3 ? CGFloat(Double(args[3]) ?? 150) : 150 | |
| let quality: CGFloat = args.count > 4 ? CGFloat(Double(args[4]) ?? 0.9) : 0.9 | |
| let scale = dpi / 72.0 | |
| let url = URL(fileURLWithPath: inputPath) | |
| guard let doc = CGPDFDocument(url as CFURL) else { | |
| FileHandle.standardError.write("error: could not open PDF\n".data(using: .utf8)!) | |
| exit(1) | |
| } | |
| try? FileManager.default.createDirectory(atPath: outDir, withIntermediateDirectories: true) | |
| let pageCount = doc.numberOfPages | |
| let pad = String(pageCount).count | |
| for i in 1...pageCount { | |
| guard let page = doc.page(at: i) else { continue } | |
| let box = page.getBoxRect(.cropBox) | |
| let w = Int((box.width * scale).rounded()) | |
| let h = Int((box.height * scale).rounded()) | |
| let cs = CGColorSpaceCreateDeviceRGB() | |
| guard let ctx = CGContext(data: nil, width: w, height: h, bitsPerComponent: 8, | |
| bytesPerRow: 0, space: cs, | |
| bitmapInfo: CGImageAlphaInfo.noneSkipLast.rawValue) else { continue } | |
| // white background | |
| ctx.setFillColor(red: 1, green: 1, blue: 1, alpha: 1) | |
| ctx.fill(CGRect(x: 0, y: 0, width: w, height: h)) | |
| ctx.scaleBy(x: scale, y: scale) | |
| ctx.translateBy(x: -box.origin.x, y: -box.origin.y) | |
| ctx.drawPDFPage(page) | |
| guard let image = ctx.makeImage() else { continue } | |
| let num = String(format: "%0\(pad)d", i) | |
| let outURL = URL(fileURLWithPath: outDir).appendingPathComponent("page-\(num).jpg") | |
| guard let dest = CGImageDestinationCreateWithURL(outURL as CFURL, UTType.jpeg.identifier as CFString, 1, nil) else { continue } | |
| let opts: [CFString: Any] = [kCGImageDestinationLossyCompressionQuality: quality] | |
| CGImageDestinationAddImage(dest, image, opts as CFDictionary) | |
| if CGImageDestinationFinalize(dest) { | |
| print("wrote \(outURL.path) (\(w)x\(h))") | |
| } else { | |
| FileHandle.standardError.write("failed to write page \(i)\n".data(using: .utf8)!) | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment