README.md 13.2 KB
Newer Older
Ronald van Engelen's avatar
Ronald van Engelen committed
1 2
# best-served-local

3
> aka "Yet Another Google Web Font Downloader written in bash"
Ronald van Engelen's avatar
Ronald van Engelen committed
4 5

## Rationale
Ronald van Engelen's avatar
Ronald van Engelen committed
6

7 8
With the advent of using web fonts on your website --which is a good
idea-- you may (involuntary) submit some of the online behaviour of
9
*your visitors* (called [behavioural targeting]) to Google without their
10 11 12 13 14 15 16 17 18 19 20 21
knowledge or consent. This of course is a very bad idea. This script
assist you in preserving the good while preventing the bad by
downloading Google web fonts to your own webserver and create the
accompying CSS code to serve that yourself, instead of letting your
users downloading them from Google which each visit to your pages.

The script was inspired by [google-font-download] by Clemens Lang and
the functionality of [google-webfonts-helper] (client side browser app
in javascript). The latter uses javascript while I needed bash (v4),
while mr. Lang's script missed some functionality I would like. This
script adds [automation] and creates high quality
[@font-face css3 rules] using the [bulletproof @font-face method] by
Ronald van Engelen's avatar
Ronald van Engelen committed
22 23
Paul Irish and the format presets by Chris Coyier from
css-tricks.com.
24

Ronald van Engelen's avatar
Ronald van Engelen committed
25 26
Apart from [bash] version 4, this script only depends on either [curl] 
or [wget]. The script hasn't been tested on OSX and Cygwin yet.
Ronald van Engelen's avatar
Ronald van Engelen committed
27

Ronald van Engelen's avatar
Ronald van Engelen committed
28

29
## Simple usage example
Ronald van Engelen's avatar
Ronald van Engelen committed
30

Ronald van Engelen's avatar
Ronald van Engelen committed
31 32 33
Each of the following commands will lead to the same result:

* just specifying the font-family and optionally the font-weight(s):
Ronald van Engelen's avatar
Ronald van Engelen committed
34
```bash
Ronald van Engelen's avatar
Ronald van Engelen committed
35
./best-served-local "Open Sans" "Roboto:bold,thin" > /tmp/fonts.css
Ronald van Engelen's avatar
Ronald van Engelen committed
36
```
37 38 39
* or, use the html `link` element from your current templates and
  pages to extract the `FONTSPEC`, together with the `-o|--outputfile`
  argument to safely write the css to the file specified:
40
```bash
Ronald van Engelen's avatar
Ronald van Engelen committed
41
bash best-served-local -o /tmp/fonts.css \
Ronald van Engelen's avatar
Ronald van Engelen committed
42
  "<link href='https://fonts.googleapis.com/css?family=Open+Sans|Roboto:700,100' rel='stylesheet' type='text/css'>"
43
```
44 45
* or, run the script directly from the web, while using a css
  `@import` rule to extract the `FONTSPEC` from:
46
```bash
Ronald van Engelen's avatar
Ronald van Engelen committed
47 48
bash <(wget -q -O- "https://lacocina.nl/best-served-local") \
   --outputfile /tmp/fonts.css \
Ronald van Engelen's avatar
Ronald van Engelen committed
49
  "@import url(https://fonts.googleapis.com/css?family=Open+Sans|Roboto:700,100);"
50 51
```

Ronald van Engelen's avatar
Ronald van Engelen committed
52
All three commands will create the file `/tmp/fonts.css` with the
Ronald van Engelen's avatar
Ronald van Engelen committed
53
following `@font-face` css at-rules:
Ronald van Engelen's avatar
Ronald van Engelen committed
54

Ronald van Engelen's avatar
Ronald van Engelen committed
55 56 57
```css
@font-face {
   font-family: 'Open Sans';
58 59 60 61 62
   src:         local('Open Sans'), local('OpenSans'), 
                url('Open_Sans_v13_latin_400.woff2') format('woff2'),
                url('Open_Sans_v13_latin_400.woff') format('woff');
   font-style:  normal;
   font-weight: 400;
Ronald van Engelen's avatar
Ronald van Engelen committed
63 64
}
@font-face {
65 66 67 68 69 70
   font-family: 'Roboto';
   src:         local('Roboto Bold'), local('Roboto-Bold'), 
                url('Roboto_Bold_v15_latin_700.woff2') format('woff2'),
                url('Roboto_Bold_v15_latin_700.woff') format('woff');
   font-style:  normal;
   font-weight: 700;
Ronald van Engelen's avatar
Ronald van Engelen committed
71 72
}
@font-face {
73 74 75 76 77 78
   font-family: 'Roboto';
   src:         local('Roboto Thin'), local('Roboto-Thin'), 
                url('Roboto_Thin_v15_latin_100.woff2') format('woff2'),
                url('Roboto_Thin_v15_latin_100.woff') format('woff');
   font-style:  normal;
   font-weight: 100;
Ronald van Engelen's avatar
Ronald van Engelen committed
79 80
}
```
Ronald van Engelen's avatar
Ronald van Engelen committed
81

82 83 84
... and in a (new) temporary directory displayed on the screen, the
following files are downloaded, ready to be served to your visitors by
your webserver:
Ronald van Engelen's avatar
Ronald van Engelen committed
85 86 87 88 89

```
/tmp/best-served-local.XXXX
├── Open_Sans_v13_latin_400.woff
├── Open_Sans_v13_latin_400.woff2
90 91 92 93
├── Roboto_Bold_v15_latin_700.woff
├── Roboto_Bold_v15_latin_700.woff2
├── Roboto_Thin_v15_latin_100.woff
└── Roboto_Thin_v15_latin_100.woff2
Ronald van Engelen's avatar
Ronald van Engelen committed
94 95 96
```


97
## Getting and running the script
Ronald van Engelen's avatar
Ronald van Engelen committed
98

99
The script can be cloned or forked from its [gitlab repository],
100 101
[downloaded], or started straight from the web (although some would
advise against that):
Ronald van Engelen's avatar
Ronald van Engelen committed
102 103

```bash
Ronald van Engelen's avatar
Ronald van Engelen committed
104
## running it straight from the web
Ronald van Engelen's avatar
Ronald van Engelen committed
105
bash <(wget -q -O - "https://lacocina.nl/best-served-local") "Roboto:100,900"
Ronald van Engelen's avatar
Ronald van Engelen committed
106 107
```

Ronald van Engelen's avatar
Ronald van Engelen committed
108 109
Or, when you prefer `curl`:
```bash
Ronald van Engelen's avatar
Ronald van Engelen committed
110
bash <(curl -sL "https://lacocina.nl/best-served-local") -f all Slabo\ 27px
Ronald van Engelen's avatar
Ronald van Engelen committed
111 112
```

Ronald van Engelen's avatar
Ronald van Engelen committed
113 114 115 116 117 118 119 120 121 122
Or, download first, run next:
```bash
wget "https://lacocina.nl/best-served-local"
bash best-served-local "Roboto:100,900"
```

Or, download to path, make executable, then run:
```bash
wget -O /usr/local/bin/best-served-local "https://lacocina.nl/best-served-local"
chmod +x /usr/local/bin/best-served-local
Ronald van Engelen's avatar
Ronald van Engelen committed
123
best-served-local -o /tmp/fonts.css "Roboto:100,900" "Slabo 27px"
Ronald van Engelen's avatar
Ronald van Engelen committed
124 125
```

126 127
To display the basic [commandline arguments], run it with `--help` (or `-h`)
argument or, to display all arguments, use  `--advanced-help` (or `-hh`). The latter will display something like:
128

Ronald van Engelen's avatar
Ronald van Engelen committed
129
```bash
130 131 132 133 134 135 136 137 138 139 140 141 142
Usage:
   best-served-local FONTSPEC
   -or-
   best-served-local \
        [-o|--outputfile PATH] [-d|--fontdirectory PATH] \
        [-i|--incss-fontpath PATH] \
     	[--overwrite | [--overwrite-css] [--overwrite-fonts]] \
        [-f|--formats FORMATSPEC] [-s|--subsetspec SUBSETSPEC] \
        [-n|--skip-downloads] [-x|--skip-local] \
        [--include-chrome] [-v|--verbose] \
        [-h|--help] \
        FONTSPEC
...
Ronald van Engelen's avatar
Ronald van Engelen committed
143 144
```

145 146
The only required argument is [FONTSPEC]; all other arguments are
[optional].
147

Ronald van Engelen's avatar
Ronald van Engelen committed
148 149 150

## Features

151 152 153 154 155 156 157 158 159 160 161 162 163
* It uses the version of the font in the local file name and css
  `url()` references.
* It never overwrites existing css or font files (unless the
  `--overwrite-...` arguments are used);
  * When an existing css file is present, the script will notify the
    user and print the result to `stdout`.
* It stores downloaded web fonts in a temporary directory, which
  path is displayed when the script finishes (unless the
  `--fontdirectory` argument together with a writable path is
  specified).
* Warnings and messages are redirected to `stderr` so it should be
  save to redirect the output using a pipe or redirection,
  eg. `./best-served-local ... > myfile.css`.
Ronald van Engelen's avatar
Ronald van Engelen committed
164 165


166
## Fully automated usage example
Ronald van Engelen's avatar
Ronald van Engelen committed
167

168 169 170 171
Using (valid and tested) values for the commandline arguments
`--incss-fontpath`, `--outputfile`, `--fontdirectory` and using
`--overwrite-fonts` and `--overwrite-cssfile`, the script can be used
to get a fully automated powertool for your webserver:
Ronald van Engelen's avatar
Ronald van Engelen committed
172 173

```bash
Ronald van Engelen's avatar
Ronald van Engelen committed
174

Ronald van Engelen's avatar
Ronald van Engelen committed
175
./best-served-local --incss-fontpath /static/fonts \
Ronald van Engelen's avatar
Ronald van Engelen committed
176 177
--outputfile /var/www/example.org/static/css/fontdefs.css \
--fontdirectory /var/www/example.org/static/fonts/
178
--overwrite \
Ronald van Engelen's avatar
Ronald van Engelen committed
179 180 181
--formats superprogressive \
--subsets latin-ext \
"Open Sans:300,400,700" "Roboto:100,100italic,regular,italic,900"
Ronald van Engelen's avatar
Ronald van Engelen committed
182 183
```

184
## Some extra features
Ronald van Engelen's avatar
Ronald van Engelen committed
185 186

Setting a value for the `--incss-fontpath` (or `-i`) argument will
Ronald van Engelen's avatar
Ronald van Engelen committed
187
cause the resulting `url()` values in the `src` attribute to use that
Ronald van Engelen's avatar
Ronald van Engelen committed
188 189 190 191
as the path to the font file, eg using `-i ../static/fonts` will
result in the following css:

```css
Ronald van Engelen's avatar
Ronald van Engelen committed
192
src: url('../static/fonts/A_Web_Font_v1_latin.woff2')
Ronald van Engelen's avatar
Ronald van Engelen committed
193 194 195 196
```

While omitting that option would result in:
```css
Ronald van Engelen's avatar
Ronald van Engelen committed
197
src: url('A_Web_Font_v1_latin.woff2')
Ronald van Engelen's avatar
Ronald van Engelen committed
198 199
```

Ronald van Engelen's avatar
Ronald van Engelen committed
200 201 202 203 204
Using the `--skip-local` (or `-x`) argument will make the script skip
the `local` references in the `src` attributes, which comes in handy
if you want maximum control over the appearance of your website.

Using `--skip-downloads` (or `-n`) will not download the font files,
Ronald van Engelen's avatar
Ronald van Engelen committed
205
but just output the proper CSS. It will however contact Goggle-server
Ronald van Engelen's avatar
Ronald van Engelen committed
206 207
to verify the proper css attributes and such.

208
## Commandline arguments reference
209

210
### Required argument
Ronald van Engelen's avatar
Ronald van Engelen committed
211

212
**`FONTSPEC`**
213 214 215
: A `FONTSPEC` is either a space separated list of family names,
  surrounded with quotes or space-escaped, with an optional suffix,
  consisting of `:`, followed by a comma separated list of font
Ronald van Engelen's avatar
Ronald van Engelen committed
216 217
  weight/style values, or a full html `link` element or css `@import`
  rule, like the ones produced by https://www.google.com/fonts.
218 219 220
  
  For example, to use *"Open Sans"* in the regular font weight and
  style use:
Ronald van Engelen's avatar
Ronald van Engelen committed
221 222 223
```bash
./best-served-local "Open Sans"
```
224 225
  To get the italic variant with the same weight, next to the regular
  one, specify both, eg:
Ronald van Engelen's avatar
Ronald van Engelen committed
226 227
```bash
./best-served-local "Open Sans:regular:italic"
228 229
## or
./best-served-local "Open Sans:400:italic"
Ronald van Engelen's avatar
Ronald van Engelen committed
230
```
231
  Multiple `FONTSPEC`s can be set as follows:
Ronald van Engelen's avatar
Ronald van Engelen committed
232
```bash
233
./best-served-local "Open Sans:regular:italic" "Web Font A:extrabold,superlight"
Ronald van Engelen's avatar
Ronald van Engelen committed
234 235
```

236
  Or use an html `link` element:
237 238 239 240
```bash
./best-served-local "<link href='https://fonts.googleapis.com/css?family=Open+Sans:400,400italic' rel='stylesheet' type='text/css'>"
```

241
  Or use a css `@import` rule:
242 243 244 245
```bash
./best-served-local "@import url(https://fonts.googleapis.com/css?family=Open+Sans:400,400italic);"
```

246

247
### Optional arguments
Ronald van Engelen's avatar
Ronald van Engelen committed
248

Ronald van Engelen's avatar
Ronald van Engelen committed
249 250 251 252
**`-o PATH`** or **`--outputfile PATH`**
: The `PATH` of the file to save the generated css in. `PATH` can be
  relative to the working directory (eg. `fonts.css` or
  `../static/css/`) or absolute
Ronald van Engelen's avatar
Ronald van Engelen committed
253 254
  (eg. `/srv/www/example.org/static/css/fonts.css`). When not
  specified, the script prints the resulting css to `stdout`.
Ronald van Engelen's avatar
Ronald van Engelen committed
255 256 257 258 259

**`-d PATH`** or **`--fontdirectory PATH`**
: The `PATH` of the directory to save the downloaded web fonts
  in. `PATH` can be relative to the working directory
  (eg. `../static/fonts`) or absolute
Ronald van Engelen's avatar
Ronald van Engelen committed
260 261
  (eg. `/srv/www/example.org/static/css/fonts`). When not specified,
  the script will save the fonts in a temporary directory, which it
Ronald van Engelen's avatar
Ronald van Engelen committed
262
  will display when the script has finished.
Ronald van Engelen's avatar
Ronald van Engelen committed
263 264 265 266 267 268 269

**`-i PATH`** or **`--incss-fontpath PATH`**
: The `PATH` of the directory to which font files will be referenced
  in the generated css using its `url` value. For example setting
  `PATH` to `../downloadedfonts` will lead to the following css `src:
  url('../downloadedfonts/A_Web_Font_v1_latin.woff2')`.

270 271 272 273
**`-overwrite-css`**
: When used together with `--outputfile PATH`, using this argument
makes the script overwrite the file specified with `PATH` in case it
exists, which normally does not happen.
Ronald van Engelen's avatar
Ronald van Engelen committed
274

275 276 277 278 279 280 281 282 283 284
**`-overwrite-fonts`**
: When used together with `--fontdirectory PATH`, using this argument
  makes the script overwrite the font files in the directory specified
  with `PATH` in case they exists, which normally does not happen.

**`-overwrite`**
: Sets both `--overwrite-css` and `--overwrite-fonts`.

**`-f FORMATSPEC`** or **`--formats FORMATSPEC`**
: `FORMATSPEC` define the font file format(s) to use. It should be
Ronald van Engelen's avatar
Ronald van Engelen committed
285
  specified as one of the *presets*, or a comma-separated list of
Ronald van Engelen's avatar
Ronald van Engelen committed
286 287
  specific formats. The script recognize the presets from
  [Using @font-face] by Chris Coyier from css-tricks.com:
Ronald van Engelen's avatar
Ronald van Engelen committed
288

Ronald van Engelen's avatar
Ronald van Engelen committed
289
  * `superprogressive`: [woff2]
Ronald van Engelen's avatar
Ronald van Engelen committed
290
  * `practical`: `superprogressive` + [woff] \(= default)
Ronald van Engelen's avatar
Ronald van Engelen committed
291 292
  * `slightlydeeper`: `practical` + [ttf]
  * `all`: `slightlydeeper` + [eot] + [otf] + [svg]
293

294
  For example, to use the default *practical* set, consisting of the
295
  `woff` and `woff2` formats, for the "Open Sans" font, use:
Ronald van Engelen's avatar
Ronald van Engelen committed
296

297 298 299 300 301 302 303 304 305 306 307 308 309 310
```bash
./best-served-local "Open Sans" 
```

  To get full browser support (and thus heavy per page downloads), use:
```bash
./best-served-local -f all "Open Sans" 
```

  You want something funky? Just tell the script to do so:
```bash
./best-served-local -f "eot,svg" "Open Sans" 
```

Ronald van Engelen's avatar
Ronald van Engelen committed
311
**`-s SUBSETSPEC`** or **`--subsets SUBSETSPEC`**
312 313 314 315
: Comma-separated list of a `SUBSETSPEC`. Should be `all`, or one or
  more of the following separate predefined names: `cyrillic`,
  `cyrillic-ext`, `greek`, `greek-ext`, `latin` (default),
  `latin-ext`, or `vietnamese`.
Ronald van Engelen's avatar
Ronald van Engelen committed
316

317 318 319 320 321
**`-n`** or **`--no-downloads`**
: Setting this argument causes the script to **not download** the web
  font files, which it by default does do.
  > However, this does **not prevent the script to contact google's font
  > servers** for obtaining information on each requested font.
Ronald van Engelen's avatar
Ronald van Engelen committed
322

323 324 325 326 327 328
**`-x`** or **`--skip-local`**
: Prevents the inclusion of the `local()` references in the `src`
  attribute. Although the use of such references to fonts installed on
  the browsers' system saves downloading them at page load, they might
  differ from the versions served by your webserver. Skipping such
  references makes sure the browser gets what you want them to get.
Ronald van Engelen's avatar
Ronald van Engelen committed
329

330 331 332 333 334 335
**`--include-chrome`**
: Surrounds the generated css using a header and footer as a css
  comment indicating where the script generated css starts and
  ends. The comment header also contains the version of the script,
  the date/time the script was executed and the command line arguments
  used.
Ronald van Engelen's avatar
Ronald van Engelen committed
336

337 338
**`-v`** or **`--verbose`** 
: Increases verbosity printed to stdout while executing the script
Ronald van Engelen's avatar
Ronald van Engelen committed
339

340 341 342 343 344 345 346 347 348 349 350
---------------

[google-font-download]: 
  https://github.com/neverpanic/google-font-download.git/ "`google-font-download`"

[google-webfonts-helper]: 
  https://github.com/majodev/google-webfonts-helper/ "`google-webfonts-helper`"

[@font-face css3 rules]: 
  https://www.w3.org/TR/css-fonts-3/#font-face-rule

Ronald van Engelen's avatar
Ronald van Engelen committed
351
[woff2]: 
Ronald van Engelen's avatar
Ronald van Engelen committed
352
  https://www.w3.org/TR/WOFF2/
Ronald van Engelen's avatar
Ronald van Engelen committed
353 354

[woff]: 
Ronald van Engelen's avatar
Ronald van Engelen committed
355
  https://www.w3.org/TR/WOFF/
Ronald van Engelen's avatar
Ronald van Engelen committed
356

Ronald van Engelen's avatar
Ronald van Engelen committed
357 358 359 360 361 362 363 364 365 366 367 368
[ttf]:
  https://www.microsoft.com/typography/TrueTypeFonts.mspx

[eot]:
  https://en.wikipedia.org/wiki/Embedded_OpenType

[otf]:
  http://www.iso.org/iso/home/store/catalogue_ics/catalogue_detail_ics.htm?csnumber=66391

[svg]:
  https://www.w3.org/TR/SVG/fonts.html

369
[bulletproof @font-face method]: 
370 371
  http://www.paulirish.com/2009/bulletproof-font-face-implementation-syntax/ 
  "*bulletproof @font-face method*"
372 373 374 375 376 377
  
[bash]: 
  https://www.gnu.org/software/bash/
  
[curl]:
  https://curl.haxx.se/
Ronald van Engelen's avatar
Ronald van Engelen committed
378

379 380 381
[wget]:
  https://www.gnu.org/software/wget/
  
382 383
[gitlab repository]: 
  https://gitlab.com/ronalde/best-served-local
384 385

[downloaded]:
Ronald van Engelen's avatar
Ronald van Engelen committed
386
  https://lacocina.nl/best-served-local
387

Ronald van Engelen's avatar
Ronald van Engelen committed
388 389 390
[using @font-face]:
  https://css-tricks.com/snippets/css/using-font-face

391
[automation]: #fully-automated-usage-example
392

Ronald van Engelen's avatar
Ronald van Engelen committed
393
[FONTSPEC]: #required-argument
394

Ronald van Engelen's avatar
Ronald van Engelen committed
395
[commandline arguments]: #commandline-arguments-reference
396

Ronald van Engelen's avatar
Ronald van Engelen committed
397
[optional]: #optional-arguments
398

399
[behavioural targeting]:
400
	http://dare.uva.nl/document/2/154442