ios - 从 pdf 数据中提取单个页面(或页面范围)而不加载整个 pdf(有时会占用太多 RAM)

标签 ios swift pdf

在swift中使用PDFKit,可以使用PDFDocument打开pdf文件。 这很容易,而且效果很好。但是我正在构建一个适合我需要的自定义 pdf 查看器(用于漫画书 pdf),但我遇到了一个问题。在查看器中,我不需要将整个 pdf 文件存储在内存中。我一次只需要几页。

此外,pdf 仅包含图像。没有文字或任何东西。

当实例化一个 PDFDocument 时,整个 pdf 数据被加载到内存中。如果您有非常大的 pdf 文件(超过 1GB),这不是最佳选择(并且可能在某些设备上崩溃)。据我所知,PDFKit 无法只加载部分 pdf 文档。

我能做些什么吗?我还没有找到可以执行此操作的 swift/obj-c 库(尽管我真的不知道搜索它的正确关键字)。

我的解决方法是使用 FileManager 预处理 pdf 并在 .documents 目录(或类似目录)中将每个页面保存为图像。这会产生大量文件,但会解决内存问题。不过,我不确定我是否喜欢这种方法。

更新:

所以我按照@Prcela 和@Sahil Manchanda 的建议做了。它现在似乎正在工作。

@yms:嗯,这确实是个问题。当只有图像时会发生这种情况吗? pdf 中没有任何其他内容。

@Carpsen90:它们是本地的(保存在文档目录中)。

编辑:我没有接受下面的答案,也没有给它赏金。这是自动的。它不能解决问题。它仍然会将整个 PDF 加载到内存中!

最佳答案

我知道如何在 PDFKit 中实现这一点。阅读文档后,有一个功能可以选择某些页面。如果您将它添加到 collectionFlowView,这可能会解决您的问题。

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

然而,当我读到你主要有图像时,还有另一个函数允许基于 CGPoints 提取部分 pdf:

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:这不是 OP 正在寻找的答案。这还将整个 pdf 加载到内存中。 阅读评论

关于ios - 从 pdf 数据中提取单个页面(或页面范围)而不加载整个 pdf(有时会占用太多 RAM),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52127446/

相关文章:

ios - Swift Dispatch Source Timer 没有按计划触发

swift - 如何在应用程序退出时获取 DispatchQueue() 代码进行清理

java - 支持文本提取的 PDF 查看器 Eclipse 插件/库

ios - 无声通知

ios - 在 UITextview 中复制 NSDictionary 值

ios - iOS 是否支持 SVG Tiny?

php - 使用 PHP(或可能是 perl)将 PDF 文档拆分为单独的页面

html - pdf.js 与本地 pdf 文件

ios - 如何避免 UITableView reloadData 中的竞争条件?

ios - 透明 View 背景变黑