To keep a Drupal site’s speed on the higher end, the SVG logo can be added directly to the block template instead of using an <img> tag with src attribute. But after a while, I noticed that Internet Explorer 11 doesn’t calculate the size of the inline SVG properly.

After some research I found this article. Sadly, none of its recommendations meet my needs: while in the header I need to size the logo’s height to match the header’s height and leave the width auto-calculated, in the footer I need auto height and make the width equal to its container width.

But I had an idea: if I get the width and the height of the SVG, create a placeholder image and wrap the placeholder and the SVG into a div or span, it will be possible to get around the sizing issues with a small amount of CSS.

Get the dimensions of the SVG

Let’s assume that we already have the content of the SVG as a SimpleXMLElement object. It’s a smart idea to wrap the instantiation into a try-catch block for the case when the SVG (that is actually an XML actually) is malformed.

Getting the width and height is really easy if the SVG has declared width or height attributes:

1
2
3
4
5
6
if ($simple_xml_element['width'] && $simple_xml_element['height']) {
  $dimensions = [
    'width' => (string) $simple_xml_element['width'],
    'height' => (string) $simple_xml_element['height'],
  ];
}

But if we don’t have them, we have to check viewBox, and if that exists, parse its value:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
elseif ($simple_xml_element['viewBox']) {
  $vbox_array = array_values(
   // Filter array fix the case when the vievBox value contains extra spaces.
    array_filter(explode(' ', (string) $simple_xml_element['viewBox']), function ($value) {
      return $value === '0' || !empty($value);
    })
  );
  if (count($vbox_array) === 4) {
    list(, , $width, $height) = $vbox_array;
    $dimensions = [
      'width' => $width,
      'height' => $height,
    ];
  }
}

Create the placeholder image

You probably noticed the weird type-castings above. I use the GMP (GNU Multiple Precision) extension’s gmb_gcd() function as well to get the greatest common divisor of width and height: That will be practical for reducing the size of the transparent PNG image as much as possible.

1
2
3
$gcd = (int) gmp_gcd($dimensions['width'], $dimensions['height']);
$dimensions['width'] = ((int) $dimensions['width']) / $gcd;
$dimensions['height'] = ((int) $dimensions['height']) / $gcd;

Let’s generate the placeholder image:

1
2
3
4
$img = imagecreatetruecolor($dimensions['width'] , $dimensions['height']);
imagesavealpha($img, true);
$color = imagecolorallocatealpha($img, 0, 0, 0, 127);
imagefill($img, 0, 0, $color);

Generate the HTML markup

Now we have almost everything to generate the actual HTML markup that’s ready for use. But instead of saving the placeholder PNG and referencing it as a resource, we’ll insert it as a base64-encoded content.

1
2
3
4
5
6
7
8
// Using imagepng() without stream or path resource will output the raw image
// stream directly, so we need to start output buffering.
ob_start();
imagepng($img);
$contents = ob_get_contents();
// End the output buffer.
ob_end_clean();
$placeholder_base64 = base64_encode($contents);

Gorgeous! At this point we really have everything for generating the markup:

1
2
3
4
5
6
7
8
9
10
11
$image = '<img ' . 
  'class="svg-canvas__image"' .
  'src="data:image/png;base64,' . $placeholder_base64 . '"' .
  'alt="Site logo placeholder" ' .
  'aria-hidden="true" />';

return
  '<div class="svg-canvas">' .
  $image .
  $svgcontent .
  '</div>';

(I always prefer using renderable arrays, but I think that using strings above makes the solution easier to understand.)

The CSS

So the idea was to leave the placeholder image to be scaled automatically whilst the SVG is stretched to its parent wrapper:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* The SVG canvas. */
.svg-canvas {
  position: relative;
  line-height: 0;
}

/* Placeholder image. */
.svg-canvas__image {
  width: 100%;
  height: auto;
  visibility: hidden;
}

/* The inlined SVG. */
.svg-canvas > svg {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
}

Summary

With the above, we’re able to reduce the number of render blocking resources by at least one and keep our browser support level at the same time – with a minimal amount of extra bytes.


Sources: