Forum Feature requests

Enhanced Control Over FontConfig for Web Applications

adam447
I’d like to propose a feature to enhance user control over FontConfig, particularly to address potential limitations in web applications.

The goal is to allow users to upload and manage their own fonts, organization structured under their individual directories. While it's feasible to configure FontConfig by setting various environment variables (such as HOME, XDG_DATA_HOME, XDG_CACHE_HOME, and FONTCONFIG_PATH), there is a significant challenge in a web context: the risk of race conditions. When multiple requests are processed simultaneously, the environment variables set by one request can easily be overwritten by another, leading to inconsistencies and errors.

Having recently integrated FontConfig into a PHP C extension, I believe there's a straightforward solution. I propose the addition of new command-line options that would enable individual font and configuration directories without relying on environment variables. For instance:

  • --fontconfig--config-dir=/mnt/user/078123/fonts
  • --fontconfig--font-dir=/mnt/other/customfont

Allowing these options to be specified multiple times would enable users to add several configuration or font directories as needed.

The FontConfig library is relatively easy to work with, and I envision that, after parsing these inputs, we could effectively utilize functions such as:

  • FcBool FcConfigAddConfigDir(FcConfig *config, const FcChar8 *d)
  • FcBool FcConfigAddFontDir(FcConfig *config, const FcChar8 *d, const FcChar8 *m, const FcChar8 *salt)

Implementing this feature would significantly improve the flexibility and user experience of font management in web applications, reducing the likelihood of conflicts and race conditions. Thank you for considering this request!
mikeday
I'm curious about the use case for this, are you using a local instance of FontConfig and not the system FontConfig?
adam447
I'm using a system wide installation of FontConfig, as well as using the development libraries which a custom PHP C extension utilizes, in order to perform typical FontConfig actions like gathering font info, searching, clearing cache etc. The main purpose of the PHP Extension is text rendering, outputting to various formats. FontConfig plays a part in this.

A use-case for this feature request:

Imagine a web application, allowing users to upload and manage there own fonts. There's no problem here if designing for a single-tenant architecture. As in there's an environment VM, container etc for each user of the system.

But when developing for a multi-tenant architecture, you need a way to specify (and most likely limit) which fonts are available for a particular user. As mentioned, there's environment variables (such as HOME, XDG_DATA_HOME, XDG_CACHE_HOME, and FONTCONFIG_PATH) which can achieve this, but this is error prone when multiple requests are processed simultaneously.

My suggestion, in allowing FontConfig directories to be specified, solves these problems (no more passing around environment variables) and gives Prince users a bit more control in regards to font management.

Assuming you're already utilizing FontConfig, loading system "fonts.conf" files already, it's probably trivial to implement and I cannot see how this could break existing setups, as it would be an opt in feature. Let me know if anything is unclear, I hope you'll consider implementing something like this.

FcInit()
FcConfig *config = FcInitLoadConfig();
// Loop over each --fontconfig--config-dir
FcConfigAddConfigDir(config, (const FcChar8 *) dir);








mikeday
In the meantime the other approach is to link to local font files using @font-face rules in the CSS.
adam447
Okay I'll keep that in mind. Is this a feature you'll consider implementing? Or am I wrong in assuming this is something that can be easily added?
mikeday
We can add it to the development roadmap.
wangp
When multiple requests are processed simultaneously, the environment variables set by one request can easily be overwritten by another, leading to inconsistencies and errors.

That sounds like you expect to modify the environment in your server process each time you execute prince. Rather, you should call a function that creates a new process with an environment or list of environment variables to use for that new process only. proc_open() appears to be that function for PHP.

Or you could use the `env` utility to execute prince in a modified environment, without touching the environment of the parent process.

Furthermore, I would suggest using Linux namespaces to isolate users from each other. For example, `bwrap` would give you much more control over what files each prince process has access to, beyond just fontconfig.
adam447
I've been down the road of proc_open and environment variables, below is the .conf file I used to create a persistent "global" font directory and a "user" font directory (which can change per request). I'll revisit this if need be.

HOME=/tmp/user
XDG_DATA_HOME=/tmp/global
XDG_CACHE_HOME=/tmp/global
FONTCONFIG_PATH/tmp/global/fonts.conf


<?xml version="1.0"?>
<!DOCTYPE fontconfig SYSTEM "fonts.dtd">
<fontconfig>
    <!-- User font directory -->
    <dir>~/fonts</dir>
    <!-- Global font directory -->
    <dir prefix="xdg">fonts</dir>
    <!-- User configuration override -->
    <include ignore_missing="yes">~/fonts.conf</include>
</fontconfig>


While this kind of works, I was able to create an easier and less restrictive solution with only a few lines of code.

config = FcInitLoadConfigAndFonts();

if (config_files != NULL) {
    for (size_t i = 0; i < config_files_count; i++) {
        const char *file = config_files[i];
        FcConfigParseAndLoad(config, (const FcChar8 *) file, FcFalse);
    }
}

if (font_directories != NULL) {
    for (size_t i = 0; i < font_directories_count; i++) {
        const char *directory = font_directories[i];
        FcConfigAppFontAddDir(config, (const FcChar8 *) directory);
    }
}

FcConfigBuildFonts(config)


Below is what gets exposed as client-side PHP, very similar to my initial proposal (Prince providing some additional CLI arguments). Where any number of .conf files or directories can be defined, without being limited by the environment variables FontConfig supports or a particular directory structure.

$fontConfig = new FontConfig();
$fontConfig->addConfigFile('/tmp/user/fonts.conf');
$fontConfig->addConfigFile('/tmp/global/fonts.conf');
$fontConfig->addFontDirectory('/tmp/other/fonts');

Edited by adam447