angularhighchartspdf-generationhtml2pdf

Custom Font-Family for Highcharts Labels Not Applied When Printing with html2pdf in Angular


I am using Highcharts in my Angular application and I have successfully changed the font-family of the axis labels using the following CSS code:

.highcharts-yaxis-labels, .highcharts-xaxis-labels, .highcharts-root, .mat-form-field { font-family: "YekanBakh" !important; }

This works perfectly when displaying the chart in the browser. However, when I try to generate a PDF using the html2pdf library, the custom font family is not applied in the generated PDF.

Here is the code I am using to generate the PDF:

public onPrint(): void {
    const content = document.getElementById('print-layout');
    const options = {
        margin: [5, 2, 5, 2],
        filename: 'document.pdf',
        pagebreak: {
            mode: ['avoid-all', 'css'], 
            before: "#nextpage"
        },
        image: { type: 'jpeg', quality: 0.98 },
        html2canvas: { scale: 2 },
        jsPDF: { 
            unit: 'mm', 
            orientation: 'p', 
            format: 'a4', 
            putOnlyUsedFonts: true, 
            compress: true, 
            floatPrecision: 'smart'
        },
    };

    html2pdf()
        .from(content)
        .set(options)
        .save();
}

When the chart is printed to a PDF, the YekanBakh font for the Highcharts labels is not applied. Instead, it defaults to some other font, despite the CSS being applied correctly in the browser.

My Question: How can I make sure that the custom font ("YekanBakh") is applied to the axis labels in the PDF generated by html2pdf?

Verified that the font is properly applied in the browser.
Added !important to the CSS rule to ensure it's being applied.
Ensured that Highcharts rendering works well on the screen, but the issue persists in the PDF output.

Solution

  • I found the answer on my own

    The issue wasn't directly related to Highcharts itself but rather to how fonts are handled in SVG elements when generating PDFs. Highcharts charts are rendered as SVG, and html2pdf doesn’t automatically embed the custom fonts inside these SVGs. This is why the custom "YekanBakh" font didn't appear in the PDF, even though it worked fine in the browser.

    To ensure the custom font is applied in the PDF, you need to inject the font into the elements before generating the PDF. This way, the font will be embedded into the SVG, and it will be preserved when converting the HTML content to a PDF.

    Here's how you can solve the issue:

    Extract all SVG elements from the DOM. Insert the @font-face rule dynamically inside each SVG using and . Generate the PDF after the font has been injected into the SVG.

      const content: any = document.getElementById('print-layout');
    
    // Step 1: Find all SVG elements in the print-layout
    const svgs = content?.querySelectorAll('svg');
    
    // Step 2: Define the font-face to be embedded in SVG
    const fontFace = `
      @font-face {
        font-family: 'YekanBakh';
        src: url(data:application/font-woff;charset=utf-8;base64,AAEAAAAPAIAAAwBwRFNJRwAAAAEAANI8AAAACEdERUYKqwqtAADSRAAA) format('woff');
      }
    `;
    
    // Step 3: Loop through each SVG and inject the font-face style
    svgs?.forEach((svg: any) => {
      const defs = svg.querySelector('defs') || document.createElement('defs');
      let styleElement = svg.querySelector('style');
    
      // If <style> tag doesn't exist, create one and append to defs
      if (!styleElement) {
        styleElement = document.createElement('style');
        defs.appendChild(styleElement);
      }
    
      // Inject the font-face rule into the style tag
      styleElement.innerHTML = fontFace;
    
      // Append defs to the SVG if it's not already present
      if (!svg.querySelector('defs')) {
        svg.appendChild(defs);
      }
    });