Извлеките одну страницу (или диапазон страниц) из данных pdf без загрузки всего pdf (что иногда занимает слишком много ОЗУ)

Используя PDFKit в swift, вы можете использовать PDFDocument для открытия PDF файлов. Это легко и хорошо работает. Но я создаю пользовательский просмотрщик PDF (для pdf файлов комиксов), который соответствует моим потребностям, и есть одна проблема, которую я имею. В телезрителе мне не нужно иметь весь файл PDF в памяти. Мне нужно всего лишь несколько страниц за раз.

Кроме того, PDF файлы состоят только из изображений. Там нет текста или чего-то еще.

При создании экземпляра PDFDocument все данные PDF загружаются в память. Если у вас действительно огромные pdf файлы (более 1 ГБ), это не оптимально (и может произойти сбой на некоторых устройствах). Насколько я знаю, в PDFKit нет способа загружать только части PDF-документа.

Что я могу с этим поделать? Я не нашел библиотеку swift/obj-c, которая может это сделать (хотя я не знаю правильных ключевых слов для ее поиска).

Моим обходным решением было бы предварительно обработать PDF файлы и сохранить каждую страницу как изображение в директории.documents (или аналогичной) с помощью FileManager. Это приведет к огромному количеству файлов, но решит проблему с памятью. Хотя я не уверен, что мне нравится этот подход.

Обновить:

Поэтому я сделал то, что предложили @Prcela и @Sahil Manchanda. Кажется, сейчас работает.

@yms: Хм, это может быть проблемой. Это происходит даже тогда, когда есть только изображения? Без всего остального в pdf.

@Carpsen90: они локальны (сохранены в каталоге документов).

EDIT: Я не принял ответ ниже или дал ему щедрость. Это было автоматически. Это не решает проблему. Он по-прежнему загружает весь PDF файл в память!

Ответы

Ответ 1

У меня есть идея, как вы могли бы добиться этого в PDFKit. После прочтения документации есть функция, которая позволяет выбирать определенные страницы. Что, вероятно, решит вашу проблему, если вы добавите ее в collectionFlowView.

func selection(from startPage: PDFPage, atCharacterIndex startCharacter: Int, to endPage: PDFPage, atCharacterIndex endCharacter: Int) -> PDFSelection?

Однако, как я прочитал, что у вас есть в основном изображения, есть еще одна функция, которая позволяет извлекать части PDF файла на основе CGPoints:

func selection(from startPage: PDFPage, at startPoint: CGPoint, to endPage: PDFPage, at endPoint: CGPoint) -> PDFSelection?

Также посмотрите на это: https://developer.apple.com/documentation/pdfkit/pdfview

так как это может быть то, что вам нужно, если вы хотите только просматривать страницы без редактирования аннотаций и т.д.

Я также подготовил небольшой код для извлечения одной страницы ниже. Надеюсь, поможет.

import PDFKit
import UIKit

class PDFViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        guard let url = Bundle.main.url(forResource: "myPDF", withExtension: "pdf") else {fatalError("INVALID URL")}
        let pdf = PDFDocument(url: url)
        let page = pdf?.page(at: 10) // returns a PDFPage instance
        // now you have one page extracted and you can play around with it.
    }
}

РЕДАКТИРОВАТЬ 1: Посмотрите на это извлечение кода. Я понимаю, что весь PDF загружается, однако этот подход может быть более эффективным с точки зрения памяти, так как, возможно, iOS будет лучше справляться с этим в PDFView:

func readBook() {

if let oldBookView = self.view.viewWithTag(3) {
    oldBookView.removeFromSuperview()
    // This removes the old book view when the user chooses a new book language
}

if #available(iOS 11.0, *) {
    let pdfView: PDFView = PDFView()
    let path = BookManager.getBookPath(bookLanguageCode: book.bookLanguageCode)
    let url = URL(fileURLWithPath: path)
    if let pdfDocument = PDFDocument(url: url) {
        pdfView.displayMode = .singlePageContinuous
        pdfView.autoScales = true
        pdfView.document = pdfDocument
        pdfView.tag = 3 // I assigned a tag to this view so that later on I can easily find and remove it when the user chooses a new book language
        let lastReadPage = getLastReadPage()

        if let page = pdfDocument.page(at: lastReadPage) {
            pdfView.go(to: page)
            // Subscribe to notifications so the last read page can be saved
            // Must subscribe after displaying the last read page or else, the first page will be displayed instead
            NotificationCenter.default.addObserver(self, selector: #selector(self.saveLastReadPage),name: .PDFViewPageChanged, object: nil)
        }
    }

    self.containerView.addSubview(pdfView)
    setConstraints(view: pdfView)
    addTapGesture(view: pdfView)
}

РЕДАКТИРОВАТЬ 2: это не тот ответ, который искал ОП. Это также загружает весь PDF в память. Читать комментарии