use std::{
ffi::{CStr, CString},
path::PathBuf,
};
use crate::{Fallback, Stretch, Style, Weight, XFontType};
#[allow(non_snake_case)]
pub struct XFont {
lib: *mut core::ffi::c_void,
AFontMatcher_create: Option<extern "system" fn() -> *mut AFontMatcher>,
AFontMatcher_destroy: Option<extern "system" fn(matcher: *mut AFontMatcher)>,
AFontMatcher_setStyle: Option<extern "system" fn(matcher: *mut AFontMatcher, weight: u16, italic: bool)>,
AFontMatcher_match: Option<
extern "system" fn(
matcher: *const AFontMatcher,
familyName: *const core::ffi::c_char,
text: *const u16,
textLength: u32,
runLengthOut: *mut u32,
) -> *mut AFont,
>,
AFont_close: Option<extern "system" fn(font: *mut AFont)>,
AFont_getFontFilePath: Option<extern "system" fn(font: *const AFont) -> *const core::ffi::c_char>,
AFont_getCollectionIndex: Option<extern "system" fn(font: *const AFont) -> usize>,
}
impl Default for XFont {
fn default() -> Self {
unsafe {
use std::mem::transmute;
use std::ptr;
let lib = dlopen(b"libandroid.so\0".as_ptr() as _, RTLD_LAZY | RTLD_LOCAL);
if lib.is_null() {
Self {
lib: ptr::null_mut(),
AFontMatcher_create: None,
AFontMatcher_destroy: None,
AFontMatcher_setStyle: None,
AFontMatcher_match: None,
AFont_close: None,
AFont_getFontFilePath: None,
AFont_getCollectionIndex: None,
}
} else {
Self {
lib,
AFontMatcher_create: transmute(dlsym(lib, b"AFontMatcher_create\0".as_ptr() as _)),
AFontMatcher_destroy: transmute(dlsym(lib, b"AFontMatcher_destroy\0".as_ptr() as _)),
AFontMatcher_setStyle: transmute(dlsym(lib, b"AFontMatcher_setStyle\0".as_ptr() as _)),
AFontMatcher_match: transmute(dlsym(lib, b"AFontMatcher_match\0".as_ptr() as _)),
AFont_close: transmute(dlsym(lib, b"AFont_close\0".as_ptr() as _)),
AFont_getFontFilePath: transmute(dlsym(lib, b"AFont_getFontFilePath\0".as_ptr() as _)),
AFont_getCollectionIndex: transmute(dlsym(lib, b"AFont_getCollectionIndex\0".as_ptr() as _)),
}
}
}
}
}
impl XFontType for XFont {
fn fallback(&self, text: &str, name: &str, weight: Weight, stretch: Stretch, style: Style) -> Fallback {
self.fallback_impl(text, name, weight, stretch, style).unwrap_or_default()
}
fn system() -> String {
"sans-serif".to_string()
}
}
impl XFont {
fn fallback_impl(&self, text: &str, name: &str, weight: Weight, _stretch: Stretch, style: Style) -> Option<Fallback> {
let text = if text.is_empty() { "a" } else { text };
unsafe {
let matcher = self.AFontMatcher_create?();
if matcher.is_null() {
return None;
}
let matcher = FontMatcher(matcher, self.AFontMatcher_destroy);
self.AFontMatcher_setStyle?(matcher.0, weight.0, matches!(style, Style::Italic));
let mut run_out = 0;
let family_name = CString::new(name).ok()?;
let mut text: Vec<u16> = text.encode_utf16().collect();
text.push(0);
let font = self.AFontMatcher_match?(matcher.0, family_name.as_ptr(), text.as_ptr(), (text.len() - 1) as _, &mut run_out);
if font.is_null() {
return None;
}
let font = Font(font, self.AFont_close);
let path = self.AFont_getFontFilePath?(font.0);
if path.is_null() {
return None;
}
let file = PathBuf::from(CStr::from_ptr(path).to_str().ok()?);
let index = self.AFont_getCollectionIndex?(font.0);
Some(Fallback {
index: Some(index as _),
unique: None,
file,
})
}
}
}
pub struct FontMatcher(*mut AFontMatcher, Option<extern "system" fn(matcher: *mut AFontMatcher)>);
impl Drop for FontMatcher {
fn drop(&mut self) {
if let Some(fun) = self.1 {
fun(self.0);
}
}
}
pub struct Font(*mut AFont, Option<extern "system" fn(font: *mut AFont)>);
impl Drop for Font {
fn drop(&mut self) {
if let Some(fun) = self.1 {
fun(self.0);
}
}
}
const RTLD_LAZY: core::ffi::c_int = 1; const RTLD_LOCAL: core::ffi::c_int = 0; #[link(name = "dl")]
extern "C" {
fn dlopen(filename: *const core::ffi::c_char, flags: core::ffi::c_int) -> *mut core::ffi::c_void;
fn dlclose(handle: *mut core::ffi::c_void) -> core::ffi::c_int;
fn dlsym(handle: *mut core::ffi::c_void, symbol: *const core::ffi::c_char) -> *mut core::ffi::c_void;
fn dlerror() -> *mut core::ffi::c_char;
}
pub const AFONT_WEIGHT_MIN: core::ffi::c_uint = 0;
pub const AFONT_WEIGHT_THIN: core::ffi::c_uint = 100;
pub const AFONT_WEIGHT_EXTRA_LIGHT: core::ffi::c_uint = 200;
pub const AFONT_WEIGHT_LIGHT: core::ffi::c_uint = 300;
pub const AFONT_WEIGHT_NORMAL: core::ffi::c_uint = 400;
pub const AFONT_WEIGHT_MEDIUM: core::ffi::c_uint = 500;
pub const AFONT_WEIGHT_SEMI_BOLD: core::ffi::c_uint = 600;
pub const AFONT_WEIGHT_BOLD: core::ffi::c_uint = 700;
pub const AFONT_WEIGHT_EXTRA_BOLD: core::ffi::c_uint = 800;
pub const AFONT_WEIGHT_BLACK: core::ffi::c_uint = 900;
pub const AFONT_WEIGHT_MAX: core::ffi::c_uint = 1000;
pub const AFAMILY_VARIANT_DEFAULT: core::ffi::c_uint = 0;
pub const AFAMILY_VARIANT_COMPACT: core::ffi::c_uint = 1;
pub const AFAMILY_VARIANT_ELEGANT: core::ffi::c_uint = 2;
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct AFont {
_unused: [u8; 0],
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct AFontMatcher {
_unused: [u8; 0],
}
#[repr(C)]
#[derive(Debug, Copy, Clone)]
pub struct ASystemFontIterator {
_unused: [u8; 0],
}