@@ -492,6 +492,67 @@ function removeAnimations(string $svg): string
492
492
return $ svg ;
493
493
}
494
494
495
+ /**
496
+ * Convert a color from hex 3/4/6/8 digits to hex 6 digits and opacity (0-1)
497
+ *
498
+ * @param string $color The color to convert
499
+ * @return array<string, string> The converted color
500
+ */
501
+ function convertHexColor (string $ color ): array
502
+ {
503
+ $ color = preg_replace ("/[^0-9a-fA-F]/ " , "" , $ color );
504
+
505
+ // double each character if the color is in 3/4 digit format
506
+ if (strlen ($ color ) === 3 ) {
507
+ $ chars = str_split ($ color );
508
+ $ color = "{$ chars [0 ]}{$ chars [0 ]}{$ chars [1 ]}{$ chars [1 ]}{$ chars [2 ]}{$ chars [2 ]}" ;
509
+ } elseif (strlen ($ color ) === 4 ) {
510
+ $ chars = str_split ($ color );
511
+ $ color = "{$ chars [0 ]}{$ chars [0 ]}{$ chars [1 ]}{$ chars [1 ]}{$ chars [2 ]}{$ chars [2 ]}{$ chars [3 ]}{$ chars [3 ]}" ;
512
+ }
513
+
514
+ // convert to 6 digit hex and opacity
515
+ if (strlen ($ color ) === 6 ) {
516
+ return [
517
+ "color " => "# {$ color }" ,
518
+ "opacity " => 1 ,
519
+ ];
520
+ } elseif (strlen ($ color ) === 8 ) {
521
+ return [
522
+ "color " => "# " . substr ($ color , 0 , 6 ),
523
+ "opacity " => hexdec (substr ($ color , 6 , 2 )) / 255 ,
524
+ ];
525
+ }
526
+ throw new AssertionError ("Invalid color: " . $ color );
527
+ }
528
+
529
+ /**
530
+ * Convert transparent hex colors (4/8 digits) in an SVG to hex 6 digits and corresponding opacity attribute (0-1)
531
+ *
532
+ * @param string $svg The SVG for the card as a string
533
+ * @return string The SVG with converted colors
534
+ */
535
+ function convertHexColors (string $ svg ): string
536
+ {
537
+ // convert "transparent" to "#0000"
538
+ $ svg = preg_replace ("/(fill|stroke)=[' \"]transparent[' \"]/m " , '\1="#0000" ' , $ svg );
539
+
540
+ // convert hex colors to 6 digits and corresponding opacity attribute
541
+ $ svg = preg_replace_callback (
542
+ "/(fill|stroke)=[' \"]#([0-9a-fA-F]{4}|[0-9a-fA-F]{8})[' \"]/m " ,
543
+ function ($ matches ) {
544
+ $ attribute = $ matches [1 ];
545
+ $ result = convertHexColor ($ matches [2 ]);
546
+ $ color = $ result ["color " ];
547
+ $ opacity = $ result ["opacity " ];
548
+ return "{$ attribute }=' {$ color }' {$ attribute }-opacity=' {$ opacity }' " ;
549
+ },
550
+ $ svg
551
+ );
552
+
553
+ return $ svg ;
554
+ }
555
+
495
556
/**
496
557
* Converts an SVG card to a PNG image
497
558
*
@@ -506,15 +567,6 @@ function convertSvgToPng(string $svg): string
506
567
// remove style and animations
507
568
$ svg = removeAnimations ($ svg );
508
569
509
- // replace all fully transparent colors in fill or stroke with "none"
510
- // this is a workaround for what seems to be a bug in inkscape where rgba alpha values are ignored
511
- // TODO: find a way to make partially transparent colors work (eg. #ffffff50)
512
- $ svg = preg_replace (
513
- "/(fill|stroke)=[' \"](?:#[0-9a-fA-F]{6}00|#[0-9a-fA-F]{3}0|transparent)[' \"]/m " ,
514
- '\1="none" ' ,
515
- $ svg
516
- );
517
-
518
570
// escape svg for shell
519
571
$ svg = escapeshellarg ($ svg );
520
572
@@ -560,8 +612,13 @@ function generateOutput(string|array $output, array $params = null): array
560
612
"body " => json_encode ($ data ),
561
613
];
562
614
}
563
- // Generate SVG card
564
- $ svg = gettype ($ output ) === "string " ? generateErrorCard ($ output ) : generateCard ($ output );
615
+
616
+ // generate SVG card
617
+ $ svg = gettype ($ output ) === "string " ? generateErrorCard ($ output , $ params ) : generateCard ($ output , $ params );
618
+
619
+ // some renderers such as inkscape doesn't support transparent colors in hex format, so we need to convert them
620
+ $ svg = convertHexColors ($ svg );
621
+
565
622
// output PNG card
566
623
if ($ requestedType === "png " ) {
567
624
try {
@@ -574,14 +631,16 @@ function generateOutput(string|array $output, array $params = null): array
574
631
return [
575
632
"contentType " => "image/svg+xml " ,
576
633
"status " => 500 ,
577
- "body " => generateErrorCard ($ e ->getMessage ()),
634
+ "body " => generateErrorCard ($ e ->getMessage (), $ params ),
578
635
];
579
636
}
580
637
}
638
+
581
639
// remove animations if disable_animations is set
582
640
if (isset ($ params ["disable_animations " ]) && $ params ["disable_animations " ] == "true " ) {
583
641
$ svg = removeAnimations ($ svg );
584
642
}
643
+
585
644
// output SVG card
586
645
return [
587
646
"contentType " => "image/svg+xml " ,
0 commit comments