[iphone] 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

해결 방법으로 CoreGraphics PDF 생성 대신 iPhone에서 libHaru를 성공적으로 사용했습니다. 처음에는 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);
        }
    }
}

그것이 당신에게 아이디어를 제공하기를 바랍니다.


답변

답장이 늦었지만 pdf 생성에 많은 어려움을 겪었 기 때문에 내 의견을 공유하는 것이 가치가 있다고 생각했습니다. Core 그래픽 대신 컨텍스트를 생성하기 위해 UIKit 메서드를 사용하여 pdf를 생성 할 수도 있습니다.

Apple은 드로잉 및 인쇄 가이드 에서이를 잘 문서화했습니다 .


답변

iOS의 PDF 기능은 모두 CoreGraphics 기반이며, 이는 iOS의 그리기 기본 요소도 CoreGraphics 기반이기 때문입니다. 2D 프리미티브를 PDF로 직접 렌더링하려면 CoreGraphics를 사용해야합니다.

이미지와 같이 UIKit에있는 개체에 대한 몇 가지 바로 가기가 있습니다. PDF 컨텍스트에 PNG를 그리 CGContextDrawImage려면를 호출해야 하지만 UIImage에서 가져올 수있는 CGImage를 사용하여 수행 할 수 있습니다. 예 :

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

전체적인 디자인에 대한 더 많은 지침이 필요한 경우 “앱 전체의 다양한 개체”가 의미하는 바와 달성하려는 작업에 대해 더 명확하게 설명하세요.


답변

늦었지만 다른 사람들에게 도움이 될 수 있습니다. PDF 템플릿 스타일의 작업이 코드로 PDF를 작성하는 것보다 원하는 접근 방식 일 수 있습니다. 어쨌든 이메일을 보내고 싶기 때문에 인터넷에 연결되어 있으므로 효과적으로 메일 병합 서비스 인 Docmosis 클라우드 서비스 와 같은 것을 사용할 수 있습니다 . 템플릿과 병합 할 데이터 / 이미지를 보냅니다. 이러한 유형의 접근 방식은 코드가 훨씬 적고 iPad 앱에서 대부분의 처리를 오프로드하는 이점이 있습니다. 나는 그것이 iPad 앱에서 사용되는 것을 보았고 좋았습니다.


답변