Fix SVG sprites: convert classes to inline styles, prefix IDs

- 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>
This commit is contained in:
2026-01-26 20:08:23 +00:00
parent 19a7fe06a8
commit 724eba4cd6
29 changed files with 5384 additions and 5313 deletions

View File

@@ -21,6 +21,79 @@ $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
*/
@@ -47,35 +120,15 @@ function buildSprite(string $sourceDir, string $outputFile, string $modeSuffix):
preg_match('/viewBox="([^"]+)"/', $content, $viewBoxMatch);
$viewBox = $viewBoxMatch[1] ?? '0 0 45 45';
// Extract width/height if present
preg_match('/width="([^"]+)"/', $content, $widthMatch);
preg_match('/height="([^"]+)"/', $content, $heightMatch);
$width = $widthMatch[1] ?? null;
$height = $heightMatch[1] ?? null;
// Process the icon (convert styles, prefix IDs)
$innerContent = processIcon($content, $filename);
// Remove XML declaration and outer SVG tags
$content = preg_replace('/<\?xml[^>]+\?>/', '', $content);
$content = preg_replace('/<!--[^>]*-->/', '', $content);
// Extract inner content (everything between <svg> and </svg>)
if (preg_match('/<svg[^>]*>(.*)<\/svg>/s', $content, $match)) {
$innerContent = trim($match[1]);
} else {
if (empty($innerContent)) {
echo " Warning: Could not parse $filename\n";
continue;
}
// Clean up the inner content - remove defs with clip-paths that reference unique IDs
// and inline styles where possible
$innerContent = preg_replace('/<defs>.*?<\/defs>/s', '', $innerContent);
$innerContent = preg_replace('/clip-path="url\([^)]+\)"/', '', $innerContent);
$innerContent = preg_replace('/<clipPath[^>]*>.*?<\/clipPath>/s', '', $innerContent);
$innerContent = preg_replace('/<g[^>]*>\s*<\/g>/', '', $innerContent);
// Use filename as ID (already includes mode suffix like visa_lm)
$id = $filename;
$symbols[] = " <symbol id=\"$id\" viewBox=\"$viewBox\">$innerContent</symbol>";
$symbols[] = " <symbol id=\"$filename\" viewBox=\"$viewBox\">$innerContent</symbol>";
}
if (empty($symbols)) {
@@ -83,7 +136,7 @@ function buildSprite(string $sourceDir, string $outputFile, string $modeSuffix):
return;
}
$sprite = "<svg xmlns=\"http://www.w3.org/2000/svg\" style=\"display:none\">\n";
$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";