Development Tip

iOS SDK-프로그래밍 방식으로 PDF 파일 생성

yourdevel 2020. 10. 10. 12:04
반응형

iOS SDK-프로그래밍 방식으로 PDF 파일 생성


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 프로젝트에서 libHaru사용 하는 GitHub 의 예입니다 .


몇 가지 ...

첫째, 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

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.

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.

EDIT: (response to 1st comment)

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

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";

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

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.


It's a late reply, but as i struggled a lot with pdf generation, i considered it worthwhile to share my views. Instead of Core graphics, to create a context, you can also use UIKit methods to generate a pdf.

Apple has documented it well in the drawing and printing guide.


The PDF functions on iOS are all CoreGraphics based, which makes sense, because the draw primitives in iOS are also CoreGraphics based. If you want to be rendering 2D primitives directly into a PDF, you'll have to use CoreGraphics.

There are some shortcuts for objects that live in UIKit as well, like images. Drawing a PNG to a PDF context still requires the call to CGContextDrawImage, but you can do it with the CGImage that you can get from a UIImage, e.g.:

UIImage * myPNG = [UIImage imageNamed:@"mypng.png"];
CGContextDrawImage (pdfContext, CGRectMake(200, 200, 207, 385), [myPNG CGImage]);

If you want more guidance on overall design, please be more explicit about what you mean by "various objects throughout your app" and what you're trying to accomplish.


Late, but possibly helpful to others. It sounds like a PDF template style of operation might be the approach you want rather than building the PDF in code. Since you want to email it off anyway, you are connected to the net so you could use something like the Docmosis cloud service which is effectively a mail-merge service. Send it the data/images to merge with your template. That type of approach has the benefit of a lot less code and offloading most of the processing from your iPad app. I've seen it used in an iPad app and it was nice.

참고URL : https://stackoverflow.com/questions/4362734/ios-sdk-programmatically-generate-a-pdf-file

반응형