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

woobm2wo  于 2023-04-22  发布在  iOS
关注(0)|答案(4)|浏览(152)

老实说,当涉及到以编程方式绘制PDF文件时,使用CoreGraphics框架是乏味的工作。
我想以编程方式创建一个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, 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示例。

yks3o0rb

yks3o0rb1#

有几件事...
首先,在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
作为一种解决方法,我已经在iPhone上成功地使用libHaru作为CoreGraphics PDF生成的替代品。最初让libHaru与我的项目一起构建有点棘手,但一旦我正确设置了我的项目,它就能很好地满足我的需求。
其次,根据PDF的格式/布局,您可以考虑使用Interface Builder创建一个视图,作为PDF输出的“模板”。(例如,为UILabels设置文本等),然后将视图的各个元素渲染到PDF中。换句话说,使用IB指定坐标,字体,图像,等,并编写代码以通用的方式呈现各种元素(例如,UILabelUIImageView等),这样你就不必硬编码所有内容。我使用了这种方法,它非常适合我的需求。同样,这可能对你的情况有意义,也可能没有意义,这取决于你的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_ 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);
        }
    }
}

希望这能给你一个想法。

z3yyvxxp

z3yyvxxp2#

这是一个迟来的回复,但由于我在PDF生成方面做了很多努力,我认为分享我的观点是值得的。除了Core graphics,你也可以使用UIKit方法来生成PDF。
Apple在绘图和打印指南中很好地记录了它。

wb1gzix0

wb1gzix03#

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

如果你想要更多关于整体设计的指导,请更明确地说明你所说的“应用中的各种对象”是什么意思,以及你想要实现什么。

rdlzhqv9

rdlzhqv94#

晚了,但可能对其他人有帮助。听起来像PDF模板风格的操作可能是你想要的方法,而不是用代码构建PDF。既然你无论如何都想通过电子邮件发送,你已经连接到网络,所以你可以使用类似Docmosis云服务的东西,这是一个有效的邮件合并服务。发送数据/图像与你的模板合并。这种方法的好处是代码少得多,并且可以从iPad应用程序中卸载大部分处理。

相关问题