tags missing width/height and injects them
* based on file metadata to prevent Cumulative Layout Shift (CLS).
*
* @package Search Atlas SEO
* @copyright Copyright (C) 2021-2025, Search Atlas Group - support@searchatlas.com
* @since 2.6.0
*/
if (!defined('ABSPATH')) {
exit;
}
class Metasync_Dimension_Injector {
/** In-memory cache to avoid repeated file lookups within a request. */
private array $cache = [];
public function __construct() {
add_filter('the_content', [$this, 'inject_dimensions'], 30);
add_filter('post_thumbnail_html', [$this, 'inject_dimensions'], 30);
add_filter('widget_text', [$this, 'inject_dimensions'], 30);
}
/**
* Find
tags missing width or height and inject dimensions.
*/
public function inject_dimensions(string $content): string {
if (empty($content) || is_admin()) {
return $content;
}
return preg_replace_callback('/
]+>/i', function ($matches) {
$tag = $matches[0];
$has_width = preg_match('/\swidth\s*=/i', $tag);
$has_height = preg_match('/\sheight\s*=/i', $tag);
// Both already present - nothing to do
if ($has_width && $has_height) {
return $tag;
}
$dims = $this->get_dimensions($tag);
if (!$dims) {
return $tag;
}
if (!$has_width) {
$tag = $this->add_attribute($tag, 'width', (string) $dims['width']);
}
if (!$has_height) {
$tag = $this->add_attribute($tag, 'height', (string) $dims['height']);
}
return $tag;
}, $content);
}
/**
* Try to determine image dimensions from multiple sources.
*/
private function get_dimensions(string $img_tag): ?array {
if (!preg_match('/src=["\']([^"\']+)["\']/i', $img_tag, $m)) {
return null;
}
$src = $m[1];
if (isset($this->cache[$src])) {
return $this->cache[$src];
}
$dims = null;
// Strategy 1: Try to find WordPress attachment by URL
$dims = $this->get_dims_from_attachment($src);
// Strategy 2: Try to read the local file directly
if (!$dims) {
$dims = $this->get_dims_from_local_file($src);
}
// Strategy 3: For external images, try getimagesize with URL (slower)
if (!$dims) {
$dims = $this->get_dims_from_remote($src);
}
if ($dims) {
$this->cache[$src] = $dims;
}
return $dims;
}
/**
* Look up dimensions from WP attachment metadata.
*/
private function get_dims_from_attachment(string $url): ?array {
$attachment_id = attachment_url_to_postid($url);
if (!$attachment_id) {
// Try without size suffix (e.g., image-300x200.jpg -> image.jpg)
$clean_url = preg_replace('/-\d+x\d+(\.\w+)$/', '$1', $url);
$attachment_id = attachment_url_to_postid($clean_url);
}
if (!$attachment_id) {
return null;
}
$meta = wp_get_attachment_metadata($attachment_id);
if (!$meta) {
return null;
}
// Check sub-sizes first
if (!empty($meta['sizes'])) {
$filename = wp_basename($url);
foreach ($meta['sizes'] as $size_data) {
if ($size_data['file'] === $filename) {
return [
'width' => (int) $size_data['width'],
'height' => (int) $size_data['height'],
];
}
}
}
// Fall back to full size
if (!empty($meta['width']) && !empty($meta['height'])) {
return [
'width' => (int) $meta['width'],
'height' => (int) $meta['height'],
];
}
return null;
}
/**
* Convert URL to local path and use getimagesize().
*/
private function get_dims_from_local_file(string $url): ?array {
$upload_dir = wp_get_upload_dir();
if (strpos($url, $upload_dir['baseurl']) === false) {
return null;
}
$relative = str_replace($upload_dir['baseurl'], '', $url);
$file = $upload_dir['basedir'] . $relative;
if (!file_exists($file)) {
return null;
}
$info = @getimagesize($file);
if ($info && $info[0] > 0 && $info[1] > 0) {
return ['width' => $info[0], 'height' => $info[1]];
}
return null;
}
/**
* Fetch dimensions from a remote URL.
* Downloads just enough bytes to read image headers.
*/
private function get_dims_from_remote(string $url): ?array {
if (strpos($url, 'http') !== 0) {
return null;
}
$response = wp_remote_get($url, [
'timeout' => 3,
'headers' => ['Range' => 'bytes=0-32767'],
]);
if (is_wp_error($response)) {
return null;
}
$tmp = wp_tempnam($url);
$body = wp_remote_retrieve_body($response);
if (empty($body)) {
return null;
}
file_put_contents($tmp, $body);
$info = @getimagesize($tmp);
@unlink($tmp);
if ($info && $info[0] > 0 && $info[1] > 0) {
return ['width' => $info[0], 'height' => $info[1]];
}
return null;
}
/**
* Insert an attribute into an
tag.
*/
private function add_attribute(string $tag, string $name, string $value): string {
return preg_replace(
'/(![]()