", {"class": "working"});
const doFetchP = async (url) => {
try {
const x = await fetchJsonp(url);
return x.json();
- } catch (e) {console.log(e)}
+ } catch (e) {
+ $working.remove();
+ alert(e);
+ console.log(e)
+ }
}
const doFetch = async (url) => {
try {
const x = await fetch(url);
const json = await x.json();
return json;
- } catch (e) {console.log(e)}
+ } catch (e) {
+ $working.remove();
+ alert(e);
+ console.log(e)
+ }
}
- const $working = $("
", {"class": "working"});
$("body").append($working);
const form = $(event.target);
const currentMeetingList = form.children('input[name="current-meeting-list"]').val();
From 50c14fbda294f380867774a968ea2bd0972511c0 Mon Sep 17 00:00:00 2001
From: otrok7 <50595291+otrok7@users.noreply.github.com>
Date: Wed, 10 Jun 2026 12:28:15 +0200
Subject: [PATCH 18/34] Allow custom font extensions
---
admin/class-bread-admin.php | 38 ++---------
admin/css/admin-fonts.css | 24 -------
admin/partials/_layout_setup.php | 12 ++--
includes/class-bread.php | 10 ++-
public/class-bread-public.php | 107 ++++++++++++++++++-------------
5 files changed, 86 insertions(+), 105 deletions(-)
delete mode 100644 admin/css/admin-fonts.css
diff --git a/admin/class-bread-admin.php b/admin/class-bread-admin.php
index 93febba7..dc20d101 100644
--- a/admin/class-bread-admin.php
+++ b/admin/class-bread-admin.php
@@ -74,7 +74,6 @@ public function enqueue_styles($hook)
wp_enqueue_style("tooltipster", plugin_dir_url(__FILE__) . "css/tooltipster.bundle.min.css", false, BREAD_VERSION, 'all');
wp_enqueue_style("tooltipster-noir", plugin_dir_url(__FILE__) . "css/tooltipster-sideTip-noir.min.css", false, BREAD_VERSION, 'all');
wp_enqueue_style("admin", plugin_dir_url(__FILE__) . "css/admin.css", false, BREAD_VERSION, 'all');
- wp_enqueue_style("bread-fonts", plugin_dir_url(__FILE__) . "css/admin-fonts.css", false, BREAD_VERSION, 'all');
wp_enqueue_style("select2", plugin_dir_url(__FILE__) . "css/select2.min.css", false, BREAD_VERSION, 'all');
wp_enqueue_style("smartWizard-dots", plugin_dir_url(__FILE__) . "css/smart_wizard_dots.css", false, BREAD_VERSION, 'all');
}
@@ -204,46 +203,23 @@ function tiny_tweaks($initArray)
{
if (function_exists('get_current_screen')) {
$screen = get_current_screen();
+ $fonts = apply_filters('bread_custom_fonts', $this->bread->fonts);
if ($screen != null && str_ends_with($screen->id, $this->bmltEnabled_admin->getSlug())) {
$initArray['fontsize_formats'] = "5pt 6pt 7pt 8pt 9pt 10pt 11pt 12pt 13pt 14pt 15pt 16pt 17pt 18pt 19pt 20pt 22pt 24pt 26pt 28pt 30pt 32pt 34pt 36pt 38pt";
$initArray['theme_advanced_blockformats'] = 'h2,h3,h4,p';
$initArray['wordpress_adv_hidden'] = false;
- $initArray['font_formats'] = 'Arial (Sans-Serif)=arial;';
- $initArray['font_formats'] .= 'Times (Sans-Serif)=times;';
- $initArray['font_formats'] .= 'Courier (Monospace)=courier;';
- $initArray['font_formats'] .= 'DejaVu (Sans-Serif)=DejaVuSansCondensed;';
+ $initArray['font_formats'] = '';
+ foreach ($fonts as $font_key => $font_name) {
+ $initArray['font_formats'] .= "$font_name=$font_key;";
+ }
$dir = plugin_dir_url(__FILE__);
$font = $this->bread->getOption('base_font');
- if ($font == 'dejavusanscondensed') {
- $initArray['content_style'] = "@import url('$dir/css/fonts.css'); body { font-family: DejaVuSansCondensed; }";
- } elseif ($font == 'times') {
- $initArray['content_style'] = "@import url('$dir/css/fonts.css'); body { font-family: Times; }";
- } elseif ($font == 'courier') {
- $initArray['content_style'] = "@import url('$dir/css/fonts.css'); body { font-family: Courier; }";
- } else {
- $initArray['content_style'] = "@import url('$dir/css/fonts.css'); body { font-family: Arial; }";
- }
- //$initArray['content_style'] = "body { font-family: Arial; }";
+ $initArray['content_style'] = apply_filters('bread_content_style', "@import url('$dir/css/fonts.css');");
+ $initArray['content_style'] .= "body { font-family: $font; }";
}
}
return $initArray;
}
- function is_root_server_missing()
- {
- if (!function_exists('get_current_screen')) {
- return;
- }
- $screen = get_current_screen();
- if ($screen != null && str_ends_with($screen->id, $this->bmltEnabled_admin->getSlug())) {
- $root_server = $this->bread->getOption('root_server');
- if ($root_server == '') {
- echo '
Missing BMLT Server in settings for bread.
';
- $url = admin_url('options-general.php?page=class-bread-admin.php');
- echo "
Settings
";
- echo '
';
- }
- }
- }
function pwsix_process_rename_settings()
{
diff --git a/admin/css/admin-fonts.css b/admin/css/admin-fonts.css
deleted file mode 100644
index 93686b8f..00000000
--- a/admin/css/admin-fonts.css
+++ /dev/null
@@ -1,24 +0,0 @@
-@font-face {
- font-family:'DejaVuSansCondensed';
- src:url(../../vendor/mpdf/mpdf/ttfonts/DejaVuSansCondensed.ttf) format('truetype');
- font-display: block;
-}
-@font-face {
- font-family:'DejaVuSansCondensed';
- src:url(../../vendor/mpdf/mpdf/ttfonts/DejaVuSansCondensed-Bold.ttf) format('truetype');
- font-display: block;
- font-weight: bold;
-}
-@font-face {
- font-family:'DejaVuSansCondensed';
- src:url(../../vendor/mpdf/mpdf/ttfonts/DejaVuSansCondensed-BoldOblique.ttf) format('truetype');
- font-display: block;
- font-weight: bold;
- font-style: italic;
-}
-@font-face {
- font-family:'DejaVuSansCondensed';
- src:url(../../vendor/mpdf/mpdf/ttfonts/DejaVuSansCondensed-Oblique.ttf) format('truetype');
- font-display: block;
- font-style: italic;
-}
\ No newline at end of file
diff --git a/admin/partials/_layout_setup.php b/admin/partials/_layout_setup.php
index 7f8659b3..f8a8662f 100644
--- a/admin/partials/_layout_setup.php
+++ b/admin/partials/_layout_setup.php
@@ -2,6 +2,13 @@
if (! defined('ABSPATH')) {
exit;
}
+function echo_font_options($base_font, $bread)
+{
+ $fonts = apply_filters('bread_custom_fonts', $bread->fonts);
+ foreach ($fonts as $font_key => $font_name) {
+ echo '
' . esc_html($font_name) . ' ';
+ }
+}
function Bread_layout_setup_page_render(Bread_AdminDisplay $breadAdmin)
{
$bread = $breadAdmin->getBreadInstance();
@@ -152,10 +159,7 @@ function Bread_layout_setup_page_render(Bread_AdminDisplay $breadAdmin)
- getOption('base_font') == 'dejavusanscondensed' ? "selected=\"selected\"" : "" ?>>
- getOption('base_font') == 'courier' ? "selected=\"selected\"" : "" ?>>
- getOption('base_font') == 'times' ? "selected=\"selected\"" : "" ?>>
- getOption('base_font') == 'arial' ? "selected=\"selected\"" : "" ?>>
+ getOption('base_font'), $bread); ?>
diff --git a/includes/class-bread.php b/includes/class-bread.php
index 1d63ba50..9f327506 100644
--- a/includes/class-bread.php
+++ b/includes/class-bread.php
@@ -56,6 +56,7 @@ class Bread
private string $optionsName;
private $allSettings = array();
private $maxSetting = 1;
+ public$fonts = [];
/**
* The setting we are editing, generating or otherwise working with. Generally set with query string "?current-meeting-list".
*
@@ -385,6 +386,14 @@ public function __construct()
$this->load_translations();
$this->define_admin_hooks();
$this->define_public_hooks();
+
+ $this->fonts = [
+ 'dejavusanscondensed' => __('DejaVu Sans Condensed', 'bread'),
+ 'ccourier' => __('Courier', 'bread'),
+ 'ctimes' => __('Times', 'bread'),
+ 'chelvetica' => __('Helvetica', 'bread'),
+ 'arial' => __('Arial', 'bread'),
+ ];
}
function load_translations()
{
@@ -501,7 +510,6 @@ private function define_admin_hooks()
$this->loader->add_filter('tiny_mce_before_init', $plugin_admin, 'tiny_tweaks');
$this->loader->add_filter('mce_external_plugins', $plugin_admin, 'my_custom_plugins');
$this->loader->add_filter('mce_buttons', $plugin_admin, 'my_register_mce_button');
- //add_action("admin_notices", $plugin_admin, "is_root_server_missing");
$this->loader->add_action("admin_init", $plugin_admin, "my_theme_add_editor_styles");
$this->loader->add_action("wp_default_editor", $plugin_admin, "ml_default_editor");
$this->loader->add_filter('tiny_mce_version', $plugin_admin, 'force_mce_refresh');
diff --git a/public/class-bread-public.php b/public/class-bread-public.php
index d2bfb2da..4beab26b 100644
--- a/public/class-bread-public.php
+++ b/public/class-bread-public.php
@@ -3,10 +3,11 @@
exit;
}
use Mpdf\Mpdf;
+use Mpdf\Config\ConfigVariables;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Level;
-
+define('MPDF_MODE', 's'); // Try to keep the file small by subsetting fonts and only including the characters we need. This is especially important for languages with large character sets like Chinese, Japanese, and Korean. If you have a lot of different characters in your meeting list, you may want to switch to 'utf-8' or 'c' mode which includes more characters but results in larger file sizes.
/**
* Main class for generating the PDF meeting list.
*
@@ -19,7 +20,6 @@
*/
class Bread_Public
{
-
/**
* The ID of this plugin.
*
@@ -199,13 +199,7 @@ public function bmlt_meeting_list()
exit;
}
}
- $page_type_settings = $this->constuct_page_type_settings();
- $default_font = $this->options['base_font'] == "freesans" ? "dejavusanscondensed" : $this->options['base_font'];
- $mode = 's';
- $mpdf_init_options = $this->construct_init_options($default_font, $mode, $page_type_settings);
- if (isset($this->options['packTabledata']) && $this->options['packTabledata']) {
- $mpdf_init_options['packTabledata'] = true;
- }
+
@ob_end_clean();
// We load mPDF only when we need to and as late as possible. This prevents
// conflicts with other plugins that use the same PSRs in different versions
@@ -214,6 +208,11 @@ public function bmlt_meeting_list()
include_once plugin_dir_path(__FILE__) . '../vendor/autoload.php';
require_once __DIR__ . '/class-bread-content-generator.php';
require_once __DIR__ . '/class-bread-format-manager.php';
+ $default_font = $this->get_default_font();
+ $mpdf_init_options = $this->construct_init_options($default_font);
+ if (isset($this->options['packTabledata']) && $this->options['packTabledata']) {
+ $mpdf_init_options['packTabledata'] = true;
+ }
$this->mpdf = new mPDF($mpdf_init_options);
if (isset($this->options['logging']) && $this->options['logging']) {
$logger = new Logger('bread-log');
@@ -244,7 +243,7 @@ public function bmlt_meeting_list()
if ($this->options['column_line'] == 1
&& ($this->options['page_fold'] == 'tri' || $this->options['page_fold'] == 'quad')
) {
- $this->drawLinesSeperatingColumns($mode, $mpdf_init_options['format'], $default_font);
+ $this->drawLinesSeperatingColumns($mpdf_init_options['format'], $default_font);
}
$result = $this->bread->bmlt()->doMainQuery();
@@ -295,7 +294,7 @@ public function bmlt_meeting_list()
$generator = new Bread_ContentGenerator($this->mpdf, $this->bread, $result_meetings, $formatsManager);
$generator->generate($num_columns);
$this->mpdf->SetDisplayMode('fullpage', 'two');
- $this->reorder_booklet_pages($mode);
+ $this->reorder_booklet_pages();
if ($this->options['include_protection'] == 1) {
// 'copy','print','modify','annot-forms','fill-forms','extract','assemble','print-highres'
$this->mpdf->SetProtection(array('copy', 'print', 'print-highres'), '', $this->options['protection_password']);
@@ -319,7 +318,7 @@ public function bmlt_meeting_list()
}
$this->bread->removeTempDir();
}
- private function constuct_page_type_settings()
+ private function construct_page_type_settings(): array
{
$page_type_settings = array();
// TODO: The page number is always 5 from botton...this should be adjustable
@@ -377,42 +376,41 @@ private function constuct_page_type_settings()
}
return $page_type_settings;
}
- private function construct_init_options($default_font, $mode, $page_type_settings): array
+ private function construct_init_options(string $default_font): array
{
- if ($default_font == 'arial' || $default_font == 'times' || $default_font == 'courier') {
- $mpdf_init_options = [
-
+ $defaultConfig = (new ConfigVariables())->getDefaults();
+ $mpdf_init_options = [
+ 'mode' => 's',
'tempDir' => $this->bread->temp_dir(),
- 'mode' => $mode,
'default_font_size' => 7,
'default_font' => $default_font,
- 'useSubstitutions' => false,
'margin_left' => $this->options['margin_left'],
'margin_right' => $this->options['margin_right'],
'margin_top' => $this->options['margin_top'],
'margin_bottom' => $this->options['margin_bottom'],
'margin_header' => $this->options['margin_header'],
- ];
- } else {
- $mpdf_init_options = [
- 'mode' => $mode,
- 'tempDir' => $this->bread->temp_dir(),
- 'default_font_size' => 7,
- 'default_font' => $default_font,
- 'margin_left' => $this->options['margin_left'],
- 'margin_right' => $this->options['margin_right'],
- 'margin_top' => $this->options['margin_top'],
- 'margin_bottom' => $this->options['margin_bottom'],
- 'margin_header' => $this->options['margin_header'],
- ];
+ 'restrictColorSpace' => $this->options['colorspace'],
+ 'fontDir' => $defaultConfig['fontDir'], // Set these, so that life is easier to add custom fonts in filters.
+ 'fontData' => [
+ "dejavusanscondensed" => [
+ 'R' => "DejaVuSansCondensed.ttf",
+ 'B' => "DejaVuSansCondensed-Bold.ttf",
+ 'I' => "DejaVuSansCondensed-Oblique.ttf",
+ 'BI' => "DejaVuSansCondensed-BoldOblique.ttf",
+ 'useOTL' => 0xFF,
+ 'useKashida' => 75,
+ ], // Set these, so that life is easier to add custom fonts in filters.
+ ],
+ ];
+ if ($default_font == 'arial' || $default_font == 'times' || $default_font == 'courier') {
+ $mpdf_init_options['useSubstitutions'] = true;
}
- $mpdf_init_options['restrictColorSpace'] = $this->options['colorspace'];
- $mpdf_init_options = array_merge($mpdf_init_options, $page_type_settings);
+ $mpdf_init_options = array_merge($mpdf_init_options, $this->construct_page_type_settings());
$mpdf_init_options = apply_filters("Bread_Mpdf_Init_Options", $mpdf_init_options, $this->options);
return $mpdf_init_options;
}
- private function drawLinesSeperatingColumns($mode, $format, $default_font)
+ private function drawLinesSeperatingColumns($format, $default_font)
{
$html = '';
if ($this->options['page_fold'] == 'tri') {
@@ -440,7 +438,7 @@ private function drawLinesSeperatingColumns($mode, $format, $default_font)
}
$mpdf_column = new mPDF(
[
- 'mode' => $mode,
+ 'mode' => MPDF_MODE,
'tempDir' => $this->bread->temp_dir(),
'format' => $format,
'default_font_size' => 7,
@@ -462,13 +460,13 @@ private function drawLinesSeperatingColumns($mode, $format, $default_font)
$tplId = $this->mpdf->importPage($pagecount);
$this->mpdf->SetPageTemplate($tplId);
}
- private function reorder_booklet_pages($mode)
+ private function reorder_booklet_pages()
{
if ($this->options['page_fold'] == 'half') {
$FilePath = $this->bread->temp_dir() . DIRECTORY_SEPARATOR . $this->get_FilePath('_half');
$this->mpdf->Output($FilePath, 'F');
$mpdfOptions = [
- 'mode' => $mode,
+ 'mode' => MPDF_MODE,
'tempDir' => $this->bread->temp_dir(),
'default_font_size' => '',
'margin_left' => 0,
@@ -511,8 +509,9 @@ private function reorder_booklet_pages($mode)
} else if ($this->options['page_fold'] == 'full' && $this->options['booklet_pages']) {
$FilePath = $this->bread->temp_dir() . DIRECTORY_SEPARATOR . $this->get_FilePath('_full');
$this->mpdf->Output($FilePath, 'F');
+ $defaultConfig = (new ConfigVariables())->getDefaults();
$mpdfOptions = [
- 'mode' => $mode,
+ 'mode' => MPDF_MODE,
'tempDir' => $this->bread->temp_dir(),
'default_font_size' => '',
'margin_left' => 0,
@@ -522,9 +521,20 @@ private function reorder_booklet_pages($mode)
'margin_footer' => 6,
'orientation' => $this->options['page_orientation'],
'restrictColorSpace' => $this->options['colorspace'],
+ 'format' => $this->options['page_size'] . "-" . $this->options['page_orientation'],
+ 'default_font' => $this->get_default_font(),
+ 'fontDir' => $defaultConfig['fontDir'], // Set these, so that life is easier to add custom fonts in filters.
+ 'fontData' => [
+ "dejavusanscondensed" => [
+ 'R' => "DejaVuSansCondensed.ttf",
+ 'B' => "DejaVuSansCondensed-Bold.ttf",
+ 'I' => "DejaVuSansCondensed-Oblique.ttf",
+ 'BI' => "DejaVuSansCondensed-BoldOblique.ttf",
+ 'useOTL' => 0xFF,
+ 'useKashida' => 75,
+ ],
+ ],
];
- $mpdfOptions['format'] = $this->options['page_size'] . "-" . $this->options['page_orientation'];
- $mpdfOptions['default_font'] = $this->options['base_font'] == "freesans" ? "dejavusanscondensed" : $this->options['base_font'];
$mpdfOptions = apply_filters("Bread_Mpdf_Init_Options", $mpdfOptions, $this->options);
$mpdftmp = new mPDF($mpdfOptions);
$this->mpdf->shrink_tables_to_fit = 1;
@@ -546,7 +556,7 @@ private function reorder_booklet_pages($mode)
$FilePath = $this->bread->temp_dir() . DIRECTORY_SEPARATOR . $this->get_FilePath('_pocket');
$this->mpdf->Output($FilePath, 'F');
$mpdfOptions = [
- 'mode' => $mode,
+ 'mode' => MPDF_MODE,
'tempDir' => $this->bread->temp_dir(),
'default_font_size' => '',
'margin_left' => 0,
@@ -579,7 +589,7 @@ private function reorder_booklet_pages($mode)
$FilePath = $this->bread->temp_dir() . DIRECTORY_SEPARATOR . $this->get_FilePath('_flyer');
$this->mpdf->Output($FilePath, 'F');
$mpdfOptions = [
- 'mode' => $mode,
+ 'mode' => MPDF_MODE,
'tempDir' => $this->bread->temp_dir(),
'default_font_size' => '',
'margin_left' => 0,
@@ -617,7 +627,14 @@ private function reorder_booklet_pages($mode)
$this->mpdf = $mpdftmp;
}
}
- function get_booklet_pages($np, $backcover = true)
+ private function get_default_font()
+ {
+ if ($this->options['base_font'] == "freesans") {
+ return "dejavusanscondensed";
+ }
+ return $this->options['base_font'];
+ }
+ private function get_booklet_pages($np, $backcover = true)
{
$lastpage = $np;
$np = 4 * ceil($np / 4);
@@ -639,7 +656,7 @@ function get_booklet_pages($np, $backcover = true)
}
return $pp;
}
- function get_FilePath($pos = '')
+ private function get_FilePath($pos = '')
{
$site = '';
if (is_multisite()) {
@@ -647,7 +664,7 @@ function get_FilePath($pos = '')
}
return "meetinglist_" . $site . $this->bread->getRequestedSetting() . $pos . '_' . strtolower(gmdate("njYghis")) . ".pdf";
}
- function columnSeparators($oh)
+ private function columnSeparators($oh)
{
if ($this->options['column_line'] == 1) {
return '
From 10c95083ab791165c9905b2e70e31c7148ac9697 Mon Sep 17 00:00:00 2001
From: otrok7 <50595291+otrok7@users.noreply.github.com>
Date: Wed, 10 Jun 2026 16:08:39 +0200
Subject: [PATCH 19/34] Keep more DejaVu fonts as replacements for standard
fonts
---
includes/class-bread.php | 13 +++++++++----
public/class-bread-public.php | 11 ++---------
readme.txt | 2 ++
simplify-mpdf.sh | 2 +-
4 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/includes/class-bread.php b/includes/class-bread.php
index 9f327506..d49e43cb 100644
--- a/includes/class-bread.php
+++ b/includes/class-bread.php
@@ -389,10 +389,11 @@ public function __construct()
$this->fonts = [
'dejavusanscondensed' => __('DejaVu Sans Condensed', 'bread'),
- 'ccourier' => __('Courier', 'bread'),
- 'ctimes' => __('Times', 'bread'),
- 'chelvetica' => __('Helvetica', 'bread'),
- 'arial' => __('Arial', 'bread'),
+ 'times' => __('New Times Roman (DejaVu Serif)', 'bread'),
+ 'courier' => __('Courier (DejaVu Sans Mono)', 'bread'),
+ 'ccourier' => __('PDF Core Font - Courier', 'bread'),
+ 'ctimes' => __('PDF Core Font - New Times Roman', 'bread'),
+ 'chelvetica' => __('PDF Core Font - Helvetica', 'bread'),
];
}
function load_translations()
@@ -823,6 +824,10 @@ private function upgrade_settings(): void
unset($this->options['service_body_3']);
unset($this->options['service_body_4']);
unset($this->options['service_body_5']);
+
+ if ($this->options['base_font'] == 'arial') {
+ $this->options['base_font'] = 'dejabusanscondensed';
+ }
}
$this->options['bread_version'] = BREAD_VERSION;
}
diff --git a/public/class-bread-public.php b/public/class-bread-public.php
index 4beab26b..0165d271 100644
--- a/public/class-bread-public.php
+++ b/public/class-bread-public.php
@@ -208,7 +208,7 @@ public function bmlt_meeting_list()
include_once plugin_dir_path(__FILE__) . '../vendor/autoload.php';
require_once __DIR__ . '/class-bread-content-generator.php';
require_once __DIR__ . '/class-bread-format-manager.php';
- $default_font = $this->get_default_font();
+ $default_font = $this->options['base_font'];
$mpdf_init_options = $this->construct_init_options($default_font);
if (isset($this->options['packTabledata']) && $this->options['packTabledata']) {
$mpdf_init_options['packTabledata'] = true;
@@ -522,7 +522,7 @@ private function reorder_booklet_pages()
'orientation' => $this->options['page_orientation'],
'restrictColorSpace' => $this->options['colorspace'],
'format' => $this->options['page_size'] . "-" . $this->options['page_orientation'],
- 'default_font' => $this->get_default_font(),
+ 'default_font' => $this->options['base_font'],
'fontDir' => $defaultConfig['fontDir'], // Set these, so that life is easier to add custom fonts in filters.
'fontData' => [
"dejavusanscondensed" => [
@@ -627,13 +627,6 @@ private function reorder_booklet_pages()
$this->mpdf = $mpdftmp;
}
}
- private function get_default_font()
- {
- if ($this->options['base_font'] == "freesans") {
- return "dejavusanscondensed";
- }
- return $this->options['base_font'];
- }
private function get_booklet_pages($np, $backcover = true)
{
$lastpage = $np;
diff --git a/readme.txt b/readme.txt
index 6665f427..7f29bb7d 100644
--- a/readme.txt
+++ b/readme.txt
@@ -57,6 +57,8 @@ Follow all these steps, keep in mind that once you start using bread, it's not g
= 2.10.0 =
* [bread_button] shortcode added that can generate PDFs even when the hosting provider has blocked access to the BMLT server.
+* PDF Core Fonts (Helvertica, New Times Roman and Courier) may be selected
++ Extension points added to allow for custom fonts
= 2.9.15 =
* More commas cleaned up.
diff --git a/simplify-mpdf.sh b/simplify-mpdf.sh
index de0648f2..5afb4af9 100755
--- a/simplify-mpdf.sh
+++ b/simplify-mpdf.sh
@@ -1 +1 @@
-find vendor/mpdf/mpdf/ttfonts -type f ! -name 'DejaVuSansC*.ttf' -delete
+find vendor/mpdf/mpdf/ttfonts -type f ! \( -name 'DejaVuSansC*.ttf' -o -name 'DejaVuSerifC*.ttf' -o -name 'DejaVuSansMono*.ttf' \) -delete
From 908787d1dc8bd22c2c64a6517837fb3ee333a503 Mon Sep 17 00:00:00 2001
From: otrok7 <50595291+otrok7@users.noreply.github.com>
Date: Wed, 10 Jun 2026 16:57:29 +0200
Subject: [PATCH 20/34] lint
---
includes/class-bread.php | 2 +-
public/class-bread-public.php | 37 ++++++++++++++++++-----------------
2 files changed, 20 insertions(+), 19 deletions(-)
diff --git a/includes/class-bread.php b/includes/class-bread.php
index d49e43cb..8a27021a 100644
--- a/includes/class-bread.php
+++ b/includes/class-bread.php
@@ -56,7 +56,7 @@ class Bread
private string $optionsName;
private $allSettings = array();
private $maxSetting = 1;
- public$fonts = [];
+ public $fonts = [];
/**
* The setting we are editing, generating or otherwise working with. Generally set with query string "?current-meeting-list".
*
diff --git a/public/class-bread-public.php b/public/class-bread-public.php
index 0165d271..0584942f 100644
--- a/public/class-bread-public.php
+++ b/public/class-bread-public.php
@@ -7,6 +7,7 @@
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Level;
+
define('MPDF_MODE', 's'); // Try to keep the file small by subsetting fonts and only including the characters we need. This is especially important for languages with large character sets like Chinese, Japanese, and Korean. If you have a lot of different characters in your meeting list, you may want to switch to 'utf-8' or 'c' mode which includes more characters but results in larger file sizes.
/**
* Main class for generating the PDF meeting list.
@@ -392,15 +393,15 @@ private function construct_init_options(string $default_font): array
'restrictColorSpace' => $this->options['colorspace'],
'fontDir' => $defaultConfig['fontDir'], // Set these, so that life is easier to add custom fonts in filters.
'fontData' => [
- "dejavusanscondensed" => [
- 'R' => "DejaVuSansCondensed.ttf",
- 'B' => "DejaVuSansCondensed-Bold.ttf",
- 'I' => "DejaVuSansCondensed-Oblique.ttf",
- 'BI' => "DejaVuSansCondensed-BoldOblique.ttf",
- 'useOTL' => 0xFF,
- 'useKashida' => 75,
- ], // Set these, so that life is easier to add custom fonts in filters.
- ],
+ "dejavusanscondensed" => [
+ 'R' => "DejaVuSansCondensed.ttf",
+ 'B' => "DejaVuSansCondensed-Bold.ttf",
+ 'I' => "DejaVuSansCondensed-Oblique.ttf",
+ 'BI' => "DejaVuSansCondensed-BoldOblique.ttf",
+ 'useOTL' => 0xFF,
+ 'useKashida' => 75,
+ ], // Set these, so that life is easier to add custom fonts in filters.
+ ],
];
if ($default_font == 'arial' || $default_font == 'times' || $default_font == 'courier') {
$mpdf_init_options['useSubstitutions'] = true;
@@ -509,7 +510,7 @@ private function reorder_booklet_pages()
} else if ($this->options['page_fold'] == 'full' && $this->options['booklet_pages']) {
$FilePath = $this->bread->temp_dir() . DIRECTORY_SEPARATOR . $this->get_FilePath('_full');
$this->mpdf->Output($FilePath, 'F');
- $defaultConfig = (new ConfigVariables())->getDefaults();
+ $defaultConfig = (new ConfigVariables())->getDefaults();
$mpdfOptions = [
'mode' => MPDF_MODE,
'tempDir' => $this->bread->temp_dir(),
@@ -525,14 +526,14 @@ private function reorder_booklet_pages()
'default_font' => $this->options['base_font'],
'fontDir' => $defaultConfig['fontDir'], // Set these, so that life is easier to add custom fonts in filters.
'fontData' => [
- "dejavusanscondensed" => [
- 'R' => "DejaVuSansCondensed.ttf",
- 'B' => "DejaVuSansCondensed-Bold.ttf",
- 'I' => "DejaVuSansCondensed-Oblique.ttf",
- 'BI' => "DejaVuSansCondensed-BoldOblique.ttf",
- 'useOTL' => 0xFF,
- 'useKashida' => 75,
- ],
+ "dejavusanscondensed" => [
+ 'R' => "DejaVuSansCondensed.ttf",
+ 'B' => "DejaVuSansCondensed-Bold.ttf",
+ 'I' => "DejaVuSansCondensed-Oblique.ttf",
+ 'BI' => "DejaVuSansCondensed-BoldOblique.ttf",
+ 'useOTL' => 0xFF,
+ 'useKashida' => 75,
+ ],
],
];
$mpdfOptions = apply_filters("Bread_Mpdf_Init_Options", $mpdfOptions, $this->options);
From 5ff33fc15d2ab8af67cf472475069bf2fda3601d Mon Sep 17 00:00:00 2001
From: otrok7 <50595291+otrok7@users.noreply.github.com>
Date: Mon, 15 Jun 2026 09:12:04 +0200
Subject: [PATCH 21/34] Test adding fonts through plugins
---
admin/css/fonts.css | 12 +++++++++
public/class-bread-content-generator.php | 8 +++---
public/class-bread-public.php | 31 ++++++------------------
3 files changed, 23 insertions(+), 28 deletions(-)
diff --git a/admin/css/fonts.css b/admin/css/fonts.css
index cc6106df..5a91b439 100644
--- a/admin/css/fonts.css
+++ b/admin/css/fonts.css
@@ -21,4 +21,16 @@
src:url(../../../vendor/mpdf/mpdf/ttfonts/DejaVuSansCondensed-Oblique.ttf) format('truetype');
font-display: block;
font-style: italic;
+}
+@font-face {
+ font-family: ccourier;
+ src: local('Courier');
+}
+@font-face {
+ font-family: ctimes;
+ src: local('Times New Roman');
+}
+@font-face {
+ font-family: chelvetica;
+ src: local('Helvetica');
}
\ No newline at end of file
diff --git a/public/class-bread-content-generator.php b/public/class-bread-content-generator.php
index df3fe591..17acf4d7 100644
--- a/public/class-bread-content-generator.php
+++ b/public/class-bread-content-generator.php
@@ -143,10 +143,10 @@ function __construct(Mpdf $mpdf, Bread $bread, array $result_meetings, Bread_For
);
$this->shortcodes = apply_filters("Bread_Section_Shortcodes", $this->shortcodes, $this->bread->bmlt()->get_areas(), $formatsManager->getFormatsUsed());
if ($this->options['page_fold'] == 'half' || $this->options['page_fold'] == 'full') {
- $this->mpdf->DefHTMLFooterByName('MyFooter', '
' . $this->options['nonmeeting_footer'] . '
');
- $this->mpdf->DefHTMLFooterByName('_default', '
' . $this->options['nonmeeting_footer'] . '
');
- $this->mpdf->DefHTMLFooterByName('Meeting1Footer', '
' . $this->options['meeting1_footer'] . '
');
- $this->mpdf->DefHTMLFooterByName('Meeting2Footer', '
' . $this->options['meeting2_footer'] . '
');
+ @$this->mpdf->DefHTMLFooterByName('MyFooter', '
' . $this->options['nonmeeting_footer'] . '
');
+ @$this->mpdf->DefHTMLFooterByName('_default', '
' . $this->options['nonmeeting_footer'] . '
');
+ @$this->mpdf->DefHTMLFooterByName('Meeting1Footer', '
' . $this->options['meeting1_footer'] . '
');
+ @$this->mpdf->DefHTMLFooterByName('Meeting2Footer', '
' . $this->options['meeting2_footer'] . '
');
}
if (!empty($this->options['pageheader_content'])) {
$data = $this->standard_shortcode_replacement('pageheader');
diff --git a/public/class-bread-public.php b/public/class-bread-public.php
index 0584942f..3714eb26 100644
--- a/public/class-bread-public.php
+++ b/public/class-bread-public.php
@@ -4,11 +4,12 @@
}
use Mpdf\Mpdf;
use Mpdf\Config\ConfigVariables;
+use Mpdf\Config\FontVariables;
use Monolog\Logger;
use Monolog\Handler\StreamHandler;
use Monolog\Level;
-define('MPDF_MODE', 's'); // Try to keep the file small by subsetting fonts and only including the characters we need. This is especially important for languages with large character sets like Chinese, Japanese, and Korean. If you have a lot of different characters in your meeting list, you may want to switch to 'utf-8' or 'c' mode which includes more characters but results in larger file sizes.
+define('MPDF_MODE', 'utf-8-s'); // Try to keep the file small by subsetting fonts and only including the characters we need. This is especially important for languages with large character sets like Chinese, Japanese, and Korean. If you have a lot of different characters in your meeting list, you may want to switch to 'utf-8' or 'c' mode which includes more characters but results in larger file sizes.
/**
* Main class for generating the PDF meeting list.
*
@@ -214,7 +215,7 @@ public function bmlt_meeting_list()
if (isset($this->options['packTabledata']) && $this->options['packTabledata']) {
$mpdf_init_options['packTabledata'] = true;
}
- $this->mpdf = new mPDF($mpdf_init_options);
+ $this->mpdf = @new mPDF($mpdf_init_options);
if (isset($this->options['logging']) && $this->options['logging']) {
$logger = new Logger('bread-log');
$site = '';
@@ -380,8 +381,9 @@ private function construct_page_type_settings(): array
private function construct_init_options(string $default_font): array
{
$defaultConfig = (new ConfigVariables())->getDefaults();
+ $defaultFontsConfig = (new FontVariables())->getDefaults();
$mpdf_init_options = [
- 'mode' => 's',
+ 'mode' => MPDF_MODE,
'tempDir' => $this->bread->temp_dir(),
'default_font_size' => 7,
'default_font' => $default_font,
@@ -392,16 +394,7 @@ private function construct_init_options(string $default_font): array
'margin_header' => $this->options['margin_header'],
'restrictColorSpace' => $this->options['colorspace'],
'fontDir' => $defaultConfig['fontDir'], // Set these, so that life is easier to add custom fonts in filters.
- 'fontData' => [
- "dejavusanscondensed" => [
- 'R' => "DejaVuSansCondensed.ttf",
- 'B' => "DejaVuSansCondensed-Bold.ttf",
- 'I' => "DejaVuSansCondensed-Oblique.ttf",
- 'BI' => "DejaVuSansCondensed-BoldOblique.ttf",
- 'useOTL' => 0xFF,
- 'useKashida' => 75,
- ], // Set these, so that life is easier to add custom fonts in filters.
- ],
+ 'fontdata' => $defaultFontsConfig ['fontdata'], // Set these, so that life is easier to add custom fonts in filters.
];
if ($default_font == 'arial' || $default_font == 'times' || $default_font == 'courier') {
$mpdf_init_options['useSubstitutions'] = true;
@@ -524,17 +517,7 @@ private function reorder_booklet_pages()
'restrictColorSpace' => $this->options['colorspace'],
'format' => $this->options['page_size'] . "-" . $this->options['page_orientation'],
'default_font' => $this->options['base_font'],
- 'fontDir' => $defaultConfig['fontDir'], // Set these, so that life is easier to add custom fonts in filters.
- 'fontData' => [
- "dejavusanscondensed" => [
- 'R' => "DejaVuSansCondensed.ttf",
- 'B' => "DejaVuSansCondensed-Bold.ttf",
- 'I' => "DejaVuSansCondensed-Oblique.ttf",
- 'BI' => "DejaVuSansCondensed-BoldOblique.ttf",
- 'useOTL' => 0xFF,
- 'useKashida' => 75,
- ],
- ],
+ 'fontDir' => $defaultConfig['fontDir'], // Set these, so that life is easier to add custom fonts in filters.
];
$mpdfOptions = apply_filters("Bread_Mpdf_Init_Options", $mpdfOptions, $this->options);
$mpdftmp = new mPDF($mpdfOptions);
From 1d93db6de98be05686c52d688cfeb46aa639e32a Mon Sep 17 00:00:00 2001
From: otrok7 <50595291+otrok7@users.noreply.github.com>
Date: Sat, 20 Jun 2026 17:52:38 +0200
Subject: [PATCH 22/34] Load Remote Fonts
---
admin/class-bread-admin.php | 43 ++-
admin/css/fonts.css | 48 ++++
admin/partials/_custom_fonts_setup.php | 78 ++++++
admin/partials/_layout_setup.php | 11 +-
admin/partials/bread-admin-display.php | 9 +
bmlt-meeting-list.php | 1 +
.../class-bread-loadable-fonts.php | 250 ++++++++++++++++++
includes/class-bread.php | 67 ++++-
8 files changed, 486 insertions(+), 21 deletions(-)
create mode 100644 admin/partials/_custom_fonts_setup.php
create mode 100644 bread_loadable_fonts/class-bread-loadable-fonts.php
diff --git a/admin/class-bread-admin.php b/admin/class-bread-admin.php
index dc20d101..74224eaa 100644
--- a/admin/class-bread-admin.php
+++ b/admin/class-bread-admin.php
@@ -73,10 +73,16 @@ public function enqueue_styles($hook)
wp_enqueue_style("spectrum", plugin_dir_url(__FILE__) . "css/spectrum.min.css", false, BREAD_VERSION, 'all');
wp_enqueue_style("tooltipster", plugin_dir_url(__FILE__) . "css/tooltipster.bundle.min.css", false, BREAD_VERSION, 'all');
wp_enqueue_style("tooltipster-noir", plugin_dir_url(__FILE__) . "css/tooltipster-sideTip-noir.min.css", false, BREAD_VERSION, 'all');
- wp_enqueue_style("admin", plugin_dir_url(__FILE__) . "css/admin.css", false, BREAD_VERSION, 'all');
+ wp_enqueue_style("bread-admin", plugin_dir_url(__FILE__) . "css/admin.css", false, BREAD_VERSION, 'all');
wp_enqueue_style("select2", plugin_dir_url(__FILE__) . "css/select2.min.css", false, BREAD_VERSION, 'all');
+ wp_add_inline_style("bread-admin", $this->generate_custom_fonts_script());
wp_enqueue_style("smartWizard-dots", plugin_dir_url(__FILE__) . "css/smart_wizard_dots.css", false, BREAD_VERSION, 'all');
}
+ public function generate_custom_fonts_script(): string
+ {
+ $dir = plugin_dir_url(__FILE__);
+ return apply_filters('bread_content_style', "@import url('$dir/css/fonts.css');");
+ }
/**
* Register the JavaScript for the admin area.
*
@@ -197,24 +203,27 @@ function my_custom_plugins()
}
return $plugins_array;
}
-
// Enable font size & font family selects in the editor
function tiny_tweaks($initArray)
{
if (function_exists('get_current_screen')) {
$screen = get_current_screen();
- $fonts = apply_filters('bread_custom_fonts', $this->bread->fonts);
+ $fonts = $this->bread->getAvailableFonts();
+ $active_fonts = $this->bread->getActiveFonts();
if ($screen != null && str_ends_with($screen->id, $this->bmltEnabled_admin->getSlug())) {
$initArray['fontsize_formats'] = "5pt 6pt 7pt 8pt 9pt 10pt 11pt 12pt 13pt 14pt 15pt 16pt 17pt 18pt 19pt 20pt 22pt 24pt 26pt 28pt 30pt 32pt 34pt 36pt 38pt";
$initArray['theme_advanced_blockformats'] = 'h2,h3,h4,p';
$initArray['wordpress_adv_hidden'] = false;
$initArray['font_formats'] = '';
- foreach ($fonts as $font_key => $font_name) {
+ foreach ($fonts as $font_key => $font_info) {
+ if (!in_array($font_key, $active_fonts)) {
+ continue;
+ }
+ $font_name = $font_info['name'];
$initArray['font_formats'] .= "$font_name=$font_key;";
}
- $dir = plugin_dir_url(__FILE__);
$font = $this->bread->getOption('base_font');
- $initArray['content_style'] = apply_filters('bread_content_style', "@import url('$dir/css/fonts.css');");
+ $initArray['content_style'] = $this->generate_custom_fonts_script();
$initArray['content_style'] .= "body { font-family: $font; }";
}
}
@@ -422,6 +431,27 @@ function admin_submenu_link($parent_slug)
function admin_options_page()
{
$filename = '';
+ if (!empty($_GET['fontAction'])) {
+ $action = sanitize_key($_GET['fontAction']);
+ if (!wp_verify_nonce($_GET['nonce'], 'bread_font_action')) {
+ wp_die('Request invalid due to timeout');
+ }
+ $fonts = $this->bread->getAvailableFonts();
+ $font = $_GET['font'];
+ if (!isset($fonts[$font])) {
+ wp_die('Request invalid');
+ }
+ if (!isset($fonts[$font])) {
+ wp_die('Request invalid');
+ }
+ if (!isset($fonts[$font]['actions'])) {
+ wp_die('Request invalid');
+ }
+ if (!isset($fonts[$font]['actions'][$action])) {
+ wp_die('Request invalid');
+ }
+ call_user_func($fonts[$font]['actions'][$action]['lambda'], $font);
+ }
if (!empty($_POST['pwsix_action']) && (!isset($_POST['bmltmeetinglistsave']) || $_POST['bmltmeetinglistsave'] != 'Save Changes')) {
switch ($_POST['pwsix_action']) {
case 'settings_admin':
@@ -436,6 +466,7 @@ function admin_options_page()
case 'import_settings':
$filename = $this->pwsix_process_settings_import();
break;
+ case 'uploadFont':
default:
break;
}
diff --git a/admin/css/fonts.css b/admin/css/fonts.css
index 5a91b439..5ca85866 100644
--- a/admin/css/fonts.css
+++ b/admin/css/fonts.css
@@ -22,6 +22,54 @@
font-display: block;
font-style: italic;
}
+@font-face {
+ font-family:'DejaVuSerifCondensed';
+ src:url(../../../vendor/mpdf/mpdf/ttfonts/DejaVuSerifCondensed.ttf) format('truetype');
+ font-display: block;
+}
+@font-face {
+ font-family:'DejaVuSerifCondensed';
+ src:url(../../../vendor/mpdf/mpdf/ttfonts/DejaVuSerifCondensed-Bold.ttf) format('truetype');
+ font-display: block;
+ font-weight: bold;
+}
+@font-face {
+ font-family:'DejaVuSerifCondensed';
+ src:url(../../../vendor/mpdf/mpdf/ttfonts/DejaVuSerifCondensed-BoldOblique.ttf) format('truetype');
+ font-display: block;
+ font-weight: bold;
+ font-style: italic;
+}
+@font-face {
+ font-family:'DejaVuSerifCondensed';
+ src:url(../../../vendor/mpdf/mpdf/ttfonts/DejaVuSerifCondensed-Oblique.ttf) format('truetype');
+ font-display: block;
+ font-style: italic;
+}
+@font-face {
+ font-family:'DejaVuSansMono';
+ src:url(../../../vendor/mpdf/mpdf/ttfonts/DejaVuSansMono.ttf) format('truetype');
+ font-display: block;
+}
+@font-face {
+ font-family:'DejaVuSansMono';
+ src:url(../../../vendor/mpdf/mpdf/ttfonts/DejaVuSansMono-Bold.ttf) format('truetype');
+ font-display: block;
+ font-weight: bold;
+}
+@font-face {
+ font-family:'DejaVuSansMono';
+ src:url(../../../vendor/mpdf/mpdf/ttfonts/DejaVuSansMono-BoldOblique.ttf) format('truetype');
+ font-display: block;
+ font-weight: bold;
+ font-style: italic;
+}
+@font-face {
+ font-family:'DejaVuSansMono';
+ src:url(../../../vendor/mpdf/mpdf/ttfonts/DejaVuSansMono-Oblique.ttf) format('truetype');
+ font-display: block;
+ font-style: italic;
+}
@font-face {
font-family: ccourier;
src: local('Courier');
diff --git a/admin/partials/_custom_fonts_setup.php b/admin/partials/_custom_fonts_setup.php
new file mode 100644
index 00000000..a5065e18
--- /dev/null
+++ b/admin/partials/_custom_fonts_setup.php
@@ -0,0 +1,78 @@
+bread = $bread;
+ $this->fonts = $bread->getAvailableFonts();
+ $this->nonce = wp_create_nonce("bread_font_action");
+ return parent::__construct();
+ }
+ function get_columns()
+ {
+ return [
+ 'name' => 'Font Family',
+ 'type' => 'Type',
+ 'scripts' => 'Character Sets',
+ 'description' => 'Description',
+ ];
+ }
+ function column_default($font, $column)
+ {
+ switch ($column) {
+ case 'type':
+ $stack = $font['stack'];
+ $array = explode(',', $stack);
+ return $array[count($array)-1];
+ case 'scripts':
+ return implode(',', $font['scripts']);
+ default:
+ if (!isset($font[$column])) {
+ return '';
+ }
+ return $font[$column];
+ }
+ }
+ function column_name($font)
+ {
+ $actions = [];
+ if (isset($font['actions'])) {
+ foreach ($font['actions'] as $key => $action) {
+ $actions[$key] = sprintf('
' . $action['text'] . ' ', $_REQUEST['page'], $action['action'], $font['slug'], $this->nonce);
+ }
+ }
+ return sprintf('%1$s %2$s', $font['name'], $this->row_actions($actions));
+ }
+ function prepare_items()
+ {
+ $columns = $this->get_columns();
+ $hidden = array();
+ $sortable = array();
+ $this->_column_headers = array($columns, $hidden, $sortable);
+ $this->items = $this->fonts;
+ foreach ($this->items as $slug => &$info) {
+ $info['slug'] = $slug;
+ }
+ }
+}
+
+function Bread_custom_fonts_setup_page_render(Bread_AdminDisplay $breadAdminDisplay)
+{
+ $table = new Bread_Custom_Fonts_Table($breadAdminDisplay->getBreadInstance());
+ $table->prepare_items();
+ $table->display();
+}
diff --git a/admin/partials/_layout_setup.php b/admin/partials/_layout_setup.php
index f8a8662f..686d92c5 100644
--- a/admin/partials/_layout_setup.php
+++ b/admin/partials/_layout_setup.php
@@ -2,11 +2,14 @@
if (! defined('ABSPATH')) {
exit;
}
-function echo_font_options($base_font, $bread)
+function echo_font_options(string $base_font, Bread $bread)
{
- $fonts = apply_filters('bread_custom_fonts', $bread->fonts);
- foreach ($fonts as $font_key => $font_name) {
- echo '
' . esc_html($font_name) . ' ';
+ $fonts = $bread->getActiveFonts();
+ $infos = $bread->getAvailableFonts();
+ foreach ($fonts as $font_key) {
+ if (!isset($infos[$font_key])) continue;
+ $font_info = $infos[$font_key];
+ echo '
' . esc_html($font_info['name']) . ' ';
}
}
function Bread_layout_setup_page_render(Bread_AdminDisplay $breadAdmin)
diff --git a/admin/partials/bread-admin-display.php b/admin/partials/bread-admin-display.php
index aeedc854..9fb200bf 100644
--- a/admin/partials/bread-admin-display.php
+++ b/admin/partials/bread-admin-display.php
@@ -19,6 +19,7 @@
include_once '_meetings_setup.php';
include_once '_custom_section_setup.php';
include_once '_backup_restore_setup.php';
+include_once '_custom_fonts_setup.php';
class Bread_AdminDisplay
{
@@ -37,6 +38,10 @@ public function getBreadInstance()
{
return $this->bread;
}
+ public function getAdmin()
+ {
+ return $this->admin;
+ }
public function getServerVersion()
{
return $this->server_version;
@@ -84,6 +89,7 @@ function admin_options_page($filename = '')
@@ -126,6 +132,9 @@ function admin_options_page($filename = '')
+
+
+
diff --git a/bmlt-meeting-list.php b/bmlt-meeting-list.php
index ef93c6c5..3c13e6d4 100644
--- a/bmlt-meeting-list.php
+++ b/bmlt-meeting-list.php
@@ -61,6 +61,7 @@ function Bread_deactivate()
*/
require plugin_dir_path(__FILE__) . 'includes/class-bread.php';
require plugin_dir_path(__FILE__) . 'includes/class-bread-bmlt.php';
+require plugin_dir_path(__FILE__) . 'bread_loadable_fonts/class-bread-loadable-fonts.php';
/**
* Begins execution of the plugin.
*
diff --git a/bread_loadable_fonts/class-bread-loadable-fonts.php b/bread_loadable_fonts/class-bread-loadable-fonts.php
new file mode 100644
index 00000000..4026d756
--- /dev/null
+++ b/bread_loadable_fonts/class-bread-loadable-fonts.php
@@ -0,0 +1,250 @@
+ ['name' => 'Roboto',
+ 'stack' => 'Helvetica, sans-serif',
+ 'scripts' => ['latin', 'latin-ext', 'cyrillic', 'cyrillic-ext', 'greek', 'greek-ext'],
+ 'description' => 'Roboto has a dual nature. It has a mechanical skeleton and the forms are largely geometric. At the same time, the font features friendly and open curves. While some grotesks distort their letterforms to force a rigid rhythm, Roboto doesn’t compromise, allowing letters to be settled into their natural width. This makes for a more natural reading rhythm more commonly found in humanist and serif types.',
+ 'remote' => [
+ 'directory' => 'https://github.com/google/fonts/raw/refs/heads/main/ofl/roboto/',
+ 'R' => 'Roboto[wdth,wght].ttf',
+ 'I' => 'Roboto-Italic[wdth,wght].ttf',
+ 'type' => 'variable',
+ ],
+ 'configuration' => [
+ 'useOTL' => '0xFF',
+ 'kashida' => 75,
+ ]],
+ 'rubik' => ['name' => 'Rubik',
+ 'stack' => 'Helvetica, sans-serif',
+ 'scripts' => ['latin', 'latin-ext', 'cyrillic', 'cyrillic-ext', 'arabic'],
+ 'remote' => [
+ 'directory' => 'https://github.com/google/fonts/raw/refs/heads/main/ofl/rubik/',
+ 'R' => 'Rubik[wght].ttf',
+ 'I' => 'Rubik-Italic[wght].ttf',
+ 'type' => 'variable',
+ ],
+ 'configuration' => [
+ 'useOTL' => '0xFF',
+ 'kashida' => 75,
+ ]],
+ ];
+ public function __construct()
+ {
+ foreach ($this->custom_fonts as $key => &$font) {
+ $font['actions'] = $this->calcActions($key);
+ }
+ $this->addAdminHooks();
+ }
+ public function addAdminHooks()
+ {
+ add_filter('bread_custom_fonts', [$this, 'bread_custom_fonts'], 1);
+ add_filter('bread_content_style', [$this, 'bread_content_style'], 1);
+ add_filter('Bread_active_fonts', [$this, 'bread_active_fonts'], 1);
+ }
+ public function addPublicHooks()
+ {
+ add_filter("Bread_Mpdf_Init_Options", [$this, 'mpdf_init_options']);
+ }
+ public function bread_custom_fonts($fonts)
+ {
+ return array_merge($fonts, $this->custom_fonts);
+ }
+ private function getUploadDirectory($fontFamily = '', $url = false)
+ {
+ $uploads = wp_upload_dir();
+ if (!empty($uploads['error']) || empty($uploads['basedir'])) {
+ return false;
+ }
+ $dir = $url ? $uploads['baseurl'] : $uploads['basedir'];
+ $dir = trailingslashit($dir) . 'bread-uploaded-fonts';
+ if (!$url && !wp_mkdir_p($dir)) {
+ return false;
+ }
+ if ($fontFamily != '') {
+ $dir = trailingslashit($dir) . $fontFamily;
+ if (!$url && !wp_mkdir_p($dir)) {
+ return false;
+ }
+ }
+ return $dir;
+ }
+ public function bread_active_fonts($fonts)
+ {
+ return array_merge($fonts, $this->getUploadedFonts());
+ }
+ private function getUploadedFonts()
+ {
+ $ret = [];
+ $subdirs = (new WP_Filesystem_Direct(null))->dirlist($this->getUploadDirectory(), false, true);
+ foreach ($subdirs as $subdir) {
+ if ($subdir['type'] !== 'd') {
+ continue;
+ }
+ if ($this->fontComplete($subdir['name'], array_keys($subdir['files']))) {
+ $ret[] = $subdir['name'];
+ }
+ }
+ return $ret;
+ }
+ private function fontComplete($fontFamily, $fontFiles)
+ {
+ if ($fontFamily === '') {
+ return false;
+ }
+ if (!isset($this->custom_fonts[$fontFamily])) {
+ throw new Exception("$fontFamily not in list of defined fonts");
+ }
+
+ $fontInfo = $this->custom_fonts[$fontFamily];
+ foreach (['R', 'B', 'I', 'BI'] as $key) {
+ if (!isset($fontInfo['remote'][$key])) {
+ continue;
+ }
+ if (!in_array($fontInfo['remote'][$key], $fontFiles)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ public function installFont($fontFamily)
+ {
+ if (!current_user_can('manage_options')) {
+ $this->outputWarning("You must be an administrator to install fonts");
+ return;
+ }
+ if (!isset($this->custom_fonts[$fontFamily])) {
+ $this->outputWarning("$fontFamily not in list of defined fonts");
+ return;
+ }
+
+ $fontInfo = $this->custom_fonts[$fontFamily];
+ $directory = $fontInfo['remote']['directory'];
+ $localDir = $this->getUploadDirectory($fontFamily);
+ foreach (['R', 'B', 'I', 'BI'] as $key) {
+ if (!isset($fontInfo['remote'][$key])) {
+ continue;
+ }
+
+ $file = $fontInfo['remote'][$key];
+ $local = trailingslashit($localDir) . $file;
+ $url = $directory . rawurlencode($file);
+ $response = wp_remote_get($url, array('timeout' => 15, 'redirection' => 3));
+ if (is_wp_error($response)) {
+ $this->outputWarning("Could not retrieve $url");
+ return;
+ }
+ $code = wp_remote_retrieve_response_code($response);
+ $body = wp_remote_retrieve_body($response);
+ if ($code === 200 && is_string($body) && strlen($body) > 1000) {
+ $saved = file_put_contents($local, $body);
+ if (!$saved || $saved < 1000) {
+ $this->outputWarning("Could not retrieve $url");
+ return;
+ }
+ }
+ }
+ $this->outputSuccess("Font $fontFamily successfully uploaded");
+ }
+ private function outputWarning($str)
+ {
+ echo '
';
+ echo '
'.esc_html($str).'
';
+ echo '
';
+ }
+ private function outputSuccess($str)
+ {
+ echo '
';
+ echo '
'.esc_html($str).'
';
+ echo '
';
+ }
+ public function removeFont($fontFamily)
+ {
+ if (!current_user_can('manage_options')) {
+ $this->outputWarning("You must be an administrator to uninstall fonts");
+ return;
+ }
+ $dirname = trailingslashit($this->getUploadDirectory()) . $fontFamily;
+ array_map('unlink', glob("$dirname/*.*"));
+ rmdir($dirname);
+ $this->outputSuccess("Font $fontFamily removed.");
+ }
+ public function bread_content_style($content_style)
+ {
+ foreach ($this->getUploadedFonts() as $font) {
+ $fontInfo = $this->custom_fonts[$font];
+ $dir = trailingslashit($this->getUploadDirectory($font, true));
+ foreach (['R', 'B', 'I', 'BI'] as $key) {
+ if (!isset($fontInfo['remote'][$key])) {
+ continue;
+ }
+ $loc = $dir . rawurlencode($fontInfo['remote'][$key]);
+ $content_style .= "@font-face {";
+ $content_style .= "font-family: $font;";
+ $content_style .= "src:url($loc) format('truetype');";
+ $content_style .= "font-display: block;";
+ if (isset($fontInfo['remote']['variable']) && $fontInfo['remote']['variable']) {
+ $content_style .= "font-weight: 100 1000;";
+ } elseif ($key == 'B' || $key == 'BI') {
+ $content_style .= "font-weight: bold;";
+ }
+ if ($key == 'I' || $key == 'BI') {
+ $content_style .= "font-style: italic;";
+ }
+ $content_style .= "}";
+ }
+ }
+ return $content_style;
+ }
+ function calcActions($font)
+ {
+ if (in_array($font, $this->getUploadedFonts())) {
+ return [
+ 'removefont' => [
+ 'text' => __('remove', 'bread'),
+ 'action' => 'removefont',
+ 'lambda' => [$this, 'removeFont']
+ ]];
+ } else {
+ return [
+ 'installfont' => [
+ 'text' => __('Install', 'bread'),
+ 'action' => 'installfont',
+ 'lambda' => [$this, 'installFont']
+ ]];
+ }
+ }
+ public function mpdf_init_options( $options ) {
+ $fontDirs = $options['fontDir']??[];
+ $fontdata = $options['fontdata']??[];
+ foreach($this->getUploadedFonts() as $fontKey) {
+ $fontDirs[] = $this->getUploadDirectory($fontKey);
+ $info = $this->custom_fonts[$fontKey];
+ $fontdata[$fontKey] = [];
+ foreach(['R', 'B', 'I', 'BI'] as $style) {
+ if (isset($info['remote'][$style])) {
+ $fontdata[$fontKey][$style] = $info['remote'][$style];
+ } elseif ($style === 'B') {
+ $fontdata[$fontKey][$style] = $info['remote']['R'];
+ } elseif ($style === 'BI') {
+ $fontdata[$fontKey][$style] = $info['remote']['I'];
+ }
+ }
+ if (isset($info['configuration'])) {
+ foreach ($info['configuration'] as $key => $value) {
+ $fontdata[$fontKey][$key] = $value;
+ }
+ }
+ }
+ $options['fontDir'] = $fontDirs;
+ $options['fontdata'] = $fontdata;
+ }
+}
\ No newline at end of file
diff --git a/includes/class-bread.php b/includes/class-bread.php
index 8a27021a..3fef367f 100644
--- a/includes/class-bread.php
+++ b/includes/class-bread.php
@@ -23,7 +23,15 @@
*/
class Bread
{
-
+ /**
+ * The loader that's responsible for maintaining and registering all hooks that power
+ * the plugin.
+ *
+ * @since 2.19.0
+ * @access protected
+ * @var BreadLoadableFonts $fontLoader Maintains and registers all hooks for the plugin.
+ */
+ protected $fontLoader;
/**
* The loader that's responsible for maintaining and registering all hooks that power
* the plugin.
@@ -227,6 +235,47 @@ public function loadAllSettings($holder): int
}
return isset($holder['current-meeting-list']) ? intval($holder['current-meeting-list']) : 1;
}
+ public function getIncludedFonts()
+ {
+ $fonts = [
+ 'dejavusanscondensed' => ['name' => 'DejaVu Sans Condensed',
+ 'stack' => 'Helvetica, sans-serif',
+ 'scripts' => ['latin', 'latin-ext', 'cyrillic', 'cyrillic-ext', 'greek', 'greek-ext', 'arabic', 'vietnamese', 'hebrew'],
+ 'description' => 'A clean and modern sans-serif font with extensive language support. It is a condensed version of the DejaVu Sans font, making it ideal for fitting more text in limited space while maintaining readability.',
+ 'required' => true],
+ 'dejavuserifcondensed' => ['name' => 'DejaVu Serif Condensed',
+ 'stack' => 'Times New Roman, serif',
+ 'scripts' => ['latin', 'latin-ext', 'cyrillic', 'cyrillic-ext', 'greek', 'greek-ext'],
+ 'description' => 'A clean and modern serif font with extensive language support. It is a condensed version of the DejaVu Serif font, making it ideal for fitting more text in limited space while maintaining readability.',
+ 'required' => true],
+ 'dejavusansmono' => ['name' => 'DejaVu Sans Mono',
+ 'stack' => 'Courier New, monospace',
+ 'scripts' => ['latin', 'latin-ext', 'cyrillic', 'cyrillic-ext', 'greek', 'greek-ext', 'arabic', 'vietnamese'],
+ 'description' => 'A clean and modern monospace font with extensive language support. It is ideal for displaying code or tabular data, as each character occupies the same amount of horizontal space.',
+ 'required' => true],
+ 'chelvetica' => ['name' => 'Helvetica (PDF Core Font)',
+ 'stack' => 'dejavusanscondensed, sans-serif',
+ 'scripts' => ['latin'],
+ 'description' => 'Using PDF Core Fonts results in smaller files.',],
+ 'ccourier' => ['name' => 'Courier New (PDF Core Font)',
+ 'stack' => 'Courier New, monospace',
+ 'scripts' => ['latin'],
+ 'description' => 'Using PDF Core Fonts results in smaller files.',],
+ 'ctimes' => ['name' => 'Times New Roman (PDF Core Font)',
+ 'stack' => 'dejavuserifcondensed, serif',
+ 'scripts' => ['latin'],
+ 'description' => 'Using PDF Core Fonts results in smaller files.',],
+ ];
+ return $fonts;
+ }
+ public function getAvailableFonts(): array
+ {
+ return apply_filters('bread_custom_fonts', $this->getIncludedFonts());
+ }
+ public function getActiveFonts(): array
+ {
+ return apply_filters('Bread_active_fonts', array_keys($this->getIncludedFonts()));
+ }
/**
* Undocumented function
*
@@ -386,15 +435,6 @@ public function __construct()
$this->load_translations();
$this->define_admin_hooks();
$this->define_public_hooks();
-
- $this->fonts = [
- 'dejavusanscondensed' => __('DejaVu Sans Condensed', 'bread'),
- 'times' => __('New Times Roman (DejaVu Serif)', 'bread'),
- 'courier' => __('Courier (DejaVu Sans Mono)', 'bread'),
- 'ccourier' => __('PDF Core Font - Courier', 'bread'),
- 'ctimes' => __('PDF Core Font - New Times Roman', 'bread'),
- 'chelvetica' => __('PDF Core Font - Helvetica', 'bread'),
- ];
}
function load_translations()
{
@@ -471,7 +511,12 @@ private function load_dependencies()
* side of the site.
*/
include_once plugin_dir_path(dirname(__FILE__)) . 'public/class-bread-public.php';
-
+ /**
+ * Loading fonts uses the API created to add fonts via plugin.
+ * We need to instanciate this plugin and let the constructor do its work
+ */
+ include_once plugin_dir_path(dirname(__FILE__)) . 'bread_loadable_fonts/class-bread-loadable-fonts.php';
+ $this->fontLoader = new BreadLoadableFonts();
$this->loader = new Bread_Loader();
}
From 4292f6aaa00eacca08170bbd28a23327b615648d Mon Sep 17 00:00:00 2001
From: otrok7 <50595291+otrok7@users.noreply.github.com>
Date: Sun, 21 Jun 2026 14:29:11 +0200
Subject: [PATCH 23/34] Read URLs from GoogleFonts manifest rather than GitHub
---
.../class-bread-loadable-fonts.php | 74 ++++++++++++++-----
1 file changed, 56 insertions(+), 18 deletions(-)
diff --git a/bread_loadable_fonts/class-bread-loadable-fonts.php b/bread_loadable_fonts/class-bread-loadable-fonts.php
index 4026d756..ae17e21b 100644
--- a/bread_loadable_fonts/class-bread-loadable-fonts.php
+++ b/bread_loadable_fonts/class-bread-loadable-fonts.php
@@ -1,9 +1,4 @@
['latin', 'latin-ext', 'cyrillic', 'cyrillic-ext', 'greek', 'greek-ext'],
'description' => 'Roboto has a dual nature. It has a mechanical skeleton and the forms are largely geometric. At the same time, the font features friendly and open curves. While some grotesks distort their letterforms to force a rigid rhythm, Roboto doesn’t compromise, allowing letters to be settled into their natural width. This makes for a more natural reading rhythm more commonly found in humanist and serif types.',
'remote' => [
- 'directory' => 'https://github.com/google/fonts/raw/refs/heads/main/ofl/roboto/',
- 'R' => 'Roboto[wdth,wght].ttf',
- 'I' => 'Roboto-Italic[wdth,wght].ttf',
- 'type' => 'variable',
+ 'manifest' => 'https://fonts.google.com/download/list?family=Roboto',
+ 'R' => 'Roboto_Condensed-Regular.ttf',
+ 'B' => 'Roboto_Condensed-Bold.ttf',
+ 'I' => 'Roboto_Condensed-Italic.ttf',
+ 'BI' => 'Roboto_Condensed-BoldItalic.ttf',
],
'configuration' => [
- 'useOTL' => '0xFF',
+ 'useOTL' => 0xFF,
'kashida' => 75,
]],
'rubik' => ['name' => 'Rubik',
'stack' => 'Helvetica, sans-serif',
'scripts' => ['latin', 'latin-ext', 'cyrillic', 'cyrillic-ext', 'arabic'],
'remote' => [
- 'directory' => 'https://github.com/google/fonts/raw/refs/heads/main/ofl/rubik/',
- 'R' => 'Rubik[wght].ttf',
- 'I' => 'Rubik-Italic[wght].ttf',
- 'type' => 'variable',
+ 'manifest' => 'https://fonts.google.com/download/list?family=Rubik',
+ 'R' => 'Rubik-Regular.ttf',
+ 'B' => 'Rubik-Bold.ttf',
+ 'I' => 'Rubik-Italic.ttf',
+ 'BI' => 'Rubik-BoldItalic.ttf',
],
'configuration' => [
- 'useOTL' => '0xFF',
+ 'useOTL' => 0xFF,
'kashida' => 75,
]],
];
@@ -42,6 +39,7 @@ public function __construct()
$font['actions'] = $this->calcActions($key);
}
$this->addAdminHooks();
+ $this->addPublicHooks();
}
public function addAdminHooks()
{
@@ -115,7 +113,27 @@ private function fontComplete($fontFamily, $fontFiles)
return true;
}
- public function installFont($fontFamily)
+ private function getURLtoFileMap(string $manifest): array|false
+ {
+ $response = wp_remote_get($manifest, array('timeout' => 15, 'redirection' => 3));
+ if (is_wp_error($response)) {
+ $this->outputWarning("Could not retrieve $manifest");
+ return false;
+ }
+ $code = wp_remote_retrieve_response_code($response);
+ $body = wp_remote_retrieve_body($response);
+ if ($code === 200 && is_string($body)) {
+ $contents = json_decode(substr($body,4), true);
+ $ret = [];
+ foreach ($contents['manifest']['fileRefs'] as $item) {
+ $split = explode('/', $item['filename']);
+ $ret[end($split)] = $item['url'];
+ }
+ return $ret;
+ }
+ return false;
+ }
+ public function installFont(string $fontFamily)
{
if (!current_user_can('manage_options')) {
$this->outputWarning("You must be an administrator to install fonts");
@@ -127,7 +145,22 @@ public function installFont($fontFamily)
}
$fontInfo = $this->custom_fonts[$fontFamily];
- $directory = $fontInfo['remote']['directory'];
+ $fileToUrl = [];
+ if (isset($fontInfo['remote']['manifest'])) {
+ $fileToUrl = $this->getURLtoFileMap($fontInfo['remote']['manifest']);
+ if (!$fileToUrl) {
+ $this->outputWarning("Could not parse manifest file");
+ return;
+ }
+ } elseif (isset(($fontInfo['remote']['directory']))) {
+ $directory = $fontInfo['remote']['directory'];
+ foreach (['R', 'B', 'I', 'BI'] as $key) {
+ if (!isset($fontInfo['remote'][$key])) {
+ continue;
+ }
+ $fileToUrl[$fontInfo['remote'][$key]] = $directory . rawurlencode($fontInfo['remote'][$key]);
+ }
+ }
$localDir = $this->getUploadDirectory($fontFamily);
foreach (['R', 'B', 'I', 'BI'] as $key) {
if (!isset($fontInfo['remote'][$key])) {
@@ -136,7 +169,11 @@ public function installFont($fontFamily)
$file = $fontInfo['remote'][$key];
$local = trailingslashit($localDir) . $file;
- $url = $directory . rawurlencode($file);
+ if (!isset($fileToUrl[$fontInfo['remote'][$key]])) {
+ $this->outputWarning("No URL for " . $fileToUrl[$fontInfo['remote'][$key]]);
+ return;
+ }
+ $url = $fileToUrl[$fontInfo['remote'][$key]];
$response = wp_remote_get($url, array('timeout' => 15, 'redirection' => 3));
if (is_wp_error($response)) {
$this->outputWarning("Could not retrieve $url");
@@ -246,5 +283,6 @@ public function mpdf_init_options( $options ) {
}
$options['fontDir'] = $fontDirs;
$options['fontdata'] = $fontdata;
+ return $options;
}
}
\ No newline at end of file
From 787fdabe6f31c8b91bdd437bb59c304cb59c86e5 Mon Sep 17 00:00:00 2001
From: otrok7 <50595291+otrok7@users.noreply.github.com>
Date: Mon, 22 Jun 2026 12:38:13 +0200
Subject: [PATCH 24/34] Added some fonts
---
admin/class-bread-admin.php | 52 +++++--
admin/partials/_custom_fonts_setup.php | 12 +-
admin/partials/_layout_setup.php | 4 +-
.../class-bread-loadable-fonts.php | 144 +++++++++++++-----
includes/class-bread.php | 21 ++-
readme.txt | 3 +-
6 files changed, 167 insertions(+), 69 deletions(-)
diff --git a/admin/class-bread-admin.php b/admin/class-bread-admin.php
index 74224eaa..885bac08 100644
--- a/admin/class-bread-admin.php
+++ b/admin/class-bread-admin.php
@@ -428,29 +428,51 @@ function admin_submenu_link($parent_slug)
2
);
}
+ private function outputSuccess(string $str)
+ {
+ echo '
';
+ echo '
'.esc_html($str).'
';
+ echo '
';
+ }
+ private function outputWarning(string $str)
+ {
+ echo '
';
+ echo '
'.esc_html($str).'
';
+ echo '
';
+ }
function admin_options_page()
{
$filename = '';
- if (!empty($_GET['fontAction'])) {
+ if ($_SERVER['REQUEST_METHOD'] === 'GET' && !empty($_GET['fontAction'])) {
$action = sanitize_key($_GET['fontAction']);
if (!wp_verify_nonce($_GET['nonce'], 'bread_font_action')) {
wp_die('Request invalid due to timeout');
}
- $fonts = $this->bread->getAvailableFonts();
- $font = $_GET['font'];
- if (!isset($fonts[$font])) {
- wp_die('Request invalid');
- }
- if (!isset($fonts[$font])) {
- wp_die('Request invalid');
- }
- if (!isset($fonts[$font]['actions'])) {
- wp_die('Request invalid');
- }
- if (!isset($fonts[$font]['actions'][$action])) {
- wp_die('Request invalid');
+ switch ($action) {
+ case 'success':
+ $this->outputSuccess(esc_html($_GET['message']));
+ break;
+ case 'warning':
+ $this->outputWarning(esc_html($_GET['message']));
+ break;
+ default:
+ $fonts = $this->bread->getAvailableFonts();
+ $font = $_GET['font'];
+ if (!isset($fonts[$font])) {
+ wp_die('Request invalid');
+ }
+ if (!isset($fonts[$font])) {
+ wp_die('Request invalid');
+ }
+ if (!isset($fonts[$font]['actions'])) {
+ wp_die('Request invalid');
+ }
+ if (!isset($fonts[$font]['actions'][$action])) {
+ wp_die('Request invalid');
+ }
+ call_user_func($fonts[$font]['actions'][$action]['lambda'], $font);
+ break;
}
- call_user_func($fonts[$font]['actions'][$action]['lambda'], $font);
}
if (!empty($_POST['pwsix_action']) && (!isset($_POST['bmltmeetinglistsave']) || $_POST['bmltmeetinglistsave'] != 'Save Changes')) {
switch ($_POST['pwsix_action']) {
diff --git a/admin/partials/_custom_fonts_setup.php b/admin/partials/_custom_fonts_setup.php
index a5065e18..9e8916bd 100644
--- a/admin/partials/_custom_fonts_setup.php
+++ b/admin/partials/_custom_fonts_setup.php
@@ -9,6 +9,7 @@ class Bread_Custom_Fonts_Table extends WP_List_Table
{
private Bread $bread;
private array $fonts;
+ private array $active;
private string $nonce;
function getTypeFromStack(string $stack): string
{
@@ -19,6 +20,7 @@ function __construct(Bread $bread)
{
$this->bread = $bread;
$this->fonts = $bread->getAvailableFonts();
+ $this->active = $bread->getActiveFonts();
$this->nonce = wp_create_nonce("bread_font_action");
return parent::__construct();
}
@@ -28,7 +30,7 @@ function get_columns()
'name' => 'Font Family',
'type' => 'Type',
'scripts' => 'Character Sets',
- 'description' => 'Description',
+ 'specimen' => 'More Information',
];
}
function column_default($font, $column)
@@ -52,10 +54,14 @@ function column_name($font)
$actions = [];
if (isset($font['actions'])) {
foreach ($font['actions'] as $key => $action) {
- $actions[$key] = sprintf('
' . $action['text'] . ' ', $_REQUEST['page'], $action['action'], $font['slug'], $this->nonce);
+ $actions[$key] = sprintf('
' . $action['text'] . ' ', $_REQUEST['page'], $action['action'], $font['slug'], $this->nonce);
}
}
- return sprintf('%1$s %2$s', $font['name'], $this->row_actions($actions));
+ $name = $font['name'];
+ if (in_array($font['slug'], $this->active)) {
+ $name = "
" . $name . " ";
+ }
+ return sprintf('%1$s %2$s', $name, $this->row_actions($actions));
}
function prepare_items()
{
diff --git a/admin/partials/_layout_setup.php b/admin/partials/_layout_setup.php
index 686d92c5..848755a1 100644
--- a/admin/partials/_layout_setup.php
+++ b/admin/partials/_layout_setup.php
@@ -7,7 +7,9 @@ function echo_font_options(string $base_font, Bread $bread)
$fonts = $bread->getActiveFonts();
$infos = $bread->getAvailableFonts();
foreach ($fonts as $font_key) {
- if (!isset($infos[$font_key])) continue;
+ if (!isset($infos[$font_key])) {
+ continue;
+ }
$font_info = $infos[$font_key];
echo '
' . esc_html($font_info['name']) . ' ';
}
diff --git a/bread_loadable_fonts/class-bread-loadable-fonts.php b/bread_loadable_fonts/class-bread-loadable-fonts.php
index ae17e21b..d3a34753 100644
--- a/bread_loadable_fonts/class-bread-loadable-fonts.php
+++ b/bread_loadable_fonts/class-bread-loadable-fonts.php
@@ -1,12 +1,15 @@
['name' => 'Roboto',
- 'stack' => 'Helvetica, sans-serif',
+ 'stack' => 'Sans-Serif - Neo-Grotesque',
'scripts' => ['latin', 'latin-ext', 'cyrillic', 'cyrillic-ext', 'greek', 'greek-ext'],
- 'description' => 'Roboto has a dual nature. It has a mechanical skeleton and the forms are largely geometric. At the same time, the font features friendly and open curves. While some grotesks distort their letterforms to force a rigid rhythm, Roboto doesn’t compromise, allowing letters to be settled into their natural width. This makes for a more natural reading rhythm more commonly found in humanist and serif types.',
+ 'specimen' => '
Google Fonts ',
'remote' => [
'manifest' => 'https://fonts.google.com/download/list?family=Roboto',
'R' => 'Roboto_Condensed-Regular.ttf',
@@ -19,8 +22,9 @@ class BreadLoadableFonts
'kashida' => 75,
]],
'rubik' => ['name' => 'Rubik',
- 'stack' => 'Helvetica, sans-serif',
+ 'stack' => 'Sans-Serif - Neo-Grotesque',
'scripts' => ['latin', 'latin-ext', 'cyrillic', 'cyrillic-ext', 'arabic'],
+ 'specimen' => '
Google Fonts ',
'remote' => [
'manifest' => 'https://fonts.google.com/download/list?family=Rubik',
'R' => 'Rubik-Regular.ttf',
@@ -32,7 +36,76 @@ class BreadLoadableFonts
'useOTL' => 0xFF,
'kashida' => 75,
]],
- ];
+ 'oswald' => ['name' => 'Oswald',
+ 'stack' => 'Sans-Serif - Neo-Grotesque',
+ 'scripts' => ['latin', 'latin-ext', 'cyrillic', 'cyrillic-ext'],
+ 'specimen' => '
Google Fonts ',
+ 'remote' => [
+ 'manifest' => 'https://fonts.google.com/download/list?family=Oswald',
+ 'R' => 'Oswald-Regular.ttf',
+ 'B' => 'Oswald-Bold.ttf',
+ ],
+ 'configuration' => [
+ 'useOTL' => 0xFF,
+ 'kashida' => 75,
+ ]],
+ 'opensans' => ['name' => 'Open Sans Condensed',
+ 'stack' => 'Sans-Serif - Humanist',
+ 'scripts' => ['latin', 'latin-ext', 'cyrillic', 'cyrillic-ext', 'greek'],
+ 'specimen' => '
Google Fonts ',
+ 'remote' => [
+ 'manifest' => 'https://fonts.google.com/download/list?family=Open%20Sans',
+ 'R' => 'OpenSans_Condensed-Regular.ttf',
+ 'B' => 'OpenSans_Condensed-Bold.ttf',
+ 'I' => 'OpenSans_Condensed-Italic.ttf',
+ 'BI' => 'OpenSans_Condensed-BoldItalic.ttf',
+ ],
+ 'configuration' => [
+ 'useOTL' => 0xFF,
+ 'kashida' => 75,
+ ]],
+ 'arimo' => ['name' => 'Arimo',
+ 'stack' => 'Sans-Serif - Arial-like',
+ 'scripts' => ['latin', 'latin-ext', 'cyrillic', 'cyrillic-ext', 'greek', 'hebrew'],
+ 'specimen' => '
Google Fonts ',
+ 'remote' => [
+ 'manifest' => 'https://fonts.google.com/download/list?family=Arimo',
+ 'R' => 'Arimo-Regular.ttf',
+ 'B' => 'Arimo-Bold.ttf',
+ 'I' => 'Arimo-Italic.ttf',
+ 'BI' => 'Arimo-BoldItalic.ttf',
+ ],
+ 'configuration' => [
+ 'useOTL' => 0xFF,
+ 'kashida' => 75,
+ ]],
+ 'arvo' => ['name' => 'Arvo',
+ 'stack' => 'Slab-Serif',
+ 'scripts' => ['latin', 'latin-ext'],
+ 'specimen' => '
Google Fonts ',
+ 'remote' => [
+ 'manifest' => 'https://fonts.google.com/download/list?family=Arvo',
+ 'R' => 'Arvo-Regular.ttf',
+ 'B' => 'Arvo-Bold.ttf',
+ 'I' => 'Arvo-Italic.ttf',
+ 'BI' => 'Arvo-BoldItalic.ttf',
+ ],
+ 'configuration' => [
+ ]],
+ 'cairo' => ['name' => 'Cairo',
+ 'stack' => 'Sans-Serif - Superellipse',
+ 'scripts' => ['latin', 'latin-ext', 'arabic'],
+ 'specimen' => '
Google Fonts ',
+ 'remote' => [
+ 'manifest' => 'https://fonts.google.com/download/list?family=Cairo',
+ 'R' => 'Cairo-Regular.ttf',
+ 'B' => 'Cairo-Bold.ttf',
+ ],
+ 'configuration' => [
+ 'useOTL' => 0xFF,
+ 'kashida' => 75,
+ ]],
+ ];
public function __construct()
{
foreach ($this->custom_fonts as $key => &$font) {
@@ -74,7 +147,7 @@ private function getUploadDirectory($fontFamily = '', $url = false)
}
return $dir;
}
- public function bread_active_fonts($fonts)
+ public function bread_active_fonts(array $fonts): array
{
return array_merge($fonts, $this->getUploadedFonts());
}
@@ -98,7 +171,7 @@ private function fontComplete($fontFamily, $fontFiles)
return false;
}
if (!isset($this->custom_fonts[$fontFamily])) {
- throw new Exception("$fontFamily not in list of defined fonts");
+ return false;
}
$fontInfo = $this->custom_fonts[$fontFamily];
@@ -123,7 +196,7 @@ private function getURLtoFileMap(string $manifest): array|false
$code = wp_remote_retrieve_response_code($response);
$body = wp_remote_retrieve_body($response);
if ($code === 200 && is_string($body)) {
- $contents = json_decode(substr($body,4), true);
+ $contents = json_decode(substr($body, 4), true);
$ret = [];
foreach ($contents['manifest']['fileRefs'] as $item) {
$split = explode('/', $item['filename']);
@@ -189,19 +262,18 @@ public function installFont(string $fontFamily)
}
}
}
+ $this->custom_fonts[$fontFamily]['actions'] = $this->calcActions($fontFamily);
$this->outputSuccess("Font $fontFamily successfully uploaded");
}
private function outputWarning($str)
{
- echo '
';
- echo '
'.esc_html($str).'
';
- echo '
';
+ wp_redirect(admin_url('admin.php?page=bmlt-enabled-bread&fontAction=warning&message=' . rawurlencode($str) . '&nonce=' . wp_create_nonce('bread_font_action')));
+ exit;
}
private function outputSuccess($str)
{
- echo '
';
- echo '
'.esc_html($str).'
';
- echo '
';
+ wp_redirect(admin_url('admin.php?page=bmlt-enabled-bread&fontAction=success&message=' . rawurlencode($str) . '&nonce=' . wp_create_nonce('bread_font_action')));
+ exit;
}
public function removeFont($fontFamily)
{
@@ -211,7 +283,8 @@ public function removeFont($fontFamily)
}
$dirname = trailingslashit($this->getUploadDirectory()) . $fontFamily;
array_map('unlink', glob("$dirname/*.*"));
- rmdir($dirname);
+ (new WP_Filesystem_Direct(null))->rmdir($dirname);
+ $this->custom_fonts[$fontFamily]['actions'] = $this->calcActions($fontFamily);
$this->outputSuccess("Font $fontFamily removed.");
}
public function bread_content_style($content_style)
@@ -259,30 +332,27 @@ function calcActions($font)
]];
}
}
- public function mpdf_init_options( $options ) {
- $fontDirs = $options['fontDir']??[];
- $fontdata = $options['fontdata']??[];
- foreach($this->getUploadedFonts() as $fontKey) {
- $fontDirs[] = $this->getUploadDirectory($fontKey);
- $info = $this->custom_fonts[$fontKey];
- $fontdata[$fontKey] = [];
- foreach(['R', 'B', 'I', 'BI'] as $style) {
- if (isset($info['remote'][$style])) {
- $fontdata[$fontKey][$style] = $info['remote'][$style];
- } elseif ($style === 'B') {
- $fontdata[$fontKey][$style] = $info['remote']['R'];
- } elseif ($style === 'BI') {
- $fontdata[$fontKey][$style] = $info['remote']['I'];
+ public function mpdf_init_options($options)
+ {
+ $fontDirs = $options['fontDir']??[];
+ $fontdata = $options['fontdata']??[];
+ foreach ($this->getUploadedFonts() as $fontKey) {
+ $fontDirs[] = $this->getUploadDirectory($fontKey);
+ $info = $this->custom_fonts[$fontKey];
+ $fontdata[$fontKey] = [];
+ foreach (['R', 'B', 'I', 'BI'] as $style) {
+ if (isset($info['remote'][$style])) {
+ $fontdata[$fontKey][$style] = $info['remote'][$style];
+ }
}
- }
- if (isset($info['configuration'])) {
- foreach ($info['configuration'] as $key => $value) {
- $fontdata[$fontKey][$key] = $value;
+ if (isset($info['configuration'])) {
+ foreach ($info['configuration'] as $key => $value) {
+ $fontdata[$fontKey][$key] = $value;
+ }
}
}
+ $options['fontDir'] = $fontDirs;
+ $options['fontdata'] = $fontdata;
+ return $options;
}
- $options['fontDir'] = $fontDirs;
- $options['fontdata'] = $fontdata;
- return $options;
- }
-}
\ No newline at end of file
+}
diff --git a/includes/class-bread.php b/includes/class-bread.php
index 3fef367f..05cdc0e9 100644
--- a/includes/class-bread.php
+++ b/includes/class-bread.php
@@ -239,32 +239,29 @@ public function getIncludedFonts()
{
$fonts = [
'dejavusanscondensed' => ['name' => 'DejaVu Sans Condensed',
- 'stack' => 'Helvetica, sans-serif',
+ 'stack' => 'Sans-Serif',
'scripts' => ['latin', 'latin-ext', 'cyrillic', 'cyrillic-ext', 'greek', 'greek-ext', 'arabic', 'vietnamese', 'hebrew'],
- 'description' => 'A clean and modern sans-serif font with extensive language support. It is a condensed version of the DejaVu Sans font, making it ideal for fitting more text in limited space while maintaining readability.',
- 'required' => true],
+ 'specimen' => '
DejaVu Fonts ',],
'dejavuserifcondensed' => ['name' => 'DejaVu Serif Condensed',
- 'stack' => 'Times New Roman, serif',
+ 'stack' => 'serif',
'scripts' => ['latin', 'latin-ext', 'cyrillic', 'cyrillic-ext', 'greek', 'greek-ext'],
- 'description' => 'A clean and modern serif font with extensive language support. It is a condensed version of the DejaVu Serif font, making it ideal for fitting more text in limited space while maintaining readability.',
- 'required' => true],
+ 'specimen' => '
DejaVu Fonts ',],
'dejavusansmono' => ['name' => 'DejaVu Sans Mono',
- 'stack' => 'Courier New, monospace',
+ 'stack' => 'monospace',
'scripts' => ['latin', 'latin-ext', 'cyrillic', 'cyrillic-ext', 'greek', 'greek-ext', 'arabic', 'vietnamese'],
- 'description' => 'A clean and modern monospace font with extensive language support. It is ideal for displaying code or tabular data, as each character occupies the same amount of horizontal space.',
- 'required' => true],
+ 'specimen' => '
DejaVu Fonts ',],
'chelvetica' => ['name' => 'Helvetica (PDF Core Font)',
'stack' => 'dejavusanscondensed, sans-serif',
'scripts' => ['latin'],
- 'description' => 'Using PDF Core Fonts results in smaller files.',],
+ 'specimen' => 'Using PDF Core Fonts results in smaller files.',],
'ccourier' => ['name' => 'Courier New (PDF Core Font)',
'stack' => 'Courier New, monospace',
'scripts' => ['latin'],
- 'description' => 'Using PDF Core Fonts results in smaller files.',],
+ 'specimen' => 'Using PDF Core Fonts results in smaller files.',],
'ctimes' => ['name' => 'Times New Roman (PDF Core Font)',
'stack' => 'dejavuserifcondensed, serif',
'scripts' => ['latin'],
- 'description' => 'Using PDF Core Fonts results in smaller files.',],
+ 'specimen' => 'Using PDF Core Fonts results in smaller files.',],
];
return $fonts;
}
diff --git a/readme.txt b/readme.txt
index 7f29bb7d..def11d64 100644
--- a/readme.txt
+++ b/readme.txt
@@ -58,7 +58,8 @@ Follow all these steps, keep in mind that once you start using bread, it's not g
= 2.10.0 =
* [bread_button] shortcode added that can generate PDFs even when the hosting provider has blocked access to the BMLT server.
* PDF Core Fonts (Helvertica, New Times Roman and Courier) may be selected
-+ Extension points added to allow for custom fonts
+* Extension points added to allow for custom fonts
+* A selection of Google Fonts can be downloaded by administrator in the new "Custom Fonts" tab,
= 2.9.15 =
* More commas cleaned up.
From 3dca927b3da5591feac74846c130be1005d1be99 Mon Sep 17 00:00:00 2001
From: otrok7 <50595291+otrok7@users.noreply.github.com>
Date: Mon, 22 Jun 2026 16:06:35 +0200
Subject: [PATCH 25/34] cleanup unused code
---
admin/class-bread-admin.php | 3 ---
bread_loadable_fonts/class-bread-loadable-fonts.php | 4 +---
2 files changed, 1 insertion(+), 6 deletions(-)
diff --git a/admin/class-bread-admin.php b/admin/class-bread-admin.php
index 885bac08..b0362d10 100644
--- a/admin/class-bread-admin.php
+++ b/admin/class-bread-admin.php
@@ -461,9 +461,6 @@ function admin_options_page()
if (!isset($fonts[$font])) {
wp_die('Request invalid');
}
- if (!isset($fonts[$font])) {
- wp_die('Request invalid');
- }
if (!isset($fonts[$font]['actions'])) {
wp_die('Request invalid');
}
diff --git a/bread_loadable_fonts/class-bread-loadable-fonts.php b/bread_loadable_fonts/class-bread-loadable-fonts.php
index d3a34753..c52a8cf5 100644
--- a/bread_loadable_fonts/class-bread-loadable-fonts.php
+++ b/bread_loadable_fonts/class-bread-loadable-fonts.php
@@ -301,9 +301,7 @@ public function bread_content_style($content_style)
$content_style .= "font-family: $font;";
$content_style .= "src:url($loc) format('truetype');";
$content_style .= "font-display: block;";
- if (isset($fontInfo['remote']['variable']) && $fontInfo['remote']['variable']) {
- $content_style .= "font-weight: 100 1000;";
- } elseif ($key == 'B' || $key == 'BI') {
+ if ($key == 'B' || $key == 'BI') {
$content_style .= "font-weight: bold;";
}
if ($key == 'I' || $key == 'BI') {
From ca579d9a2615bec3912bc1b5e4d019009017c942 Mon Sep 17 00:00:00 2001
From: otrok7 <50595291+otrok7@users.noreply.github.com>
Date: Mon, 22 Jun 2026 16:10:37 +0200
Subject: [PATCH 26/34] Turn plugin check back on
---
.github/workflows/pull-requests.yml | 13 ++++++-------
1 file changed, 6 insertions(+), 7 deletions(-)
diff --git a/.github/workflows/pull-requests.yml b/.github/workflows/pull-requests.yml
index 86e9e4d1..d2693705 100644
--- a/.github/workflows/pull-requests.yml
+++ b/.github/workflows/pull-requests.yml
@@ -30,10 +30,9 @@ jobs:
echo "ZIP_FILENAME=${ZIP_FILENAME}" >> $GITHUB_ENV
PROD=1 make build
unzip "${BUILD_DIR}/${ZIP_FILENAME}" -d "./bread"
-# Removed until https://github.com/WordPress/plugin-check-action/issues/579 is fixed
-# - name: Run plugin check
-# uses: wordpress/plugin-check-action@v1
-# with:
-# exclude-directories: 'vendor'
-# categories: 'plugin_repo'
-# ignore-warnings: true
+ - name: Run plugin check
+ uses: wordpress/plugin-check-action@v1
+ with:
+ exclude-directories: 'vendor'
+ categories: 'plugin_repo'
+ ignore-warnings: true
From 1d8427901a6ae9c989880dfe8a3612207ad4b4e7 Mon Sep 17 00:00:00 2001
From: otrok7 <50595291+otrok7@users.noreply.github.com>
Date: Mon, 22 Jun 2026 16:33:47 +0200
Subject: [PATCH 27/34] fix build
---
.github/workflows/pull-requests.yml | 2 ++
1 file changed, 2 insertions(+)
diff --git a/.github/workflows/pull-requests.yml b/.github/workflows/pull-requests.yml
index d2693705..20f8d809 100644
--- a/.github/workflows/pull-requests.yml
+++ b/.github/workflows/pull-requests.yml
@@ -30,9 +30,11 @@ jobs:
echo "ZIP_FILENAME=${ZIP_FILENAME}" >> $GITHUB_ENV
PROD=1 make build
unzip "${BUILD_DIR}/${ZIP_FILENAME}" -d "./bread"
+
- name: Run plugin check
uses: wordpress/plugin-check-action@v1
with:
+ build-dir: "./bread"
exclude-directories: 'vendor'
categories: 'plugin_repo'
ignore-warnings: true
From c6aa289e501cd4a5a923db989704b851cbf16694 Mon Sep 17 00:00:00 2001
From: otrok7 <50595291+otrok7@users.noreply.github.com>
Date: Tue, 23 Jun 2026 15:19:56 +0200
Subject: [PATCH 28/34] Added filters to font table
---
admin/js/bmlt_meeting_list.js | 3 +
admin/partials/_custom_fonts_setup.php | 62 ++++++++++++++++++-
.../class-bread-loadable-fonts.php | 15 +++++
3 files changed, 78 insertions(+), 2 deletions(-)
diff --git a/admin/js/bmlt_meeting_list.js b/admin/js/bmlt_meeting_list.js
index 50cec5e6..c806032e 100644
--- a/admin/js/bmlt_meeting_list.js
+++ b/admin/js/bmlt_meeting_list.js
@@ -596,4 +596,7 @@
hideAllTabs();
activateTab($(this));
});
+ $('.bread-font-filter').change(function() {
+ $('#filter-fonts-form').submit();
+ })
});
diff --git a/admin/partials/_custom_fonts_setup.php b/admin/partials/_custom_fonts_setup.php
index 9e8916bd..8d3c96b3 100644
--- a/admin/partials/_custom_fonts_setup.php
+++ b/admin/partials/_custom_fonts_setup.php
@@ -70,15 +70,73 @@ function prepare_items()
$sortable = array();
$this->_column_headers = array($columns, $hidden, $sortable);
$this->items = $this->fonts;
+ if (isset($_GET['script']) && $_GET['script'] != '*') {
+ $this->items = array_filter($this->items, function($font) { return in_array($_GET['script'], $font['scripts']);});
+ }
+ if (isset($_GET['letterform']) && $_GET['letterform'] != '*') {
+ $this->items = array_filter($this->items, function($font) { return $_GET['letterform'] == $font['scripts'];});
+ }
foreach ($this->items as $slug => &$info) {
$info['slug'] = $slug;
}
}
+ private function selected($a, $b): string
+ {
+ return ($a == $b) ? 'selected' : '';
+ }
+ private function getAllScripts(): array
+ {
+ $ret = [];
+ foreach($this->fonts as $font) {
+ foreach($font['scripts'] as $script) {
+ if (!in_array($script, $ret)) {
+ $ret[] = $script;
+ }
+ }
+ }
+ return $ret;
+ }
+ private function getAllLetterforms(): array
+ {
+ $ret = [];
+ foreach($this->fonts as $font) {
+ if (!in_array($font['stack'], $ret)) {
+ $ret[] = $font['stack'];
+ }
+ }
+ return $ret;
+ }
+ protected function extra_tablenav( $which )
+ {
+ $letterform = $_GET['letterform'] ?? '*';
+ $script = $_GET['script'] ?? '*'; ?>
+
+ getBreadInstance());
$table->prepare_items();
$table->display();
-}
+}
\ No newline at end of file
diff --git a/bread_loadable_fonts/class-bread-loadable-fonts.php b/bread_loadable_fonts/class-bread-loadable-fonts.php
index c52a8cf5..027121e8 100644
--- a/bread_loadable_fonts/class-bread-loadable-fonts.php
+++ b/bread_loadable_fonts/class-bread-loadable-fonts.php
@@ -79,6 +79,21 @@ class BreadLoadableFonts
'useOTL' => 0xFF,
'kashida' => 75,
]],
+ 'ibmplexsans' => ['name' => 'IBM Plex Sans',
+ 'stack' => 'Sans-Serif - Neo Grotesque',
+ 'scripts' => ['latin', 'latin-ext', 'cyrillic', 'cyrillic-ext', 'greek'],
+ 'specimen' => '
Google Fonts ',
+ 'remote' => [
+ 'manifest' => 'https://fonts.google.com/download/list?family=IBM%20Plex%20Sans%20Condensed',
+ 'R' => 'IBMPlexSansCondensed-Regular.ttf',
+ 'B' => 'IBMPlexSansCondensed-Bold.ttf',
+ 'I' => 'IBMPlexSansCondensed-Italic.ttf',
+ 'BI' => 'IBMPlexSansCondensed-BoldItalic.ttf',
+ ],
+ 'configuration' => [
+ 'useOTL' => 0xFF,
+ 'kashida' => 75,
+ ]],
'arvo' => ['name' => 'Arvo',
'stack' => 'Slab-Serif',
'scripts' => ['latin', 'latin-ext'],
From cb58fc0eac52cf366942a42084728b1443890b0a Mon Sep 17 00:00:00 2001
From: otrok7 <50595291+otrok7@users.noreply.github.com>
Date: Tue, 23 Jun 2026 15:56:17 +0200
Subject: [PATCH 29/34] cleanup
---
admin/partials/_custom_fonts_setup.php | 18 +++++++++---------
.../class-bread-loadable-fonts.php | 16 ++++++++--------
includes/class-bread.php | 12 ++++++------
3 files changed, 23 insertions(+), 23 deletions(-)
diff --git a/admin/partials/_custom_fonts_setup.php b/admin/partials/_custom_fonts_setup.php
index 8d3c96b3..50d1f816 100644
--- a/admin/partials/_custom_fonts_setup.php
+++ b/admin/partials/_custom_fonts_setup.php
@@ -37,7 +37,7 @@ function column_default($font, $column)
{
switch ($column) {
case 'type':
- $stack = $font['stack'];
+ $stack = $font['letterform'];
$array = explode(',', $stack);
return $array[count($array)-1];
case 'scripts':
@@ -74,13 +74,13 @@ function prepare_items()
$this->items = array_filter($this->items, function($font) { return in_array($_GET['script'], $font['scripts']);});
}
if (isset($_GET['letterform']) && $_GET['letterform'] != '*') {
- $this->items = array_filter($this->items, function($font) { return $_GET['letterform'] == $font['scripts'];});
+ $this->items = array_filter($this->items, function($font) { return $_GET['letterform'] == $font['letterform'];});
}
foreach ($this->items as $slug => &$info) {
$info['slug'] = $slug;
}
}
- private function selected($a, $b): string
+ private function selected($a, $b)
{
return ($a == $b) ? 'selected' : '';
}
@@ -100,8 +100,8 @@ private function getAllLetterforms(): array
{
$ret = [];
foreach($this->fonts as $font) {
- if (!in_array($font['stack'], $ret)) {
- $ret[] = $font['stack'];
+ if (!in_array($font['letterform'], $ret)) {
+ $ret[] = $font['letterform'];
}
}
return $ret;
@@ -114,19 +114,19 @@ protected function extra_tablenav( $which )
Filter by supported scripts
- selected( $script, '*' ); ?> value="*">
+ selected( $script, '*' ); ?> value="*">
getAllScripts() as $s) {
- echo 'selected($script, $s) . "value='$s'>$s ";
+ echo 'selected($script, $s) . " value='$s'>$s ";
}
?>
Filter by letterform
- selected( $letterform, '*' ); ?> value="*">
+ selected( $letterform, '*' ); ?> value="*">
getAllLetterforms() as $s) {
- echo 'selected($script, $s) . "value='$s'>$s ";
+ echo 'selected($letterform, $s) . " value='$s'>$s ";
}
?>
diff --git a/bread_loadable_fonts/class-bread-loadable-fonts.php b/bread_loadable_fonts/class-bread-loadable-fonts.php
index 027121e8..3061b695 100644
--- a/bread_loadable_fonts/class-bread-loadable-fonts.php
+++ b/bread_loadable_fonts/class-bread-loadable-fonts.php
@@ -7,7 +7,7 @@ class BreadLoadableFonts
var $custom_fonts = [
'roboto' => ['name' => 'Roboto',
- 'stack' => 'Sans-Serif - Neo-Grotesque',
+ 'letterform' => 'Sans-Serif - Neo-Grotesque',
'scripts' => ['latin', 'latin-ext', 'cyrillic', 'cyrillic-ext', 'greek', 'greek-ext'],
'specimen' => '
Google Fonts ',
'remote' => [
@@ -22,7 +22,7 @@ class BreadLoadableFonts
'kashida' => 75,
]],
'rubik' => ['name' => 'Rubik',
- 'stack' => 'Sans-Serif - Neo-Grotesque',
+ 'letterform' => 'Sans-Serif - Neo-Grotesque',
'scripts' => ['latin', 'latin-ext', 'cyrillic', 'cyrillic-ext', 'arabic'],
'specimen' => '
Google Fonts ',
'remote' => [
@@ -37,7 +37,7 @@ class BreadLoadableFonts
'kashida' => 75,
]],
'oswald' => ['name' => 'Oswald',
- 'stack' => 'Sans-Serif - Neo-Grotesque',
+ 'letterform' => 'Sans-Serif - Neo-Grotesque',
'scripts' => ['latin', 'latin-ext', 'cyrillic', 'cyrillic-ext'],
'specimen' => '
Google Fonts ',
'remote' => [
@@ -50,7 +50,7 @@ class BreadLoadableFonts
'kashida' => 75,
]],
'opensans' => ['name' => 'Open Sans Condensed',
- 'stack' => 'Sans-Serif - Humanist',
+ 'letterform' => 'Sans-Serif - Humanist',
'scripts' => ['latin', 'latin-ext', 'cyrillic', 'cyrillic-ext', 'greek'],
'specimen' => '
Google Fonts ',
'remote' => [
@@ -65,7 +65,7 @@ class BreadLoadableFonts
'kashida' => 75,
]],
'arimo' => ['name' => 'Arimo',
- 'stack' => 'Sans-Serif - Arial-like',
+ 'letterform' => 'Sans-Serif - Arial-like',
'scripts' => ['latin', 'latin-ext', 'cyrillic', 'cyrillic-ext', 'greek', 'hebrew'],
'specimen' => '
Google Fonts ',
'remote' => [
@@ -80,7 +80,7 @@ class BreadLoadableFonts
'kashida' => 75,
]],
'ibmplexsans' => ['name' => 'IBM Plex Sans',
- 'stack' => 'Sans-Serif - Neo Grotesque',
+ 'letterform' => 'Sans-Serif - Neo Grotesque',
'scripts' => ['latin', 'latin-ext', 'cyrillic', 'cyrillic-ext', 'greek'],
'specimen' => '
Google Fonts ',
'remote' => [
@@ -95,7 +95,7 @@ class BreadLoadableFonts
'kashida' => 75,
]],
'arvo' => ['name' => 'Arvo',
- 'stack' => 'Slab-Serif',
+ 'letterform' => 'Slab-Serif',
'scripts' => ['latin', 'latin-ext'],
'specimen' => '
Google Fonts ',
'remote' => [
@@ -108,7 +108,7 @@ class BreadLoadableFonts
'configuration' => [
]],
'cairo' => ['name' => 'Cairo',
- 'stack' => 'Sans-Serif - Superellipse',
+ 'letterform' => 'Sans-Serif - Superellipse',
'scripts' => ['latin', 'latin-ext', 'arabic'],
'specimen' => '
Google Fonts ',
'remote' => [
diff --git a/includes/class-bread.php b/includes/class-bread.php
index 05cdc0e9..c8391678 100644
--- a/includes/class-bread.php
+++ b/includes/class-bread.php
@@ -239,27 +239,27 @@ public function getIncludedFonts()
{
$fonts = [
'dejavusanscondensed' => ['name' => 'DejaVu Sans Condensed',
- 'stack' => 'Sans-Serif',
+ 'letterform' => 'Sans-Serif',
'scripts' => ['latin', 'latin-ext', 'cyrillic', 'cyrillic-ext', 'greek', 'greek-ext', 'arabic', 'vietnamese', 'hebrew'],
'specimen' => '
DejaVu Fonts ',],
'dejavuserifcondensed' => ['name' => 'DejaVu Serif Condensed',
- 'stack' => 'serif',
+ 'letterform' => 'Serif',
'scripts' => ['latin', 'latin-ext', 'cyrillic', 'cyrillic-ext', 'greek', 'greek-ext'],
'specimen' => '
DejaVu Fonts ',],
'dejavusansmono' => ['name' => 'DejaVu Sans Mono',
- 'stack' => 'monospace',
+ 'letterform' => 'Monospace',
'scripts' => ['latin', 'latin-ext', 'cyrillic', 'cyrillic-ext', 'greek', 'greek-ext', 'arabic', 'vietnamese'],
'specimen' => '
DejaVu Fonts ',],
'chelvetica' => ['name' => 'Helvetica (PDF Core Font)',
- 'stack' => 'dejavusanscondensed, sans-serif',
+ 'letterform' => 'Sans-Serif',
'scripts' => ['latin'],
'specimen' => 'Using PDF Core Fonts results in smaller files.',],
'ccourier' => ['name' => 'Courier New (PDF Core Font)',
- 'stack' => 'Courier New, monospace',
+ 'letterform' => 'Monospace',
'scripts' => ['latin'],
'specimen' => 'Using PDF Core Fonts results in smaller files.',],
'ctimes' => ['name' => 'Times New Roman (PDF Core Font)',
- 'stack' => 'dejavuserifcondensed, serif',
+ 'letterform' => 'Serif',
'scripts' => ['latin'],
'specimen' => 'Using PDF Core Fonts results in smaller files.',],
];
From 4c250a1797a3f93c58c66437c99d2bae67d7ab7a Mon Sep 17 00:00:00 2001
From: otrok7 <50595291+otrok7@users.noreply.github.com>
Date: Tue, 23 Jun 2026 23:30:25 +0200
Subject: [PATCH 30/34] Corrections to fonts
---
admin/class-bread-admin.php | 4 +-
admin/partials/_custom_fonts_setup.php | 34 ++++----
.../class-bread-loadable-fonts.php | 83 ++++++++++++++-----
3 files changed, 84 insertions(+), 37 deletions(-)
diff --git a/admin/class-bread-admin.php b/admin/class-bread-admin.php
index b0362d10..f87b9625 100644
--- a/admin/class-bread-admin.php
+++ b/admin/class-bread-admin.php
@@ -450,7 +450,9 @@ function admin_options_page()
}
switch ($action) {
case 'success':
- $this->outputSuccess(esc_html($_GET['message']));
+ if (isset($_GET['message'])) {
+ $this->outputSuccess(esc_html($_GET['message']));
+ }
break;
case 'warning':
$this->outputWarning(esc_html($_GET['message']));
diff --git a/admin/partials/_custom_fonts_setup.php b/admin/partials/_custom_fonts_setup.php
index 50d1f816..bde9b6df 100644
--- a/admin/partials/_custom_fonts_setup.php
+++ b/admin/partials/_custom_fonts_setup.php
@@ -71,10 +71,14 @@ function prepare_items()
$this->_column_headers = array($columns, $hidden, $sortable);
$this->items = $this->fonts;
if (isset($_GET['script']) && $_GET['script'] != '*') {
- $this->items = array_filter($this->items, function($font) { return in_array($_GET['script'], $font['scripts']);});
+ $this->items = array_filter($this->items, function ($font) {
+ return in_array($_GET['script'], $font['scripts']);
+ });
}
if (isset($_GET['letterform']) && $_GET['letterform'] != '*') {
- $this->items = array_filter($this->items, function($font) { return $_GET['letterform'] == $font['letterform'];});
+ $this->items = array_filter($this->items, function ($font) {
+ return $_GET['letterform'] == $font['letterform'];
+ });
}
foreach ($this->items as $slug => &$info) {
$info['slug'] = $slug;
@@ -87,8 +91,8 @@ private function selected($a, $b)
private function getAllScripts(): array
{
$ret = [];
- foreach($this->fonts as $font) {
- foreach($font['scripts'] as $script) {
+ foreach ($this->fonts as $font) {
+ foreach ($font['scripts'] as $script) {
if (!in_array($script, $ret)) {
$ret[] = $script;
}
@@ -99,39 +103,39 @@ private function getAllScripts(): array
private function getAllLetterforms(): array
{
$ret = [];
- foreach($this->fonts as $font) {
+ foreach ($this->fonts as $font) {
if (!in_array($font['letterform'], $ret)) {
$ret[] = $font['letterform'];
}
}
return $ret;
}
- protected function extra_tablenav( $which )
+ protected function extra_tablenav($which)
{
$letterform = $_GET['letterform'] ?? '*';
$script = $_GET['script'] ?? '*'; ?>
- ['name' => 'Roboto',
'letterform' => 'Sans-Serif - Neo-Grotesque',
- 'scripts' => ['latin', 'latin-ext', 'cyrillic', 'cyrillic-ext', 'greek', 'greek-ext'],
+ 'scripts' => ['latin', 'cyrillic', 'greek', 'greek-ext'],
'specimen' => '
Google Fonts ',
'remote' => [
'manifest' => 'https://fonts.google.com/download/list?family=Roboto',
@@ -23,7 +23,7 @@ class BreadLoadableFonts
]],
'rubik' => ['name' => 'Rubik',
'letterform' => 'Sans-Serif - Neo-Grotesque',
- 'scripts' => ['latin', 'latin-ext', 'cyrillic', 'cyrillic-ext', 'arabic'],
+ 'scripts' => ['latin', 'cyrillic', 'arabic', 'hebrew'],
'specimen' => '
Google Fonts ',
'remote' => [
'manifest' => 'https://fonts.google.com/download/list?family=Rubik',
@@ -36,22 +36,9 @@ class BreadLoadableFonts
'useOTL' => 0xFF,
'kashida' => 75,
]],
- 'oswald' => ['name' => 'Oswald',
- 'letterform' => 'Sans-Serif - Neo-Grotesque',
- 'scripts' => ['latin', 'latin-ext', 'cyrillic', 'cyrillic-ext'],
- 'specimen' => '
Google Fonts ',
- 'remote' => [
- 'manifest' => 'https://fonts.google.com/download/list?family=Oswald',
- 'R' => 'Oswald-Regular.ttf',
- 'B' => 'Oswald-Bold.ttf',
- ],
- 'configuration' => [
- 'useOTL' => 0xFF,
- 'kashida' => 75,
- ]],
'opensans' => ['name' => 'Open Sans Condensed',
'letterform' => 'Sans-Serif - Humanist',
- 'scripts' => ['latin', 'latin-ext', 'cyrillic', 'cyrillic-ext', 'greek'],
+ 'scripts' => ['latin', 'cyrillic', 'greek'],
'specimen' => '
Google Fonts ',
'remote' => [
'manifest' => 'https://fonts.google.com/download/list?family=Open%20Sans',
@@ -66,7 +53,7 @@ class BreadLoadableFonts
]],
'arimo' => ['name' => 'Arimo',
'letterform' => 'Sans-Serif - Arial-like',
- 'scripts' => ['latin', 'latin-ext', 'cyrillic', 'cyrillic-ext', 'greek', 'hebrew'],
+ 'scripts' => ['latin', 'cyrillic', 'greek', 'hebrew'],
'specimen' => '
Google Fonts ',
'remote' => [
'manifest' => 'https://fonts.google.com/download/list?family=Arimo',
@@ -79,9 +66,22 @@ class BreadLoadableFonts
'useOTL' => 0xFF,
'kashida' => 75,
]],
+ 'lato' => ['name' => 'Lato',
+ 'letterform' => 'Sans-Serif - Humanist',
+ 'scripts' => ['latin'],
+ 'specimen' => '
Google Fonts ',
+ 'remote' => [
+ 'manifest' => 'https://fonts.google.com/download/list?family=Lato',
+ 'R' => 'Lato-Regular.ttf',
+ 'B' => 'Lato-Bold.ttf',
+ 'I' => 'Lato-Italic.ttf',
+ 'BI' => 'Lato-BoldItalic.ttf',
+ ],
+ 'configuration' => [
+ ]],
'ibmplexsans' => ['name' => 'IBM Plex Sans',
'letterform' => 'Sans-Serif - Neo Grotesque',
- 'scripts' => ['latin', 'latin-ext', 'cyrillic', 'cyrillic-ext', 'greek'],
+ 'scripts' => ['latin', 'cyrillic', 'greek'],
'specimen' => '
Google Fonts ',
'remote' => [
'manifest' => 'https://fonts.google.com/download/list?family=IBM%20Plex%20Sans%20Condensed',
@@ -94,6 +94,47 @@ class BreadLoadableFonts
'useOTL' => 0xFF,
'kashida' => 75,
]],
+ 'ibmplexsansarabic' => ['name' => 'IBM Plex Sans Arabic',
+ 'letterform' => 'Sans-Serif - Neo Grotesque',
+ 'scripts' => ['latin', 'arabic'],
+ 'specimen' => '
Google Fonts ',
+ 'remote' => [
+ 'manifest' => 'https://fonts.google.com/download/list?family=IBM%20Plex%20Sans%20Arabic',
+ 'R' => 'IBMPlexSansArabic-Regular.ttf',
+ 'B' => 'IBMPlexSansArabic-Bold.ttf',
+ ],
+ 'configuration' => [
+ 'useOTL' => 0xFF,
+ 'kashida' => 75,
+ ]],
+ 'ibmplexsanshebrew' => ['name' => 'IBM Plex Sans Hebrew',
+ 'letterform' => 'Sans-Serif - Neo Grotesque',
+ 'scripts' => ['latin', 'hebrew'],
+ 'specimen' => '
Google Fonts ',
+ 'remote' => [
+ 'manifest' => 'https://fonts.google.com/download/list?family=IBM%20Plex%20Sans%20Hebrew',
+ 'R' => 'IBMPlexSansHebrew-Regular.ttf',
+ 'B' => 'IBMPlexSansHebrew-Bold.ttf',
+ ],
+ 'configuration' => [
+ 'useOTL' => 0xFF,
+ 'kashida' => 75,
+ ]],
+ 'ibmplexserif' => ['name' => 'IBM Plex Serif',
+ 'letterform' => 'Serif - Scotch',
+ 'scripts' => ['latin', 'cyrillic', 'cyrillic-ext'],
+ 'specimen' => '
Google Fonts ',
+ 'remote' => [
+ 'manifest' => 'https://fonts.google.com/download/list?family=IBM%20Plex%20Serif',
+ 'R' => 'IBMPlexSerif-Regular.ttf',
+ 'B' => 'IBMPlexSerif-Bold.ttf',
+ 'I' => 'IBMPlexSerif-Italic.ttf',
+ 'BI' => 'IBMPlexSerif-BoldItalic.ttf',
+ ],
+ 'configuration' => [
+ 'useOTL' => 0xFF,
+ 'kashida' => 75,
+ ]],
'arvo' => ['name' => 'Arvo',
'letterform' => 'Slab-Serif',
'scripts' => ['latin', 'latin-ext'],
@@ -109,7 +150,7 @@ class BreadLoadableFonts
]],
'cairo' => ['name' => 'Cairo',
'letterform' => 'Sans-Serif - Superellipse',
- 'scripts' => ['latin', 'latin-ext', 'arabic'],
+ 'scripts' => ['latin', 'arabic'],
'specimen' => '
Google Fonts ',
'remote' => [
'manifest' => 'https://fonts.google.com/download/list?family=Cairo',
@@ -332,14 +373,14 @@ function calcActions($font)
if (in_array($font, $this->getUploadedFonts())) {
return [
'removefont' => [
- 'text' => __('remove', 'bread'),
+ 'text' => 'Remove',
'action' => 'removefont',
'lambda' => [$this, 'removeFont']
]];
} else {
return [
'installfont' => [
- 'text' => __('Install', 'bread'),
+ 'text' => 'Install',
'action' => 'installfont',
'lambda' => [$this, 'installFont']
]];
From d570f925cffa87e0695a64af76ec29f6bf6eef68 Mon Sep 17 00:00:00 2001
From: otrok7 <50595291+otrok7@users.noreply.github.com>
Date: Wed, 24 Jun 2026 00:23:36 +0200
Subject: [PATCH 31/34] lint
---
admin/partials/_custom_fonts_setup.php | 10 +++++-----
bread_loadable_fonts/class-bread-loadable-fonts.php | 2 +-
2 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/admin/partials/_custom_fonts_setup.php b/admin/partials/_custom_fonts_setup.php
index bde9b6df..b20ac83a 100644
--- a/admin/partials/_custom_fonts_setup.php
+++ b/admin/partials/_custom_fonts_setup.php
@@ -115,22 +115,22 @@ protected function extra_tablenav($which)
$letterform = $_GET['letterform'] ?? '*';
$script = $_GET['script'] ?? '*'; ?>