1use libc::{self, c_char, c_float, size_t};
6
7use std::ffi::{CStr, CString};
8use std::ptr;
9
10mod compiler;
11mod document;
12
13use crate::compiler::compile;
14use crate::document::Document;
15
16pub struct Context {
17 doc: Result<Document, String>, out: *const u8, len: usize, err: *const c_char, }
22
23impl Drop for Context {
24 fn drop(&mut self) {
25 unsafe {
26 libc::free(self.out as *mut libc::c_void);
27 libc::free(self.err as *mut libc::c_void);
28 }
29 }
30}
31
32const VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), "\0");
33
34#[unsafe(no_mangle)]
39pub unsafe extern "C" fn typst_cffi_version() -> *const c_char {
40 VERSION.as_ptr() as *const c_char
41}
42
43#[unsafe(no_mangle)]
47pub unsafe extern "C" fn typst_free(ptr: *mut Context) {
48 if ptr.is_null() {
49 return;
50 }
51 unsafe {
52 drop(Box::from_raw(ptr));
53 }
54}
55
56#[unsafe(no_mangle)]
61pub unsafe extern "C" fn typst_get_err(ptr: *const Context) -> *const c_char {
62 if ptr.is_null() {
63 return ptr::null();
64 }
65
66 let ctx = unsafe { &*ptr };
67 ctx.err
68}
69
70#[unsafe(no_mangle)]
75pub unsafe extern "C" fn typst_get_buf(ptr: *const Context, len: *mut size_t) -> *const u8 {
76 if ptr.is_null() {
77 unsafe {
78 *len = 0;
79 }
80 return ptr::null();
81 }
82
83 let ctx = unsafe { &*ptr };
84 unsafe {
85 *len = ctx.len;
86 }
87 ctx.out
88}
89
90#[unsafe(no_mangle)]
94pub unsafe extern "C" fn typst_get_npages(ptr: *const Context) -> size_t {
95 if ptr.is_null() {
96 return 0;
97 }
98
99 let ctx = unsafe { &*ptr };
100
101 match &ctx.doc {
102 Err(_) => 0,
103 Ok(doc) => doc.npages(),
104 }
105}
106
107#[unsafe(no_mangle)]
112pub unsafe extern "C" fn typst_compile(src: *const c_char) -> *mut Context {
113 if src.is_null() {
114 return Box::into_raw(Box::new(Context {
115 doc: Err("no document".to_string()),
116 out: ptr::null(),
117 len: 0,
118 err: CString::new("NULL pointer to document".to_string())
119 .unwrap()
120 .into_raw(),
121 }));
122 }
123
124 let c_str = unsafe { CStr::from_ptr(src) };
125 let v_str = match c_str.to_str() {
126 Ok(s) => s,
127 Err(err) => {
128 return Box::into_raw(Box::new(Context {
129 doc: Err(err.to_string()),
130 out: ptr::null(),
131 len: 0,
132 err: CString::new(err.to_string()).unwrap().into_raw(),
133 }));
134 }
135 };
136
137 match compile(v_str.to_string()) {
138 Err(err) => Box::into_raw(Box::new(Context {
139 doc: Err(err.clone()),
140 out: ptr::null(),
141 len: 0,
142 err: CString::new(err.to_string()).unwrap().into_raw(),
143 })),
144 Ok(doc) => Box::into_raw(Box::new(Context {
145 doc: Ok(doc),
146 out: ptr::null(),
147 len: 0,
148 err: ptr::null(),
149 })),
150 }
151}
152
153#[unsafe(no_mangle)]
158pub unsafe extern "C" fn typst_compile_pdf(ptr: *mut Context) {
159 if ptr.is_null() {
160 return;
161 }
162
163 let ctx = unsafe { &mut *ptr };
164 unsafe {
165 libc::free(ctx.out as *mut libc::c_void);
166 libc::free(ctx.err as *mut libc::c_void);
167 }
168
169 match &ctx.doc {
170 Err(err) => {
171 ctx.err = CString::new(err.to_string()).unwrap().into_raw();
172 }
173 Ok(doc) => match doc.compile_pdf() {
174 Ok(buf) => {
175 let len = buf.len();
176 let ptr = unsafe { libc::malloc(len) as *mut u8 };
177 if !ptr.is_null() {
178 unsafe {
179 ptr.copy_from_nonoverlapping(buf.as_ptr(), len);
180 }
181 }
182 ctx.len = len;
183 ctx.out = ptr;
184 }
185 Err(err) => {
186 ctx.err = CString::new(err.to_string()).unwrap().into_raw();
187 }
188 },
189 }
190}
191
192#[unsafe(no_mangle)]
197pub unsafe extern "C" fn typst_compile_png(ptr: *mut Context, page: size_t, ppi: c_float) {
198 if ptr.is_null() {
199 return;
200 }
201
202 let ctx = unsafe { &mut *ptr };
203 unsafe {
204 libc::free(ctx.out as *mut libc::c_void);
205 libc::free(ctx.err as *mut libc::c_void);
206 }
207
208 match &ctx.doc {
209 Err(err) => {
210 ctx.err = CString::new(err.to_string()).unwrap().into_raw();
211 }
212 Ok(doc) => match doc.compile_png(page, ppi) {
213 Ok(buf) => {
214 let len = buf.len();
215 let ptr = unsafe { libc::malloc(len) as *mut u8 };
216 if !ptr.is_null() {
217 unsafe {
218 ptr.copy_from_nonoverlapping(buf.as_ptr(), len);
219 }
220 }
221 ctx.len = len;
222 ctx.out = ptr;
223 }
224 Err(err) => {
225 ctx.err = CString::new(err.to_string()).unwrap().into_raw();
226 }
227 },
228 }
229}
230
231#[unsafe(no_mangle)]
236pub unsafe extern "C" fn typst_compile_svg(ptr: *mut Context) {
237 if ptr.is_null() {
238 return;
239 }
240
241 let ctx = unsafe { &mut *ptr };
242 unsafe {
243 libc::free(ctx.out as *mut libc::c_void);
244 libc::free(ctx.err as *mut libc::c_void);
245 }
246
247 match &ctx.doc {
248 Err(err) => {
249 ctx.err = CString::new(err.to_string()).unwrap().into_raw();
250 }
251 Ok(doc) => match doc.compile_svg() {
252 Ok(buf) => {
253 let len = buf.len();
254 let ptr = unsafe { libc::malloc(len) as *mut u8 };
255 if !ptr.is_null() {
256 unsafe {
257 ptr.copy_from_nonoverlapping(buf.as_ptr(), len);
258 }
259 }
260 ctx.len = len;
261 ctx.out = ptr;
262 }
263 Err(err) => {
264 ctx.err = CString::new(err.to_string()).unwrap().into_raw();
265 }
266 },
267 }
268}