Converting my OTF font into multiple web fonts with this bash script
Tags: fonts, webdev, bash, ubuntu
Published: 2021-04-21 10:23:08

I came across this use-case where we had to use a specific custom font but it was only available in `.otf`. However, we want to support multiple formats to ensure even deprecated browsers can load the font. Otf has a global coverage of [97.89%](https://caniuse.com/?search=otf) but we didn't want to take any chances of the font not loading as it's a crucial feature in our app.

I wanted to convert the font to support the following browsers:

- woff2 - Latest browsers :rocket:
- woff - Modern browsers :+1:
- ttf - Apple and mobile OS :apple::iphone:
- ~~svg~~ - older safari and ios support :cry:
- eot - older IE support :cry:

I am skipping svg as I found no converter package, fulfilling the same requirements as the others (available from Ubuntu repositories without additional sources).

## The goal

Consider my font being `custom.otf` then the [font face](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face) should look like the following:

```css
@font-face {
  font-family: 'Custom';
  src: url('custom.woff2') format('woff2'),
       url('custom.woff') format('woff'), 
       url('custom.ttf')  format('truetype'),
       url('custom.otf')  format('truetype'),
       url('custom.eot') format('embedded-opentype');
}
```

The loading order is from latest to oldest so the majority should get the woff2/woff files.


## Why not using an online converter?

Now I know there are many converters out there but I have some issues with them

- I can't upload the font, since the license forbids it
- I don't know what I download with the font into my system
- I don't have the time to check each of them

## Let's see what Ubuntu provides out of the box

I immediatley thought I should check my Ubuntu repositories and do some research on tools to convert into all the required types:

| package | command | description |
| ------- | ------- | ----------- 
| `eot-utils` | `mkeot` | convert otf to eot |
| `eot2ttf` | `eot2ttf` | convert eot to ttf |
| `woff-tools` | `sfnt2woff` | convert otf to woff |
| `woff2` | `woff2_compress` | convert ttf to woff2 |
| - | - | convert any of them to svg |

## Let's create a simple bash script

The script should take in a single otf file and convert it to separate oet, ttf, woff and woff2 files on a given destination folder.

It should furthermore check, if the inputs are correct and the packages are installed.

The next parts are showing the script from top to bottom, split by some comments. If you combine all the parts into one file you will get the final script.

First of all, we create a new file

```
$ touch convertOtf.sh
```

Then we open it with our editor of choice, add the shebang and set the script to exit on the first error:

```bash
#!/usr/bin/env bash

set -e
```

Now check the parameters and create local variables from them:

```bash
SOURCE=$1
DESTINATION=$2
FILENAME=$(basename "${SOURCE%.otf}")

# we omit mime check since we use this script only locally and assume it's otf
if [[ ${SOURCE: -4} != ".otf" ]]; then
  echo "usage: convertOtf input.otf /path/to/output/"
  exit 1
fi

# check the destination is a folder
if [[ ! -d "$DESTINATION" ]]; then
  echo "usage: convertOtf input.otf /path/to/output/"
  exit 1
fi
```

We could directly add installing the commands into the script but it should be kept simple so we just warn of missing packages and exit:

```bash
ALLCOMMANDS=(
  mkeot
  eot2ttf
  sfnt2woff
  woff2_compress
)

# iterate the ALLCOMMANDS list to check for each required command
# skip as soon as a command is not found
for CMD in "${ALLCOMMANDS[@]}"
do
   :
   if [[ ! -f $(command -v "$CMD") ]];then
        echo "Cannot convert ($CMD). The following packages are required: eot-utils eot2ttf woff-tools woff2"
        exit 1
    fi
done
```

Now that we've covered the requirements to convert our file we can now run the tools. Note, that they have to be in the right order, because some depend on the ouput of one another:

```bash
EOT_PATH="$DESTINATION/$FILENAME.eot"
echo "[OTF ---> EOT]: (over-)write to $EOT_PATH"
mkeot ${SOURCE} > ${EOT_PATH}

TTF_PATH="$DESTINATION/$FILENAME.ttf"
echo "[EOT ---> TTF]: (over-)write to $TTF_PATH"
eot2ttf ${EOT_PATH} ${TTF_PATH}

WOFF_PATH="$DESTINATION/$FILENAME.woff"
echo "[OTF --> WOFF]: (over-)write to $WOFF_PATH"
sfnt2woff ${SOURCE} > ${WOFF_PATH}

WOFF2_PATH="$DESTINATION/$FILENAME.woff2"
echo "[TTF -> WOFF2]: (over-)write to $WOFF2_PATH"
woff2_compress ${TTF_PATH} > ${WOFF2_PATH}

exit 0
``` 

## Running the script

First, make the script executable, then run it with our otf font as first input and a destination folder as second input:

```bash
$ chmod +x ./convertOtf.sh
$ ./convertOtf.sh custom.otf ./out
[OTF ---> EOT]: (over-)write to ./out/custom.eot
[EOT ---> TTF]: (over-)write to ./out/custom.ttf
[OTF --> WOFF]: (over-)write to ./out/custom.woff
[TTF -> WOFF2]: (over-)write to ./out/custom.woff2
Compressed 25698 to 16389.
```

## Summary

The script is a simple way to convert an otf to multiple webfont formats using only tools that Ubuntu brings with it's default repositories.

I am no bash guru, so please comment if you think there are parts in the code (or in the article in general) that can be improved :bow:

**Resources used for this article**

https://caniuse.com/
https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face
https://css-tricks.com/snippets/css/using-font-face/
https://github.com/ikatyang/emoji-cheat-sheet
https://stackoverflow.com/
https://help.ubuntu.com/stable/ubuntu-help/addremove-install-synaptic.html.en