Skip to content

Commit 47b3400

Browse files
committed
[Mime] Add a way to control the HTML to text conversion
1 parent 0adad51 commit 47b3400

File tree

3 files changed

+31
-28
lines changed

3 files changed

+31
-28
lines changed

Mime/BodyRenderer.php

Lines changed: 9 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,12 @@
1111

1212
namespace Symfony\Bridge\Twig\Mime;
1313

14-
use League\HTMLToMarkdown\HtmlConverter;
14+
use League\HTMLToMarkdown\HtmlConverterInterface;
1515
use Symfony\Component\Mime\BodyRendererInterface;
1616
use Symfony\Component\Mime\Exception\InvalidArgumentException;
17+
use Symfony\Component\Mime\HtmlToTextConverter\DefaultHtmlToTextConverter;
18+
use Symfony\Component\Mime\HtmlToTextConverter\HtmlToTextConverterInterface;
19+
use Symfony\Component\Mime\HtmlToTextConverter\LeagueHtmlToMarkdownConverter;
1720
use Symfony\Component\Mime\Message;
1821
use Twig\Environment;
1922

@@ -24,19 +27,13 @@ final class BodyRenderer implements BodyRendererInterface
2427
{
2528
private Environment $twig;
2629
private array $context;
27-
private HtmlConverter $converter;
30+
private HtmlToTextConverterInterface $converter;
2831

29-
public function __construct(Environment $twig, array $context = [])
32+
public function __construct(Environment $twig, array $context = [], HtmlToTextConverterInterface $converter = null)
3033
{
3134
$this->twig = $twig;
3235
$this->context = $context;
33-
if (class_exists(HtmlConverter::class)) {
34-
$this->converter = new HtmlConverter([
35-
'hard_break' => true,
36-
'strip_tags' => true,
37-
'remove_nodes' => 'head style',
38-
]);
39-
}
36+
$this->converter = $converter ?: (interface_exists(HtmlConverterInterface::class) ? new LeagueHtmlToMarkdownConverter() : new DefaultHtmlToTextConverter());
4037
}
4138

4239
public function render(Message $message): void
@@ -74,16 +71,8 @@ public function render(Message $message): void
7471

7572
// if text body is empty, compute one from the HTML body
7673
if (!$message->getTextBody() && null !== $html = $message->getHtmlBody()) {
77-
$message->text($this->convertHtmlToText(\is_resource($html) ? stream_get_contents($html) : $html));
78-
}
79-
}
80-
81-
private function convertHtmlToText(string $html): string
82-
{
83-
if (isset($this->converter)) {
84-
return $this->converter->convert($html);
74+
$text = $this->converter->convert(\is_resource($html) ? stream_get_contents($html) : $html, $message->getHtmlCharset());
75+
$message->text($text, $message->getHtmlCharset());
8576
}
86-
87-
return strip_tags(preg_replace('{<(head|style)\b.*?</\1>}is', '', $html));
8877
}
8978
}

Tests/Mime/BodyRendererTest.php

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
use Symfony\Bridge\Twig\Mime\BodyRenderer;
1616
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
1717
use Symfony\Component\Mime\Exception\InvalidArgumentException;
18+
use Symfony\Component\Mime\HtmlToTextConverter\DefaultHtmlToTextConverter;
19+
use Symfony\Component\Mime\HtmlToTextConverter\HtmlToTextConverterInterface;
1820
use Symfony\Component\Mime\Part\Multipart\AlternativePart;
1921
use Twig\Environment;
2022
use Twig\Loader\ArrayLoader;
@@ -27,14 +29,24 @@ public function testRenderTextOnly()
2729
$this->assertEquals('Text', $email->getBody()->bodyToString());
2830
}
2931

30-
public function testRenderHtmlOnly()
32+
public function testRenderHtmlOnlyWithDefaultConverter()
3133
{
32-
$html = '<head>head</head><b>HTML</b><style type="text/css">css</style>';
33-
$email = $this->prepareEmail(null, $html);
34+
$html = '<head><meta charset="utf-8"></head><b>HTML</b><style>css</style>';
35+
$email = $this->prepareEmail(null, $html, [], new DefaultHtmlToTextConverter());
3436
$body = $email->getBody();
3537
$this->assertInstanceOf(AlternativePart::class, $body);
3638
$this->assertEquals('HTML', $body->getParts()[0]->bodyToString());
37-
$this->assertEquals(str_replace('=', '=3D', $html), $body->getParts()[1]->bodyToString());
39+
$this->assertEquals(str_replace(['=', "\n"], ['=3D', "\r\n"], $html), $body->getParts()[1]->bodyToString());
40+
}
41+
42+
public function testRenderHtmlOnlyWithLeagueConverter()
43+
{
44+
$html = '<head><meta charset="utf-8"></head><b>HTML</b><style>css</style>';
45+
$email = $this->prepareEmail(null, $html);
46+
$body = $email->getBody();
47+
$this->assertInstanceOf(AlternativePart::class, $body);
48+
$this->assertEquals('**HTML**', $body->getParts()[0]->bodyToString());
49+
$this->assertEquals(str_replace(['=', "\n"], ['=3D', "\r\n"], $html), $body->getParts()[1]->bodyToString());
3850
}
3951

4052
public function testRenderMultiLineHtmlOnly()
@@ -50,7 +62,7 @@ public function testRenderMultiLineHtmlOnly()
5062
$email = $this->prepareEmail(null, $html);
5163
$body = $email->getBody();
5264
$this->assertInstanceOf(AlternativePart::class, $body);
53-
$this->assertEquals('HTML', str_replace(["\r", "\n"], '', $body->getParts()[0]->bodyToString()));
65+
$this->assertEquals('**HTML**', str_replace(["\r", "\n"], '', $body->getParts()[0]->bodyToString()));
5466
$this->assertEquals(str_replace(['=', "\n"], ['=3D', "\r\n"], $html), $body->getParts()[1]->bodyToString());
5567
}
5668

@@ -121,15 +133,15 @@ public function testRenderedOnceUnserializableContext()
121133
$this->assertEquals('Text', $email->getTextBody());
122134
}
123135

124-
private function prepareEmail(?string $text, ?string $html, array $context = []): TemplatedEmail
136+
private function prepareEmail(?string $text, ?string $html, array $context = [], HtmlToTextConverterInterface $converter = null): TemplatedEmail
125137
{
126138
$twig = new Environment(new ArrayLoader([
127139
'text' => $text,
128140
'html' => $html,
129141
'document.txt' => 'Some text document...',
130142
'image.jpg' => 'Some image data',
131143
]));
132-
$renderer = new BodyRenderer($twig);
144+
$renderer = new BodyRenderer($twig, [], $converter);
133145
$email = (new TemplatedEmail())
134146
135147

composer.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
"require-dev": {
2424
"doctrine/annotations": "^1.12",
2525
"egulias/email-validator": "^2.1.10|^3",
26+
"league/html-to-markdown": "^5.0",
2627
"phpdocumentor/reflection-docblock": "^3.0|^4.0|^5.0",
2728
"symfony/asset": "^5.4|^6.0",
2829
"symfony/dependency-injection": "^5.4|^6.0",
@@ -32,7 +33,7 @@
3233
"symfony/http-foundation": "^5.4|^6.0",
3334
"symfony/http-kernel": "^6.2",
3435
"symfony/intl": "^5.4|^6.0",
35-
"symfony/mime": "^5.4|^6.0",
36+
"symfony/mime": "^6.2",
3637
"symfony/polyfill-intl-icu": "~1.0",
3738
"symfony/property-info": "^5.4|^6.0",
3839
"symfony/routing": "^5.4|^6.0",
@@ -59,6 +60,7 @@
5960
"symfony/form": "<6.1",
6061
"symfony/http-foundation": "<5.4",
6162
"symfony/http-kernel": "<6.2",
63+
"symfony/mime": "<6.2",
6264
"symfony/translation": "<5.4",
6365
"symfony/workflow": "<5.4"
6466
},

0 commit comments

Comments
 (0)