@@ -17,9 +17,15 @@ import dev.nucleusframework.pdfium.native.FPDFText_GetUnicode
1717import dev.nucleusframework.pdfium.native.FPDFText_LoadPage
1818import kotlinx.cinterop.DoubleVar
1919import kotlinx.cinterop.value
20+ import dev.nucleusframework.pdfium.native.FORM_OnAfterLoadPage
21+ import dev.nucleusframework.pdfium.native.FORM_OnBeforeClosePage
22+ import dev.nucleusframework.pdfium.native.FPDFDOC_ExitFormFillEnvironment
23+ import dev.nucleusframework.pdfium.native.FPDFDOC_InitFormFillEnvironment
2024import dev.nucleusframework.pdfium.native.FPDF_ANNOT
2125import dev.nucleusframework.pdfium.native.FPDF_CloseDocument
2226import dev.nucleusframework.pdfium.native.FPDF_ClosePage
27+ import dev.nucleusframework.pdfium.native.FPDF_FFLDraw
28+ import dev.nucleusframework.pdfium.native.FPDF_FORMFILLINFO
2329import dev.nucleusframework.pdfium.native.FPDF_GetLastError
2430import dev.nucleusframework.pdfium.native.FPDF_GetMetaText
2531import dev.nucleusframework.pdfium.native.FPDF_GetPageCount
@@ -31,6 +37,8 @@ import dev.nucleusframework.pdfium.native.FPDF_LoadMemDocument64
3137import dev.nucleusframework.pdfium.native.FPDF_LoadPage
3238import dev.nucleusframework.pdfium.native.FPDF_RenderPageBitmap
3339import cnames.structs.fpdf_document_t__
40+ import cnames.structs.fpdf_form_handle_t__
41+ import kotlinx.cinterop.nativeHeap
3442import kotlin.concurrent.AtomicReference
3543import kotlinx.cinterop.ByteVar
3644import kotlinx.cinterop.CPointer
@@ -81,6 +89,11 @@ internal actual class PdfDocument(
8189 // The pin must stay alive until close() or PDFium will dereference freed memory on the
8290 // next FPDF_LoadPage / FPDF_GetMetaText call.
8391 private val pinnedBuffer : Pinned <ByteArray >,
92+ // Per-document FPDF_FORMHANDLE used for FPDF_FFLDraw widget overlay (signatures, form
93+ // fields). May be null if PDFium refused init — render falls back to no overlay.
94+ private val formHandle : CPointer <fpdf_form_handle_t__>? ,
95+ // The FPDF_FORMFILLINFO backing the form handle. Must out-live the handle; freed in close().
96+ private val formInfoPtr : CPointer <FPDF_FORMFILLINFO >? ,
8497) {
8598 actual val pageCount: Int = FPDF_GetPageCount (handle)
8699 actual val metadata: PdfMetadata = PdfMetadata (
@@ -127,6 +140,13 @@ internal actual class PdfDocument(
127140 RenderQuality .FULL -> FPDF_ANNOT
128141 }
129142 FPDF_RenderPageBitmap (bmp, page, 0 , 0 , widthPx, heightPx, 0 , flags)
143+ // Form widget overlay (signatures, fillable fields). Only at FULL quality —
144+ // PREVIEW renders are thumbnails and don't need the extra pass.
145+ if (quality == RenderQuality .FULL && formHandle != null ) {
146+ FORM_OnAfterLoadPage (page, formHandle)
147+ FPDF_FFLDraw (formHandle, bmp, page, 0 , 0 , widthPx, heightPx, 0 , flags)
148+ FORM_OnBeforeClosePage (page, formHandle)
149+ }
130150 FPDFBitmap_Destroy (bmp)
131151 } finally {
132152 FPDF_ClosePage (page)
@@ -238,6 +258,10 @@ internal actual class PdfDocument(
238258 }
239259
240260 actual fun close () {
261+ // Form-fill env must be torn down BEFORE the document — PDFium dereferences the
262+ // document inside FPDFDOC_ExitFormFillEnvironment.
263+ if (formHandle != null ) FPDFDOC_ExitFormFillEnvironment (formHandle)
264+ if (formInfoPtr != null ) nativeHeap.free(formInfoPtr.rawValue)
241265 FPDF_CloseDocument (handle)
242266 pinnedBuffer.unpin()
243267 }
@@ -277,5 +301,11 @@ internal actual suspend fun openPdfDocument(bytes: ByteArray, password: String?)
277301 pinned.unpin()
278302 error(" PDFium refused to open document (err=$err )" )
279303 }
280- PdfDocument (handle, pinned)
304+ // Form-fill env: minimal callbacks (version=2, all null) is enough for static widget
305+ // rendering. The struct lives in nativeHeap because PDFium retains a borrowed pointer
306+ // to it for the form handle's lifetime.
307+ val formInfo = nativeHeap.alloc<FPDF_FORMFILLINFO >().apply { version = 2 }
308+ val formHandle = FPDFDOC_InitFormFillEnvironment (handle, formInfo.ptr)
309+ if (formHandle == null ) nativeHeap.free(formInfo.rawPtr)
310+ PdfDocument (handle, pinned, formHandle, if (formHandle != null ) formInfo.ptr else null )
281311 }
0 commit comments