web-dev-qa-db-ja.com

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

編集:libHaruを使用したGitHub のiPhoneプロジェクトの例です。

79
WrightsCS

いくつかのこと...

まず、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を使用して座標、フォント、画像などを指定し、さまざまな要素(UILabelUIImageViewなど)を一般的な方法でレンダリングするコードを記述します。すべてをハードコーディングする必要はありません。私はこのアプローチを使用し、私のニーズにぴったり合っていました。繰り返しますが、これは、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);
        }
    }
}

うまくいけばアイデアが得られます。

56
cbranch

返信は遅くなりましたが、pdfの生成に苦労したため、意見を共有する価値があると考えました。 Coreグラフィックの代わりに、コンテキストを作成するために、UIKitメソッドを使用してpdfを生成することもできます。

Appleは 描画および印刷ガイド で文書化しています

12
Bani Uppal

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

全体的な設計に関する詳細なガイダンスが必要な場合は、「アプリ全体のさまざまなオブジェクト」が意味することと、達成しようとしていることをより明確にしてください。

8
Ben Zotto

遅いが、おそらく他の人に役立つ。 PDFテンプレートスタイルの操作スタイルが、コードでPDFを構築するよりも、あなたが望むアプローチかもしれません。とにかくメールで送信したいので、 Docmosis クラウドサービスのようなものを使用できるようにネットに接続します。これは事実上、メールマージサービスです。テンプレートにマージするデータ/イメージを送信します。コードがはるかに少なく、iPadアプリからほとんどの処理をオフロードできるという利点があります。

2
Paul Jowett