iphone – iOS SDK – 以编程方式生成PDF文件

使用CoreGraphics框架是乏味的工作,在我的诚实观点,当涉及到编程绘制一个PDF文件

我想以编程方式创建一个PDF,在我的应用程序中使用视图中的各种对象。

我有兴趣知道是否有任何良好的PDF教程周围的iOS SDK,也许在图书馆一滴。

我看过这个教程,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,&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,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);
}

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

解决方法

几件事…

首先,在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,那么您应该仔细看看这个问题。这里有一些额外的信息:

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

作为解决方法,我使用libHaru成功在iPhone上替代CoreGraphics PDF生成。这是一个有点棘手让libHaru最初与我的项目建立,但一旦我得到我的项目设置正确,它的工作正常为我的需要。

其次,根据PDF的格式/布局,您可以考虑使用Interface Builder创建一个作为PDF输出的“模板”的视图。然后,您将编写代码以加载视图,填写任何数据(例如,为UILabels设置文本等),然后将视图的各个元素渲染到PDF中。换句话说,使用IB来指定坐标,字体,图像等,并以通用方式编写代码来渲染各种元素(例如UILabel,UIImageView等),这样您就不必对所有内容进行硬编码。我使用这种方法,它为我的需要很大。同样,这可能或可能不会对您的情况有意义,具体取决于您的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_函数来自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);
        }
    }
}

希望这给了你的想法。

相关文章

UITabBarController 是 iOS 中用于管理和显示选项卡界面的一...
UITableView的重用机制避免了频繁创建和销毁单元格的开销,使...
Objective-C中,类的实例变量(instance variables)和属性(...
从内存管理的角度来看,block可以作为方法的传入参数是因为b...
WKWebView 是 iOS 开发中用于显示网页内容的组件,它是在 iO...
OC中常用的多线程编程技术: 1. NSThread NSThread是Objecti...