Dave Jarvis' Repositories

git clone https://repo.autonoma.ca/repo/whitemagicsoftware.com.git
#!/bin/bash

# Static website build script

# Directory where script resides
DIR="$(cd "$(dirname "${BASH_SRC_DIR[0]}")" >/dev/null 2>&1 && pwd)"

# Used to display usage
SCRIPT_NAME=$(basename $0)

# ANSI colour escape sequences
COLOUR_BLUE='\033[1;34m'
COLOUR_PINK='\033[1;35m'
COLOUR_DKGRAY='\033[30m'
COLOUR_DKRED='\033[31m'
COLOUR_YELLOW='\033[1;33m'
COLOUR_OFF='\033[0m'

# Colour definitions used by script
COLOUR_LOGGING=$COLOUR_BLUE
COLOUR_WARNING=$COLOUR_YELLOW

TPL_DIR="$DIR/template"
INC_DIR="$TPL_DIR/includes"

# Don't generate fonts by default
ARG_FONT=false

# Set to anything to show help and exit
unset ARG_HELP

unset ARG_DEBUG
unset ARG_FONT

# -----------------------------------------------------------------------------
# Script starts here
# -----------------------------------------------------------------------------
main() {
  check_requirements

  parse_commandline $@

  if [ ! -z "$ARG_HELP" ]; then
    show_usage
  fi

  # Extract only those glyphs shown on the web page. This is accomplished
  # by first generating the full web page without CSS, then by using Lynx
  # to render the template-generated web page as text only.
	if [ ! -z "$ARG_FONT" ]; then
    > $INC_DIR/css/main.min.css
    convert_markdown index.md
    apply_templates index.html

		minify_fonts index.html
	fi

  minify_artefacts
  convert_markdown index.md
  apply_templates index.html

  minify_html index.html
}

# -----------------------------------------------------------------------------
# Uses pandoc to convert a Markdown file to HTML.
#
# $1 - The markdown file to convert to the HTML body.
# -----------------------------------------------------------------------------
convert_markdown() {
  log "Convert $1 to HTML..."
  pandoc "$1" \
    --lua-filter svg.lua \
    -f markdown-auto_identifiers \
    -o "$INC_DIR/html/body.html"
}

# -----------------------------------------------------------------------------
# Uses Closure to merge and minify all CSS files.
# -----------------------------------------------------------------------------
minify_artefacts() {
  log "Minify CSS..."
  closure.sh --allow-unrecognized-properties css/*.css > "$INC_DIR/css/main.min.css"
}

# -----------------------------------------------------------------------------
# Uses FMPP to inject a web page body into a standard template.
#
# $1 - The web page template.
# -----------------------------------------------------------------------------
apply_templates() {
  log "Generate $1 using files from template directory..."
  fmpp -Q -S "$TPL_DIR" -O . "$1"
}

# -----------------------------------------------------------------------------
# Uses HTML Tidy to remove all the whitespace from the HTML.
#
# $1 - The web page to minify.
# -----------------------------------------------------------------------------
minify_html() {
  log "Minify ${1}..."
  tidy -m -q \
    --wrap 0 \
    --tidy-mark no \
    --vertical-space auto \
    --doctype html5 \
    "$1"
}

# -----------------------------------------------------------------------------
# Uses numerous tools to remove glyphs from font files. Repackages the
# slimmed font file as WOFF.
# -----------------------------------------------------------------------------
minify_fonts() {
  log "Minify fonts based on text from ${1}..."
  # Create a string of all unique characters used in the HTML files
  CHARSET=$(lynx -dump -nolist "$1" \
    | sed 's/./&\n/g' \
    | sort -u \
    | uniq \
    | tr -d '\n')

  log "Glyphs: $CHARSET"

  # Make parsing filenames easier by jumping into the fonts directory
  pushd fonts > /dev/null

  # Create subsets and generate WOFFs from TTFs
  for i in *; do
    log "Remove glyphs from ${i}..."
    FILE_FONT=ss_$i
    TEMPDIR=/tmp
    pyftsubset $i --text="$CHARSET" --output-file="$TEMPDIR/$FILE_FONT"
    sfnt2woff $TEMPDIR/$FILE_FONT

    FILE_WOFF="${FILE_FONT%.*}".woff
    FILE_WOFF_BASE64=$TEMPDIR/$FILE_WOFF.b64

    # Convert WOFFs to base64 for inlining, without line wrapping
    base64 -w 0 $TEMPDIR/$FILE_WOFF > $FILE_WOFF_BASE64

    # Extract all the font attributes (the name/family will not include weight)
    FONT_NAME=${i%.*}
    FONT_FAMILY=${i%-*}
    FONT_WEIGHT=${i#*-}
    FONT_WEIGHT=${FONT_WEIGHT%.*}

    CSS_FONT_WEIGHT=400
    CSS_FONT_STYLE=normal

    log "Parse font family ${FONT_NAME}..."
    case "$FONT_WEIGHT" in
      ExtraLight|UltraLight)
      CSS_FONT_WEIGHT=100
      ;;
      Light|Thin)
      CSS_FONT_WEIGHT=200
      ;;
      Book|Demi)
      CSS_FONT_WEIGHT=300
      ;;
      Medium)
      CSS_FONT_WEIGHT=500
      ;;
      SemiBold|DemiBold)
      CSS_FONT_WEIGHT=600
      ;;
      Bold)
      CSS_FONT_WEIGHT=700
      ;;
      Black|ExtraBold|Heavy)
      CSS_FONT_WEIGHT=800
      ;;
      ExtraBlack|Fat|Poster|UltraBlack)
      CSS_FONT_WEIGHT=900
      ;;
      Italic)
      CSS_FONT_STYLE=italic;
      ;;
      Oblique)
      CSS_FONT_STYLE=oblique;
      ;;
      LightItalic|ThinItalic)
      CSS_FONT_WEIGHT=200
      CSS_FONT_STYLE=italic;
      ;;
      MediumItalic)
      CSS_FONT_WEIGHT=500
      CSS_FONT_STYLE=italic;
      ;;
      BoldItalic)
      CSS_FONT_WEIGHT=500
      CSS_FONT_STYLE=italic;
      ;;
      BoldOblique)
      CSS_FONT_WEIGHT=500
      CSS_FONT_STYLE=oblique;
      ;;
    esac

    log "Font weight: ${CSS_FONT_WEIGHT}"
    log "Font style: ${CSS_FONT_STYLE}"

  # Create font-faces for each font
    cat > ../css/font-$FONT_NAME.css << EOF
@font-face {
  font-family: '${FONT_FAMILY}';
  src: url(data:font/woff;charset=utf-8;base64,$(cat $FILE_WOFF_BASE64)) format('woff');
  font-weight: ${CSS_FONT_WEIGHT};
  font-style: ${CSS_FONT_STYLE};
  font-display: block;
}
EOF

  done

  popd > /dev/null
}

# -----------------------------------------------------------------------------
# Checks for required utilities and exits if not found.
# -----------------------------------------------------------------------------
check_requirements() {
  required pyftsubset "https://github.com/fonttools/fonttools"
  required base64 "https://linux.die.net/man/1/base64"
  required tidy "https://github.com/htacg/tidy-html5"
  required fmpp "http://fmpp.sourceforge.net/"
  required pandoc "http://pandoc.org/"
  required closure.sh "https://developers.google.com/closure/"
  required sfnt2woff "https://github.com/kseo/sfnt2woff"
}

# -----------------------------------------------------------------------------
# Checks for a required utility and exits if not found.
#
# $1 - Command to execute.
# $2 - Where to find the command.
# -----------------------------------------------------------------------------
required() {
  if ! command -v $1 > /dev/null 2>&1; then
    warning "Install $1 from $2"
    exit 1
  fi
}

# -----------------------------------------------------------------------------
# Prints coloured text to standard output.
# -----------------------------------------------------------------------------
coloured_text() {
  printf "%b$1%b\n" "$2" "$COLOUR_OFF"
}

# -----------------------------------------------------------------------------
# Prints a warning message to standard output.
# -----------------------------------------------------------------------------
warning() {
  coloured_text "$1" "$COLOUR_WARNING"
}

# -----------------------------------------------------------------------------
# Writes a message to standard output.
# -----------------------------------------------------------------------------
log() {
	if [ ! -z "$ARG_DEBUG" ]; then
    printf "[$(date +%H:%I:%S.%4N)] %b" "$COLOUR_LOGGING"
    echo -e $1
    printf "%b" "$COLOUR_OFF"
  fi
}

# -----------------------------------------------------------------------------
# Sets the global command line argument values.
# -----------------------------------------------------------------------------
parse_commandline() {
  POSITIONAL=()
  while [[ $# -gt 0 ]]; do
    key="$1"

    case $key in
      -d|--debug)
      ARG_DEBUG="true"
      shift
      ;;
      -h|-\?|--help)
      ARG_HELP="true"
      shift
      ;;
      -f|--font)
      ARG_FONT="true"
      shift
      ;;
      *)
      # Skip argument
      shift
      ;;
    esac
  done
  set -- "${POSITIONAL[@]}"
}

# -----------------------------------------------------------------------------
# Shows accepted command line parameters.
# -----------------------------------------------------------------------------
show_usage() {
  printf "Usage: $SCRIPT_NAME [-f|--font]\n" >&2
  printf "Where: --font Regenerate minified fonts\n" >&2
  exit 1
}

main $@