Skip to content

Unable to add PDF as attachment

So I am using magento 2.4.6 and have created a PDF that customer can either download or email. The PDF is created and downlaoded successfully.
Below is my code which sends an email without PDF for now, I have tried so many different approaches but still unable to attach PDF to the email.

<?php
namespace VendorModuleControllerIndex;

use MagentoFrameworkAppActionAction;
use MagentoFrameworkAppActionContext;
use MagentoFrameworkControllerResultJsonFactory;
use MagentoQuoteApiCartRepositoryInterface;
use VendorModuleBlockImages;
use Zend_Pdf;
use Zend_Pdf_Page;
use Zend_Pdf_Style;
use Zend_Pdf_Color_Rgb;
use Zend_Pdf_Font;
use Zend_Pdf_Image;
use ZendMimePart as MimePart;
use ZendMimeMime;
use MagentoFrameworkAppResponseHttpFileFactory;
use MagentoFrameworkMailTemplateTransportBuilder;
use MagentoFrameworkTranslateInlineStateInterface;
use MagentoStoreModelStoreManagerInterface;
use MagentoFrameworkUrlInterface;

class GeneratePdf extends Action
{
    protected $resultJsonFactory;
    protected $quoteRepository;
    protected $imagesBlock;
    protected $transportBuilder;
    protected $inlineTranslation;
    protected $storeManager;
    protected $fileFactory;
    protected $url;

    public function __construct(
        Context $context,
        JsonFactory $resultJsonFactory,
        CartRepositoryInterface $quoteRepository,
        Images $imagesBlock,
        TransportBuilder $transportBuilder,
        StateInterface $inlineTranslation,
        StoreManagerInterface $storeManager,
        FileFactory $fileFactory,
        UrlInterface $url // Add this dependency
    ) {
        $this->resultJsonFactory = $resultJsonFactory;
        $this->quoteRepository = $quoteRepository;
        $this->imagesBlock = $imagesBlock;
        $this->transportBuilder = $transportBuilder;
        $this->inlineTranslation = $inlineTranslation;
        $this->storeManager = $storeManager;
        $this->fileFactory = $fileFactory;
        $this->url = $url; // Assign it to the class property
        parent::__construct($context);
    }

    public function execute()
    {
        $quoteId = $this->getRequest()->getParam('quote_id');
        $action = $this->getRequest()->getParam('action');
    
        try {
            $quote = $this->quoteRepository->get($quoteId);
    
            $itemsByCategory = [];
            foreach ($quote->getAllItems() as $item) {
                $categoryId = $item->getCategoryId();
                $itemsByCategory[$categoryId][] = $item;
            }
    
            $pdf = new Zend_Pdf();
            $page = $pdf->newPage(Zend_Pdf_Page::SIZE_A4);
            $pdf->pages[] = $page;
    
            // Get media URL and convert to file path
            $mediaUrl = $this->imagesBlock->getMediaUrl('porto_child/images/foodpass_logo.png');
            $baseUrl = $this->storeManager->getStore()->getBaseUrl(MagentoFrameworkUrlInterface::URL_TYPE_MEDIA);
            $mediaPath = str_replace($baseUrl, '', $mediaUrl); // Remove base URL part
            $imagePath = BP . '/pub/media/' . ltrim($mediaPath, '/'); // BP is Magento base path
    
            if (file_exists($imagePath)) {
                $image = Zend_Pdf_Image::imageWithPath($imagePath);
                $page->drawImage($image, 30, 770, 130, 820); // Adjust coordinates as needed
            } else {
                throw new Exception("Image file not found: " . $imagePath);
            }
    
            $style = new Zend_Pdf_Style();
            $style->setLineColor(new Zend_Pdf_Color_Rgb(0, 0, 0));
            $font = Zend_Pdf_Font::fontWithName(Zend_Pdf_Font::FONT_HELVETICA); // Using Arial (Helvetica in Zend_Pdf)
            $style->setFont($font, 16);
            $page->setStyle($style);
            $x = 30;
            $y = 730; // Adjusted to make space for the image
    
            foreach ($itemsByCategory as $categoryId => $items) {
                $categoryName = $this->imagesBlock->getCategoryNameById($categoryId);
    
                $page->setFont($font, 16);
                $page->drawText("Store: " . $categoryName, $x, $y);
                $y -= 20;
    
                $page->setFont($font, 13);
    
                // Draw table headers
                $headers = ["Product", "Format", "Brand", "Quantity", "Price"];
                $headerX = [$x, $x + 150, $x + 250, $x + 350, $x + 450];
                foreach ($headers as $index => $header) {
                    $page->drawText($header, $headerX[$index], $y);
                }
                $y -= 20;
    
                // Draw table line
                $page->setLineWidth(0.5);
                $page->drawLine($x, $y, $x + 500, $y);
                $y -= 20;
    
                foreach ($items as $item) {
                    if ($y < 50) {
                        $page = $pdf->newPage(Zend_Pdf_Page::SIZE_A4);
                        $pdf->pages[] = $page;
                        $page->setStyle($style);
                        $y = 750;
                    }
    
                    $itemDetail = $this->imagesBlock->getProductById($item->getProductId());
    
                    // Draw item details in table format with text wrapping
                    $productName = $item->getName();
                    $price = number_format($item->getPrice(), 2); // Format price to 2 decimal places
                    $quantity = $item->getQty();
                    $productFormatAttribute = $itemDetail->getCustomAttribute('product_format');
                    $productFormat = $productFormatAttribute ? $productFormatAttribute->getValue() : '';
                    $productBrandAttribute = $itemDetail->getCustomAttribute('product_brand');
                    $productBrand = $productBrandAttribute ? $productBrandAttribute->getValue() : '';
    
                    // Wrap text for product name if it's too long
                    $maxProductNameWidth = 150;
                    $wrappedProductName = $this->wrapText($productName, $page->getFont(), $page->getFontSize(), $maxProductNameWidth);
    
                    // Draw the wrapped text line by line
                    foreach ($wrappedProductName as $line) {
                        $page->drawText($line, $x, $y);
                        $y -= 20;
                    }
                    $page->drawText($productFormat, $x + 150, $y + 20 * count($wrappedProductName)); // Draw format at correct position
                    $page->drawText($productBrand, $x + 250, $y + 20 * count($wrappedProductName)); // Draw brand at correct position
                    $page->drawText($quantity, $x + 350, $y + 20 * count($wrappedProductName)); // Draw quantity at correct position
                    $page->drawText($price . '$', $x + 450, $y + 20 * count($wrappedProductName)); // Draw price at correct position
    
                    $y -= 20;
                }
                $y -= 20; // Extra space after each category
            }
    
            $fileName = 'quote_' . $quoteId . '.pdf';
            $filePath = 'var/' . $fileName;
    
            $directory = dirname($filePath);
            if (!is_dir($directory)) {
                mkdir($directory, 0777, true);
            }
    
            file_put_contents($filePath, $pdf->render());
    
            if ($action === 'email') {
                $this->sendEmail($quote, $filePath);
            } else {
                $this->getResponse()->setHeader('Content-type', 'application/pdf');
                $this->getResponse()->setHeader('Content-Disposition', 'attachment; filename="' . $fileName . '"');
                $this->getResponse()->setBody(file_get_contents($filePath));
                return;
            }
    
            $result = $this->resultJsonFactory->create();
            return $result->setData(['pdf_url' => $this->url->getBaseUrl() . $filePath]);
        } catch (Exception $e) {
            // Log the error message
            $result = $this->resultJsonFactory->create();
            return $result->setData(['error' => 'Error generating PDF: ' . $e->getMessage()]);
        }
    }
    
    protected function wrapText($text, $font, $fontSize, $maxWidth)
    {
        $wrappedLines = [];
        $words = explode(' ', $text);
        $line = '';
    
        foreach ($words as $word) {
            $testLine = $line ? $line . ' ' . $word : $word;
            $testWidth = $this->getTextWidth($testLine, $font, $fontSize);
    
            if ($testWidth <= $maxWidth) {
                $line = $testLine;
            } else {
                if ($line) {
                    $wrappedLines[] = $line;
                }
                $line = $word;
            }
        }
    
        if ($line) {
            $wrappedLines[] = $line;
        }
    
        return $wrappedLines;
    }
    
    protected function getTextWidth($text, $font, $fontSize)
    {
        $drawingText = iconv('UTF-8', 'UTF-16BE//IGNORE', $text);
        $characters = [];
        for ($i = 0; $i < strlen($drawingText); $i++) {
            $characters[] = (ord($drawingText[$i++]) << 8) | ord($drawingText[$i]);
        }
    
        $glyphs = $font->glyphNumbersForCharacters($characters);
        $widths = $font->widthsForGlyphs($glyphs);
        $textWidth = (array_sum($widths) / $font->getUnitsPerEm()) * $fontSize;
    
        return $textWidth;
    }

    public function sendEmail($quote, $filePath)
    {
        $writer = new Zend_Log_Writer_Stream(BP . '/var/log/custom.log');
        $logger = new Zend_Log();
        $logger->addWriter($writer);
        try {
            $customerEmail = $quote->getCustomerEmail();
            $this->inlineTranslation->suspend();
            $sender = [
                'name' => 'Emailname',
                'email' => '[email protected]'
            ];
            $transport = $this->transportBuilder
                ->setTemplateIdentifier('foodpass_quote_email_template')
                ->setTemplateOptions(
                    [
                        'area' => MagentoFrameworkAppArea::AREA_FRONTEND,
                        'store' => MagentoStoreModelStore::DEFAULT_STORE_ID,
                    ]
                )
                ->setTemplateVars([
                    'templateVar'  => 'My Topic',
                ])
                ->setFrom($sender)
                ->addTo($customerEmail)
                ->getTransport();
                
            $transport->sendMessage();
            $this->inlineTranslation->resume();
        } catch (Exception $e) {
            $logger->info($e->getMessage());
            $this->logger->debug($e->getMessage());
        }
    }
}