font handling fix
This commit is contained in:
parent
d5ecb31dad
commit
fda7bd1d12
6 changed files with 727 additions and 22 deletions
|
|
@ -0,0 +1,182 @@
|
|||
<?php
|
||||
/**
|
||||
* Font Server for Maple Local Fonts.
|
||||
*
|
||||
* Serves font files and CSS through PHP with proper headers.
|
||||
* This ensures CORS and MIME types work regardless of server configuration.
|
||||
*
|
||||
* @package Maple_Local_Fonts
|
||||
*/
|
||||
|
||||
if (!defined('ABSPATH')) {
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Class MLF_Font_Server
|
||||
*
|
||||
* Handles serving font files and CSS with proper headers via REST API.
|
||||
*/
|
||||
class MLF_Font_Server {
|
||||
|
||||
/**
|
||||
* Register REST API routes.
|
||||
*/
|
||||
public function register_routes() {
|
||||
// Serve individual font files
|
||||
register_rest_route('mlf/v1', '/font/(?P<filename>[a-zA-Z0-9_\-]+\.woff2)', [
|
||||
'methods' => 'GET',
|
||||
'callback' => [$this, 'serve_font'],
|
||||
'permission_callback' => '__return_true',
|
||||
'args' => [
|
||||
'filename' => [
|
||||
'required' => true,
|
||||
'validate_callback' => [$this, 'validate_filename'],
|
||||
],
|
||||
],
|
||||
]);
|
||||
|
||||
// Serve CSS file (like Google Fonts does)
|
||||
register_rest_route('mlf/v1', '/css', [
|
||||
'methods' => 'GET',
|
||||
'callback' => [$this, 'serve_css'],
|
||||
'permission_callback' => '__return_true',
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate font filename.
|
||||
*
|
||||
* @param string $filename The filename to validate.
|
||||
* @return bool True if valid.
|
||||
*/
|
||||
public function validate_filename($filename) {
|
||||
// Only allow alphanumeric, underscore, hyphen, and .woff2 extension
|
||||
if (!preg_match('/^[a-zA-Z0-9_\-]+\.woff2$/', $filename)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prevent directory traversal
|
||||
if (strpos($filename, '..') !== false || strpos($filename, '/') !== false) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve a font file with proper headers.
|
||||
*
|
||||
* @param WP_REST_Request $request The request object.
|
||||
* @return WP_REST_Response|WP_Error Response or error.
|
||||
*/
|
||||
public function serve_font($request) {
|
||||
$filename = $request->get_param('filename');
|
||||
|
||||
// Get font directory
|
||||
$font_dir = wp_get_font_dir();
|
||||
$file_path = trailingslashit($font_dir['path']) . $filename;
|
||||
|
||||
// Validate file exists and is within fonts directory
|
||||
$real_path = realpath($file_path);
|
||||
$fonts_path = realpath($font_dir['path']);
|
||||
|
||||
if (!$real_path || !$fonts_path || strpos($real_path, $fonts_path) !== 0) {
|
||||
return new WP_Error('not_found', 'Font not found', ['status' => 404]);
|
||||
}
|
||||
|
||||
if (!file_exists($real_path)) {
|
||||
return new WP_Error('not_found', 'Font not found', ['status' => 404]);
|
||||
}
|
||||
|
||||
// Verify it's a woff2 file
|
||||
if (pathinfo($real_path, PATHINFO_EXTENSION) !== 'woff2') {
|
||||
return new WP_Error('invalid_type', 'Invalid file type', ['status' => 400]);
|
||||
}
|
||||
|
||||
// Read file content
|
||||
$content = file_get_contents($real_path);
|
||||
|
||||
if ($content === false) {
|
||||
return new WP_Error('read_error', 'Could not read font file', ['status' => 500]);
|
||||
}
|
||||
|
||||
// Verify WOFF2 magic bytes
|
||||
if (substr($content, 0, 4) !== 'wOF2') {
|
||||
return new WP_Error('invalid_format', 'Invalid font format', ['status' => 400]);
|
||||
}
|
||||
|
||||
// Send font with proper headers
|
||||
$response = new WP_REST_Response($content);
|
||||
$response->set_status(200);
|
||||
$response->set_headers([
|
||||
'Content-Type' => 'font/woff2',
|
||||
'Content-Length' => strlen($content),
|
||||
'Access-Control-Allow-Origin' => '*',
|
||||
'Access-Control-Allow-Methods' => 'GET, OPTIONS',
|
||||
'Cross-Origin-Resource-Policy' => 'cross-origin',
|
||||
'Cache-Control' => 'public, max-age=31536000, immutable',
|
||||
'Vary' => 'Accept-Encoding',
|
||||
]);
|
||||
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Serve CSS file with @font-face rules (mimics Google Fonts).
|
||||
*
|
||||
* @param WP_REST_Request $request The request object.
|
||||
* @return WP_REST_Response Response with CSS content.
|
||||
*/
|
||||
public function serve_css($request) {
|
||||
$registry = new MLF_Font_Registry();
|
||||
$fonts = $registry->get_imported_fonts();
|
||||
|
||||
if (empty($fonts)) {
|
||||
$response = new WP_REST_Response('/* No fonts installed */');
|
||||
$response->set_headers(['Content-Type' => 'text/css; charset=UTF-8']);
|
||||
return $response;
|
||||
}
|
||||
|
||||
$css = "/* Maple Local Fonts - Generated CSS */\n\n";
|
||||
|
||||
foreach ($fonts as $font) {
|
||||
$font_slug = sanitize_title($font['name']);
|
||||
|
||||
foreach ($font['variants'] as $variant) {
|
||||
$weight = $variant['weight'];
|
||||
$style = $variant['style'];
|
||||
|
||||
// Build filename
|
||||
if (strpos($weight, ' ') !== false) {
|
||||
$filename = sprintf('%s_%s_variable.woff2', $font_slug, $style);
|
||||
} else {
|
||||
$filename = sprintf('%s_%s_%s.woff2', $font_slug, $style, $weight);
|
||||
}
|
||||
|
||||
// Use REST API URL for font file
|
||||
$font_url = rest_url('mlf/v1/font/' . $filename);
|
||||
|
||||
// Generate @font-face rule (format like Google Fonts)
|
||||
$css .= "/* {$style} {$weight} */\n";
|
||||
$css .= "@font-face {\n";
|
||||
$css .= " font-family: '{$font['name']}';\n";
|
||||
$css .= " font-style: {$style};\n";
|
||||
$css .= " font-weight: {$weight};\n";
|
||||
$css .= " font-display: swap;\n";
|
||||
$css .= " src: url({$font_url}) format('woff2');\n";
|
||||
$css .= "}\n\n";
|
||||
}
|
||||
}
|
||||
|
||||
$response = new WP_REST_Response($css);
|
||||
$response->set_status(200);
|
||||
$response->set_headers([
|
||||
'Content-Type' => 'text/css; charset=UTF-8',
|
||||
'Access-Control-Allow-Origin' => '*',
|
||||
'Cache-Control' => 'public, max-age=86400',
|
||||
]);
|
||||
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue