skip to Main Content

I’m doing an Android Kotlin project which will auto-generate certificates when we enter some details in the EditTexts. I have word files (.docx) in my assets folder which has some variables which will be replaced by the data entered in the EditTexts. I’m getting proper output word file, and it downloads properly. Now I need to convert that converted word to pdf in-app itself, and it must be downloaded in my phone instead of the word file.

I used these libraries for reading & editing word file –

implementation 'org.apache.poi:poi:5.2.4'
implementation 'org.apache.poi:poi-ooxml:5.2.4'

This the function that edits the original word file and downloads the new word file :

private fun editWordFile(context: Context, name: String, date: String assetsName: String) {
    
    // assetsName = "joinai.docx" This is written somewhere on the top where the function is called   

    try {
        val assetManager = applicationContext.assets
        val wordFileName = assetsName.toString()

        val inputStream: InputStream = assetManager.open(wordFileName)
        val outputDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)

        val outputName = "${name}_${date}"


        try {

            // Read Word file
            val document = XWPFDocument(inputStream)

            // Replace placeholders/variables with EditText data
            for (paragraph: XWPFParagraph in document.paragraphs) {
                for (run: XWPFRun in paragraph.runs) {
                    val text = run.text()
                    println("Text: $text, Date: $date")

                    if (text.contains("XDATEX")) {
                        run.setText(text.replace("XDATEX", date), 0)
                    }
                    if (text.contains("XLAMAX")) {
                        run.setText(text.replace("XLAMAX", name), 0)
                    }
                 
                }
            }


            // Save modified Word file
            val outputFile = File(outputDir, "${outputName}.docx")
            val fos = FileOutputStream(outputFile)
            document.write(fos)
            fos.close()

            Toast.makeText(this, "File $outputName downloaded.", Toast.LENGTH_SHORT).show()

            // Convert Word to PDF
            // You may implement PDF conversion logic here

        } catch (e: Exception) {
            e.printStackTrace()
        } finally {
            inputStream.close()
        }
    }
    catch (e: Exception) {
        e.printStackTrace()
        Toast.makeText(context, "Error occurred: ${e.message}! Contact developer", Toast.LENGTH_SHORT).show()
    }

}

I need to convert this resulting .docx to .pdf, which has good quality, and is most probably free of cost (if you are recommending any service APIs).

2

Answers


  1. Chosen as BEST ANSWER

    Comment to @Zeros-N-Ones As you told , extra processing need to be done for images, I have background image for this word file, which isn't visible in pdf file, and also the formatting is getting wrecked. What to do next? I've give below my updated code and sample ss images of output word and pdf files.

    private fun editWordFileAndConvertToPdf2(
        context: Context, name: String, program: String, date: String,
        start: String, end: String, assetsName: String, type: String
    ) {
        try {
            val assetManager = context.assets
            val wordFileName = assetsName
            val inputStream: InputStream = assetManager.open(wordFileName)
    
            val outputName = "${name}_${type}_joining.pdf"
            val wordOutputName = "${name}_${type}_joining.docx"
            val outputDir = "/storage/emulated/0/Download/"
    
            // Ensure permissions are granted
            if (!isPermissionGranted(context)) {
                requestPermission(context)
                Toast.makeText(context, "Permission required for file operations", Toast.LENGTH_SHORT).show()
                return
            }
    
            try {
                // Read Word file
                val document = XWPFDocument(inputStream)
    
                // Replace placeholders with data
                for (paragraph: XWPFParagraph in document.paragraphs) {
                    for (run: XWPFRun in paragraph.runs) {
                        val text = run.text()
                        println("Text: $text, Program: $program")
    
                        if (text.contains("XDATEX")) {
                            run.setText(text.replace("XDATEX", date), 0)
                        }
                        if (text.contains("XLAMAX")) {
                            run.setText(text.replace("XLAMAX", name), 0)
                        }
                        if (text.contains("XVARIXBLEX", ignoreCase = true)) {
                            run.setText(text.replace("XVARIXBLEX", program), 0)
                        }
                        if (text.contains("XNOXNOXXXXOXX")) {
                            run.setText(text.replace("XNOXNOXXXXOXX", start), 0)
                        }
                        if (text.contains("YNOXNOXXXXOXY")) {
                            run.setText(text.replace("YNOXNOXXXXOXY", end), 0)
                        }
                    }
                }
    
                // Save the modified Word file using FileOutputStream
                val wordFile = File(outputDir, wordOutputName)
                val fos = FileOutputStream(wordFile)
                document.write(fos)
                fos.close()
                // Save the Word file to Downloads folder as well
                saveWordToDownloads(context, wordFile, wordOutputName)
    
                // Convert Word to PDF
                val pdfFile = File(context.cacheDir, outputName)
                convertWordToPDF(wordFile, pdfFile)
    
                // Save PDF to Downloads folder
                savePDFToDownloads(context, pdfFile, outputName)
    
    
    
                // Delete temporary files if needed
                wordFile.delete()
                outputNameShare= outputName
                imgShare.visibility=VISIBLE
                Toast.makeText(context, "Word and PDF files saved to Downloads: $wordOutputName, $outputName", Toast.LENGTH_SHORT).show()
    
            } catch (e: Exception) {
                e.printStackTrace()
                Toast.makeText(context, "Error occurred: ${e.message}", Toast.LENGTH_SHORT).show()
            } finally {
                inputStream.close()
            }
        } catch (e: Exception) {
            e.printStackTrace()
            Toast.makeText(context, "Error occurred: ${e.message}! Contact developer", Toast.LENGTH_SHORT).show()
        }
    }
    
    
    
    
    
    
    
    private fun isPermissionGranted(context: Context): Boolean {
        return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            // No explicit storage permission needed for scoped storage
            true
        } else {
            val permissionCheck = ContextCompat.checkSelfPermission(
                context, Manifest.permission.WRITE_EXTERNAL_STORAGE
            )
            permissionCheck == PackageManager.PERMISSION_GRANTED
        }
    }
    
    private fun requestPermission(context: Context) {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
            ActivityCompat.requestPermissions(
                context as Activity,
                arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
                100
            )
        }
    }
    
    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        if (requestCode == 100 && grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            Toast.makeText(this, "Permission granted!", Toast.LENGTH_SHORT).show()
        } else {
            Toast.makeText(this, "Permission denied!", Toast.LENGTH_SHORT).show()
        }
    }
    
    private fun savePDFToDownloads(context: Context, sourceFile: File, fileName: String) {
        val resolver = context.contentResolver
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            val values = ContentValues().apply {
                put(MediaStore.MediaColumns.DISPLAY_NAME, fileName)
                put(MediaStore.MediaColumns.MIME_TYPE, "application/pdf")
                put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS)
            }
            val uri = resolver.insert(MediaStore.Files.getContentUri("external"), values)
            uri?.let {
                resolver.openOutputStream(it).use { outputStream ->
                    sourceFile.inputStream().use { inputStream ->
                        inputStream.copyTo(outputStream!!)
                    }
                }
            }
        } else {
            val downloadsDir = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), fileName)
            sourceFile.copyTo(downloadsDir, overwrite = true)
        }
    }
    
    
    private fun saveWordToDownloads(context: Context, sourceFile: File, fileName: String) {
        val resolver = context.contentResolver
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
            val values = ContentValues().apply {
                put(MediaStore.MediaColumns.DISPLAY_NAME, fileName)
                put(MediaStore.MediaColumns.MIME_TYPE, "application/vnd.openxmlformats-officedocument.wordprocessingml.document") // MIME type for Word
                put(MediaStore.MediaColumns.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS)
            }
            val uri = resolver.insert(MediaStore.Files.getContentUri("external"), values)
            uri?.let {
                resolver.openOutputStream(it).use { outputStream ->
                    sourceFile.inputStream().use { inputStream ->
                        inputStream.copyTo(outputStream!!)
                    }
                }
            }
        } else {
            // For Android versions lower than Q
            val downloadsDir = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), fileName)
            sourceFile.copyTo(downloadsDir, overwrite = true)
        }
    }
    
    
    
    
    private fun convertWordToPDF(wordFile: File, pdfFile: File) {
        try {
            val document = Document()
            PdfWriter.getInstance(document, FileOutputStream(pdfFile))
            document.open()
    
            // Read the Word document
            val docxDocument = XWPFDocument(FileInputStream(wordFile))
    
            // Convert paragraphs
            for (paragraph in docxDocument.paragraphs) {
                val text = paragraph.text
                if (text.isNotEmpty()) {
                    document.add(Paragraph(text))
                }
            }
    
            // Convert tables
            for (table in docxDocument.tables) {
                for (row in table.rows) {
                    val rowText = row.tableCells.joinToString(" | ") { it.text }
                    document.add(Paragraph(rowText))
                }
            }
    
            document.close()
            docxDocument.close()
        } catch (e: Exception) {
            throw e
        }
    }
    

    Word output ss Pdf output ss


  2. I think the best approach to this is using Apache POI with iText, which is an open-source pdf library. See modified code below:

    // Add these dependencies to your build.gradle
    // implementation 'com.itextpdf:itextpdf:5.5.13.3'
    // implementation 'org.apache.poi:poi:5.2.4'
    // implementation 'org.apache.poi:poi-ooxml:5.2.4'
    // implementation 'org.apache.xmlgraphics:fop:2.8'
    
    import com.itextpdf.text.Document
    import com.itextpdf.text.Paragraph
    import com.itextpdf.text.pdf.PdfWriter
    import org.apache.poi.xwpf.usermodel.XWPFDocument
    import java.io.*
    import android.os.Environment
    import android.content.Context
    import android.widget.Toast
    
    private fun editWordFileAndConvertToPDF(context: Context, name: String, date: String, assetsName: String) {
        try {
            val assetManager = context.applicationContext.assets
            val wordFileName = assetsName
            val inputStream: InputStream = assetManager.open(wordFileName)
            val outputDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
            val outputName = "${name}_${date}"
    
            try {
                // Read and modify Word file
                val document = XWPFDocument(inputStream)
                
                // Replace placeholders/variables with EditText data
                for (paragraph in document.paragraphs) {
                    for (run in paragraph.runs) {
                        val text = run.text()
                        when {
                            text.contains("XDATEX") -> run.setText(text.replace("XDATEX", date), 0)
                            text.contains("XLAMAX") -> run.setText(text.replace("XLAMAX", name), 0)
                        }
                    }
                }
    
                // First save the modified Word file temporarily
                val tempWordFile = File(context.cacheDir, "temp_${outputName}.docx")
                FileOutputStream(tempWordFile).use { fos ->
                    document.write(fos)
                }
    
                // Convert to PDF
                val pdfFile = File(outputDir, "${outputName}.pdf")
                convertWordToPDF(tempWordFile, pdfFile)
    
                // Delete temporary Word file
                tempWordFile.delete()
    
                Toast.makeText(context, "PDF file saved: ${pdfFile.name}", Toast.LENGTH_SHORT).show()
    
            } catch (e: Exception) {
                e.printStackTrace()
                Toast.makeText(context, "Error during conversion: ${e.message}", Toast.LENGTH_SHORT).show()
            } finally {
                inputStream.close()
            }
        } catch (e: Exception) {
            e.printStackTrace()
            Toast.makeText(context, "Error occurred: ${e.message}! Contact developer", Toast.LENGTH_SHORT).show()
        }
    }
    
    private fun convertWordToPDF(wordFile: File, pdfFile: File) {
        try {
            val document = Document()
            PdfWriter.getInstance(document, FileOutputStream(pdfFile))
            document.open()
    
            // Read the Word document
            val docxDocument = XWPFDocument(FileInputStream(wordFile))
            
            // Convert paragraphs
            for (paragraph in docxDocument.paragraphs) {
                val text = paragraph.text
                if (text.isNotEmpty()) {
                    document.add(Paragraph(text))
                }
            }
    
            // Convert tables
            for (table in docxDocument.tables) {
                for (row in table.rows) {
                    val rowText = row.tableCells.joinToString(" | ") { it.text }
                    document.add(Paragraph(rowText))
                }
            }
    
            document.close()
            docxDocument.close()
        } catch (e: Exception) {
            throw e
        }
    }
    

    The above solution uses iText library for pdf conversion; creates a temporary Word file in the cache directory; converts the Word content to pdf while maintaining text formatting; automatically deletes the temporary Word file; and saves only the final pdf to the Downloads folder.

    To implement this:

    Add the iText dependency to your build.gradle:

    implementation 'com.itextpdf:itextpdf:5.5.13.3'
    

    Replace your existing editWordFile function with the new editWordFileAndConvertToPDF function from the artifact.

    Note that this basic implementation handles text and tables. If you need more advanced formatting (like images, complex layouts, or specific fonts), you might need to add additional conversion logic in the convertWordToPDF function.

    Some limitations to be aware of:

    • Some complex formatting might not transfer perfectly
    • Images will need additional handling if present in your documents
    • Very large documents might need optimisation
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search