@@ -322,6 +322,19 @@ function getCardWidth(array $params, int $numColumns = 3): int
322
322
return max ($ minimumWidth , intval ($ params ["card_width " ] ?? $ defaultWidth ));
323
323
}
324
324
325
+ /**
326
+ * Get the card height from params taking into account minimum and default values
327
+ *
328
+ * @param array<string,string> $params Request parameters
329
+ * @return int Card width
330
+ */
331
+ function getCardHeight (array $ params ): int
332
+ {
333
+ $ defaultHeight = 195 ;
334
+ $ minimumHeight = 170 ;
335
+ return max ($ minimumHeight , intval ($ params ["card_height " ] ?? $ defaultHeight ));
336
+ }
337
+
325
338
/**
326
339
* Generate SVG output for a stats array
327
340
*
@@ -364,7 +377,11 @@ function generateCard(array $stats, array $params = null): string
364
377
$ rectWidth = $ cardWidth - 1 ;
365
378
$ columnWidth = $ numColumns > 0 ? $ cardWidth / $ numColumns : 0 ;
366
379
367
- // offsets for the bars between columns
380
+ $ cardHeight = getCardHeight ($ params );
381
+ $ rectHeight = $ cardHeight - 1 ;
382
+ $ heightOffset = ($ cardHeight - 195 ) / 2 ;
383
+
384
+ // X offsets for the bars between columns
368
385
$ barOffsets = [-999 , -999 ];
369
386
for ($ i = 0 ; $ i < $ numColumns - 1 ; $ i ++) {
370
387
$ barOffsets [$ i ] = $ columnWidth * ($ i + 1 );
@@ -384,6 +401,22 @@ function generateCard(array $stats, array $params = null): string
384
401
$ currentStreakOffset = $ showCurrentStreak ? $ columnOffsets [$ nextColumnIndex ++] : -999 ;
385
402
$ longestStreakOffset = $ showLongestStreak ? $ columnOffsets [$ nextColumnIndex ++] : -999 ;
386
403
404
+ // Y offsets for the bars
405
+ $ barHeightOffsets = [28 + $ heightOffset / 2 , 170 + $ heightOffset ];
406
+ // Y offsets for the numbers and dates
407
+ $ longestStreakHeightOffset = $ totalContributionsHeightOffset = [
408
+ 48 + $ heightOffset ,
409
+ 84 + $ heightOffset ,
410
+ 114 + $ heightOffset ,
411
+ ];
412
+ $ currentStreakHeightOffset = [
413
+ 48 + $ heightOffset ,
414
+ 108 + $ heightOffset ,
415
+ 145 + $ heightOffset ,
416
+ 71 + $ heightOffset ,
417
+ 19.5 + $ heightOffset ,
418
+ ];
419
+
387
420
// total contributions
388
421
$ totalContributions = $ numFormatter ->format ($ stats ["totalContributions " ]);
389
422
$ firstContribution = formatDate ($ stats ["firstContribution " ], $ dateFormat , $ localeCode );
@@ -440,7 +473,7 @@ function generateCard(array $stats, array $params = null): string
440
473
}
441
474
442
475
return "<svg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'
443
- style='isolation: isolate' viewBox='0 0 {$ cardWidth } 195 ' width=' {$ cardWidth }px' height='195px ' direction=' {$ direction }'>
476
+ style='isolation: isolate' viewBox='0 0 {$ cardWidth } { $ cardHeight } ' width=' {$ cardWidth }px' height=' { $ cardHeight } px ' direction=' {$ direction }'>
444
477
<style>
445
478
@keyframes currstreak {
446
479
0% { font-size: 3px; opacity: 0.2; }
@@ -454,94 +487,94 @@ function generateCard(array $stats, array $params = null): string
454
487
</style>
455
488
<defs>
456
489
<clipPath id='outer_rectangle'>
457
- <rect width=' {$ cardWidth }' height='195 ' rx=' {$ borderRadius }'/>
490
+ <rect width=' {$ cardWidth }' height=' { $ cardHeight } ' rx=' {$ borderRadius }'/>
458
491
</clipPath>
459
492
<mask id='mask_out_ring_behind_fire'>
460
- <rect width=' {$ cardWidth }' height='195 ' fill='white'/>
493
+ <rect width=' {$ cardWidth }' height=' { $ cardHeight } ' fill='white'/>
461
494
<ellipse id='mask-ellipse' cx=' {$ currentStreakOffset }' cy='32' rx='13' ry='18' fill='black'/>
462
495
</mask>
463
496
{$ theme ["backgroundGradient " ]}
464
497
</defs>
465
498
<g clip-path='url(#outer_rectangle)'>
466
499
<g style='isolation: isolate'>
467
- <rect stroke=' {$ theme ["border " ]}' fill=' {$ theme ["background " ]}' rx=' {$ borderRadius }' x='0.5' y='0.5' width=' {$ rectWidth }' height='194 '/>
500
+ <rect stroke=' {$ theme ["border " ]}' fill=' {$ theme ["background " ]}' rx=' {$ borderRadius }' x='0.5' y='0.5' width=' {$ rectWidth }' height=' { $ rectHeight } '/>
468
501
</g>
469
502
<g style='isolation: isolate'>
470
- <line x1=' {$ barOffsets [0 ]}' y1='28 ' x2=' {$ barOffsets [0 ]}' y2='170 ' vector-effect='non-scaling-stroke' stroke-width='1' stroke=' {$ theme ["stroke " ]}' stroke-linejoin='miter' stroke-linecap='square' stroke-miterlimit='3'/>
471
- <line x1=' {$ barOffsets [1 ]}' y1='28 ' x2=' {$ barOffsets [1 ]}' y2='170 ' vector-effect='non-scaling-stroke' stroke-width='1' stroke=' {$ theme ["stroke " ]}' stroke-linejoin='miter' stroke-linecap='square' stroke-miterlimit='3'/>
503
+ <line x1=' {$ barOffsets [0 ]}' y1=' { $ barHeightOffsets [ 0 ]} ' x2=' {$ barOffsets [0 ]}' y2=' { $ barHeightOffsets [ 1 ]} ' vector-effect='non-scaling-stroke' stroke-width='1' stroke=' {$ theme ["stroke " ]}' stroke-linejoin='miter' stroke-linecap='square' stroke-miterlimit='3'/>
504
+ <line x1=' {$ barOffsets [1 ]}' y1=' $ barHeightOffsets [ 0 ] ' x2=' {$ barOffsets [1 ]}' y2=' $ barHeightOffsets [ 1 ] ' vector-effect='non-scaling-stroke' stroke-width='1' stroke=' {$ theme ["stroke " ]}' stroke-linejoin='miter' stroke-linecap='square' stroke-miterlimit='3'/>
472
505
</g>
473
506
<g style='isolation: isolate'>
474
507
<!-- Total Contributions big number -->
475
- <g transform='translate( {$ totalContributionsOffset },48 )'>
508
+ <g transform='translate( {$ totalContributionsOffset }, { $ totalContributionsHeightOffset [ 0 ]} )'>
476
509
<text x='0' y='32' stroke-width='0' text-anchor='middle' fill=' {$ theme ["sideNums " ]}' stroke='none' font-family=' \"Segoe UI \", Ubuntu, sans-serif' font-weight='700' font-size='28px' font-style='normal' style='opacity: 0; animation: fadein 0.5s linear forwards 0.6s'>
477
510
{$ totalContributions }
478
511
</text>
479
512
</g>
480
513
481
514
<!-- Total Contributions label -->
482
- <g transform='translate( {$ totalContributionsOffset },84 )'>
515
+ <g transform='translate( {$ totalContributionsOffset }, { $ totalContributionsHeightOffset [ 1 ]} )'>
483
516
<text x='0' y='32' stroke-width='0' text-anchor='middle' fill=' {$ theme ["sideLabels " ]}' stroke='none' font-family=' \"Segoe UI \", Ubuntu, sans-serif' font-weight='400' font-size='14px' font-style='normal' style='opacity: 0; animation: fadein 0.5s linear forwards 0.7s'>
484
517
{$ totalContributionsText }
485
518
</text>
486
519
</g>
487
520
488
521
<!-- Total Contributions range -->
489
- <g transform='translate( {$ totalContributionsOffset },114 )'>
522
+ <g transform='translate( {$ totalContributionsOffset }, { $ totalContributionsHeightOffset [ 2 ]} )'>
490
523
<text x='0' y='32' stroke-width='0' text-anchor='middle' fill=' {$ theme ["dates " ]}' stroke='none' font-family=' \"Segoe UI \", Ubuntu, sans-serif' font-weight='400' font-size='12px' font-style='normal' style='opacity: 0; animation: fadein 0.5s linear forwards 0.8s'>
491
524
{$ totalContributionsRange }
492
525
</text>
493
526
</g>
494
527
</g>
495
528
<g style='isolation: isolate'>
496
529
<!-- Current Streak big number -->
497
- <g transform='translate( {$ currentStreakOffset },48 )'>
530
+ <g transform='translate( {$ currentStreakOffset }, { $ currentStreakHeightOffset [ 0 ]} )'>
498
531
<text x='0' y='32' stroke-width='0' text-anchor='middle' fill=' {$ theme ["currStreakNum " ]}' stroke='none' font-family=' \"Segoe UI \", Ubuntu, sans-serif' font-weight='700' font-size='28px' font-style='normal' style='animation: currstreak 0.6s linear forwards'>
499
532
{$ currentStreak }
500
533
</text>
501
534
</g>
502
535
503
536
<!-- Current Streak label -->
504
- <g transform='translate( {$ currentStreakOffset },108 )'>
537
+ <g transform='translate( {$ currentStreakOffset }, { $ currentStreakHeightOffset [ 1 ]} )'>
505
538
<text x='0' y='32' stroke-width='0' text-anchor='middle' fill=' {$ theme ["currStreakLabel " ]}' stroke='none' font-family=' \"Segoe UI \", Ubuntu, sans-serif' font-weight='700' font-size='14px' font-style='normal' style='opacity: 0; animation: fadein 0.5s linear forwards 0.9s'>
506
539
{$ currentStreakText }
507
540
</text>
508
541
</g>
509
542
510
543
<!-- Current Streak range -->
511
- <g transform='translate( {$ currentStreakOffset },145 )'>
544
+ <g transform='translate( {$ currentStreakOffset }, { $ currentStreakHeightOffset [ 2 ]} )'>
512
545
<text x='0' y='21' stroke-width='0' text-anchor='middle' fill=' {$ theme ["dates " ]}' stroke='none' font-family=' \"Segoe UI \", Ubuntu, sans-serif' font-weight='400' font-size='12px' font-style='normal' style='opacity: 0; animation: fadein 0.5s linear forwards 0.9s'>
513
546
{$ currentStreakRange }
514
547
</text>
515
548
</g>
516
549
517
550
<!-- Ring around number -->
518
551
<g mask='url(#mask_out_ring_behind_fire)'>
519
- <circle cx=' {$ currentStreakOffset }' cy='71 ' r='40' fill='none' stroke=' {$ theme ["ring " ]}' stroke-width='5' style='opacity: 0; animation: fadein 0.5s linear forwards 0.4s'></circle>
552
+ <circle cx=' {$ currentStreakOffset }' cy=' { $ currentStreakHeightOffset [ 3 ]} ' r='40' fill='none' stroke=' {$ theme ["ring " ]}' stroke-width='5' style='opacity: 0; animation: fadein 0.5s linear forwards 0.4s'></circle>
520
553
</g>
521
554
<!-- Fire icon -->
522
- <g transform='translate( {$ currentStreakOffset }, 19.5 )' stroke-opacity='0' style='opacity: 0; animation: fadein 0.5s linear forwards 0.6s'>
555
+ <g transform='translate( {$ currentStreakOffset }, { $ currentStreakHeightOffset [ 4 ]} )' stroke-opacity='0' style='opacity: 0; animation: fadein 0.5s linear forwards 0.6s'>
523
556
<path d='M -12 -0.5 L 15 -0.5 L 15 23.5 L -12 23.5 L -12 -0.5 Z' fill='none'/>
524
557
<path d='M 1.5 0.67 C 1.5 0.67 2.24 3.32 2.24 5.47 C 2.24 7.53 0.89 9.2 -1.17 9.2 C -3.23 9.2 -4.79 7.53 -4.79 5.47 L -4.76 5.11 C -6.78 7.51 -8 10.62 -8 13.99 C -8 18.41 -4.42 22 0 22 C 4.42 22 8 18.41 8 13.99 C 8 8.6 5.41 3.79 1.5 0.67 Z M -0.29 19 C -2.07 19 -3.51 17.6 -3.51 15.86 C -3.51 14.24 -2.46 13.1 -0.7 12.74 C 1.07 12.38 2.9 11.53 3.92 10.16 C 4.31 11.45 4.51 12.81 4.51 14.2 C 4.51 16.85 2.36 19 -0.29 19 Z' fill=' {$ theme ["fire " ]}' stroke-opacity='0'/>
525
558
</g>
526
559
527
560
</g>
528
561
<g style='isolation: isolate'>
529
562
<!-- Longest Streak big number -->
530
- <g transform='translate( {$ longestStreakOffset },48 )'>
563
+ <g transform='translate( {$ longestStreakOffset }, { $ longestStreakHeightOffset [ 0 ]} )'>
531
564
<text x='0' y='32' stroke-width='0' text-anchor='middle' fill=' {$ theme ["sideNums " ]}' stroke='none' font-family=' \"Segoe UI \", Ubuntu, sans-serif' font-weight='700' font-size='28px' font-style='normal' style='opacity: 0; animation: fadein 0.5s linear forwards 1.2s'>
532
565
{$ longestStreak }
533
566
</text>
534
567
</g>
535
568
536
569
<!-- Longest Streak label -->
537
- <g transform='translate( {$ longestStreakOffset },84 )'>
570
+ <g transform='translate( {$ longestStreakOffset }, { $ longestStreakHeightOffset [ 1 ]} )'>
538
571
<text x='0' y='32' stroke-width='0' text-anchor='middle' fill=' {$ theme ["sideLabels " ]}' stroke='none' font-family=' \"Segoe UI \", Ubuntu, sans-serif' font-weight='400' font-size='14px' font-style='normal' style='opacity: 0; animation: fadein 0.5s linear forwards 1.3s'>
539
572
{$ longestStreakText }
540
573
</text>
541
574
</g>
542
575
543
576
<!-- Longest Streak range -->
544
- <g transform='translate( {$ longestStreakOffset },114 )'>
577
+ <g transform='translate( {$ longestStreakOffset }, { $ longestStreakHeightOffset [ 2 ]} )'>
545
578
<text x='0' y='32' stroke-width='0' text-anchor='middle' fill=' {$ theme ["dates " ]}' stroke='none' font-family=' \"Segoe UI \", Ubuntu, sans-serif' font-weight='400' font-size='12px' font-style='normal' style='opacity: 0; animation: fadein 0.5s linear forwards 1.4s'>
546
579
{$ longestStreakRange }
547
580
</text>
@@ -575,25 +608,31 @@ function generateErrorCard(string $message, array $params = null): string
575
608
$ rectWidth = $ cardWidth - 1 ;
576
609
$ centerOffset = $ cardWidth / 2 ;
577
610
578
- return "<svg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' style='isolation: isolate' viewBox='0 0 {$ cardWidth } 195' width=' {$ cardWidth }px' height='195px'>
611
+ // read card_height parameter
612
+ $ cardHeight = getCardHeight ($ params );
613
+ $ rectHeight = $ cardHeight - 1 ;
614
+ $ heightOffset = ($ cardHeight - 195 ) / 2 ;
615
+ $ errorLabelOffset = $ cardHeight / 2 + 10.5 ;
616
+
617
+ return "<svg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' style='isolation: isolate' viewBox='0 0 {$ cardWidth } {$ cardHeight }' width=' {$ cardWidth }px' height=' {$ cardHeight }px'>
579
618
<style>
580
619
a {
581
620
fill: {$ theme ["dates " ]};
582
621
}
583
622
</style>
584
623
<defs>
585
624
<clipPath id='outer_rectangle'>
586
- <rect width=' {$ cardWidth }' height='195 ' rx=' {$ borderRadius }'/>
625
+ <rect width=' {$ cardWidth }' height=' { $ cardHeight } ' rx=' {$ borderRadius }'/>
587
626
</clipPath>
588
627
{$ theme ["backgroundGradient " ]}
589
628
</defs>
590
629
<g clip-path='url(#outer_rectangle)'>
591
630
<g style='isolation: isolate'>
592
- <rect stroke=' {$ theme ["border " ]}' fill=' {$ theme ["background " ]}' rx=' {$ borderRadius }' x='0.5' y='0.5' width=' {$ rectWidth }' height='194 '/>
631
+ <rect stroke=' {$ theme ["border " ]}' fill=' {$ theme ["background " ]}' rx=' {$ borderRadius }' x='0.5' y='0.5' width=' {$ rectWidth }' height=' { $ rectHeight } '/>
593
632
</g>
594
633
<g style='isolation: isolate'>
595
634
<!-- Error lable -->
596
- <g transform='translate( {$ centerOffset },108 )'>
635
+ <g transform='translate( {$ centerOffset }, { $ errorLabelOffset } )'>
597
636
<text x='0' y='50' dy='0.25em' stroke-width='0' text-anchor='middle' fill=' {$ theme ["sideLabels " ]}' stroke='none' font-family=' \"Segoe UI \", Ubuntu, sans-serif' font-weight='400' font-size='14px' font-style='normal'>
598
637
{$ message }
599
638
</text>
@@ -607,7 +646,7 @@ function generateErrorCard(string $message, array $params = null): string
607
646
</mask>
608
647
</defs>
609
648
<!-- Sad face -->
610
- <g transform='translate( {$ centerOffset }, 0 )'>
649
+ <g transform='translate( {$ centerOffset }, { $ heightOffset } )'>
611
650
<path fill=' {$ theme ["fire " ]}' d='M0,35.8c-25.2,0-45.7,20.5-45.7,45.7s20.5,45.8,45.7,45.8s45.7-20.5,45.7-45.7S25.2,35.8,0,35.8z M0,122.3c-11.2,0-21.4-4.5-28.8-11.9c-2.9-2.9-5.4-6.3-7.4-10c-3-5.7-4.6-12.1-4.6-18.9c0-22.5,18.3-40.8,40.8-40.8 c10.7,0,20.4,4.1,27.7,10.9c3.8,3.5,6.9,7.7,9.1,12.4c2.6,5.3,4,11.3,4,17.6C40.8,104.1,22.5,122.3,0,122.3z'/>
612
651
<path fill=' {$ theme ["fire " ]}' d='M4.8,93.8c5.4,1.1,10.3,4.2,13.7,8.6l3.9-3c-4.1-5.3-10-9-16.6-10.4c-10.6-2.2-21.7,1.9-28.3,10.4l3.9,3 C-13.1,95.3-3.9,91.9,4.8,93.8z'/>
613
652
<circle fill=' {$ theme ["fire " ]}' cx='-15' cy='71' r='4.9'/>
@@ -704,7 +743,7 @@ function ($matches) {
704
743
* @param int $cardWidth The width of the card
705
744
* @return string The generated PNG data
706
745
*/
707
- function convertSvgToPng (string $ svg , int $ cardWidth ): string
746
+ function convertSvgToPng (string $ svg , int $ cardWidth, int $ cardHeight ): string
708
747
{
709
748
// trim off all whitespaces to make it a valid SVG string
710
749
$ svg = trim ($ svg );
@@ -722,7 +761,7 @@ function convertSvgToPng(string $svg, int $cardWidth): string
722
761
// `--export-filename -`: write output to stdout
723
762
// `-w 495 -h 195`: set width and height of the output image
724
763
// `--export-type png`: set the output format to PNG
725
- $ cmd = "echo {$ svg } | inkscape --pipe --export-filename - -w {$ cardWidth } -h 195 --export-type png " ;
764
+ $ cmd = "echo {$ svg } | inkscape --pipe --export-filename - -w {$ cardWidth } -h { $ cardHeight } --export-type png " ;
726
765
727
766
// convert svg to png
728
767
$ png = shell_exec ($ cmd ); // skipcq: PHP-A1009
@@ -772,7 +811,8 @@ function generateOutput(string|array $output, array $params = null): array
772
811
try {
773
812
// extract width from SVG
774
813
$ cardWidth = (int ) preg_replace ("/.*width=[ \"'](\d+)px[ \"'].*/ " , "$1 " , $ svg );
775
- $ png = convertSvgToPng ($ svg , $ cardWidth );
814
+ $ cardHeight = (int ) preg_replace ("/.*height=[ \"'](\d+)px[ \"'].*/ " , "$1 " , $ svg );
815
+ $ png = convertSvgToPng ($ svg , $ cardWidth , $ cardHeight );
776
816
return [
777
817
"contentType " => "image/png " ,
778
818
"body " => $ png ,
0 commit comments