Skip to content

Commit

Permalink
feat: Added card_height option (#673)
Browse files Browse the repository at this point in the history
Co-authored-by: Jonah Lawrence <jonah@freshidea.com>
  • Loading branch information
hany-z and DenverCoder1 authored May 5, 2024
1 parent efeb0c0 commit 46b4048
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 39 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ If the `theme` parameter is specified, any color customizations specified will b
| `exclude_days` | List of days of the week to exclude from streaks | Comma-separated list of day abbreviations (Sun, Mon, Tue, Wed, Thu, Fri, Sat) e.g. `Sun,Sat` |
| `disable_animations` | Disable SVG animations (Default: `false`) | `true` or `false` |
| `card_width` | Width of the card in pixels (Default: `495`) | Positive integer, minimum width is 100px per column |
| `card_height` | Height of the card in pixels (Default: `195`) | Positive integer, minimum height is 170px |
| `hide_total_contributions` | Hide the total contributions (Default: `false`) | `true` or `false` |
| `hide_current_streak` | Hide the current streak (Default: `false`) | `true` or `false` |
| `hide_longest_streak` | Hide the longest streak (Default: `false`) | `true` or `false` |
Expand Down
92 changes: 66 additions & 26 deletions src/card.php
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,19 @@ function getCardWidth(array $params, int $numColumns = 3): int
return max($minimumWidth, intval($params["card_width"] ?? $defaultWidth));
}

/**
* Get the card height from params taking into account minimum and default values
*
* @param array<string,string> $params Request parameters
* @return int Card width
*/
function getCardHeight(array $params): int
{
$defaultHeight = 195;
$minimumHeight = 170;
return max($minimumHeight, intval($params["card_height"] ?? $defaultHeight));
}

/**
* Generate SVG output for a stats array
*
Expand Down Expand Up @@ -364,7 +377,11 @@ function generateCard(array $stats, array $params = null): string
$rectWidth = $cardWidth - 1;
$columnWidth = $numColumns > 0 ? $cardWidth / $numColumns : 0;

// offsets for the bars between columns
$cardHeight = getCardHeight($params);
$rectHeight = $cardHeight - 1;
$heightOffset = ($cardHeight - 195) / 2;

// X offsets for the bars between columns
$barOffsets = [-999, -999];
for ($i = 0; $i < $numColumns - 1; $i++) {
$barOffsets[$i] = $columnWidth * ($i + 1);
Expand All @@ -384,6 +401,22 @@ function generateCard(array $stats, array $params = null): string
$currentStreakOffset = $showCurrentStreak ? $columnOffsets[$nextColumnIndex++] : -999;
$longestStreakOffset = $showLongestStreak ? $columnOffsets[$nextColumnIndex++] : -999;

// Y offsets for the bars
$barHeightOffsets = [28 + $heightOffset / 2, 170 + $heightOffset];
// Y offsets for the numbers and dates
$longestStreakHeightOffset = $totalContributionsHeightOffset = [
48 + $heightOffset,
84 + $heightOffset,
114 + $heightOffset,
];
$currentStreakHeightOffset = [
48 + $heightOffset,
108 + $heightOffset,
145 + $heightOffset,
71 + $heightOffset,
19.5 + $heightOffset,
];

// total contributions
$totalContributions = $numFormatter->format($stats["totalContributions"]);
$firstContribution = formatDate($stats["firstContribution"], $dateFormat, $localeCode);
Expand Down Expand Up @@ -440,7 +473,7 @@ function generateCard(array $stats, array $params = null): string
}

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' direction='{$direction}'>
style='isolation: isolate' viewBox='0 0 {$cardWidth} {$cardHeight}' width='{$cardWidth}px' height='{$cardHeight}px' direction='{$direction}'>
<style>
@keyframes currstreak {
0% { font-size: 3px; opacity: 0.2; }
Expand All @@ -454,94 +487,94 @@ function generateCard(array $stats, array $params = null): string
</style>
<defs>
<clipPath id='outer_rectangle'>
<rect width='{$cardWidth}' height='195' rx='{$borderRadius}'/>
<rect width='{$cardWidth}' height='{$cardHeight}' rx='{$borderRadius}'/>
</clipPath>
<mask id='mask_out_ring_behind_fire'>
<rect width='{$cardWidth}' height='195' fill='white'/>
<rect width='{$cardWidth}' height='{$cardHeight}' fill='white'/>
<ellipse id='mask-ellipse' cx='{$currentStreakOffset}' cy='32' rx='13' ry='18' fill='black'/>
</mask>
{$theme["backgroundGradient"]}
</defs>
<g clip-path='url(#outer_rectangle)'>
<g style='isolation: isolate'>
<rect stroke='{$theme["border"]}' fill='{$theme["background"]}' rx='{$borderRadius}' x='0.5' y='0.5' width='{$rectWidth}' height='194'/>
<rect stroke='{$theme["border"]}' fill='{$theme["background"]}' rx='{$borderRadius}' x='0.5' y='0.5' width='{$rectWidth}' height='{$rectHeight}'/>
</g>
<g style='isolation: isolate'>
<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'/>
<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'/>
<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'/>
<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'/>
</g>
<g style='isolation: isolate'>
<!-- Total Contributions big number -->
<g transform='translate({$totalContributionsOffset},48)'>
<g transform='translate({$totalContributionsOffset}, {$totalContributionsHeightOffset[0]})'>
<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'>
{$totalContributions}
</text>
</g>
<!-- Total Contributions label -->
<g transform='translate({$totalContributionsOffset},84)'>
<g transform='translate({$totalContributionsOffset}, {$totalContributionsHeightOffset[1]})'>
<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'>
{$totalContributionsText}
</text>
</g>
<!-- Total Contributions range -->
<g transform='translate({$totalContributionsOffset},114)'>
<g transform='translate({$totalContributionsOffset}, {$totalContributionsHeightOffset[2]})'>
<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'>
{$totalContributionsRange}
</text>
</g>
</g>
<g style='isolation: isolate'>
<!-- Current Streak big number -->
<g transform='translate({$currentStreakOffset},48)'>
<g transform='translate({$currentStreakOffset}, {$currentStreakHeightOffset[0]})'>
<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'>
{$currentStreak}
</text>
</g>
<!-- Current Streak label -->
<g transform='translate({$currentStreakOffset},108)'>
<g transform='translate({$currentStreakOffset}, {$currentStreakHeightOffset[1]})'>
<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'>
{$currentStreakText}
</text>
</g>
<!-- Current Streak range -->
<g transform='translate({$currentStreakOffset},145)'>
<g transform='translate({$currentStreakOffset}, {$currentStreakHeightOffset[2]})'>
<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'>
{$currentStreakRange}
</text>
</g>
<!-- Ring around number -->
<g mask='url(#mask_out_ring_behind_fire)'>
<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>
<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>
</g>
<!-- Fire icon -->
<g transform='translate({$currentStreakOffset}, 19.5)' stroke-opacity='0' style='opacity: 0; animation: fadein 0.5s linear forwards 0.6s'>
<g transform='translate({$currentStreakOffset}, {$currentStreakHeightOffset[4]})' stroke-opacity='0' style='opacity: 0; animation: fadein 0.5s linear forwards 0.6s'>
<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'/>
<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'/>
</g>
</g>
<g style='isolation: isolate'>
<!-- Longest Streak big number -->
<g transform='translate({$longestStreakOffset},48)'>
<g transform='translate({$longestStreakOffset}, {$longestStreakHeightOffset[0]})'>
<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'>
{$longestStreak}
</text>
</g>
<!-- Longest Streak label -->
<g transform='translate({$longestStreakOffset},84)'>
<g transform='translate({$longestStreakOffset}, {$longestStreakHeightOffset[1]})'>
<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'>
{$longestStreakText}
</text>
</g>
<!-- Longest Streak range -->
<g transform='translate({$longestStreakOffset},114)'>
<g transform='translate({$longestStreakOffset}, {$longestStreakHeightOffset[2]})'>
<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'>
{$longestStreakRange}
</text>
Expand Down Expand Up @@ -575,25 +608,31 @@ function generateErrorCard(string $message, array $params = null): string
$rectWidth = $cardWidth - 1;
$centerOffset = $cardWidth / 2;

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'>
// read card_height parameter
$cardHeight = getCardHeight($params);
$rectHeight = $cardHeight - 1;
$heightOffset = ($cardHeight - 195) / 2;
$errorLabelOffset = $cardHeight / 2 + 10.5;

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'>
<style>
a {
fill: {$theme["dates"]};
}
</style>
<defs>
<clipPath id='outer_rectangle'>
<rect width='{$cardWidth}' height='195' rx='{$borderRadius}'/>
<rect width='{$cardWidth}' height='{$cardHeight}' rx='{$borderRadius}'/>
</clipPath>
{$theme["backgroundGradient"]}
</defs>
<g clip-path='url(#outer_rectangle)'>
<g style='isolation: isolate'>
<rect stroke='{$theme["border"]}' fill='{$theme["background"]}' rx='{$borderRadius}' x='0.5' y='0.5' width='{$rectWidth}' height='194'/>
<rect stroke='{$theme["border"]}' fill='{$theme["background"]}' rx='{$borderRadius}' x='0.5' y='0.5' width='{$rectWidth}' height='{$rectHeight}'/>
</g>
<g style='isolation: isolate'>
<!-- Error lable -->
<g transform='translate({$centerOffset},108)'>
<g transform='translate({$centerOffset}, {$errorLabelOffset})'>
<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'>
{$message}
</text>
Expand All @@ -607,7 +646,7 @@ function generateErrorCard(string $message, array $params = null): string
</mask>
</defs>
<!-- Sad face -->
<g transform='translate({$centerOffset}, 0)'>
<g transform='translate({$centerOffset}, {$heightOffset})'>
<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'/>
<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'/>
<circle fill='{$theme["fire"]}' cx='-15' cy='71' r='4.9'/>
Expand Down Expand Up @@ -704,7 +743,7 @@ function ($matches) {
* @param int $cardWidth The width of the card
* @return string The generated PNG data
*/
function convertSvgToPng(string $svg, int $cardWidth): string
function convertSvgToPng(string $svg, int $cardWidth, int $cardHeight): string
{
// trim off all whitespaces to make it a valid SVG string
$svg = trim($svg);
Expand All @@ -722,7 +761,7 @@ function convertSvgToPng(string $svg, int $cardWidth): string
// `--export-filename -`: write output to stdout
// `-w 495 -h 195`: set width and height of the output image
// `--export-type png`: set the output format to PNG
$cmd = "echo {$svg} | inkscape --pipe --export-filename - -w {$cardWidth} -h 195 --export-type png";
$cmd = "echo {$svg} | inkscape --pipe --export-filename - -w {$cardWidth} -h {$cardHeight} --export-type png";

// convert svg to png
$png = shell_exec($cmd); // skipcq: PHP-A1009
Expand Down Expand Up @@ -772,7 +811,8 @@ function generateOutput(string|array $output, array $params = null): array
try {
// extract width from SVG
$cardWidth = (int) preg_replace("/.*width=[\"'](\d+)px[\"'].*/", "$1", $svg);
$png = convertSvgToPng($svg, $cardWidth);
$cardHeight = (int) preg_replace("/.*height=[\"'](\d+)px[\"'].*/", "$1", $svg);
$png = convertSvgToPng($svg, $cardWidth, $cardHeight);
return [
"contentType" => "image/png",
"body" => $png,
Expand Down
5 changes: 4 additions & 1 deletion src/demo/index.php
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,9 @@ function gtag() {
<label for="card-width">Card Width</label>
<input class="param" type="number" id="card-width" name="card_width" placeholder="495" value="495" step="1" min="300" />

<label for="card-width">Card Height</label>
<input class="param" type="number" id="card-width" name="card_height" placeholder="195" value="195" step="1" min="170" />

<label for="type">Output Type</label>
<select class="param" id="type" name="type">
<option value="svg">SVG</option>
Expand Down Expand Up @@ -276,4 +279,4 @@ function gtag() {
</a>
</body>

</html>
</html>
1 change: 1 addition & 0 deletions src/demo/js/script.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const preview = {
type: "svg",
exclude_days: "",
card_width: "495",
card_height: "195",
hide_total_contributions: "false",
hide_current_streak: "false",
hide_longest_streak: "false",
Expand Down
Loading

1 comment on commit 46b4048

@Vishnu068709
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.

Please sign in to comment.