Skip to content

Let Multisite site admins install fonts into a per-site folder #2019

Closed

Description

@gravitypdf-bot

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_fonts in gfpdf_settings), but the files all go to template_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 .ttf it 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 ID myfont. mPDF's ttfontdata cache 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_location and a per-blog variant, matching the gfpdf_multisite_template_location filters.
  • On Multisite, pass the site folder to Controller_Custom_Fonts from bootstrap.php. It already takes the path as a string, so the constructor doesn't change.
  • Add the site folder to mPDF's fontDir ahead of the shared folder (Helper_PDF.php:763).
  • Include the site folder in add_unregistered_fonts_to_mPDF(), and in Helper_Misc::get_managed_folders() so it gets created and checked.
  • Give each site its own font metrics cache, and have FlushCache clear only that site's entries.
  • Delete the folder on wp_uninitialize_site, the same hook Controller_Pdf_Cache uses, 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 .ttf files 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_forms is 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

  1. jakejackson1 commented on Nov 9, 2026

    @jakejackson1
    Member

    Upgrade routine to copy subsite fonts to new directory.

  2. gravitypdf-bot commented on Nov 10, 2026

    @gravitypdf-bot
    ContributorAuthor

    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 without switch_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:

    • It matches by basename. Once a site's record points at {blog_id}/fonts/Foo.ttf, it still counts as using the shared Foo.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.
    • It's memoised per request and per site. A batch that updates records and looks again in the same request needs Model_Custom_Fonts::flush() in between.
  3. added 2 commits that reference this issue on Nov 10, 2026
    6600131
    9bb0bef
  4. added 2 commits that reference this issue on Nov 10, 2026
    736474e
    7a02a8f
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions