use super::{Handle, HandleStore};
use std::ffi::{CStr, CString};
use std::os::raw::c_char;
use std::sync::LazyLock;
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OutlineFlag {
None = 0,
Bold = 1,
Italic = 2,
}
#[repr(C)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum OutlineIteratorResult {
DidNotMove = -1,
AtItem = 0,
AtEmpty = 1,
}
#[derive(Debug, Clone, Default)]
pub struct Location {
pub chapter: i32,
pub page: i32,
}
#[derive(Debug, Clone, Default)]
pub struct OutlineItem {
pub title: Option<String>,
pub uri: Option<String>,
pub is_open: bool,
pub flags: u8,
pub r: f32,
pub g: f32,
pub b: f32,
}
#[repr(C)]
pub struct FzOutlineItem {
pub title: *mut c_char,
pub uri: *mut c_char,
pub is_open: i32,
pub flags: i32,
pub r: f32,
pub g: f32,
pub b: f32,
}
impl Default for FzOutlineItem {
fn default() -> Self {
Self {
title: std::ptr::null_mut(),
uri: std::ptr::null_mut(),
is_open: 0,
flags: 0,
r: 0.0,
g: 0.0,
b: 0.0,
}
}
}
#[derive(Debug, Clone)]
pub struct Outline {
pub refs: i32,
pub title: Option<String>,
pub uri: Option<String>,
pub page: Location,
pub x: f32,
pub y: f32,
pub next: Option<Handle>,
pub down: Option<Handle>,
pub is_open: bool,
pub flags: u8,
pub r: u8,
pub g: u8,
pub b: u8,
}
impl Default for Outline {
fn default() -> Self {
Self {
refs: 1,
title: None,
uri: None,
page: Location::default(),
x: 0.0,
y: 0.0,
next: None,
down: None,
is_open: false,
flags: 0,
r: 0,
g: 0,
b: 0,
}
}
}
#[derive(Debug)]
pub struct OutlineIterator {
stack: Vec<(Handle, usize)>,
current: Option<Handle>,
#[allow(dead_code)]
document: Option<Handle>,
current_item: Option<OutlineItem>,
}
impl OutlineIterator {
pub fn new() -> Self {
Self {
stack: Vec::new(),
current: None,
document: None,
current_item: None,
}
}
pub fn from_outline(outline: Handle) -> Self {
Self {
stack: Vec::new(),
current: Some(outline),
document: None,
current_item: None,
}
}
pub fn item(&mut self) -> Option<&OutlineItem> {
if let Some(handle) = self.current {
if let Some(outline_arc) = OUTLINES.get(handle) {
if let Ok(outline) = outline_arc.lock() {
self.current_item = Some(OutlineItem {
title: outline.title.clone(),
uri: outline.uri.clone(),
is_open: outline.is_open,
flags: outline.flags,
r: outline.r as f32 / 255.0,
g: outline.g as f32 / 255.0,
b: outline.b as f32 / 255.0,
});
return self.current_item.as_ref();
}
}
}
None
}
pub fn next(&mut self) -> i32 {
if let Some(handle) = self.current {
if let Some(outline_arc) = OUTLINES.get(handle) {
if let Ok(outline) = outline_arc.lock() {
if let Some(next_handle) = outline.next {
self.current = Some(next_handle);
return OutlineIteratorResult::AtItem as i32;
}
}
}
}
OutlineIteratorResult::DidNotMove as i32
}
pub fn prev(&mut self) -> i32 {
if let Some((parent_handle, idx)) = self.stack.last() {
if *idx > 0 {
if let Some(parent_arc) = OUTLINES.get(*parent_handle) {
if let Ok(parent) = parent_arc.lock() {
if let Some(first_child) = parent.down {
let mut current = first_child;
for _ in 0..(idx - 1) {
if let Some(arc) = OUTLINES.get(current) {
if let Ok(node) = arc.lock() {
if let Some(next) = node.next {
current = next;
} else {
return OutlineIteratorResult::DidNotMove as i32;
}
}
}
}
self.current = Some(current);
return OutlineIteratorResult::AtItem as i32;
}
}
}
}
}
OutlineIteratorResult::DidNotMove as i32
}
pub fn up(&mut self) -> i32 {
if let Some((parent_handle, _)) = self.stack.pop() {
self.current = Some(parent_handle);
return OutlineIteratorResult::AtItem as i32;
}
OutlineIteratorResult::DidNotMove as i32
}
pub fn down(&mut self) -> i32 {
if let Some(handle) = self.current {
if let Some(outline_arc) = OUTLINES.get(handle) {
if let Ok(outline) = outline_arc.lock() {
if let Some(down_handle) = outline.down {
self.stack.push((handle, 0));
self.current = Some(down_handle);
return OutlineIteratorResult::AtItem as i32;
}
}
}
}
OutlineIteratorResult::DidNotMove as i32
}
pub fn insert(&mut self, item: &OutlineItem) -> i32 {
let new_outline = Outline {
refs: 1,
title: item.title.clone(),
uri: item.uri.clone(),
page: Location::default(),
x: 0.0,
y: 0.0,
next: self.current,
down: None,
is_open: false, flags: item.flags,
r: (item.r * 255.0) as u8,
g: (item.g * 255.0) as u8,
b: (item.b * 255.0) as u8,
};
let new_handle = OUTLINES.insert(new_outline);
if let Some((parent_handle, idx)) = self.stack.last_mut() {
if *idx == 0 {
if let Some(parent_arc) = OUTLINES.get(*parent_handle) {
if let Ok(mut parent) = parent_arc.lock() {
parent.down = Some(new_handle);
}
}
}
*idx += 1;
}
OutlineIteratorResult::AtItem as i32
}
pub fn delete(&mut self) -> i32 {
if let Some(handle) = self.current {
let next_result = self.next();
OUTLINES.remove(handle);
return next_result;
}
OutlineIteratorResult::DidNotMove as i32
}
pub fn update(&mut self, item: &OutlineItem) {
if let Some(handle) = self.current {
if let Some(outline_arc) = OUTLINES.get(handle) {
if let Ok(mut outline) = outline_arc.lock() {
outline.title = item.title.clone();
outline.uri = item.uri.clone();
outline.is_open = item.is_open;
outline.flags = item.flags;
outline.r = (item.r * 255.0) as u8;
outline.g = (item.g * 255.0) as u8;
outline.b = (item.b * 255.0) as u8;
}
}
}
}
}
impl Default for OutlineIterator {
fn default() -> Self {
Self::new()
}
}
pub static OUTLINES: LazyLock<HandleStore<Outline>> = LazyLock::new(HandleStore::new);
pub static OUTLINE_ITERATORS: LazyLock<HandleStore<OutlineIterator>> =
LazyLock::new(HandleStore::new);
thread_local! {
static CURRENT_TITLE: std::cell::RefCell<Option<CString>> = const { std::cell::RefCell::new(None) };
static CURRENT_URI: std::cell::RefCell<Option<CString>> = const { std::cell::RefCell::new(None) };
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_new_outline(_ctx: Handle) -> Handle {
OUTLINES.insert(Outline::default())
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_keep_outline(_ctx: Handle, outline: Handle) -> Handle {
if let Some(arc) = OUTLINES.get(outline) {
if let Ok(mut o) = arc.lock() {
o.refs += 1;
}
}
outline
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_drop_outline(_ctx: Handle, outline: Handle) {
if outline == 0 {
return;
}
let should_drop = if let Some(arc) = OUTLINES.get(outline) {
if let Ok(mut o) = arc.lock() {
o.refs -= 1;
o.refs <= 0
} else {
false
}
} else {
false
};
if should_drop {
let (next, down) = if let Some(arc) = OUTLINES.get(outline) {
if let Ok(o) = arc.lock() {
(o.next, o.down)
} else {
(None, None)
}
} else {
(None, None)
};
OUTLINES.remove(outline);
if let Some(next_handle) = next {
fz_drop_outline(_ctx, next_handle);
}
if let Some(down_handle) = down {
fz_drop_outline(_ctx, down_handle);
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_outline_title(_ctx: Handle, outline: Handle) -> *const c_char {
if let Some(arc) = OUTLINES.get(outline) {
if let Ok(o) = arc.lock() {
if let Some(ref title) = o.title {
if let Ok(cstr) = CString::new(title.as_str()) {
CURRENT_TITLE.with(|cell| {
*cell.borrow_mut() = Some(cstr);
});
return CURRENT_TITLE.with(|cell| {
cell.borrow()
.as_ref()
.map(|s| s.as_ptr())
.unwrap_or(std::ptr::null())
});
}
}
}
}
std::ptr::null()
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_set_outline_title(_ctx: Handle, outline: Handle, title: *const c_char) {
if let Some(arc) = OUTLINES.get(outline) {
if let Ok(mut o) = arc.lock() {
if title.is_null() {
o.title = None;
} else {
let cstr = unsafe { CStr::from_ptr(title) };
o.title = cstr.to_str().ok().map(String::from);
}
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_outline_uri(_ctx: Handle, outline: Handle) -> *const c_char {
if let Some(arc) = OUTLINES.get(outline) {
if let Ok(o) = arc.lock() {
if let Some(ref uri) = o.uri {
if let Ok(cstr) = CString::new(uri.as_str()) {
CURRENT_URI.with(|cell| {
*cell.borrow_mut() = Some(cstr);
});
return CURRENT_URI.with(|cell| {
cell.borrow()
.as_ref()
.map(|s| s.as_ptr())
.unwrap_or(std::ptr::null())
});
}
}
}
}
std::ptr::null()
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_set_outline_uri(_ctx: Handle, outline: Handle, uri: *const c_char) {
if let Some(arc) = OUTLINES.get(outline) {
if let Ok(mut o) = arc.lock() {
if uri.is_null() {
o.uri = None;
} else {
let cstr = unsafe { CStr::from_ptr(uri) };
o.uri = cstr.to_str().ok().map(String::from);
}
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_outline_page(_ctx: Handle, outline: Handle) -> i32 {
if let Some(arc) = OUTLINES.get(outline) {
if let Ok(o) = arc.lock() {
return o.page.page;
}
}
-1
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_set_outline_page(_ctx: Handle, outline: Handle, chapter: i32, page: i32) {
if let Some(arc) = OUTLINES.get(outline) {
if let Ok(mut o) = arc.lock() {
o.page.chapter = chapter;
o.page.page = page;
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_outline_is_open(_ctx: Handle, outline: Handle) -> i32 {
if let Some(arc) = OUTLINES.get(outline) {
if let Ok(o) = arc.lock() {
return o.is_open as i32;
}
}
0
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_set_outline_is_open(_ctx: Handle, outline: Handle, is_open: i32) {
if let Some(arc) = OUTLINES.get(outline) {
if let Ok(mut o) = arc.lock() {
o.is_open = is_open != 0;
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_outline_flags(_ctx: Handle, outline: Handle) -> i32 {
if let Some(arc) = OUTLINES.get(outline) {
if let Ok(o) = arc.lock() {
return o.flags as i32;
}
}
0
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_set_outline_flags(_ctx: Handle, outline: Handle, flags: i32) {
if let Some(arc) = OUTLINES.get(outline) {
if let Ok(mut o) = arc.lock() {
o.flags = flags as u8;
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_outline_color_r(_ctx: Handle, outline: Handle) -> f32 {
if let Some(arc) = OUTLINES.get(outline) {
if let Ok(o) = arc.lock() {
return o.r as f32 / 255.0;
}
}
0.0
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_outline_color_g(_ctx: Handle, outline: Handle) -> f32 {
if let Some(arc) = OUTLINES.get(outline) {
if let Ok(o) = arc.lock() {
return o.g as f32 / 255.0;
}
}
0.0
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_outline_color_b(_ctx: Handle, outline: Handle) -> f32 {
if let Some(arc) = OUTLINES.get(outline) {
if let Ok(o) = arc.lock() {
return o.b as f32 / 255.0;
}
}
0.0
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_set_outline_color(_ctx: Handle, outline: Handle, r: f32, g: f32, b: f32) {
if let Some(arc) = OUTLINES.get(outline) {
if let Ok(mut o) = arc.lock() {
o.r = (r.clamp(0.0, 1.0) * 255.0) as u8;
o.g = (g.clamp(0.0, 1.0) * 255.0) as u8;
o.b = (b.clamp(0.0, 1.0) * 255.0) as u8;
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_outline_next(_ctx: Handle, outline: Handle) -> Handle {
if let Some(arc) = OUTLINES.get(outline) {
if let Ok(o) = arc.lock() {
return o.next.unwrap_or(0);
}
}
0
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_set_outline_next(_ctx: Handle, outline: Handle, next: Handle) {
if let Some(arc) = OUTLINES.get(outline) {
if let Ok(mut o) = arc.lock() {
o.next = if next == 0 { None } else { Some(next) };
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_outline_down(_ctx: Handle, outline: Handle) -> Handle {
if let Some(arc) = OUTLINES.get(outline) {
if let Ok(o) = arc.lock() {
return o.down.unwrap_or(0);
}
}
0
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_set_outline_down(_ctx: Handle, outline: Handle, down: Handle) {
if let Some(arc) = OUTLINES.get(outline) {
if let Ok(mut o) = arc.lock() {
o.down = if down == 0 { None } else { Some(down) };
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_outline_x(_ctx: Handle, outline: Handle) -> f32 {
if let Some(arc) = OUTLINES.get(outline) {
if let Ok(o) = arc.lock() {
return o.x;
}
}
0.0
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_outline_y(_ctx: Handle, outline: Handle) -> f32 {
if let Some(arc) = OUTLINES.get(outline) {
if let Ok(o) = arc.lock() {
return o.y;
}
}
0.0
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_set_outline_xy(_ctx: Handle, outline: Handle, x: f32, y: f32) {
if let Some(arc) = OUTLINES.get(outline) {
if let Ok(mut o) = arc.lock() {
o.x = x;
o.y = y;
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_outline_iterator_from_outline(_ctx: Handle, outline: Handle) -> Handle {
if outline == 0 {
return 0;
}
OUTLINE_ITERATORS.insert(OutlineIterator::from_outline(outline))
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_new_outline_iterator(_ctx: Handle) -> Handle {
OUTLINE_ITERATORS.insert(OutlineIterator::new())
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_drop_outline_iterator(_ctx: Handle, iter: Handle) {
if iter != 0 {
OUTLINE_ITERATORS.remove(iter);
}
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_outline_iterator_item(_ctx: Handle, iter: Handle) -> *const FzOutlineItem {
thread_local! {
static ITEM: std::cell::RefCell<FzOutlineItem> = std::cell::RefCell::new(FzOutlineItem::default());
}
if let Some(arc) = OUTLINE_ITERATORS.get(iter) {
if let Ok(mut iterator) = arc.lock() {
if let Some(item) = iterator.item() {
if let Some(ref title) = item.title {
if let Ok(cstr) = CString::new(title.as_str()) {
CURRENT_TITLE.with(|cell| {
*cell.borrow_mut() = Some(cstr);
});
}
}
if let Some(ref uri) = item.uri {
if let Ok(cstr) = CString::new(uri.as_str()) {
CURRENT_URI.with(|cell| {
*cell.borrow_mut() = Some(cstr);
});
}
}
ITEM.with(|cell| {
let mut fz_item = cell.borrow_mut();
fz_item.title = CURRENT_TITLE.with(|c| {
c.borrow()
.as_ref()
.map(|s| s.as_ptr() as *mut c_char)
.unwrap_or(std::ptr::null_mut())
});
fz_item.uri = CURRENT_URI.with(|c| {
c.borrow()
.as_ref()
.map(|s| s.as_ptr() as *mut c_char)
.unwrap_or(std::ptr::null_mut())
});
fz_item.is_open = item.is_open as i32;
fz_item.flags = item.flags as i32;
fz_item.r = item.r;
fz_item.g = item.g;
fz_item.b = item.b;
});
return ITEM.with(|cell| cell.as_ptr());
}
}
}
std::ptr::null()
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_outline_iterator_next(_ctx: Handle, iter: Handle) -> i32 {
if let Some(arc) = OUTLINE_ITERATORS.get(iter) {
if let Ok(mut iterator) = arc.lock() {
return iterator.next();
}
}
OutlineIteratorResult::DidNotMove as i32
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_outline_iterator_prev(_ctx: Handle, iter: Handle) -> i32 {
if let Some(arc) = OUTLINE_ITERATORS.get(iter) {
if let Ok(mut iterator) = arc.lock() {
return iterator.prev();
}
}
OutlineIteratorResult::DidNotMove as i32
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_outline_iterator_up(_ctx: Handle, iter: Handle) -> i32 {
if let Some(arc) = OUTLINE_ITERATORS.get(iter) {
if let Ok(mut iterator) = arc.lock() {
return iterator.up();
}
}
OutlineIteratorResult::DidNotMove as i32
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_outline_iterator_down(_ctx: Handle, iter: Handle) -> i32 {
if let Some(arc) = OUTLINE_ITERATORS.get(iter) {
if let Ok(mut iterator) = arc.lock() {
return iterator.down();
}
}
OutlineIteratorResult::DidNotMove as i32
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_outline_iterator_insert(
_ctx: Handle,
iter: Handle,
item: *const FzOutlineItem,
) -> i32 {
if item.is_null() {
return OutlineIteratorResult::DidNotMove as i32;
}
let fz_item = unsafe { &*item };
let rust_item = OutlineItem {
title: if fz_item.title.is_null() {
None
} else {
unsafe { CStr::from_ptr(fz_item.title) }
.to_str()
.ok()
.map(String::from)
},
uri: if fz_item.uri.is_null() {
None
} else {
unsafe { CStr::from_ptr(fz_item.uri) }
.to_str()
.ok()
.map(String::from)
},
is_open: fz_item.is_open != 0,
flags: fz_item.flags as u8,
r: fz_item.r,
g: fz_item.g,
b: fz_item.b,
};
if let Some(arc) = OUTLINE_ITERATORS.get(iter) {
if let Ok(mut iterator) = arc.lock() {
return iterator.insert(&rust_item);
}
}
OutlineIteratorResult::DidNotMove as i32
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_outline_iterator_delete(_ctx: Handle, iter: Handle) -> i32 {
if let Some(arc) = OUTLINE_ITERATORS.get(iter) {
if let Ok(mut iterator) = arc.lock() {
return iterator.delete();
}
}
OutlineIteratorResult::DidNotMove as i32
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_outline_iterator_update(
_ctx: Handle,
iter: Handle,
item: *const FzOutlineItem,
) {
if item.is_null() {
return;
}
let fz_item = unsafe { &*item };
let rust_item = OutlineItem {
title: if fz_item.title.is_null() {
None
} else {
unsafe { CStr::from_ptr(fz_item.title) }
.to_str()
.ok()
.map(String::from)
},
uri: if fz_item.uri.is_null() {
None
} else {
unsafe { CStr::from_ptr(fz_item.uri) }
.to_str()
.ok()
.map(String::from)
},
is_open: fz_item.is_open != 0,
flags: fz_item.flags as u8,
r: fz_item.r,
g: fz_item.g,
b: fz_item.b,
};
if let Some(arc) = OUTLINE_ITERATORS.get(iter) {
if let Ok(mut iterator) = arc.lock() {
iterator.update(&rust_item);
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_load_outline_from_iterator(_ctx: Handle, iter: Handle) -> Handle {
if let Some(arc) = OUTLINE_ITERATORS.get(iter) {
if let Ok(iterator) = arc.lock() {
if let Some(current) = iterator.current {
return current;
}
}
}
0
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_outline_count(_ctx: Handle, outline: Handle) -> i32 {
fn count_recursive(handle: Handle) -> i32 {
if handle == 0 {
return 0;
}
let (next, down) = if let Some(arc) = OUTLINES.get(handle) {
if let Ok(o) = arc.lock() {
(o.next, o.down)
} else {
(None, None)
}
} else {
return 0;
};
let mut count = 1;
if let Some(next_handle) = next {
count += count_recursive(next_handle);
}
if let Some(down_handle) = down {
count += count_recursive(down_handle);
}
count
}
count_recursive(outline)
}
#[unsafe(no_mangle)]
pub extern "C" fn fz_outline_depth(_ctx: Handle, iter: Handle) -> i32 {
if let Some(arc) = OUTLINE_ITERATORS.get(iter) {
if let Ok(iterator) = arc.lock() {
return iterator.stack.len() as i32;
}
}
0
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_outline_create() {
let ctx = 0;
let outline = fz_new_outline(ctx);
assert!(outline > 0);
fz_drop_outline(ctx, outline);
}
#[test]
fn test_outline_title() {
let ctx = 0;
let outline = fz_new_outline(ctx);
let title = CString::new("Chapter 1").unwrap();
fz_set_outline_title(ctx, outline, title.as_ptr());
let got_title = fz_outline_title(ctx, outline);
assert!(!got_title.is_null());
let got_str = unsafe { CStr::from_ptr(got_title) };
assert_eq!(got_str.to_str().unwrap(), "Chapter 1");
fz_drop_outline(ctx, outline);
}
#[test]
fn test_outline_tree() {
let ctx = 0;
let root = fz_new_outline(ctx);
let title = CString::new("Root").unwrap();
fz_set_outline_title(ctx, root, title.as_ptr());
let child = fz_new_outline(ctx);
let title = CString::new("Child").unwrap();
fz_set_outline_title(ctx, child, title.as_ptr());
fz_set_outline_down(ctx, root, child);
let got_child = fz_outline_down(ctx, root);
assert_eq!(got_child, child);
let count = fz_outline_count(ctx, root);
assert_eq!(count, 2);
fz_drop_outline(ctx, root);
}
#[test]
fn test_outline_iterator() {
let ctx = 0;
let root = fz_new_outline(ctx);
let title = CString::new("Root").unwrap();
fz_set_outline_title(ctx, root, title.as_ptr());
let child1 = fz_new_outline(ctx);
let title = CString::new("Child 1").unwrap();
fz_set_outline_title(ctx, child1, title.as_ptr());
let child2 = fz_new_outline(ctx);
let title = CString::new("Child 2").unwrap();
fz_set_outline_title(ctx, child2, title.as_ptr());
fz_set_outline_down(ctx, root, child1);
fz_set_outline_next(ctx, child1, child2);
let iter = fz_outline_iterator_from_outline(ctx, root);
assert!(iter > 0);
let item = fz_outline_iterator_item(ctx, iter);
assert!(!item.is_null());
let result = fz_outline_iterator_down(ctx, iter);
assert_eq!(result, OutlineIteratorResult::AtItem as i32);
let result = fz_outline_iterator_next(ctx, iter);
assert_eq!(result, OutlineIteratorResult::AtItem as i32);
let result = fz_outline_iterator_up(ctx, iter);
assert_eq!(result, OutlineIteratorResult::AtItem as i32);
fz_drop_outline_iterator(ctx, iter);
fz_drop_outline(ctx, root);
}
#[test]
fn test_outline_color() {
let ctx = 0;
let outline = fz_new_outline(ctx);
fz_set_outline_color(ctx, outline, 1.0, 0.5, 0.0);
let r = fz_outline_color_r(ctx, outline);
let g = fz_outline_color_g(ctx, outline);
let b = fz_outline_color_b(ctx, outline);
assert!((r - 1.0).abs() < 0.01);
assert!((g - 0.5).abs() < 0.01);
assert!((b - 0.0).abs() < 0.01);
fz_drop_outline(ctx, outline);
}
#[test]
fn test_outline_flags() {
let ctx = 0;
let outline = fz_new_outline(ctx);
fz_set_outline_flags(
ctx,
outline,
OutlineFlag::Bold as i32 | OutlineFlag::Italic as i32,
);
let flags = fz_outline_flags(ctx, outline);
assert_eq!(flags, 3);
fz_drop_outline(ctx, outline);
}
#[test]
fn test_outline_page() {
let ctx = 0;
let outline = fz_new_outline(ctx);
fz_set_outline_page(ctx, outline, 0, 42);
let page = fz_outline_page(ctx, outline);
assert_eq!(page, 42);
fz_drop_outline(ctx, outline);
}
#[test]
fn test_outline_invalid_handle() {
let ctx = 0;
assert!(fz_outline_title(ctx, 99999).is_null());
assert!(fz_outline_uri(ctx, 99999).is_null());
assert_eq!(fz_outline_page(ctx, 99999), -1);
assert_eq!(fz_outline_is_open(ctx, 99999), 0);
assert_eq!(fz_outline_flags(ctx, 99999), 0);
assert!((fz_outline_color_r(ctx, 99999) - 0.0).abs() < 0.001);
assert_eq!(fz_outline_next(ctx, 99999), 0);
assert_eq!(fz_outline_down(ctx, 99999), 0);
assert!((fz_outline_x(ctx, 99999) - 0.0).abs() < 0.001);
assert!((fz_outline_y(ctx, 99999) - 0.0).abs() < 0.001);
assert_eq!(fz_outline_count(ctx, 99999), 0);
}
#[test]
fn test_outline_drop_zero() {
fz_drop_outline(0, 0);
}
#[test]
fn test_outline_set_null_title_uri() {
let ctx = 0;
let outline = fz_new_outline(ctx);
fz_set_outline_title(ctx, outline, std::ptr::null());
fz_set_outline_uri(ctx, outline, std::ptr::null());
assert!(fz_outline_title(ctx, outline).is_null());
assert!(fz_outline_uri(ctx, outline).is_null());
fz_drop_outline(ctx, outline);
}
#[test]
fn test_outline_uri() {
let ctx = 0;
let outline = fz_new_outline(ctx);
let uri = CString::new("page=5").unwrap();
fz_set_outline_uri(ctx, outline, uri.as_ptr());
let got = fz_outline_uri(ctx, outline);
assert!(!got.is_null());
assert_eq!(unsafe { CStr::from_ptr(got) }.to_str().unwrap(), "page=5");
fz_drop_outline(ctx, outline);
}
#[test]
fn test_outline_is_open() {
let ctx = 0;
let outline = fz_new_outline(ctx);
fz_set_outline_is_open(ctx, outline, 1);
assert_eq!(fz_outline_is_open(ctx, outline), 1);
fz_set_outline_is_open(ctx, outline, 0);
assert_eq!(fz_outline_is_open(ctx, outline), 0);
fz_drop_outline(ctx, outline);
}
#[test]
fn test_outline_xy() {
let ctx = 0;
let outline = fz_new_outline(ctx);
fz_set_outline_xy(ctx, outline, 100.5, 200.25);
assert!((fz_outline_x(ctx, outline) - 100.5).abs() < 0.001);
assert!((fz_outline_y(ctx, outline) - 200.25).abs() < 0.001);
fz_drop_outline(ctx, outline);
}
#[test]
fn test_outline_iterator_zero() {
let ctx = 0;
assert_eq!(fz_outline_iterator_from_outline(ctx, 0), 0);
}
#[test]
fn test_outline_iterator_drop_zero() {
fz_drop_outline_iterator(0, 0);
}
#[test]
fn test_outline_iterator_invalid() {
let ctx = 0;
assert!(fz_outline_iterator_item(ctx, 99999).is_null());
assert_eq!(
fz_outline_iterator_next(ctx, 99999),
OutlineIteratorResult::DidNotMove as i32
);
assert_eq!(
fz_outline_iterator_prev(ctx, 99999),
OutlineIteratorResult::DidNotMove as i32
);
assert_eq!(
fz_outline_iterator_up(ctx, 99999),
OutlineIteratorResult::DidNotMove as i32
);
assert_eq!(
fz_outline_iterator_down(ctx, 99999),
OutlineIteratorResult::DidNotMove as i32
);
assert_eq!(fz_outline_depth(ctx, 99999), 0);
assert_eq!(fz_load_outline_from_iterator(ctx, 99999), 0);
}
#[test]
fn test_outline_iterator_insert_update() {
let ctx = 0;
let root = fz_new_outline(ctx);
let iter = fz_outline_iterator_from_outline(ctx, root);
let title = CString::new("New").unwrap();
let uri = CString::new("page=1").unwrap();
let mut item = FzOutlineItem::default();
item.title = title.as_ptr() as *mut std::os::raw::c_char;
item.uri = uri.as_ptr() as *mut std::os::raw::c_char;
item.is_open = 1;
item.flags = OutlineFlag::Bold as i32;
item.r = 1.0;
item.g = 0.0;
item.b = 0.0;
let result = fz_outline_iterator_insert(ctx, iter, &item);
assert_eq!(result, OutlineIteratorResult::AtItem as i32);
fz_outline_iterator_update(ctx, iter, &item);
fz_drop_outline_iterator(ctx, iter);
fz_drop_outline(ctx, root);
}
#[test]
fn test_outline_iterator_insert_null() {
let ctx = 0;
let root = fz_new_outline(ctx);
let iter = fz_outline_iterator_from_outline(ctx, root);
assert_eq!(
fz_outline_iterator_insert(ctx, iter, std::ptr::null()),
OutlineIteratorResult::DidNotMove as i32
);
fz_drop_outline_iterator(ctx, iter);
fz_drop_outline(ctx, root);
}
#[test]
fn test_outline_new_iterator() {
let ctx = 0;
let iter = fz_new_outline_iterator(ctx);
assert!(iter > 0);
fz_drop_outline_iterator(ctx, iter);
}
#[test]
fn test_outline_keep() {
let ctx = 0;
let outline = fz_new_outline(ctx);
let kept = fz_keep_outline(ctx, outline);
assert_eq!(kept, outline);
fz_drop_outline(ctx, outline);
fz_drop_outline(ctx, kept);
}
}