读不了的 PDF:没装 poppler、文本层还是空的,用 macOS 自带 Swift PDFKit 渲染
要从一份中文简历 PDF 里读项目经历——结果读取工具直接罢工,连环踩了一串「装不上 / 提不出」的坑,最后用 macOS 自带的能力一招解决。
现象
pdftoppm is not installed. Install poppler-utils (e.g. brew install poppler ...)
并且一路 fallback 全军覆没:
pdftotext/pdftoppm——没装(poppler),装它太重- 系统
python3配pypdf/PyPDF2——No module named - 系统
python3配Quartz(PyObjC)——No module named 'Quartz' osascript -l JavaScript走 PDFKit 的 ObjC bridge——静默返回空- Swift PDFKit 的
doc.string——打印出页数了,但文本是空的
坑:很多中文/扫描 PDF 根本没有可提取的文本层
中文简历、扫描件、设计稿导出的 PDF,常用子集字体且不带 ToUnicode 映射——doc.string 拿到的是空或乱码。这类 PDF 你再怎么折腾文本提取都没用,只能把页面当图片来看。
根因
两件事叠一起:① 渲染工具(poppler)没装;② 就算绕过渲染去抽文本,这份 PDF 压根没有干净的文本层。所以正确路线不是「修文本提取」,而是「把页面光栅化成图片,再用视觉理解去读」。
而光栅化根本不需要 poppler——macOS 自带的 PDFKit + AppKit 就能干,swift 还能直接跑脚本文件,零安装。
正解
用一段几十行的 Swift 把每页渲染成 PNG,再把 PNG 当图片读:
// /tmp/pdf2png.swift
import Foundation
import PDFKit
import AppKit
let p = CommandLine.arguments[1]
let outDir = CommandLine.arguments[2]
guard let doc = PDFDocument(url: URL(fileURLWithPath: p)) else { print("DOC_NONE"); exit(1) }
let scale: CGFloat = 2.0 // 2x,让小字在 PNG 里看得清
for i in 0..<doc.pageCount {
guard let page = doc.page(at: i) else { continue }
let b = page.bounds(for: .mediaBox)
let img = NSImage(size: NSSize(width: b.width*scale, height: b.height*scale))
img.lockFocus()
NSColor.white.set() // PDF 页是透明的,先铺白底
NSRect(x:0,y:0,width:b.width*scale,height:b.height*scale).fill()
let ctx = NSGraphicsContext.current!.cgContext
ctx.saveGState(); ctx.scaleBy(x: scale, y: scale)
page.draw(with: .mediaBox, to: ctx)
ctx.restoreGState()
img.unlockFocus()
guard let tiff = img.tiffRepresentation, let rep = NSBitmapImageRep(data: tiff),
let png = rep.representation(using: .png, properties: [:]) else { continue }
try! png.write(to: URL(fileURLWithPath: "\(outDir)/page-\(i+1).png"))
print("\(outDir)/page-\(i+1).png")
}mkdir -p /tmp/pdf-pages
swift /tmp/pdf2png.swift "/abs/path/file.pdf" /tmp/pdf-pages然后逐张读 /tmp/pdf-pages/page-N.png——视觉理解能直接吃下中文文字和版面,哪怕没有文本层。
正解:Swift + PDFKit 渲染 PNG,零安装,绕开文本层缺失
swift 打印出每页的 PNG 路径;读这些 PNG 能拿到连贯的页面内容。小字看不清就把 scale 提到 3.0~4.0。
几个细节
swift直接跑脚本文件,不用建 Xcode 工程,前提是装了 Command Line Tools;PDFKit / AppKit 都是系统框架。- 白底要铺:PDF 页面透明,不铺白底,深色主题下渲出来的图会很难辨认。
- 如果文本层其实存在、你只要文字,
doc.string(Swift)比渲染快——但本文这个触发场景它是空的,直接上 PNG。
一句话外卖
读不了的 PDF 别死磕装 poppler 或抽文本——macOS 自带 Swift + PDFKit 把页面渲成 PNG,再当图片视觉读,对没有文本层的中文 PDF 尤其有效。