Die Verwendung des CoreGraphics- Frameworks ist meiner ehrlichen Meinung nach eine mühsame Arbeit, wenn es darum geht, eine PDF-Datei programmgesteuert zu zeichnen.
Ich möchte programmgesteuert ein PDF mit verschiedenen Objekten aus Ansichten in meiner App erstellen .
Ich bin interessiert zu wissen, ob es gute PDF-Tutorials für das iOS SDK gibt, vielleicht einen Drop in der Bibliothek.
Ich habe dieses Tutorial gesehen, das PDF-Erstellungs-Tutorial , aber es wurde größtenteils in C geschrieben. Auf der Suche nach mehr Objective-C-Stil. Dies scheint auch eine lächerliche Möglichkeit zu sein, in eine PDF-Datei zu schreiben und zu berechnen, wo Linien und andere Objekte platziert werden.
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);
}
BEARBEITEN: Hier ist ein Beispiel für GitHub mit libHaru in einem iPhone-Projekt.
quelle
Antworten:
Ein paar Dinge ...
Erstens gibt es einen Fehler bei der CoreGraphics PDF-Generierung in iOS, der zu beschädigten PDFs führt. Ich weiß, dass dieses Problem bis einschließlich iOS 4.1 besteht (ich habe iOS 4.2 nicht getestet). Das Problem hängt mit Schriftarten zusammen und wird nur angezeigt, wenn Sie Text in Ihre PDF-Datei aufnehmen. Das Symptom ist, dass beim Generieren der PDF-Datei Fehler in der Debug-Konsole angezeigt werden, die folgendermaßen aussehen:
<Error>: can't get CIDs for glyphs for 'TimesNewRomanPSMT'
Der schwierige Aspekt ist, dass das resultierende PDF in einigen PDF-Readern gut gerendert wird, an anderen Stellen jedoch nicht gerendert werden kann. Wenn Sie also die Kontrolle über die Software haben, die zum Öffnen Ihrer PDF-Datei verwendet wird, können Sie dieses Problem möglicherweise ignorieren (z. B. wenn Sie die PDF-Datei nur auf dem iPhone- oder Mac-Desktop anzeigen möchten, sollten Sie sie problemlos verwenden können CoreGraphics). Wenn Sie jedoch eine PDF-Datei erstellen müssen, die überall funktioniert, sollten Sie sich dieses Problem genauer ansehen. Hier einige zusätzliche Informationen:
http://www.iphonedevsdk.com/forum/iphone-sdk-development/15505-pdf-font-problem-cant-get-cids-glyphs.html#post97854
Als Problemumgehung habe ich libHaru erfolgreich auf dem iPhone als Ersatz für die CoreGraphics PDF-Generierung verwendet. Es war anfangs etwas schwierig, libHaru mit meinem Projekt zu erstellen, aber sobald ich mein Projekt richtig eingerichtet hatte, funktionierte es gut für meine Bedürfnisse.
Zweitens können Sie je nach Format / Layout Ihrer PDF-Datei in Betracht ziehen, Interface Builder zu verwenden, um eine Ansicht zu erstellen, die als "Vorlage" für Ihre PDF-Ausgabe dient. Sie würden dann Code schreiben, um die Ansicht zu laden, Daten einzugeben (z. B. Text für UILabels festlegen usw.) und dann die einzelnen Elemente der Ansicht in das PDF zu rendern. (ZB mit anderen Worten, zu spezifizieren Verwendung IB Koordinaten, Schriften, Bildern, etc. und Code schreiben , um verschiedene Elemente zu machen
UILabel
,UIImageView
usw.) in allgemeiner Weise , so dass Sie hart Code alles nicht haben. Ich habe diesen Ansatz verwendet und er hat sich hervorragend für meine Bedürfnisse bewährt. Auch dies kann je nach Formatierungs- / Layoutanforderungen Ihrer PDF-Datei für Ihre Situation sinnvoll sein oder auch nicht.EDIT: (Antwort auf 1. Kommentar)
Meine Implementierung ist Teil eines kommerziellen Produkts, was bedeutet, dass ich nicht den vollständigen Code freigeben kann, aber einen allgemeinen Überblick geben kann:
Ich habe eine .xib-Datei mit einer Ansicht erstellt und die Ansicht auf 850 x 1100 skaliert (meine PDF-Datei war auf 8,5 x 11 Zoll ausgerichtet, sodass die Übersetzung in / von Entwurfszeitkoordinaten einfach ist).
Im Code lade ich die Ansicht:
- (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; }
Ich fülle dann verschiedene Elemente aus. Ich habe Tags verwendet, um die entsprechenden Elemente zu finden, aber Sie können dies auch auf andere Weise tun. Beispiel:
UILabel *label = (UILabel *)[templateView viewWithTag:TAG_FIRST_NAME]; if (label != nil) { label.text = (firstName != nil) ? firstName : @"None";
Dann rufe ich eine Funktion auf, um die Ansicht in die PDF-Datei zu rendern. Diese Funktion durchläuft rekursiv die Ansichtshierarchie und rendert jede Unteransicht. Für mein Projekt muss ich nur Label, ImageView und View unterstützen (für verschachtelte Ansichten):
- (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]; } } }
Als Beispiel ist hier meine Implementierung von addImageView (HPDF_-Funktionen stammen von 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); } } }
Hoffentlich kommt Ihnen das auf die Idee.
quelle
Es ist eine späte Antwort, aber da ich viel mit der PDF-Generierung zu kämpfen hatte, hielt ich es für sinnvoll, meine Ansichten zu teilen. Anstelle von Core-Grafiken können Sie zum Erstellen eines Kontexts auch UIKit-Methoden verwenden, um ein PDF zu generieren.
Apple hat dies im Zeichen- und Druckhandbuch gut dokumentiert .
quelle
Die PDF-Funktionen unter iOS basieren alle auf CoreGraphics, was sinnvoll ist, da die Zeichenprimitive in iOS auch auf CoreGraphics basieren. Wenn Sie 2D-Grundelemente direkt in eine PDF-Datei rendern möchten, müssen Sie CoreGraphics verwenden.
Es gibt einige Verknüpfungen für Objekte, die ebenfalls in UIKit leben, wie z. B. Bilder. Das Zeichnen eines PNG in einen PDF-Kontext erfordert weiterhin den Aufruf von
CGContextDrawImage
, aber Sie können dies mit dem CGImage tun, das Sie von einem UIImage erhalten können, z.UIImage * myPNG = [UIImage imageNamed:@"mypng.png"]; CGContextDrawImage (pdfContext, CGRectMake(200, 200, 207, 385), [myPNG CGImage]);
Wenn Sie weitere Anleitungen zum Gesamtdesign wünschen, geben Sie bitte genauer an, was Sie unter "verschiedenen Objekten in Ihrer App" verstehen und was Sie erreichen möchten.
quelle
Spät, aber möglicherweise hilfreich für andere. Es hört sich so an, als wäre eine PDF-Vorlage der gewünschte Ansatz, anstatt die PDF-Datei im Code zu erstellen. Da Sie es trotzdem per E-Mail versenden möchten, sind Sie mit dem Netz verbunden, sodass Sie so etwas wie den Docmosis- Clouddienst verwenden können, der praktisch ein Seriendruckdienst ist. Senden Sie ihm die Daten / Bilder, um sie mit Ihrer Vorlage zusammenzuführen. Diese Art von Ansatz hat den Vorteil, dass viel weniger Code vorhanden ist und der größte Teil der Verarbeitung von Ihrer iPad-App abgeladen wird. Ich habe gesehen, dass es in einer iPad-App verwendet wurde und es war schön.
quelle