Как вы извлекаете изображения из PDF с помощью Swift?
Я понимаю PDFKit
позволяет извлечь текст + форматирование как NSAttributedString
, но я не могу найти какую-либо информацию по извлечению каждого отдельного рисунка из любого документа PDF с помощью Swift.
Любая помощь будет принята с благодарностью, спасибо!
edit: /questions/47045519/konvertirovat-pdf-v-uiimage/47045549#47045549 объясняет, как преобразовать всю страницу в изображение, однако мне нужно проанализировать все изображения, уже являющиеся частью серии PDF-документов, не зная, где они расположены, поэтому это решение не соответствует моему вопросу.
0 ответов
Вот функция Swift, которая извлекает изображения, а точнее все объекты с подтипом "Image", со страниц pdf:
import PDFKit
func extractImages(from pdf: PDFDocument, extractor: @escaping (ImageInfo)->Void) throws {
for pageNumber in 0..<pdf.pageCount {
guard let page = pdf.page(at: pageNumber) else {
throw PDFReadError.couldNotOpenPageNumber(pageNumber)
}
try extractImages(from: page, extractor: extractor)
}
}
func extractImages(from page: PDFPage, extractor: @escaping (ImageInfo)->Void) throws {
let pageNumber = page.label ?? "unknown page"
guard let page = page.pageRef else {
throw PDFReadError.couldNotOpenPage(pageNumber)
}
guard let dictionary = page.dictionary else {
throw PDFReadError.couldNotOpenDictionaryOfPage(pageNumber)
}
guard let resources = dictionary[CGPDFDictionaryGetDictionary, "Resources"] else {
throw PDFReadError.couldNotReadResources(pageNumber)
}
if let xObject = resources[CGPDFDictionaryGetDictionary, "XObject"] {
print("reading resources of page", pageNumber)
func extractImage(key: UnsafePointer<Int8>, object: CGPDFObjectRef, info: UnsafeMutableRawPointer?) -> Bool {
guard let stream: CGPDFStreamRef = object[CGPDFObjectGetValue, .stream] else { return true }
guard let dictionary = CGPDFStreamGetDictionary(stream) else {return true}
guard dictionary.getName("Subtype", CGPDFDictionaryGetName) == "Image" else {return true}
let colorSpaces = dictionary.getNameArray(for: "ColorSpace") ?? []
let filter = dictionary.getNameArray(for: "Filter") ?? []
var format = CGPDFDataFormat.raw
guard let data = CGPDFStreamCopyData(stream, &format) as Data? else { return false }
extractor(
ImageInfo(
name: String(cString: key),
colorSpaces: colorSpaces,
filter: filter,
format: format,
data: data
)
)
return true
}
CGPDFDictionaryApplyBlock(xObject, extractImage, nil)
}
}
struct ImageInfo: CustomDebugStringConvertible {
let name: String
let colorSpaces: [String]
let filter: [String]
let format: CGPDFDataFormat
let data: Data
var debugDescription: String {
"""
Image "\(name)"
- color spaces: \(colorSpaces)
- format: \(format == .JPEG2000 ? "JPEG2000" : format == .jpegEncoded ? "jpeg" : "raw")
- filters: \(filter)
- size: \(ByteCountFormatter.string(fromByteCount: Int64(data.count), countStyle: .binary))
"""
}
}
extension CGPDFObjectRef {
func getName<K>(_ key: K, _ getter: (OpaquePointer, K, UnsafeMutablePointer<UnsafePointer<Int8>?>)->Bool) -> String? {
guard let pointer = self[getter, key] else { return nil }
return String(cString: pointer)
}
func getName<K>(_ key: K, _ getter: (OpaquePointer, K, UnsafeMutableRawPointer?)->Bool) -> String? {
guard let pointer: UnsafePointer<UInt8> = self[getter, key] else { return nil }
return String(cString: pointer)
}
subscript<R, K>(_ getter: (OpaquePointer, K, UnsafeMutablePointer<R?>)->Bool, _ key: K) -> R? {
var result: R!
guard getter(self, key, &result) else { return nil }
return result
}
subscript<R, K>(_ getter: (OpaquePointer, K, UnsafeMutableRawPointer?)->Bool, _ key: K) -> R? {
var result: R!
guard getter(self, key, &result) else { return nil }
return result
}
func getNameArray(for key: String) -> [String]? {
var object: CGPDFObjectRef!
guard CGPDFDictionaryGetObject(self, key, &object) else { return nil }
if let name = object.getName(.name, CGPDFObjectGetValue) {
return [name]
} else {
guard let array: CGPDFArrayRef = object[CGPDFObjectGetValue, .array] else {return nil}
var names = [String]()
for index in 0..<CGPDFArrayGetCount(array) {
guard let name = array.getName(index, CGPDFArrayGetName) else { continue }
names.append(name)
}
return names
}
}
}
enum PDFReadError: Error {
case couldNotOpenPageNumber(Int)
case couldNotOpenPage(String)
case couldNotOpenDictionaryOfPage(String)
case couldNotReadResources(String)
case cannotReadXObjectStream(xObject: String, page: String)
}
Вы должны знать, что изображения в PDF-файлах могут быть представлены по-разному. Они могут быть встроены как автономные файлы JPG или они могут быть встроены как необработанные данные пикселей (со сжатием без потерь или без) с метаинформацией о сжатии, цветовом пространстве, ширине, высоте и т. Д.
Поэтому, если вы хотите экспортировать встроенные файлы JPG: этот код отлично работает. Но если вы также хотите визуализировать необработанные изображения, вам понадобится еще больше кода синтаксического анализа. Для начала вы можете взглянуть на спецификацию PDF 2.0 (или более старую бесплатную версию спецификации) и эту суть, которая интерпретирует JPG в любом цветовом профиле и необработанных изображениях с любым из следующих цветовых профилей:
- УстройствоСерый
- Устройство RGB
- Устройство CMYK
- Проиндексировано
- ICCBased