degooglify.sh 9.46 KB
Newer Older
Michał 'rysiek' Woźniak's avatar
Michał 'rysiek' Woźniak committed
1 2 3 4 5
#!/bin/bash

#
# Font DeGooglifier
#
6
# $1, ... - CSS files or URLs to de-googlify
Michał 'rysiek' Woźniak's avatar
Michał 'rysiek' Woźniak committed
7 8
#

9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46

#
# helper function -- downloading a file using curl or wget
# depending on which one is installed
# 
# using wget if both installed, decided by a coin toss
# 
# if the destination file exists, a notice is displayed
# and the file is *NOT* overwritten
#
# $1 -- URL
# $2 -- destination file
function curlwget {
    
    # does the destination file exist?
    # we do *not* want to overwrite files!
    if [ -f "$FONT_FILE" ]; then
        echo "NOTICE: file exists, not downloading"
        return 0
    fi

    # wget?
    if command -v wget >/dev/null; then
        wget --quiet -O "$2" "$1"
      
    # curl?
    elif command -v curl >/dev/null; then
        curl -s -S "$1" > "$2"
        
    # nope?
    else
        echo
        echo "ERROR: neither curl nor wget found!"
        exit 1
    fi
}


47 48 49 50 51 52
#
# helper function -- getting a CSS file name
# from a fonts.googleapis.com URL
#
# $1 -- URL to handle
function get_local_filename_from_url() {
53
    echo "$1.css" | sed -E -e 's%https://fonts.googleapis.com/css\?family=%%' -e 's/&(amp;)?/__/' | tr '|:+,=' '_-'
54
}
Michał 'rysiek' Woźniak's avatar
Michał 'rysiek' Woźniak committed
55

56

Michał 'rysiek' Woźniak's avatar
Michał 'rysiek' Woźniak committed
57
#
58 59 60
# in a local file replace '@import url()' lines
# that lead to fonts.googleapis.com
# with locally downloaded CSS files
Michał 'rysiek' Woźniak's avatar
Michał 'rysiek' Woźniak committed
61
# 
62 63
# $1 -- font directory to dump the fonts to
# $2 -- the CSS file to degooglify
64
function degooglify_css_file_import() {
Michał 'rysiek' Woźniak's avatar
Michał 'rysiek' Woźniak committed
65 66

    # source CSS file
67
    local CSS_IMPORT_SRC="$2"
Michał 'rysiek' Woźniak's avatar
Michał 'rysiek' Woźniak committed
68 69
    
    # destination CSS file
70 71 72 73 74 75 76 77 78 79
    local CSS_IMPORT_DEST="${CSS_IMPORT_SRC/.css/.degooglified.css}"
    
    # destination directory
    # assuming it exists and is writeable
    local FONT_DIR="$1"
    
    # inform
    echo "+-- de-googlifying import statements: $CSS_IMPORT_SRC"
    echo "    +-- destination font directory : $FONT_DIR/"
    echo "    +-- destination CSS file       : $CSS_IMPORT_DEST"
Michał 'rysiek' Woźniak's avatar
Michał 'rysiek' Woźniak committed
80 81
    
    # make a copy to later work on
82 83 84 85 86 87 88 89 90
    # (unless it exists already)
    if [ ! -f "$CSS_IMPORT_DEST" ]; then
        if ! cp "$CSS_IMPORT_SRC" "$CSS_IMPORT_DEST"; then
            echo "ERROR: unable to create the destination CSS file: '$CSS_IMPORT_DEST'"
            exit 1
        fi
    else
        echo
        echo "NOTICE: destination CSS file already exists: '$CSS_IMPORT_DEST'"
91 92
        echo "NOTICE: * this will not work great if the source or destination *"
        echo "NOTICE: * files were changed by external programs               *"
93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
        echo
    fi
    [ ! -r "$CSS_IMPORT_DEST" ] && echo "ERROR: destination CSS file is not readable: '$CSS_IMPORT_DEST'" && exit 1
    [ ! -w "$CSS_IMPORT_DEST" ] && echo "ERROR: destination CSS file is not writable: '$CSS_IMPORT_DEST'" && exit 1
    
    
    # we need to split fields on newlines
    OLDIFS="$IFS"
    IFS=$'\n'
   
    # first, get the @import statements
    # that fetch remote fonts.googleapis.com CSS files
    for CSS_IMPORT_LINE in $( egrep "^[[:space:]]*@import url\('?https://fonts.googleapis.com/css" "$CSS_IMPORT_SRC" ); do
        
        # inform
        echo "    +-- working with: $CSS_IMPORT_LINE"
        
        # get the URL
        # this sed expression will get the *last* occurence of the 'url()' stanza
112
        local CSS_IMPORT_URL="$( echo "$CSS_IMPORT_LINE" | sed -E -e "s/.*url\('?([^')]+)'?\).*/\1/g" )"
113 114 115 116 117 118 119 120 121 122
        echo "        +-- CSS remote URL: $CSS_IMPORT_URL"
        
        # get the local filename
        local CSS_IMPORT_LOCAL="$( get_local_filename_from_url "$CSS_IMPORT_URL" )"
        echo "        +-- local filename: $CSS_IMPORT_LOCAL"
        
        # fetch and degooglify that file, why not
        degooglify_css_url "$FONT_DIR" "$CSS_IMPORT_URL"
        
        # replace the URL with the local file name
Michał 'rysiek' Woźniak's avatar
bugfix  
Michał 'rysiek' Woźniak committed
123 124 125
        # adding '.degooglified' before '.css'
        # since we want to use the degooglified version of that file 
        sed -i -e "s%$CSS_IMPORT_URL%${CSS_IMPORT_LOCAL/.css/.degooglified.css}%" "$CSS_IMPORT_DEST"
126 127 128 129 130 131
    done
    
    # revert to the original IFS
    IFS="$OLDIFS"
}

132

133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
#
# the actual workhorse
# 
# handling the 'src:' stanzas
# downloading the fonts and replacing 'url()' sources with locally downloaded files
# 
# $1 -- font directory to dump the fonts to
# $2 -- the CSS file to degooglify
function degooglify_css_file_src() {

    # source CSS file
    local CSS_SRC="$2"
    
    # destination CSS file
    local CSS_DEST="${CSS_SRC/.css/.degooglified.css}"
Michał 'rysiek' Woźniak's avatar
Michał 'rysiek' Woźniak committed
148 149 150
    
    # destination directory
    # assuming it exists and is writeable
151
    local FONT_DIR="$1"
Michał 'rysiek' Woźniak's avatar
Michał 'rysiek' Woźniak committed
152 153
    
    # inform
154
    echo "+-- de-googlifying src lines: $CSS_SRC"
Michał 'rysiek' Woźniak's avatar
Michał 'rysiek' Woźniak committed
155 156 157
    echo "    +-- destination font directory : $FONT_DIR/"
    echo "    +-- destination CSS file       : $CSS_DEST"
    
158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
    # make a copy to later work on
    # (unless it exists already)
    if [ ! -f "$CSS_DEST" ]; then
        if ! cp "$CSS_SRC" "$CSS_DEST"; then
            echo "ERROR: unable to create the destination CSS file: '$CSS_DEST'"
            exit 1
        fi
    else
        echo
        echo "NOTICE: destination CSS file already exists: '$CSS_DEST'"
        echo "NOTICE: (this will not work great if the source or destination)"
        echo "NOTICE: (files were changed by external programs              )"
        echo
    fi
    [ ! -r "$CSS_DEST" ] && echo "ERROR: destination CSS file is not readable: '$CSS_DEST'" && exit 1
    [ ! -w "$CSS_DEST" ] && echo "ERROR: destination CSS file is not writable: '$CSS_DEST'" && exit 1
    
    
Michał 'rysiek' Woźniak's avatar
Michał 'rysiek' Woźniak committed
176 177 178 179 180 181 182 183
    # we need to split fields on newlines
    OLDIFS="$IFS"
    IFS=$'\n'
   
    # first, get the URLs of fonts
    #
    # assumptions:
    # - there is only one instance of each FONT_URL in the whole file
184
    for FONT_SRC_LINE in $( egrep '^[[:space:]]*src' "$CSS_SRC" ); do
Michał 'rysiek' Woźniak's avatar
Michał 'rysiek' Woźniak committed
185 186 187 188 189 190
    
        # inform
        echo "    +-- working with: $FONT_SRC_LINE"
        
        # get the URL
        # this sed expression will get the *last* occurence of the 'url()' stanza
191
        local FONT_URL="$( echo "$FONT_SRC_LINE" | sed -E -e "s/.*url\('?([^')]+)'?\).*/\1/g" )"
Michał 'rysiek' Woźniak's avatar
Michał 'rysiek' Woźniak committed
192 193
        echo "        +-- URL: $FONT_URL"
        
194 195 196 197 198 199
        # check if the URL is a fonts.gstatic.com one
        if [[ $FONT_URL != "https://fonts.gstatic.com"* ]]; then
            echo "        +-- not a Google font URL, ignoring..."
            continue
        fi
        
Michał 'rysiek' Woźniak's avatar
Michał 'rysiek' Woźniak committed
200
        # we also need the extension
201
        local FONT_EXT="${FONT_URL##*.}"
Michał 'rysiek' Woźniak's avatar
Michał 'rysiek' Woźniak committed
202 203 204 205
        
        # get the local name
        # this sed expression will get the *last* occurence of the 'local()' stanza
        # tr removes spaces just in case
206
        local FONT_NAME="$( echo "$FONT_SRC_LINE" | sed -E -e "s/.*local\('?([^')]+)'?\).*/\1/g" | tr -d ' ' )"
Michał 'rysiek' Woźniak's avatar
Michał 'rysiek' Woźniak committed
207 208
        
        # download the font
209
        local FONT_FILE="$FONT_DIR/$FONT_NAME.$FONT_EXT"
Michał 'rysiek' Woźniak's avatar
Michał 'rysiek' Woźniak committed
210
        echo "        +-- target: $FONT_FILE"
211
        curlwget "$FONT_URL" "$FONT_FILE"
Michał 'rysiek' Woźniak's avatar
Michał 'rysiek' Woźniak committed
212 213
        
        # replace the url in the source line in the file
214
        sed -i -e "s%$FONT_URL%$FONT_FILE%" "$CSS_DEST"
Michał 'rysiek' Woźniak's avatar
Michał 'rysiek' Woźniak committed
215 216 217 218 219 220 221
        
    done
    
    # revert to the original IFS
    IFS="$OLDIFS"
}

222

223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238
#
# handle a local file
#
# $1 -- font directory to dump the fonts to
# $2 -- the CSS file to degooglify
function degooglify_css_file () {
  
    # replace '@import url()' lines leading to fonts.googleapis.com
    # with locally downloaded CSS files
    degooglify_css_file_import "$1" "$2"
    
    # replace 'src:' lines leading to rempote resources
    # with locally downloaded fonts
    degooglify_css_file_src "$1" "$2"
}

239 240 241 242 243
#
# handle a CSS file available from fonts.googleapis.com
# 
# $1 -- font directory to dump the fonts to
# $2 -- URL
244
function degooglify_css_url() {
245 246 247
    
    # destination directory
    # assuming it exists and is writeable
248
    local FONT_DIR="$1"
249 250
    
    # remote CSS file
251
    local CSS_REMOTE="$2"
252 253 254 255 256 257 258 259
    
    # reality check -- is this a fonts.googleapis.com/css link?
    if [[ $CSS_REMOTE != "https://fonts.googleapis.com/css"* ]]; then
        echo "ERROR: supplied URL is not a link to fonts.googleapis.com: '$CSS_REMOTE'"
    fi
    
    # we're good, I guess
    # get the filename from the URL
260
    local CSS_LOCAL=$( get_local_filename_from_url "$CSS_REMOTE" )
261 262 263 264 265 266 267
    
    # inform
    echo "+-- downloading remote CSS:"
    echo "    +-- source URL       : $CSS_REMOTE"
    echo "    +-- destination file : $CSS_LOCAL"
    
    # download
268
    curlwget "$CSS_REMOTE" "$CSS_LOCAL"
269 270
    
    # handle the downlaoded file
271
    degooglify_css_file "$FONT_DIR" "$CSS_LOCAL" || exit 1
272 273 274 275
}



Michał 'rysiek' Woźniak's avatar
Michał 'rysiek' Woźniak committed
276 277 278 279 280 281 282 283
# target directory
# TODO: make that configurable on the command line
TARGET_FONT_DIR="fonts/"
# remove the ending slash in case it's there
TARGET_FONT_DIR=${TARGET_FONT_DIR%/}

# reality checks
# making sure the destination directory exists and is readable and writeable
284 285 286 287 288 289 290 291
if [ ! -d "$TARGET_FONT_DIR" ]; then
    if ! mkdir -p "$TARGET_FONT_DIR"; then
        echo "ERROR: unable to create the destination font directory: '$TARGET_FONT_DIR'"
        exit 1
    fi
fi
[ ! -r "$TARGET_FONT_DIR" ] && echo "ERROR: destination font directory not readable: '$TARGET_FONT_DIR'" && exit 1
[ ! -w "$TARGET_FONT_DIR" ] && echo "ERROR: destination font directory not writeable: '$TARGET_FONT_DIR'" && exit 1
Michał 'rysiek' Woźniak's avatar
Michał 'rysiek' Woźniak committed
292

293

Michał 'rysiek' Woźniak's avatar
Michał 'rysiek' Woźniak committed
294
# do the magic
295 296 297
for SOURCE_OF_CSS in "$@"; do
    
    # are we dealing with a remote resource?
298 299 300 301 302
    # 
    # we're lowercasing here only since it's needed for the comparison
    # however, URLs are case-sensitive,
    # so we can't just go with the lowercase version everywhere
    if [[ $( echo "${SOURCE_OF_CSS:0:8}" | tr '[:upper:]' '[:lower:]' ) == "https://" ]]; then
303
        # yes we are! deal with it, then
304
        degooglify_css_url "$TARGET_FONT_DIR" "$SOURCE_OF_CSS"
305 306
    else
        # nope, all local
307
        degooglify_css_file "$TARGET_FONT_DIR" "$SOURCE_OF_CSS"
308
    fi
Michał 'rysiek' Woźniak's avatar
Michał 'rysiek' Woźniak committed
309
done