Home > Net >  Saving PDF for sharing using iText with backwards compatibility
Saving PDF for sharing using iText with backwards compatibility

Time:02-10

A problem with saving a PDF file on Android has been bugging me for weeks, making every Google search I do result in a list of purple links.

I have attempted numerous approaches, but have only managed to save a PDF file using the built in PDF creator, which does not fulfill my purpose.

I have a number of questions that I seek an answer to, and hope some of you can help.

Question 1: Where should you save a PDF file you intend to share and then delete immediately after?

Q1 a) Is a ContentResolver & ContentProvider required, or is this only when sharing a 'directory' between apps?

Intuitively it makes most sense for me to save the PDF in the internal cache directory, share it from there and then delete it.

Question 2: What is the approach for storing files with backwards compatibility?

From what I have read, different SDK levels require different approaches for file storage, being:

  • <= SDK 28 uses traditional storage methods, which is (using File API)?
  • <= SDK 29 you should request permission for legacy storage, if using external storage
  • >= SDK 30 Android enforces scoped storage

Using SAF (Storage Access Framework) seems to be the recommended way to store documents like PDFs. However, I don't intend to display a file picker to the user, when they are simply generating and sharing a PDF.

I am encountering this very generic exception, which occurs when iText closes the output stream. Below are some code examples and their resulting errors.

SDK 30 File API test

fun createPdfInternalSDK30(context: Context) {
    val fileName = "test.pdf"

    //  MODE_PRIVATE creates in internal storage, right?
    val out = context.openFileOutput(fileName, Context.MODE_PRIVATE)
    // out path: /data/user/0/dk.overlevelsesguiden.de10her/files/test.pdf

    try {
        val writer = PdfWriter(out)
        val pdf = PdfDocument(writer)
        
        Document(pdf, PageSize.A4, false).apply { 
            add(Paragraph("Test"))
            close()
        }
    } catch (e: IOException) {
        e.printStackTrace()
    }
}

Error

java.lang.ExceptionInInitializerError
        at com.itextpdf.commons.actions.producer.ProducerBuilder.modifyProducer(ProducerBuilder.java:97)
        at com.itextpdf.kernel.actions.events.FlushPdfDocumentEvent.doAction(FlushPdfDocumentEvent.java:103)
        at com.itextpdf.commons.actions.EventManager.onEvent(EventManager.java:71)
        at com.itextpdf.kernel.pdf.PdfDocument.close(PdfDocument.java:849)
        at com.itextpdf.layout.Document.close(Document.java:117)
        at dk.overlevelsesguiden.de10her.business.PDFService.createPdfInternalSDK30(PDFService.kt:118)
    Caused by: java.util.regex.PatternSyntaxException: Syntax error in regexp pattern near index 12
        \$\{([^}]*)}

SDK 29 Scoped Storage test

    @RequiresApi(api = Build.VERSION_CODES.Q)
    fun createPdfSharedSDK29plus(context: Context) {
        val pdfCollection = MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)

        val contentValues = ContentValues().apply {
            put(MediaStore.MediaColumns.DISPLAY_NAME, "title")
            put(MediaStore.MediaColumns.MIME_TYPE, "application/pdf")
        }

        var uri: Uri? = null

        try {
            uri = context.contentResolver.insert(pdfCollection, contentValues) ?: throw IOException("Failed to create new MediaStore record")
            val out = context.contentResolver.openOutputStream(uri)
            val writer = PdfWriter(out)
            val pdf = PdfDocument(writer)
            Document(pdf, PageSize.A4, false).apply {
                add(Paragraph("Test"))
                close()
            }
        } catch (e: IOException) {
            uri?.let { orphanUri ->
                context.contentResolver.delete(orphanUri, null, null)
            }
        }
    }

Error

java.lang.ExceptionInInitializerError
        at com.itextpdf.commons.actions.producer.ProducerBuilder.modifyProducer(ProducerBuilder.java:97)
        at com.itextpdf.kernel.actions.events.FlushPdfDocumentEvent.doAction(FlushPdfDocumentEvent.java:103)
        at com.itextpdf.commons.actions.EventManager.onEvent(EventManager.java:71)
        at com.itextpdf.kernel.pdf.PdfDocument.close(PdfDocument.java:849)
        at com.itextpdf.layout.Document.close(Document.java:117)
        at dk.overlevelsesguiden.de10her.business.PDFService.finalTest(PDFService.kt:118)
    Caused by: java.util.regex.PatternSyntaxException: Syntax error in regexp pattern near index 12
        \$\{([^}]*)}

Manifest permissions

<!-- Without this folders will be inaccessible in Android-11 and above devices -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
    
<!-- Without this entry storage-permission entry will not be visible under app-info permissions list Android-10 and below -->
<uses-permission 
    android:name="android.permission.WRITE_EXTERNAL_STORAGE"
    android:maxSdkVersion="28"
/>

CodePudding user response:

  •  Tags:  
  • Related