2010-12-06 10 views
76

El uso del marco CoreGraphics es un trabajo tedioso, en mi opinión sincera, cuando se trata de dibujar un archivo PDF mediante programación.iOS SDK - Genere mediante programación un archivo PDF

Me gustaría crear programáticamente un PDF, usando varios objetos de vistas en toda mi aplicación.

Estoy interesado en saber si hay algunos buenos tutoriales en PDF para iOS SDK, tal vez una caída en la biblioteca.

He visto este tutorial, PDF Creation Tutorial, pero fue escrito principalmente en C. Buscando más estilo Objective-C. Esto también parece una forma ridícula de escribir en un archivo PDF, teniendo que calcular dónde se colocarán las líneas y otros objetos.

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: He aquí un ejemplo de GitHub using libHaru en un proyecto de iPhone.

+1

, sé que esto es viejo, pero de 100 páginas PDF en un iPhone no es nada. hará hincapié en el servidor más que en el dispositivo iOS, ya que el servidor tendrá que multiplicarlo por el número de usuarios concurrentes que intentan hacerlo. – Armand

Respuesta

56

Un par de cosas ...

En primer lugar, hay un error con la generación CoreGraphics PDF en IOS que se traduce en archivos PDF corruptos. Sé que este problema existe e incluye iOS 4.1 (no he probado iOS 4.2). El problema está relacionado con las fuentes y solo aparece si incluye texto en su PDF. El síntoma es que, al generar el PDF, verá errores en la consola de depuración que se ven así:

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

El aspecto difícil es que el PDF resultante rendir bien en algunos lectores de PDF, pero no pueden renderizar en otros lugares. Por lo tanto, si tiene control sobre el software que se utilizará para abrir su PDF, puede ignorar este problema (por ejemplo, si solo desea mostrar el PDF en los escritorios de iPhone o Mac, entonces debería estar bien usando CoreGraphics). Sin embargo, si necesita crear un PDF que funcione en cualquier lugar, debe analizar este problema con más detalle. Aquí hay algo de información adicional:

http://www.iphonedevsdk.com/forum/iphone-sdk-development/15505-pdf-font-problem-cant-get-cids-glyphs.html#post97854

Como solución, he utilizado con éxito en libHaru iPhone como un reemplazo para la generación CoreGraphics PDF. Fue un poco complicado conseguir que libHaru construyera con mi proyecto inicialmente, pero una vez que obtuve la configuración de mi proyecto correctamente, funcionó bien para mis necesidades.

En segundo lugar, dependiendo del formato/disposición de su PDF, puede considerar el uso de Interface Builder para crear una vista que sirva como una "plantilla" para su salida de PDF. Luego, debe escribir el código para cargar la vista, completar los datos (por ejemplo, establecer el texto para UILabels, etc.) y luego representar los elementos individuales de la vista en el PDF.En otras palabras, use IB para especificar coordenadas, fuentes, imágenes, etc. y escriba código para procesar varios elementos (por ejemplo, UILabel, UIImageView, etc.) de forma genérica, para que no tenga que codificar todo. Usé este enfoque y funcionó muy bien para mis necesidades. Nuevamente, esto puede o no tener sentido para su situación dependiendo de las necesidades de formato/diseño de su PDF.

EDIT: (respuesta a la primera comentario)

Mi aplicación es parte de un producto comercial que significa que no puedo compartir el código completo, pero puede dar un esquema general:

I Creé un archivo .xib con una vista y dimensioné la vista a 850 x 1100 (mi PDF apuntaba a 8.5 x 11 pulgadas, por lo que facilita la traducción a/desde las coordenadas de tiempo de diseño).

En el código, I cargar la vista:

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

luego Yo lleno en diversos elementos. Usé etiquetas para encontrar los elementos apropiados, pero podrías hacerlo de otras maneras. Ejemplo:

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

Luego invoco una función para mostrar la vista al archivo PDF. Esta función recorre de forma recursiva la jerarquía de vistas y representa cada subvista. Para mi proyecto, tengo que soportar sólo la etiqueta, ImageView y Vista (para las vistas anidadas):

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

A modo de ejemplo, aquí está mi aplicación de addImageView (funciones HPDF_ son de 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); 
     } 
    } 
} 

Espero que eso te dé la idea.

+0

. ¿Tiene algún ejemplo de cómo usar un XIB como plantilla? – WrightsCS

8

Las funciones de PDF en iOS están todas basadas en CoreGraphics, lo que tiene sentido, porque las primitivas de extracción en iOS también están basadas en CoreGraphics. Si quiere renderizar primitivas 2D directamente en un PDF, deberá usar CoreGraphics.

También hay algunos atajos para objetos que también viven en UIKit, como las imágenes. Dibujo de un PNG a un contexto PDF todavía requiere la llamada a CGContextDrawImage, pero se puede hacer con el CGImage que se puede obtener de un UIImage, por ejemplo:

UIImage * myPNG = [UIImage imageNamed:@"mypng.png"]; 
CGContextDrawImage (pdfContext, CGRectMake(200, 200, 207, 385), [myPNG CGImage]); 

Si desea más orientación sobre el diseño en general, por favor más explícito sobre lo que quiere decir con "varios objetos en toda su aplicación" y lo que está tratando de lograr.

+0

Gracias por la respuesta. Por objetos, simplemente me refiero a que estoy tomando texto ingresado por el usuario y quiero colocarlo en varias ubicaciones en el PDF. Y como ha señalado, algunos gráficos. Algo así como un formulario que puede ser enviado por correo electrónico. – WrightsCS

+0

Hola, tengo una pregunta: de esta manera (CGContextDrawImage), el pdf ya no es vector. ¿Qué debo hacer si quiero renderizar gráficos vectoriales múltiples en la página única de un pdf? Mientras tanto, asegúrese de que el pdf sea también el vector – Paradise

2

Tarde, pero posiblemente útil para otros. Parece que el estilo de operación de una plantilla de PDF podría ser el enfoque que desea en lugar de construir el PDF en el código. Dado que desea enviarlo por correo electrónico de todos modos, está conectado a la red, por lo que podría usar algo como el servicio en la nube Docmosis que es efectivamente un servicio de combinación de correspondencia. Envíele los datos/imágenes para combinarlos con su plantilla. Ese tipo de enfoque tiene el beneficio de un código mucho menor y la descarga de la mayoría de los procesos de su aplicación para iPad. Lo he visto usado en una aplicación para iPad y fue agradable.

12

Es una respuesta tardía, pero como luché mucho con la generación de PDF, consideré que valía la pena compartir mis puntos de vista. En lugar de gráficos Core, para crear un contexto, también puede usar los métodos UIKit para generar un pdf.

Apple ha documentado bien en el jaja drawing and printing guide.

+4

FWIW: el sitio para el tutorial no tiene final en Godaddy. – CuriousRabbit

Cuestiones relacionadas