Repository navigation
Description
On Multisite, every site's custom fonts land in one shared folder, so a font installed on one site can affect PDFs on the others. Site admins should be able to install their own fonts into a folder that belongs to their site. Core fonts stay super admin only.
What happens today
Line references are to the refactor/unify-multisite-fonts branch (#2004).
- Custom font records are already per site (
custom_fontsingfpdf_settings), but the files all go totemplate_font_location, which is install-wide. - The upload routes only check
gravityforms_edit_forms(Controller_Custom_Fonts::check_permissions()). There's no Multisite gate on custom fonts, so a site admin with that capability writes to the shared folder today. Model_PDF::add_unregistered_fonts_to_mPDF()globs the shared folder and registers any.ttfit finds. A font uploaded on site A becomes available to mPDF on site B, even though it isn't in site B's Font Manager.Model_Custom_Fonts::get_unique_id()only checks the current site's fonts, so two sites can both have a font with the IDmyfont. mPDF'sttfontdatacache is keyed on the font ID and only revalidated on file size (Mpdf.php:3595,:3627), and it's shared as well. Files of the same size reuse each other's metrics, and files of different sizes regenerate the cache each time the other site renders.FlushCache::flush()for one site's font also removes the cache entry for another site's font with the same ID.
Proposal
Store each site's custom fonts in {multisite_template_location}fonts/ (PDF_EDIT_TEMPLATES/{blog_id}/fonts/ by default).
- Add
gfpdf_multisite_font_locationand a per-blog variant, matching thegfpdf_multisite_template_locationfilters. - On Multisite, pass the site folder to
Controller_Custom_Fontsfrombootstrap.php. It already takes the path as a string, so the constructor doesn't change. - Add the site folder to mPDF's
fontDirahead of the shared folder (Helper_PDF.php:763). - Include the site folder in
add_unregistered_fonts_to_mPDF(), and inHelper_Misc::get_managed_folders()so it gets created and checked. - Give each site its own font metrics cache, and have
FlushCacheclear only that site's entries. - Delete the folder on
wp_uninitialize_site, the same hookController_Pdf_Cacheuses, and on plugin uninstall.
Core fonts don't change. They stay in the shared folder, and Model_Font_Downloader::can_change_core_fonts() keeps install and delete super admin only in the Font Manager. A custom font must still not reuse a core font's ID (matches_core_font_id()).
Open points
- Existing fonts: records store absolute paths, but rendering uses the basename, so fonts already in the shared folder keep working if that folder stays in
fontDir. The simplest option is to leave them there and only send new uploads to the site folder. A file can't safely move without knowing that no other site references it. - The auto-load glob: should the shared folder still register unlisted
.ttffiles on every site? Keeping it preserves the ability to drop fonts into the folder by hand, but any custom font already there stays visible to every site until it's moved. - Storage quota:
Helper_Misc::get_upload_details()switches to blog 1, so these folders sit in the main site's uploads and don't count towards a site's quota. With site admins uploading by design, networks may want a limit. - Network control: should a super admin be able to turn off site-level font installs?
gravityforms_edit_formsis the only gate today. - Add-ons and the API:
GPDFAPI::add_pdf_font()needs to follow the same location rules.
Depends on #2004, which makes the core font state install-wide. #2018 changes how font usage is stored, and custom fonts remain per site under both.
Activity
jakejackson1 commentedon Nov 9, 2026
Upgrade routine to copy subsite fonts to new directory.
gravitypdf-bot commentedon Nov 10, 2026
The upgrade routine can reuse
Model_Custom_Fonts::get_files_used_by_other_sites()from #2012 (not merged yet) to decide when a copied font's original can be deleted from the shared folder. It lists the file names other sites' custom font records use, reading each site's options table withoutswitch_to_blog(), and it answers the "no other site references it" question in the open points above.Two things to handle when using it here:
{blog_id}/fonts/Foo.ttf, it still counts as using the sharedFoo.ttf, so the original would never be deleted. The migration needs a variant that compares against the shared folder's path, or that ignores records already moved.Model_Custom_Fonts::flush()in between.Keep each Multisite site's custom fonts to itself
Keep each Multisite site's custom fonts to itself
Keep each Multisite site's custom fonts to itself
Keep each Multisite site's custom fonts to itself