Skip to content

Commit

Permalink
fix: add Mutex guard to GlobalFont
Browse files Browse the repository at this point in the history
  • Loading branch information
Brooooooklyn committed Sep 22, 2022
1 parent 8ece352 commit 7e0a51a
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 38 deletions.
12 changes: 8 additions & 4 deletions src/ctx.rs
Expand Up @@ -8,6 +8,7 @@ use cssparser::{Color as CSSColor, Parser, ParserInput, RGBA};
use libavif::AvifData;
use napi::{bindgen_prelude::*, JsBuffer, JsString, NapiRaw, NapiValue};

use crate::global_fonts::get_font;
use crate::{
avif::Config,
error::SkError,
Expand Down Expand Up @@ -666,6 +667,7 @@ impl Context {
let surface = &mut self.surface;
surface.save();
Self::apply_shadow_offset_matrix(surface, state.shadow_offset_x, state.shadow_offset_y)?;
let font = get_font()?;
surface.canvas.draw_text(
text,
x,
Expand All @@ -675,17 +677,18 @@ impl Context {
weight,
stretch as i32,
slant,
&*crate::global_fonts::GLOBAL_FONT_COLLECTION,
&*font,
state.font_style.size,
&state.font_style.family,
state.text_baseline,
state.text_align,
state.text_direction,
&shadow_paint,
)?;
mem::drop(font);
surface.restore();
}

let font = get_font()?;
self.surface.canvas.draw_text(
text,
x,
Expand All @@ -695,7 +698,7 @@ impl Context {
weight,
stretch as i32,
slant,
&*crate::global_fonts::GLOBAL_FONT_COLLECTION,
&*font,
state.font_style.size,
&state.font_style.family,
state.text_baseline,
Expand All @@ -712,9 +715,10 @@ impl Context {
let weight = state.font_style.weight;
let stretch = state.font_style.stretch;
let slant = state.font_style.style;
let font = get_font()?;
let line_metrics = LineMetrics(self.surface.canvas.get_line_metrics(
text,
&*crate::global_fonts::GLOBAL_FONT_COLLECTION,
&*font,
state.font_style.size,
weight,
stretch as i32,
Expand Down
6 changes: 6 additions & 0 deletions src/error.rs
Expand Up @@ -48,3 +48,9 @@ impl From<NulError> for SkError {
Self::NulError
}
}

impl<T> From<std::sync::PoisonError<T>> for SkError {
fn from(err: std::sync::PoisonError<T>) -> Self {
Self::Generic(format!("PoisonError {}", err))
}
}
55 changes: 36 additions & 19 deletions src/global_fonts.rs
@@ -1,5 +1,6 @@
use std::fs::read_dir;
use std::path;
use std::sync::{LockResult, Mutex, MutexGuard, PoisonError};

use once_cell::sync::{Lazy, OnceCell};

Expand All @@ -14,60 +15,75 @@ const FONT_PATH: &str = "/usr/share/fonts/";
#[cfg(target_os = "android")]
const FONT_PATH: &str = "/system/fonts";

static FONT_DIR: OnceCell<u32> = OnceCell::new();
static FONT_DIR: OnceCell<napi::Result<u32>> = OnceCell::new();

pub(crate) static GLOBAL_FONT_COLLECTION: Lazy<FontCollection> = Lazy::new(FontCollection::new);
pub(crate) static GLOBAL_FONT_COLLECTION: Lazy<Mutex<FontCollection>> =
Lazy::new(|| Mutex::new(FontCollection::new()));

#[inline]
pub(crate) fn get_font<'a>() -> LockResult<MutexGuard<'a, FontCollection>> {
GLOBAL_FONT_COLLECTION.lock()
}

#[inline]
fn into_napi_error(err: PoisonError<MutexGuard<'_, FontCollection>>) -> napi::Error {
napi::Error::new(napi::Status::GenericFailure, format!("{err}"))
}

#[napi]
#[allow(non_snake_case)]
pub mod GlobalFonts {
use napi::bindgen_prelude::*;

use super::{FONT_DIR, FONT_PATH, GLOBAL_FONT_COLLECTION};
use super::{get_font, into_napi_error, FONT_DIR, FONT_PATH};

#[napi]
pub fn register(font_data: Buffer, name_alias: Option<String>) -> bool {
pub fn register(font_data: Buffer, name_alias: Option<String>) -> Result<bool> {
let maybe_name_alias = name_alias.and_then(|s| if s.is_empty() { None } else { Some(s) });
GLOBAL_FONT_COLLECTION.register(font_data.as_ref(), maybe_name_alias)
let font = get_font().map_err(into_napi_error)?;
Ok(font.register(font_data.as_ref(), maybe_name_alias))
}

#[napi]
pub fn register_from_path(font_path: String, name_alias: Option<String>) -> bool {
pub fn register_from_path(font_path: String, name_alias: Option<String>) -> Result<bool> {
let maybe_name_alias = name_alias.and_then(|s| if s.is_empty() { None } else { Some(s) });
GLOBAL_FONT_COLLECTION.register_from_path(font_path.as_str(), maybe_name_alias)
let font = get_font().map_err(into_napi_error)?;
Ok(font.register_from_path(font_path.as_str(), maybe_name_alias))
}

#[napi]
pub fn get_families() -> Result<String> {
Ok(serde_json::to_string(
&GLOBAL_FONT_COLLECTION.get_families(),
)?)
let font = get_font().map_err(into_napi_error)?;
Ok(serde_json::to_string(&font.get_families())?)
}

#[napi]
pub fn load_system_fonts() -> u32 {
*FONT_DIR.get_or_init(move || super::load_fonts_from_dir(FONT_PATH))
pub fn load_system_fonts() -> Result<u32> {
FONT_DIR
.get_or_init(move || super::load_fonts_from_dir(FONT_PATH))
.clone()
}

#[napi]
pub fn load_fonts_from_dir(dir: String) -> u32 {
pub fn load_fonts_from_dir(dir: String) -> Result<u32> {
super::load_fonts_from_dir(dir.as_str())
}

#[napi]
pub fn set_alias(font_name: String, alias: String) {
GLOBAL_FONT_COLLECTION.set_alias(font_name.as_str(), alias.as_str());
pub fn set_alias(font_name: String, alias: String) -> Result<()> {
let font = get_font().map_err(into_napi_error)?;
font.set_alias(font_name.as_str(), alias.as_str());
Ok(())
}
}

fn load_fonts_from_dir<P: AsRef<path::Path>>(dir: P) -> u32 {
fn load_fonts_from_dir<P: AsRef<path::Path>>(dir: P) -> napi::Result<u32> {
let mut count = 0u32;
let font_collection = &*GLOBAL_FONT_COLLECTION;
if let Ok(dir) = read_dir(dir) {
for f in dir.flatten() {
if let Ok(meta) = f.metadata() {
if meta.is_dir() {
load_fonts_from_dir(f.path());
load_fonts_from_dir(f.path())?;
} else {
let p = f.path();
let ext = p.extension().and_then(|s| s.to_str());
Expand All @@ -76,6 +92,7 @@ fn load_fonts_from_dir<P: AsRef<path::Path>>(dir: P) -> u32 {
Some("ttf") | Some("ttc") | Some("otf") | Some("pfb") | Some("woff2")
| Some("woff") => {
if let Some(p) = p.into_os_string().to_str() {
let font_collection = get_font().map_err(into_napi_error)?;
if font_collection.register_from_path::<String>(p, None) {
count += 1;
}
Expand All @@ -87,5 +104,5 @@ fn load_fonts_from_dir<P: AsRef<path::Path>>(dir: P) -> u32 {
}
}
}
count
Ok(count)
}
28 changes: 13 additions & 15 deletions src/svg.rs
Expand Up @@ -2,27 +2,25 @@ use std::mem;

use napi::{bindgen_prelude::*, JsBuffer};

use crate::sk::sk_svg_text_to_path;
use crate::{error::SkError, global_fonts::get_font, sk::sk_svg_text_to_path};

#[napi(js_name = "convertSVGTextToPath")]
pub fn convert_svg_text_to_path(
env: Env,
input: Either3<Buffer, String, Unknown>,
) -> Result<JsBuffer> {
sk_svg_text_to_path(
input.as_bytes()?,
&*crate::global_fonts::GLOBAL_FONT_COLLECTION,
)
.ok_or_else(|| {
Error::new(
Status::InvalidArg,
"Convert svg text to path failed".to_owned(),
)
})
.and_then(|v| unsafe {
env.create_buffer_with_borrowed_data(v.0.ptr, v.0.size, v, |d, _| mem::drop(d))
})
.map(|b| b.into_raw())
let font = get_font().map_err(SkError::from)?;
sk_svg_text_to_path(input.as_bytes()?, &*font)
.ok_or_else(|| {
Error::new(
Status::InvalidArg,
"Convert svg text to path failed".to_owned(),
)
})
.and_then(|v| unsafe {
env.create_buffer_with_borrowed_data(v.0.ptr, v.0.size, v, |d, _| mem::drop(d))
})
.map(|b| b.into_raw())
}

trait AsBytes {
Expand Down

0 comments on commit 7e0a51a

Please sign in to comment.