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).