libass 0.2.0

Safe bindings for libass
Documentation
use std::{ffi::CStr, os::raw::c_int};
use std::ptr;
use std::ptr::NonNull;
use std::slice;
use std::{ffi::CString, marker::PhantomData};

use libass_sys as ffi;

use crate::renderer::Renderer;
use crate::track::Track;
use crate::{err_if_null, Result};

#[derive(Debug, Clone, Eq, PartialEq)]
pub enum DefaultFontProvider {
    None,
    Autodetect,
    CoreText,
    Fontconfig,
    DirectWrite,
}

pub fn version() -> i32 {
    unsafe { ffi::ass_library_version() }
}

pub struct Library<'a> {
    handle: NonNull<ffi::ass_library>,
    phantom: PhantomData<&'a mut ffi::ass_library>,
}

impl<'a> Library<'a> {
    pub fn new() -> Result<Self> {
        let lib = unsafe { ffi::ass_library_init() };
        err_if_null!(lib);
        Ok(Library {
            handle: unsafe { NonNull::new_unchecked(lib) },
            phantom: PhantomData,
        })
    }

    pub fn set_fonts_dir(&mut self, fonts_dir: &str) {
        let fonts_dir = CString::new(fonts_dir).unwrap();
        unsafe { ffi::ass_set_fonts_dir(self.handle.as_ptr(), fonts_dir.as_ptr()) }
    }

    pub fn set_extract_fonts(&mut self, extract: bool) {
        unsafe { ffi::ass_set_extract_fonts(self.handle.as_ptr(), extract as c_int) }
    }

    pub fn set_style_overrides(&mut self, list: &[&CStr]) {
        unsafe {
            ffi::ass_set_style_overrides(
                self.handle.as_ptr(),
                list.iter()
                    .map(|x| x.as_ptr())
                    .collect::<Vec<_>>()
                    .as_slice()
                    .as_ptr() as *mut *mut _,
            )
        };
    }

    pub fn add_font(&mut self, name: &str, data: &[u8]) {
        let name = CString::new(name).unwrap();
        unsafe {
            ffi::ass_add_font(
                self.handle.as_ptr(),
                name.as_ptr() as *mut _,
                data.as_ptr() as *mut _,
                data.len() as c_int,
            )
        }
    }

    pub fn clear_fonts(&mut self) {
        unsafe { ffi::ass_clear_fonts(self.handle.as_ptr()) }
    }

    pub fn get_available_font_providers(&mut self) -> Vec<DefaultFontProvider> {
        let mut providers: *mut ffi::ASS_DefaultFontProvider = ptr::null_mut();
        let providers_ptr = &mut providers as *mut *mut ffi::ASS_DefaultFontProvider;

        let mut size: usize = 0;
        let size_ptr = &mut size as *mut usize;

        unsafe {
            ffi::ass_get_available_font_providers(self.handle.as_ptr(), providers_ptr, size_ptr)
        };

        let providers_slice = unsafe { slice::from_raw_parts(providers, size) };

        let mut vec: Vec<DefaultFontProvider> = Vec::with_capacity(size);

        for provider in providers_slice {
            use crate::library::DefaultFontProvider::*;
            use ffi::ASS_DefaultFontProvider::*;
            vec.push(match provider {
                ASS_FONTPROVIDER_NONE => None,
                ASS_FONTPROVIDER_AUTODETECT => Autodetect,
                ASS_FONTPROVIDER_CORETEXT => CoreText,
                ASS_FONTPROVIDER_FONTCONFIG => Fontconfig,
                ASS_FONTPROVIDER_DIRECTWRITE => DirectWrite,
            })
        }

        unsafe { libc::free(providers as *mut libc::c_void) };

        vec
    }

    pub fn new_renderer(&self) -> Result<Renderer> {
        let renderer = unsafe { ffi::ass_renderer_init(self.handle.as_ptr() as *mut _) };
        err_if_null!(renderer);
        unsafe { Ok(Renderer::new_unchecked(renderer)) }
    }

    pub fn new_track(&self) -> Result<Track> {
        let track = unsafe { ffi::ass_new_track(self.handle.as_ptr() as *mut _) };
        err_if_null!(track);
        unsafe { Ok(Track::new_unchecked(track)) }
    }

    pub fn new_track_from_file(&self, filename: &str, codepage: &str) -> Result<Track> {
        let filename = CString::new(filename).unwrap();
        let cp = CString::new(codepage).unwrap();
        let track = unsafe {
            ffi::ass_read_file(
                self.handle.as_ptr() as *mut _,
                filename.as_ptr() as *mut _,
                cp.as_ptr() as *mut _,
            )
        };

        err_if_null!(track);
        unsafe { Ok(Track::new_unchecked(track)) }
    }

    pub fn new_track_from_memory(&self, data: &[u8], codepage: &str) -> Result<Track> {
        let cp = CString::new(codepage).unwrap();
        let track = unsafe {
            ffi::ass_read_memory(
                self.handle.as_ptr() as *mut _,
                data.as_ptr() as *mut _,
                data.len(),
                cp.as_ptr() as *mut _,
            )
        };

        err_if_null!(track);
        unsafe { Ok(Track::new_unchecked(track)) }
    }
}

impl<'a> Drop for Library<'a> {
    fn drop(&mut self) {
        unsafe { ffi::ass_library_done(self.handle.as_ptr()) }
    }
}