Skip to content

Commit

Permalink
switch to yeslogic-fontconfig-sys from servo-fontconfig
Browse files Browse the repository at this point in the history
This allows setting the RUST_FONTCONFIG_DLOPEN environment variable
to dlopen fontconfig at runtime rather than linking it at build
time. This is helpful for cross compiling to Linux, particularly
because fontconfig has lots of C dependencies. Building a vendored
copy of fontconfig does not work as expected:
slint-ui#88
  • Loading branch information
Be-ing committed Apr 6, 2022
1 parent 206b765 commit 2d7648f
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 36 deletions.
15 changes: 14 additions & 1 deletion examples/plotter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,24 @@ name = "plotter"

[dependencies]
slint = { path = "../../api/rs/slint" }
plotters = { version = "0.3.1", default-features = false, features = ["bitmap_backend", "surface_series"] }
# TODO: switch to upstream plotters when https://github.com/38/plotters/pull/337 is merged
plotters = { git = "https://github.com/Be-ing/plotters.git", branch = "font-kit-update", default-features = false, features = ["bitmap_backend", "surface_series"] }

[build-dependencies]
slint-build = { path = "../../api/rs/build" }

[features]
# This is required so plotters enables font-kit's source-fontconfig-dlopen feature
# when building the workspace with `cargo build --workspace --all-features` on CI. This is because
# `--workspace --all-features` enables i-slint-backend-gl and i-slint-compiler's fontconfig-dlopen
# features. Without this, font-kit would fail to build because Cargo would activate the dlopen
# feature of yeslogic-fontconfig-sys but not font-kit's source-fontconfig-dlopen feature.
#
# If you want to enable this feature apart from `--workspace --all-features`, set the environment
# variable RUST_FONTCONFIG_DLOPEN=on to enable the fontconfig-dlopen features of
# i-slint-backend-gl and i-slint-compiler.
fontconfig-dlopen = ["plotters/fontconfig-dlopen"]

# Remove the `#wasm#` to uncomment the wasm build.
# This is commented out by default because we don't want to build it as a library by default
# The CI has a script that does sed "s/#wasm# //" to generate the wasm build.
Expand Down
8 changes: 5 additions & 3 deletions internal/backends/gl/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ path = "lib.rs"
svg = ["resvg", "usvg", "tiny-skia"]
wayland = ["winit/wayland", "glutin/wayland", "copypasta/wayland"]
x11 = ["winit/x11", "glutin/x11", "copypasta/x11"]
# dlopen fontconfig at runtime rather than link at build time.
# useful for cross compiling
# Using a vendored fontconfig C library does not work well, refer to Issue #88.
fontconfig-dlopen = [ "font-kit/source-fontconfig-dlopen", "yeslogic-fontconfig-sys/dlopen" ]

rtti = ["i-slint-core/rtti"]

Expand Down Expand Up @@ -70,9 +74,7 @@ winapi = { version = "0.3", features = ["dwrite"] }

[target.'cfg(not(any(target_family = "windows", target_os = "macos", target_os = "ios", target_arch = "wasm32")))'.dependencies]
libc = { version = "0.2" }
# Require font-config from the system on Linux. Issue #88 indicates that the copy provided by servo-fontconfig may be incompatible
# with distros at times.
servo-fontconfig = { version = "0.5", features = [ "force_system_lib" ] }
yeslogic-fontconfig-sys = "3.0.0"

[target.'cfg(target_os = "macos")'.dependencies]
cocoa = { version = "0.24.0" }
Expand Down
10 changes: 10 additions & 0 deletions internal/backends/gl/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Copyright © SixtyFPS GmbH <[email protected]>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial

fn main() {
println!("cargo:rerun-if-env-changed=RUST_FONTCONFIG_DLOPEN");
let dlopen = std::env::var("RUST_FONTCONFIG_DLOPEN").is_ok();
if dlopen {
println!("cargo:rustc-cfg=feature=\"fontconfig-dlopen\"");
}
}
57 changes: 43 additions & 14 deletions internal/backends/gl/fonts/fontconfig.rs
Original file line number Diff line number Diff line change
@@ -1,29 +1,58 @@
// Copyright © SixtyFPS GmbH <[email protected]>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial

use fontconfig::fontconfig;
use fontconfig_sys as ffi;
use fontconfig_sys::ffi_dispatch;

#[cfg(feature = "fontconfig-dlopen")]
use ffi::statics::LIB;
#[cfg(not(feature = "fontconfig-dlopen"))]
use ffi::*;

// This is duplicated in the slint-compiler's glyph embedding code
pub fn find_families(requested_family: &str) -> Vec<String> {
unsafe {
let config = fontconfig::FcInitLoadConfigAndFonts();
let config = ffi_dispatch!(feature = "fontconfig-dlopen", LIB, FcInitLoadConfigAndFonts,);
let family_cstr = std::ffi::CString::new(requested_family).unwrap();
let pattern = fontconfig::FcNameParse(family_cstr.as_ptr() as *mut libc::c_uchar);
fontconfig::FcConfigSubstitute(std::ptr::null_mut(), pattern, fontconfig::FcMatchPattern);
fontconfig::FcDefaultSubstitute(pattern);
let mut sort_result = fontconfig::FcResultMatch;
let result_set =
fontconfig::FcFontSort(config, pattern, 1, std::ptr::null_mut(), &mut sort_result);
let pattern = ffi_dispatch!(
feature = "fontconfig-dlopen",
LIB,
FcNameParse,
family_cstr.as_ptr() as *mut libc::c_uchar
);
ffi_dispatch!(
feature = "fontconfig-dlopen",
LIB,
FcConfigSubstitute,
std::ptr::null_mut(),
pattern,
ffi::FcMatchPattern
);
ffi_dispatch!(feature = "fontconfig-dlopen", LIB, FcDefaultSubstitute, pattern);
let mut sort_result = ffi::FcResultMatch;
let result_set = ffi_dispatch!(
feature = "fontconfig-dlopen",
LIB,
FcFontSort,
config,
pattern,
1,
std::ptr::null_mut(),
&mut sort_result
);

let mut families = Vec::new();
for idx in 0..(*result_set).nfont {
let mut raw_family_name = std::ptr::null_mut();
if fontconfig::FcPatternGetString(
if ffi_dispatch!(
feature = "fontconfig-dlopen",
LIB,
FcPatternGetString,
*(*result_set).fonts.offset(idx as isize),
b"family\0".as_ptr() as *const libc::c_char,
0,
&mut raw_family_name,
) != fontconfig::FcResultMatch
&mut raw_family_name
) != ffi::FcResultMatch
{
continue;
}
Expand All @@ -41,9 +70,9 @@ pub fn find_families(requested_family: &str) -> Vec<String> {
}
}

fontconfig::FcFontSetDestroy(result_set);
fontconfig::FcPatternDestroy(pattern);
fontconfig::FcConfigDestroy(config);
ffi_dispatch!(feature = "fontconfig-dlopen", LIB, FcFontSetDestroy, result_set);
ffi_dispatch!(feature = "fontconfig-dlopen", LIB, FcPatternDestroy, pattern);
ffi_dispatch!(feature = "fontconfig-dlopen", LIB, FcConfigDestroy, config);
families
}
}
8 changes: 5 additions & 3 deletions internal/compiler/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ path = "lib.rs"
# Generators
cpp = []
rust = ["quote", "proc-macro2"]
# dlopen fontconfig at runtime rather than link at build time.
# useful for cross compiling
# Using a vendored fontconfig C library does not work well, refer to Issue #88.
fontconfig-dlopen = [ "yeslogic-fontconfig-sys/dlopen" ]

# Support for proc_macro spans in the token (only useful for use within a proc macro)
proc_macro_span = ["quote", "proc-macro2"]
Expand Down Expand Up @@ -61,9 +65,7 @@ usvg = "0.22"

[target.'cfg(not(any(target_family = "windows", target_os = "macos", target_os = "ios", target_arch = "wasm32")))'.dependencies]
libc = { version = "0.2" }
# Require font-config from the system on Linux. Issue #88 indicates that the copy provided by servo-fontconfig may be incompatible
# with distros at times.
servo-fontconfig = { version = "0.5", features = [ "force_system_lib" ] }
yeslogic-fontconfig-sys = "3.0.0"

[dev-dependencies]
i-slint-parser-test-macro = { path = "./parser-test-macro" }
Expand Down
6 changes: 6 additions & 0 deletions internal/compiler/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ use std::io::Write;
use std::path::{Path, PathBuf};

fn main() -> std::io::Result<()> {
println!("cargo:rerun-if-env-changed=RUST_FONTCONFIG_DLOPEN");
let dlopen = std::env::var("RUST_FONTCONFIG_DLOPEN").is_ok();
if dlopen {
println!("cargo:rustc-cfg=feature=\"fontconfig-dlopen\"");
}

let mut library_dir = PathBuf::from(std::env::var_os("CARGO_MANIFEST_DIR").unwrap());
library_dir.push("widgets");

Expand Down
59 changes: 44 additions & 15 deletions internal/compiler/passes/embed_glyphs/fontconfig.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,59 @@
// Copyright © SixtyFPS GmbH <[email protected]>
// SPDX-License-Identifier: GPL-3.0-only OR LicenseRef-Slint-commercial

use fontconfig::fontconfig;
use fontconfig_sys as ffi;
use fontconfig_sys::ffi_dispatch;

// This is duplicated from the GL backend
#[cfg(feature = "fontconfig-dlopen")]
use ffi::statics::LIB;
#[cfg(not(feature = "fontconfig-dlopen"))]
use ffi::*;

// This is duplicated in the slint-compiler's glyph embedding code
pub fn find_families(requested_family: &str) -> Vec<String> {
#[allow(unsafe_code)]
unsafe {
let config = fontconfig::FcInitLoadConfigAndFonts();
let config = ffi_dispatch!(feature = "fontconfig-dlopen", LIB, FcInitLoadConfigAndFonts,);
let family_cstr = std::ffi::CString::new(requested_family).unwrap();
let pattern = fontconfig::FcNameParse(family_cstr.as_ptr() as *mut libc::c_uchar);
fontconfig::FcConfigSubstitute(std::ptr::null_mut(), pattern, fontconfig::FcMatchPattern);
fontconfig::FcDefaultSubstitute(pattern);
let mut sort_result = fontconfig::FcResultMatch;
let result_set =
fontconfig::FcFontSort(config, pattern, 1, std::ptr::null_mut(), &mut sort_result);
let pattern = ffi_dispatch!(
feature = "fontconfig-dlopen",
LIB,
FcNameParse,
family_cstr.as_ptr() as *mut libc::c_uchar
);
ffi_dispatch!(
feature = "fontconfig-dlopen",
LIB,
FcConfigSubstitute,
std::ptr::null_mut(),
pattern,
ffi::FcMatchPattern
);
ffi_dispatch!(feature = "fontconfig-dlopen", LIB, FcDefaultSubstitute, pattern);
let mut sort_result = ffi::FcResultMatch;
let result_set = ffi_dispatch!(
feature = "fontconfig-dlopen",
LIB,
FcFontSort,
config,
pattern,
1,
std::ptr::null_mut(),
&mut sort_result
);

let mut families = Vec::new();
for idx in 0..(*result_set).nfont {
let mut raw_family_name = std::ptr::null_mut();
if fontconfig::FcPatternGetString(
if ffi_dispatch!(
feature = "fontconfig-dlopen",
LIB,
FcPatternGetString,
*(*result_set).fonts.offset(idx as isize),
b"family\0".as_ptr() as *const libc::c_char,
0,
&mut raw_family_name,
) != fontconfig::FcResultMatch
&mut raw_family_name
) != ffi::FcResultMatch
{
continue;
}
Expand All @@ -42,9 +71,9 @@ pub fn find_families(requested_family: &str) -> Vec<String> {
}
}

fontconfig::FcFontSetDestroy(result_set);
fontconfig::FcPatternDestroy(pattern);
fontconfig::FcConfigDestroy(config);
ffi_dispatch!(feature = "fontconfig-dlopen", LIB, FcFontSetDestroy, result_set);
ffi_dispatch!(feature = "fontconfig-dlopen", LIB, FcPatternDestroy, pattern);
ffi_dispatch!(feature = "fontconfig-dlopen", LIB, FcConfigDestroy, config);
families
}
}

0 comments on commit 2d7648f

Please sign in to comment.