iOS SDK - 以编程方式生成PDF文件 [英] iOS SDK - Programmatically generate a PDF file

查看:194
本文介绍了iOS SDK - 以编程方式生成PDF文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在我的诚实意见中,使用 CoreGraphics 框架是一项繁琐的工作,当涉及以编程方式绘制PDF文件时。

Using the CoreGraphics framework is tedious work, in my honest opinion, when it comes to programmatically drawing a PDF file.

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

I would like to programmatically create a PDF, using various objects from views throughout my app.

我很想知道是否有任何好的PDF教程对于iOS SDK来说,可能是库中的一个下降。

I am interested to know if there are any good PDF tutorials around for the iOS SDK, maybe a drop in library.

我见过这个教程, PDF创建教程,但它主要是用C语言编写的。寻找更多的Objective-C风格。这似乎是一种写入PDF文件的荒谬方式,必须计算线和其他对象的放置位置。

I've seen this tutorial, PDF Creation Tutorial, but it was mostly written in C. Looking for more Objective-C style. This also seems like a ridiculous way to write to a PDF file, having to calculate where lines and other objects will be placed.

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);
}

编辑:以下是 GitHub使用libHaru

Here's an example on GitHub using libHaru in an iPhone project.

推荐答案

有几件事......

A couple things...

首先,有iOS中CoreGraphics PDF生成导致PDF损坏的错误。我知道这个问题存在并包括iOS 4.1(我还没有测试iOS 4.2)。该问题与字体有关,只有在PDF中包含文本时才会显示。症状是,在生成PDF时,您将在调试控制台中看到如下所示的错误:

First, there is a bug with CoreGraphics PDF generation in iOS that results in corrupted PDFs. I know this issue exists up to and including iOS 4.1 (I haven't tested iOS 4.2). The issue is related to fonts and only shows up if you include text in your PDF. The symptom is that, when generating the PDF, you'll see errors in the debug console that look like this:

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

棘手的方面是结果PDF将在某些PDF阅读器中呈现正常,但无法在其他位置呈现。因此,如果您可以控制将用于打开PDF的软件,您可以忽略此问题(例如,如果您只打算在iPhone或Mac桌面上显示PDF,那么您应该可以使用CoreGraphics中)。但是,如果您需要创建可在任何地方使用的PDF,那么您应该仔细查看此问题。这里有一些额外的信息:

The tricky aspect is that the resulting PDF will render fine in some PDF readers, but fail to render in other places. So, if you have control over the software that will be used to open your PDF, you may be able to ignore this issue (e.g., if you only intend to display the PDF on the iPhone or Mac desktops, then you should be fine using CoreGraphics). However, if you need to create a PDF that works anywhere, then you should take a closer look at this issue. Here's some additional info:

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

作为解决方法,我在iPhone上成功使用了libHaru作为CoreGraphics PDF生成的替代品。最初使用我的项目建立libHaru有点棘手,但是一旦我的项目设置正确,它就能满足我的需求。

As a workaround, I've used libHaru successfully on iPhone as a replacement for CoreGraphics PDF generation. It was a little tricky getting libHaru to build with my project initially, but once I got my project setup properly, it worked fine for my needs.

其次,取决于在PDF格式/布局中,您可以考虑使用Interface Builder创建一个视图,作为PDF输出的模板。然后,您将编写代码以加载视图,填写任何数据(例如,为UILabels设置文本等),然后将视图的各个元素呈现为PDF。换句话说,使用IB来指定坐标,字体,图像等,并编写代码来呈现各种元素(例如, UILabel UIImageView 等)以通用方式,因此您不必对所有内容进行硬编码。我使用了这种方法,它很好地满足了我的需求。同样,这可能对您的情况有意义,也可能没有意义,具体取决于PDF的格式/布局需求。

Second, depending on the format/layout of your PDF, you might consider using Interface Builder to create a view that serves as a "template" for your PDF output. You would then write code to load the view, fill in any data (e.g., set text for UILabels, etc.), then render the individual elements of the view into the PDF. In other words, use IB to specify coordinates, fonts, images, etc. and write code to render various elements (e.g., UILabel, UIImageView, etc.) in a generic way so you don't have to hard-code everything. I used this approach and it worked out great for my needs. Again, this may or may not make sense for your situation depending on the formatting/layout needs of your PDF.

编辑:(对第一条评论)

我的实施是商业产品的一部分,这意味着我无法分享完整的代码,但我可以给出一个大致的概述:

My implementation is part of a commercial product meaning that I can't share the full code, but I can give a general outline:

我创建了一个带视图的.xib文件,并将视图大小调整为850 x 1100(我的PDF格式为8.5 x 11英寸,因此可以轻松转换为设计时/从设计时转换坐标)。

I created a .xib file with a view and sized the view to 850 x 1100 (my PDF was targeting 8.5 x 11 inches, so this makes it easy to translate to/from design-time coordinates).

在代码中,我加载视图:

In code, I load the view:

- (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;
}

然后我填写各种元素。我使用标签来查找适当的元素,但您可以通过其他方式执行此操作。示例:

I then fill in various elements. I used tags to find the appropriate elements, but you could do this other ways. Example:

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

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

Then I call a function to render the view to the PDF file. This function recursively walks the view hierarchy and renders each subview. For my project, I need to support only Label, ImageView, and View (for nested views):

- (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):

As an example, here's my implementation of addImageView (HPDF_ functions are from 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);
        }
    }
}

希望这会给你一个想法。

Hopefully that gives you the idea.

这篇关于iOS SDK - 以编程方式生成PDF文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆