pythonpdfctypeslibharupython-bindings

Has anybody attempted to update the Python bindings of LibHaru for Python 3.9 (64 bit)?


I am running Python 3.9, 64bit, and have compiled libharu and some extensions into a DLL, including libpng, with VS 2022. The DLL can load into Python after adding minimal code tweaking to find the VS runtime DLL, which libhpdf.dll depends on, and seems to work (there are only 5 .py files in the bindings) I´ve built the DLL enabling the PTRACE, to follow progress. Have also built the C demos dynamically with that DLL and they all work, generating the PDFs.

I´m trying to run a python demo sample contained in the bindings, arc_demo.py hoping to generate and identical PDF file

I can tell everything works fine till the line adding a document: pdf = HPDF_New (error_handler, NULL) Similar output to the trace obtained with the compiled C version of the sample.

But... the var "pdf" is not what HP_AddPage() expects because when issuing the next call, to add a page page = HPDF_AddPage (pdf) I run into this: **ctypes.ArgumentError: argument 1: <class 'OverflowError'>: int too long to convert**

I think it might have to do with the fact that the original bindings were built and tested with 32bit only. The other suspect is the ctypes of the porting.

So now I am modifying the bindings, mainly hpdf.py. Currently, fighting with the Python to DLL intercommunication, checking if ctypes is handling things as expected. Using byref , casting *pdf *to a c_void_p,... no luck. I get rid of the error if the call is done byref(c_void_p(pdf)), but can not access the contents of the HPDF_Doc structure correctly

Any suggestions? Help you might give? A contrasted method for debugging Python with a C-based DLL?

Thanks, Ignacio

PS: Eventually will write classes for the Haru PDF internal structures. But that will come later, after I am able to run a python sample without errors.


Solution

  • The ctypes interface in if/python/hpdf.py has no .argtypes defined for all the functions. It is particularly important for 64-bit handles and pointers to have proper argument types defined for each function. The original developer probably didn't understand that as indicated by converting from WinDLL to CDLL interfaces. WinDLL uses __stdcall calling convention and needs to know the argument sizes.

    For example, HPDF_Doc is defined as HPDF_HANDLE which is defined as a ctypes.c_void_p. That's a 64-bit pointer on a 64-bit OS. HPDF_New and HPDF_AddPage are defined as:

    #HPDF_Doc HPDF_New (HPDF_Error_Handler user_error_fn, void *user_data)
    HPDF_New=haru.HPDF_New
    HPDF_New.restype=HPDF_Doc
    
    #HPDF_Page HPDF_AddPage (HPDF_Doc pdf)
    HPDF_AddPage=haru.HPDF_AddPage
    HPDF_AddPage.restype=HPDF_Page
    

    ctypes assumes the parameter passed to HPDF_AddPage is a c_int since to has no argtypes. The handle value is >32-bit hence the error seen. Ideally, all functions should explicitly declare their argument types so ctypes can do type checking and properly marshal (convert) parameters from Python objects to C types, e.g.:

    HPDF_AddPage.argtypes = HPDF_Doc,  # must be a list or tuple...comma makes this a 1-tuple.
    HPDF_New.argtypes = HPDF_Error_Handler, c_void_p
    

    Note that argument types must be based on ctypes types. You'll have to carefully trace the arguments and declare the .argtypes for each function.