阅读 PDF 时如何从这些回调中获取值?

Posted

技术标签:

【中文标题】阅读 PDF 时如何从这些回调中获取值?【英文标题】:How to get values out of these callbacks when reading a PDF? 【发布时间】:2017-05-30 12:31:16 【问题描述】:

我正在尝试阅读 PDF 文件。下面的回调也打印消息,但我无法从 PDF 中获取任何信息。

    let pdfBundlePath = Bundle.main.path(forResource: "sample", ofType: "pdf")
    let pdfURL = URL.init(fileURLWithPath: pdfBundlePath!)
    let pdf = CGPDFDocument(pdfURL as CFURL)        

    let operatorTableRef = CGPDFOperatorTableCreate()

    CGPDFOperatorTableSetCallback(operatorTableRef!, "BT")  (scanner, info) in
        print("Begin text object")
    
    CGPDFOperatorTableSetCallback(operatorTableRef!, "ET")  (scanner, info) in
        print("End text object")
    
    CGPDFOperatorTableSetCallback(operatorTableRef!, "Tf")  (scanner, info) in
        print("Select font")
    
    CGPDFOperatorTableSetCallback(operatorTableRef!, "Tj")  (scanner, info) in
        print("Show text")
    
    CGPDFOperatorTableSetCallback(operatorTableRef!, "TJ")  (scanner, info) in
        print("Show text, allowing individual glyph positioning")
    

        let page = pdf!.page(at: 1)
        let stream = CGPDFContentStreamCreateWithPage(page!)
        let scanner = CGPDFScannerCreate(stream, operatorTableRef, nil)
        CGPDFScannerScan(scanner)
        CGPDFScannerRelease(scanner)
        CGPDFContentStreamRelease(stream)

输出:

Begin text object
Select font
Show text, allowing individual glyph positioning
End text object

// the same output for at least 10 or more times.

但我不确定如何从中获取实际的字符串?任何建议将不胜感激。

【问题讨论】:

是的,我有同样的麻烦,即使我想在 pdf 文档中搜索。我建议您阅读 Adob​​e 规范以了解我们正在尝试做什么,以及为什么它不那么容易:-) 创建一个 pdf paser 要复杂得多,正如我之前想象的那样。看到这个slideshare.net/KazYoshikawa/extracting-text-from-pdf-ios有一个想法:-)。对不起。我现在无法做出更好的答案,但我很早就开始“研究”如何做同样的事情,就像你想做的那样。 感谢您的提示。但是,至少您可以从中打印出一些东西?这是目前最重要的。 如果你能阅读 ObjC,Apple 发布了一份关于如何做到这一点的指南:developer.apple.com/library/content/documentation/…不确定它离解决你的问题有多近 @Hemang 不幸的是,不是:-(。我决定使用“天真的”方法。在 TextEdit 中我写了 Hello, World!。接下来我将它导出为 pdf 并尝试再次在 TextEdit 中打开它.过去两天我试图了解那里有什么:-),遵循Adobe规范......接下来,我对“空”文档和只有一个“空格”的文档做了同样的事情。结果“令人惊讶”。 Code Different 提到的指南对我没有任何帮助。 @user3441734:每个操作员接受扫描仪“堆栈”上的一些操作数。这是另一个演示如何解析内容的项目:github.com/KurtCode/PDFKitten。它也在 Objective-C 中,但应该看到总体思路。例如,“显示文本”运算符采用字符串操作数,该操作数可以通过CGPDFScannerPopString 获得。 – 但是您必须阅读 PDF 规范才能了解哪个运算符采用哪种操作数,这是没有办法的。 【参考方案1】:

我有带有“hello, world”文本的 pdf(通过从 TextEdit 导出为 pdf 创建)

这个回调函数

CGPDFOperatorTableSetCallback(operatorTableRef!, "TJ")  (scanner, info) in
    print("Show text, allowing individual glyph positioning")
    var pa: CGPDFArrayRef?
    withUnsafeMutablePointer(to: &pa,  (ppa) -> () in
        let r = CGPDFScannerPopArray(scanner, ppa)
        print("TJ", r)
        if r 
            let count = CGPDFArrayGetCount(ppa.pointee!)
            var j = 0
            for i in 0..<count 
                var str: CGPDFStringRef?
                let r = CGPDFArrayGetString(ppa.pointee!, i, &str)
                if r 
                    let string = String(cString: CGPDFStringGetBytePtr(str!)!)
                    print(string, i, j)
                    j += 1
                
            
        
    )

打印我

Show text, allowing individual glyph positioning
TJ true
h 0 0
e 2 1
l 4 2
l 6 3
o 8 4
, 10 5
  12 6
w 14 7
o 16 8
rl 18 9
d 20 10

我认为这表明,获取字符串是可能的 :-),至少对于拉丁字母而言。

对于Tj算子,回调函数可以很简单

CGPDFOperatorTableSetCallback(operatorTableRef!, "Tj")  (scanner, info) in
        print("Show text")
        var text: CGPDFStringRef?
        withUnsafeMutablePointer(to: &text,  (p) -> () in
            let r = CGPDFScannerPopString(scanner, p)
            if r 
                let string = String(cString: CGPDFStringGetBytePtr(p.pointee!)!)
                print(string)
            
        )
    

警告! 要正确显示所有字符,必须使用字体信息,但这是另一回事。对于拉丁字符,此解决方案应按原样工作。

为了能够“提取”所有字符串,必须实现所有文本显示运算符

更新 因为 PDFKit 在两个苹果平台上都可用(从 iOS11) 我建议使用它来提取文本。这个过程非常简单

【讨论】:

以上是关于阅读 PDF 时如何从这些回调中获取值?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 Ruby 中解析 pdf

当用户从另一个应用程序(如文件管理器应用程序)单击 pdf 文件时,如何将我的 pdf 阅读器应用程序添加为隐式意图?

如何在 android 中阅读和查看 PDF?

如何从回调操作的结果中获取类型?

使用 PDF 阅读器获取文本?

如何在 python 中阅读 pdf? [复制]