IOS SDK-以编程方式生成 PDF 文件

使用 CoreGraphics框架是一项单调乏味的工作,在我看来,当涉及到以编程方式绘制 PDF 文件时。

我想编程创建一个 PDF,使用各种对象从视图我的整个应用程序。

我很想知道是否有任何关于 iOS SDK 的好的 PDF 教程,也许是一个图书馆。

我看过这个教程,PDF 创作教程,但它大部分是用 C 写的,寻找更多的 Objective-C 风格。这似乎也是一个可笑的方式写入 PDF 文件,必须计算行和其他对象将放置在哪里。

void CreatePDFFile (CGRect pageRect, const char *filename)
{
// This code block sets up our PDF Context so that we can draw to it
CGContextRef pdfContext;
CFStringRef path;
CFURLRef url;
CFMutableDictionaryRef myDictionary = NULL;


// Create a CFString from the filename we provide to this method when we call it
path = CFStringCreateWithCString (NULL, filename,
kCFStringEncodingUTF8);


// Create a CFURL using the CFString we just defined
url = CFURLCreateWithFileSystemPath (NULL, path,
kCFURLPOSIXPathStyle, 0);
CFRelease (path);
// This dictionary contains extra options mostly for 'signing' the PDF
myDictionary = CFDictionaryCreateMutable(NULL, 0,
&kCFTypeDictionaryKeyCallBacks,
&kCFTypeDictionaryValueCallBacks);


CFDictionarySetValue(myDictionary, kCGPDFContextTitle, CFSTR("My PDF File"));
CFDictionarySetValue(myDictionary, kCGPDFContextCreator, CFSTR("My Name"));
// Create our PDF Context with the CFURL, the CGRect we provide, and the above defined dictionary
pdfContext = CGPDFContextCreateWithURL (url, &pageRect, myDictionary);
// Cleanup our mess
CFRelease(myDictionary);
CFRelease(url);
// Done creating our PDF Context, now it's time to draw to it


// Starts our first page
CGContextBeginPage (pdfContext, &pageRect);


// Draws a black rectangle around the page inset by 50 on all sides
CGContextStrokeRect(pdfContext, CGRectMake(50, 50, pageRect.size.width - 100, pageRect.size.height - 100));


// This code block will create an image that we then draw to the page
const char *picture = "Picture";
CGImageRef image;
CGDataProviderRef provider;
CFStringRef picturePath;
CFURLRef pictureURL;


picturePath = CFStringCreateWithCString (NULL, picture,
kCFStringEncodingUTF8);
pictureURL = CFBundleCopyResourceURL(CFBundleGetMainBundle(), picturePath, CFSTR("png"), NULL);
CFRelease(picturePath);
provider = CGDataProviderCreateWithURL (pictureURL);
CFRelease (pictureURL);
image = CGImageCreateWithPNGDataProvider (provider, NULL, true, kCGRenderingIntentDefault);
CGDataProviderRelease (provider);
CGContextDrawImage (pdfContext, CGRectMake(200, 200, 207, 385),image);
CGImageRelease (image);
// End image code


// Adding some text on top of the image we just added
CGContextSelectFont (pdfContext, "Helvetica", 16, kCGEncodingMacRoman);
CGContextSetTextDrawingMode (pdfContext, kCGTextFill);
CGContextSetRGBFillColor (pdfContext, 0, 0, 0, 1);
const char *text = "Hello World!";
CGContextShowTextAtPoint (pdfContext, 260, 390, text, strlen(text));
// End text


// We are done drawing to this page, let's end it
// We could add as many pages as we wanted using CGContextBeginPage/CGContextEndPage
CGContextEndPage (pdfContext);


// We are done with our context now, so we release it
CGContextRelease (pdfContext);
}

编辑: 这是一个关于 < strong > GitHub using libHaru 在 iPhone 项目中的例子。

77230 次浏览

IOS 上的 PDF 函数都是基于 CoreGraphics 的,这是有意义的,因为 iOS 中的绘图原语也是基于 CoreGraphics 的。如果你想直接将2D 原语渲染成 PDF,你必须使用 CoreGraphics。

对于存在于 UIKit 中的对象,也有一些快捷方式,比如图像。将 PNG 绘制成 PDF 上下文仍然需要调用 CGContextDrawImage,但是你可以使用从 UIImage 获得的 CGImage,例如:

UIImage * myPNG = [UIImage imageNamed:@"mypng.png"];
CGContextDrawImage (pdfContext, CGRectMake(200, 200, 207, 385), [myPNG CGImage]);

如果你想获得更多关于整体设计的指导,请更明确地说明你所说的“应用程序中的各种对象”以及你想要实现的目标。

有几件事..。

首先,在 iOS 中生成 CoreGraphics PDF 时出现了一个 bug,导致 PDF 文件损坏。我知道这个问题一直存在到 iOS 4.1(我还没有测试过 iOS 4.2)。这个问题与字体有关,只有在 PDF 中包含文本时才会出现。症状是,在生成 PDF 时,您会在调试控制台中看到如下错误:

<Error>: can't get CIDs for glyphs for 'TimesNewRomanPSMT'

棘手的方面是,生成的 PDF 在某些 PDF 阅读器中可以很好地呈现,但在其他地方无法呈现。所以,如果你能控制打开 PDF 的软件,你可以忽略这个问题(例如,如果你只想在 iPhone 或 Mac 桌面上显示 PDF,那么你可以使用 CoreGraphics)。然而,如果您需要创建一个可以在任何地方工作的 PDF,那么您应该仔细研究一下这个问题。下面是一些额外的信息:

Http://www.iphonedevsdk.com/forum/iphone-sdk-development/15505-pdf-font-problem-cant-get-cids-glyphs.html#post97854

作为一种解决方案,我已经成功地在 iPhone 上使用 libHaru 作为 CoreGraphics PDF 生成的替代品。最初让 libHaru 用我的项目来构建有点棘手,但是一旦我正确地设置了我的项目,它就可以很好地满足我的需要。

其次,根据 PDF 的格式/布局,你可以考虑使用 Interface Builder 来创建一个视图,作为 PDF 输出的“模板”。然后编写加载视图的代码,填充任何数据(例如,为 UILabels 设置文本等) ,然后将视图的各个元素呈现到 PDF 中。换句话说,使用 IB 指定坐标、字体、图像等,并编写代码以通用方式呈现各种元素(例如 UILabelUIImageView等) ,这样就不必硬编码所有内容。我使用了这种方法,结果非常符合我的需要。同样,这可能对您的情况有意义,也可能没有意义,这取决于您的 PDF 的格式/布局需求。

编辑: (回应第一条评论)

我的实现是一个商业产品的一部分,意思是我不能共享完整的代码,但我可以给出一个大致的轮廓:

我创造了一个。Xib 文件的视图和大小视图为850 x 1100(我的 PDF 的目标是8.5 x 11英寸,所以这使得它很容易转换到/从设计时坐标)。

在代码中,我加载视图:

- (UIView *)loadTemplate
{
NSArray *nib = [[NSBundle mainBundle] loadNibNamed:@"ReportTemplate" owner:self options:nil];
for (id view in nib) {
if ([view isKindOfClass: [UIView class]]) {
return view;
}
}


return nil;
}

然后填充各种元素。我使用标记来查找适当的元素,但是您可以使用其他方法。例如:

UILabel *label = (UILabel *)[templateView viewWithTag:TAG_FIRST_NAME];
if (label != nil) {
label.text = (firstName != nil) ? firstName : @"None";

然后调用一个函数将视图呈现到 PDF 文件。此函数递归地遍历视图层次结构并呈现每个子视图。对于我的项目,我只需要支持 Label、 ImageView 和 View (对于嵌套视图) :

- (void)addObject:(UIView *)view
{
if (view != nil && !view.hidden) {
if ([view isKindOfClass:[UILabel class]]) {
[self addLabel:(UILabel *)view];
} else if ([view isKindOfClass:[UIImageView class]]) {
[self addImageView:(UIImageView *)view];
} else if ([view isKindOfClass:[UIView class]]) {
[self addContainer:view];
}
}
}

例如,下面是 addImageView (HPDF _ function 来自 libHaru)的实现:

- (void)addImageView:(UIImageView *)imageView
{
NSData *pngData = UIImagePNGRepresentation(imageView.image);
if (pngData != nil) {
HPDF_Image image = HPDF_LoadPngImageFromMem(_pdf, [pngData bytes], [pngData length]);
if (image != NULL) {
CGRect destRect = [self rectToPDF:imageView.frame];


float x = destRect.origin.x;
float y = destRect.origin.y - destRect.size.height;
float width = destRect.size.width;
float height = destRect.size.height;


HPDF_Page page = HPDF_GetCurrentPage(_pdf);
HPDF_Page_DrawImage(page, image, x, y, width, height);
}
}
}

希望这能让你明白。

迟到了,但可能对其他人有帮助。这听起来像是一个 PDF 模板样式的操作可能是您想要的方法,而不是在代码中构建 PDF。既然你想把它发出去,你可以连接到网络,这样你就可以使用像 睡眠云服务这样的东西,这是一个有效的邮件合并服务。将数据/图像发送给它,以便与模板合并。这种方法的好处是可以大大减少代码量,并且可以减轻 iPad 应用程序的大部分处理负担。我见过它在 iPad 应用程序中使用,它很不错。

这是一个很晚的回复,但由于我挣扎了很多与 pdf 一代,我认为它值得分享我的看法。你也可以使用 UIKit 方法来生成 pdf,而不是 Core 图形来创建上下文。

苹果已经在 绘图和印刷指南。中详细记录了这一点