Johnny
Johnny

Reputation: 133

Replace Google Fonts with self hosted fonts

I'm currently porting parts of a legacy codebase which has more than 100 themes that each come with their own css files. Those files are full with hardcoded links to Google Fonts which need to be replaced due to GDPR.

Is there some kind of automated tool available which scans through these files, replaces the link to Google Fonts and downloads all the assets? I've found a couple of semi-automated tools online but they all require copy & paste and manual download of the files. That's okay for 2-3 fonts but not for hundreds of them. Any tips for that?

Upvotes: 1

Views: 881

Answers (3)

bluelights
bluelights

Reputation: 1296

I have put some efforts to create this NodeJS script.

This script searches for all css files and extracts the woff font url. Then, it replaces it with absolute path of the downloaded file against the url it found, also downloads the file in the relevant directory which can be clearly identified in the snippet as specified with fontDownloadDirectoryPath variable.

This script can be modified and improved further but as of now, provides the required functionality at its base level.

I hope this can serve as a starting point atleast to solve the stated problem or can be used completely as a solution changes few variables, given that my assumptions of few required things to get to this solution are correct.

Please feel free to modify, accordingly like the regex pattern to match something else, adding few other font types in the pattern, adding few more code to make it more robust and generalised, etc for other possibilities.

const path = require('path');
const fs = require('fs');
const https = require("https");

// update assets/css with your css path
const directoryPath = path.join(__dirname, 'assets/css');

let fontDownloadDirectoryPath = path.join(__dirname, 'assets/fonts')
let fontDownloadDirectoryFileFullPath = path.join(__dirname, 'assets/fonts/fontsDownloadUrlList.json')

fs.readdir(directoryPath, function (err, files) {
    //handling error
    if (err) {
        return console.log('Unable to scan directory: ' + err);
    }
    //listing all files using forEach
    files.forEach(function (file) {
        // Do whatever you want to do with the file
        let file_full_path = directoryPath + "/" + file
        fs.readFile(file_full_path, 'utf8', (err, content) => {
            if (err) {
                console.error(err);
                return;
            }
            // console.log(content);// show the content of readed file
            let found = content.match(/url\(['"]([^"']+(woff2|eot|woff|ttf)["'])+\)/gi)
            console.log(file_full_path, found);
            let updated_content = content
            if (found) {
                if (fs.existsSync(fontDownloadDirectoryFileFullPath)) {
                    // enter the code to execute after the folder is there.
                    console.log('file exists')
                    fs.readFile(fontDownloadDirectoryFileFullPath, 'utf8', (err, read_content) => {
                        let read_content_json = JSON.parse(read_content)
                        read_content_json.push(...found)
                        fs.writeFile(fontDownloadDirectoryFileFullPath, JSON.stringify(read_content_json), function () { })
                    })
                } else {
                    fs.writeFile(fontDownloadDirectoryFileFullPath, JSON.stringify(found), function () { })
                }
                console.log(found)
                found.forEach((item) => {
                    let fontFileUrl = item.split("'")[1]
                    let fontFileName = fontFileUrl.split("/")[fontFileUrl.split("/").length - 1]
                    console.log(fontFileUrl, fontFileName)
                    https.get(fontFileUrl, response => {
                        var body = '';
                        var i = 0;
                        response.on('data', function (chunk) {
                            i++;
                            body += chunk;
                            console.log('BODY Part: ' + i);
                        });
                        response.on('end', function () {

                            console.log(body);
                            fs.writeFileSync(fontDownloadDirectoryPath + "/" + fontFileName, body, { encoding: 'utf8', flag: 'w' }, (err) => { console.log(err) })
                            console.log('Finished');
                        });
                    });
                    updated_content = updated_content.replace(item, "url('" + fontDownloadDirectoryPath + "/" + fontFileName + "')")
                })
            } else {
                updated_content = content;
            }

            fs.writeFileSync(file_full_path, updated_content, { encoding: 'utf8', flag: 'w' })
        });

    });
});

I used below css file in root/assets/css directory with styles.css name for testing the above script:

@font-face {
font-family: 'BR Firma';
src: url('https://fonts.gstatic.com/s/opensans/v29/memSYaGs126MiZpBA-UvWbX2vVnXBbObj2OVZyOOSr4dVJWUgsiH0B4taVQUwaEQbjB_mQ.woff') format('woff');
font-weight: bold;
font-style: normal;
font-display: swap;

}

Upvotes: 1

Martin Zeitler
Martin Zeitler

Reputation: 76569

Any IDE can do, just "search and replace in files", with the appropriate patterns. For example: PHPStorm: Find and replace text using regular expressions. Finding all the occurrences alone is already worth something and an IDE might help with "porting parts of a legacy codebase".

Upvotes: 0

César P.
César P.

Reputation: 7

without having more background on the project, directory structure, and so on, I will outline how the task could be done as of now:

  1. Scan all the directories or URLs of the project (if you run it locally or remotely) if the fonts are not being imported from one main CSS file (could happen).
  2. Get all the Google Fonts URLs
  3. Download all the assets (i.e. fonts from the links, maybe some pics also, etc.)

So, although you can totally do this locally with directories, here I will explain a way to do it with the browser for brevity - and possibly convenience - with Python. I am assuming you have access to the project's URLs, ofc.

  1. You can follow this approach to scrape the URLs you want. Pass it a list from the sitemap to go through all the URLs in sequence. Then you can filter the list you get to account only for Google Fonts, simply add the in operator (as in here) to get true or false, respectively.

     substring = 'https://fonts.googleapis.com'
     if substring in element:
         list.append(element)
     else:
         #do nothing, don't add it to list
    
  2. Now you should have all the URLs you are interested in. If the project has several HTML pages with different fonts, those are the ones you need to scan - or maybe just all of them to be sure. Note: it is usually useful to store the list in a file to further add this code to the previous script. More info.

    with open('urls.txt', 'w') as f:
        f.write(element)
  1. To download the assets, you can use this approach. Since you have all the URLs, you should be able to.

And that's pretty much it! If you add more insight into the project structure we could complete the scripts more precisely. Also, you can quickly use Jupyter Notebook to run the scripts as you tune them. In the meantime, the open details to clarify would be:

  • Scan websites or files?
  • Only HTML files or all the projects?
  • What to download? The font assets only?
  • Python script works fine for this task?

Upvotes: 0

Related Questions