/** * Docutain SDK React Native * Copyright (c) INFOSOFT Informations- und Dokumentationssysteme GmbH. All rights reserved. * * Docutain SDK React Native is a commercial product and requires a license. * Details found in the LICENSE file in the root directory of this source tree. */ package com.docutainsdk import android.app.Activity import android.content.Intent import android.net.Uri import android.util.Log import com.facebook.react.bridge.ActivityEventListener import com.facebook.react.bridge.BaseActivityEventListener import com.facebook.react.bridge.Promise import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReactContextBaseJavaModule import com.facebook.react.bridge.ReactMethod import com.facebook.react.bridge.ReadableMap import com.facebook.react.bridge.ReadableArray import com.facebook.react.bridge.ReadableType import de.docutain.sdk.Document import de.docutain.sdk.DocutainSDK import de.docutain.sdk.Logger import de.docutain.sdk.dataextraction.AnalyzeConfiguration import de.docutain.sdk.dataextraction.DocumentDataReader import de.docutain.sdk.ui.DocumentScanner import de.docutain.sdk.ui.DocumentScannerConfiguration import de.docutain.sdk.ui.DocutainButton import de.docutain.sdk.ui.DocutainColor import de.docutain.sdk.ui.ScanFilter import de.docutain.sdk.ui.Source import java.io.File import android.util.Base64 class DocutainSdkModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) { private val logTag = "DocutainSdk" private val SCAN_DOCUMENT_RESULT_CODE = 1 private var callerContext: ReactApplicationContext; private var scanPromise : Promise? = null private val mActivityEventListener: ActivityEventListener = object : BaseActivityEventListener() { override fun onActivityResult(activity: Activity?, requestCode: Int, resultCode: Int, intent: Intent?) { Log.v(logTag, "onActivityResult" ) if (requestCode == SCAN_DOCUMENT_RESULT_CODE) { if (scanPromise != null) { scanPromise!!.resolve(if(resultCode == Activity.RESULT_OK) true else false) scanPromise = null Log.v(logTag, if(resultCode == Activity.RESULT_OK) "scannen finished" else "scannen canceled" ) } } } } init { callerContext = reactContext callerContext.addActivityEventListener(mActivityEventListener) } override fun getName(): String { return NAME } @ReactMethod fun initSDK(licenseKey: String, promise: Promise) { Log.v(logTag, "InitSDK Start") if(!DocutainSDK.initSDK(callerContext.getApplicationContext() as android.app.Application, licenseKey)){ //init of Docutain SDK failed, get the last error message Log.e(logTag,"Initialization of the Docutain SDK failed: ${DocutainSDK.getLastError()}") //your logic to deactivate access to SDK functionality if(licenseKey == "YOUR_LICENSE_KEY_HERE"){ licenseKeyMissing = true } promise.resolve(false); return; } promise.resolve(true); } @ReactMethod fun deleteTempFiles(deleteTraceFileContent: Boolean ,promise: Promise) { Log.v(logTag, "deleteTempFiles") try { val rc = DocutainSDK.deleteTempFiles(deleteTraceFileContent) promise.resolve(rc) } catch(ex:Exception ) { Log.e(logTag, "deleteTempFiles Exception"+ ex) promise.reject(ex); } } @ReactMethod fun getLastError( promise: Promise) { Log.v(logTag, "getLastError") try { val text = DocutainSDK.getLastError() promise.resolve(text) } catch(ex:Exception ) { Log.e(logTag, "getLastError Exception"+ ex); promise.reject(ex); } } @ReactMethod fun setLogLevel(LogLevel: String, promise: Promise) { Log.v(logTag, "setLogLevel:" + LogLevel) try { val logLevel: Logger.Level = getLogLevelFromString(LogLevel); Log.d(logTag, "setLogLevel " + logLevel) Logger.setLogLevel(logLevel); promise.resolve(true) } catch(ex:Exception ) { Log.e(logTag, "setLogLevel Exception"+ ex) promise.reject(ex); } } @ReactMethod fun getTraceFile( promise: Promise) { Log.v(logTag, "getTraceFile") try { val text = Logger.getTraceFile().getPath(); promise.resolve(text) } catch(ex:Exception ) { Log.e(logTag, "getLastError Exception"+ ex) promise.reject(ex); } } @ReactMethod fun setAnalyzeConfiguration(config: ReadableMap, promise: Promise) { Log.v(logTag, "setAnalyzeConfiguration") val analyzeConfig = AnalyzeConfiguration() mapAnalyzeConfiguration(config, analyzeConfig); Log.v(logTag, "analyzeConfig.readBIC:" + analyzeConfig.readBIC) if(!DocumentDataReader.setAnalyzeConfiguration(analyzeConfig)){ Log.e(logTag,"Setting AnalyzeConfiguration failed: ${DocutainSDK.getLastError()}") promise.resolve(false); } else { promise.resolve(true); } } @ReactMethod fun scanDocument(options: ReadableMap, promise: Promise) { Log.v(logTag, "scanDocument Start") try { val currentActivity: Activity? = getCurrentActivity() if (currentActivity == null) { promise.resolve(false) return; } scanPromise = promise val nativeScanConfig = DocumentScannerConfiguration() mapDocumentScannerConfiguration(options, nativeScanConfig); Log.d(logTag, "allowCaptureModeSetting:" + nativeScanConfig.allowCaptureModeSetting) val intent = DocumentScanner.newScanIntent(callerContext.getApplicationContext(), nativeScanConfig) currentActivity.startActivityForResult(intent, SCAN_DOCUMENT_RESULT_CODE) Log.v(logTag, "scanDocument start OK") } catch(ex:Exception ) { Log.e(logTag, "scanDocument Exception"+ ex) promise.reject(ex); } } @ReactMethod fun loadFile(filepath: String, promise: Promise) { Log.v(logTag, "loadFile:" + filepath) try { val fileUri:Uri = Uri.parse(filepath) val rc = DocumentDataReader.loadFile(fileUri) Log.d(logTag, "loadFile rc:" + rc) promise.resolve(rc) } catch(ex:Exception ) { Log.e(logTag, "loadFile Exception"+ ex) promise.reject(ex); } } @ReactMethod fun writePDF(filepath: String, overWrite: Boolean, PageFormat: String, promise: Promise) { Log.v(logTag, "PageFormat:" + PageFormat) try { val PDFFormat: Document.PDFPageFormat = getPDFPageFormatFromString(PageFormat); Log.d(logTag, "PDFFormat:" + PDFFormat) val file = File(filepath); val rc = Document.writePDF(file,overWrite, PDFFormat, 0) promise.resolve(rc?.absolutePath) } catch(ex:Exception ) { Log.e(logTag, "writePDF Exception"+ ex) promise.reject(ex); } } @ReactMethod fun writeImage(pageNumber: Int, filepath: String, promise: Promise) { try { val file = File(filepath); val rc = Document.writeImage(pageNumber, file) promise.resolve(rc?.absolutePath) } catch(ex:Exception ) { Log.e(logTag, "writeImage Exception"+ ex) promise.reject(ex); } } @ReactMethod fun getImageBytes(pageNumber: Int, pageSourceType: String, promise: Promise) { try { val sourceType: Document.PageSourceType = getPageSourceTypeFromString(pageSourceType); val bytes = Document.getImageBytes(pageNumber, sourceType) if(bytes == null){ promise.resolve(null) } else{ promise.resolve(Base64.encodeToString(bytes, Base64.DEFAULT)) } } catch(ex:Exception ) { Log.e(logTag, "writeImage Exception"+ ex) promise.reject(ex); } } @ReactMethod fun getText(promise: Promise) { try { promise.resolve(DocumentDataReader.getText()) } catch(ex:Exception ) { Log.e(logTag, "getText Exception"+ ex) promise.reject(ex); } } @ReactMethod fun getTextPage(pageNumber: Int, promise: Promise) { try { promise.resolve(DocumentDataReader.getText(pageNumber)) } catch(ex:Exception ) { Log.e(logTag, "getText Exception"+ ex) promise.reject(ex); } } @ReactMethod fun analyze( promise: Promise) { Log.v(logTag, "analyze") try { val text = DocumentDataReader.analyze() Log.d(logTag, "analyze:" + text) promise.resolve(text) } catch(ex:Exception ) { Log.e(logTag, "analyze Exception"+ ex) promise.reject(ex); } } @ReactMethod fun pageCount(promise: Promise) { try { promise.resolve(Document.pageCount()) } catch(ex:Exception ) { Log.e(logTag, "pageCount Exception" + ex) promise.reject(ex); } } companion object { const val NAME = "DocutainSdk" var licenseKeyMissing = false } private fun getLogLevelFromString(loglevel: String) : Logger.Level { when (loglevel.uppercase()) { "DISABLE" -> return Logger.Level.DISABLE; "ASSERT" -> return Logger.Level.ASSERT; "ERROR" -> return Logger.Level.ERROR; "WARNING" -> return Logger.Level.WARNING; "INFO" -> return Logger.Level.INFO; "DEBUG" -> return Logger.Level.DEBUG; "VERBOSE" -> return Logger.Level.VERBOSE; else -> return Logger.Level.ERROR; } } private fun getScanFilterFromString(scanFilter: String) : ScanFilter { when (scanFilter.uppercase()) { "AUTO" -> return ScanFilter.AUTO; "GRAY" -> return ScanFilter.GRAY; "BLACKWHITE" -> return ScanFilter.BLACKWHITE; "ORIGINAL" -> return ScanFilter.ORIGINAL; "TEXT" -> return ScanFilter.TEXT; "AUTO2" -> return ScanFilter.AUTO2; "ILLUSTRATION" -> return ScanFilter.ILLUSTRATION; else -> return ScanFilter.ILLUSTRATION; } } private fun getScanSourceFromString(scanSource: String) : Source { when (scanSource.uppercase()) { "CAMERA" -> return Source.CAMERA; "IMAGE" -> return Source.IMAGE; "GALLERY" -> return Source.GALLERY; "GALLERY_MULTIPLE" -> return Source.GALLERY_MULTIPLE; else -> return Source.CAMERA; } } private fun getPDFPageFormatFromString(pageFormat: String) : Document.PDFPageFormat { when (pageFormat.uppercase()) { "FIT_TO_PAGES" -> return Document.PDFPageFormat.FIT_TO_PAGES; "A4" -> return Document.PDFPageFormat.A4; "A4_LANDSCAPE" -> return Document.PDFPageFormat.A4_LANDSCAPE; "A5" -> return Document.PDFPageFormat.A5; "A5_LANDSCAPE" -> return Document.PDFPageFormat.A5_LANDSCAPE; "LETTER" -> return Document.PDFPageFormat.LETTER; "LETTER_LANDSCAPE" -> return Document.PDFPageFormat.LETTER_LANDSCAPE; "LEGAL" -> return Document.PDFPageFormat.LEGAL; "LEGAL_LANDSCAPE" -> return Document.PDFPageFormat.LEGAL_LANDSCAPE; else -> return Document.PDFPageFormat.A4; } } private fun getPageSourceTypeFromString(pageSourceType: String) : Document.PageSourceType { when (pageSourceType.uppercase()) { "ORIGINAL" -> return Document.PageSourceType.ORIGINAL; "CUT_FILTER" -> return Document.PageSourceType.CUT_FILTER; "CUT_ONLY" -> return Document.PageSourceType.CUT_ONLY; else -> return Document.PageSourceType.CUT_FILTER; } } private fun mapAnalyzeConfiguration(config: ReadableMap, analyzeConfig: AnalyzeConfiguration) { if(config.hasKey("readBIC")) analyzeConfig.readBIC = config.getBoolean("readBIC") if(config.hasKey("readPaymentState")) analyzeConfig.readPaymentState = config.getBoolean("readPaymentState") } private fun setButtonConfig(docutainButton: DocutainButton, docutainButtonEntry: ReadableMap) { if (docutainButtonEntry.hasKey("title")) docutainButton.title = docutainButtonEntry.getString("title"); if (docutainButtonEntry.hasKey("icon")) { val imgSource = docutainButtonEntry.getString("icon")!! val imgIdentifier = DocutainSDK.context.resources.getIdentifier(imgSource, "drawable", DocutainSDK.context.packageName) docutainButton.icon = imgIdentifier } } private fun mapDocumentScannerConfiguration(options: ReadableMap, scanConfig: DocumentScannerConfiguration) { if(options.hasKey("pageEditConfig")) { val optionsPallowCaptureModeSetting = options.getMap("pageEditConfig") if(optionsPallowCaptureModeSetting != null) { if(optionsPallowCaptureModeSetting.hasKey("allowPageFilter")) { scanConfig.pageEditConfig.allowPageFilter = optionsPallowCaptureModeSetting.getBoolean("allowPageFilter") } if(optionsPallowCaptureModeSetting.hasKey("allowPageRotation")) { scanConfig.pageEditConfig.allowPageRotation = optionsPallowCaptureModeSetting.getBoolean("allowPageRotation") } if(optionsPallowCaptureModeSetting.hasKey("allowPageArrangement")) { scanConfig.pageEditConfig.allowPageArrangement = optionsPallowCaptureModeSetting.getBoolean("allowPageArrangement") } if(optionsPallowCaptureModeSetting.hasKey("allowPageCropping")) { scanConfig.pageEditConfig.allowPageCropping = optionsPallowCaptureModeSetting.getBoolean("allowPageCropping") } if(optionsPallowCaptureModeSetting.hasKey("allowPageRetake")) { scanConfig.pageEditConfig.allowPageRetake = optionsPallowCaptureModeSetting.getBoolean("allowPageRetake") } if(optionsPallowCaptureModeSetting.hasKey("pageArrangementShowDeleteButton")) { scanConfig.pageEditConfig.pageArrangementShowDeleteButton = optionsPallowCaptureModeSetting.getBoolean("pageArrangementShowDeleteButton") } if(optionsPallowCaptureModeSetting.hasKey("pageArrangementShowPageNumber")) { scanConfig.pageEditConfig.pageArrangementShowPageNumber = optionsPallowCaptureModeSetting.getBoolean("pageArrangementShowPageNumber") } } } try { //am 7.10.2024 umgestellt auf kleinschreibung, aus kompatibilitätsgründen die großschreibung noch drin //sollte nach 1 - 2 jahren raus gemacht werden und nur noch kleinschreibung zulassen if(options.hasKey("colorConfig") || options.hasKey("ColorConfig")) { val optionsColorConfig = options.getMap("colorConfig") ?: options.getMap("ColorConfig") val iterator = optionsColorConfig!!.keySetIterator() while (iterator.hasNextKey()) { val key = iterator.nextKey() val docutainColorEntry = optionsColorConfig.getMap(key) if (docutainColorEntry != null) { val light = docutainColorEntry.getString("Light") val dark = docutainColorEntry.getString("Dark") if (light != null && dark != null) { val docutainColor = DocutainColor(light, dark) Log.v("DocutainSdkFull", key + " Light:" + light + " Dark:" + dark) when (key.uppercase()) { "COLORPRIMARY" -> scanConfig.colorConfig.colorPrimary = docutainColor "COLORSECONDARY" -> scanConfig.colorConfig.colorSecondary = docutainColor "COLORONSECONDARY" -> scanConfig.colorConfig.colorOnSecondary = docutainColor "COLORSCANBUTTONSLAYOUTBACKGROUND" -> scanConfig.colorConfig.colorScanButtonsLayoutBackground = docutainColor "COLORSCANBUTTONSFOREGROUND" -> scanConfig.colorConfig.colorScanButtonsForeground = docutainColor "COLORSCANPOLYGON" -> scanConfig.colorConfig.colorScanPolygon = docutainColor "COLORBOTTOMBARBACKGROUND" -> scanConfig.colorConfig.colorBottomBarBackground = docutainColor "COLORBOTTOMBARFOREGROUND" -> scanConfig.colorConfig.colorBottomBarForeground = docutainColor "COLORTOPBARBACKGROUND" -> scanConfig.colorConfig.colorTopBarBackground = docutainColor "COLORTOPBARFOREGROUND" -> scanConfig.colorConfig.colorTopBarForeground = docutainColor else -> Log.e("DocutainSdkFull","mapDocumentScannerConfiguration color not valid " + key ) } } } } } if(options.hasKey("allowCaptureModeSetting")) scanConfig.allowCaptureModeSetting = options.getBoolean("allowCaptureModeSetting") if(options.hasKey("autoCapture")) scanConfig.autoCapture = options.getBoolean("autoCapture") if(options.hasKey("onboardingImageSource")){ val imgSource = options.getString("onboardingImageSource")!! val imgIdentifier = DocutainSDK.context.resources.getIdentifier(imgSource, "drawable", DocutainSDK.context.packageName) scanConfig.onboardingImageSource = imgIdentifier } if(options.hasKey("defaultScanFilter")) scanConfig.defaultScanFilter = getScanFilterFromString(options.getString("defaultScanFilter")!!) if(options.hasKey("source")) scanConfig.source = getScanSourceFromString(options.getString("source")!!) if(options.hasKey("sourceImages")){ val items = options.getArray("sourceImages")!! as ReadableArray val sourceImages = ArrayList() for (i in 0..items.size()-1) { sourceImages.add(java.io.File(items.getString(i))) } scanConfig.sourceImages = sourceImages } if(options.hasKey("autoCrop")) scanConfig.autoCrop = options.getBoolean("autoCrop") if(options.hasKey("multiPage")) scanConfig.multiPage = options.getBoolean("multiPage") if(options.hasKey("preCaptureFocus")) scanConfig.preCaptureFocus = options.getBoolean("preCaptureFocus") if(options.hasKey("textConfig")) { val optionsTextConfig = options.getMap("textConfig") if(optionsTextConfig != null) { if (optionsTextConfig.hasKey("textSizeBottomToolbar")) scanConfig.textConfig.textSizeBottomToolbar = optionsTextConfig.getDouble("textSizeBottomToolbar").toFloat() if (optionsTextConfig.hasKey("textSizeTopToolbar")) scanConfig.textConfig.textSizeTopToolbar = optionsTextConfig.getDouble("textSizeTopToolbar").toFloat() if (optionsTextConfig.hasKey("textSizeScanButtons")) scanConfig.textConfig.textSizeScanButtons = optionsTextConfig.getDouble("textSizeScanButtons").toFloat() if (optionsTextConfig.hasKey("textSizeTitle")) scanConfig.textConfig.textSizeTitle = optionsTextConfig.getDouble("textSizeTitle").toFloat() if (optionsTextConfig.hasKey("textTitleScanPage")) scanConfig.textConfig.textTitleScanPage = optionsTextConfig.getString("textTitleScanPage") if (optionsTextConfig.hasKey("textTitleEditPage")) scanConfig.textConfig.textTitleEditPage = optionsTextConfig.getString("textTitleEditPage") if (optionsTextConfig.hasKey("textTitleFilterPage")) scanConfig.textConfig.textTitleFilterPage = optionsTextConfig.getString("textTitleFilterPage") if (optionsTextConfig.hasKey("textTitleCroppingPage")) scanConfig.textConfig.textTitleCroppingPage = optionsTextConfig.getString("textTitleCroppingPage") if (optionsTextConfig.hasKey("textTitleArrangementPage")) scanConfig.textConfig.textTitleArrangementPage = optionsTextConfig.getString("textTitleArrangementPage") if (optionsTextConfig.hasKey("textTitleConfirmationPage")) scanConfig.textConfig.textTitleConfirmationPage = optionsTextConfig.getString("textTitleConfirmationPage") if (optionsTextConfig.hasKey("textDocumentTitle")) scanConfig.textConfig.textDocumentTitle = optionsTextConfig.getString("textDocumentTitle") if (optionsTextConfig.hasKey("textOnboardingTitle")) scanConfig.textConfig.textOnboardingTitle = optionsTextConfig.getString("textOnboardingTitle") if (optionsTextConfig.hasKey("textOnboardingMessage")) scanConfig.textConfig.textOnboardingMessage = optionsTextConfig.getString("textOnboardingMessage") if (optionsTextConfig.hasKey("textOnboardingCloseButton")) scanConfig.textConfig.textOnboardingCloseButton = optionsTextConfig.getString("textOnboardingCloseButton") if (optionsTextConfig.hasKey("textSizeOnboardingTitle")) scanConfig.textConfig.textSizeOnboardingTitle = optionsTextConfig.getDouble("textSizeOnboardingTitle").toFloat() if (optionsTextConfig.hasKey("textSizeOnboardingMessage")) scanConfig.textConfig.textSizeOnboardingMessage = optionsTextConfig.getDouble("textSizeOnboardingMessage").toFloat() if (optionsTextConfig.hasKey("textFocusHint")) scanConfig.textConfig.textFocusHint = optionsTextConfig.getString("textFocusHint") if (optionsTextConfig.hasKey("textFirstPageHint")) scanConfig.textConfig.textFirstPageHint = optionsTextConfig.getString("textFirstPageHint") if (optionsTextConfig.hasKey("textLastPageHint")) scanConfig.textConfig.textLastPageHint = optionsTextConfig.getString("textLastPageHint") if (optionsTextConfig.hasKey("textOnePageHint")) scanConfig.textConfig.textOnePageHint = optionsTextConfig.getString("textOnePageHint") if (optionsTextConfig.hasKey("textScanProgress")) scanConfig.textConfig.textScanProgress = optionsTextConfig.getString("textScanProgress") if (optionsTextConfig.hasKey("textDeleteDialogCurrentPage")) scanConfig.textConfig.textDeleteDialogCurrentPage = optionsTextConfig.getString("textDeleteDialogCurrentPage")!! if (optionsTextConfig.hasKey("textDeleteDialogAllPages")) scanConfig.textConfig.textDeleteDialogAllPages = optionsTextConfig.getString("textDeleteDialogAllPages")!! if (optionsTextConfig.hasKey("textDeleteDialogCancel")) scanConfig.textConfig.textDeleteDialogCancel = optionsTextConfig.getString("textDeleteDialogCancel") } } if(options.hasKey("buttonConfig")) { val optionsButtonConfig = options.getMap("buttonConfig") val buttonIterator = optionsButtonConfig!!.keySetIterator() while (buttonIterator.hasNextKey()) { val key = buttonIterator.nextKey() val docutainButtonEntry = optionsButtonConfig.getMap(key) if(docutainButtonEntry!=null){ when (key.uppercase()) { "BUTTONEDITROTATE" -> setButtonConfig(scanConfig.buttonConfig.buttonEditRotate, docutainButtonEntry) "BUTTONEDITCROP" -> setButtonConfig(scanConfig.buttonConfig.buttonEditCrop, docutainButtonEntry) "BUTTONEDITFILTER" -> setButtonConfig(scanConfig.buttonConfig.buttonEditFilter, docutainButtonEntry) "BUTTONEDITARRANGE" -> setButtonConfig(scanConfig.buttonConfig.buttonEditArrange, docutainButtonEntry) "BUTTONEDITRETAKE" -> setButtonConfig(scanConfig.buttonConfig.buttonEditRetake, docutainButtonEntry) "BUTTONEDITDELETE" -> setButtonConfig(scanConfig.buttonConfig.buttonEditDelete, docutainButtonEntry) "BUTTONEDITFINISH" -> setButtonConfig(scanConfig.buttonConfig.buttonEditFinish, docutainButtonEntry) "BUTTONCROPEXPAND" -> setButtonConfig(scanConfig.buttonConfig.buttonCropExpand, docutainButtonEntry) "BUTTONCROPSNAP" -> setButtonConfig(scanConfig.buttonConfig.buttonCropSnap, docutainButtonEntry) "BUTTONCROPFINISH" -> setButtonConfig(scanConfig.buttonConfig.buttonCropFinish, docutainButtonEntry) "BUTTONSCANAUTOCAPTUREON" -> setButtonConfig(scanConfig.buttonConfig.buttonScanAutoCaptureOn, docutainButtonEntry) "BUTTONSCANAUTOCAPTUREOFF" -> setButtonConfig(scanConfig.buttonConfig.buttonScanAutoCaptureOff, docutainButtonEntry) "BUTTONSCANTORCH" -> setButtonConfig(scanConfig.buttonConfig.buttonScanTorch, docutainButtonEntry) "BUTTONSCANCAPTURE" -> setButtonConfig(scanConfig.buttonConfig.buttonScanCapture, docutainButtonEntry) "BUTTONSCANFINISH" -> setButtonConfig(scanConfig.buttonConfig.buttonScanFinish, docutainButtonEntry) "BUTTONCONFIRMATIONFINISH" -> setButtonConfig(scanConfig.buttonConfig.buttonConfirmationFinish, docutainButtonEntry) else -> Log.e("DocutainSdkFull", "mapDocumentScannerConfiguration button not valid "+ key) } } } } if(options.hasKey("confirmPages")) scanConfig.confirmPages = options.getBoolean("confirmPages") if(options.hasKey("allowPageEditing")) scanConfig.allowPageEditing = options.getBoolean("allowPageEditing") } catch(ex:Exception ) { Log.e("DocutainSdkFull", "mapDocumentScannerConfiguration Exception"+ ex) } } }