- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
一段时间以来,我一直在研究如何在我的 iOS 应用程序中使用 PDF。我已经解决了一些难题,例如扫描运算符和在 UIWebView 中显示 PDF。但是,我真正需要做的是识别 PDF 文档中的可编辑字段。
理想情况下,我希望能够直接与字段交互,但这听起来非常困难,而且不是显而易见的第一步。我已经在与可以以这种方式操作 PDF 的 Windows 服务进行交互,并且可以满足于识别可编辑字段、在表单 View 中从用户那里收集字段数据,并将该数据发布回服务器。问题是我看不到如何识别字段。我正在处理政府颁发的 PDF,例如 I-9 和 W-4,因此我无法控制 PDF 的创建或字段的命名。这就是为什么我需要动态提取它们的原因。任何帮助和/或引用将不胜感激。
我正在使用来自 Apple 的 Quatrz 2D 的 [this reference](https://developer.apple.com/library/mac/#documentation/graphicsimaging/conceptual/drawingwithquartz2d/dq_pdf_scan/dq_pdf_scan.html“PDF Document Parsing”)扫描 PDF 时触发运算符(operator)回调的编程指南,但这并不能帮助我找到可编辑的字段。
我还简单地加载了一个 UIWebView 和 PDF 数据以显示给用户。
[_webView loadData:decodedData MIMEType:@"application/pdf" textEncodingName:@"utf-8" baseURL:nil];
更新:
我构建了一个 PDF Helper 类(如下所示)来遍历目录中所有可能的对象类型。最初我没有处理数组中的嵌套字典,所以我没有看到表单字段。一旦我解决了这个问题,我就意识到我必须考虑父引用以避免会启动无限循环的循环递归调用。下面的代码显示了文档目录中的大量信息。现在我只需要解析它来隔离我需要的表单字段。
PDFHelper.h
#import <Foundation/Foundation.h>
id selfClass;
@interface PDFHelper : NSObject
@property (nonatomic, strong) NSData *pdfData;
@property (nonatomic, strong) NSMutableDictionary *pdfDict;
@property (nonatomic) int catalogLevel;
-(NSArray *) copyPDFArray:(CGPDFArrayRef)arr referencingDictionary:(CGPDFDictionaryRef)dict referencingKey:(const char *)key;
-(NSArray *) getFormFields;
-(CGPDFDictionaryRef) getDocumentCatalog;
@end
PDFHelper.m
#import "PDFHelper.h"
#import "FileHelpers.h"
#import "Log.h"
@implementation PDFHelper
@synthesize pdfData = _pdfData;
@synthesize pdfDict = _pdfDict;
@synthesize catalogLevel = _catalogLevel;
-(id)init
{
self = [super init];
if(self)
{
selfClass = self;
_pdfDict = [[NSMutableDictionary alloc] init];
_catalogLevel = 1;
}
return self;
}
-(NSArray *) getFormFields
{
CGPDFDictionaryRef acroForm = NULL;
if (CGPDFDictionaryGetDictionary([self getPdfDocDictionary], "AcroForm", &acroForm))
CGPDFDictionaryApplyFunction(acroForm, getDictionaryObjects, acroForm);
return [_pdfDict objectForKey:@"XFA"];
}
-(CGPDFDictionaryRef) getDocumentCatalog
{
CGPDFDictionaryRef docCatalog = [self getPdfDocDictionary];
CGPDFDictionaryApplyFunction(docCatalog, getDictionaryObjects, docCatalog);
return docCatalog;
}
-(CGPDFDictionaryRef) getPdfDocDictionary
{
NSURL *pdf = [[NSURL alloc] initFileURLWithPath:[FileHelpers pathInLibraryDirectory:@"file.pdf"]];
[_pdfData writeToFile:[pdf path] atomically:YES];
CGPDFDocumentRef pdfDocument = CGPDFDocumentCreateWithURL((__bridge CFURLRef)pdf);
CGPDFDictionaryRef returnDict = CGPDFDocumentGetCatalog(pdfDocument);
return returnDict;
}
void getDictionaryObjects (const char *key, CGPDFObjectRef object, void *info) {
NSString *logString = [[NSString alloc] initWithString:[NSString stringWithFormat:@"key: %s", key]];
for (int i = 0; i < [selfClass catalogLevel]; i++)
logString = [NSString stringWithFormat:@"-%@", logString];
[Log LogDebug:logString];
CGPDFDictionaryRef contentDict = (CGPDFDictionaryRef)info;
CGPDFObjectType type = CGPDFObjectGetType(object);
switch (type) {
case kCGPDFObjectTypeNull: {
[Log LogDebug:[NSString stringWithFormat:@"*****pdf null value"]];
break;
}
case kCGPDFObjectTypeBoolean: {
CGPDFBoolean objectBoolean;
if (CGPDFObjectGetValue(object, kCGPDFObjectTypeBoolean, &objectBoolean)) {
NSString *logString = [[NSString alloc] initWithString:[NSString stringWithFormat:@"pdf boolean value: %@", [NSNumber numberWithBool:objectBoolean]]];
for (int i = 0; i < [selfClass catalogLevel]; i++)
logString = [NSString stringWithFormat:@"-%@", logString];
[Log LogDebug:logString];
[[selfClass pdfDict] setObject:[NSNumber numberWithBool:objectBoolean]
forKey:[NSString stringWithCString:key encoding:NSUTF8StringEncoding]];
}
break;
}
case kCGPDFObjectTypeInteger: {
CGPDFInteger objectInteger;
if (CGPDFObjectGetValue(object, kCGPDFObjectTypeInteger, &objectInteger)) {
NSString *logString = [[NSString alloc] initWithString:[NSString stringWithFormat:@"pdf integer value: %ld", (long int)objectInteger]];
for (int i = 0; i < [selfClass catalogLevel]; i++)
logString = [NSString stringWithFormat:@"-%@", logString];
[Log LogDebug:logString];
[[selfClass pdfDict] setObject:[NSNumber numberWithInt:objectInteger]
forKey:[NSString stringWithCString:key encoding:NSUTF8StringEncoding]];
}
break;
}
case kCGPDFObjectTypeReal: {
CGPDFReal objectReal;
if (CGPDFObjectGetValue(object, kCGPDFObjectTypeReal, &objectReal)) {
NSString *logString = [[NSString alloc] initWithString:[NSString stringWithFormat:@"pdf real value: %ld", (long int)objectReal]];
for (int i = 0; i < [selfClass catalogLevel]; i++)
logString = [NSString stringWithFormat:@"-%@", logString];
[Log LogDebug:logString];
[[selfClass pdfDict] setObject:[NSNumber numberWithInt:objectReal]
forKey:[NSString stringWithCString:key encoding:NSUTF8StringEncoding]];
}
break;
}
case kCGPDFObjectTypeName: {
const char *name;
if (CGPDFDictionaryGetName(contentDict, key, &name))
{
NSString *dictName = [[NSString alloc] initWithCString:name encoding:NSUTF8StringEncoding];
if (dictName)
{
NSString *logString = [[NSString alloc] initWithString:[NSString stringWithFormat:@"pdf name value: %@", dictName]];
for (int i = 0; i < [selfClass catalogLevel]; i++)
logString = [NSString stringWithFormat:@"-%@", logString];
[Log LogDebug:logString];
[[selfClass pdfDict] setObject:dictName
forKey:[NSString stringWithCString:key encoding:NSUTF8StringEncoding]];
}
}
break;
}
case kCGPDFObjectTypeString: {
CGPDFStringRef objectString;
if (CGPDFObjectGetValue(object, kCGPDFObjectTypeString, &objectString)) {
NSString *logString = [[NSString alloc] initWithString:[NSString stringWithFormat:@"pdf string value: %@", (__bridge NSString *)CGPDFStringCopyTextString(objectString)]];
for (int i = 0; i < [selfClass catalogLevel]; i++)
logString = [NSString stringWithFormat:@"-%@", logString];
[Log LogDebug:logString];
[[selfClass pdfDict] setObject:(__bridge NSString *)CGPDFStringCopyTextString(objectString)
forKey:[NSString stringWithCString:key encoding:NSUTF8StringEncoding]];
}
break;
}
case kCGPDFObjectTypeArray: {
CGPDFArrayRef objectArray;
if (CGPDFObjectGetValue(object, kCGPDFObjectTypeArray, &objectArray)) {
NSArray *myArray=[selfClass copyPDFArray:objectArray referencingDictionary:contentDict referencingKey:key];
[[selfClass pdfDict] setObject:myArray
forKey:[NSString stringWithCString:key encoding:NSUTF8StringEncoding]];
}
break;
}
case kCGPDFObjectTypeDictionary: {
CGPDFDictionaryRef objectDictionary;
if (CGPDFObjectGetValue(object, kCGPDFObjectTypeDictionary, &objectDictionary)) {
NSString *logString = @"Found dictionary";
for (int i = 0; i < [selfClass catalogLevel]; i++)
logString = [NSString stringWithFormat:@"-%@", logString];
//[Log LogDebug:logString];
NSString *keyCheck = [[NSString alloc] initWithUTF8String:key];
if (![keyCheck isEqualToString:@"Parent"] && ![keyCheck isEqualToString:@"P"])
{
[selfClass setCatalogLevel:[selfClass catalogLevel] + 1];
CGPDFDictionaryApplyFunction(objectDictionary, getDictionaryObjects, objectDictionary);
[selfClass setCatalogLevel:[selfClass catalogLevel] - 1];
}
}
break;
}
case kCGPDFObjectTypeStream: {
CGPDFStreamRef objectStream;
if (CGPDFObjectGetValue(object, kCGPDFObjectTypeStream, &objectStream)) {
CGPDFDictionaryRef dict = CGPDFStreamGetDictionary( objectStream );
CGPDFDataFormat fmt = CGPDFDataFormatRaw;
CFDataRef streamData = CGPDFStreamCopyData(objectStream, &fmt);
NSData *data = [[NSData alloc] initWithData:(__bridge NSData *)(streamData)];
[data writeToFile:[FileHelpers pathInDocumentDirectory:@"data.dat"] atomically:YES];
NSString *dataString = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
//if (!dataString) {
// dataString = [[NSString alloc] initWithData:(__bridge NSData *)(streamData) encoding:NSUTF16StringEncoding];
// }
NSString *logString = [[NSString alloc] initWithString:[NSString stringWithFormat:@"pdf stream length: %ld - %@", (long int)CFDataGetLength( streamData ), dataString]];
for (int i = 0; i < [selfClass catalogLevel]; i++)
logString = [NSString stringWithFormat:@"-%@", logString];
[Log LogDebug:logString];
NSString *keyCheck = [[NSString alloc] initWithUTF8String:key];
if( dict && ![keyCheck isEqualToString:@"Parent"] && ![keyCheck isEqualToString:@"P"])
{
[selfClass setCatalogLevel:[selfClass catalogLevel] + 1];
CGPDFDictionaryApplyFunction(dict, getDictionaryObjects, dict);
[selfClass setCatalogLevel:[selfClass catalogLevel] - 1];
}
}
}
}
}
- (NSArray *)copyPDFArray:(CGPDFArrayRef)arr referencingDictionary:(CGPDFDictionaryRef)dict referencingKey:(const char *)key
{
int i = 0;
NSMutableArray *temp = [[NSMutableArray alloc] init];
NSString *logString = [[NSString alloc] initWithString:[NSString stringWithFormat:@"pdf array count: %zu", CGPDFArrayGetCount(arr)]];
for (int i = 0; i < [selfClass catalogLevel]; i++)
logString = [NSString stringWithFormat:@"-%@", logString];
[Log LogDebug:logString];
for(i=0; i<CGPDFArrayGetCount(arr); i++){
CGPDFObjectRef object;
CGPDFArrayGetObject(arr, i, &object);
CGPDFObjectType type = CGPDFObjectGetType(object);
switch(type){
case kCGPDFObjectTypeNull: {
NSString *logString = [[NSString alloc] initWithString:[NSString stringWithFormat:@"pdf array null(%d)", i]];
for (int i = 0; i < [selfClass catalogLevel]; i++)
logString = [NSString stringWithFormat:@"-%@", logString];
[Log LogDebug:logString];
break;
}
case kCGPDFObjectTypeBoolean: {
CGPDFBoolean objectBool;
if (CGPDFObjectGetValue(object, kCGPDFObjectTypeBoolean, &objectBool)) {
NSString *logString = [[NSString alloc] initWithString:[NSString stringWithFormat:@"pdf array boolean value(%d): %@", i, [NSNumber numberWithBool:objectBool]]];
for (int i = 0; i < [selfClass catalogLevel]; i++)
logString = [NSString stringWithFormat:@"-%@", logString];
[Log LogDebug:logString];
[temp addObject:[NSNumber numberWithBool:objectBool]];
}
break;
}
case kCGPDFObjectTypeInteger: {
CGPDFInteger objectInteger;
if (CGPDFObjectGetValue(object, kCGPDFObjectTypeInteger, &objectInteger)) {
NSString *logString = [[NSString alloc] initWithString:[NSString stringWithFormat:@"pdf array integer value(%d): %ld", i, (long int)objectInteger]];
for (int i = 0; i < [selfClass catalogLevel]; i++)
logString = [NSString stringWithFormat:@"-%@", logString];
[Log LogDebug:logString];
[temp addObject:[NSNumber numberWithInt:objectInteger]];
}
break;
}
case kCGPDFObjectTypeReal:
{
CGPDFReal objectReal;
if (CGPDFObjectGetValue(object, kCGPDFObjectTypeReal, &objectReal))
{
NSString *logString = [[NSString alloc] initWithString:[NSString stringWithFormat:@"pdf array real(%d): %ld", i, (long int)objectReal]];
for (int i = 0; i < [selfClass catalogLevel]; i++)
logString = [NSString stringWithFormat:@"-%@", logString];
[Log LogDebug:logString];
[temp addObject:[NSNumber numberWithInt:objectReal]];
}
break;
}
case kCGPDFObjectTypeName:
{
const char *name;
if (CGPDFDictionaryGetName(dict, key, &name))
{
NSString *dictName = [[NSString alloc] initWithCString:name encoding:NSUTF8StringEncoding];
if (dictName)
{
NSString *logString = [[NSString alloc] initWithString:[NSString stringWithFormat:@"pdf array name value(%d): %@", i, dictName]];
for (int i = 0; i < [selfClass catalogLevel]; i++)
logString = [NSString stringWithFormat:@"-%@", logString];
[Log LogDebug:logString];
[[selfClass pdfDict] setObject:dictName
forKey:[NSString stringWithCString:key encoding:NSUTF8StringEncoding]];
}
}
break;
}
case kCGPDFObjectTypeString:
{
CGPDFStringRef objectString;
if (CGPDFObjectGetValue(object, kCGPDFObjectTypeString, &objectString))
{
NSString *tempStr = (__bridge NSString *)CGPDFStringCopyTextString(objectString);
NSString *logString = [[NSString alloc] initWithString:[NSString stringWithFormat:@"pdf array string(%d): %@", i, tempStr]];
for (int i = 0; i < [selfClass catalogLevel]; i++)
logString = [NSString stringWithFormat:@"-%@", logString];
[Log LogDebug:logString];
[temp addObject:tempStr];
}
break;
}
case kCGPDFObjectTypeArray :
{
CGPDFArrayRef objectArray;
if (CGPDFObjectGetValue(object, kCGPDFObjectTypeArray, &objectArray))
{
NSArray *tempArr = [selfClass copyPDFArray:objectArray referencingDictionary:dict referencingKey:key];
[temp addObject:tempArr];
}
break;
}
case kCGPDFObjectTypeDictionary :
{
CGPDFDictionaryRef objectDict;
NSString *keyCheck = [[NSString alloc] initWithUTF8String:key];
if (CGPDFObjectGetValue(object, kCGPDFObjectTypeDictionary, &objectDict) && ![keyCheck isEqualToString:@"Parent"] && ![keyCheck isEqualToString:@"P"])
{
[selfClass setCatalogLevel:[selfClass catalogLevel] + 1];
CGPDFDictionaryApplyFunction( objectDict, getDictionaryObjects, objectDict);
[selfClass setCatalogLevel:[selfClass catalogLevel] - 1];
}
break;
}
case kCGPDFObjectTypeStream :
{
CGPDFStreamRef objectStream;
if (CGPDFObjectGetValue(object, kCGPDFObjectTypeStream, &objectStream))
{
CGPDFDictionaryRef streamDict = CGPDFStreamGetDictionary( objectStream );
CGPDFDataFormat fmt = CGPDFDataFormatRaw;
CFDataRef streamData = CGPDFStreamCopyData(objectStream, &fmt);
NSString *dataString = [[NSString alloc] initWithData:(__bridge NSData *)(streamData) encoding:NSUTF8StringEncoding];
NSString *logString = [[NSString alloc] initWithString:[NSString stringWithFormat:@"pdf array stream length: (%d): %ld - %@", i, (long int)CFDataGetLength( streamData ), dataString]];
for (int i = 0; i < [selfClass catalogLevel]; i++)
logString = [NSString stringWithFormat:@"-%@", logString];
[Log LogDebug:logString];
NSString *keyCheck = [[NSString alloc] initWithUTF8String:key];
if( streamDict && ![keyCheck isEqualToString:@"Parent"] && ![keyCheck isEqualToString:@"P"])
{
[selfClass setCatalogLevel:[selfClass catalogLevel] + 1];
CGPDFDictionaryApplyFunction( streamDict, getDictionaryObjects, streamDict );
[selfClass setCatalogLevel:[selfClass catalogLevel] - 1];
}
}
}
}
}
return temp;
}
@end
最佳答案
“可编辑字段”是指可以使用 Acrobat 或 Adobe Reader 填写的表单元素类型?
这些字段不是实际页面描述的一部分。如果您查看 PDF 规范文档,您会在第 12.7 章中找到对“交互式表单”的描述,其中解释了文档的字段字典是从文档目录中名为“AcroForm”的元素开始存储的。
据我所知,iOS 确实允许您访问文档目录,因此您必须在该目录字典中找到“AcroForm”字段,然后进入字段字典结构以收集您想要的信息。完整文档中的所有字段都以分层方式存储在这个地方。
关于ios - 在 Objective-C 中从 PDF 中提取可编辑字段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14163313/
我的代码有一些问题。我正在尝试遍历包含许多 PDF 的 Drive 文件夹,然后将它们合并为一个文件。当我使用我的代码时,它只是为 Drive 文件夹中的最后一个 PDF 创建一个 PDF,而不是按预
我从 PDF Specification 获取了 PDF 规范中的最小 PDF 示例。 ,将其复制到记事本,将文件重命名为扩展名为 .pdf。 我可以用其他 PDF 查看器(PDF-XChange、S
感谢您在以下方面的帮助: 我有 2 个部分可访问的 PDF(包含标签),我想使用一些命令行工具(如 PDFtk 或 Ghostscript,或任何 Perl 模块)将它们连接起来: 我已经尝试使用 P
我想使用 ghostscript 将矢量 pdf 转换为光栅 pdf(即光栅化矢量 pdf)。但是即使我添加了解析参数 -r300,我也找不到合适的参数来执行此操作。 我使用的代码是-dSAFER -
我无法在 FAQ 中找到这个功能是否存在于 API 中,尽管它在书中提到作为潜在可用的东西。有没有人有任何实现此功能的经验? 最佳答案 在 This thread (日期为 2007 年 6 月)Pa
我要放文件sample.pdf在我的网站上,并希望使用 pdf.js 显示它.我想要的是显示我自己的文件,如 demo ,带有工具栏,放大/缩小等。到目前为止,我还不能这样做。 我确实检查了 hell
我知道这可能不是严格意义上的编程问题(也许是,我不知道)但我在尝试转换常规 pdf(带有超链接、书签、图像、嵌入字体等)时遇到了严重问题.) 转换为 PDF/A-1 格式。 当我用 pdfaPilot
这是 PDF.js 网站 https://github.com/mozilla/pdf.js 我正在搜索和阅读很多文章,大多数编码都是将 pdf 导入 pdf.js 并在浏览器上显示,我不明白是不是
谁能建议我如何将扫描图像转换为可搜索图像或如何将扫描 pdf 转换为可搜索 pdf? 很长一段时间以来,我一直陷入这种情况。 我已经在 ubuntu 中尝试过 pdfocr 应用程序,但没有成功。 最
作为我对客户端/服务器 pdf 签名研究的一部分,我测试了 itext pdf 延迟签名示例。不幸的是,我生成的 pdf 即合并空签名 pdf 和哈希值的输出显示无效签名。 我的代码片段如下 cla
我想将一个 PDF 页面插入到另一个已缩放的 PDF 页面中。我想使用 iTextSharp 来实现此目的。 我有一个矢量绘图,可以导出为单页 PDF 文件。我想将此文件添加到其他 PDF 文档的页面
作为我对客户端/服务器 pdf 签名研究的一部分,我测试了 itext pdf 延迟签名示例。不幸的是,我生成的 pdf 即合并空签名 pdf 和哈希值的输出显示无效签名。 我的代码片段如下 cla
我想为 Kindle 转换电子书。我尝试使用 Calibre 将具有复杂格式样式和图像的基于两种语言的基于文本的大型 PDF 电子书转换为适用于 Kindle 的 AZW3 电子书,并且还尝试了亚马逊
我在 Google Chrome 中显示 pdf 时遇到问题。问题是 Chrome 将 pdf 的某些页面显示为黑色。 启用 Chrome PDF 查看器时会发生这种情况。如果我禁用此插件并使用 Ad
我确信这个问题无处不在,尽管我似乎找不到答案。我希望我的 PDF 文档在 PDF 阅读器中显示时没有空白页,但随后在封面后打印空白页,这样打印出来的文档在右侧甚至左侧都有奇数页。还有其他人遇到过这个问
我需要自动裁剪 pdf 文件(去除白边)。到目前为止,我尝试了两种并不完美的工具: pdf裁剪 问题:它不会裁剪某些 pdf。 pdf-crop-margins 问题:有时它裁剪得太多(精细的细节)。
This PDF由几个源文件组成。其中五个是包含 alpha channel 的 PNG。一种是没有 alpha channel 的 PNG。最后一 block 是带有透明效果的 Photoshop
我的团队将内部 wiki 页面用于各种内容。这些页面是使用 MediaWiki 创建的。我想知道是否有任何方法可以将 wiki 页面转换为 PDF 格式。我必须用它来将用户文档转换为 PDF 格式,以
我希望能够从我可能在数据库或 xml 或任何其他结构化形式中拥有的数据生成高度图形化(也包含大量文本内容)的 PDF 文件。 目前,我们的平面设计师在将内容作为 MS Word 文档后,在 Photo
我正在寻找可以帮助我找到重复 PDF 的实用程序。问题:我有 1000 个 PDF 文件。有些是重复的。由于不同的文件名和文件大小的微小差异,它们不容易被检测到。是否有实用程序/算法/库可以帮助我找到
我是一名优秀的程序员,十分优秀!