use super::{Handle, HandleStore, safe_helpers};
use crate::fitz::geometry::Matrix;
use crate::fitz::text::Text;
use std::sync::{Arc, LazyLock};
pub static TEXTS: LazyLock<HandleStore<Text>> = LazyLock::new(HandleStore::default);
#[unsafe(no_mangle)]
pub extern "C" fn fz_new_text(_ctx: Handle) -> Handle {
let text = Text::new();
TEXTS.insert(text)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_keep_text(_ctx: Handle, text: Handle) -> Handle {
TEXTS.keep(text)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_drop_text(_ctx: Handle, text: Handle) {
let _ = TEXTS.remove(text);
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_show_glyph(
_ctx: Handle,
text: Handle,
font: Handle,
transform: super::geometry::fz_matrix,
glyph: i32,
unicode: i32,
wmode: i32,
) {
if let Some(t) = TEXTS.get(text) {
if let Ok(mut guard) = t.lock() {
if let Some(f) = super::font::FONTS.get(font) {
if let Ok(font_guard) = f.lock() {
let matrix = Matrix::new(
transform.a,
transform.b,
transform.c,
transform.d,
transform.e,
transform.f,
);
let font_arc = Arc::new(font_guard.clone());
guard.show_glyph(
font_arc,
matrix,
glyph,
unicode,
wmode != 0,
0, crate::fitz::text::BidiDirection::Ltr, crate::fitz::text::TextLanguage::Unset, );
}
}
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_show_string(
_ctx: Handle,
text: Handle,
font: Handle,
transform: super::geometry::fz_matrix,
string: *const std::ffi::c_char,
wmode: i32,
) {
let s = match safe_helpers::c_str_to_str(string) {
Some(s) => s,
None => return,
};
if let Some(t) = TEXTS.get(text) {
if let Ok(mut guard) = t.lock() {
if let Some(f) = super::font::FONTS.get(font) {
if let Ok(font_guard) = f.lock() {
let matrix = Matrix::new(
transform.a,
transform.b,
transform.c,
transform.d,
transform.e,
transform.f,
);
let font_arc = Arc::new(font_guard.clone());
let _ = guard.show_string(
font_arc,
matrix,
s,
wmode != 0,
0, crate::fitz::text::BidiDirection::Ltr, crate::fitz::text::TextLanguage::Unset, );
}
}
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_bound_text(
_ctx: Handle,
text: Handle,
stroke: Handle,
transform: super::geometry::fz_matrix,
) -> super::geometry::fz_rect {
if let Some(t) = TEXTS.get(text) {
if let Ok(guard) = t.lock() {
let matrix = Matrix::new(
transform.a,
transform.b,
transform.c,
transform.d,
transform.e,
transform.f,
);
let stroke_opt = if stroke != 0 {
super::path::STROKE_STATES
.get(stroke)
.and_then(|s| s.lock().ok().map(|guard| guard.clone()))
} else {
None
};
let bounds = guard.bounds(stroke_opt.as_ref(), &matrix);
return super::geometry::fz_rect {
x0: bounds.x0,
y0: bounds.y0,
x1: bounds.x1,
y1: bounds.y1,
};
}
}
super::geometry::fz_rect {
x0: 0.0,
y0: 0.0,
x1: 0.0,
y1: 0.0,
}
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_clone_text(_ctx: Handle, text: Handle) -> Handle {
if let Some(t) = TEXTS.get(text) {
if let Ok(guard) = t.lock() {
let cloned = guard.clone();
return TEXTS.insert(cloned);
}
}
0
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_text_language(
_ctx: Handle,
text: Handle,
buf: *mut std::ffi::c_char,
len: i32,
) -> i32 {
if let Some(t) = TEXTS.get(text) {
if let Ok(_guard) = t.lock() {
let lang = "en"; return safe_helpers::str_to_c_buffer(lang, buf, len);
}
}
0
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_set_text_language(_ctx: Handle, text: Handle, lang: *const std::ffi::c_char) {
if let Some(lang_str) = safe_helpers::c_str_to_str(lang) {
if let Some(t) = TEXTS.get(text) {
if let Ok(mut guard) = t.lock() {
let language = crate::fitz::text::TextLanguage::from_string(lang_str);
guard.set_language(language);
}
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_text_count_spans(_ctx: Handle, text: Handle) -> i32 {
if let Some(t) = TEXTS.get(text) {
if let Ok(guard) = t.lock() {
return guard.span_count() as i32;
}
}
0
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_text_count_items(_ctx: Handle, text: Handle) -> i32 {
if let Some(t) = TEXTS.get(text) {
if let Ok(guard) = t.lock() {
return guard.item_count() as i32;
}
}
0
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_clear_text(_ctx: Handle, text: Handle) {
if let Some(t) = TEXTS.get(text) {
if let Ok(mut guard) = t.lock() {
guard.clear();
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_text_is_valid(_ctx: Handle, text: Handle) -> i32 {
if TEXTS.get(text).is_some() { 1 } else { 0 }
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_text_is_empty(_ctx: Handle, text: Handle) -> i32 {
if let Some(t) = TEXTS.get(text) {
if let Ok(guard) = t.lock() {
return if guard.item_count() == 0 { 1 } else { 0 };
}
}
1
}
type TextWalkCallback = extern "C" fn(
*mut std::ffi::c_void,
Handle,
*const super::geometry::fz_matrix,
i32,
i32,
) -> i32;
#[unsafe(no_mangle)]
pub extern "C" fn fz_text_walk(
_ctx: Handle,
text: Handle,
callback: *const std::ffi::c_void,
arg: *mut std::ffi::c_void,
) -> i32 {
if callback.is_null() {
return 0;
}
if let Some(txt) = TEXTS.get(text) {
if let Ok(guard) = txt.lock() {
let cb: TextWalkCallback = unsafe { std::mem::transmute(callback) };
for span in guard.spans() {
let font_handle = super::font::FONTS.insert((*span.font).clone());
let trm = super::geometry::fz_matrix {
a: span.trm.a,
b: span.trm.b,
c: span.trm.c,
d: span.trm.d,
e: span.trm.e,
f: span.trm.f,
};
for item in span.items() {
let result = cb(arg, font_handle, &trm, item.ucs, item.gid);
if result == 0 {
return 0; }
}
}
return 1;
}
}
0
}
#[cfg(test)]
mod tests {
use super::*;
use crate::fitz::font::Font;
#[test]
fn test_new_text() {
let text_handle = fz_new_text(0);
assert_ne!(text_handle, 0);
fz_drop_text(0, text_handle);
}
#[test]
fn test_keep_text() {
let text_handle = fz_new_text(0);
let kept = fz_keep_text(0, text_handle);
assert_eq!(kept, text_handle);
fz_drop_text(0, text_handle);
}
#[test]
fn test_clone_text() {
let text_handle = fz_new_text(0);
let cloned = fz_clone_text(0, text_handle);
assert_ne!(cloned, 0);
fz_drop_text(0, cloned);
fz_drop_text(0, text_handle);
}
#[test]
fn test_bound_text() {
let text_handle = fz_new_text(0);
let bounds = fz_bound_text(
0,
text_handle,
0,
super::super::geometry::fz_matrix::identity(),
);
assert_eq!(bounds.x0, f32::INFINITY);
assert_eq!(bounds.y0, f32::INFINITY);
assert_eq!(bounds.x1, f32::NEG_INFINITY);
assert_eq!(bounds.y1, f32::NEG_INFINITY);
fz_drop_text(0, text_handle);
}
#[test]
fn test_count_spans() {
let text_handle = fz_new_text(0);
let count = fz_text_count_spans(0, text_handle);
assert_eq!(count, 0); fz_drop_text(0, text_handle);
}
#[test]
fn test_count_items() {
let text_handle = fz_new_text(0);
let count = fz_text_count_items(0, text_handle);
assert_eq!(count, 0); fz_drop_text(0, text_handle);
}
#[test]
fn test_clear_text() {
let text_handle = fz_new_text(0);
fz_clear_text(0, text_handle);
let count = fz_text_count_items(0, text_handle);
assert_eq!(count, 0);
fz_drop_text(0, text_handle);
}
#[test]
fn test_text_language() {
let text_handle = fz_new_text(0);
let mut buf = [0i8; 8];
let len = fz_text_language(0, text_handle, buf.as_mut_ptr(), 8);
assert!(len > 0);
fz_drop_text(0, text_handle);
}
#[test]
fn test_set_text_language() {
let text_handle = fz_new_text(0);
fz_set_text_language(0, text_handle, c"en".as_ptr());
fz_drop_text(0, text_handle);
}
#[test]
fn test_show_string() {
let font = Font::new("Test");
let font_handle = super::super::font::FONTS.insert(font);
let text_handle = fz_new_text(0);
fz_show_string(
0,
text_handle,
font_handle,
super::super::geometry::fz_matrix::identity(),
c"Hello".as_ptr(),
0,
);
fz_drop_text(0, text_handle);
super::super::font::FONTS.remove(font_handle);
}
#[test]
fn test_show_string_null() {
let font = Font::new("Test");
let font_handle = super::super::font::FONTS.insert(font);
let text_handle = fz_new_text(0);
fz_show_string(
0,
text_handle,
font_handle,
super::super::geometry::fz_matrix::identity(),
std::ptr::null(),
0,
);
fz_drop_text(0, text_handle);
super::super::font::FONTS.remove(font_handle);
}
#[test]
fn test_show_glyph() {
let font = Font::new("Test");
let font_handle = super::super::font::FONTS.insert(font);
let text_handle = fz_new_text(0);
fz_show_glyph(
0,
text_handle,
font_handle,
super::super::geometry::fz_matrix::identity(),
65,
65,
0,
);
assert_eq!(fz_text_count_items(0, text_handle), 1);
fz_drop_text(0, text_handle);
super::super::font::FONTS.remove(font_handle);
}
#[test]
fn test_bound_text_with_stroke() {
let text_handle = fz_new_text(0);
let stroke_handle = super::super::path::fz_new_stroke_state(0);
let bounds = fz_bound_text(
0,
text_handle,
stroke_handle,
super::super::geometry::fz_matrix::identity(),
);
assert_eq!(bounds.x0, f32::INFINITY);
assert_eq!(bounds.y0, f32::INFINITY);
fz_drop_text(0, text_handle);
super::super::path::fz_drop_stroke_state(0, stroke_handle);
}
#[test]
fn test_text_language_null_buf() {
let text_handle = fz_new_text(0);
let r = fz_text_language(0, text_handle, std::ptr::null_mut(), 8);
assert!(r <= 0);
fz_drop_text(0, text_handle);
}
#[test]
fn test_set_text_language_null() {
let text_handle = fz_new_text(0);
fz_set_text_language(0, text_handle, std::ptr::null());
fz_drop_text(0, text_handle);
}
#[test]
fn test_text_is_valid() {
let text_handle = fz_new_text(0);
assert_eq!(fz_text_is_valid(0, text_handle), 1);
assert_eq!(fz_text_is_valid(0, 99999), 0);
fz_drop_text(0, text_handle);
}
#[test]
fn test_text_is_empty() {
let text_handle = fz_new_text(0);
assert_eq!(fz_text_is_empty(0, text_handle), 1);
fz_drop_text(0, text_handle);
}
#[test]
fn test_text_walk_null_callback() {
let text_handle = fz_new_text(0);
assert_eq!(
fz_text_walk(0, text_handle, std::ptr::null(), std::ptr::null_mut()),
0
);
fz_drop_text(0, text_handle);
}
#[test]
fn test_show_glyph_invalid() {
fz_show_glyph(
0,
99999,
0,
super::super::geometry::fz_matrix::identity(),
65,
65,
0,
);
}
#[test]
fn test_clone_text_invalid() {
assert_eq!(fz_clone_text(0, 0), 0);
}
}