Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions android/app/src/main/java/com/lensapp/PdfGeneratorModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,61 @@ class PdfGeneratorModule(private val reactContext: ReactApplicationContext) :
return out
}

@ReactMethod
fun generateIdCard(imagePaths: ReadableArray, fileName: String, matrix: ReadableArray, promise: Promise) {
try {
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
val pageWidth = 595
val pageHeight = 842
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 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 (slot in 0 until 2) {
val imgIndex = p * 2 + slot
if (imgIndex >= count) break

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()

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)
}

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 {
Expand Down
13 changes: 13 additions & 0 deletions patches/react-native-document-scanner-plugin+2.0.4.patch
Original file line number Diff line number Diff line change
@@ -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
Expand Down
25 changes: 24 additions & 1 deletion src/screens/ViewerScreen.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';

Expand All @@ -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));
Expand Down Expand Up @@ -132,6 +151,10 @@ export default function ViewerScreen({document, onBack, onDelete, onRename}: Pro
<Text style={[styles.toolIcon, {color: t.accent}]}>📄</Text>
<Text style={[styles.toolLabel, {color: t.accent}]}>Share All</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.toolBtn} onPress={shareIdCard} disabled={generating}>
<Text style={[styles.toolIcon, {color: t.accent}]}>🪪</Text>
<Text style={[styles.toolLabel, {color: t.accent}]}>ID Card</Text>
</TouchableOpacity>
<TouchableOpacity
style={styles.toolBtn}
onPress={() => {setRenameText(document.name); setRenaming(true);}}>
Expand Down
6 changes: 6 additions & 0 deletions src/utils/generatePdf.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<string> {
const imagePaths = pages.map(p => p.uri);
const fileName = name.replace(/[^a-z0-9_\-]/gi, '_');
return PdfGenerator.generateIdCard(imagePaths, fileName, getFilterMatrix(filter));
}