From 0de6e3c4260f1319b241c83f9e7e99169fe28da3 Mon Sep 17 00:00:00 2001 From: geisserml Date: Tue, 14 Mar 2023 21:36:06 +0100 Subject: [PATCH] Continue on 260140b / c2d4a17 (CC #191) This is an API-breaking change. --- docs/devel/changelog_staging.md | 2 ++ src/pypdfium2/_helpers/document.py | 33 +++++++++++++++++++++--------- 2 files changed, 25 insertions(+), 10 deletions(-) diff --git a/docs/devel/changelog_staging.md b/docs/devel/changelog_staging.md index d15b83b2a..ba0d23a8e 100644 --- a/docs/devel/changelog_staging.md +++ b/docs/devel/changelog_staging.md @@ -4,3 +4,5 @@ # Changelog for next release +- On multi-page rendering, `PdfDocument` objects constructed in parallel jobs now correctly initialize a form env if the triggering `PdfDocument` has an active one. (In versions 4.1 and 4.2, a form env would never be initialized and thus forms never rendered. In v4.0, a form env would always be initialized for documents with forms.) +- API-breaking change on `PdfDocument.init_forms()`: `config` (`FPDF_FORMFILLINFO`) parameter changed to `config_maker` (`Callable` -> `FPDF_FORMFILLINFO`), on behalf of the multi-page renderer. diff --git a/src/pypdfium2/_helpers/document.py b/src/pypdfium2/_helpers/document.py index ec5e74188..44f5a5aa6 100644 --- a/src/pypdfium2/_helpers/document.py +++ b/src/pypdfium2/_helpers/document.py @@ -75,6 +75,7 @@ def __init__( self._data_holder = [] self._data_closer = [] self.formenv = None + self._form_config_maker = None if isinstance(self._input, pdfium_c.FPDF_DOCUMENT): self.raw = self._input @@ -133,7 +134,12 @@ def new(cls): return cls(new_pdf) - def init_forms(self, config=None): + @staticmethod + def _default_form_config_maker(): + return pdfium_c.FPDF_FORMFILLINFO(version=2) + + + def init_forms(self, config_maker=None): """ Initialize a form env, if the document has forms. If already initialized, nothing will be done. See the :attr:`formenv` attribute. @@ -143,16 +149,23 @@ def init_forms(self, config=None): before getting any page handles (due to PDFium's API). Parameters: - config (FPDF_FORMFILLINFO): - Caller-provided form config interface to use. Optional for default builds. - Must be given if using V8 enabled PDFium (:data:`.V_PDFIUM_IS_V8`), with all required fields implemented. + config_maker (typing.Callable | None): + Callback returning a custom form config interface. Optional for default builds. + Must be given if using V8 enabled PDFium (:data:`.V_PDFIUM_IS_V8`), and the returned config must implement all required fields. """ + if (self.get_formtype() == pdfium_c.FORMTYPE_NONE) or self.formenv: return - if not config: + + if config_maker: + self._form_config_maker = config_maker + else: if V_PDFIUM_IS_V8: - raise RuntimeError("A caller-provided form config is required with V8 enabled PDFium.") - config = pdfium_c.FPDF_FORMFILLINFO(version=2) + raise RuntimeError("A caller-provided form config maker is required with V8 enabled PDFium.") + else: + config_maker = PdfDocument._default_form_config_maker + + config = config_maker() raw = pdfium_c.FPDFDOC_InitFormFillEnvironment(self, config) self.formenv = PdfFormEnv(raw, config, self) @@ -540,15 +553,14 @@ def get_toc( @classmethod - def _process_page(cls, index, input_data, password, renderer, converter, pass_info, need_formenv, **kwargs): + def _process_page(cls, index, input_data, password, renderer, converter, pass_info, need_formenv, form_config_maker, **kwargs): pdf = cls( input_data, password = password, ) if need_formenv: - # TODO handle custom form config - as ctypes objects can't be pickled, we can't directly pass in a form config (which recursively consists of ctypes objects), so we'll need some different mechanism, likely a callback to create the form config. - pdf.init_forms() + pdf.init_forms(config_maker=form_config_maker) page = pdf[index] bitmap = renderer(page, **kwargs) @@ -617,6 +629,7 @@ def render( converter = converter, pass_info = pass_info, need_formenv = bool(self.formenv), + form_config_maker = self._form_config_maker, **kwargs )