Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom font loading issue #334

Closed
sebekgr opened this issue Oct 9, 2018 · 15 comments
Closed

Custom font loading issue #334

sebekgr opened this issue Oct 9, 2018 · 15 comments
Labels

Comments

@sebekgr
Copy link

sebekgr commented Oct 9, 2018

OS:
Linux

React-pdf version:
"@react-pdf/renderer": "1.0.0-alpha.18"

Description:
I have a problem with load custom font.
In docs we can read "Specifies the source of the font. This can either be a valid URL, or an absolute path if you're using react-pdf on Node."

So in first approach i tried include font from google font
https://fonts.gstatic.com/s/roboto/v18/KFOmCnqEu92Fr1Mu72xKOzY.woff2
but this format is not support.
So in second approach I tried upload Roboto font in tff format to my private hosting but in DEMO code there was some message about mix content http/https .. so I upload font into github which has https protocol.

Also I tried served locally font in my react app but there is message error:
" No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://localhost:3005' is therefore not allowed access. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled."

I built local server for serving font with enabled cors but it doesn't work

I provide react demo where I tried generate pdf with some custom pdf.
https://codesandbox.io/s/5k72n7zq2n

I think there is a problem with fetch method. Exactly in file react-pdf.browser.es.js with
fetchFont method

var fetchFont = function fetchFont(src) {
return fetch(src).then(function (response) {
if (response.buffer) {
return response.buffer();
}
return response.arrayBuffer();
}).then(function (arrayBuffer) {
return Buffer.from(arrayBuffer);
});
};
I think that fetch method should include some headers and cors mode for fetching successfully any resources

So the final questions is:
How do I use some custom font ?

@jasonbarry
Copy link

I have the same question. I haven't been able to use a custom font successfully. It would be helpful to know what font types are supported (.otf, .ttf, .woff, etc) and how to fetch them via URL without running into CORS issues (both during development and in production).

@diegomura diegomura added the bug label Oct 23, 2018
@emorling
Copy link

emorling commented Mar 9, 2019

The React PDF Playground has a link to
https://fonts.gstatic.com/s/oswald/v13/Y_TKV6o8WovbUd3m_X9aAA.ttf

Does anyone know if these TTF links exist for all Google Fonts?

@diegomura
Copy link
Owner

Nope. Not sure if they do. Sorry

@diegomura
Copy link
Owner

@emorling I recently came across to this repo https://github.com/google/fonts that has all the google fonts in .ttf format

@diegomura
Copy link
Owner

@jasonbarry Only .ttf fonts supported for the moment

@diegomura
Copy link
Owner

Closing this since all points had been cleared

@marticorena
Copy link

marticorena commented Apr 29, 2019

The React PDF Playground has a link to
https://fonts.gstatic.com/s/oswald/v13/Y_TKV6o8WovbUd3m_X9aAA.ttf

Does anyone know if these TTF links exist for all Google Fonts?

Here you can find a similar link to all google fonts:
https://gist.github.com/sadikay/d5457c52e7fb2347077f5b0fe5ba9300

@x-dune
Copy link

x-dune commented Nov 16, 2019

@jasonbarry Only .ttf fonts supported for the moment

I think this information would be valuable in the docs. 😃

@earthnoob
Copy link

Are WOFF/WOFF2 formats supported? I've been trying to import both in files and from Google Font CDN links but to no avail.

@moparlakci
Copy link

Any known issues when using docker? On development it works fine. But on my production environment, with docker alpine linux it doens't show some of the text. I am using flex to style the items but as I said it works fine on dev.

see screenshots

dev
prod

@tom2strobl
Copy link

@jasonbarry Only .ttf fonts supported for the moment

I think this information would be valuable in the docs. 😃

lol, it took me two days and a look at ~50 github issues in this repo to find this random comment in a 2018 issue to confirm that only ttf is working. Which is really troublesome since ttf is a largely deprecated format and lots of new fonts are only released as otf (which is also a better and more modern format in every way). The original fontkit also seems to support otf/woff, so (obviously without knowing the full picture) it sounds like not too far fetched?

@diegomura what are the major show-stoppers? Where do we stand, is there a specific point where I could help?

@bradenbiz
Copy link

Does anyone know if these TTF links exist for all Google Fonts?

Yes, you just have to use curl on the URL you get from fonts.google.com to get that gstatic URL, which seems to be the only URL that works in getting the font registered with react-pdf.

@schnz
Copy link

schnz commented Oct 5, 2023

Only .ttf fonts supported for the moment

@diegomura Has this changed since you wrote this comment? Because .woff fonts seem to work fine, whereas .woff2 seem to be problematic.

@denisbetsi
Copy link

denisbetsi commented May 24, 2024

Until woff2 is supported. I've created a python script which accepts Google fonts URL, it downloads and converts file into ttf and saves them in ./fonts directory, it then generates react PDF Font.register with all of the font styles,weights and references to the ttf converted font files.

Usage:

python get-fonts.py "<URL>" <unicode-range>

Example:

python get-fonts.py "https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,300;0,400;0,900;1,300;1,400;1,900&display=swap" latin

Downloads & Converts font files, then create output.ts file example:

import { Font } from '@react-pdf/renderer';

type FontWeight = 'normal' | 'bold' | 'light' | undefined;
type FontStyle = 'normal' | 'italic' | 'oblique' | undefined;

Font.register({
    family: 'Roboto:ital,wght@0,300;0,400;0,900;1,300;1,400;1,900',
    fonts: [
        {
            src: 'fonts/KFOjCnqEu92Fr1Mu51TjASc1CsTKlA.ttf',
            fontStyle: 'italic' as FontStyle,
            fontWeight: '300' as FontWeight,
        },
        {
            src: 'fonts/KFOjCnqEu92Fr1Mu51TjASc0CsTKlA.ttf',
            fontStyle: 'italic' as FontStyle,
            fontWeight: '300' as FontWeight,
        },
        {
            src: 'fonts/KFOkCnqEu92Fr1Mu51xHIzIFKw.ttf',
            fontStyle: 'italic' as FontStyle,
            fontWeight: '400' as FontWeight,
        },
        {
            src: 'fonts/KFOkCnqEu92Fr1Mu51xGIzIFKw.ttf',
            fontStyle: 'italic' as FontStyle,
            fontWeight: '400' as FontWeight,
        },
        {
            src: 'fonts/KFOjCnqEu92Fr1Mu51TLBCc1CsTKlA.ttf',
            fontStyle: 'italic' as FontStyle,
            fontWeight: '900' as FontWeight,
        },
        {
            src: 'fonts/KFOjCnqEu92Fr1Mu51TLBCc0CsTKlA.ttf',
            fontStyle: 'italic' as FontStyle,
            fontWeight: '900' as FontWeight,
        },
        {
            src: 'fonts/KFOlCnqEu92Fr1MmSU5fCxc4EsA.ttf',
            fontStyle: 'normal' as FontStyle,
            fontWeight: '300' as FontWeight,
        },
        {
            src: 'fonts/KFOlCnqEu92Fr1MmSU5fChc4EsA.ttf',
            fontStyle: 'normal' as FontStyle,
            fontWeight: '300' as FontWeight,
        },
        {
            src: 'fonts/KFOmCnqEu92Fr1Mu7WxKOzY.ttf',
            fontStyle: 'normal' as FontStyle,
            fontWeight: '400' as FontWeight,
        },
        {
            src: 'fonts/KFOmCnqEu92Fr1Mu7GxKOzY.ttf',
            fontStyle: 'normal' as FontStyle,
            fontWeight: '400' as FontWeight,
        },
        {
            src: 'fonts/KFOlCnqEu92Fr1MmYUtfCxc4EsA.ttf',
            fontStyle: 'normal' as FontStyle,
            fontWeight: '900' as FontWeight,
        },
        {
            src: 'fonts/KFOlCnqEu92Fr1MmYUtfChc4EsA.ttf',
            fontStyle: 'normal' as FontStyle,
            fontWeight: '900' as FontWeight,
        },
    ],
});

Code:

import os
import requests
from fontTools.ttLib import TTFont
from bs4 import BeautifulSoup
from tqdm import tqdm
import sys
import subprocess

def download_and_convert_fonts(url: str, unicode_range: str):
    # Set headers to mimic a request from a Chrome browser
    headers = {
        "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3"
    }

    # Parse the file for each font and style and weight and font family name
    response = requests.get(url, headers=headers)
    soup = BeautifulSoup(response.text, 'html.parser')
    font_faces = soup.string.split('@font-face')

    # Create a temp directory to store downloaded fonts
    temp_dir = "fonts"
    os.makedirs(temp_dir, exist_ok=True)

    # Create a list to store font registration details
    font_registers = []

    # Extract the font family name from the URL
    family_name = url.split('family=')[1].split('&')[0].replace('+', ' ')

    # Progress bar setup
    total_fonts = len([font_face for font_face in font_faces if 'src:' in font_face and unicode_range in font_face])
    print(f"Total number of fonts to be processed: {total_fonts}")
    pbar = tqdm(total=total_fonts, desc=f"Processing fonts for family: {family_name}")

    for font_face in font_faces:
        if 'src:' in font_face and unicode_range in font_face:
            font_family = font_face.split("font-family: '")[1].split("';")[0]
            font_style = font_face.split("font-style: ")[1].split(";")[0]
            font_weight = font_face.split("font-weight: ")[1].split(";")[0]
            font_url = font_face.split("src: url(")[1].split(") format('")[0]

            # Get the font from the URL into temp directory
            font_response = requests.get(font_url)
            font_filename = os.path.join(temp_dir, font_url.split('/')[-1])
            with open(font_filename, 'wb') as font_file:
                font_file.write(font_response.content)

            # Convert WOFF2 to TTF if necessary using pyftsubset
            if font_filename.endswith('.woff2'):
                ttf_filename = font_filename.replace('.woff2', '.ttf')
                subprocess.run(['pyftsubset', font_filename, '--output-file=' + ttf_filename], check=True)
            else:
                ttf_filename = font_filename

            # Use font tools to convert to a ttf and save it in current directory
            font = TTFont(ttf_filename)
            font.save(ttf_filename)

            # Add to font registration list
            font_registers.append({
                "family": font_family,
                "src": ttf_filename,
                "fontStyle": font_style,
                "fontWeight": font_weight
            })

            # Update progress bar
            pbar.update(1)

    pbar.close()

    # Create output.ts with Font.register
    with open('output.ts', 'w') as output_file:
        output_file.write("import { Font } from '@react-pdf/renderer';\n\n")
        output_file.write("type FontWeight = 'normal' | 'bold' | 'light' | undefined;\n")
        output_file.write("type FontStyle = 'normal' | 'italic' | 'oblique' | undefined;\n\n")
        output_file.write("Font.register({\n")
        output_file.write(f"    family: '{family_name}',\n")
        output_file.write("    fonts: [\n")
        for font in font_registers:
            output_file.write(f"        {{\n")
            output_file.write(f"            src: '{font['src']}',\n")
            output_file.write(f"            fontStyle: '{font['fontStyle']}' as FontStyle,\n")
            output_file.write(f"            fontWeight: '{font['fontWeight']}' as FontWeight,\n")
            output_file.write(f"        }},\n")
        output_file.write("    ],\n")
        output_file.write("});\n")

if __name__ == "__main__":
    if len(sys.argv) != 3:
        print("Usage: python get-fonts.py <URL> <unicode-range>")
        sys.exit(1)
    
    url = sys.argv[1]
    unicode_range = sys.argv[2]
    download_and_convert_fonts(url, unicode_range)

@karlhorky
Copy link
Contributor

Has this changed since you wrote this comment? Because .woff fonts seem to work fine, whereas .woff2 seem to be problematic.

I can confirm that WOFF fonts work, but WOFF2 fonts do not.

I've opened a PR to document WOFF support here:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests