提问人:Jonas Mohr Pedersen 提问时间:2/10/2022 最后编辑:Jonas Mohr Pedersen 更新时间:2/10/2022 访问量:377
保存 PDF 以使用具有向后兼容性的 iText 进行共享
Saving PDF for sharing using iText with backwards compatibility
问:
在 Android 上保存 PDF 文件的问题已经困扰了我数周,使我所做的每次 Google 搜索都会导致紫色链接列表。
我尝试了多种方法,但只设法使用内置的 PDF 创建器保存 PDF 文件,这并不能达到我的目的。
我有一些问题需要回答,希望你们中的一些人能有所帮助。
问题 1:您应该在哪里保存您打算共享的 PDF 文件,然后在之后立即删除?
问题1 a)ContentResolver 和 ContentProvider 是必需的,还是仅在应用程序之间共享“目录”时?
直观地说,对我来说,将 PDF 保存在内部缓存目录中,从那里共享然后删除它最有意义。
问题 2:存储向后兼容的文件的方法是什么?
从我所读到的内容来看,不同的 SDK 级别需要不同的文件存储方法,即:
- <= SDK 28 使用传统的存储方式,即(使用 File API)?
- <= SDK 29 如果使用外部存储,则应请求旧存储的权限
- >= SDK 30 Android 强制实施分区存储
使用 SAF(存储访问框架)似乎是存储 PDF 等文档的推荐方法。但是,当用户只是生成和共享 PDF 时,我不打算向用户显示文件选取器。
我遇到了这个非常通用的异常,当 iText 关闭输出流时会发生这种情况。下面是一些代码示例及其产生的错误。
SDK 30 文件 API 测试
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()
}
}
错误
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+ 范围存储测试
@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)
}
}
}
错误
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
\$\{([^}]*)}
清单权限
<!-- 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"
/>
答:
你提出了一些问题。此答案侧重于您显示的实际错误。
您显示的实际错误都会导致
java.util.regex.PatternSyntaxException: Syntax error in regexp pattern near index 12
\$\{([^}]*)}
在 iText 类的初始化过程中。它发生在这里:ProducerBuilder
private static final String PATTERN_STRING = "\\$\\{([^}]*)}";
private static final Pattern PATTERN = Pattern.compile(PATTERN_STRING);
实际上,JRE中的正则表达式与Android中的正则表达式不同。在纯 Java 中,正则表达式是被接受的,如果之前没有未转义的开口,则不需要转义为字符。另一方面,Android 要求即使那样也要对其进行转义。\$\{([^}]*)}
}
{
昨天已经提交了对此问题的修复 - https://github.com/itext/itext7/commit/32bf2552770866dce4516798e7b11f26631ae92f - 现在的模式字符串是:
private static final String PATTERN_STRING = "\\$\\{([^}]*)\\}";
因此,在下一个iText版本(或在当前SNAPSHOT中)中,该错误将不再发生。
评论
ntuitively it makes most sense for me to save the PDF in the internal cache directory, share it from there and then delete it.
当然没关系。为什么要再说下去?What is the approach for storing files with backwards compatibility?
我不知道你所说的向后兼容性是什么意思。PatternSyntaxException