Реалізація генерації 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 дні.







