├── .gitignore ├── .travis.yml ├── Dockerfile ├── LICENSE ├── README.md ├── libreofficekit.go ├── libreofficekit_test.go ├── lokbridge.h └── testdata └── sample.docx /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.test 24 | *.prof 25 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | services: 3 | - docker 4 | before_install: 5 | - sudo docker build -t go-libreofficekit . 6 | script: 7 | - sudo docker run go-libreofficekit bash test.sh 8 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:16.04 2 | MAINTAINER Dmitry Veselov 3 | 4 | RUN apt-get update 5 | RUN apt-get install -y software-properties-common 6 | RUN add-apt-repository -y ppa:libreoffice/ppa 7 | RUN apt-get update 8 | RUN apt-get install -y golang git curl 9 | RUN apt-get install -y libreoffice libreofficekit-dev 10 | 11 | ENV GOPATH /go 12 | 13 | RUN mkdir /go 14 | ADD . /go/src/github.com/docsbox/go-libreofficekit 15 | WORKDIR /go/src/github.com/docsbox/go-libreofficekit 16 | RUN echo "go test -race -coverprofile=coverage.txt -covermode=atomic && bash <(curl -s https://codecov.io/bash) -t 473da5a7-66ec-45dc-b4ed-eb758ce8a66b" > test.sh 17 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 docsbox 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-libreofficekit [![Build Status](https://travis-ci.org/dveselov/go-libreofficekit.svg?branch=master)](https://travis-ci.org/dveselov/go-libreofficekit) [![](https://godoc.org/github.com/docsbox/go-libreofficekit?status.svg)](https://godoc.org/github.com/docsbox/go-libreofficekit) [![Go Report Card](https://goreportcard.com/badge/github.com/dveselov/go-libreofficekit)](https://goreportcard.com/report/github.com/dveselov/go-libreofficekit) [![codecov](https://codecov.io/gh/dveselov/go-libreofficekit/branch/master/graph/badge.svg)](https://codecov.io/gh/dveselov/go-libreofficekit) 2 | 3 | CGo bindings to [LibreOfficeKit](https://docs.libreoffice.org/libreofficekit.html) 4 | 5 | # Install 6 | ```bash 7 | # Latest version of LibreOffice (5.2) is required 8 | $ sudo add-apt-repository ppa:libreoffice/ppa 9 | $ sudo apt-get update 10 | $ sudo apt-get install libreoffice libreofficekit-dev 11 | $ go get github.com/docsbox/go-libreofficekit 12 | ``` 13 | 14 | # Usage 15 | 16 | This example demonstrates how to convert Microsoft Office document to PDF 17 | 18 | ```go 19 | package main 20 | 21 | import "github.com/dveselov/go-libreofficekit" 22 | 23 | func main() { 24 | office, _ := libreofficekit.NewOffice("/path/to/libreoffice") 25 | 26 | document, _ := office.LoadDocument("kittens.docx") 27 | document.SaveAs("kittens.pdf", "pdf", "skipImages") 28 | 29 | document.Close() 30 | office.Close() 31 | } 32 | 33 | ``` 34 | 35 | This example demonstrates how to get presentation slides titles 36 | 37 | ```go 38 | package main 39 | 40 | import "fmt" 41 | import "github.com/dveselov/go-libreofficekit" 42 | 43 | func main() { 44 | office, _ := libreofficekit.NewOffice("/path/to/libreoffice") 45 | 46 | document, _ := office.LoadDocument("kittens.pptx") 47 | slidesCount := document.GetParts() 48 | 49 | for i := 1; i < slidesCount; i++ { 50 | document.SetPart(i) 51 | currentPart = document.GetPart() 52 | fmt.Println("Current slide =", currentPart) 53 | currentPartName = document.GetPartName(i) 54 | fmt.Println("Current slide title =", currentPartName) 55 | } 56 | 57 | document.Close() 58 | office.Close() 59 | } 60 | ``` 61 | 62 | Next example demonstrates how to use built-in LibreOffice rendering engine for creating page-by-page documents previews. 63 | 64 | ```go 65 | package main 66 | 67 | import ( 68 | "os" 69 | "fmt" 70 | "unsafe" 71 | "image" 72 | "image/png" 73 | ) 74 | import "github.com/dveselov/go-libreofficekit" 75 | 76 | func main() { 77 | office, _ := libreofficekit.NewOffice("/path/to/libreoffice") 78 | document, _ := office.LoadDocument("kittens.docx") 79 | 80 | rectangles := document.GetPartPageRectangles() 81 | canvasWidth := libreofficekit.TwipsToPixels(rectangles[0].Dx(), 120) 82 | canvasHeight := libreofficekit.TwipsToPixels(rectangles[0].Dy(), 120) 83 | 84 | m := image.NewRGBA(image.Rect(0, 0, canvasWidth, canvasHeight)) 85 | 86 | for i, rectangle := range rectangles { 87 | document.PaintTile(unsafe.Pointer(&m.Pix[0]), canvasWidth, canvasHeight, rectangle.Min.X, rectangle.Min.Y, rectangle.Dx(), rectangle.Dy()) 88 | libreofficekit.BGRA(m.Pix) 89 | out, _ := os.Create(fmt.Sprintf("page_%v.png", i)) 90 | png.Encode(out, m) 91 | out.Close() 92 | } 93 | } 94 | ``` 95 | -------------------------------------------------------------------------------- /libreofficekit.go: -------------------------------------------------------------------------------- 1 | package libreofficekit 2 | 3 | /* 4 | #cgo CFLAGS: -I ./ -D LOK_USE_UNSTABLE_API 5 | #cgo LDFLAGS: -ldl 6 | #include 7 | extern void libre_init(); 8 | */ 9 | import "C" 10 | import ( 11 | "fmt" 12 | "image" 13 | "strconv" 14 | "strings" 15 | "sync" 16 | "unsafe" 17 | ) 18 | 19 | func init() { 20 | C.libre_init() 21 | } 22 | 23 | // TwipsToPixels converts given twips to pixels with given dpi 24 | func TwipsToPixels(twips int, dpi int) int { 25 | return int(float32(twips) / 1440.0 * float32(dpi)) 26 | } 27 | 28 | // PixelsToTwips is like TwipsToPixels, but to another way 29 | func PixelsToTwips(pixels int, dpi int) int { 30 | return int((float32(pixels) / float32(dpi)) * 1440.0) 31 | } 32 | 33 | // BGRA converts BGRA array of pixels to RGBA 34 | // https://github.com/golang/exp/blob/master/shiny/driver/internal/swizzle/swizzle_common.go#L13 35 | func BGRA(p []uint8) { 36 | for i := 0; i < len(p); i += 4 { 37 | p[i+0], p[i+2] = p[i+2], p[i+0] 38 | } 39 | } 40 | 41 | type Office struct { 42 | handle *C.struct__LibreOfficeKit 43 | Mutex *sync.Mutex 44 | } 45 | 46 | // NewOffice returns new Office or error if LibreOfficeKit fails to load 47 | // required libs (actually, when libreofficekit-dev package isn't installed or path is invalid) 48 | func NewOffice(path string) (*Office, error) { 49 | office := new(Office) 50 | 51 | cPath := C.CString(path) 52 | defer C.free(unsafe.Pointer(cPath)) 53 | 54 | lokit := C.lok_init(cPath) 55 | if lokit == nil { 56 | return nil, fmt.Errorf("failed to initialize LibreOfficeKit with path: '%s'", path) 57 | } 58 | 59 | office.handle = lokit 60 | office.Mutex = &sync.Mutex{} 61 | 62 | return office, nil 63 | 64 | } 65 | 66 | // Close destroys C LibreOfficeKit instance 67 | func (office *Office) Close() { 68 | C.destroy_office(office.handle) 69 | } 70 | 71 | // GetError returns last happened error message in human-readable format 72 | func (office *Office) GetError() string { 73 | message := C.get_error(office.handle) 74 | return C.GoString(message) 75 | } 76 | 77 | // LoadDocument return Document or error, if LibreOffice fails to open document at provided path. 78 | // Actual error message can be retrieved by office.GetError method 79 | func (office *Office) LoadDocument(path string) (*Document, error) { 80 | document := new(Document) 81 | cPath := C.CString(path) 82 | defer C.free(unsafe.Pointer(cPath)) 83 | handle := C.document_load(office.handle, cPath) 84 | if handle == nil { 85 | return nil, fmt.Errorf("failed to load document") 86 | } 87 | document.handle = handle 88 | return document, nil 89 | } 90 | 91 | func (office *Office) GetFilters() string { 92 | filters := C.get_filter_types(office.handle) 93 | defer C.free(unsafe.Pointer(filters)) 94 | return C.GoString(filters) 95 | } 96 | 97 | // Types of documents returned by Document.GetType function 98 | const ( 99 | TextDocument = iota 100 | SpreadsheetDocument 101 | PresentationDocument 102 | DrawingDocument 103 | OtherDocument 104 | ) 105 | 106 | // Types of tile color mode 107 | const ( 108 | RGBATilemode = iota 109 | BGRATilemode 110 | ) 111 | 112 | const ( 113 | SetGraphicSelectionStart = iota 114 | SetGraphicSelectionEnd 115 | ) 116 | 117 | type Document struct { 118 | handle *C.struct__LibreOfficeKitDocument 119 | } 120 | 121 | // Close destroys document 122 | func (document *Document) Close() { 123 | C.destroy_document(document.handle) 124 | } 125 | 126 | // GetType returns type of loaded document 127 | func (document *Document) GetType() int { 128 | return int(C.get_document_type(document.handle)) 129 | } 130 | 131 | // GetParts returns count of slides (for presentations) or pages (for text documents) 132 | func (document *Document) GetParts() int { 133 | return int(C.get_document_parts(document.handle)) 134 | } 135 | 136 | // GetPart returns current part of document, e.g. 137 | // if document was just loaded it's current part will be 0 138 | func (document *Document) GetPart() int { 139 | return int(C.get_document_part(document.handle)) 140 | } 141 | 142 | // SetPart updates current part of document 143 | func (document *Document) SetPart(part int) { 144 | C.set_document_part(document.handle, C.int(part)) 145 | } 146 | 147 | // GetPartName returns current slide title (for presentations) or page title (for text documents) 148 | func (document *Document) GetPartName(part int) string { 149 | cPart := C.int(part) 150 | cPartName := C.get_document_part_name(document.handle, cPart) 151 | defer C.free(unsafe.Pointer(cPartName)) 152 | return C.GoString(cPartName) 153 | } 154 | 155 | // GetSize returns width and height of document in twips (1 Twip = 1/1440th of an inch) 156 | // You can convert twips to pixels by this formula: (width or height) * (1.0 / 1440.0) * DPI 157 | func (document *Document) GetSize() (int, int) { 158 | width := C.long(0) 159 | heigth := C.long(0) 160 | C.get_document_size(document.handle, &width, &heigth) 161 | return int(width), int(heigth) 162 | } 163 | 164 | // InitializeForRendering must be called before performing any rendering-related actions 165 | func (document *Document) InitializeForRendering(arguments string) { 166 | cArguments := C.CString(arguments) 167 | defer C.free(unsafe.Pointer(cArguments)) 168 | C.initialize_for_rendering(document.handle, cArguments) 169 | } 170 | 171 | // SaveAs saves document at desired path in desired format with applied filter rules 172 | // Actual (from libreoffice) error message can be read with Office.GetError 173 | func (document *Document) SaveAs(path string, format string, filter string) error { 174 | cPath := C.CString(path) 175 | defer C.free(unsafe.Pointer(cPath)) 176 | cFormat := C.CString(format) 177 | defer C.free(unsafe.Pointer(cFormat)) 178 | cFilter := C.CString(filter) 179 | defer C.free(unsafe.Pointer(cFilter)) 180 | status := C.document_save(document.handle, cPath, cFormat, cFilter) 181 | if status != 1 { 182 | return fmt.Errorf("failed to save document") 183 | } 184 | return nil 185 | } 186 | 187 | // CreateView return id if newly created view 188 | func (document *Document) CreateView() int { 189 | return int(C.create_view(document.handle)) 190 | } 191 | 192 | // GetView returns current document view id 193 | func (document *Document) GetView() int { 194 | return int(C.get_view(document.handle)) 195 | } 196 | 197 | // GetViewsCount returns total number of views in document 198 | func (document *Document) GetViewsCount() int { 199 | return int(C.get_views_count(document.handle)) 200 | } 201 | 202 | // GetTileMode returns tile mode of document, currently only RGBA or BGRA (5.2). 203 | // You can compare returned int with RGBATilemode / BGRATilemode. 204 | func (document *Document) GetTileMode() int { 205 | return int(C.get_tile_mode(document.handle)) 206 | } 207 | 208 | func (document *Document) GetTextSelection(mimetype string) string { 209 | cMimetype := C.CString(mimetype) 210 | defer C.free(unsafe.Pointer(cMimetype)) 211 | return C.GoString(C.get_text_selection(document.handle, cMimetype)) 212 | } 213 | 214 | func (document *Document) SetTextSelection(sType int, x int, y int) { 215 | C.set_text_selection( 216 | document.handle, 217 | C.int(sType), 218 | C.int(x), 219 | C.int(y), 220 | ) 221 | } 222 | 223 | func (document *Document) ResetTextSelection() { 224 | C.reset_selection(document.handle) 225 | } 226 | 227 | // GetPartPageRectangles array of image.Rectangle, with actually TextDocument page 228 | // rectangles. Useful, when rendering text document page-by-page. 229 | func (document *Document) GetPartPageRectangles() []image.Rectangle { 230 | var rectangles []image.Rectangle 231 | rawRectangles := C.GoString(C.get_part_page_rectangles(document.handle)) 232 | pageRectangles := strings.Split(rawRectangles, ";") 233 | for _, points := range pageRectangles { 234 | var intPoints []int 235 | strPoints := strings.Split(points, ",") 236 | for _, point := range strPoints { 237 | point = strings.Trim(point, " ") 238 | i, _ := strconv.Atoi(point) 239 | intPoints = append(intPoints, i) 240 | } 241 | x0, y0 := intPoints[0], intPoints[1] 242 | x1, y1 := x0+intPoints[2], y0+intPoints[3] 243 | rectangles = append(rectangles, image.Rect(x0, y0, x1, y1)) 244 | } 245 | return rectangles 246 | } 247 | 248 | // PaintTile renders tile to given buf (which size must be a `4 * canvasWidth * canvasHeight`). 249 | // In practice buf must be a pointer to image.Image.Pix array's first element, e.g. unsafe.Pointer(&image.Pix[0]) 250 | func (document *Document) PaintTile(buf unsafe.Pointer, canvasWidth int, canvasHeight int, tilePosX int, tilePosY int, tileWidth int, tileHeight int) { 251 | C.paint_tile( 252 | document.handle, 253 | (*C.uchar)(buf), 254 | (C.int)(canvasWidth), 255 | (C.int)(canvasHeight), 256 | (C.int)(tilePosX), 257 | (C.int)(tilePosY), 258 | (C.int)(tileWidth), 259 | (C.int)(tileHeight), 260 | ) 261 | } 262 | -------------------------------------------------------------------------------- /libreofficekit_test.go: -------------------------------------------------------------------------------- 1 | package libreofficekit 2 | 3 | import ( 4 | "testing" 5 | "os" 6 | ) 7 | 8 | const ( 9 | DefaultLibreOfficePath = "/usr/lib/libreoffice/program/" 10 | DocumentThatDoesntExist = "testdata/kittens.docx" 11 | SampleDocument = "testdata/sample.docx" 12 | SaveDocumentPath = "/tmp/out.docx" 13 | SaveDocumentFormat = "docx" 14 | ) 15 | 16 | func TestInvalidOfficePath(t *testing.T) { 17 | _, err := NewOffice("/etc/passwd") 18 | if err == nil { 19 | t.Fail() 20 | } 21 | } 22 | 23 | func TestValidOfficePath(t *testing.T) { 24 | _, err := NewOffice(DefaultLibreOfficePath) 25 | if err != nil { 26 | t.Fail() 27 | } 28 | } 29 | 30 | func TestGetOfficeErrorMessage(t *testing.T) { 31 | office, _ := NewOffice(DefaultLibreOfficePath) 32 | office.LoadDocument(DocumentThatDoesntExist) 33 | message := office.GetError() 34 | if len(message) == 0 { 35 | t.Fail() 36 | } 37 | } 38 | 39 | func TestLoadDocumentThatDoesntExist(t *testing.T) { 40 | office, _ := NewOffice(DefaultLibreOfficePath) 41 | _, err := office.LoadDocument(DocumentThatDoesntExist) 42 | if err == nil { 43 | t.Fail() 44 | } 45 | } 46 | 47 | func TestSuccessLoadDocument(t *testing.T) { 48 | office, _ := NewOffice(DefaultLibreOfficePath) 49 | _, err := office.LoadDocument(SampleDocument) 50 | if err != nil { 51 | t.Fail() 52 | } 53 | } 54 | 55 | func TestSuccessfulLoadAndSaveDocument(t *testing.T) { 56 | office, _ := NewOffice(DefaultLibreOfficePath) 57 | doc, err := office.LoadDocument(SampleDocument) 58 | if err != nil { 59 | t.Fail() 60 | } 61 | 62 | err = doc.SaveAs(SaveDocumentPath, SaveDocumentFormat, "") 63 | if err != nil { 64 | t.Fail() 65 | } 66 | 67 | defer os.Remove(SaveDocumentPath) 68 | } 69 | 70 | func TestGetPartPageRectangles(t *testing.T) { 71 | office, _ := NewOffice(DefaultLibreOfficePath) 72 | document, _ := office.LoadDocument(SampleDocument) 73 | rectangles := document.GetPartPageRectangles() 74 | if len(rectangles) != 2 { 75 | t.Fail() 76 | } 77 | } 78 | 79 | func TestGetParts(t *testing.T) { 80 | office, _ := NewOffice(DefaultLibreOfficePath) 81 | document, _ := office.LoadDocument(SampleDocument) 82 | parts := document.GetParts() 83 | if parts != 2 { 84 | t.Fail() 85 | } 86 | } 87 | 88 | func TestGetTileMode(t *testing.T) { 89 | office, _ := NewOffice(DefaultLibreOfficePath) 90 | document, _ := office.LoadDocument(SampleDocument) 91 | mode := document.GetTileMode() 92 | if mode != RGBATilemode && mode != BGRATilemode { 93 | t.Fail() 94 | } 95 | } 96 | 97 | func TestGetType(t *testing.T) { 98 | office, _ := NewOffice(DefaultLibreOfficePath) 99 | document, _ := office.LoadDocument(SampleDocument) 100 | documentType := document.GetType() 101 | if documentType != TextDocument { 102 | t.Fail() 103 | } 104 | } 105 | 106 | func TestTextSelection(t *testing.T) { 107 | office, _ := NewOffice(DefaultLibreOfficePath) 108 | document, _ := office.LoadDocument(SampleDocument) 109 | rectangle := document.GetPartPageRectangles()[0] 110 | document.SetTextSelection(SetGraphicSelectionStart, rectangle.Min.X, rectangle.Min.Y) 111 | document.SetTextSelection(SetGraphicSelectionEnd, rectangle.Max.X, rectangle.Max.Y) 112 | plaintext := document.GetTextSelection("text/plain;charset=utf-8") 113 | if len(plaintext) < 1000 { 114 | t.Fail() 115 | } 116 | document.ResetTextSelection() 117 | plaintext = document.GetTextSelection("text/plain;charset=utf-8") 118 | if len(plaintext) != 0 { 119 | t.Fail() 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /lokbridge.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "LibreOfficeKit/LibreOfficeKitInit.h" 7 | #include "LibreOfficeKit/LibreOfficeKit.h" 8 | 9 | 10 | void destroy_office(LibreOfficeKit* pThis) { 11 | return pThis->pClass->destroy(pThis); 12 | }; 13 | 14 | char* get_error(LibreOfficeKit* pThis) { 15 | return pThis->pClass->getError(pThis); 16 | }; 17 | 18 | void free_error(LibreOfficeKit* pThis, char* message) { 19 | return pThis->pClass->freeError(message); 20 | }; 21 | 22 | char* get_filter_types(LibreOfficeKit* pThis) { 23 | return pThis->pClass->getFilterTypes(pThis); 24 | }; 25 | 26 | LibreOfficeKitDocument* document_load(LibreOfficeKit* pThis, const char* pURL) { 27 | return pThis->pClass->documentLoad(pThis, pURL); 28 | }; 29 | 30 | void destroy_document(LibreOfficeKitDocument* pThis) { 31 | return pThis->pClass->destroy(pThis); 32 | }; 33 | 34 | void get_document_size(LibreOfficeKitDocument* pThis, long* pWidth, long* pHeight) { 35 | return pThis->pClass->getDocumentSize(pThis, pWidth, pHeight); 36 | }; 37 | void set_document_part(LibreOfficeKitDocument* pThis, int nPart) { 38 | return pThis->pClass->setPart(pThis, nPart); 39 | }; 40 | 41 | int get_document_type(LibreOfficeKitDocument* pThis) { 42 | return pThis->pClass->getDocumentType(pThis); 43 | }; 44 | 45 | int get_document_parts(LibreOfficeKitDocument* pThis) { 46 | return pThis->pClass->getParts(pThis); 47 | }; 48 | 49 | int get_document_part(LibreOfficeKitDocument* pThis) { 50 | return pThis->pClass->getPart(pThis); 51 | }; 52 | 53 | char* get_document_part_name(LibreOfficeKitDocument* pThis, int nPart) { 54 | return pThis->pClass->getPartName(pThis, nPart); 55 | }; 56 | 57 | void initialize_for_rendering(LibreOfficeKitDocument* pThis, const char* pArguments) { 58 | return pThis->pClass->initializeForRendering(pThis, pArguments); 59 | }; 60 | 61 | int document_save(LibreOfficeKitDocument* pThis, const char* pUrl, const char* pFormat, const char* pFilterOptions) { 62 | return pThis->pClass->saveAs(pThis, pUrl, pFormat, pFilterOptions); 63 | }; 64 | 65 | int create_view(LibreOfficeKitDocument* pThis) { 66 | return pThis->pClass->createView(pThis); 67 | }; 68 | 69 | int get_view(LibreOfficeKitDocument* pThis) { 70 | return pThis->pClass->getView(pThis); 71 | }; 72 | 73 | int get_views_count(LibreOfficeKitDocument* pThis) { 74 | return pThis->pClass->getViewsCount(pThis); 75 | }; 76 | 77 | void paint_tile(LibreOfficeKitDocument* pThis, unsigned char* pBuffer, const int nCanvasWidth, const int nCanvasHeight, const int nTilePosX, const int nTilePosY, const int nTileWidth, const int nTileHeight) { 78 | return pThis->pClass->paintTile(pThis, pBuffer, nCanvasWidth, nCanvasHeight, nTilePosX, nTilePosY, nTileWidth, nTileHeight); 79 | }; 80 | 81 | int get_tile_mode(LibreOfficeKitDocument* pThis) { 82 | return pThis->pClass->getTileMode(pThis); 83 | }; 84 | 85 | char* get_part_page_rectangles(LibreOfficeKitDocument* pThis) { 86 | return pThis->pClass->getPartPageRectangles(pThis); 87 | }; 88 | 89 | void set_text_selection(LibreOfficeKitDocument* pThis, int nType, int nX, int nY) { 90 | return pThis->pClass->setTextSelection(pThis, nType, nX, nY); 91 | }; 92 | 93 | void reset_selection(LibreOfficeKitDocument* pThis) { 94 | return pThis->pClass->resetSelection(pThis); 95 | }; 96 | 97 | char* get_text_selection(LibreOfficeKitDocument* pThis, const char* pMimeType) { 98 | return pThis->pClass->getTextSelection(pThis, pMimeType, NULL); 99 | }; 100 | 101 | static void libre_fix_signal(int signum) 102 | { 103 | struct sigaction st; 104 | 105 | if (sigaction(signum, NULL, &st) < 0) { 106 | goto fix_signal_error; 107 | } 108 | 109 | st.sa_flags |= SA_ONSTACK; 110 | 111 | if (sigaction(signum, &st, NULL) < 0) { 112 | goto fix_signal_error; 113 | } 114 | return; 115 | fix_signal_error: 116 | fprintf(stderr, "error fixing handler for signal %d", signum); 117 | } 118 | 119 | void libre_init() 120 | { 121 | signal(SIGURG, libre_fix_signal); 122 | } 123 | -------------------------------------------------------------------------------- /testdata/sample.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dveselov/go-libreofficekit/96cbcdcf00f426aaf5f13d36f1133318f0a9a0d0/testdata/sample.docx --------------------------------------------------------------------------------