| Server IP : 46.105.57.169 / Your IP : 216.73.216.144 Web Server : Apache System : Linux webd003.cluster120.gra.hosting.ovh.net 5.15.206-ovh-vps-grsec-zfs-classid #1 SMP Fri May 15 02:41:25 UTC 2026 x86_64 User : maitricfuz ( 93378) PHP Version : 8.4.10 Disable Function : _dyuweyrj4,_dyuweyrj4r,dl MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : OFF | Pkexec : OFF Directory : /home/maitricfuz/www/saint-martin-lg/plugins/system/jtaldef/src/Service/ |
Upload File : |
<?php
/**
* Automatic local download external files
*
* @package Joomla.Plugin
* @subpackage System.Jtaldef
*
* @author Guido De Gobbis <support@joomtools.de>
* @copyright JoomTools.de - All rights reserved.
* @license GNU General Public License version 3 or later
*/
namespace JoomTools\Plugin\System\Jtaldef\Service;
// phpcs:disable PSR1.Files.SideEffects
\defined('_JEXEC') or die;
// phpcs:enable PSR1.Files.SideEffects
use Joomla\CMS\Factory;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Uri\Uri;
use Joomla\Filesystem\File;
use JoomTools\Plugin\System\Jtaldef\Helper\JtaldefHelper;
use JoomTools\Plugin\System\Jtaldef\JtaldefAwareTrait;
use JoomTools\Plugin\System\Jtaldef\JtaldefInterface;
/**
* Download and save Google Fonts
*
* @since 2.0.4
*/
class GoogleFontsFontsource implements JtaldefInterface
{
use JtaldefAwareTrait {
JtaldefAwareTrait::getNewFileContentLink as getNewFileContentLinkTrait;
}
/**
* URL to fonts API.
*
* @var string
* @since 2.0.4
*/
const GF_DATA_API = 'https://api.fontsource.org/v1/fonts';
/**
* All the Google Fonts data for the font.
*
* @var array
* @since 2.0.4
*/
private static $googleFontsJson = [];
/**
* Font name of the Google Font.
*
* @var string
* @since 2.0.4
*/
private $fontName;
/**
* Value of font-variant for the Google Font.
*
* @var array
* @since 2.0.4
*/
private $variants;
/**
* Subsets of the Google Font.
*
* @var array
* @since 2.0.4
*/
private $fontsSubsets;
/**
* Value of font-display for the Google Font.
*
* @var string
* @since 2.0.4
*/
private $fontsDisplay;
/**
* Font data collected from API - via JSON for this font.
*
* @var array
* @since 2.0.4
*/
private $fontData;
/**
* Constructor
*
* @return void
*
* @since 2.0.4
*/
public function __construct()
{
// The real name of the Service.
$this->set('name', 'Google Fonts (Fontsource)');
// Trigger to parse <script/> tags.
$this->set('parseScripts', false);
// List of values to trigger the service.
$this->set(
'stringsToTrigger',
['fonts.googleapis.com']
);
// List of namespaces to remove matches from DOM if not parsed.
$this->set(
'nsToRemoveNotParsedItemsFromDom',
[
"//*[contains(@href,'fonts.gstatic.com') or contains(@href,'fonts.googleapis.com')]",
"//*[contains(@src,'fonts.gstatic.com') or contains(@src,'fonts.googleapis.com')]",
]
);
}
/**
* Description
*
* @param string $link Link to parse.
*
* @return string False if no font info is set in the query else the local path to the css file.
* @throws \Exception If the file couldn't be saved.
*
* @since 2.0.4
*/
public function getNewFileContentLink($link)
{
$css = [];
$link = $this->getNewFileContentLinkTrait($link);
$fonts = $this->getFontInfoByQuery($link);
if (!$fonts) {
return false;
}
$this->fontsSubsets = $fonts['subsets'];
$this->fontsDisplay = $fonts['display'] ?? null;
foreach ($fonts['families'] as $font) {
$this->fontName = $font['name'];
$this->variants = $font['variants'];
$this->fontData = $this->getGoogleFontsJson();
// Generate the CSS for this family
$css = array_merge(
$css,
$this->generateCss()
);
}
if (empty($css)) {
return false;
}
$css = implode(PHP_EOL, $css);
$filename = md5($css) . '.css';
return JtaldefHelper::saveFile($filename, $css);
}
/**
* Get fonts credentials
*
* @param string $url Url to parse
*
* @return array|boolean Return false if no query is set
*
* @since 2.0.4
*/
private function getFontInfoByQuery($url)
{
$url = JtaldefHelper::normalizeUrl($url);
// Parse URL to determine families and subsets
$query = parse_url($url, PHP_URL_QUERY);
return !empty($query) ? $this->parseFontsQuery($query) : false;
}
/**
* Parses a Google Fonts query string and returns an array
* of families and subsets used.
*
* NOTE: Data must NOT be urlencoded.
*
* @param string $query The query string to scan
*
* @return array
*
* @since 2.0.4
*/
private function parseFontsQuery($query)
{
$return = [];
$parsedList = [];
$subsets = [];
// Filter empty values coming from && produced by Joomla51 themes
$parsedProcessing = array_filter(explode('&', $query));
foreach ($parsedProcessing as $var) {
list($key, $value) = explode('=', $var);
$key = trim($key);
$value = trim($value);
if ($key == 'family') {
if (false === strpos($value, '|')) {
$parsedList[$key][] = $value;
$parsedList[$key] = array_filter($parsedList[$key]);
continue;
}
$value = explode('|', $value);
$parsedList[$key] = array_filter(array_map('trim', $value));
continue;
}
$parsedList[$key] = $value;
}
$families = $parsedList['family'];
if (!empty($parsedList['subset'])) {
$subsets = explode(',', $parsedList['subset']);
}
// Define 'swap' as the default font-display, if there isn't set by URL
$return['display'] = 'swap';
if (!empty($parsedList['display'])) {
$return['display'] = $parsedList['display'];
}
// Parse variants/weights and font names
foreach ($families as $k => $font) {
$variantsList = [];
$fontQuery = explode(':', $font);
$fontName = trim($fontQuery[0]);
if (empty($fontQuery[1])) {
$variantsList['italic'] = ['100', '200', '300', '400', '500', '600', '700', '800', '900'];
$variantsList['normal'] = ['100', '200', '300', '400', '500', '600', '700', '800', '900'];
}
if (empty($variantsList)) {
$variantsList = explode(',', $fontQuery[1]);
if (false !== strpos($fontQuery[1], '@')) {
list($styleTypes, $variantsList) = explode('@', $fontQuery[1]);
$styleTypes = array_reverse(explode(',', $styleTypes));
$variantsProcessing = explode(';', $variantsList);
$variantsList = [];
foreach ($variantsProcessing as $variant) {
if (false !== strpos($variant, ',')) {
list($key, $value) = explode(',', $variant);
if (empty($styleTypes[$key])) {
continue;
}
$style = strtolower($styleTypes[$key]);
if ($style == 'ital') {
$style = 'italic';
}
if ($style == 'wght') {
$style = 'normal';
}
$variantsList[$style][] = $value;
continue;
}
if (empty($styleTypes[0])) {
$style = 'normal';
} else {
$style = strtolower($styleTypes[0]);
}
if ($style == 'ital') {
$style = 'italic';
}
if ($style == 'wght') {
$style = 'normal';
}
$variantsList[$style][] = $variant;
}
}
}
if (!empty($variantsList) && !isset($variantsList['normal'])) {
$variantsList = $this->normalizeVariants($variantsList);
}
$families[$k] = [
'name' => $fontName,
'variants' => $variantsList,
];
// Third chunk - probably a subset here
if (!empty($fontQuery[2])) {
// Split and trim
$fontSubs = array_map('trim', explode(',', $fontQuery[2]));
// Add it to the subsets array
$subsets = array_merge($subsets, $fontSubs);
}
}
// Remove duplicates
$subsets = array_unique($subsets);
$return['families'] = $families;
$return['subsets'] = $subsets;
return $return;
}
/**
* Load and return the Google Fonts data from google-webfonts-helper.herokuapp.com
*
* @return array
*
* @since 2.0.4
*/
private function getGoogleFontsJson()
{
$fontId = strtolower(str_replace([' ', '+'], '-', $this->fontName));
if (empty(self::$googleFontsJson[$fontId])) {
$cacheFile = JtaldefHelper::getCacheFilePath($fontId . '.json');
if (file_exists(JPATH_ROOT . '/' . $cacheFile)) {
$content = file_get_contents($cacheFile);
} else {
$fontApiUrl = self::GF_DATA_API . '/' . $fontId;
try {
$response = JtaldefHelper::getHttpResponseData($fontApiUrl);
$statusCode = $response->getStatusCode();
$content = (string) $response->getBody();
} catch (\RuntimeException $e) {
if (JtaldefHelper::$debug) {
Factory::getApplication()->enqueueMessage(
sprintf(
"<strong>" . addslashes(__METHOD__) . "():</strong>"
. "<br /><strong>URL:</strong> %s"
. "<br />%s",
$fontApiUrl,
$e->getMessage()
),
'error'
);
}
return [];
}
if ($statusCode != 200 || empty($content)) {
$fontsSubsets = !empty($this->fontsSubsets)
? ' (' . implode(',', $this->fontsSubsets) . ')'
: '';
if (JtaldefHelper::$debug) {
Factory::getApplication()->enqueueMessage(
Text::sprintf(
'PLG_SYSTEM_JTALDEF_ERROR_WHILE_DOWNLOADING_FONT',
$this->fontName . $fontsSubsets,
$fontApiUrl,
$content
),
'error'
);
}
return [];
}
JtaldefHelper::saveFile($fontId . '.json', $content);
}
$result = json_decode($content, true);
self::$googleFontsJson[$fontId] = $result;
}
return self::$googleFontsJson[$fontId];
}
/**
* Generate CSS based on variants for the subsets
*
* @return array
* @throws \Exception
*
* @since 2.0.4
*/
private function generateCss()
{
if (empty($this->fontData)) {
return [];
}
$css = [];
foreach ($this->variants as $style => $weights) {
if (
isset($this->fontData['styles'])
&& !in_array($style, $this->fontData['styles'])
) {
continue;
}
foreach ($weights as $weight) {
if (
isset($this->fontData['weights'])
&& !in_array($weight, $this->fontData['weights'])
) {
continue;
}
// Weight doesn't exist?
if (
!isset($this->fontData['variants'])
|| empty($data = $this->fontData['variants'][$weight][$style])
) {
continue;
}
// Font data (from JSON)
$fontData = $this->downloadFonts($data);
foreach ($fontData as $subset => $urls) {
// Common CSS rules to create
$rules = [
'font-family: "' . $this->fontData['family'] . '"',
'font-weight: ' . (int) $weight,
'font-style: ' . $style,
'font-display: ' . $this->fontsDisplay,
];
// Build src array
$src = [];
$src[] = "url(" . $urls['woff2'] . ") format('woff2')";
$src[] = "url(" . $urls['woff'] . ") format('woff')";
// Add to rules array
$rules[] = 'src: ' . implode(', ', $src);
// Set unicode ranges
$rules[] = 'unicode-range: ' . $this->fontData['unicodeRange'][$subset];
// Add some formatting
$rules = array_map(
function ($rule) {
return "\t" . $rule . ";";
},
$rules
);
// Add to final CSS
$css[] = "/* " . $subset . " */\n@font-face {\n" . implode("\n", $rules) . "\n}";
}
}
}
if (!empty($css)) {
array_unshift($css, '/* ' . $this->fontName . ' (' . implode(',', $this->fontsSubsets) . ') */');
}
return $css;
}
/**
* Normalize variants from Google API v1
*
* @param string[] $variants The list of variants from Google API v1
*
* @return array
*
* @since 2.0.4
*/
private function normalizeVariants($variants)
{
$return = [];
foreach ($variants as $variant) {
$style = 'normal';
if ($variant == 'regular') {
$variant = '400';
}
if ($variant == 'italic') {
$style = 'italic';
$variant = '400';
}
if (false !== strpos($variant, 'i')) {
$variant = str_replace(['italics', 'italic', 'i'], '', $variant);
$style = 'italic';
}
// Google API v1 supports bold and b as variants too
if (false !== stripos($variant, 'b')) {
$variant = str_replace(['bold', 'b'], '700', $variant);
}
// Fallback to 400
if (!$variant || (!strstr($variant, 'italic') && !is_numeric($variant))) {
$variant = '400';
}
$return[$style][] = $variant;
}
return $return;
}
/**
* Download google fonts to local filesystem.
*
* @param array $subsetsUrlList List of subsets containing the Urls for download the font.
*
* @return array List of subsets containing the local Urls of the downloaded font.
* @throws \Exception If the file couldn't be saved.
*
* @since 2.0.4
*/
private function downloadFonts($subsetsUrlList)
{
$newSubsetsUrlList = [];
if (empty($this->fontsSubsets)) {
$this->fontsSubsets = (array) $this->fontData['defSubset'] ?? [];
}
foreach ($this->fontsSubsets as $subset) {
if (
isset($this->fontData['subsets'])
&& !in_array($subset, (array) $this->fontData['subsets'])
) {
continue;
}
foreach ($subsetsUrlList[$subset]['url'] as $type => $url) {
if (strtolower($type) == 'ttf') {
continue;
}
// Set the file name
$safeFileName = File::makeSafe(basename($url));
$filePath = JtaldefHelper::getCacheFilePath($safeFileName);
if (!file_exists(JPATH_ROOT . '/' . $filePath)) {
try {
$response = JtaldefHelper::getHttpResponseData($url);
$statusCode = $response->getStatusCode();
$content = (string) $response->getBody();
} catch (\RuntimeException $e) {
if (JtaldefHelper::$debug) {
Factory::getApplication()->enqueueMessage(
sprintf(
"<strong>" . addslashes(__METHOD__) . "():</strong>"
. "<br /><strong>URL:</strong> %s"
. "<br />%s",
$url,
$e->getMessage()
),
'error'
);
}
continue;
}
if ($statusCode < 200 || $statusCode >= 400 || empty($content)) {
if (JtaldefHelper::$debug) {
Factory::getApplication()->enqueueMessage(
Text::sprintf(
'PLG_SYSTEM_JTALDEF_ERROR_WHILE_DOWNLOADING_FONT',
$this->fontName,
$url,
$content
),
'error'
);
}
continue;
}
$filePath = JtaldefHelper::saveFile($safeFileName, $content);
}
$newSubsetsUrlList[$subset][$type] = Uri::base(true) . '/' . $filePath;
}
}
return $newSubsetsUrlList;
}
}