Hello Magento Friends,
Have you upgraded to Magento 2.4.8? If not, avail Magento Upgrade Service.

Magento 2.4.8 has brought several performance enhancements and security updates, but like any upgrade, it might also introduce a few unexpected bugs or compatibility issues. One common issue reported by many store owners and developers after upgrading to Magento 2.4.8 is that PDF attachments are no longer working as expected, especially for order invoices, shipment PDFs, or email attachments.
Follow the steps below to fix the issue of the PDF attachment not working for your upgraded store.
Steps to Fix PDF Attachment Not Working Issue in Magento 2.4.8:
Step 1: First, we need to create a “di.xml“ file inside our extension at the following path
app\code\Vendor\Extension\etc\di.xml
Then add the code as follows
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<preference for="Magento\Framework\Mail\Template\TransportBuilder" type="Vendor\Extension\Model\Mail\TransportBuilder"/>
</config>
Step 2: After that, we need to create a “TransportBuilder.php” file inside our extension at the following path
app\code\Vendor\Extension\Model\Mail\TransportBuilder.php
And add the code as given below
<?php
declare(strict_types=1);
namespace Vendor\Extension\Model\Mail;
use Magento\Framework\App\TemplateTypesInterface;
use Magento\Framework\Exception\LocalizedException;
use Magento\Framework\Exception\MailException;
use Magento\Framework\Mail\EmailMessageInterface;
use Magento\Framework\Mail\EmailMessageInterfaceFactory;
use Magento\Framework\Mail\AddressConverter;
use Magento\Framework\Mail\Exception\InvalidArgumentException;
use Magento\Framework\Mail\MessageInterface;
use Magento\Framework\Mail\MessageInterfaceFactory;
use Magento\Framework\Mail\MimeInterface;
use Magento\Framework\Mail\MimeMessageInterfaceFactory;
use Magento\Framework\Mail\MimePartInterfaceFactory;
use Magento\Framework\Mail\Template\FactoryInterface;
use Magento\Framework\Mail\Template\SenderResolverInterface;
use Magento\Framework\Mail\TemplateInterface;
use Magento\Framework\Mail\TransportInterface;
use Magento\Framework\Mail\TransportInterfaceFactory;
use Magento\Framework\ObjectManagerInterface;
use Magento\Framework\Phrase;
use Magento\Framework\HTTP\Mime;
use Symfony\Component\Mime\Part\DataPart;
use Symfony\Component\Mime\Part\Multipart\MixedPart;
class TransportBuilder extends \Magento\Framework\Mail\Template\TransportBuilder
{
/**
* Param that used for storing all message data until it will be used
*
* @var array
*/
private $messageData = [];
/**
* @var EmailMessageInterfaceFactory
*/
private $emailMessageInterfaceFactory;
/**
* @var MimeMessageInterfaceFactory
*/
private $mimeMessageInterfaceFactory;
/**
* @var MimePartInterfaceFactory
*/
private $mimePartInterfaceFactory;
/**
* @var AddressConverter
*/
private $addressConverter;
/**
* @var string
*/
private $messageBodyParts = [];
/**
* @param FactoryInterface $templateFactory
* @param MessageInterface $message
* @param SenderResolverInterface $senderResolver
* @param ObjectManagerInterface $objectManager
* @param TransportInterfaceFactory $mailTransportFactory
* @param EmailMessageInterfaceFactory $emailMessageInterfaceFactory
* @param MimeMessageInterfaceFactory $mimeMessageInterfaceFactory
* @param MimePartInterfaceFactory $mimePartInterfaceFactory
* @param AddressConverter $addressConverter
*/
public function __construct(
FactoryInterface $templateFactory,
MessageInterface $message,
SenderResolverInterface $senderResolver,
ObjectManagerInterface $objectManager,
TransportInterfaceFactory $mailTransportFactory,
EmailMessageInterfaceFactory $emailMessageInterfaceFactory,
MimeMessageInterfaceFactory $mimeMessageInterfaceFactory,
MimePartInterfaceFactory $mimePartInterfaceFactory,
AddressConverter $addressConverter
) {
parent::__construct(
$templateFactory,
$message,
$senderResolver,
$objectManager,
$mailTransportFactory
);
$this->emailMessageInterfaceFactory = $emailMessageInterfaceFactory;
$this->mimeMessageInterfaceFactory = $mimeMessageInterfaceFactory;
$this->mimePartInterfaceFactory = $mimePartInterfaceFactory;
$this->addressConverter = $addressConverter;
}
/**
* Add cc address
*
* @param array|string $address
* @param string $name
*
* @return $this
*/
public function addCc($address, $name = '')
{
$this->addAddressByType('cc', $address, $name);
return $this;
}
/**
* Add to address
*
* @param array|string $address
* @param string $name
*
* @return $this
* @throws InvalidArgumentException
*/
public function addTo($address, $name = '')
{
$this->addAddressByType('to', $address, $name);
return $this;
}
/**
* Add bcc address
*
* @param array|string $address
*
* @return $this
* @throws InvalidArgumentException
*/
public function addBcc($address)
{
$this->addAddressByType('bcc', $address);
return $this;
}
/**
* Set Reply-To Header
*
* @param string $email
* @param string|null $name
*
* @return $this
* @throws InvalidArgumentException
*/
public function setReplyTo($email, $name = null)
{
$this->addAddressByType('replyTo', $email, $name);
return $this;
}
/**
* Set mail from address
*
* @param string|array $from
*
* @return $this
* @throws InvalidArgumentException
* @see setFromByScope()
*
* @deprecated 102.0.1 This function sets the from address but does not provide
* a way of setting the correct from addresses based on the scope.
*/
public function setFrom($from)
{
return $this->setFromByScope($from);
}
/**
* Set mail from address by scopeId
*
* @param string|array $from
* @param string|int $scopeId
*
* @return $this
* @throws InvalidArgumentException
* @throws MailException
* @since 102.0.1
*/
public function setFromByScope($from, $scopeId = null)
{
$result = $this->_senderResolver->resolve($from, $scopeId);
$this->addAddressByType('from', $result['email'], $result['name']);
return $this;
}
/**
* Reset object state
*
* @return $this
*/
protected function reset()
{
$this->messageData = [];
$this->templateIdentifier = null;
$this->templateVars = null;
$this->templateOptions = null;
$this->messageBodyParts = [];
return $this;
}
/**
* Prepare message.
*
* @return $this
* @throws LocalizedException if template type is unknown
*/
protected function prepareMessage()
{
$template = $this->getTemplate();
$content = $template->processTemplate();
switch ($template->getType()) {
case TemplateTypesInterface::TYPE_TEXT:
$partType = MimeInterface::TYPE_TEXT;
break;
case TemplateTypesInterface::TYPE_HTML:
$partType = MimeInterface::TYPE_HTML;
break;
default:
throw new LocalizedException(
new Phrase('Unknown template type')
);
}
/** @var \Magento\Framework\Mail\MimePartInterface $mimePart */
$mimePart = $this->mimePartInterfaceFactory->create(
[
'content' => $content,
'type' => $partType
]
);
$this->messageData['encoding'] = $mimePart->getCharset();
$this->messageData['body'] = $this->mimeMessageInterfaceFactory->create(
['parts' => [$mimePart]]
);
$this->messageData['subject'] = html_entity_decode(
(string)$template->getSubject(),
ENT_QUOTES
);
$this->message = $this->emailMessageInterfaceFactory->create($this->messageData);
if (count($this->messageBodyParts)) {
$this->messageData['body']->getMimeMessage()->setBody(
new MixedPart($this->messageData['body']->getMimeMessage()->getBody(), ...$this->messageBodyParts)
);
}
return $this;
}
/**
* Handles possible incoming types of email (string or array)
*
* @param string $addressType
* @param string|array $email
* @param string|null $name
*
* @return void
* @throws InvalidArgumentException
*/
private function addAddressByType(string $addressType, $email, ?string $name = null): void
{
if (is_string($email)) {
$this->messageData[$addressType][] = $this->addressConverter->convert($email, $name);
return;
}
$convertedAddressArray = $this->addressConverter->convertMany($email);
if (isset($this->messageData[$addressType])) {
$this->messageData[$addressType] = array_merge(
$this->messageData[$addressType],
$convertedAddressArray
);
} else {
$this->messageData[$addressType] = $convertedAddressArray;
}
}
/**
* @inheritdoc
*/
public function addAttachment(
$body,
$enable,
$mimeType = Mime::TYPE_OCTETSTREAM
) {
if ($enable) {
$this->messageBodyParts[] = new DataPart(
$body,
'Sample.pdf',
$mimeType,
Mime::ENCODING_BASE64
);
}
return $this;
}
}
Conclusion:
Hope the above solution helped you to solve the PDF not working issue in Magento 2.4.8.
Still facing issues? You might want to consider consulting a Magento expert.

Happy Coding!