In the business apps I’ve built (car rental contracts, shop reports, invoices), the easiest way to produce documents is to design them as HTML and convert them to PDF. You keep using CSS, and the result prints the same everywhere.

1. Install Dompdf

composer require dompdf/dompdf

2. Design the invoice as HTML

Use simple CSS: tables, fixed widths and web-safe or embedded fonts. Flexbox and grid have limited support in PDF engines.

function invoiceHtml(array $invoice): string {
    $rows = '';
    foreach ($invoice['lines'] as $line) {
        $rows .= sprintf(
            '<tr><td>%s</td><td class="num">%d</td><td class="num">%s</td></tr>',
            htmlspecialchars($line['label']),
            $line['qty'],
            number_format($line['qty'] * $line['price'], 2)
        );
    }
    return '<html><head><style>
        body { font-family: DejaVu Sans, sans-serif; font-size: 12px; }
        table { width: 100%; border-collapse: collapse; }
        td, th { border-bottom: 1px solid #ddd; padding: 6px; }
        .num { text-align: right; }
    </style></head><body>
        <h1>Invoice ' . htmlspecialchars($invoice['number']) . '</h1>
        <table><tr><th>Item</th><th class="num">Qty</th><th class="num">Total</th></tr>' . $rows . '</table>
    </body></html>';
}

Escape every value with htmlspecialchars(): a client name containing < would otherwise break the layout.

3. Render the PDF

use DompdfDompdf;
use DompdfOptions;

require 'vendor/autoload.php';

$options = new Options();
$options->set('isRemoteEnabled', false); // don't fetch external URLs found in the HTML
$options->set('defaultFont', 'DejaVu Sans');

$dompdf = new Dompdf($options);
$dompdf->loadHtml(invoiceHtml($invoice));
$dompdf->setPaper('A4', 'portrait');
$dompdf->render();

DejaVu Sans ships with Dompdf and covers accented characters, so names such as “Éloïse” render correctly.

4. Stream or save the file

// Open in the browser (Attachment => true forces a download)
$dompdf->stream('invoice-' . $invoice['number'] . '.pdf', ['Attachment' => false]);

// Or keep a copy
file_put_contents(__DIR__ . '/invoices/' . $invoice['number'] . '.pdf', $dompdf->output());

5. Right-to-left and Arabic text with mPDF

Arabic letters change shape depending on their neighbours and run right to left. mPDF handles both:

composer require mpdf/mpdf
$mpdf = new MpdfMpdf([
    'mode'          => 'utf-8',
    'format'        => 'A4',
    'default_font'  => 'dejavusans',
]);
$mpdf->autoScriptToLang = true;  // detect Arabic runs in the text
$mpdf->autoLangToFont   = true;  // pick a font that has the glyphs
$mpdf->SetDirectionality('rtl');
$mpdf->WriteHTML($html);
$mpdf->Output('report.pdf', MpdfOutputDestination::INLINE);

Also set dir="rtl" on the HTML so tables and alignment flip as expected.