- Convert Adobe Illustrator .st0/.st1 classes to inline styles - Prefix all IDs (defs, clipPaths) with icon name to avoid conflicts - Fix clip-path references in both attributes and inline styles - Keep defs/clipPaths intact for proper rendering Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
168 lines
5.3 KiB
PHP
168 lines
5.3 KiB
PHP
<?php
|
|
/**
|
|
* Build SVG sprites from individual icon files
|
|
* Run: php build-sprites.php
|
|
*/
|
|
|
|
$materialsDir = '/mnt/ssd/docker/ps178-mprexpresscheckout/materials/icons-v2';
|
|
$outputDir = __DIR__ . '/sprites';
|
|
|
|
// Mode mapping
|
|
$modes = [
|
|
'light-mode' => 'lm',
|
|
'dark-mode' => 'dm',
|
|
'light-accent' => 'la',
|
|
'dark-accent' => 'da',
|
|
];
|
|
|
|
// Payment shapes
|
|
$paymentShapes = ['icon', 'square', 'rectangle', 'text-only'];
|
|
|
|
// Social shapes
|
|
$socialShapes = ['icon', 'full-logo', 'text-only'];
|
|
|
|
/**
|
|
* Convert class-based styles to inline styles and clean up SVG
|
|
*/
|
|
function processIcon(string $content, string $iconId): string
|
|
{
|
|
// Extract style definitions
|
|
$styles = [];
|
|
if (preg_match('/<style[^>]*>(.*?)<\/style>/s', $content, $styleMatch)) {
|
|
// Parse CSS rules like .st0{fill:#FF0000;}
|
|
preg_match_all('/\.([a-zA-Z0-9_-]+)\s*\{([^}]+)\}/', $styleMatch[1], $rules, PREG_SET_ORDER);
|
|
foreach ($rules as $rule) {
|
|
$className = $rule[1];
|
|
$cssProps = trim($rule[2]);
|
|
// Convert CSS to inline style format
|
|
$styles[$className] = $cssProps;
|
|
}
|
|
}
|
|
|
|
// Remove style block
|
|
$content = preg_replace('/<style[^>]*>.*?<\/style>/s', '', $content);
|
|
|
|
// Remove XML declaration
|
|
$content = preg_replace('/<\?xml[^>]+\?>/', '', $content);
|
|
|
|
// Remove comments
|
|
$content = preg_replace('/<!--.*?-->/s', '', $content);
|
|
|
|
// Extract defs content for later re-insertion with prefixed IDs
|
|
$defsContent = '';
|
|
if (preg_match('/<defs>(.*?)<\/defs>/s', $content, $defsMatch)) {
|
|
$defsContent = $defsMatch[1];
|
|
// Prefix IDs in defs
|
|
$defsContent = preg_replace('/id="([^"]+)"/', 'id="' . $iconId . '_$1"', $defsContent);
|
|
$content = preg_replace('/<defs>.*?<\/defs>/s', '', $content);
|
|
}
|
|
|
|
// Update clipPath references to use prefixed IDs (both attribute and inline style)
|
|
$content = preg_replace('/clip-path="url\(#([^)]+)\)"/', 'clip-path="url(#' . $iconId . '_$1)"', $content);
|
|
$content = preg_replace('/clip-path:\s*url\(#([^)]+)\)/', 'clip-path:url(#' . $iconId . '_$1)', $content);
|
|
|
|
// Update xlink:href references
|
|
$content = preg_replace('/xlink:href="#([^"]+)"/', 'xlink:href="#' . $iconId . '_$1"', $content);
|
|
|
|
// Update clipPath IDs themselves
|
|
$content = preg_replace('/<clipPath id="([^"]+)"/', '<clipPath id="' . $iconId . '_$1"', $content);
|
|
|
|
// Replace class attributes with inline styles
|
|
foreach ($styles as $className => $cssProps) {
|
|
// Find elements with this class and add inline style
|
|
$content = preg_replace_callback(
|
|
'/class="([^"]*\b' . preg_quote($className, '/') . '\b[^"]*)"/',
|
|
function ($match) use ($cssProps) {
|
|
return 'style="' . $cssProps . '"';
|
|
},
|
|
$content
|
|
);
|
|
}
|
|
|
|
// Extract inner content (everything between <svg> and </svg>)
|
|
if (preg_match('/<svg[^>]*>(.*)<\/svg>/s', $content, $match)) {
|
|
$innerContent = trim($match[1]);
|
|
} else {
|
|
return '';
|
|
}
|
|
|
|
// Re-add defs with prefixed IDs if there were any
|
|
if (!empty($defsContent)) {
|
|
$innerContent = '<defs>' . $defsContent . '</defs>' . $innerContent;
|
|
}
|
|
|
|
return $innerContent;
|
|
}
|
|
|
|
/**
|
|
* Build a sprite from a directory of SVG files
|
|
*/
|
|
function buildSprite(string $sourceDir, string $outputFile, string $modeSuffix): void
|
|
{
|
|
if (!is_dir($sourceDir)) {
|
|
echo " Skipping: $sourceDir (not found)\n";
|
|
return;
|
|
}
|
|
|
|
$files = glob($sourceDir . '/*.svg');
|
|
if (empty($files)) {
|
|
echo " Skipping: $sourceDir (no SVG files)\n";
|
|
return;
|
|
}
|
|
|
|
$symbols = [];
|
|
|
|
foreach ($files as $file) {
|
|
$filename = basename($file, '.svg');
|
|
$content = file_get_contents($file);
|
|
|
|
// Extract viewBox from original SVG
|
|
preg_match('/viewBox="([^"]+)"/', $content, $viewBoxMatch);
|
|
$viewBox = $viewBoxMatch[1] ?? '0 0 45 45';
|
|
|
|
// Process the icon (convert styles, prefix IDs)
|
|
$innerContent = processIcon($content, $filename);
|
|
|
|
if (empty($innerContent)) {
|
|
echo " Warning: Could not parse $filename\n";
|
|
continue;
|
|
}
|
|
|
|
$symbols[] = " <symbol id=\"$filename\" viewBox=\"$viewBox\">$innerContent</symbol>";
|
|
}
|
|
|
|
if (empty($symbols)) {
|
|
echo " Skipping: $outputFile (no valid symbols)\n";
|
|
return;
|
|
}
|
|
|
|
$sprite = "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" style=\"display:none\">\n";
|
|
$sprite .= implode("\n", $symbols) . "\n";
|
|
$sprite .= "</svg>\n";
|
|
|
|
file_put_contents($outputFile, $sprite);
|
|
echo " Created: $outputFile (" . count($symbols) . " icons)\n";
|
|
}
|
|
|
|
// Build payment sprites
|
|
echo "Building payment sprites...\n";
|
|
foreach ($paymentShapes as $shape) {
|
|
foreach ($modes as $modeDir => $modeSuffix) {
|
|
$sourceDir = "$materialsDir/payment-icons/$shape/$modeDir";
|
|
$outputFile = "$outputDir/payments/$shape-$modeSuffix.svg";
|
|
buildSprite($sourceDir, $outputFile, $modeSuffix);
|
|
}
|
|
}
|
|
|
|
// Build social sprites
|
|
echo "\nBuilding social sprites...\n";
|
|
foreach ($socialShapes as $shape) {
|
|
foreach ($modes as $modeDir => $modeSuffix) {
|
|
$sourceDir = "$materialsDir/socials-icons/$shape/$modeDir";
|
|
$outputFile = "$outputDir/socials/$shape-$modeSuffix.svg";
|
|
buildSprite($sourceDir, $outputFile, $modeSuffix);
|
|
}
|
|
}
|
|
|
|
echo "\nDone!\n";
|