iOS应用中通过UIGraphicsBeginPDFContext相关API嵌入文件到PDF的实现方法(含ZUGFERD标准XML嵌入需求)
iOS应用中通过UIGraphicsBeginPDFContext相关API嵌入文件到PDF的实现方法(含ZUGFERD标准XML嵌入需求)
嘿,我完全懂你的困扰——iOS上用UIGraphicsBeginPDFContextToFile这类API生成PDF确实顺手,但这套API本身没提供直接嵌入附件的能力,尤其是要满足ZUGFERD标准的XML嵌入需求,之前找的.NET/Java库又没法在iOS跑,确实头疼。
给你个可行的解决方案,核心思路是:先用你熟悉的UIGraphics系列API生成基础PDF,再用Core Graphics的底层API给这个PDF添加符合要求的ZUGFERD XML附件。具体步骤如下:
第一步:生成基础PDF(复用你现有代码)
先按原来的方式生成不带附件的PDF,保存到临时路径:
NSString *tempPDFPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"temp_invoice.pdf"]; UIGraphicsBeginPDFContextToFile(tempPDFPath, CGRectZero, nil); // 这里放你原来绘制PDF内容的代码,比如绘制发票文本、表格等 // ... UIGraphicsEndPDFContext();
第二步:嵌入ZUGFERD XML附件
接下来用Core Graphics的API打开临时PDF,创建新的PDF上下文并添加附件,同时完整保留原PDF的所有内容:
// 1. 读取要嵌入的ZUGFERD XML文件(务必确保符合EN 16931标准) NSString *xmlFilePath = [[NSBundle mainBundle] pathForResource:@"zugferd_invoice" ofType:@"xml"]; NSData *xmlData = [NSData dataWithContentsOfFile:xmlFilePath]; if (!xmlData) { NSLog(@"加载XML文件失败"); return; } // 2. 打开生成的临时PDF CGPDFDocumentRef originalPDF = CGPDFDocumentCreateWithURL((__bridge CFURLRef)[NSURL fileURLWithPath:tempPDFPath]); if (!originalPDF) { NSLog(@"打开原PDF失败"); return; } // 3. 创建新的PDF上下文(最终带附件的目标PDF) NSString *finalPDFPath = [NSTemporaryDirectory() stringByAppendingPathComponent:@"zugferd_final_invoice.pdf"]; CGContextRef newPDFContext = CGPDFContextCreateWithURL((__bridge CFURLRef)[NSURL fileURLWithPath:finalPDFPath], NULL, NULL); if (!newPDFContext) { CGPDFDocumentRelease(originalPDF); NSLog(@"创建新PDF上下文失败"); return; } // 4. 复制原PDF的所有页面到新PDF size_t pageCount = CGPDFDocumentGetNumberOfPages(originalPDF); for (size_t pageIndex = 1; pageIndex <= pageCount; pageIndex++) { CGPDFPageRef page = CGPDFDocumentGetPage(originalPDF, pageIndex); CGRect mediaBox = CGPDFPageGetBoxRect(page, kCGPDFMediaBox); CGContextBeginPage(newPDFContext, &mediaBox); CGContextDrawPDFPage(newPDFContext, page); CGContextEndPage(newPDFContext); } // 5. 配置ZUGFERD附件的属性(关键!要符合标准要求) CFStringRef attachmentFilename = CFStringCreateWithCString(NULL, "ZUGFERD-invoice.xml", kCFStringEncodingUTF8); CFStringRef mimeType = CFStringCreateWithCString(NULL, "application/vnd.zugferd invoice", kCFStringEncodingUTF8); CFMutableDictionaryRef attachmentProps = CFDictionaryCreateMutable(NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); CFDictionaryAddValue(attachmentProps, kCGPDFContextFilename, attachmentFilename); CFDictionaryAddValue(attachmentProps, kCGPDFContextMimeType, mimeType); // 可选:添加描述说明这是符合EN 16931的ZUGFERD发票XML CFDictionaryAddValue(attachmentProps, kCGPDFContextDescription, CFSTR("ZUGFERD Invoice XML conforming to EN 16931")); // 6. 将XML附件添加到新PDF CGContextAddDocumentAttachment(newPDFContext, (__bridge CFDataRef)xmlData, attachmentProps); // 7. 清理资源 CFRelease(attachmentFilename); CFRelease(mimeType); CFRelease(attachmentProps); CGPDFContextClose(newPDFContext); CGContextRelease(newPDFContext); CGPDFDocumentRelease(originalPDF);
额外注意事项
- XML标准合规性:一定要确保你的XML完全符合EN 16931规范,不然ZUGFERD验证工具会判定无效。
- Swift版本适配:如果用Swift开发,只需要把上面的Objective-C代码转成对应的Swift语法(注意Core Foundation类型的桥接处理)。
- 测试验证:生成PDF后,可以用支持ZUGFERD的阅读器(比如Adobe Acrobat)查看附件,或者用官方的ZUGFERD验证工具检查是否符合标准。
备注:内容来源于stack exchange,提问作者Nfriese
相关产品推荐
相关产品推荐

