From 839b625601d13501f984050e29dbe55d608fc2db Mon Sep 17 00:00:00 2001 From: Rakshit Kumar Singh Date: Sat, 25 Apr 2026 00:15:14 +0530 Subject: [PATCH 1/4] change default to base --- ...react-native-document-scanner-plugin+2.0.4.patch | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/patches/react-native-document-scanner-plugin+2.0.4.patch b/patches/react-native-document-scanner-plugin+2.0.4.patch index e7abe22..9b145b3 100644 --- a/patches/react-native-document-scanner-plugin+2.0.4.patch +++ b/patches/react-native-document-scanner-plugin+2.0.4.patch @@ -1,3 +1,16 @@ +diff --git a/node_modules/react-native-document-scanner-plugin/android/src/main/java/com/documentscanner/DocumentScannerModule.kt b/node_modules/react-native-document-scanner-plugin/android/src/main/java/com/documentscanner/DocumentScannerModule.kt +index fbb6836..e75b6b2 100644 +--- a/node_modules/react-native-document-scanner-plugin/android/src/main/java/com/documentscanner/DocumentScannerModule.kt ++++ b/node_modules/react-native-document-scanner-plugin/android/src/main/java/com/documentscanner/DocumentScannerModule.kt +@@ -51,7 +51,7 @@ class DocumentScannerModule(reactContext: ReactApplicationContext) : + + val documentScannerOptionsBuilder = GmsDocumentScannerOptions.Builder() + .setResultFormats(GmsDocumentScannerOptions.RESULT_FORMAT_JPEG) +- .setScannerMode(GmsDocumentScannerOptions.SCANNER_MODE_FULL) ++ .setScannerMode(GmsDocumentScannerOptions.SCANNER_MODE_BASE) + + if (options.hasKey("maxNumDocuments")) { + documentScannerOptionsBuilder.setPageLimit( diff --git a/node_modules/react-native-document-scanner-plugin/android/src/main/java/com/documentscanner/DocumentScannerPackage.kt b/node_modules/react-native-document-scanner-plugin/android/src/main/java/com/documentscanner/DocumentScannerPackage.kt index 84f3fec..54a8f53 100644 --- a/node_modules/react-native-document-scanner-plugin/android/src/main/java/com/documentscanner/DocumentScannerPackage.kt From c6e632596a3deb83d7e87c2606d7f7605737e952 Mon Sep 17 00:00:00 2001 From: Rakshit Kumar Singh Date: Sat, 25 Apr 2026 00:40:51 +0530 Subject: [PATCH 2/4] id card pdf --- .../java/com/lensapp/PdfGeneratorModule.kt | 45 +++++++++++++++++++ src/screens/ViewerScreen.tsx | 27 ++++++++++- src/utils/generatePdf.ts | 6 +++ 3 files changed, 77 insertions(+), 1 deletion(-) diff --git a/android/app/src/main/java/com/lensapp/PdfGeneratorModule.kt b/android/app/src/main/java/com/lensapp/PdfGeneratorModule.kt index f3674ee..5d6e48e 100644 --- a/android/app/src/main/java/com/lensapp/PdfGeneratorModule.kt +++ b/android/app/src/main/java/com/lensapp/PdfGeneratorModule.kt @@ -28,6 +28,51 @@ class PdfGeneratorModule(private val reactContext: ReactApplicationContext) : return out } + @ReactMethod + fun generateIdCard(imagePaths: ReadableArray, fileName: String, matrix: ReadableArray, promise: Promise) { + try { + if (imagePaths.size() < 2) throw Exception("Need at least 2 images for ID card export") + val outDir = File(reactContext.filesDir, "pdfs").also { it.mkdirs() } + val outFile = File(outDir, "$fileName.pdf") + val doc = PdfDocument() + + // A4 portrait: 595 x 842 points + val pageWidth = 595 + val pageHeight = 842 + val padding = 30 + val gap = 20 + val slotW = pageWidth - padding * 2 + val slotH = (pageHeight - padding * 2 - gap) / 2 + + val pageInfo = PdfDocument.PageInfo.Builder(pageWidth, pageHeight, 1).create() + val page = doc.startPage(pageInfo) + + for (i in 0 until 2) { + val path = imagePaths.getString(i)!!.removePrefix("file://") + val raw = BitmapFactory.decodeFile(path) ?: throw Exception("Failed to decode image: $path") + val bitmap = applyFilter(raw, matrix) + if (raw !== bitmap) raw.recycle() + + val slotTop = padding + i * (slotH + gap) + val scale = minOf(slotW.toFloat() / bitmap.width, slotH.toFloat() / bitmap.height) + val scaledW = bitmap.width * scale + val scaledH = bitmap.height * scale + val left = padding + (slotW - scaledW) / 2f + val top = slotTop + (slotH - scaledH) / 2f + + page.canvas.drawBitmap(bitmap, null, RectF(left, top, left + scaledW, top + scaledH), null) + bitmap.recycle() + } + + doc.finishPage(page) + FileOutputStream(outFile).use { doc.writeTo(it) } + doc.close() + promise.resolve("file://${outFile.absolutePath}") + } catch (e: Exception) { + promise.reject("PDF_ERROR", e.message, e) + } + } + @ReactMethod fun generate(imagePaths: ReadableArray, fileName: String, matrix: ReadableArray, promise: Promise) { try { diff --git a/src/screens/ViewerScreen.tsx b/src/screens/ViewerScreen.tsx index 09ed081..bef0526 100644 --- a/src/screens/ViewerScreen.tsx +++ b/src/screens/ViewerScreen.tsx @@ -16,7 +16,7 @@ import { import Share from 'react-native-share'; import {ColorMatrix} from 'react-native-color-matrix-image-filters'; import {ScannedDocument} from '../types'; -import {generatePdf} from '../utils/generatePdf'; +import {generatePdf, generateIdCardPdf} from '../utils/generatePdf'; import {getFilterMatrix} from '../utils/filterMatrices'; import {useTheme} from '../theme'; @@ -36,6 +36,25 @@ export default function ViewerScreen({document, onBack, onDelete, onRename}: Pro const [renaming, setRenaming] = useState(false); const [renameText, setRenameText] = useState(''); + const shareIdCard = async () => { + setGenerating(true); + await new Promise(resolve => setTimeout(resolve, 0)); + try { + const label = `${document.name} — ID Card`; + const pdfPath = await generateIdCardPdf(document.pages, label, document.filter); + await Share.open({ + title: label, + type: 'application/pdf', + url: pdfPath, + failOnCancel: false, + }); + } catch (err: any) { + Alert.alert('Share failed', err?.message ?? 'Could not generate PDF.'); + } finally { + setGenerating(false); + } + }; + const sharePdf = async (pageIndices?: number[]) => { setGenerating(true); await new Promise(resolve => setTimeout(resolve, 0)); @@ -132,6 +151,12 @@ export default function ViewerScreen({document, onBack, onDelete, onRename}: Pro 📄 Share All + {document.pages.length >= 2 && ( + + 🪪 + ID Card + + )} {setRenameText(document.name); setRenaming(true);}}> diff --git a/src/utils/generatePdf.ts b/src/utils/generatePdf.ts index 160972e..34f1997 100644 --- a/src/utils/generatePdf.ts +++ b/src/utils/generatePdf.ts @@ -9,3 +9,9 @@ export async function generatePdf(pages: ScannedPage[], name: string, filter: Fi const fileName = name.replace(/[^a-z0-9_\-]/gi, '_'); return PdfGenerator.generate(imagePaths, fileName, getFilterMatrix(filter)); } + +export async function generateIdCardPdf(pages: ScannedPage[], name: string, filter: FilterMode = 'original'): Promise { + const imagePaths = pages.map(p => p.uri); + const fileName = name.replace(/[^a-z0-9_\-]/gi, '_'); + return PdfGenerator.generateIdCard(imagePaths, fileName, getFilterMatrix(filter)); +} From 1c5187bd4ac312bad2669180d79df0dc454246a8 Mon Sep 17 00:00:00 2001 From: Rakshit Kumar Singh Date: Sat, 25 Apr 2026 01:01:01 +0530 Subject: [PATCH 3/4] side by side arrangement --- .../main/java/com/lensapp/PdfGeneratorModule.kt | 17 ++++++++++------- src/screens/ViewerScreen.tsx | 2 +- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/android/app/src/main/java/com/lensapp/PdfGeneratorModule.kt b/android/app/src/main/java/com/lensapp/PdfGeneratorModule.kt index 5d6e48e..f0db270 100644 --- a/android/app/src/main/java/com/lensapp/PdfGeneratorModule.kt +++ b/android/app/src/main/java/com/lensapp/PdfGeneratorModule.kt @@ -37,12 +37,15 @@ class PdfGeneratorModule(private val reactContext: ReactApplicationContext) : val doc = PdfDocument() // A4 portrait: 595 x 842 points + // Two ID slots placed side by side, centered on the page val pageWidth = 595 val pageHeight = 842 - val padding = 30 - val gap = 20 - val slotW = pageWidth - padding * 2 - val slotH = (pageHeight - padding * 2 - gap) / 2 + val slotW = 250 + val slotH = 180 + val gap = 16 + val totalW = slotW * 2 + gap + val startX = (pageWidth - totalW) / 2f + val startY = (pageHeight - slotH) / 2f val pageInfo = PdfDocument.PageInfo.Builder(pageWidth, pageHeight, 1).create() val page = doc.startPage(pageInfo) @@ -53,12 +56,12 @@ class PdfGeneratorModule(private val reactContext: ReactApplicationContext) : val bitmap = applyFilter(raw, matrix) if (raw !== bitmap) raw.recycle() - val slotTop = padding + i * (slotH + gap) + val slotLeft = startX + i * (slotW + gap) val scale = minOf(slotW.toFloat() / bitmap.width, slotH.toFloat() / bitmap.height) val scaledW = bitmap.width * scale val scaledH = bitmap.height * scale - val left = padding + (slotW - scaledW) / 2f - val top = slotTop + (slotH - scaledH) / 2f + val left = slotLeft + (slotW - scaledW) / 2f + val top = startY + (slotH - scaledH) / 2f page.canvas.drawBitmap(bitmap, null, RectF(left, top, left + scaledW, top + scaledH), null) bitmap.recycle() diff --git a/src/screens/ViewerScreen.tsx b/src/screens/ViewerScreen.tsx index bef0526..27b9026 100644 --- a/src/screens/ViewerScreen.tsx +++ b/src/screens/ViewerScreen.tsx @@ -151,7 +151,7 @@ export default function ViewerScreen({document, onBack, onDelete, onRename}: Pro 📄 Share All - {document.pages.length >= 2 && ( + {document.pages.length === 2 && ( 🪪 ID Card From c45d200ef6b51a9fab8aa576cd4ddb39f1978925 Mon Sep 17 00:00:00 2001 From: Rakshit Kumar Singh Date: Sat, 25 Apr 2026 02:00:32 +0530 Subject: [PATCH 4/4] id card generalisation --- .../java/com/lensapp/PdfGeneratorModule.kt | 43 +++++++++++-------- src/screens/ViewerScreen.tsx | 10 ++--- 2 files changed, 29 insertions(+), 24 deletions(-) diff --git a/android/app/src/main/java/com/lensapp/PdfGeneratorModule.kt b/android/app/src/main/java/com/lensapp/PdfGeneratorModule.kt index f0db270..fde28bd 100644 --- a/android/app/src/main/java/com/lensapp/PdfGeneratorModule.kt +++ b/android/app/src/main/java/com/lensapp/PdfGeneratorModule.kt @@ -31,13 +31,13 @@ class PdfGeneratorModule(private val reactContext: ReactApplicationContext) : @ReactMethod fun generateIdCard(imagePaths: ReadableArray, fileName: String, matrix: ReadableArray, promise: Promise) { try { - if (imagePaths.size() < 2) throw Exception("Need at least 2 images for ID card export") + val count = imagePaths.size() + if (count == 0) throw Exception("No images provided") val outDir = File(reactContext.filesDir, "pdfs").also { it.mkdirs() } val outFile = File(outDir, "$fileName.pdf") val doc = PdfDocument() // A4 portrait: 595 x 842 points - // Two ID slots placed side by side, centered on the page val pageWidth = 595 val pageHeight = 842 val slotW = 250 @@ -47,27 +47,34 @@ class PdfGeneratorModule(private val reactContext: ReactApplicationContext) : val startX = (pageWidth - totalW) / 2f val startY = (pageHeight - slotH) / 2f - val pageInfo = PdfDocument.PageInfo.Builder(pageWidth, pageHeight, 1).create() - val page = doc.startPage(pageInfo) + val numPages = (count + 1) / 2 + for (p in 0 until numPages) { + val pageInfo = PdfDocument.PageInfo.Builder(pageWidth, pageHeight, p + 1).create() + val page = doc.startPage(pageInfo) - for (i in 0 until 2) { - val path = imagePaths.getString(i)!!.removePrefix("file://") - val raw = BitmapFactory.decodeFile(path) ?: throw Exception("Failed to decode image: $path") - val bitmap = applyFilter(raw, matrix) - if (raw !== bitmap) raw.recycle() + for (slot in 0 until 2) { + val imgIndex = p * 2 + slot + if (imgIndex >= count) break - val slotLeft = startX + i * (slotW + gap) - val scale = minOf(slotW.toFloat() / bitmap.width, slotH.toFloat() / bitmap.height) - val scaledW = bitmap.width * scale - val scaledH = bitmap.height * scale - val left = slotLeft + (slotW - scaledW) / 2f - val top = startY + (slotH - scaledH) / 2f + val path = imagePaths.getString(imgIndex)!!.removePrefix("file://") + val raw = BitmapFactory.decodeFile(path) ?: throw Exception("Failed to decode image: $path") + val bitmap = applyFilter(raw, matrix) + if (raw !== bitmap) raw.recycle() - page.canvas.drawBitmap(bitmap, null, RectF(left, top, left + scaledW, top + scaledH), null) - bitmap.recycle() + val slotLeft = startX + slot * (slotW + gap) + val scale = minOf(slotW.toFloat() / bitmap.width, slotH.toFloat() / bitmap.height) + val scaledW = bitmap.width * scale + val scaledH = bitmap.height * scale + val left = slotLeft + (slotW - scaledW) / 2f + val top = startY + (slotH - scaledH) / 2f + + page.canvas.drawBitmap(bitmap, null, RectF(left, top, left + scaledW, top + scaledH), null) + bitmap.recycle() + } + + doc.finishPage(page) } - doc.finishPage(page) FileOutputStream(outFile).use { doc.writeTo(it) } doc.close() promise.resolve("file://${outFile.absolutePath}") diff --git a/src/screens/ViewerScreen.tsx b/src/screens/ViewerScreen.tsx index 27b9026..241317b 100644 --- a/src/screens/ViewerScreen.tsx +++ b/src/screens/ViewerScreen.tsx @@ -151,12 +151,10 @@ export default function ViewerScreen({document, onBack, onDelete, onRename}: Pro 📄 Share All - {document.pages.length === 2 && ( - - 🪪 - ID Card - - )} + + 🪪 + ID Card + {setRenameText(document.name); setRenaming(true);}}>