You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

macOS Swift生成PDF时如何缩放图片并保留画质?

问题:macOS Ventura下Swift脚本生成PDF时画质丢失

我有一个命令行Swift脚本,用于将一系列像素尺寸相近的图片生成PDF。脚本会将图片标准化,让每张图片作为PDF页面时拥有相同的英寸高宽(比如把扫描的6×9英寸文档的所有图片统一为该尺寸)。由于图片校正和页面老化,各图片像素差异可达3.5%,不标准化会出现留白。

以下是核心缩放逻辑代码,其中height和width为PDF文档期望的英寸高宽:

// 获取当前每英寸像素数
let imageRep = image.representations[0]
let originalWidth = CGFloat(imageRep.pixelsWide)
let originalHeight = CGFloat(imageRep.pixelsHigh)
let pixelsPerInch = originalWidth*72.0/imageRep.size.width

// 计算水平缩放比例
let inchesWide = originalWidth/pixelsPerInch
let scaleX = width/inchesWide

// 计算垂直缩放比例
let inchesHigh = originalHeight/pixelsPerInch
let scaleY = height/inchesHigh

// 标准化图片,使其添加到PDF时拥有正确的英寸尺寸
let pageSize = NSSize(width:image.size.width*scaleX, height:image.size.height*scaleY)
let normalizedImage = NSImage(size:pageSize, flipped:false) { (resizedRect) -> Bool in
    image.draw(in:resizedRect)
    return true
}

系统版本差异问题

  • 在macOS Monterey系统中,脚本可正常缩放尺寸且不改变底层图片质量:比如10.4MB的图片生成的PDF约为10.6MB,修改尺寸仅调整PDF显示尺寸,文件大小基本不变。
  • 升级到macOS 13.3.1(Ventura)后,缩放尺寸正确但画质大幅下降:同样10.4MB的图片生成的PDF仅3.5MB,文本几乎无法辨认。

临时方案的局限性

脚本现在会根据NSImage的缩放调整画质,而之前不会。目前尝试过两种临时方案,但都存在问题:

  • 增大尺寸可提升可读性,但PDF打开时尺寸过大;
  • 直接修改原图片的size属性,但图片不再标准化,会出现留白。

通过对比发现,代码创建的normalizedImage和从文件生成的image在设置.size时表现不同,推测问题与图片来源有关。需求是将每张扫描图片标准化为精确的英寸尺寸,同时保留原始图片的画质。

测试用精简脚本

// 从一系列图片创建PDF
import PDFKit

class PDFCreator {
    let images: [NSImage]
    var document = PDFDocument()

    init(images: [NSImage]) {
        self.images = images
    }

    public func create() {
        // 将图片添加到PDF
        for var image in self.images {
            print("添加…", image.name()!, image.size)
            image = self.normalizeImage(image: image)

            // 添加到PDF文档
            guard let page = PDFPage(image: image) else {
                print("无法添加", image.name()!, "到文档。")
                exit(0);
            }
            self.document.insert(page, at: self.document.pageCount)
        }

        // 保存PDF到文件
        print("保存PDF为", outputPath)
        let fileURL = NSURL.fileURL(withPath: outputPath)
        self.document.write(to: fileURL)
    }

    // 将图片调整为合适的英寸宽高,同时保留画质
    private func normalizeImage(image: NSImage) -> NSImage {
        if width <= 0 || height <= 0 { return image }

        // 获取当前每英寸像素数
        let imageRep = image.representations[0]
        let originalWidth = CGFloat(imageRep.pixelsWide)
        let originalHeight = CGFloat(imageRep.pixelsHigh)
        let pixelsPerInch = originalWidth*72.0/imageRep.size.width

        // 计算水平缩放比例
        let inchesWide = originalWidth/pixelsPerInch
        let scaleX = width/inchesWide
    
        // 计算垂直缩放比例
        let inchesHigh = originalHeight/pixelsPerInch
        let scaleY = height/inchesHigh

        // 标准化图片,使其添加到PDF时拥有正确的英寸尺寸
        let pageSize = NSSize(width:image.size.width*scaleX, height:image.size.height*scaleY)
        let normalizedImage = NSImage(size:pageSize, flipped:false) { (resizedRect) -> Bool in
            image.draw(in:resizedRect)
            return true
        }

        return normalizedImage
    }
}

// NSImage无法自行解析别名
func resolvePath(path: String) -> URL {
    do {
        let file = NSURL.fileURL(withPath: path)
        return try URL(resolvingAliasFileAt: file)
    } catch {
        print("无法找到文件", path)
        exit(0)
    }
}

// 设置默认值并读取图片
let outputPath = "test.pdf"
var pages:[NSImage] = []
let width:CGFloat = 5.625
let height:CGFloat = 8.125
for file in CommandLine.arguments[1...] {
    let resolvedPath = resolvePath(path:file)
    let image = NSImage(byReferencing: resolvedPath)
    image.setName(resolvedPath.lastPathComponent)
    pages.append(image)
}
if pages == [] { print("至少需要一张图片。");exit(0) }

// 创建PDF
let pdf = PDFCreator(images: pages)
pdf.create()

内容的提问来源于stack exchange,提问作者Jerry Stratton

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 12:37:41