font-audit.sh 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. #!/usr/bin/env bash
  2. # mac-ops :: font-audit.sh
  3. # Font inventory + duplicate detection. Font conflicts cause real problems —
  4. # Office crashes, Adobe app crashes, font rendering glitches, login slowness
  5. # (Font Book validates ALL fonts at login if registry is corrupt).
  6. set -u
  7. while [[ $# -gt 0 ]]; do
  8. case "$1" in
  9. --help|-h)
  10. cat <<EOF
  11. Usage: $0 [options]
  12. --json, --redact, --quiet, --verbose
  13. Reports:
  14. 1. Font directories + counts (system, user, library, fontd cache)
  15. 2. fontd process state + recent errors
  16. 3. Duplicate fonts (same family in multiple locations)
  17. 4. Disabled fonts in Font Book
  18. 5. Suspicious / corrupt font files
  19. Common fixes:
  20. - Open Font Book → File → Validate fonts → resolve duplicates
  21. - sudo atsutil databases -remove # clear font cache, reboot
  22. - sudo atsutil server -shutdown # restart font server
  23. EOF
  24. exit 0 ;;
  25. *) shift ;;
  26. esac
  27. done
  28. source "$(dirname "$0")/_lib/common.sh"
  29. parse_common_flags "$@"
  30. maybe_filter_self "$@"
  31. # ----------------------------------------------------------------------------
  32. section "1. FONT DIRECTORIES"
  33. # ----------------------------------------------------------------------------
  34. declare -a font_dirs=(
  35. "/System/Library/Fonts"
  36. "/Library/Fonts"
  37. "$HOME/Library/Fonts"
  38. )
  39. for d in "${font_dirs[@]}"; do
  40. if [[ -d "$d" ]]; then
  41. count=$(find "$d" -maxdepth 2 -type f \( -name "*.ttf" -o -name "*.otf" -o -name "*.ttc" -o -name "*.dfont" \) 2>/dev/null | wc -l | tr -d ' \n')
  42. size=$(du -sh "$d" 2>/dev/null | awk '{print $1}')
  43. log_info "$d" "$count fonts, $size"
  44. fi
  45. done
  46. # Adobe / third-party font cache directories
  47. adobe_fonts=$(find /Users -maxdepth 5 -path "*/Library/Application Support/Adobe/CoreSync/plugins/livetype/r" -type d 2>/dev/null | head -3)
  48. if [[ -n "$adobe_fonts" ]]; then
  49. while IFS= read -r d; do
  50. count=$(find "$d" -maxdepth 2 -type f \( -name "*.ttf" -o -name "*.otf" \) 2>/dev/null | wc -l | tr -d ' \n')
  51. note " $d ($count Adobe Fonts)"
  52. done <<< "$adobe_fonts"
  53. fi
  54. # ----------------------------------------------------------------------------
  55. section "2. fontd PROCESS"
  56. # ----------------------------------------------------------------------------
  57. if pgrep -x fontd >/dev/null; then
  58. pid=$(pgrep -x fontd | head -1)
  59. cpu=$(ps -p "$pid" -o pcpu= 2>/dev/null | tr -d ' ')
  60. rss=$(ps -p "$pid" -o rss= 2>/dev/null | tr -d ' ')
  61. log_pass "fontd running" "PID $pid (CPU ${cpu:-0}%, RSS ${rss:-?} KB)"
  62. else
  63. log_info "fontd" "not running (Font Book may not be open)"
  64. fi
  65. # Recent fontd errors
  66. font_errors=$(log show --last 24h --style compact \
  67. --predicate '(process == "fontd" OR process == "FontRegistry" OR eventMessage CONTAINS "font") AND (messageType == "Error" OR messageType == "Fault")' \
  68. 2>/dev/null | grep -iE "font" | tail -5)
  69. if [[ -n "$font_errors" ]]; then
  70. n=$(echo "$font_errors" | wc -l | tr -d ' \n')
  71. log_warn "Font errors in log (24h)" "$n"
  72. echo "$font_errors" | head -3 | sed 's/^/ /'
  73. fi
  74. # ----------------------------------------------------------------------------
  75. section "3. DUPLICATE FONTS"
  76. # ----------------------------------------------------------------------------
  77. # Find filename collisions across all font dirs
  78. all_fonts=$(for d in "${font_dirs[@]}"; do
  79. [[ -d "$d" ]] && find "$d" -maxdepth 2 -type f \( -name "*.ttf" -o -name "*.otf" -o -name "*.ttc" \) 2>/dev/null
  80. done)
  81. dupes=$(echo "$all_fonts" | awk -F/ '{print $NF}' | sort | uniq -d)
  82. if [[ -n "$dupes" ]]; then
  83. n=$(echo "$dupes" | wc -l | tr -d ' \n')
  84. log_warn "Duplicate font filenames" "$n"
  85. note " Duplicates (filename, then locations):"
  86. echo "$dupes" | head -10 | while read -r f; do
  87. printf " %s\n" "$f"
  88. echo "$all_fonts" | grep "/$f\$" | sed 's/^/ /'
  89. done
  90. else
  91. log_pass "Duplicate font filenames" "0"
  92. fi
  93. # ----------------------------------------------------------------------------
  94. section "4. FONT BOOK DISABLED FONTS"
  95. # ----------------------------------------------------------------------------
  96. # Font Book stores disabled-state in a plist (path varies by macOS version)
  97. disabled_plist=$(find "$HOME/Library/FontCollections" -maxdepth 2 -name "*.collection" 2>/dev/null | head -3)
  98. if [[ -n "$disabled_plist" ]]; then
  99. note " Font collections found:"
  100. echo "$disabled_plist" | sed 's/^/ /'
  101. fi
  102. # ----------------------------------------------------------------------------
  103. section "5. CACHE / VALIDATION"
  104. # ----------------------------------------------------------------------------
  105. note " Font cache reset (drastic) requires sudo:"
  106. note " sudo atsutil databases -remove # clears all font caches"
  107. note " sudo atsutil server -shutdown # restarts font server"
  108. note ""
  109. note " To open Font Book and validate:"
  110. note " open -a 'Font Book' # then File → Validate Fonts"
  111. # ----------------------------------------------------------------------------
  112. emit_summary