1. ホーム
  2. iphone

iOS SDK - プログラムでPDFファイルを生成する

2023-09-26 19:12:37

質問

を使用して コアグラフィックス フレームワークを使用することは、プログラムで PDF ファイルを描画する場合、正直なところ面倒な作業です。

をプログラム的に作成したいのですが PDF を作成したいのですが、アプリ全体のビューから様々なオブジェクトを使用しています。

iOS SDK のための良い PDF チュートリアルがあるかどうか、おそらくドロップインライブラリについて知りたいです。

このチュートリアルを見たことがあります。 PDF 作成チュートリアル しかし、それはほとんど 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です。 以下は libHaru を使った GitHub の例です。 をiPhoneのプロジェクトで使っています。

どのように解決するのですか?

いくつかあるのですが...

まず、iOS での CoreGraphics PDF 生成には、破損した PDF を生成するバグがあります。 この問題は、iOS 4.1 までとそれ以降に存在することがわかっています (iOS 4.2 ではテストしていません)。 この問題はフォントに関連しており、PDFにテキストが含まれている場合のみ表示されます。 症状は、PDF を生成するときに、デバッグ コンソールに次のようなエラーが表示されることです。

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

厄介な点は、作成された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

回避策として、私は iPhone で CoreGraphics PDF 生成の代わりとして libHaru をうまく使っています。 最初に libHaru を私のプロジェクトと一緒にビルドするのは少し厄介でしたが、いったん私のプロジェクトを適切にセットアップすると、私のニーズに対して問題なく機能しました。

次に、PDF のフォーマットやレイアウトによっては、Interface Builder を使用して、PDF 出力のテンプレートとなるビューを作成することを検討することができます。 次に、PDFのフォーマットやレイアウトによっては、Interface Builderを使って、PDF出力のテンプレートとなるビューを作成することもできます。 言い換えれば、IBを使用して座標、フォント、画像などを指定し、様々な要素をレンダリングするコードを書きます (例, UILabel , UIImageView など)を汎用的に使用することで、すべてをハードコードする必要がありません。 私はこの方法を使いましたが、私のニーズにはぴったりでした。 繰り返しますが、これは、PDFのフォーマット/レイアウトのニーズに応じて、あなたの状況にとって意味をなすかもしれませんし、そうでないかもしれません。

編集。 (1番目のコメントへの返答)

私の実装は商用製品の一部であるため、完全なコードを共有することはできませんが、大まかな概要を説明することはできます。

私はビューを持つ .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);
        }
    }
}

これでお分かりいただけたでしょうか?