在我的诚实意见中,使用 CoreGraphics 框架是一项繁琐的工作,当涉及以编程方式绘制PDF文件时。
我想以编程方式创建 PDF ,使用我应用中各种视图中的各种对象。
我很想知道iOS SDK是否有任何好的PDF教程,也许是图书馆的下载。
我已经看过这个教程PDF Creation Tutorial,但它主要是用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);
}
编辑:以下是iPhone项目中GitHub using libHaru的示例。
答案 0 :(得分:56)
有几件事......
首先,iOS中的CoreGraphics PDF生成存在一个错误,导致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,那么您应该仔细查看此问题。这是一些额外的信息:
作为一种解决方法,我在iPhone上成功使用了libHaru作为CoreGraphics PDF生成的替代品。最初使用我的项目构建libHaru有点棘手,但是一旦我正确地设置了项目,它就可以满足我的需求。
其次,根据PDF的格式/布局,您可以考虑使用Interface Builder创建一个视图,作为PDF输出的“模板”。然后,您将编写代码以加载视图,填写任何数据(例如,为UILabels设置文本等),然后将视图的各个元素呈现为PDF。换句话说,使用IB指定坐标,字体,图像等,并编写代码以通用方式呈现各种元素(例如UILabel
,UIImageView
等),这样您就不会必须硬编码一切。我使用这种方法,它很好地满足了我的需求。同样,根据PDF的格式/布局需求,这可能对您的情况有意义,也可能没有意义。
编辑:(对第1条评论的回应)
我的实施是商业产品的一部分,这意味着我无法分享完整的代码,但我可以给出一个大致的概述:
我创建了一个带视图的.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_函数来自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);
}
}
}
希望能给你这个想法。
答案 1 :(得分:12)
这是一个迟到的回复,但由于我在pdf世代中苦苦挣扎,我认为值得分享我的观点。您可以使用UIKit方法生成pdf,而不是Core图形来创建上下文。
Apple已在drawing and printing guide.
中详细记录了这一点答案 2 :(得分:8)
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]);
如果您需要有关整体设计的更多指导,请更明确地了解“整个应用中的各种对象”的含义以及您要完成的工作。
答案 3 :(得分:2)
迟到,但可能对其他人有帮助。听起来像PDF模板操作风格可能是您想要的方法,而不是在代码中构建PDF。由于您无论如何都希望通过电子邮件发送,因此您可以使用网络,因此您可以使用Docmosis云服务,这实际上是一种邮件合并服务。发送数据/图像以与模板合并。这种方法的好处是可以减少代码,并从iPad应用程序中卸载大部分处理。我已经看到它在iPad应用程序中使用它很不错。