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

我想以编程方式创建一个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);
}

EDIT:这里是一个iPhone项目中关于GitHub using libHaru的示例.

推荐答案

有几件事...

首先,iOS中的CoreGraphics PDF生成存在导致PDF损坏的错误.我知道这个问题存在于iOS4.1之前(包括iOS4.1)(我还没有测试iOS4.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的格式/布局需求.

编辑:(回复第一条 comments )

我的实现是商业产品的一部分,这意味着我不能共享完整的代码,但我可以给出大致的概述:

我创造了一个.具有视图的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文件.此函数递归遍历视图层次 struct 并呈现每个子视图.对于我的项目,我只需要支持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_Functions来自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);
        }
    }
}

希望这能给你带来灵感.

Ios相关问答推荐

(Flutter)获取图像从图像拾取器保存和加载问题

SwiftUI中ForEach和@ State数组的可能并发问题

在TVOS中访问SWIFT中的objc++函数时发送给类的无法识别的 Select 符

由于已存在同名项目,因此无法将Mapbox.xcframework-ios.sign复制到Signature中

如何删除点击时按钮的不透明动画?

Strange UIView.animate更改UIButton标题 colored颜色 的行为

如果使用 .onAppear 设置,为什么 SwiftUI Picker 不显示正确的选定值

为什么 Swift 不在启动的同一线程上恢复异步函数?

FileHandle 不会在 iOS 中释放内存

VStack 显示错误实例方法 'sheet(item:onDismiss:content:)' 要求 'Int' 符合 'Identifiable'

通过MatchedGeometryEffect向上滑动视图

如何使用 Swift 文档注释

如何用 Swift 圆化 UILabel 的边缘

用 PC 调试 ipad safari

是否可以使用 sharedHTTPCookieStorage 为 UIWebView 手动设置 cookie?

~= Swift 中的运算符

如何缩小 UIImage 并使其同时变得清晰/锐利而不是模糊?

在 iOS 上存储身份验证令牌 - NSUserDefaults 与 keys 串?

为给定的 UIColor 获取更亮和更暗的 colored颜色 变化

zoom UIButton 动画 - Swift