package com.htmltopdf import android.os.Environment import android.print.PrintAttributes import android.util.Log import com.facebook.react.bridge.Arguments import com.facebook.react.bridge.Promise import com.facebook.react.bridge.ReactApplicationContext import com.facebook.react.bridge.ReadableMap import com.facebook.react.bridge.WritableMap import com.facebook.react.module.annotations.ReactModule import java.io.File import java.io.IOException import java.util.UUID @ReactModule(name = HtmlToPdfModule.NAME) class HtmlToPdfModule(private val reactContext: ReactApplicationContext) : NativeHtmlToPdfSpec(reactContext) { companion object { const val NAME = "HtmlToPdf" private const val TAG = "HtmlToPdf" // Constants from original Java code private const val HTML = "html" private const val FILE_NAME = "fileName" private const val DIRECTORY = "directory" private const val BASE_64 = "base64" private const val BASE_URL = "baseURL" private const val HEIGHT = "height" private const val WIDTH = "width" private const val PDF_EXTENSION = ".pdf" private const val PDF_PREFIX = "PDF_" } override fun getName(): String = NAME override fun convert(options: ReadableMap, promise: Promise) { Log.d(TAG, "convert called with options: $options") try { // Check if we need to force reset the converter (as a safety measure) val pdfConverter = android.print.PdfConverter.getInstance() // If this is a retry after getting CONVERSION_IN_PROGRESS, force reset if (options.hasKey("forceReset") && options.getBoolean("forceReset")) { Log.d(TAG, "Force resetting PDF converter as requested") pdfConverter.forceReset() } // Get HTML content val htmlString = if (options.hasKey(HTML)) { options.getString(HTML) } else { null } if (htmlString.isNullOrEmpty()) { Log.e(TAG, "Invalid htmlString parameter") promise.reject("INVALID_HTML", "RNHTMLtoPDF error: Invalid htmlString parameter.") return } // Get fileName val fileName = if (options.hasKey(FILE_NAME)) { val name = options.getString(FILE_NAME) if (name != null && !isFileNameValid(name)) { Log.e(TAG, "Invalid fileName parameter: $name") promise.reject("INVALID_FILENAME", "RNHTMLtoPDF error: Invalid fileName parameter.") return } name ?: (PDF_PREFIX + UUID.randomUUID().toString()) } else { PDF_PREFIX + UUID.randomUUID().toString() } // Determine destination file val destinationFile = if (options.hasKey(DIRECTORY)) { val directoryName = options.getString(DIRECTORY) val state = Environment.getExternalStorageState() val path = if (Environment.MEDIA_MOUNTED == state) { File(reactContext.getExternalFilesDir(null), directoryName) } else { File(reactContext.filesDir, directoryName) } if (!path.exists() && !path.mkdirs()) { Log.e(TAG, "Could not create folder structure: ${path.absolutePath}") promise.reject("FOLDER_ERROR", "RNHTMLtoPDF error: Could not create folder structure.") return } File(path, fileName + PDF_EXTENSION) } else { getTempFile(fileName) } // Handle custom page size val pageSize = if (options.hasKey(HEIGHT) && options.hasKey(WIDTH)) { val width = options.getInt(WIDTH) val height = options.getInt(HEIGHT) Log.d(TAG, "Custom page size: ${width}x${height}") PrintAttributes.Builder() .setMediaSize( PrintAttributes.MediaSize( "custom", "CUSTOM", (width * 1000 / 72.0).toInt(), (height * 1000 / 72.0).toInt() ) ) .setResolution( PrintAttributes.Resolution("RESOLUTION_ID", "RESOLUTION_ID", 600, 600) ) .setMinMargins(PrintAttributes.Margins.NO_MARGINS) .build() } else { null } // Get other options val shouldEncode = options.hasKey(BASE_64) && options.getBoolean(BASE_64) val baseURL = if (options.hasKey(BASE_URL)) options.getString(BASE_URL) else null Log.d(TAG, "Converting HTML to PDF:") Log.d(TAG, "- File: ${destinationFile.absolutePath}") Log.d(TAG, "- Base64: $shouldEncode") Log.d(TAG, "- BaseURL: $baseURL") // Convert to PDF convertToPDF( htmlString = htmlString, file = destinationFile, shouldEncode = shouldEncode, promise = promise, baseURL = baseURL, printAttributes = pageSize ) } catch (e: Exception) { Log.e(TAG, "Error in convert method", e) promise.reject("PDF_GENERATION_ERROR", e.message, e) } } private fun convertToPDF( htmlString: String, file: File, shouldEncode: Boolean, promise: Promise, baseURL: String?, printAttributes: PrintAttributes? ) { try { Log.d(TAG, "Starting PDF conversion using PdfConverter") // Create the result map that will be passed to PdfConverter val resultMap: WritableMap = Arguments.createMap() // Get PdfConverter instance and set custom print attributes if provided val pdfConverter = android.print.PdfConverter.getInstance() if (printAttributes != null) { pdfConverter.setPdfPrintAttrs(printAttributes) } // Convert HTML to PDF using the actual PdfConverter pdfConverter.convert( reactContext, htmlString, file, shouldEncode, resultMap, promise, baseURL ) Log.d(TAG, "PDF conversion initiated successfully") } catch (e: Exception) { Log.e(TAG, "Error during PDF conversion", e) promise.reject("PDF_CONVERSION_ERROR", "Failed to convert HTML to PDF: ${e.message}", e) } } @Throws(IOException::class) private fun getTempFile(fileName: String): File { val outputDir = reactApplicationContext.cacheDir return File.createTempFile(fileName, PDF_EXTENSION, outputDir) } @Throws(Exception::class) private fun isFileNameValid(fileName: String): Boolean { return File(fileName).canonicalFile.name == fileName } }