Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add font-fallback on OpenHarmony and fix several compilation issues #32141

Merged
merged 6 commits into from
May 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 2 additions & 2 deletions components/allocator/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ path = "lib.rs"
[features]
use-system-allocator = ["libc"]

[target.'cfg(not(any(windows, target_os = "android")))'.dependencies]
[target.'cfg(not(any(windows, target_os = "android", target_env = "ohos")))'.dependencies]
jemallocator = { workspace = true }
jemalloc-sys = { workspace = true }
libc = { workspace = true, optional = true }

[target.'cfg(windows)'.dependencies]
winapi = { workspace = true, features = ["heapapi"] }

[target.'cfg(target_os = "android")'.dependencies]
[target.'cfg(any(target_os = "android", target_env = "ohos"))'.dependencies]
libc = { workspace = true }
13 changes: 11 additions & 2 deletions components/allocator/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ static ALLOC: Allocator = Allocator;

pub use crate::platform::*;

#[cfg(not(any(windows, target_os = "android", feature = "use-system-allocator")))]
#[cfg(not(any(
windows,
target_os = "android",
feature = "use-system-allocator",
target_env = "ohos"
)))]
mod platform {
use std::os::raw::c_void;

Expand All @@ -32,7 +37,11 @@ mod platform {

#[cfg(all(
not(windows),
any(target_os = "android", feature = "use-system-allocator")
any(
target_os = "android",
feature = "use-system-allocator",
target_env = "ohos"
)
))]
mod platform {
pub use std::alloc::System as Allocator;
Expand Down
8 changes: 6 additions & 2 deletions components/gfx/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ euclid = { workspace = true }
fnv = { workspace = true }
fontsan = { git = "https://github.com/servo/fontsan" }
gfx_traits = { workspace = true }
harfbuzz-sys = "0.6"
harfbuzz-sys = "0.6.1"
ipc-channel = { workspace = true }
lazy_static = { workspace = true }
libc = { workspace = true }
Expand Down Expand Up @@ -55,12 +55,16 @@ harfbuzz-sys = { version = "0.6", features = ["bundled"] }
freetype = "0.7"
servo_allocator = { path = "../allocator" }

[target.'cfg(target_os = "linux")'.dependencies]
[target.'cfg(all(target_os = "linux", not(target_env = "ohos")))'.dependencies]
fontconfig_sys = { package = "yeslogic-fontconfig-sys", version = "5" }

[target.'cfg(target_os = "android")'.dependencies]
xml-rs = "0.8"

[target.'cfg(target_env = "ohos")'.dependencies]
harfbuzz-sys = { version = "0.6.1", features = ["bundled"] }


[target.'cfg(target_os = "windows")'.dependencies]
harfbuzz-sys = { version = "0.6", features = ["bundled"] }
dwrote = "0.11"
Expand Down
241 changes: 241 additions & 0 deletions components/gfx/platform/freetype/ohos/font_list.rs
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Perhaps pay special attention to this file. It's based on an earlier version of android/font_list.rs, which received some updates in the recent days.
My current approach was to just hardcode one version of the HarmonyOS Sans font family available on ohos devices and update the list of fallback families for codepoints.
There are quite a few HarmonyOS_Sans_*.ttf files available on ohos devices. I chose one with the largest filesize, and that seemed to work well in practice.

There is also a fontconfig.json available, which could be parsed (for an example see https://github.com/jschwe/ohos-fontconfig/blob/main/src/lib.rs)

Original file line number Diff line number Diff line change
@@ -0,0 +1,241 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */

use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};

use log::warn;
use serde::{Deserialize, Serialize};
use style::values::computed::{
FontStretch as StyleFontStretch, FontStyle as StyleFontStyle, FontWeight as StyleFontWeight,
};
use style::Atom;
use ucd::{Codepoint, UnicodeBlock};
use webrender_api::NativeFontHandle;

use crate::font_template::{FontTemplate, FontTemplateDescriptor};
use crate::text::util::is_cjk;

lazy_static::lazy_static! {
static ref FONT_LIST: FontList = FontList::new();
}

/// An identifier for a local font on OpenHarmony systems.
#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
pub struct LocalFontIdentifier {
/// The path to the font.
pub path: Atom,
}

impl LocalFontIdentifier {
pub(crate) fn index(&self) -> u32 {
0
}

pub(crate) fn read_data_from_file(&self) -> Vec<u8> {
let mut bytes = Vec::new();
File::open(Path::new(&*self.path))
.expect("Couldn't open font file!")
.read_to_end(&mut bytes)
.unwrap();
bytes
}
}

struct Font {
filename: String,
weight: Option<i32>,
style: Option<String>,
}

struct FontFamily {
name: String,
fonts: Vec<Font>,
}

struct FontAlias {
from: String,
to: String,
weight: Option<i32>,
}

struct FontList {
families: Vec<FontFamily>,
aliases: Vec<FontAlias>,
}

impl FontList {
fn new() -> FontList {
// We don't support parsing `/system/etc/fontconfig.json` yet.
FontList {
families: Self::fallback_font_families(),
aliases: Vec::new(),
}
}

// Fonts expected to exist in OpenHarmony devices.
// Used until parsing of the fontconfig.json file is implemented.
fn fallback_font_families() -> Vec<FontFamily> {
let alternatives = [
("HarmonyOS Sans", "HarmonyOS_Sans_SC_Regular.ttf"),
("sans-serif", "HarmonyOS_Sans_SC_Regular.ttf"),
];

alternatives
.iter()
.filter(|item| Path::new(&Self::font_absolute_path(item.1)).exists())
.map(|item| FontFamily {
name: item.0.into(),
fonts: vec![Font {
filename: item.1.into(),
weight: None,
style: None,
}],
})
.collect()
}

// OHOS fonts are located in /system/fonts
fn font_absolute_path(filename: &str) -> String {
if filename.starts_with("/") {
String::from(filename)
} else {
format!("/system/fonts/{}", filename)
}
}

fn find_family(&self, name: &str) -> Option<&FontFamily> {
self.families.iter().find(|f| f.name == name)
}

fn find_alias(&self, name: &str) -> Option<&FontAlias> {
self.aliases.iter().find(|f| f.from == name)
}
}

// Functions used by FontCacheThread
pub fn for_each_available_family<F>(mut callback: F)
where
F: FnMut(String),
{
for family in &FONT_LIST.families {
callback(family.name.clone());
}
for alias in &FONT_LIST.aliases {
callback(alias.from.clone());
}
}

pub fn for_each_variation<F>(family_name: &str, mut callback: F)
where
F: FnMut(FontTemplate),
{
let mut produce_font = |font: &Font| {
let local_font_identifier = LocalFontIdentifier {
path: Atom::from(FontList::font_absolute_path(&font.filename)),
};
let stretch = StyleFontStretch::NORMAL;
let weight = font
.weight
.map(|w| StyleFontWeight::from_float(w as f32))
.unwrap_or(StyleFontWeight::NORMAL);
let style = match font.style.as_deref() {
Some("italic") => StyleFontStyle::ITALIC,
Some("normal") => StyleFontStyle::NORMAL,
Some(value) => {
warn!(
"unknown value \"{value}\" for \"style\" attribute in the font {}",
font.filename
);
StyleFontStyle::NORMAL
},
None => StyleFontStyle::NORMAL,
};
let descriptor = FontTemplateDescriptor {
weight,
stretch,
style,
};
callback(FontTemplate::new_local(local_font_identifier, descriptor));
};

if let Some(family) = FONT_LIST.find_family(family_name) {
for font in &family.fonts {
produce_font(font);
}
return;
}

if let Some(alias) = FONT_LIST.find_alias(family_name) {
if let Some(family) = FONT_LIST.find_family(&alias.to) {
for font in &family.fonts {
match (alias.weight, font.weight) {
(None, _) => produce_font(font),
(Some(w1), Some(w2)) if w1 == w2 => produce_font(font),
_ => {},
}
}
}
}
}

pub fn system_default_family(generic_name: &str) -> Option<String> {
if let Some(family) = FONT_LIST.find_family(&generic_name) {
Some(family.name.clone())
} else if let Some(alias) = FONT_LIST.find_alias(&generic_name) {
Some(alias.from.clone())
} else {
FONT_LIST.families.get(0).map(|family| family.name.clone())
}
}

// Based on fonts present in OpenHarmony.
pub fn fallback_font_families(codepoint: Option<char>) -> Vec<&'static str> {
let mut families = vec![];

if let Some(block) = codepoint.and_then(|c| c.block()) {
match block {
UnicodeBlock::Hebrew => {
families.push("Noto Sans Hebrew");
},

UnicodeBlock::Arabic => {
families.push("HarmonyOS Sans Naskh Arabic");
},

UnicodeBlock::Devanagari => {
families.push("Noto Sans Devanagari");
},

UnicodeBlock::Tamil => {
families.push("Noto Sans Tamil");
},

UnicodeBlock::Thai => {
families.push("Noto Sans Thai");
},

UnicodeBlock::Georgian | UnicodeBlock::GeorgianSupplement => {
families.push("Noto Sans Georgian");
},

UnicodeBlock::Ethiopic | UnicodeBlock::EthiopicSupplement => {
families.push("Noto Sans Ethiopic");
},

_ => {
if is_cjk(codepoint.unwrap()) {
families.push("Noto Sans JP");
families.push("Noto Sans KR");
}
},
}
}

families.push("HarmonyOS Sans");
families
}

pub static SANS_SERIF_FONT_FAMILY: &'static str = "HarmonyOS Sans";
8 changes: 7 additions & 1 deletion components/gfx/platform/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ mod freetype {

pub mod font;

#[cfg(target_os = "linux")]
#[cfg(all(target_os = "linux", not(target_env = "ohos")))]
pub mod font_list;
#[cfg(target_os = "android")]
mod android {
Expand All @@ -35,6 +35,12 @@ mod freetype {
}
#[cfg(target_os = "android")]
pub use self::android::font_list;
#[cfg(target_env = "ohos")]
mod ohos {
pub mod font_list;
}
#[cfg(target_env = "ohos")]
pub use self::ohos::font_list;

pub mod library_handle;
}
Expand Down
1 change: 1 addition & 0 deletions components/profile/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,5 @@ regex = { workspace = true }

[target.'cfg(not(any(target_os = "windows", target_os = "android")))'.dependencies]
libc = { workspace = true }
[target.'cfg(not(any(target_os = "windows", target_os = "android", target_env = "ohos")))'.dependencies]
jemalloc-sys = { workspace = true }
14 changes: 7 additions & 7 deletions components/profile/mem.rs
Original file line number Diff line number Diff line change
Expand Up @@ -387,16 +387,16 @@ impl ReportsForest {
//---------------------------------------------------------------------------

mod system_reporter {
#[cfg(not(any(target_os = "windows", target_os = "android")))]
#[cfg(not(any(target_os = "windows", target_os = "android", target_env = "ohos")))]
use std::ffi::CString;
#[cfg(not(any(target_os = "windows", target_os = "android")))]
#[cfg(not(any(target_os = "windows", target_os = "android", target_env = "ohos")))]
use std::mem::size_of;
#[cfg(not(any(target_os = "windows", target_os = "android")))]
#[cfg(not(any(target_os = "windows", target_os = "android", target_env = "ohos")))]
use std::ptr::null_mut;

#[cfg(target_os = "linux")]
use libc::c_int;
#[cfg(not(any(target_os = "windows", target_os = "android")))]
#[cfg(not(any(target_os = "windows", target_os = "android", target_env = "ohos")))]
use libc::{c_void, size_t};
use profile_traits::mem::{Report, ReportKind, ReporterRequest};
use profile_traits::path;
Expand Down Expand Up @@ -499,10 +499,10 @@ mod system_reporter {
None
}

#[cfg(not(any(target_os = "windows", target_os = "android")))]
#[cfg(not(any(target_os = "windows", target_os = "android", target_env = "ohos")))]
use jemalloc_sys::mallctl;

#[cfg(not(any(target_os = "windows", target_os = "android")))]
#[cfg(not(any(target_os = "windows", target_os = "android", target_env = "ohos")))]
fn jemalloc_stat(value_name: &str) -> Option<usize> {
// Before we request the measurement of interest, we first send an "epoch"
// request. Without that jemalloc gives cached statistics(!) which can be
Expand Down Expand Up @@ -549,7 +549,7 @@ mod system_reporter {
Some(value as usize)
}

#[cfg(any(target_os = "windows", target_os = "android"))]
#[cfg(any(target_os = "windows", target_os = "android", target_env = "ohos"))]
fn jemalloc_stat(_value_name: &str) -> Option<usize> {
None
}
Expand Down