Generación de documentos PDF
Spanish (Español) translation by steven (you can also view the original English article)
En un tutorial anterior, demostré cómo leer y mostrar documentos PDF con la ayuda de la biblioteca de código abierto VFR Reader. En esta lección de seguimiento, también te enseñaré cómo generar tus propios documentos PDF personalizados en la aplicación.
Antecedentes del proyecto
En iOS, hay algunas formas diferentes de dibujar contenido en la pantalla, o incluso fuera de ella. Si bien no será necesario tener un conocimiento profundo de estos diferentes métodos para seguir este tutorial, valdría la pena dedicar tu tiempo a familiarizarte con Core Graphics, UIKit y tal vez incluso con OpenGL ES. Puedes leer más sobre estos temas en la documentación de Apple.
Usaremos el framework UIKit en nuestro tutorial de hoy, lo que nos dará una buena flexibilidad sin inferir demasiada sobrecarga y desarrollo complejo. Antes de seguir leyendo, sería MUY buena idea leer al menos la sección introductoria sobre "Generación de contenido PDF" de la documentación oficial de Apple.
Básicamente, hay cinco pasos para generar un PDF en iOS. Los detalles pueden variar, pero este proceso será el mismo para todos los PDF.
- Crear un nuevo archivo PDF en el directorio de documentos: aquí estableceremos el nombre y la ubicación de nuestro nuevo PDF.
- Crear un contexto de gráficos: Este es el "entorno" en el que crearemos nuestro contenido. Es como el "libro" donde colocaremos nuestras páginas PDF.
- Comenzar una nueva página: Esto crea una página en blanco para dibujar.
- Dibujar nuestro contenido: Aquí, colocaremos texto, imágenes y otro contenido en nuestra nueva página.
- Finalizar nuestro documento: Básicamente cerramos y guardamos nuestro nuevo PDF.
Configuración del proyecto
Agregando nuestro recurso
Dibujaremos una imagen en nuestro PDF, así que continúa y agrega la imagen "tree.jpg" a nuestro proyecto. Dado que este tutorial es de dificultad intermedia, omitiré los pasos necesarios para agregar imágenes a un proyecto de Xcode, pero si no estás seguro de qué hacer, hay muchos buenos tutoriales que puedes encontrar en Tuts+ que te ayudarán afuera.
Configurando nuestra vista
Antes de comenzar con esos sencillos pasos, continúa y abre el proyecto PDFViewer de la primera parte.
Agregaremos un nuevo botón en la página "MTViewController.xib" y lo etiquetaremos como "Crear PDF" o "Make PDF".


A continuación, cambiaremos a nuestro editor "Asistente".


Ahora podemos hacer clic derecho (control-clic) y arrastrar una IBAction a nuestro "MTViewController.h", y la llamaremos "didClickMakePDF".


Ahora podemos volver al editor "Estándar" y abrir el archivo "MTViewController.m", donde implementaremos el resto de nuestros métodos de creación de PDF.


Estableciendo nuestros atributos
Para evitar que el contenido que dibujamos en nuestra página PDF se dibuje en los bordes, estableceremos un relleno de 20 puntos. En la parte superior de nuestro archivo "MTViewController.m", justo debajo de la declaración "#import", agrega lo siguiente:
1 |
#define kPadding 20
|
Y finalmente, para la configuración, crearemos un ivar que podemos usar para establecer el tamaño de nuestras páginas PDF. Agregaremos esto a @interface MTViewController(), que podemos encontrar justo debajo de nuestra nueva declaración #define.
1 |
@interface MTViewController () |
2 |
{
|
3 |
CGSize _pageSize; |
4 |
}
|
Y eso es todo por la configuración. Ahora, pasemos a los métodos PDF reales.
Paso 1: Crea un nuevo archivo PDF
Nuestro primer método configurará nuestro documento PDF y nuestro contexto PDF.
Justo debajo del método vacío "didClickMadePDF", que fue creado para nosotros cuando vinculamos nuestro botón "Crear PDF" anteriormente, crearemos un nuevo método llamado "setupPDFDocumentNamed:Width:Height". Como sugiere el nombre, tomaremos tres parámetros: un NSString para nuestro nombre PDF y dos flotantes. Un flotante será para el ancho y el otro para la altura de nuestro documento PDF.
- (void)setupPDFDocumentNamed:(NSString*)name Width:(float)width Height:(float)height {}
Dentro de este método, estableceremos el tamaño de página del documento con el ivar que creamos en nuestra interfaz.
_pageSize = CGSizeMake(width, height);
A continuación, obtendremos la ruta al directorio de documentos de nuestra aplicación y le agregaremos nuestro nombre de archivo para obtener la ruta completa del nuevo PDF.
1 |
NSString *newPDFName = [NSString stringWithFormat:@"%@.pdf", name]; |
2 |
|
3 |
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES); |
4 |
NSString *documentsDirectory = [paths objectAtIndex:0]; |
5 |
|
6 |
NSString *pdfPath = [documentsDirectory stringByAppendingPathComponent:newPDFName]; |
Paso 2: Crear un contexto gráfico
Para crear nuestro contexto de gráficos PDF, agregaremos una sola línea al final del método "setupPDF:" que acabamos de crear.
UIGraphicsBeginPDFContextToFile(pdfPath, CGRectZero, nil);
Ahora, en el método "didClickMakePDF", podemos llamar a "setupPDF:" para comenzar la creación del PDF. Estoy llamando a mi documento "NewPDF", y usaremos ese mismo nombre más adelante. Si cambias el nombre de tu documento, asegúrate de recordar usar el mismo nombre en el paso 6.
[self setupPDFDocumentNamed:@"NewPDF" Width:850 Height:1100];
Paso 3: Comenzar una nueva página
Vamos a crear un método simple llamado "beginPDFPage", usando el tamaño de página del documento que configuramos en nuestro método "setupPDF".
Justo debajo del método "setupPDF:", agrega lo siguiente:
1 |
- (void)beginPDFPage { |
2 |
UIGraphicsBeginPDFPageWithInfo(CGRectMake(0, 0, _pageSize.width, _pageSize.height), nil); |
3 |
}
|
Y así, podemos crear una nueva página PDF llamando a [self beginPDFPage]; de nuestro método "didClickMakePDF", que ahora se verá así:
1 |
- (IBAction)didClickMakePDF { |
2 |
[self setupPDFDocumentNamed:@"NewPDF" Width:850 Height:1100]; |
3 |
|
4 |
[self beginPDFPage]; |
5 |
}
|
Paso 4: Dibujar nuestro contenido
Hasta ahora, nuestros métodos han sido muy sencillos. Ahora, sin embargo, vamos a entrar en un código más grande y un poco más complejo.
Creando nuestros métodos de dibujo
Lo que queremos hacer ahora es dibujar nuestro contenido deseado en nuestra nueva página PDF. Los tres elementos que agregaremos a nuestra página son:
- Texto
- Líneas (o cajas sólidas)
- Imágenes
Como puedes imaginar, crearemos nuevos métodos para cada uno de estos elementos. Debajo de nuestro método "beginPDFPage", agrega los siguientes tres métodos:
1 |
- (CGRect)addText:(NSString*)text withFrame:(CGRect)frame fontSize:(float)fontSize { } |
2 |
|
3 |
- (CGRect)addLineWithFrame:(CGRect)frame withColor:(UIColor*)color { } |
4 |
|
5 |
- (CGRect)addImage:(UIImage*)image atPoint:(CGPoint)point { } |
Notarás que cada uno de estos métodos devuelve un CGRect, esto es para que sepamos dónde está el marco del elemento en nuestro documento, para que no dibujemos accidentalmente sobre algo.
Comencemos con el texto.
Primero, queremos configurar nuestra fuente. Aceptamos un flotante para nuestro tamaño de fuente en nuestra llamada "addText:withFrame:fontSize:", por lo que podemos crear fácilmente nuestra fuente con esta línea:
UIFont *font = [UIFont systemFontOfSize:fontSize];
A continuación, calcularemos el tamaño de nuestra cadena para asegurarnos de no derramar texto fuera de la página. También configuraremos nuestra línea para que se ajuste después de las palabras (en lugar de ajustar entre caracteres, etc.).
1 |
CGSize stringSize = [text sizeWithFont:font constrainedToSize:CGSizeMake(_pageSize.width - 2*20-2*20, _pageSize.height - 2*20 - 2*20) lineBreakMode:UILineBreakModeWordWrap]; |
2 |
|
3 |
float textWidth = frame.size.width; |
4 |
|
5 |
if (textWidth < stringSize.width) |
6 |
textWidth = stringSize.width; |
7 |
if (textWidth > _pageSize.width) |
8 |
textWidth = _pageSize.width - frame.origin.x; |
Luego, crearemos un marco para nuestro texto y lo dibujaremos en nuestra página.
1 |
CGRect renderingRect = CGRectMake(frame.origin.x, frame.origin.y, textWidth, stringSize.height); |
2 |
|
3 |
[text drawInRect:renderingRect |
4 |
withFont:font |
5 |
lineBreakMode:UILineBreakModeWordWrap |
6 |
alignment:UITextAlignmentLeft]; |
7 |
|
8 |
frame = CGRectMake(frame.origin.x, frame.origin.y, textWidth, stringSize.height); |
Y finalmente, devolveremos la variable "frame" CGRect a nuestro método de llamada.
return frame;
Y eso es todo para el texto. Ahora para las líneas...
En nuestro método "addLineWithFrame:withColor", pasaremos un UIColor para establecer el color de nuestra línea. En nuestro método "addLine", comenzamos obteniendo nuestro contexto actual, y luego establecemos el color para dibujar.
1 |
CGContextRef currentContext = UIGraphicsGetCurrentContext(); |
2 |
|
3 |
CGContextSetStrokeColorWithColor(currentContext, color.CGColor); |
Con nuestro método "addLine:", también estamos aceptando un CGRect con las coordenadas de dónde comenzar nuestra línea (x, y), cuánto durará (ancho) y qué tan grueso será (alto). Con estas coordenadas, podemos establecer la línea en su lugar:
1 |
// this is the thickness of the line
|
2 |
CGContextSetLineWidth(currentContext, frame.size.height); |
3 |
|
4 |
CGPoint startPoint = frame.origin; |
5 |
CGPoint endPoint = CGPointMake(frame.origin.x + frame.size.width, frame.origin.y); |
Y ahora, haremos el dibujo real a nuestro contexto:
1 |
CGContextBeginPath(currentContext); |
2 |
CGContextMoveToPoint(currentContext, startPoint.x, startPoint.y); |
3 |
CGContextAddLineToPoint(currentContext, endPoint.x, endPoint.y); |
4 |
|
5 |
CGContextClosePath(currentContext); |
6 |
CGContextDrawPath(currentContext, kCGPathFillStroke); |
Y, por supuesto, terminamos devolviendo el marco de nuestra línea al método de llamada:
return frame;
Agreguemos una imagen.
Nuestro método "addImage:" es realmente simple. Simplemente tomamos nuestro UIImage y lo dibujamos en el lugar apropiado de la página:
1 |
CGRect imageFrame = CGRectMake(point.x, point.y, image.size.width, image.size.height); |
2 |
[image drawInRect:imageFrame]; |
Y luego devolvemos nuestro marco:
return imageFrame;
Ten en cuenta que en cada uno de estos métodos, simplemente dibujamos en el PDF llamando a "drawInRect:" o dibujando directamente en nuestro contexto, como en "CGContextDrawPath". En ese momento, tomamos los elementos que creamos y los colocamos en la página.
Llamando a nuestros métodos de dibujo
Llamaremos a todos nuestros métodos de dibujo desde el método "didClickMakePDF", justo debajo de la llamada a "beginPDFPage". Recuerda que estamos devolviendo los marcos CGRect para cada uno de nuestros elementos, por lo que los guardaremos en variables a medida que los llamamos y luego los usaremos para posicionar nuestro siguiente elemento.
Empezando por el texto:
1 |
CGRect textRect = [self addText:@"This is some nice text here, don't you agree?" |
2 |
withFrame:CGRectMake(kPadding, kPadding, 400, 200) fontSize:48.0f]; |
Esto aparecerá 20 puntos desde arriba y 20 puntos desde la izquierda, como lo indica la constante "kPadding" que establecimos en el paso 1. Hasta ahora, todo va bien.
Ahora, tomaremos el marco "textRect" de nuestro elemento de texto y lo usaremos para colocar una línea azul.
1 |
CGRect blueLineRect = [self addLineWithFrame:CGRectMake(kPadding, textRect.origin.y + textRect.size.height + kPadding, _pageSize.width - kPadding*2, 4) |
2 |
withColor:[UIColor blueColor]]; |
A continuación, crearemos un UIImage usando la imagen "tree.jpg" que agregamos durante la configuración. Luego, dibujaremos esa imagen en nuestro PDF llamando a nuestro método "addImage:". Observa que ahora estamos usando el marco "blueLineRect" para posicionar nuestro UIImage.
1 |
UIImage *anImage = [UIImage imageNamed:@"tree.jpg"]; |
2 |
CGRect imageRect = [self addImage:anImage |
3 |
atPoint:CGPointMake((_pageSize.width/2)-(anImage.size.width/2), blueLineRect.origin.y + blueLineRect.size.height + kPadding)]; |
Y finalmente, podemos agregar otra línea para ayudar a diferenciar la imagen en nuestra página. Haremos que esta línea sea roja y, dado que no colocamos ningún elemento después de ella, no necesitamos almacenar su marco.
1 |
[self addLineWithFrame:CGRectMake(kPadding, imageRect.origin.y + imageRect.size.height + kPadding, _pageSize.width - kPadding*2, 4) |
2 |
withColor:[UIColor redColor]]; |
Paso 5: Terminando nuestro documento
Ahora que nuestros elementos han sido dibujados a nuestro contexto PDF, necesitamos terminar ese contexto, lo que cerrará nuestro archivo PDF. Crearemos otro método para hacer esto llamado "finishPDF".
1 |
- (void)finishPDF { |
2 |
UIGraphicsEndPDFContext(); |
3 |
}
|
Luego llamaremos a "[self finishPDF]" al final de nuestro método "didClickMakePDF".
[self finishPDF];
Paso 6: Ver nuestro documento
Todo lo que tenemos que hacer para ver nuestro documento PDF ahora, es volver al método "didClickOpenPDF" que creamos en el último tutorial, y modificarlo para abrir nuestro nuevo PDF, "NewPDF.pdf" en mi caso. Para hacer esto, tendremos que cambiar la mayor parte del método, ya que ahora estamos trabajando con el directorio de documentos para encontrar el archivo y cargarlo, así que continúa y reemplaza el "didClickOpenPDF" actual con lo siguiente:
1 |
- (IBAction)didClickOpenPDF { |
2 |
|
3 |
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES); |
4 |
NSString *documentsDirectory = [paths objectAtIndex:0]; |
5 |
NSString *pdfPath = [documentsDirectory stringByAppendingPathComponent:@"NewPDF.pdf"]; |
6 |
|
7 |
if([[NSFileManager defaultManager] fileExistsAtPath:pdfPath]) { |
8 |
|
9 |
ReaderDocument *document = [ReaderDocument withDocumentFilePath:pdfPath password:nil]; |
10 |
|
11 |
if (document != nil) |
12 |
{
|
13 |
ReaderViewController *readerViewController = [[ReaderViewController alloc] initWithReaderDocument:document]; |
14 |
readerViewController.delegate = self; |
15 |
|
16 |
readerViewController.modalTransitionStyle = UIModalTransitionStyleCrossDissolve; |
17 |
readerViewController.modalPresentationStyle = UIModalPresentationFullScreen; |
18 |
|
19 |
[self presentModalViewController:readerViewController animated:YES]; |
20 |
}
|
21 |
}
|
22 |
}
|
Ahora, cuando ejecutes tu aplicación, toca una vez en el botón "Crear PDF", y luego toca en el botón "Abrir PDF", y deberías ver tu nuevo PDF. Luego, podrás enviarlo por correo electrónico, imprimirlo o cerrarlo.
Conclusión
Hay muchas más cosas que se pueden dibujar en el contexto de los gráficos, pero con un poco de retoques y los documentos de Apple listos a mano, es un trabajo corto crear fantásticos PDF para tus necesidades específicas.



