Реалізація генерації PDF у мобільному додатку
Клієнти часто просять вивантажити рахунок або акт із додатку. Ми зіткнулися з цим завданням у проєкті для фінансового сервісу: потрібно було за 2 тижні зробити генерацію invoice.pdf з логотипом, таблицею позицій та кирилицею. Підхід обрали різний для iOS та Android, але результат один. Ми займаємося мобільною розробкою понад 5 років і реалізували генерацію PDF для 30+ додатків. Зараз розповімо, як вирішуємо такі завдання під ключ.
Кожен проєкт унікальний за складністю верстки та вимогами до шрифтів, але загальні принципи залишаються незмінними. Вибір підходу впирається у складність документа — простий текст плюс таблиця вирішується Canvas API, а складна верстка із зображеннями та нестандартними шрифтами вимагає HTML → PDF. Порівняємо у таблиці.
| Підхід | iOS | Android | Складність верстки | Рекомендація |
|---|---|---|---|---|
| Canvas API | UIGraphicsPDFRenderer | PdfDocument | Низька-середня | Прості рахунки, акти |
| HTML → PDF | WKWebView.createPDF | WebView + PrintManager | Висока | Складні звіти, брошури |
| Бібліотеки (iText, OpenPDF) | – | iText 7 / OpenPDF | Будь-яка | Комерційні шаблони |
Для документів з нестандартними шрифтами або складною сіткою HTML-підхід дає більше гнучкості, але вимагає обережності з розривами сторінок. Canvas API, навпаки, дає повний контроль над пікселями, але кожен елемент доводиться позиціонувати вручну. Отримайте консультацію щодо вашого проєкту — ми допоможемо підібрати оптимальний підхід.
Як обрати підхід до генерації PDF?
Вибір впирається у складність документа. Якщо потрібен простий рахунок з парою полів і таблицею — використовуйте Canvas API. Для багатосторінкових звітів з логотипами, колонтитулами та довільними шрифтами — HTML→PDF. У нас був випадок, коли клієнт хотів PDF з динамічними графіками; ми обрали HTML+svg і конвертували через WKWebView. Все влізло на 3 сторінки з автоматичними розривами.
iOS: PDFKit та UIGraphicsPDFRenderer
На iOS два основних підходи.
UIGraphicsPDFRenderer — малюємо PDF як Canvas. Повний контроль над розташуванням, але кожен елемент треба розміщувати вручну. Ось приклад генерації рахунку:
func generateInvoicePDF(invoice: Invoice) -> Data {
let pageRect = CGRect(x: 0, y: 0, width: 595.2, height: 841.8) // A4 в pt
let renderer = UIGraphicsPDFRenderer(bounds: pageRect)
return renderer.pdfData { context in
context.beginPage()
let ctx = context.cgContext
// Заголовок
let titleAttrs: [NSAttributedString.Key: Any] = [
.font: UIFont.systemFont(ofSize: 18, weight: .bold),
.foregroundColor: UIColor.black
]
"СЧЁТ №\(invoice.number)".draw(at: CGPoint(x: 40, y: 40), withAttributes: titleAttrs)
// Таблиця позицій
drawInvoiceTable(invoice.items, in: ctx, startY: 120, pageWidth: 595.2)
// Якщо контент не вміщується — beginPage() для наступної сторінки
}
}Рендеринг HTML → PDF. Складну верстку (таблиці, колонки, зображення) простіше описати HTML/CSS і сконвертувати через 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+) — найпростіший спосіб для складної верстки. CSS @page правила керують розривами сторінок (page-break-before: always), шрифтами, відступами.
PDFKit — для маніпуляцій з існуючими PDF: додати сторінку, вставити анотацію, злити кілька документів в один. PDFDocument, PDFPage, PDFAnnotation — простий API. Детальніше в PDFKit.
Android: PdfDocument та WebView
android.graphics.pdf.PdfDocument — нативна генерація через Canvas API, аналогічно 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.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 — стандартний шлях на Android. Але це відкриває діалог друку, а не повертає ByteArray. Для програмної генерації — WebView з PrintDocumentAdapter через reflection або сторонні бібліотеки.
Альтернатива — iText 7 (AGPL, платна для комерції) або Apache PDFBox (Apache 2.0). PdfBox на Android — порт з деякими обмеженнями, але для більшості завдань достатній. OpenPDF (форк iText 2.x, LGPL) — хороший баланс функціональності та ліцензії. Детальніше див. OpenPDF на GitHub.
Чому важливо вбудовувати шрифти?
Стандартні системні шрифти в PDF можуть не включатися коректно при відкритті на іншому пристрої. Правильний підхід — embed шрифт у PDF. На iOS: UIFont(name:size:) з bundled TTF-файлом. На Android з iText/OpenPDF: PdfFont.createFont("assets/fonts/Roboto-Regular.ttf", PdfEncodings.IDENTITY_H, true) — true означає embed у документ.
Без embed кирилиця часто відображається як квадратики — до 40% користувачів можуть зіткнутися з цією проблемою, якщо шрифт не вбудований. Рішення: завжди вбудовуйте шрифти.
Шарінг PDF з додатку
Після генерації — UIActivityViewController (iOS) або FileProvider + ACTION_SEND Intent (Android). Для збереження в Files/Downloads: UIDocumentPickerViewController (iOS) або MediaStore.Downloads URI (Android 10+). Перегляд без зовнішнього додатку: PDFView з PDFKit (iOS), PdfRenderer (Android) — посторінковий рендеринг з файлу в Bitmap.
Типові проблеми та їх вирішення
| Проблема | Рішення |
|---|---|
| Кирилиця відображається квадратиками | Вбудувати шрифт за допомогою embed=true |
| Розрив сторінки посередині таблиці | Використовувати CSS page-break-inside: avoid або Canvas з ручним контролем |
| PDF не відкривається на старих пристроях | Генерувати у форматі PDF 1.4 або нижче |
Що входить у роботу?
- Аналіз макету та вимог до PDF-документу.
- Вибір оптимального підходу (Canvas / HTML / бібліотека).
- Реалізація генерації з урахуванням верстки, шрифтів, кирилиці.
- Інтеграція збереження та шарінгу.
- Тестування на реальних пристроях (5+ моделей).
- Передача вихідного коду та документації.
Терміни та вартість
2–3 робочих дні для стандартних документів (рахунок, акт, звіт). Складна верстка із зображеннями, таблицями та багатосторінковим flow — до 5 днів. Вартість розраховується індивідуально. Отримайте консультацію щодо оптимального підходу для вашого завдання. Зв'яжіться з нами — оцінимо ваш проєкт за 2 дні.







