Implementing PDF Generation in Mobile Apps
PDF generation on mobile needed in specific scenarios: export invoice or act in finance app, save report in fitness tracker, export request in CRM. Approach depends on document complexity: simple text + table done in a day, complex layout with images and custom fonts takes longer.
iOS: PDFKit and UIGraphicsPDFRenderer
Two main approaches on iOS.
UIGraphicsPDFRenderer—draw PDF as Canvas. Full control over placement, but each element positioned manually:
func generateInvoicePDF(invoice: Invoice) -> Data {
let pageRect = CGRect(x: 0, y: 0, width: 595.2, height: 841.8) // A4 in pt
let renderer = UIGraphicsPDFRenderer(bounds: pageRect)
return renderer.pdfData { context in
context.beginPage()
let ctx = context.cgContext
// Header
let titleAttrs: [NSAttributedString.Key: Any] = [
.font: UIFont.systemFont(ofSize: 18, weight: .bold),
.foregroundColor: UIColor.black
]
"INVOICE #\(invoice.number)".draw(at: CGPoint(x: 40, y: 40), withAttributes: titleAttrs)
// Items table
drawInvoiceTable(invoice.items, in: ctx, startY: 120, pageWidth: 595.2)
// If content doesn't fit — beginPage() for next page
}
}
HTML → PDF rendering. Complex layouts (tables, columns, images) easier described in HTML/CSS and converted via WKWebView:
class HTMLToPDFConverter: NSObject, WKNavigationDelegate {
private var webView: WKWebView!
func convert(html: String, completion: @escaping (Data?) -> Void) {
webView = WKWebView(frame: CGRect(x: 0, y: 0, width: 595, height: 842))
webView.navigationDelegate = self
webView.loadHTMLString(html, baseURL: nil)
self.completion = completion
}
func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
let config = WKPDFConfiguration()
config.rect = CGRect(origin: .zero, size: CGSize(width: 595, height: 842))
webView.createPDF(configuration: config) { result in
self.completion?(try? result.get())
}
}
}
WKWebView.createPDF (iOS 14+)—easiest for complex layouts. CSS @page rules control page breaks (page-break-before: always), fonts, margins.
PDFKit—for manipulating existing PDFs: add page, insert annotation, merge documents. PDFDocument, PDFPage, PDFAnnotation—simple API.
Android: PdfDocument and WebView
android.graphics.pdf.PdfDocument—native generation via Canvas API, like UIGraphicsPDFRenderer:
fun generatePdf(invoice: Invoice): ByteArray {
val document = PdfDocument()
val pageInfo = PdfDocument.PageInfo.Builder(595, 842, 1).create() // A4
val page = document.startPage(pageInfo)
val canvas = page.canvas
val paint = Paint().apply {
textSize = 18f
isFakeBoldText = true
}
canvas.drawText("INVOICE #${invoice.number}", 40f, 60f, paint)
drawInvoiceTable(canvas, invoice.items, startY = 120f)
document.finishPage(page)
val output = ByteArrayOutputStream()
document.writeTo(output)
document.close()
return output.toByteArray()
}
WebView → PDF. WebView.createPrintDocumentAdapter + PrintManager—standard on Android. But opens print dialog, doesn't return ByteArray. For programmatic—WebView + PrintDocumentAdapter with custom PrintManager via reflection or libraries.
Alternative—iText 7 (AGPL, paid for commercial) or Apache PDFBox (Apache 2.0). PdfBox on Android—port with some limits, sufficient for most. OpenPDF (iText 2.x fork, LGPL)—good balance of features and license.
Cyrillic and Fonts
Standard system fonts in PDF may not embed correctly when opened elsewhere. Right approach—embed font in PDF. iOS: UIFont(name:size:) with bundled TTF. Android with iText/OpenPDF: PdfFont.createFont("assets/fonts/Roboto-Regular.ttf", PdfEncodings.IDENTITY_H, true)—true means embed in document.
Without embed, Cyrillic often displays as squares on devices without Russian fonts.
Share and Save
After generation—UIActivityViewController (iOS) or FileProvider + ACTION_SEND Intent (Android). Save to Files/Downloads: UIDocumentPickerViewController (iOS) or MediaStore.Downloads URI (Android 10+).
View without external app: PDFView from PDFKit (iOS), PdfRenderer (Android)—page-by-page rendering from file to Bitmap.
Timelines
2–3 working days for standard documents (invoice, act, report). Complex layout with images, tables, multipage flow—up to 5 days. Cost calculated individually.







