Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pdfium/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ kotlin {
// kotlinx-browser exposes the org.khronos.webgl.* typed arrays as a shared API
// across js + wasmJs, so they resolve in the webMain metadata compilation.
webMain.dependencies { implementation(libs.kotlinx.browser) }
jvmTest.dependencies { implementation(libs.kotlin.test) }
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,8 +199,9 @@ internal actual suspend fun openPdfDocument(bytes: ByteArray, password: String?)
for (i in 0 until POOL_SIZE) {
val h = PdfiumBridge.nOpenDocumentFromMemory(bufferAddr, bytes.size.toLong(), password)
if (h == 0L) {
// NOTE: the buffer is freed by the catch below — freeing it here too
// double-frees it (native abort, not an exception).
for (j in 0 until i) PdfiumBridge.nCloseDocument(handles[j])
PdfiumBridge.nFreeBuffer(bufferAddr)
error("PDFium refused to open document (err=${PdfiumBridge.nGetLastError()})")
Comment on lines 201 to 205
}
handles[i] = h
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,9 +224,9 @@ internal actual suspend fun openPdfDocument(bytes: ByteArray, password: String?)
for (i in 0 until POOL_SIZE) {
val h = PdfiumBridge.nOpenDocumentFromMemory(bufferAddr, bytes.size.toLong(), password)
if (h == 0L) {
// Cleanup previously opened handles + buffer on failure.
// Cleanup previously opened handles. The buffer is freed by the catch
// below — freeing it here too double-frees it (native abort).
for (j in 0 until i) PdfiumBridge.nCloseDocument(handles[j])
PdfiumBridge.nFreeBuffer(bufferAddr)
error("PDFium refused to open document (err=${PdfiumBridge.nGetLastError()})")
Comment on lines 226 to 230
}
handles[i] = h
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package dev.nucleusframework.pdfium

import kotlin.test.Test
import kotlin.test.assertFailsWith
import kotlinx.coroutines.runBlocking

class OpenPdfDocumentTest {
/**
* Regression: bytes PDFium refuses must surface as an exception, not kill the
* process. The open-failure branch used to free the native buffer that the
* enclosing `catch` frees as well, so a non-PDF body double-freed it and the
* host aborted — SIGABRT on the JVM, "Scudo ERROR: invalid chunk state when
* deallocating" on Android. Reaching the assertion at all IS the test.
*/
@Test
fun refusedBytesThrowInsteadOfAbortingTheProcess() = runBlocking {
assertFailsWith<IllegalStateException> {
openPdfDocument("definitely not a pdf".encodeToByteArray(), null)
}
Unit
}

@Test
fun aValidDocumentStillOpens() = runBlocking {
val doc = openPdfDocument(minimalPdf(), null)
try {
kotlin.test.assertEquals(1, doc.pageCount)
} finally {
doc.close()
}
}
}

/** A minimal, valid one-page PDF (built-in /Helvetica, no embedded font program). */
private fun minimalPdf(): ByteArray = java.util.Base64.getDecoder().decode(

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feel free to replace this with a different PDF test file that you control.

"JVBERi0xLjQKMSAwIG9iago8PCAvVHlwZSAvQ2F0YWxvZyAvUGFnZXMgMiAwIFIgPj4KZW5kb2Jq" +
"CjIgMCBvYmoKPDwgL1R5cGUgL1BhZ2VzIC9LaWRzIFszIDAgUl0gL0NvdW50IDEgPj4KZW5kb2Jq" +
"CjMgMCBvYmoKPDwgL1R5cGUgL1BhZ2UgL1BhcmVudCAyIDAgUiAvTWVkaWFCb3ggWzAgMCA2MTIg" +
"NzkyXSAvQ29udGVudHMgNCAwIFIgL1Jlc291cmNlcyA8PCAvRm9udCA8PCAvRjEgNSAwIFIgPj4g" +
"Pj4gPj4KZW5kb2JqCjQgMCBvYmoKPDwgL0xlbmd0aCAzMzEgPj4Kc3RyZWFtCkJUIC9GMSAyNCBU" +
"ZiA3MiA3MDAgVGQgMjggVEwgKFRoZSBxdWljayBicm93biBmb3gganVtcHMgb3ZlciB0aGUgbGF6" +
"eSBkb2cuKSBUaiBUKiAoSW52b2ljZSAjMjAyNi0wNzE0ICBBbW91bnQgZHVlOiAkMSwyMzQuNTYp" +
"IFRqIFQqIChUaGlzIGlzIGEgdGV4dC1vbmx5IFBERiB0byBleGVyY2lzZSBwZGZpdW0gYXN5bmMg" +
"Z2x5cGggcGFpbnQuKSBUaiBUKiAoTGluZSBmb3VyIHdpdGggbW9yZSB3b3JkcyB0byBmaWxsIHRo" +
"ZSBwYWdlIGJvZHkgYXJlYSBuaWNlbHkuKSBUaiBUKiAoQ29udGFjdDogY2xhdWRlQG1pa2VwZW56" +
"LmRldiAgIFJlZjogUFJFVklFVy1SRVBSTykgVGogVCogRVQKZW5kc3RyZWFtCmVuZG9iago1IDAg" +
"b2JqCjw8IC9UeXBlIC9Gb250IC9TdWJ0eXBlIC9UeXBlMSAvQmFzZUZvbnQgL0hlbHZldGljYSA+" +
"PgplbmRvYmoKeHJlZgowIDYKMDAwMDAwMDAwMCA2NTUzNSBmIAowMDAwMDAwMDA5IDAwMDAwIG4g" +
"CjAwMDAwMDAwNTggMDAwMDAgbiAKMDAwMDAwMDExNSAwMDAwMCBuIAowMDAwMDAwMjQxIDAwMDAw" +
"IG4gCjAwMDAwMDA2MjMgMDAwMDAgbiAKdHJhaWxlcgo8PCAvU2l6ZSA2IC9Sb290IDEgMCBSID4+" +
"CnN0YXJ0eHJlZgo2OTMKJSVFT0Y="
)
Loading