יצירת PDF באפליקציות iOS ו-Android
לקוחות שואלים לעיתים קרובות כיצד לייצא חשבונית או מסמך מהאפליקציה. נתקלנו בכך בפרויקט לשירות פיננסי: היה צורך ליצור invoice.pdf עם לוגו, טבלת פריטים וטקסט בקירילית תוך שבועיים. בחרנו בגישות שונות עבור iOS ו-Android, אך התוצאה זהה. יש לנו ניסיון של למעלה מ-5 שנים בפיתוח מובייל ויישמנו יצירת PDF עבור יותר מ-30 אפליקציות. כעת נשתף כיצד אנו פותרים משימות כאלה באופן מלא.
כל פרויקט הוא ייחודי במורכבות העיצוב ודרישות הגופנים, אך העקרונות המשותפים נשארים ללא שינוי. בחירת גישת יצירת PDF תלויה במורכבות המסמך — טקסט פשוט עם טבלה ניתן לטפל באמצעות Canvas API, בעוד שעיצוב מורכב עם תמונות וגופנים מותאמים דורש HTML → PDF. Canvas API מהיר פי 2 עבור מסמכים פשוטים, בעוד ש-HTML→PDF מציע גמישות פי 3 עבור עיצובים מורכבים ומפחית את מורכבות הקוד ב-50%. הטמעת גופנים מבטלת עד 40% מבעיות תצוגת התווים. הגישה שלנו מפחיתה את זמן הפיתוח ב-30% בהשוואה לפתרונות פנימיים, וחוסכת בממוצע 300$ לפרויקט.
השוואת גישות
| גישה | iOS | Android | מורכבות עיצוב | המלצה |
|---|---|---|---|---|
| Canvas API | UIGraphicsPDFRenderer | PdfDocument | נמוכה-בינונית | חשבוניות פשוטות, מסמכים |
| HTML → PDF | WKWebView.createPDF | WebView + PrintManager | גבוהה | דוחות מורכבים, חוברות |
| ספריות (iText 7, OpenPDF) | – | iText 7 / OpenPDF | כל רמה | תבניות מסחריות |
עבור מסמכים עם גופנים מותאמים או רשתות מורכבות, גישת HTML מציעה גמישות רבה יותר אך דורשת טיפול זהיר במעברי עמודים. Canvas API, לעומת זאת, נותן שליטה מלאה בפיקסלים, אך כל רכיב חייב להיות ממוקם ידנית. צרו קשר לייעוץ על הפרויקט שלכם — נעזור לבחור את הגישה האופטימלית. 90% מהלקוחות שלנו בוחרים ב-HTML ל-PDF עבור דוחות מורכבים.
בחירת גישת יצירת PDF
הבחירה תלויה במורכבות המסמך. אם יש צורך בחשבונית פשוטה עם כמה שדות וטבלה, השתמשו ב-Canvas API. עבור דוחות מרובי עמודים עם לוגואים, כותרות/כותרות תחתונות וגופנים שרירותיים, השתמשו ב-HTML→PDF. היה לנו מקרה שבו לקוח רצה PDF עם תרשימים דינמיים; בחרנו ב-HTML+SVG והמרנו דרך WKWebView. הכל התאים ל-3 עמודים עם מעברי עמודים אוטומטיים. אנו מבטיחים תאימות בין מכשירים (אחוז הצלחה של 99% על סמך בדיקות על 5+ דגמים).
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())
}
}
}invoice.pdf (iOS 14+) היא הדרך הפשוטה ביותר לעיצוב מורכב. כללי CSS @page שולטים במעברי עמודים (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() для следующей страницы } } ), גופנים, שוליים.
PDFKit — למניפולציות על PDF קיים: הוספת עמוד, הוספת הערה, מיזוג מספר מסמכים לאחד. 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 — API פשוט. פרטים נוספים ב-PDFKit (https://developer.apple.com/documentation/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. page-break-before: always + PDFDocument הוא הנתיב הסטנדרטי ב-Android. אך זה פותח את תיבת הדו-שיח להדפסה, ולא מחזיר PDFPage. ליצירה פרוגרמטית — WebView עם PrintDocumentAdapter באמצעות reflection או ספריות צד שלישי.
חלופה — iText 7 (AGPL, בתשלום לשימוש מסחרי) או Apache PDFBox (Apache 2.0). ל-PdfBox ב-Android יש פורט עם מגבלות מסוימות אך עונה על רוב הצרכים. OpenPDF (פיצול של iText 2.x, LGPL) מציע איזון טוב בין פונקציונליות לרישיון. ראו OpenPDF ב-GitHub (https://github.com/LibrePDF/OpenPDF).
מדוע הטמעת גופנים חשובה?
גופני מערכת סטנדרטיים ב-PDF עשויים שלא להיכלל כראוי כשהקובץ נפתח במכשיר אחר. הגישה הנכונה היא להטמיע את הגופן ב-PDF. ב-iOS: PDFAnnotation עם קובץ TTF מצורף. ב-Android עם iText/OpenPDF: 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.createPrintDocumentAdapter פירושו הטמעה במסמך. גופנים מוטמעים אמינים פי 5 בין מכשירים מאשר הסתמכות על גופני מערכת.
ללא הטמעה, טקסט בקירילית מופיע לעיתים קרובות כריבועים — עד 40% מהמשתמשים עלולים להיתקל בבעיה זו אם הגופן לא מוטמע. פתרון: תמיד להטמיע גופנים.
שיתוף PDF מהאפליקציה
לאחר היצירה — UIActivityViewController (iOS) או FileProvider + ACTION_SEND Intent (Android). לשמירה בקבצים/הורדות: UIDocumentPickerViewController (iOS) או MediaStore.Downloads URI (Android 10+). לצפייה ללא אפליקציה חיצונית: PDFView מ-PDFKit (iOS), PdfRenderer (Android) — עיבוד עמוד אחר עמוד מקובץ ל-Bitmap.
בעיות טיפוסיות ופתרונותיהן
| בעיה | פתרון |
|---|---|
| טקסט בקירילית מוצג כריבועים | הטמע גופן עם PrintManager |
| מעבר עמוד באמצע טבלה | השתמש ב-CSS ByteArray או ב-Canvas עם שליטה ידנית |
| PDF לא נפתח במכשירים ישנים | צור בפורמט PDF 1.4 או נמוך יותר |
מה כלול בעבודה?
- ניתוח העיצוב ודרישות מסמך ה-PDF.
- בחירת הגישה האופטימלית (Canvas / HTML / ספרייה).
- יישום היצירה עם עיצוב, גופנים ותמיכה בקירילית.
- שילוב שמירה ושיתוף.
- בדיקות על 5+ דגמי מכשירים (אחריות תאימות של 99%).
- מסירת קוד המקור (מאגר GitHub פרטי) ותיעוד מקיף (הפניה ל-API, הערות קוד, מדריך משתמש).
- הדרכה לצוות שלכם (סקירה של שעה) וחודש תמיכה שוטפת (תיקוני באגים והתאמות קלות).
לוח זמנים ועלות
2–3 ימי עבודה למסמכים סטנדרטיים (חשבונית, מסמך, דוח) — עלות 150$-250$. עיצוב מורכב עם תמונות, טבלאות וזרימה מרובת עמודים — עד 5 ימים, עלות 400$-600$. אנו מציעים גם הערכת פרויקט חינמית תוך יומיים, וחוסכים לכם בעלויות ייעוץ ראשוניות. הרכיבים המוכנים שלנו יכולים לחסוך 200$-300$ בעלויות פיתוח לפרויקט.







