Skip to main content
edited tags
Link
200_success
  • 144.2k
  • 22
  • 188
  • 473
Source Link
typo
  • 171
  • 3

Bash script to encode images in base64 and generate a CSS file from them

These are the first lines of code I've ever written. I've been interested in the idea of learning to program for quite a while, but never really pulled the trigger, and now I've been playing around with some HTML & CSS and thought this might be a good starter project. So after a couple of days of furious googling, I managed to cobble this script together.

What I'm mostly curious about is the overall logic and structure of the code. The whole script grew organically from me looking up the Bash constructs and figuring out how to combine them to do what I want, so I don't know how sensible it is from the programming standpoint.

The idea is to keep the script in my project's folder and use relative paths, so I can just run it when I need to regenerate the CSS.

#!/bin/bash

# CSS ruleset template:
#
# .________::before { background-image: url(data:________;base64,__________); }
#  filename                                      mimetype        base64 str
#
# CSS class names and other identifiers can contain the characters
# A-Z, a-z, 0-9, hyphen, underscore and Unicode characters U+00A0 and higher.
# They cannot start with a digit, two hyphens or a hyphen followed by a digit.
# (https://www.w3.org/TR/CSS2/syndata.html#characters)
#
# url(), unquoted, requires escaping the characters ()'" and whitespace.
# url('') & url("") require escaping newlines and the quote used for quoting.
# (https://www.w3.org/TR/CSS2/syndata.html#uri)
#
# Base64 encoding uses characters A-Z, a-z, 0-9 and any two of these: +-.,:/_~
# Therefore, there's never need to escape anything, so no quotes are necessary.


css_path="icons.css" # relative to the script


cd "${BASH_SOURCE%/*}"

: > "$css_path"

for file in icons/*; do
    echo Processing "$file"

    filename="$(name_ext="${file##*/}"; echo "${name_ext%.*}")"
    ext="${file##*.}"

    shopt -s nocasematch
    case "$ext" in
        avif      ) mime="image/avif"   ;;
        bmp       ) mime="image/bmp"    ;;
        gif       ) mime="image/gif"    ;;
        jpg | jpeg) mime="image/jpeg"   ;;
        png       ) mime="image/png"    ;;
        svg       ) mime="image/svg+xml";;
        *         ) mime="unsupported"  ;;
    esac

    if [[ "$mime" = "unsupported" ]]; then
        unsupported+=("$file")
    elif [[ ! "$filename" =~ ^-?[A-Za-z_][A-Za-z0-9_-]*$ ]]; then
        invalid_class_name+=("$file")
    else
        base64str="$(base64 --wrap=0 "$file")"
        printf ".%s::before { background-image: url(data:%s;base64,%s); }\n" \
        "$filename" "$mime" "$base64str" \
        >> "$css_path"
    fi
done

if [[ -z "$unsupported" && -z "$invalid_class_name" ]]; then
    echo "All done!"
else
    if [[ -n "$unsupported" ]]; then
        printf "\n\n%s\n\n" "UNSUPPORTED FILES (SKIPPED)"
        printf "  %s\n"       "${unsupported[@]}"
    fi
    if [[ -n "$invalid_class_name" ]]; then
        printf "\n\n%s\n" "FILENAMES INVALID AS CSS CLASS NAMES (SKIPPED)"
        printf "  %s\n"   "Allowed characters: A-Z, a-z, 0-9, '-', '_'"
        printf "  %s\n\n" "Can't begin with: 0-9, '--', '-0' through '-9'"
        printf "  %s\n"   "${invalid_class_name[@]}"
    fi
fi
```