🐛 Fixed email rendering bug in Gmail for Android (#18886)

refs https://github.com/TryGhost/Ghost/pull/18587/files and
https://github.com/TryGhost/Ghost/pull/17475/files

- In October 2022, `juice`, a library Ghost uses to inline CSS for email
rendering, introduced a small change that began inlining `width: auto`
and `height: auto` from CSS on image tags, resulting in `width="auto"`
and `height="auto"` attributes being added to image tags in rendered emails
(cb62062794)
- This change in `juice` broke our email rendering in Outlook, which
doesn't play well with `width="auto"` attributes. The first two attempts
to workaround this new behavior in `juice` ended up fixing the issue in
Outlook, but breaking the rendering in other clients
- This commit stores the `height` and `width` attributes of all images
_before_ inlining the CSS with `juice`, and resets them to their
original values, only if they were set to `auto`
This commit is contained in:
Chris Raible 2023-11-06 16:40:08 -08:00
parent ad7efbe92e
commit 8b58eabadc
3 changed files with 31 additions and 4 deletions

View File

@ -368,15 +368,41 @@ class EmailRenderer {
}, {base});
}
// Record the original image width and height attributes before inlining the styles with juice
// If any images have `width: auto` or `height: auto` set via CSS,
// juice will explicitly set the width/height attributes to `auto` on the <img /> tag
// This is not supported by Outlook, so we need to reset the width/height attributes to the original values
// Other clients will ignore the width/height attributes and use the inlined CSS instead
$ = cheerio.load(html);
const originalImageSizes = $('img').get().map((image) => {
const src = image.attribs.src;
const width = image.attribs.width;
const height = image.attribs.height;
return {src, width, height};
});
// Juice HTML (inline CSS)
const juice = require('juice');
juice.heightElements = ['TABLE', 'TD', 'TH', 'IMG'];
juice.widthElements = ['TABLE', 'TD', 'TH', 'IMG'];
html = juice(html, {inlinePseudoElements: true, removeStyleTags: true});
// happens after inlining of CSS so we can change element types without worrying about styling
$ = cheerio.load(html);
// Reset any `height="auto"` or `width="auto"` attributes to their original values before inlining CSS
const imageTags = $('img').get();
for (let i = 0; i < imageTags.length; i += 1) {
// There shouldn't be any issues with consistency between these two lists, but just in case...
if (imageTags[i].attribs.src === originalImageSizes[i].src) {
// if the image width or height is set to 'auto', reset to its original value
if (imageTags[i].attribs.width === 'auto' && originalImageSizes[i].width) {
imageTags[i].attribs.width = originalImageSizes[i].width;
}
if (imageTags[i].attribs.height === 'auto' && originalImageSizes[i].height) {
imageTags[i].attribs.height = originalImageSizes[i].height;
}
}
}
// force all links to open in new tab
$('a').attr('target', '_blank');

View File

@ -638,6 +638,7 @@ a[data-flickr-embed] img {
display: block;
margin: 0 auto;
height: auto;
width: auto;
}
.kg-bookmark-container {

View File

@ -685,8 +685,8 @@ a[data-flickr-embed] img {
.kg-image-card img {
display: block;
margin: 0 auto;
/* width: auto;
height: auto !important; */
width: auto;
height: auto !important;
}
.kg-bookmark-container {