Skip to content

PDF Oxide 快速上手(Objective-C)

PDF Oxide 在其 Rust 核心之上提供了地道的 Objective-C 绑定 —— 文本提取平均 0.8ms,在 3,830 个 PDF 上 100% 通过率。NSObject 封装(POXDocumentPOXPdf)持有底层句柄,并在 ARC 下自动释放;返回的字符串以 NSString 形式回传;任何 C-ABI 错误码都会以 POXErrorDomain 域中的 NSError 呈现。v0.3.69 新增。

安装

Objective-C 绑定链接默认特性的 cdylib,并在 ARC 下用 clang 构建。先构建原生库,再针对它执行 make build

# 1. 构建原生库(绑定所附带的特性集)
cargo build --release --lib --features ocr,rendering,signatures,barcodes,tsa-client,system-fonts

# 2. 构建 Objective-C 绑定(clang,ARC)
cd objc
make build PDF_OXIDE_LIB_DIR="$PWD/../target/release"
DYLD_LIBRARY_PATH="$PWD/../target/release" ./basic_extraction

在源文件中导入唯一的公共头文件:

#import "POXPdfOxide.h"

快速上手

打开一个 PDF,查看其元数据,并从第一页提取文本。每个可能失败的调用都接受一个末尾的 NSError** 参数。

#import "POXPdfOxide.h"

NSError *err = nil;
POXDocument *doc = [POXDocument openPath:@"research-paper.pdf" error:&err];
if (!doc) {
    NSLog(@"open failed: %@", err.localizedDescription);
    return;
}

NSInteger pages = [doc pageCountError:&err];
POXVersion ver  = [doc version];
NSLog(@"pages: %ld  version: %d.%d", (long)pages, ver.major, ver.minor);

NSString *text = [doc extractText:0 error:&err];
NSLog(@"%@", text);

你也可以从内存中的字节数据打开 —— 当 PDF 通过网络传输或来自数据库时很有用 —— 还可以打开受密码保护的文件:

POXDocument *doc = [POXDocument openFromBytes:pdfData error:&err];

// 加密文档,预先提供密码:
POXDocument *enc = [POXDocument openWithPassword:@"confidential.pdf"
                                       password:@"secret"
                                          error:&err];

// 或在打开之后再进行身份验证:
BOOL ok = [doc authenticate:@"secret" error:&err];

文本提取

纯文本是最快的路径。可按从零开始的页索引提取单页,也可一次性提取整个文档。

// 单页
NSString *text = [doc extractText:0 error:&err];

// 整个文档,合并输出
NSString *all = [doc toPlainTextAllWithError:&err];

// 逐页提取
NSInteger count = [doc pageCountError:&err];
for (NSInteger i = 0; i < count; i++) {
    NSLog(@"--- page %ld ---\n%@", (long)i, [doc extractText:i error:&err]);
}

单词与文本行

extractWords:extractTextLines: 返回元素对象数组,包含边界框和字体元数据,全部以 PDF 用户空间点(point)为单位。

NSArray<POXWord *> *words = [doc extractWords:0 error:&err];
for (POXWord *w in words) {
    POXBbox box = w.bbox;
    NSLog(@"'%@' at (%.1f, %.1f) %.1fx%.1f  font=%@ size=%.1f  bold=%d",
          w.text, box.x, box.y, box.width, box.height,
          w.fontName, w.fontSize, w.bold);
}

NSArray<POXTextLine *> *lines = [doc extractTextLines:0 error:&err];
for (POXTextLine *line in lines) {
    NSLog(@"%@  (%ld words)", line.text, (long)line.wordCount);
}

POXChar(来自 extractChars:)在字符粒度上暴露相同的结构 —— characterbboxfontNamefontSize

Markdown 与 HTML

将单页 —— 或整个文档 —— 转换为 Markdown 或 HTML。

// 单页
NSString *md   = [doc toMarkdown:0 error:&err];
NSString *html = [doc toHtml:0 error:&err];

// 整个文档
NSString *mdAll   = [doc toMarkdownAllWithError:&err];
NSString *htmlAll = [doc toHtmlAllWithError:&err];

搜索

search:term:caseSensitive:error: 搜索单页,或用 searchAll:caseSensitive:error: 搜索整个文档。两者都返回 POXSearchResult 数组,携带匹配到的文本、页索引和边界框。

NSArray<POXSearchResult *> *hits =
    [doc searchAll:@"configuration" caseSensitive:NO error:&err];

for (POXSearchResult *r in hits) {
    POXBbox b = r.bbox;
    NSLog(@"page %ld: '%@' at (%.0f, %.0f)", (long)r.page, r.text, b.x, b.y);
}

// 单页变体:
NSArray<POXSearchResult *> *pageHits =
    [doc search:0 term:@"configuration" caseSensitive:NO error:&err];

创建 PDF

POXPdf 构建器可从 Markdown、HTML 或纯文本生成 PDF。既可保存到路径,也可将字节以 NSData 形式取回。

POXPdf *pdf = [POXPdf fromMarkdown:@"# Hello World\n\nThis is a PDF.\n"
                            error:&err];
[pdf saveToPath:@"output.pdf" error:&err];

// 或将字节保留在内存中
NSData *bytes = [pdf toBytesWithError:&err];

// 同样提供 HTML 和纯文本的构造方法
POXPdf *invoice = [POXPdf fromHtml:@"<h1>Invoice</h1><p>Amount: $42</p>"
                            error:&err];
POXPdf *notes   = [POXPdf fromText:@"Plain text content." error:&err];

从构建器直接转入文档进行提取,实现往返:

POXPdf *pdf      = [POXPdf fromMarkdown:@"# Report\n\nBody text.\n" error:&err];
POXDocument *doc = [POXDocument openFromBytes:[pdf toBytesWithError:&err]
                                        error:&err];
NSLog(@"%@", [doc extractText:0 error:&err]);

错误处理

每个可能失败的方法都会写入末尾的 NSError**,并在失败时返回 nil 或一个哨兵值。错误归入 POXErrorDomain 域。

NSError *err = nil;
POXDocument *doc = [POXDocument openPath:@"document.pdf" error:&err];
if (!doc) {
    if ([err.domain isEqualToString:POXErrorDomain]) {
        NSLog(@"PDF error: %@", err.localizedDescription);
    }
    return;
}

NSString *text = [doc extractText:0 error:&err];
if (!text) {
    NSLog(@"extract failed: %@", err.localizedDescription);
}

句柄会在 ARC 下自行释放,但你也可以用 -close(幂等)提前释放底层句柄:

[doc close];

后续步骤