On 23 May 2014, at 2:52 , Henrik Johansen <henrik.s.johansen@veloxit.no> wrote:
On 23 May 2014, at 2:36 , Torsten Bergmann <astares@gmx.de> wrote:
Hi,
as there was no responded so far I tried to track the problem deeper and did a short test client in C/C++ to verify if I can call WKHTMLTOPDF there and generate a PDF and to compare to the Pharo/NativeBoost client.
Attached is the according CPP file which works and results in a PDF with the google homepage.
I also additionally implemented the callback functions in the Pharo wrapper and using the error handling I noticed that the component returns "Failed loading page http:// ..."
With that info I compared the C/C++ calls to the Pharo/NativeBoost calls more deeply and found out that in C/C++ the following call for an object setting works (by returning 1):
// this returns 1 in C/C++ which is OK int a = wkhtmltopdf_set_object_setting(os, "page", "http://www.google.de"); printf("object set %i",a);
while in Pharo the same call fails (by returning 0):
"The following call failed and returns 0 instead of 1" self setObjectSetting: 'page' to: 'http://www.google.de' in: os.
With argument type char * , NB does not 0-terminate your strings, but passes in the argument you provided raw. ByteStrings have an internal representation size a multiplum of 4, thus âpageâ will have no accidentally terminating 0, while âoutâ does, and the call fails since whatever garbage bytes are in the image after the string instance (âpageâ) leads to the it not being recognized as a valid property name.
Change the call definitions to use String as argument type instead, and NB will convert the args to zero-terminated strings for you:
NBFFICallout stdcall: #(int wkhtmltopdf_set_object_setting(wkhtmltopdf_object_settings* settings, String aName, String value)) module: 'wkhtmltox.dll'
Alternatively, you need to provide null-terminated strings yourself:
x := self setObjectSetting: ('page' , (Character value: 0) asString) to: 'http://www.google.de', (Character value: 0) asString) in: os.
Which also returns makes it return 1.
Cheers, Henry
To test it then *actually* worked, I implemented: getObjectSetting: aName in: settings into: value ofSize: vs <primitive: #primitiveNativeCall module: #NativeBoostPlugin> ^NBFFICallout stdcall: #(int wkhtmltopdf_get_object_setting(wkhtmltopdf_object_settings* settings, String aName, char* value, int vs)) module: 'wkhtmltox.dll' Note, you *cannot* use String for a parameter being written to, while NB in that case would pass along a zero-terminated copy for you, it does *not* copy the contents back into the actual string instance @ end of call. Itâs also easy to spot garbage characters added when value was // 4 = 0 (using char * definition): x := self setObjectSetting: (âpage' , Character null asString) to: 'http://www.google.de' in: os. "http://www.google.de size = 20" Transcript show: x. buffer := String new: 40. x := self getObjectSetting: ('page' , Character null asString) in: os into: buffer ofSize: buffer size. (buffer indexOf:Character null) > 21 "Probably true..." Cheers, Henry PS: Thatâs a glorious getter API⦠It returns 1 even if your buffer is shorter than the actual parameter, so your only way to be sure youâve fetched all of it, is to test that the result give contains a null character, and if not, retry with a larger buffer⦠(The sane thing to do would be allowing you to query the parameter length, either through a * int parameter, or a separate function).