use libc::{self, c_char, c_float, size_t};
use std::ffi::{CStr, CString};
use std::ptr;
mod compiler;
mod document;
use crate::compiler::compile;
use crate::document::Document;
pub struct Context {
doc: Result<Document, String>, out: *const u8, len: usize, err: *const c_char, }
impl Drop for Context {
fn drop(&mut self) {
unsafe {
libc::free(self.out as *mut libc::c_void);
libc::free(self.err as *mut libc::c_void);
}
}
}
const VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), "\0");
#[unsafe(no_mangle)]
pub unsafe extern "C" fn typst_cffi_version() -> *const c_char {
VERSION.as_ptr() as *const c_char
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn typst_free(ptr: *mut Context) {
if ptr.is_null() {
return;
}
unsafe {
drop(Box::from_raw(ptr));
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn typst_get_err(ptr: *const Context) -> *const c_char {
if ptr.is_null() {
return ptr::null();
}
let ctx = unsafe { &*ptr };
ctx.err
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn typst_get_buf(ptr: *const Context, len: *mut size_t) -> *const u8 {
if ptr.is_null() {
unsafe {
*len = 0;
}
return ptr::null();
}
let ctx = unsafe { &*ptr };
unsafe {
*len = ctx.len;
}
ctx.out
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn typst_get_npages(ptr: *const Context) -> size_t {
if ptr.is_null() {
return 0;
}
let ctx = unsafe { &*ptr };
match &ctx.doc {
Err(_) => 0,
Ok(doc) => doc.npages(),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn typst_compile(src: *const c_char) -> *mut Context {
if src.is_null() {
return Box::into_raw(Box::new(Context {
doc: Err("no document".to_string()),
out: ptr::null(),
len: 0,
err: CString::new("NULL pointer to document".to_string())
.unwrap()
.into_raw(),
}));
}
let c_str = unsafe { CStr::from_ptr(src) };
let v_str = match c_str.to_str() {
Ok(s) => s,
Err(err) => {
return Box::into_raw(Box::new(Context {
doc: Err(err.to_string()),
out: ptr::null(),
len: 0,
err: CString::new(err.to_string()).unwrap().into_raw(),
}));
}
};
match compile(v_str.to_string()) {
Err(err) => Box::into_raw(Box::new(Context {
doc: Err(err.clone()),
out: ptr::null(),
len: 0,
err: CString::new(err.to_string()).unwrap().into_raw(),
})),
Ok(doc) => Box::into_raw(Box::new(Context {
doc: Ok(doc),
out: ptr::null(),
len: 0,
err: ptr::null(),
})),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn typst_compile_pdf(ptr: *mut Context) {
if ptr.is_null() {
return;
}
let ctx = unsafe { &mut *ptr };
unsafe {
libc::free(ctx.out as *mut libc::c_void);
libc::free(ctx.err as *mut libc::c_void);
}
match &ctx.doc {
Err(err) => {
ctx.err = CString::new(err.to_string()).unwrap().into_raw();
}
Ok(doc) => match doc.compile_pdf() {
Ok(buf) => {
let len = buf.len();
let ptr = unsafe { libc::malloc(len) as *mut u8 };
if !ptr.is_null() {
unsafe {
ptr.copy_from_nonoverlapping(buf.as_ptr(), len);
}
}
ctx.len = len;
ctx.out = ptr;
}
Err(err) => {
ctx.err = CString::new(err.to_string()).unwrap().into_raw();
}
},
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn typst_compile_png(ptr: *mut Context, page: size_t, ppi: c_float) {
if ptr.is_null() {
return;
}
let ctx = unsafe { &mut *ptr };
unsafe {
libc::free(ctx.out as *mut libc::c_void);
libc::free(ctx.err as *mut libc::c_void);
}
match &ctx.doc {
Err(err) => {
ctx.err = CString::new(err.to_string()).unwrap().into_raw();
}
Ok(doc) => match doc.compile_png(page, ppi) {
Ok(buf) => {
let len = buf.len();
let ptr = unsafe { libc::malloc(len) as *mut u8 };
if !ptr.is_null() {
unsafe {
ptr.copy_from_nonoverlapping(buf.as_ptr(), len);
}
}
ctx.len = len;
ctx.out = ptr;
}
Err(err) => {
ctx.err = CString::new(err.to_string()).unwrap().into_raw();
}
},
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn typst_compile_svg(ptr: *mut Context) {
if ptr.is_null() {
return;
}
let ctx = unsafe { &mut *ptr };
unsafe {
libc::free(ctx.out as *mut libc::c_void);
libc::free(ctx.err as *mut libc::c_void);
}
match &ctx.doc {
Err(err) => {
ctx.err = CString::new(err.to_string()).unwrap().into_raw();
}
Ok(doc) => match doc.compile_svg() {
Ok(buf) => {
let len = buf.len();
let ptr = unsafe { libc::malloc(len) as *mut u8 };
if !ptr.is_null() {
unsafe {
ptr.copy_from_nonoverlapping(buf.as_ptr(), len);
}
}
ctx.len = len;
ctx.out = ptr;
}
Err(err) => {
ctx.err = CString::new(err.to_string()).unwrap().into_raw();
}
},
}
}