Реалізація електронного підпису (малювання підпису) в мобільній програмі
Рукописна підпис на екрані смартфона — це не просто "красива картинка". Для юридичної значимості вона повинна бути прив'язана до особистості підписавця, до конкретного документа та моменту підписання. Інакше це просто PNG з каракулями, що нічого не доводить.
Захоплення рукописної підпису
Збір траєкторії дотику — основа. Потрібні не просто координати touchMove, а весь потік подій з тиском, швидкістю та часовими мітками. Це дозволяє відтворити підпис анімовано та зібрати біометричні метадані.
Android — низькорівневий введення:
override fun onTouchEvent(event: MotionEvent): Boolean {
val x = event.x
val y = event.y
val pressure = event.pressure // 0.0 - 1.0
val timestamp = event.eventTime
when (event.action) {
MotionEvent.ACTION_DOWN -> startStroke(x, y, pressure, timestamp)
MotionEvent.ACTION_MOVE -> {
// getHistorical* для проміжних точок між подіями
for (i in 0 until event.historySize) {
addHistoricalPoint(
event.getHistoricalX(i),
event.getHistoricalY(i),
event.getHistoricalPressure(i),
event.getHistoricalEventTime(i)
)
}
addPoint(x, y, pressure, timestamp)
}
MotionEvent.ACTION_UP -> endStroke(x, y, pressure, timestamp)
}
return true
}
event.historySize критично — система батчить проміжні точки між двома подіями ACTION_MOVE. Ігнорування історичних точок дає кутові штрихи замість плавних кривих.
iOS — UIBezierPath з Bezier-сглажуванням:
func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let touch = touches.first else { return }
let currentPoint = touch.location(in: self)
let previousPoint = touch.previousLocation(in: self)
let force = touch.force / touch.maximumPossibleForce // 3D Touch / Pencil
// Quadratic bezier для сглажування
let midPoint = CGPoint(
x: (currentPoint.x + previousPoint.x) / 2,
y: (currentPoint.y + previousPoint.y) / 2
)
path.addQuadCurve(to: midPoint, controlPoint: previousPoint)
setNeedsDisplay()
}
Для Apple Pencil — predictedTouches(for:) дає передбачені точки для зменшення затримки малювання. Без передбачених дотиків візуальна відповідь Pencil сприймається як запаздивающая навіть при 120 Hz.
Змінна товщина лінії
Професійна підпис виглядає природно, коли товщина штриха змінюється з тиском та швидкістю. Математика проста: width = baseWidth + pressure * maxExtraWidth. При високій швидкості (велика відстань між точками) — тонка лінія, при повільному русі — товста.
fun calculateStrokeWidth(pressure: Float, velocity: Float): Float {
val pressureComponent = pressure * MAX_PRESSURE_WIDTH
val velocityComponent = (1f - velocity.coerceIn(0f, 1f)) * MAX_VELOCITY_WIDTH
return BASE_WIDTH + pressureComponent * 0.6f + velocityComponent * 0.4f
}
Біометричні метадані
Для посилення юридичної сили — збирайте метадані процесу підписання:
- Часові мітки кожного штриху
- Швидкість руху по сегментах
- Тиск (якщо пристрій підтримує)
- Тривалість кожного штриху та паузи між ними
- Загальний час підписання
Ці дані не відображаються користувачу, але зберігаються в базі даних і можуть бути використані при судовому оспорюванні автентичності підпису — поведінкова біометрія.
Прив'язка до документа
Рисунок підпису сам по собі безкорисний. Потрібно:
- Зафіксувати хеш документа до підписання (SHA-256 PDF або текстового вмісту)
- Прикріпити підпис (PNG + метадані траєкторії) до цього хеша
- Зберегти на сервері з часовою міткою та ID підписавця
- Опціонально: вбудувати підпис у PDF через iText (Java/Android) або PDFKit (iOS)
Запис на сервері:
{
"document_hash": "sha256:abc123...",
"signer_id": "user-uuid",
"signed_at": "2025-03-26T14:22:00Z",
"ip_address": "1.2.3.4",
"device_fingerprint": "...",
"signature_image_url": "...",
"stroke_data_encrypted": "...",
"session_metadata": {
"duration_ms": 4200,
"stroke_count": 5,
"avg_pressure": 0.72
}
}
Інтеграція PDF
На iOS: PDFKit + CGContext для малювання підпису поверх сторінки PDF. Для професійного вбудовування з полями AcroForm — стороння бібліотека PSPDFKit або серверний iText через API.
На Android: PdfRenderer для відображення, вбудовування — через iText Android або Apache PDFBox.
Важливий момент: візуальне вбудовування підпису в PDF ≠ цифрова підпис PDF (створена через PKI). Це різні речі. Для юридично значимого PDF-документа потрібна саме PKI-підпис — рукописна підпис додається як додатковий візуальний елемент.
Готові SDK проти кастомної реалізації
Готові: SignaturePad (Android, iOS), react-native-signature-canvas, signature_pad (Flutter). Дають базову функціональність швидко. Не дають: біометричні метадані, змінну товщину з тиском, PKI-інтеграцію.
Якщо потрібна тільки візуальна підпис для простої ПЕП — готовий SDK виправданий. Для повноцінної ПЕП з аудит-треком — кастомна реалізація.
Часова шкала: кастомний компонент малювання з тиском, сглажуванням, експортом у PNG + серверна прив'язка до документа + вбудовування в PDF — 2–3 дня на платформу.







