2010-12-06 8 views
76

CoreGraphicsフレームワークを使用することは、プログラムでPDFファイルを描画する際には面倒な作業です。iOS SDK - プログラムで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); 
} 

EDIT:ここではiPhoneのプロジェクトにGitHub using libHaruに例を示します。

+1

笑でも、それを文書化しており、私はこれが古いですけど、iPhone上の100ページのPDFは何もありません。サーバーはiOSデバイスよりも多くの負荷をかけることになります。これは、サーバーがこれを実行しようとしている同時ユーザー数で乗算する必要があるためです。 – Armand

答えて

56

カップルの事...

まず、破損したPDFファイルになりiOS版でCoreGraphicsのPDF生成のバグがあります。私は、この問題がiOS 4.1までであることを知っています(私はiOS 4.2をテストしていません)。この問題はフォントに関連しており、PDFにテキストを含めると表示されます。

<Error>: can't get CIDs for glyphs for 'TimesNewRomanPSMT' 

トリッキーな側面は、結果のPDFは、いくつかのPDFリーダーで正常にレンダリングするが、これに失敗するということです。症状はPDFを生成するとき、あなたはこのようになり、デバッグコンソールにエラーが表示されます、ということです他の場所でレンダリングする。したがって、PDFを開くために使用されるソフトウェアを制御できれば、この問題を無視することができます(たとえば、PDFをiPhoneまたはMacデスクトップに表示するだけの場合は、 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をビルドするのはちょっと難しかったですが、プロジェクトを正しく設定したら、私のニーズにはうまくいきました。

第2に、PDFの書式/レイアウトによっては、Interface Builderを使用してPDF出力用の「テンプレート」としてのビューを作成することを検討することがあります。次に、ビューをロードしたり、データを入力したり(たとえば、UILabelsのテキストを設定するなど)、ビューの個々の要素をPDFにレンダリングするコードを記述します。言い換えれば、IBを使用して座標、フォント、画像などを指定し、さまざまな要素(たとえばUILabelUIImageViewなど)をレンダリングするコードを汎用的な方法で作成して、すべてをハードコードする必要はありません。私はこのアプローチを使用し、私のニーズに大きく貢献しました。この場合も、PDFの書式設定/レイアウトの必要性に応じて、状況によっては意味をなさない場合があります。

EDIT:(第一コメントへの応答)

私の実装では、私は完全なコードを共有することができないことを意味する商用製品の一部ですが、私は一般的な概要を与えることができます:

Iビューを含む.xibファイルを作成し、ビューを850 x 1100にサイズ調整しました(私のPDFは8.5 x 11インチをターゲットにしていたため、デザイン時座標との変換が容易になりました)。コードで

、Iビューをロード:

- (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は、次いで、様々な要素を埋めます。適切な要素を見つけるためにタグを使用しましたが、これを他の方法で行うこともできます。例:

UILabel *label = (UILabel *)[templateView viewWithTag:TAG_FIRST_NAME]; 
if (label != nil) { 
    label.text = (firstName != nil) ? firstName : @"None"; 

次に、ビューをPDFファイルにレンダリングする関数を呼び出します。この関数は、ビュー階層を再帰的に参照し、各サブビューをレンダリングします。私のプロジェクトのために、私は(ネストされたビュー用)のみラベル、ImageViewの、および表示をサポートする必要があります。

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

うまくいけば、それはあなたにアイデアを与えます。

+0

XIBをテンプレートとして使用する方法の例はありますか? – WrightsCS

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

あなたが全体のデザインに関する詳細なガイダンスをしたい場合は、してください「あなたのアプリ全体のさまざまなオブジェクト」とは何を意味しているのか、そして何を達成しようとしているのかを明確にしてください。

+0

返事をありがとう。オブジェクトでは、ユーザーが入力したテキストを取り込んでPDF上のさまざまな場所に配置したいという意味です。あなたが指摘したように、いくつかのグラフィックス。何か電子メールで送信できるフォームのようなものです。 – WrightsCS

+0

こんにちは、私は質問があります:このように(CGContextDrawImage)、PDFはもはやベクトルではありません。 PDFの単一ページに複数のベクターグラフィックスをレンダリングしたいのであれば、どうすればいいですか?また、pdfもベクターであることを確認してください。 – Paradise

2

後半ですが、おそらく他の人に役立ちます。 PDFテンプレートの操作スタイルは、コードでPDFを構築するのではなく、あなたが望むアプローチかもしれません。とにかく電子メールを送信したいので、あなたはネットに接続されているので、効果的にメールマージサービスであるDocmosisクラウドサービスのようなものを使うことができます。データ/画像を送信してテンプレートとマージします。このタイプのアプローチは、コードの量が少なくて済み、iPadアプリからの処理の負荷を軽減できます。私はそれがiPadアプリで使用されて見たことがあり、それは素晴らしかったです。

12

これは遅い回答ですが、私はpdf世代で多くの苦労をしたので、私は自分の意見を分かち合う価値があると考えました。コアグラフィックの代わりに、コンテキストを作成するために、UIKitメソッドを使用してpdfを生成することもできます。

Appleはdrawing and printing guide.

+4

FWIW-チュートリアルのサイトはGodaddyに行き詰まりました。 – CuriousRabbit

関連する問題