Closed yveszoundi closed 2 years ago
Is it possible to provide an example for the registerCallback function, for example providing a document password?
I'm not sure how to implement this too.
according to callback type definition I guess you should implement a Rust Function which used keyword extern "C"
and then pass it as a raw pointer to registerCallback
I think that it could work by implementing the "setDocumentPassword" LibreOfficeKit method: https://github.com/LibreOffice/core/blob/master/include/LibreOfficeKit/LibreOfficeKit.hxx#L993. I tried a naive implementation based on existing libreoffice-rs code, but it crashed with "Application Error".
I can add setDocumentPassword
in later release, for now your can get_error()
to see the actual error which causes LibreOffice crash.
Thanks, I'll keep trying out couple of things and learn more about ffi in general.
Indeed for the your point about registerCallback
, I think that some kind of shim with the extern "C"
needs implementation similarly to how fltk-rs interacts with some C libraries: https://github.com/fltk-rs/fltk-rs/blob/e1cc963680ca729b4b293f1e363f879b791c0cea/fltk/src/menu.rs#L662
I think that the LibreOfficeKitCallback is a typedef with 3 params, I'll keep investigating the magic that needs to happen (c_void stuff, possibly "transmutation", etc.)
I don't get to capture details about the error itself, as LibreOfficeKit crashes prior that. Environment: Void Linux rolling release amd64, cargo 1.60.0, rustc 1.60.0, libreoffice-kit 7.3.3, libreoffice 7.3.3, gcc 10.2.1, clang 12.0.1
pub fn set_document_password(&mut self, url: &str, password: &str) -> Result<(), Error> {
let c_url = CString::new(url).unwrap();
let c_password = CString::new(password).unwrap();
unsafe {
// The crash seems to happen here
(*self.lok_clz).setDocumentPassword.unwrap()(self.lok, c_url.as_ptr(), c_password.as_ptr());
let error = self.get_error();
if error != "" {
return Err(Error::new(error));
}
}
Ok(())
}
I think that some kind of shim with the extern "C" needs implementation similarly to how fltk-rs interacts with some C libraries: https://github.com/fltk-rs/fltk-rs/blob/e1cc963680ca729b4b293f1e363f879b791c0cea/fltk/src/menu.rs#L662
yes, this link sets a good example of callback-based C FFI .
I don't get to capture details about the error itself, as LibreOfficeKit crashes prior that.
I have tried this myself:
Unsupported URL
.The only option left open to us is try register callback.
I made some "relative" progress based on the documentation that you added for set_document_password
and random experiments. My current understanding is that the following needs to happen:
Apparently the password functionality requires some optional features enabled:
I didn't bother yet creating an enum matching relevant LibreOfficeKitEnums
values.
pub fn set_optional_features(&mut self, feature_flags: u64) -> Result<(), Error> {
unsafe {
let doc = (*self.lok_clz).setOptionalFeatures.unwrap()(self.lok, feature_flags);
let error = self.get_error();
if error != "" {
return Err(Error::new(error));
}
Ok(())
}
}
This is based on the fltk-rs
example and I'm able to receive a callback, not sure if I got it all correctly though.
pub fn register_callback<F: FnMut(i32, String) + 'static> (&mut self, cb: F) -> Result<(), Error> {
unsafe {
//LibreOfficeKitCallback typedef (int nType, const char* pPayload, void* pData);
unsafe extern "C" fn shim(n_type: i32, payload: *const std::os::raw::c_char, data: *mut std::os::raw::c_void) {
let a: *mut Box<dyn FnMut()> = data as *mut Box<dyn FnMut()>;
let f: &mut (dyn FnMut()) = &mut **a;
let _ = std::panic::catch_unwind(std::panic::AssertUnwindSafe(f));
}
let a: *mut Box<dyn FnMut(i32, String) > = Box::into_raw(Box::new(Box::new(cb)));
let data: *mut std::os::raw::c_void = a as *mut std::ffi::c_void;
let callback: LibreOfficeKitCallback = Some(shim);
(*self.lok_clz).registerCallback.unwrap()(self.lok, callback, data);
let error = self.get_error();
if error != "" {
return Err(Error::new(error));
}
}
Ok(())
}
It seems that I'm able to successfully invoke set_document_password
, but I then get an munmap_chunk(): invalid pointer error
. I suspect that it's due to uninitialized data (cloning the office instance) or missing logic in my register_callback
implementation...
use libreoffice_rs::Office;
use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> {
let url = "/home/vip/Tests/test.odt";
let password = "test";
let mut office = Office::new("/usr/lib/libreoffice/program")?;
office.set_optional_features(1)?;
office.register_callback({
// Derived clone to use the Office object instance inside the closure
let mut office = office.clone();
move |ntype, payload| {
println!("ntype: {:?}, payload: {:?}", ntype, payload);
// apparently libreofficekit wants a file URI and no a regular file path
let ret = office.set_document_password("file:///home/vip/Tests/test.odt", password);
println!("We are able to call set_document_password...: {:?}", ret);
}
})?;
let mut doc = office.document_load(url)?;
doc.save_as("/home/vip/Tests/doctest.pdf", "pdf", None);
Ok(())
}
No language allowlisted, turning off the language support.
ntype: -243246672, payload: ""PH�<$��"
We are able to call set_document_password...: Ok(())
munmap_chunk(): invalid pointer
Unspecified Application Error
@undeflife , I figured it out and I'm preparing a pull request for the initial implementation.
Great work!
By the way if you like create enums from LibreOfficeKitEnums
can just simply add #include "LibreOfficeKit/LibreOfficeKitEnums.h"
to wrapper.h
, bindgen will do the rest.
Notes about the above pull-request
LibreOfficeKitEnums
to generate automatically, I supposed that it's related to if defined
"macros" and how they need to be handled with bindgen
.
Is it possible to provide an example for the
registerCallback
function, for example providing a document password?If it's possible to leverage callbacks for providing passwords, then I believe that it's a very flexible option.
Possible alternative
I think that it could work by implementing the "setDocumentPassword" LibreOfficeKit method: https://github.com/LibreOffice/core/blob/master/include/LibreOfficeKit/LibreOfficeKit.hxx#L993. I tried a naive implementation based on existing
libreoffice-rs
code, but it crashed with "Application Error".My use-case
My end goal is to replace direct command-line calls against the LibreOffice binary in my application, as I can't easily deal with password protected documents.
libreoffice-rs
appears to be the most viable option in comparison to "unoconv*" or immature free libraries.