micropdf 0.17.0

A pure Rust PDF library - A pure Rust PDF library with fz_/pdf_ API compatibility
//! PDF Object Copy Operations FFI Functions

use super::super::Handle;
use super::refcount::with_obj;
use super::types::{PDF_OBJECTS, PdfObj, PdfObjHandle, PdfObjType};

#[unsafe(no_mangle)]
pub extern "C" fn pdf_copy_array(_ctx: Handle, _doc: Handle, array: PdfObjHandle) -> PdfObjHandle {
    let copied = with_obj(array, None, |o| match &o.obj_type {
        PdfObjType::Array(arr) => {
            let mut new_arr = PdfObj::new_array(arr.len());
            if let PdfObjType::Array(ref mut new_vec) = new_arr.obj_type {
                for item in arr {
                    new_vec.push(item.clone());
                }
            }
            Some(new_arr)
        }
        _ => None,
    });

    match copied {
        Some(obj) => PDF_OBJECTS.insert(obj),
        None => 0,
    }
}

#[unsafe(no_mangle)]
pub extern "C" fn pdf_copy_dict(_ctx: Handle, _doc: Handle, dict: PdfObjHandle) -> PdfObjHandle {
    let copied = with_obj(dict, None, |o| match &o.obj_type {
        PdfObjType::Dict(entries) => {
            let mut new_dict = PdfObj::new_dict(entries.len());
            if let PdfObjType::Dict(ref mut new_entries) = new_dict.obj_type {
                for (k, v) in entries {
                    new_entries.push((k.clone(), v.clone()));
                }
            }
            Some(new_dict)
        }
        _ => None,
    });

    match copied {
        Some(obj) => PDF_OBJECTS.insert(obj),
        None => 0,
    }
}

fn deep_copy_obj_inner(obj: &PdfObj) -> PdfObj {
    let new_type = match &obj.obj_type {
        PdfObjType::Null => PdfObjType::Null,
        PdfObjType::Bool(b) => PdfObjType::Bool(*b),
        PdfObjType::Int(i) => PdfObjType::Int(*i),
        PdfObjType::Real(r) => PdfObjType::Real(*r),
        PdfObjType::Name(s) => PdfObjType::Name(s.clone()),
        PdfObjType::String(s) => PdfObjType::String(s.clone()),
        PdfObjType::Array(arr) => PdfObjType::Array(arr.iter().map(deep_copy_obj_inner).collect()),
        PdfObjType::Dict(entries) => PdfObjType::Dict(
            entries
                .iter()
                .map(|(k, v)| (k.clone(), deep_copy_obj_inner(v)))
                .collect(),
        ),
        PdfObjType::Indirect { num, generation } => PdfObjType::Indirect {
            num: *num,
            generation: *generation,
        },
        PdfObjType::Stream { dict, data } => PdfObjType::Stream {
            dict: Box::new(deep_copy_obj_inner(dict)),
            data: data.clone(),
        },
    };

    PdfObj {
        obj_type: new_type,
        marked: false,
        dirty: false,
        parent_num: 0,
        refs: 1,
    }
}

#[unsafe(no_mangle)]
pub extern "C" fn pdf_deep_copy_obj(_ctx: Handle, _doc: Handle, obj: PdfObjHandle) -> PdfObjHandle {
    let copied = with_obj(obj, None, |o| Some(deep_copy_obj_inner(o)));

    match copied {
        Some(new_obj) => PDF_OBJECTS.insert(new_obj),
        None => 0,
    }
}

#[cfg(test)]
mod tests {
    use super::super::create::{pdf_new_dict, pdf_new_indirect, pdf_new_name, pdf_new_string};
    use super::super::refcount::pdf_drop_obj;
    use super::super::types::{PDF_OBJECTS, PdfObj, PdfObjType};
    use super::*;
    use std::ffi::CString;

    #[test]
    fn test_pdf_copy_array_non_array() {
        let int_obj = super::super::create::pdf_new_int(0, 42);
        let result = pdf_copy_array(0, 0, int_obj);
        assert_eq!(result, 0);
    }

    #[test]
    fn test_pdf_copy_dict_non_dict() {
        let int_obj = super::super::create::pdf_new_int(0, 42);
        let result = pdf_copy_dict(0, 0, int_obj);
        assert_eq!(result, 0);
    }

    #[test]
    fn test_pdf_deep_copy_indirect() {
        let indirect = pdf_new_indirect(0, 0, 10, 2);
        let copy = pdf_deep_copy_obj(0, 0, indirect);
        assert_ne!(copy, 0);
        pdf_drop_obj(0, copy);
        pdf_drop_obj(0, indirect);
    }

    #[test]
    fn test_pdf_deep_copy_stream() {
        let stream_dict = PdfObj::new_dict(0);
        let stream_obj = PdfObj {
            obj_type: PdfObjType::Stream {
                dict: Box::new(stream_dict),
                data: vec![1, 2, 3],
            },
            marked: false,
            dirty: false,
            parent_num: 0,
            refs: 1,
        };
        let handle = PDF_OBJECTS.insert(stream_obj);
        let copy = pdf_deep_copy_obj(0, 0, handle);
        assert_ne!(copy, 0);
        pdf_drop_obj(0, copy);
        pdf_drop_obj(0, handle);
    }

    #[test]
    fn test_pdf_deep_copy_with_name_and_string() {
        let dict = pdf_new_dict(0, 0, 2);
        let key = pdf_new_name(0, CString::new("Key").unwrap().as_ptr());
        let val = pdf_new_string(0, b"value".as_ptr() as *const i8, 5);
        super::super::dict::pdf_dict_put(0, dict, key, val);
        let copy = pdf_deep_copy_obj(0, 0, dict);
        assert_ne!(copy, 0);
        pdf_drop_obj(0, copy);
        pdf_drop_obj(0, dict);
    }
}