You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.13 17:09:32