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


79

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 의 예입니다 .


1
하하,이게 오래되었다는 건 알지만, 아이폰에서 100 페이지짜리 pdf는 아무것도 아닙니다. 서버는이를 시도하는 동시 사용자 수로 곱해야하기 때문에 iOS 장치보다 서버에 더 많은 스트레스를 줄 것입니다.
Armand

답변:


56

몇 가지 ...

첫째, 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);
        }
    }
}

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


XIB를 템플릿으로 사용하는 방법에 대한 예가 있습니까?
WrightsCS 2010

12

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

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


4
FWIW— 튜토리얼 사이트는 Godaddy로 막 다른 곳입니다.
CuriousRabbit

8

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의 다양한 위치에 배치하고 싶다는 의미입니다. 그리고 당신이 지적했듯이 일부 그래픽. 이메일로 보낼 수있는 양식과 같은 SOmething.
WrightsCS

안녕하세요, 질문이 있습니다. 이런 식으로 (CGContextDrawImage) pdf는 더 이상 벡터가 아닙니다. 여러 벡터 그래픽을 pdf의 단일 페이지로 렌더링하려면 어떻게해야합니까? 그 동안 pdf도 벡터인지 확인하십시오
Paradise

2

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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.