├── README.md ├── .gitignore ├── test.go └── wkhtmltopdf └── pdf_c_api.go /README.md: -------------------------------------------------------------------------------- 1 | Depends on wkhtmltopdf library 2 | http://wkhtmltopdf.org/downloads.html 3 | 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Example user template template 3 | ### Example user template 4 | 5 | # IntelliJ project files 6 | .idea 7 | *.iml 8 | test.pdf 9 | .DS_Store -------------------------------------------------------------------------------- /test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "./wkhtmltopdf" 5 | "fmt" 6 | "io/ioutil" 7 | ) 8 | 9 | func main() { 10 | // global settings: http://www.cs.au.dk/~jakobt/libwkhtmltox_0.10.0_doc/pagesettings.html#pagePdfGlobal 11 | gs := wkhtmltopdf.NewGlobalSettings() 12 | gs.Set("outputFormat", "pdf") 13 | gs.Set("out", "") 14 | gs.Set("orientation", "Portrait") 15 | gs.Set("colorMode", "Color") 16 | gs.Set("size.paperSize", "A4") 17 | //gs.Set("load.cookieJar", "myjar.jar") 18 | // object settings: http://www.cs.au.dk/~jakobt/libwkhtmltox_0.10.0_doc/pagesettings.html#pagePdfObject 19 | objset := wkhtmltopdf.NewObjectSettings() 20 | objset.Set("page", "http://www.slashdot.org") 21 | objset.Set("load.debugJavascript", "false") 22 | //os.Set("load.jsdelay", "1000") // wait max 1s 23 | objset.Set("web.enableJavascript", "false") 24 | objset.Set("web.enablePlugins", "false") 25 | objset.Set("web.loadImages", "true") 26 | objset.Set("web.background", "true") 27 | 28 | c := gs.NewConverter() 29 | c.Add(objset) 30 | //c.AddHtml(os, "

HELLO

World

") 31 | 32 | c.ProgressChanged = func(c *wkhtmltopdf.Converter, b int) { 33 | fmt.Printf("Progress: %d\n", b) 34 | } 35 | c.Error = func(c *wkhtmltopdf.Converter, msg string) { 36 | fmt.Printf("error: %s\n", msg) 37 | } 38 | c.Warning = func(c *wkhtmltopdf.Converter, msg string) { 39 | fmt.Printf("error: %s\n", msg) 40 | } 41 | c.Phase = func(c *wkhtmltopdf.Converter) { 42 | fmt.Printf("Phase\n") 43 | } 44 | c.Finished = func(c *wkhtmltopdf.Converter, s int) { 45 | fmt.Printf("Finished: %d\n", s) 46 | } 47 | c.Convert() 48 | 49 | payload, _ := c.Payload() 50 | 51 | ioutil.WriteFile("test.pdf", payload, 0644) 52 | 53 | fmt.Printf("Got error code: %d\n", c.ErrorCode()) 54 | } 55 | -------------------------------------------------------------------------------- /wkhtmltopdf/pdf_c_api.go: -------------------------------------------------------------------------------- 1 | package wkhtmltopdf 2 | 3 | //#cgo CFLAGS: -I/usr/local/include 4 | //#cgo LDFLAGS: -L/usr/local/lib -lwkhtmltox -Wall -ansi -pedantic -ggdb 5 | //#include 6 | //#include 7 | //#include 8 | //#include 9 | //#include 10 | //extern void finished_cb(void*, const int); 11 | //extern void progress_changed_cb(void*, const int); 12 | //extern void error_cb(void*, char *msg); 13 | //extern void warning_cb(void*, char *msg); 14 | //extern void phase_changed_cb(void*); 15 | //static void setup_callbacks(wkhtmltopdf_converter * c) { 16 | // wkhtmltopdf_set_finished_callback(c, (wkhtmltopdf_int_callback)finished_cb); 17 | // wkhtmltopdf_set_progress_changed_callback(c, (wkhtmltopdf_int_callback)progress_changed_cb); 18 | // wkhtmltopdf_set_error_callback(c, (wkhtmltopdf_str_callback)error_cb); 19 | // wkhtmltopdf_set_warning_callback(c, (wkhtmltopdf_str_callback)warning_cb); 20 | // wkhtmltopdf_set_phase_changed_callback(c, (wkhtmltopdf_void_callback)phase_changed_cb); 21 | //} 22 | import "C" 23 | 24 | import ( 25 | "fmt" 26 | "unsafe" 27 | "reflect" 28 | ) 29 | 30 | type GlobalSettings struct { 31 | s *C.wkhtmltopdf_global_settings 32 | } 33 | 34 | type ObjectSettings struct { 35 | s *C.wkhtmltopdf_object_settings 36 | } 37 | 38 | type Converter struct { 39 | c *C.wkhtmltopdf_converter 40 | Finished func(*Converter, int) 41 | ProgressChanged func(*Converter, int) 42 | Error func(*Converter, string) 43 | Warning func(*Converter, string) 44 | Phase func(*Converter) 45 | } 46 | 47 | var converter_map map[unsafe.Pointer]*Converter 48 | 49 | func init() { 50 | converter_map = map[unsafe.Pointer]*Converter{} 51 | C.wkhtmltopdf_init(C.false) 52 | } 53 | 54 | func NewGlobalSettings() *GlobalSettings { 55 | return &GlobalSettings{s: C.wkhtmltopdf_create_global_settings()} 56 | } 57 | 58 | // See https://wkhtmltopdf.org/libwkhtmltox/pagesettings.html#pagePdfGlobal for more setttings 59 | func (self *GlobalSettings) Set(name, value string) { 60 | c_name := C.CString(name) 61 | c_value := C.CString(value) 62 | defer C.free(unsafe.Pointer(c_name)) 63 | defer C.free(unsafe.Pointer(c_value)) 64 | C.wkhtmltopdf_set_global_setting(self.s, c_name, c_value) 65 | } 66 | 67 | func NewObjectSettings() *ObjectSettings { 68 | return &ObjectSettings{s: C.wkhtmltopdf_create_object_settings()} 69 | } 70 | 71 | func (self *ObjectSettings) Set(name, value string) { 72 | c_name := C.CString(name) 73 | c_value := C.CString(value) 74 | defer C.free(unsafe.Pointer(c_name)) 75 | defer C.free(unsafe.Pointer(c_value)) 76 | C.wkhtmltopdf_set_object_setting(self.s, c_name, c_value) 77 | } 78 | 79 | func (self *GlobalSettings) NewConverter() *Converter { 80 | c := &Converter{c: C.wkhtmltopdf_create_converter(self.s)} 81 | C.setup_callbacks(c.c) 82 | 83 | return c 84 | } 85 | 86 | //export finished_cb 87 | func finished_cb(c unsafe.Pointer, s C.int) { 88 | conv := converter_map[c] 89 | if conv.Finished != nil { 90 | conv.Finished(conv, int(s)) 91 | } 92 | } 93 | 94 | //export progress_changed_cb 95 | func progress_changed_cb(c unsafe.Pointer, p C.int) { 96 | conv := converter_map[c] 97 | if conv.ProgressChanged != nil { 98 | conv.ProgressChanged(conv, int(p)) 99 | } 100 | } 101 | 102 | //export error_cb 103 | func error_cb(c unsafe.Pointer, msg *C.char) { 104 | conv := converter_map[c] 105 | if conv.Error != nil { 106 | conv.Error(conv, C.GoString(msg)) 107 | } 108 | } 109 | 110 | //export warning_cb 111 | func warning_cb(c unsafe.Pointer, msg *C.char) { 112 | conv := converter_map[c] 113 | if conv.Warning != nil { 114 | conv.Warning(conv, C.GoString(msg)) 115 | } 116 | } 117 | 118 | //export phase_changed_cb 119 | func phase_changed_cb(c unsafe.Pointer) { 120 | conv := converter_map[c] 121 | if conv.Phase != nil { 122 | conv.Phase(conv) 123 | } 124 | } 125 | 126 | func (self *Converter) Convert() error { 127 | 128 | // To route callbacks right, we need to save a reference 129 | // to the converter object, base on the pointer. 130 | converter_map[unsafe.Pointer(self.c)] = self 131 | status := C.wkhtmltopdf_convert(self.c) 132 | delete(converter_map, unsafe.Pointer(self.c)) 133 | if status != C.int(1) { 134 | return fmt.Errorf("Convert failed (%d)", status) 135 | } 136 | return nil 137 | } 138 | 139 | func (self *Converter) Add(settings *ObjectSettings) { 140 | C.wkhtmltopdf_add_object(self.c, settings.s, nil) 141 | } 142 | 143 | func (self *Converter) AddHtml(settings *ObjectSettings, data string) { 144 | c_data := C.CString(data) 145 | defer C.free(unsafe.Pointer(c_data)) 146 | C.wkhtmltopdf_add_object(self.c, settings.s, c_data) 147 | } 148 | 149 | func (self *Converter) ErrorCode() int { 150 | return int(C.wkhtmltopdf_http_error_code(self.c)) 151 | } 152 | 153 | func (self *Converter) Destroy() { 154 | C.wkhtmltopdf_destroy_converter(self.c) 155 | } 156 | 157 | func (self *Converter) Payload() ([]byte, int) { 158 | var payloadptr *C.uchar 159 | length := int(C.wkhtmltopdf_get_output(self.c, &payloadptr)) 160 | var payload []byte 161 | header := (*reflect.SliceHeader)(unsafe.Pointer(&payload)) 162 | 163 | header.Len = length 164 | 165 | header.Cap = length 166 | 167 | header.Data = uintptr(unsafe.Pointer(payloadptr)) 168 | return payload, length 169 | } 170 | --------------------------------------------------------------------------------