From 60e60c170de38bcf009e441ecc92cb033f16abb7 Mon Sep 17 00:00:00 2001 From: Rakshit Kumar Singh Date: Thu, 23 Apr 2026 01:13:30 +0530 Subject: [PATCH 1/7] filter at pdf generation --- App.tsx | 4 +- .../java/com/lensapp/PdfGeneratorModule.kt | 37 +++++++++++++- ios/LensApp/PdfGenerator.m | 29 ++++++++++- package-lock.json | 49 +++++++++++++++++++ package.json | 1 + src/hooks/useDocumentStore.ts | 5 +- src/screens/ViewerScreen.tsx | 2 +- src/types/index.ts | 1 + src/utils/generatePdf.ts | 6 +-- 9 files changed, 122 insertions(+), 12 deletions(-) diff --git a/App.tsx b/App.tsx index 4108164..0eb75bc 100644 --- a/App.tsx +++ b/App.tsx @@ -29,8 +29,8 @@ export default function App() { }, []); const handleSaveDocument = useCallback( - (pages: ScannedPage[], _filter: FilterMode) => { - const doc = createDocument(pages); + (pages: ScannedPage[], filter: FilterMode) => { + const doc = createDocument(pages, filter); setPendingPages([]); setViewingDoc(doc); setScreen('viewer'); diff --git a/android/app/src/main/java/com/lensapp/PdfGeneratorModule.kt b/android/app/src/main/java/com/lensapp/PdfGeneratorModule.kt index 35abdd1..557ff24 100644 --- a/android/app/src/main/java/com/lensapp/PdfGeneratorModule.kt +++ b/android/app/src/main/java/com/lensapp/PdfGeneratorModule.kt @@ -1,6 +1,11 @@ package com.lensapp +import android.graphics.Bitmap import android.graphics.BitmapFactory +import android.graphics.Canvas +import android.graphics.ColorMatrix +import android.graphics.ColorMatrixColorFilter +import android.graphics.Paint import android.graphics.RectF import android.graphics.pdf.PdfDocument import com.facebook.react.bridge.Promise @@ -16,8 +21,34 @@ class PdfGeneratorModule(private val reactContext: ReactApplicationContext) : override fun getName() = "PdfGenerator" + private fun applyFilter(src: Bitmap, filter: String): Bitmap { + val matrix = ColorMatrix() + when (filter) { + "grayscale" -> matrix.setSaturation(0f) + "blackwhite" -> { + matrix.setSaturation(0f) + matrix.postConcat(ColorMatrix(floatArrayOf( + 4f, 0f, 0f, 0f, -512f, + 0f, 4f, 0f, 0f, -512f, + 0f, 0f, 4f, 0f, -512f, + 0f, 0f, 0f, 1f, 0f, + ))) + } + "enhanced" -> matrix.set(floatArrayOf( + 1.4f, 0f, 0f, 0f, 15f, + 0f, 1.4f, 0f, 0f, 15f, + 0f, 0f, 1.4f, 0f, 15f, + 0f, 0f, 0f, 1f, 0f, + )) + else -> return src + } + val out = Bitmap.createBitmap(src.width, src.height, Bitmap.Config.ARGB_8888) + Canvas(out).drawBitmap(src, 0f, 0f, Paint().apply { colorFilter = ColorMatrixColorFilter(matrix) }) + return out + } + @ReactMethod - fun generate(imagePaths: ReadableArray, fileName: String, promise: Promise) { + fun generate(imagePaths: ReadableArray, fileName: String, filter: String, promise: Promise) { try { val outDir = File(reactContext.filesDir, "pdfs").also { it.mkdirs() } val outFile = File(outDir, "$fileName.pdf") @@ -25,8 +56,10 @@ class PdfGeneratorModule(private val reactContext: ReactApplicationContext) : for (i in 0 until imagePaths.size()) { val path = imagePaths.getString(i)!!.removePrefix("file://") - val bitmap = BitmapFactory.decodeFile(path) + val raw = BitmapFactory.decodeFile(path) ?: throw Exception("Failed to decode image: $path") + val bitmap = applyFilter(raw, filter) + if (raw !== bitmap) raw.recycle() // A4 at 72 DPI: 595 x 842 points val pageWidth = 595 diff --git a/ios/LensApp/PdfGenerator.m b/ios/LensApp/PdfGenerator.m index e88464e..8804ea6 100644 --- a/ios/LensApp/PdfGenerator.m +++ b/ios/LensApp/PdfGenerator.m @@ -1,12 +1,36 @@ #import "PdfGenerator.h" #import +#import @implementation PdfGenerator RCT_EXPORT_MODULE(); +- (UIImage *)applyFilter:(UIImage *)image filter:(NSString *)filter { + CIImage *ci = [CIImage imageWithCGImage:image.CGImage]; + CIFilter *f = [CIFilter filterWithName:@"CIColorControls"]; + [f setValue:ci forKey:kCIInputImageKey]; + if ([filter isEqualToString:@"grayscale"]) { + [f setValue:@0.0 forKey:@"inputSaturation"]; + } else if ([filter isEqualToString:@"blackwhite"]) { + [f setValue:@0.0 forKey:@"inputSaturation"]; + [f setValue:@4.0 forKey:@"inputContrast"]; + [f setValue:@(-0.5) forKey:@"inputBrightness"]; + } else if ([filter isEqualToString:@"enhanced"]) { + [f setValue:@1.4 forKey:@"inputContrast"]; + [f setValue:@0.05 forKey:@"inputBrightness"]; + } else { + return image; + } + CGImageRef cg = [[CIContext context] createCGImage:f.outputImage fromRect:f.outputImage.extent]; + UIImage *result = [UIImage imageWithCGImage:cg]; + CGImageRelease(cg); + return result; +} + RCT_EXPORT_METHOD(generate:(NSArray *)imagePaths fileName:(NSString *)fileName + filter:(NSString *)filter resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { @@ -31,8 +55,9 @@ @implementation PdfGenerator withActions:^(UIGraphicsPDFRendererContext *ctx) { for (NSString *rawPath in imagePaths) { NSString *path = [rawPath hasPrefix:@"file://"] ? [rawPath substringFromIndex:7] : rawPath; - UIImage *image = [UIImage imageWithContentsOfFile:path]; - if (!image) continue; + UIImage *raw = [UIImage imageWithContentsOfFile:path]; + if (!raw) continue; + UIImage *image = [self applyFilter:raw filter:filter]; [ctx beginPage]; diff --git a/package-lock.json b/package-lock.json index 057551f..dc3e272 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "react": "18.2.0", "react-native": "0.73.0", + "react-native-color-matrix-image-filters": "^8.0.2", "react-native-document-scanner-plugin": "^2.0.4", "react-native-fs": "^2.20.0", "react-native-share": "^12.2.6" @@ -5840,6 +5841,12 @@ "dev": true, "license": "MIT" }, + "node_modules/clamp": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/clamp/-/clamp-1.0.1.tgz", + "integrity": "sha512-kgMuFyE78OC6Dyu3Dy7vcx4uy97EIbVxJB/B0eJ3bUNAkwdNcxYzgKltnyADiYwsR7SEqkkUPsEUT//OVS6XMA==", + "license": "MIT" + }, "node_modules/cli-cursor": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", @@ -6018,6 +6025,15 @@ "node": ">= 0.6" } }, + "node_modules/concat-color-matrices": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/concat-color-matrices/-/concat-color-matrices-1.0.0.tgz", + "integrity": "sha512-K0IMtl4m9LcHg4vVFb40Txmie/GwCJBkquGSn6wtA9yIcszzFZgGc6co4sSnjFdqeJAv+q2LrCHMUSb2GHAChA==", + "license": "MIT", + "dependencies": { + "invariant": "^2.2.4" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -12385,6 +12401,21 @@ "react": "18.2.0" } }, + "node_modules/react-native-color-matrix-image-filters": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/react-native-color-matrix-image-filters/-/react-native-color-matrix-image-filters-8.0.2.tgz", + "integrity": "sha512-RJoNJt5Mhi1ztsPdKB3kNDHMSbarrWdNkcY2gPb3G7N9Kx6TKq00hl/X90PWRk9p2lqgiD0YznD8NlxDgqgLPg==", + "license": "MIT", + "dependencies": { + "concat-color-matrices": "^1.0.0", + "rn-color-matrices": "^4.1.0", + "ts-tiny-invariant": "^2.0.5" + }, + "peerDependencies": { + "react": "*", + "react-native": "*" + } + }, "node_modules/react-native-document-scanner-plugin": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/react-native-document-scanner-plugin/-/react-native-document-scanner-plugin-2.0.4.tgz", @@ -12789,6 +12820,18 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rn-color-matrices": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/rn-color-matrices/-/rn-color-matrices-4.1.0.tgz", + "integrity": "sha512-a8++z3Zi4GhA0oWwKS3etdrVuVQ2q/ByTMh6lMxo+vaSv3SRSSg5SzpZk65GS0NCAqMhFT8WSvgSgNhO/F+xag==", + "license": "MIT", + "dependencies": { + "clamp": "^1.0.1" + }, + "peerDependencies": { + "react-native": "*" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -13853,6 +13896,12 @@ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "license": "MIT" }, + "node_modules/ts-tiny-invariant": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/ts-tiny-invariant/-/ts-tiny-invariant-2.0.5.tgz", + "integrity": "sha512-NGQzWRLGLMjOUTpsxMSRj63fuFBC8HV8L4NUxzDVgU4MFeOt3qFI2534M5L+ch22x53oDEwPaRBPXgAfRjcXHA==", + "license": "MIT" + }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", diff --git a/package.json b/package.json index eef4f72..b1b6884 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "dependencies": { "react": "18.2.0", "react-native": "0.73.0", + "react-native-color-matrix-image-filters": "^8.0.2", "react-native-document-scanner-plugin": "^2.0.4", "react-native-fs": "^2.20.0", "react-native-share": "^12.2.6" diff --git a/src/hooks/useDocumentStore.ts b/src/hooks/useDocumentStore.ts index e161391..0bad927 100644 --- a/src/hooks/useDocumentStore.ts +++ b/src/hooks/useDocumentStore.ts @@ -1,6 +1,6 @@ import {useState, useCallback, useEffect, useRef} from 'react'; import RNFS from 'react-native-fs'; -import {ScannedDocument, ScannedPage} from '../types'; +import {ScannedDocument, ScannedPage, FilterMode} from '../types'; let idCounter = 0; const uid = () => `${Date.now()}-${++idCounter}`; @@ -42,12 +42,13 @@ export function useDocumentStore() { } }, [documents]); - const createDocument = useCallback((pages: ScannedPage[]): ScannedDocument => { + const createDocument = useCallback((pages: ScannedPage[], filter: FilterMode): ScannedDocument => { const doc: ScannedDocument = { id: uid(), pages, createdAt: new Date(), name: `Scan ${new Date().toLocaleDateString()}`, + filter, }; setDocuments(prev => [doc, ...prev]); return doc; diff --git a/src/screens/ViewerScreen.tsx b/src/screens/ViewerScreen.tsx index 09232a9..3dfdb38 100644 --- a/src/screens/ViewerScreen.tsx +++ b/src/screens/ViewerScreen.tsx @@ -44,7 +44,7 @@ export default function ViewerScreen({document, onBack, onDelete, onRename}: Pro const label = pageIndices?.length === 1 ? `${document.name} — page ${pageIndices[0] + 1}` : document.name; - const pdfPath = await generatePdf(pages, label); + const pdfPath = await generatePdf(pages, label, document.filter); await Share.open({ title: label, type: 'application/pdf', diff --git a/src/types/index.ts b/src/types/index.ts index 1ca7adb..b163179 100644 --- a/src/types/index.ts +++ b/src/types/index.ts @@ -3,6 +3,7 @@ export interface ScannedDocument { pages: ScannedPage[]; createdAt: Date; name: string; + filter: FilterMode; } export interface ScannedPage { diff --git a/src/utils/generatePdf.ts b/src/utils/generatePdf.ts index a9ccb04..b827c29 100644 --- a/src/utils/generatePdf.ts +++ b/src/utils/generatePdf.ts @@ -1,10 +1,10 @@ import {NativeModules} from 'react-native'; -import {ScannedPage} from '../types'; +import {ScannedPage, FilterMode} from '../types'; const {PdfGenerator} = NativeModules; -export async function generatePdf(pages: ScannedPage[], name: string): Promise { +export async function generatePdf(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.generate(imagePaths, fileName); + return PdfGenerator.generate(imagePaths, fileName, filter); } From 7c25ad4bfdbef04f72a4853a63a13febaf609ed2 Mon Sep 17 00:00:00 2001 From: Rakshit Kumar Singh Date: Thu, 23 Apr 2026 01:44:28 +0530 Subject: [PATCH 2/7] full filter support via external package --- .../java/com/lensapp/PdfGeneratorModule.kt | 29 ++++--------------- ios/LensApp/PdfGenerator.m | 25 ++++++---------- src/screens/ReviewScreen.tsx | 22 ++++++-------- src/utils/filterMatrices.ts | 15 ++++++++++ src/utils/generatePdf.ts | 3 +- 5 files changed, 40 insertions(+), 54 deletions(-) create mode 100644 src/utils/filterMatrices.ts diff --git a/android/app/src/main/java/com/lensapp/PdfGeneratorModule.kt b/android/app/src/main/java/com/lensapp/PdfGeneratorModule.kt index 557ff24..f3674ee 100644 --- a/android/app/src/main/java/com/lensapp/PdfGeneratorModule.kt +++ b/android/app/src/main/java/com/lensapp/PdfGeneratorModule.kt @@ -21,34 +21,15 @@ class PdfGeneratorModule(private val reactContext: ReactApplicationContext) : override fun getName() = "PdfGenerator" - private fun applyFilter(src: Bitmap, filter: String): Bitmap { - val matrix = ColorMatrix() - when (filter) { - "grayscale" -> matrix.setSaturation(0f) - "blackwhite" -> { - matrix.setSaturation(0f) - matrix.postConcat(ColorMatrix(floatArrayOf( - 4f, 0f, 0f, 0f, -512f, - 0f, 4f, 0f, 0f, -512f, - 0f, 0f, 4f, 0f, -512f, - 0f, 0f, 0f, 1f, 0f, - ))) - } - "enhanced" -> matrix.set(floatArrayOf( - 1.4f, 0f, 0f, 0f, 15f, - 0f, 1.4f, 0f, 0f, 15f, - 0f, 0f, 1.4f, 0f, 15f, - 0f, 0f, 0f, 1f, 0f, - )) - else -> return src - } + private fun applyFilter(src: Bitmap, matrixValues: ReadableArray): Bitmap { + val floats = FloatArray(20) { matrixValues.getDouble(it).toFloat() } val out = Bitmap.createBitmap(src.width, src.height, Bitmap.Config.ARGB_8888) - Canvas(out).drawBitmap(src, 0f, 0f, Paint().apply { colorFilter = ColorMatrixColorFilter(matrix) }) + Canvas(out).drawBitmap(src, 0f, 0f, Paint().apply { colorFilter = ColorMatrixColorFilter(ColorMatrix(floats)) }) return out } @ReactMethod - fun generate(imagePaths: ReadableArray, fileName: String, filter: String, promise: Promise) { + fun generate(imagePaths: ReadableArray, fileName: String, matrix: ReadableArray, promise: Promise) { try { val outDir = File(reactContext.filesDir, "pdfs").also { it.mkdirs() } val outFile = File(outDir, "$fileName.pdf") @@ -58,7 +39,7 @@ class PdfGeneratorModule(private val reactContext: ReactApplicationContext) : val path = imagePaths.getString(i)!!.removePrefix("file://") val raw = BitmapFactory.decodeFile(path) ?: throw Exception("Failed to decode image: $path") - val bitmap = applyFilter(raw, filter) + val bitmap = applyFilter(raw, matrix) if (raw !== bitmap) raw.recycle() // A4 at 72 DPI: 595 x 842 points diff --git a/ios/LensApp/PdfGenerator.m b/ios/LensApp/PdfGenerator.m index 8804ea6..9cdbd8d 100644 --- a/ios/LensApp/PdfGenerator.m +++ b/ios/LensApp/PdfGenerator.m @@ -6,22 +6,15 @@ @implementation PdfGenerator RCT_EXPORT_MODULE(); -- (UIImage *)applyFilter:(UIImage *)image filter:(NSString *)filter { +- (UIImage *)applyMatrix:(UIImage *)image matrix:(NSArray *)m { CIImage *ci = [CIImage imageWithCGImage:image.CGImage]; - CIFilter *f = [CIFilter filterWithName:@"CIColorControls"]; + CIFilter *f = [CIFilter filterWithName:@"CIColorMatrix"]; [f setValue:ci forKey:kCIInputImageKey]; - if ([filter isEqualToString:@"grayscale"]) { - [f setValue:@0.0 forKey:@"inputSaturation"]; - } else if ([filter isEqualToString:@"blackwhite"]) { - [f setValue:@0.0 forKey:@"inputSaturation"]; - [f setValue:@4.0 forKey:@"inputContrast"]; - [f setValue:@(-0.5) forKey:@"inputBrightness"]; - } else if ([filter isEqualToString:@"enhanced"]) { - [f setValue:@1.4 forKey:@"inputContrast"]; - [f setValue:@0.05 forKey:@"inputBrightness"]; - } else { - return image; - } + [f setValue:[CIVector vectorWithX:[m[0] floatValue] Y:[m[1] floatValue] Z:[m[2] floatValue] W:[m[3] floatValue]] forKey:@"inputRVector"]; + [f setValue:[CIVector vectorWithX:[m[5] floatValue] Y:[m[6] floatValue] Z:[m[7] floatValue] W:[m[8] floatValue]] forKey:@"inputGVector"]; + [f setValue:[CIVector vectorWithX:[m[10] floatValue] Y:[m[11] floatValue] Z:[m[12] floatValue] W:[m[13] floatValue]] forKey:@"inputBVector"]; + [f setValue:[CIVector vectorWithX:[m[15] floatValue] Y:[m[16] floatValue] Z:[m[17] floatValue] W:[m[18] floatValue]] forKey:@"inputAVector"]; + [f setValue:[CIVector vectorWithX:[m[4] floatValue] Y:[m[9] floatValue] Z:[m[14] floatValue] W:[m[19] floatValue]] forKey:@"inputBiasVector"]; CGImageRef cg = [[CIContext context] createCGImage:f.outputImage fromRect:f.outputImage.extent]; UIImage *result = [UIImage imageWithCGImage:cg]; CGImageRelease(cg); @@ -30,7 +23,7 @@ - (UIImage *)applyFilter:(UIImage *)image filter:(NSString *)filter { RCT_EXPORT_METHOD(generate:(NSArray *)imagePaths fileName:(NSString *)fileName - filter:(NSString *)filter + matrix:(NSArray *)matrix resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) { @@ -57,7 +50,7 @@ - (UIImage *)applyFilter:(UIImage *)image filter:(NSString *)filter { NSString *path = [rawPath hasPrefix:@"file://"] ? [rawPath substringFromIndex:7] : rawPath; UIImage *raw = [UIImage imageWithContentsOfFile:path]; if (!raw) continue; - UIImage *image = [self applyFilter:raw filter:filter]; + UIImage *image = [self applyMatrix:raw matrix:matrix]; [ctx beginPage]; diff --git a/src/screens/ReviewScreen.tsx b/src/screens/ReviewScreen.tsx index b4b8194..82f4b20 100644 --- a/src/screens/ReviewScreen.tsx +++ b/src/screens/ReviewScreen.tsx @@ -10,6 +10,8 @@ import { StatusBar, Alert, } from 'react-native'; +import {ColorMatrix} from 'react-native-color-matrix-image-filters'; +import {getFilterMatrix} from '../utils/filterMatrices'; import {ScannedPage, FilterMode} from '../types'; import FilterPicker from '../components/FilterPicker'; import {useTheme} from '../theme'; @@ -28,14 +30,11 @@ export default function ReviewScreen({pages, onSave, onAddMore, onCancel}: Props const [filter, setFilter] = useState('original'); const [currentIndex, setCurrentIndex] = useState(0); - const imageContainerStyle = () => { - switch (filter) { - case 'grayscale': return styles.filterGrayscale; - case 'blackwhite': return styles.filterBW; - case 'enhanced': return styles.filterEnhanced; - default: return null; - } - }; + const renderImage = (page: ScannedPage) => ( + + + + ); return ( @@ -63,8 +62,8 @@ export default function ReviewScreen({pages, onSave, onAddMore, onCancel}: Props }}> {pages.map(page => ( - - + + {renderImage(page)} ))} @@ -128,9 +127,6 @@ const styles = StyleSheet.create({ }, imageWrapper: {flex: 1, width: '100%', borderRadius: 8, overflow: 'hidden'}, pageImage: {flex: 1, width: '100%'}, - filterGrayscale: {opacity: 0.9}, - filterBW: {opacity: 1}, - filterEnhanced: {opacity: 1}, dots: {flexDirection: 'row', justifyContent: 'center', gap: 6, paddingVertical: 8}, dot: {width: 6, height: 6, borderRadius: 3}, dotActive: {backgroundColor: '#007AFF', width: 18}, diff --git a/src/utils/filterMatrices.ts b/src/utils/filterMatrices.ts new file mode 100644 index 0000000..b998eb2 --- /dev/null +++ b/src/utils/filterMatrices.ts @@ -0,0 +1,15 @@ +import {concatColorMatrices, saturate, contrast, brightness, normal} from 'react-native-color-matrix-image-filters'; +import {FilterMode} from '../types'; + +export function getFilterMatrix(filter: FilterMode): number[] { + switch (filter) { + case 'grayscale': + return saturate(0); + case 'blackwhite': + return concatColorMatrices(saturate(0), contrast(4), brightness(-0.5)); + case 'enhanced': + return concatColorMatrices(contrast(1.4), brightness(0.05)); + default: + return normal(); + } +} diff --git a/src/utils/generatePdf.ts b/src/utils/generatePdf.ts index b827c29..160972e 100644 --- a/src/utils/generatePdf.ts +++ b/src/utils/generatePdf.ts @@ -1,10 +1,11 @@ import {NativeModules} from 'react-native'; import {ScannedPage, FilterMode} from '../types'; +import {getFilterMatrix} from './filterMatrices'; const {PdfGenerator} = NativeModules; export async function generatePdf(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.generate(imagePaths, fileName, filter); + return PdfGenerator.generate(imagePaths, fileName, getFilterMatrix(filter)); } From 0ce68fcd867bdb8372dcf657186279acbecbf751 Mon Sep 17 00:00:00 2001 From: Rakshit Kumar Singh Date: Thu, 23 Apr 2026 01:51:08 +0530 Subject: [PATCH 3/7] added to jest ignore --- jest.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jest.config.js b/jest.config.js index ed98356..8f491fa 100644 --- a/jest.config.js +++ b/jest.config.js @@ -6,6 +6,6 @@ module.exports = { ], moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json'], transformIgnorePatterns: [ - 'node_modules/(?!(react-native|@react-native|react-native-document-scanner-plugin|react-native-fs|react-native-html-to-pdf|react-native-share)/)', + 'node_modules/(?!(react-native|@react-native|react-native-document-scanner-plugin|react-native-fs|react-native-html-to-pdf|react-native-share|react-native-color-matrix-image-filters|rn-color-matrices|concat-color-matrices)/)', ], }; From 8aecb646a29d778c39f297e2ce2e9745916924e6 Mon Sep 17 00:00:00 2001 From: Rakshit Kumar Singh Date: Thu, 23 Apr 2026 02:00:26 +0530 Subject: [PATCH 4/7] fix intry --- src/__tests__/generatePdf.test.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/__tests__/generatePdf.test.ts b/src/__tests__/generatePdf.test.ts index 9a29d10..814536e 100644 --- a/src/__tests__/generatePdf.test.ts +++ b/src/__tests__/generatePdf.test.ts @@ -4,6 +4,11 @@ jest.mock('react-native', () => ({ generate: jest.fn(), }, }, + Platform: {OS: 'android'}, +})); + +jest.mock('../utils/filterMatrices', () => ({ + getFilterMatrix: jest.fn(() => new Array(20).fill(0)), })); import {NativeModules} from 'react-native'; @@ -30,6 +35,7 @@ describe('generatePdf', () => { expect(mockGenerate).toHaveBeenCalledWith( ['file:///mock/scans/scan1.jpg', '/mock/scans/scan2.jpg'], 'test', + expect.any(Array), ); }); From 44f35d24307372ca0ab0eaf42ae813876e177b9f Mon Sep 17 00:00:00 2001 From: Rakshit Kumar Singh Date: Thu, 23 Apr 2026 02:09:42 +0530 Subject: [PATCH 5/7] fix filter size --- src/screens/ReviewScreen.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/screens/ReviewScreen.tsx b/src/screens/ReviewScreen.tsx index 82f4b20..846f1a5 100644 --- a/src/screens/ReviewScreen.tsx +++ b/src/screens/ReviewScreen.tsx @@ -31,7 +31,7 @@ export default function ReviewScreen({pages, onSave, onAddMore, onCancel}: Props const [currentIndex, setCurrentIndex] = useState(0); const renderImage = (page: ScannedPage) => ( - + ); From 65d9b64b36f54a9942b3ad798e234cebe2c8fd72 Mon Sep 17 00:00:00 2001 From: Rakshit Kumar Singh Date: Thu, 23 Apr 2026 02:42:23 +0530 Subject: [PATCH 6/7] brightness weirdness fix --- src/utils/filterMatrices.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/filterMatrices.ts b/src/utils/filterMatrices.ts index b998eb2..34ba711 100644 --- a/src/utils/filterMatrices.ts +++ b/src/utils/filterMatrices.ts @@ -6,9 +6,9 @@ export function getFilterMatrix(filter: FilterMode): number[] { case 'grayscale': return saturate(0); case 'blackwhite': - return concatColorMatrices(saturate(0), contrast(4), brightness(-0.5)); + return concatColorMatrices(saturate(0), contrast(4)); case 'enhanced': - return concatColorMatrices(contrast(1.4), brightness(0.05)); + return concatColorMatrices(contrast(1.3), brightness(1.1)); default: return normal(); } From 2ce8590290936f5603cb662518b9465d920b69ab Mon Sep 17 00:00:00 2001 From: Rakshit Kumar Singh Date: Thu, 23 Apr 2026 02:54:09 +0530 Subject: [PATCH 7/7] fix preview screen colors --- src/components/DocumentCard.tsx | 6 +++++- src/screens/ViewerScreen.tsx | 14 +++++++++----- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/src/components/DocumentCard.tsx b/src/components/DocumentCard.tsx index 5218a03..0f02eb9 100644 --- a/src/components/DocumentCard.tsx +++ b/src/components/DocumentCard.tsx @@ -1,7 +1,9 @@ import React from 'react'; import {View, Text, Image, TouchableOpacity, StyleSheet} from 'react-native'; +import {ColorMatrix} from 'react-native-color-matrix-image-filters'; import {ScannedDocument} from '../types'; import {formatDate} from '../utils/imageProcessing'; +import {getFilterMatrix} from '../utils/filterMatrices'; import {useTheme} from '../theme'; interface Props { @@ -20,7 +22,9 @@ export default function DocumentCard({document, onPress}: Props) { activeOpacity={0.8}> {thumb ? ( - + + + ) : ( )} diff --git a/src/screens/ViewerScreen.tsx b/src/screens/ViewerScreen.tsx index 3dfdb38..09ed081 100644 --- a/src/screens/ViewerScreen.tsx +++ b/src/screens/ViewerScreen.tsx @@ -14,8 +14,10 @@ import { TextInput, } from 'react-native'; 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 {getFilterMatrix} from '../utils/filterMatrices'; import {useTheme} from '../theme'; const {width: SW} = Dimensions.get('window'); @@ -98,11 +100,13 @@ export default function ViewerScreen({document, onBack, onDelete, onRename}: Pro }}> {document.pages.map((page, i) => ( - + + + Page {i + 1} ))}