如何在iOS上编程生成PDF的缩略图?

14

目前我们正在使用 UIWebViews 显示 PDF 内容。理想情况下,我希望能够在 UITableView 中显示缩略图,而不必同时加载许多不同的 UIWebViews,因为它们即使加载一个文档也会很慢,更不用说10个以上了!

我该如何做到这一点?

我考虑过使用 UIDocumentInteractionControllerUIWebView 加载文档并进行屏幕截图来实现,但这意味着在显示表格之前,它们都必须被制作成缩略图。

4个回答

13

CoreGraphics级别,苹果提供了大量绘制PDF内容的方法。据我所知,它们中没有一个被整齐地打包到UIKit级别,因此如果您不太熟悉C层面的操作,这可能不适合您的项目,特别是在这个阶段。然而,相关函数是CGContextDrawPDFPage;根据一般的CoreGraphics方式,还有其他方法可以从数据源创建PDF引用,然后从PDF获取页面引用。然后,您需要处理缩放和翻译到所需的视图,并警告您需要进行水平翻转,因为PDF(和OS X)使用左下角作为原点,而iOS使用左上角。以下是我能想到的示例代码:

UIGraphicsBeginImageContext(thumbnailSize);
CGPDFDocumentRef pdfRef = CGPDFDocumentCreateWithProvider( (CGDataProviderRef)instanceOfNSDataWithPDFInside );
CGPDFPageRef pageRef = CGPDFDocumentGetPage(pdfRef, 1); // get the first page

CGContextRef contextRef = UIGraphicsGetCurrentContext();

// ignore issues of transforming here; depends on exactly what you want and
// involves a whole bunch of normal CoreGraphics stuff that is nothing to do
// with PDFs
CGContextDrawPDFPage(contextRef, pageRef);

UIImage *imageToReturn = UIGraphicsGetImageFromCurrentImageContext();

// clean up
UIGraphicsEndImageContext();
CGPDFDocumentRelease(pdfRef);

return imageToReturn;

猜测你可能需要使用 CGPDFPageGetBoxRect(pageRef, kCGPDFCropBox) 方法获取页面的裁剪框,然后计算如何将其缩放/移动以适应你的图像大小。

对于你的目的来说,可能更容易的方法是使用 CALayer 上的 -renderInContext: 方法(参见 QA 1703)- 获取截图的旧方法是 UIGetScreenImage,但那实际上从未是正式的 API,似乎只有因为 RedLaser 的意外批准而被临时允许。使用 QA 中的代码可以在不必在屏幕上显示该视图的情况下从任何其他视图中获取 UIImage。这可能解决了一些关于屏幕截图的问题?但这意味着你只能支持 OS 4.x。

无论哪种情况,PDF 文件绘制速度都不快。你可能需要在后台线程上填充表格,然后绘制缩略图,当它们可用时将它们向上推动。你不能在后台线程上安全地使用 UIKit 对象,但所有的 CoreGraphics 都是安全的。


13

这里是一个考虑变换的示例代码。:)

NSURL* pdfFileUrl = [NSURL fileURLWithPath:finalPath];
CGPDFDocumentRef pdf = CGPDFDocumentCreateWithURL((CFURLRef)pdfFileUrl);
CGPDFPageRef page;

CGRect aRect = CGRectMake(0, 0, 70, 100); // thumbnail size
UIGraphicsBeginImageContext(aRect.size);
CGContextRef context = UIGraphicsGetCurrentContext();
UIImage* thumbnailImage;


NSUInteger totalNum = CGPDFDocumentGetNumberOfPages(pdf);

for(int i = 0; i < totalNum; i++ ) {


    CGContextSaveGState(context);
    CGContextTranslateCTM(context, 0.0, aRect.size.height);
    CGContextScaleCTM(context, 1.0, -1.0);

    CGContextSetGrayFillColor(context, 1.0, 1.0);
    CGContextFillRect(context, aRect);


    // Grab the first PDF page
    page = CGPDFDocumentGetPage(pdf, i + 1);
    CGAffineTransform pdfTransform = CGPDFPageGetDrawingTransform(page, kCGPDFMediaBox, aRect, 0, true);
    // And apply the transform.
    CGContextConcatCTM(context, pdfTransform);

    CGContextDrawPDFPage(context, page);

    // Create the new UIImage from the context
    thumbnailImage = UIGraphicsGetImageFromCurrentImageContext();

    //Use thumbnailImage (e.g. drawing, saving it to a file, etc)

    CGContextRestoreGState(context);

}


UIGraphicsEndImageContext();    
CGPDFDocumentRelease(pdf);

嗨,Alones。我正在使用你的代码获取PDF缩略图。似乎CGContextConcatCTM(context, pdfTransform)这行代码会产生内存瓶颈,如果我不加上这行代码,那么就没问题了。但是我不能不加上,因为我需要它来生成正确的缩略图。你有什么想法可以改善这个问题吗?顺便说一句,谢谢。 - aslı
1
这个对我有效...上面那个我不知道为什么会生成一个虚假或垃圾缩略图...谢谢... - Underdog
文档说 UIGraphicsGetImageFromCurrentImageContext() 不是线程安全的。 - Luke Mcneice
这个代码是有效的。被接受的答案会生成一些空白图片。 - jjpp

2
这里是生成PDF页面缩略图的Swift 3方法:
static func getThumbnailForPDF(_ urlString:String, pageNumber:Int) -> UIImage? {
    let bundle = Bundle.main
    let path = bundle.path(forResource: urlString, ofType: "pdf")
    let pdfURL = URL(fileURLWithPath: path!)

    let pdf = CGPDFDocument(pdfURL as CFURL )!
    let page = pdf.page(at: pageNumber)!
    let rect = CGRect(x: 0, y: 0, width: 100.0, height: 70.0) //Image size here

    UIGraphicsBeginImageContext(rect.size)
    let context = UIGraphicsGetCurrentContext()!

    context.saveGState()
    context.translateBy(x: 0, y: rect.height)
    context.scaleBy(x: 1.0, y: -1.0)
    context.setFillColor(gray: 1.0, alpha: 1.0)
    context.fill(rect)


    let pdfTransform = page.getDrawingTransform(CGPDFBox.mediaBox, rect: rect, rotate: 0, preserveAspectRatio: true)
    context.concatenate(pdfTransform)
    context.drawPDFPage(page)

    let thumbImage = UIGraphicsGetImageFromCurrentImageContext()
    context.restoreGState()

    UIGraphicsEndImageContext()

    return thumbImage
}

嗨,Emil,你来晚了大约7年——但还是感谢你为那些遇到同样问题的人保持这个问题的活跃度;-) - Jamie Chapman

1
如果您正在使用PDFKit,那么PDFPage类中有一个名为thumbnail的方法可供使用。该方法适用于iOS11及以上版本,并且只需编写几行代码即可完成工作。下面是一个简单的方法,用于生成给定页面在PDFDocument中的缩略图。
func makeThumbnail(pdfDocument: PDFDocument?, page: Int) -> UIImage? {
        return pdfDocument?.page(at: page)?.thumbnail(of: CGSize(width: 40, height: 40), for: .artBox)
    }

很不幸,它缺少官方文档(请参见https://developer.apple.com/documentation/pdfkit/pdfpage/2869834-thumbnail)。但是,在代码中对于PDFPage有一些可访问的注释。


1
感谢您的回复。不幸的是,如果要生成多个缩略图,这种方法非常慢。 - Nostradamus
这对我有效 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接