I'm generating a PDF using OpenHTMLToPDF (PdfRendererBuilder) in a Spring Boot app, and my custom fonts (ttf) are not being applied. What am I missing for OpenHTMLToPDF to properly apply custom TTF fonts registered via useFont()? I've been struggling a few days on this

I'm trying to use NotoSansArabic (Regular + Bold), but the PDF still renders with a fallback font

Font files (in resources):

assets/fonts/NotoSansArabic-Regular.ttf assets/fonts/NotoSansArabic-Bold.ttf // From the builder class (rest was omitted) val builder = PdfRendererBuilder() .useFastMode() .withHtmlContent(html, null) .toStream(outputStream) builder.useFont( { ClassPathResource("assets/fonts/NotoSansArabic-Regular.ttf").inputStream }, "Noto Sans Arabic", 400, com.openhtmltopdf.outputdevice.helper.BaseRendererBuilder.FontStyle.NORMAL, true ) builder.useFont( { ClassPathResource("assets/fonts/NotoSansArabic-Bold.ttf").inputStream }, "Noto Sans Arabic", 700, com.openhtmltopdf.outputdevice.helper.BaseRendererBuilder.FontStyle.NORMAL, true ) builder.run() The template: <!DOCTYPE html> <html> <head> <meta charset="UTF-8" /> <style> @page { size: A4; margin: 40px 40px 60px 40px; @bottom-center { content: "Thank you for your business!"; font-size: 10px; color: #4B5563; font-family: 'Noto Sans Arabic', sans-serif; } @bottom-right { content: "{{GENERATED_DATE}} · Page " counter(page) " of " counter(pages); font-size: 9px; color: #4B5563; font-family: 'Noto Sans Arabic', sans-serif; } } body { font-family: 'Noto Sans Arabic', sans-serif; font-size: 13px; color: #111827; margin: 0; padding: 0; } .header-table { width: 100%; border-collapse: collapse; margin-bottom: 28px; } .header-table td { vertical-align: top; padding: 0; } .header-table .left-col { width: 50%; } .header-table .right-col { width: 50%; text-align: right; } .header-table img { width: 110px; height: auto; margin-bottom: 10px; } .company-info { font-size: 12px; color: #4B5563; line-height: 1.6; margin-top: 6px; } .company-name { font-size: 14px; font-weight: bold; color: #111827; } .receipt-title { font-size: 28px; font-weight: bold; color: #111827; margin: 0 0 8px 0; } .receipt-meta { font-size: 12px; color: #4B5563; line-height: 1.8; } .receipt-meta strong { color: #374151; } .divider { border: none; border-top: 0.5px solid #B0B4BC; margin: 0 0 24px 0; } .section-label { font-size: 11px; font-weight: bold; color: #4B5563; text-transform: uppercase; letter-spacing: 1px; margin-bottom: 6px; } .section-block { margin-bottom: 24px; } .section-value { font-size: 13px; color: #111827; line-height: 1.7; } .items-table { width: 100%; border-collapse: collapse; margin-bottom: 8px; } .items-table th { font-size: 11px; font-weight: bold; color: #4B5563; text-transform: uppercase; letter-spacing: 0.5px; padding: 10px 12px; border-bottom: 0.5px solid #B0B4BC; text-align: left; } .items-table th.num { text-align: right; } .items-table td { padding: 10px 12px; border-bottom: 0.5px solid #D0D3D9; font-size: 13px; color: #111827; } .items-table td.num { text-align: right; } .items-table tr:last-child td { border-bottom: 0.5px solid #B0B4BC; } .summary-table { width: 100%; border-collapse: collapse; margin-bottom: 32px; } .summary-table td { padding: 6px 12px; } .summary-table .summary-spacer { width: 55%; } .summary-table .summary-label { text-align: right; font-size: 13px; color: #4B5563; width: 25%; } .summary-table .summary-value { text-align: right; font-size: 13px; color: #111827; width: 20%; } .summary-table .total-row .summary-label { font-size: 15px; font-weight: bold; color: #111827; padding-top: 10px; border-top: 0.5px solid #B0B4BC; } .summary-table .total-row .summary-value { font-size: 15px; font-weight: bold; color: #111827; padding-top: 10px; border-top: 0.5px solid #B0B4BC; } </style> </head> <body> <!-- Header --> <table class="header-table"> <tr> <td class="left-col"> {{LOGO}} <div class="company-info"> </div> </td> <td class="right-col"> <div class="receipt-title">RECEIPT</div> <div class="receipt-meta"> <strong>Order #{{ORDER_NUMBER}}</strong><br/> Order Date: {{ORDER_DATE}}<br/> {{DELIVERED_DATE_LINE}} </div> </td> </tr> </table> <hr class="divider"/> <!-- Customer --> <div class="section-block"> <div class="section-label">Customer</div> <div class="section-value"> {{BUSINESS_NAME_LINE}} {{FULL_NAME}}<br/> {{PHONE}}<br/> {{ALT_PHONE_LINE}} {{EMAIL}} </div> </div> <!-- Delivery address --> {{DELIVERY_SECTION}} <hr class="divider"/> <!-- Items --> <table class="items-table"> <thead> <tr> <th>Product</th> <th class="num">Qty</th> <th>Unit</th> <th class="num">Price (DZD)</th> <th class="num">Total (DZD)</th> </tr> </thead> <tbody> {{PRODUCT_ROWS}} </tbody> </table> <!-- Summary --> <table class="summary-table"> <tr> <td class="summary-spacer"></td> <td class="summary-label">Subtotal</td> <td class="summary-value">{{GRAND_TOTAL}} DZD</td> </tr> <tr class="total-row"> <td class="summary-spacer"></td> <td class="summary-label">TOTAL</td> <td class="summary-value">{{GRAND_TOTAL}} DZD</td> </tr> </table> </body> </html>

user2342348's user avatar

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.