Как я могу программно сгенерировать миниатюру PDF с iOS?

В настоящее время мы отображаем содержимое PDF с помощью UIWebViews. В идеале я хотел бы иметь возможность отображать миниатюры в UITableView без одновременной загрузки множества различных UIWebViews... они достаточно медленно загружают один документ - не говоря уже о 10+!

Как мне это сделать?

Я думал о захвате экрана документа, загруженного с использованием UIDocumentInteractionController или UIWebView но это означает, что перед отображением таблицы все они должны быть уменьшены.

Ответ 1

Apple предоставляет целую кучу методов на уровне 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), чтобы получить окно обрезки страницы, а затем выработать способ масштабирования/перемещения, чтобы он соответствовал вашему размеру изображения.

Вероятно, проще для ваших целей метод -renderInContext: на CALayer (см. QA 1703) - старое средство получения скриншот был UIGetScreenImage, но это никогда не был действительно официальным API и, по-видимому, временно разрешен только из-за случайного одобрения RedLaser. С кодом в QA вы можете подстроить себя, чтобы получить UIImage из любого другого вида, не имея этого представления на экране. Что, возможно, устраняет некоторые из ваших проблем при захвате экрана? Хотя это означает, что вы можете поддерживать только OS 4.x.

В любом случае, PDF файлы просто не рисуют это быстро. Вам, вероятно, нужно заполнить таблицу, а затем нарисовать миниатюры на фоновом потоке, подталкивая их вверх, когда они доступны. Вы не можете безопасно использовать объекты UIKit для фоновых потоков, но все элементы CoreGraphics безопасны.

Ответ 2

Вот пример кода, рассматривающий преобразование.:)

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);

Ответ 3

Вот быстрый метод 3 для создания миниатюры UIImage для страницы pdf

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
}