botserver/fix_svgs_properly.py

219 lines
6.9 KiB
Python

#!/usr/bin/env python3
"""
Fix all SVG files to be properly readable on all devices
Focus on text size, contrast, and responsive design
"""
import os
import re
from pathlib import Path
def fix_svg_content(content, filename):
"""
Apply comprehensive fixes to SVG content
"""
# 1. Fix SVG header for responsiveness
# Remove any width/height attributes and ensure proper viewBox
if "<svg" in content:
# Extract viewBox if exists, or create from width/height
viewbox_match = re.search(r'viewBox="([^"]+)"', content)
width_match = re.search(r'width="(\d+)"', content)
height_match = re.search(r'height="(\d+)"', content)
if viewbox_match:
viewbox = viewbox_match.group(1)
elif width_match and height_match:
viewbox = f"0 0 {width_match.group(1)} {height_match.group(1)}"
else:
viewbox = "0 0 800 600"
# Replace the SVG opening tag
svg_pattern = r"<svg[^>]*>"
svg_replacement = f'<svg viewBox="{viewbox}" xmlns="http://www.w3.org/2000/svg" style="width: 100%; height: auto; max-width: 100%; display: block;">'
content = re.sub(svg_pattern, svg_replacement, content, count=1)
# 2. Fix all font sizes - MINIMUM 16px for readability
def increase_font_size(match):
size = int(match.group(1))
if size < 14:
return f'font-size="16"'
elif size < 16:
return f'font-size="18"'
elif size < 18:
return f'font-size="20"'
else:
return f'font-size="{size + 4}"' # Increase all fonts slightly
content = re.sub(r'font-size="(\d+)"', increase_font_size, content)
# 3. Fix ALL text colors for maximum contrast
# Replace all light colors with dark, readable ones
color_replacements = {
# Light grays to dark gray/black
"#CBD5E0": "#1F2937",
"#A0AEC0": "#374151",
"#E2E8F0": "#1F2937",
"#EDF2F7": "#111827",
"#F7FAFC": "#111827",
"#9CA3AF": "#374151",
"#D1D5DB": "#4B5563",
"#718096": "#374151",
"#4A5568": "#1F2937",
# Light blues to dark blues
"#90CDF4": "#1E40AF",
"#63B3ED": "#2563EB",
"#4A90E2": "#1D4ED8",
"#81E6D9": "#0E7490",
"#4FD1C5": "#0891B2",
"#38D4B2": "#0D9488",
# Light purples to dark purples
"#E9D8FD": "#6B21A8",
"#D6BCFA": "#7C3AED",
"#B794F4": "#9333EA",
"#9F7AEA": "#7C3AED",
# Light oranges to dark oranges
"#FBD38D": "#C2410C",
"#F6AD55": "#EA580C",
"#ED8936": "#C2410C",
# Light reds to dark reds
"#FEB2B2": "#B91C1C",
"#FC8181": "#DC2626",
"#E53E3E": "#DC2626",
# Light greens to dark greens
"#9AE6B4": "#047857",
"#68D391": "#059669",
"#48BB78": "#047857",
"#38A169": "#059669",
"#B2F5EA": "#047857",
# Generic light to dark
"#888": "#374151",
"#888888": "#374151",
"#FAFAFA": "transparent",
"#fff": "#111827",
"#ffffff": "#111827",
"#FFF": "#111827",
"#FFFFFF": "#111827",
}
for old_color, new_color in color_replacements.items():
# Replace in fill attributes
content = re.sub(
f'fill="{old_color}"', f'fill="{new_color}"', content, flags=re.IGNORECASE
)
# Replace in stroke attributes
content = re.sub(
f'stroke="{old_color}"',
f'stroke="{new_color}"',
content,
flags=re.IGNORECASE,
)
# Replace in style attributes
content = re.sub(
f"fill:{old_color}", f"fill:{new_color}", content, flags=re.IGNORECASE
)
content = re.sub(
f"stroke:{old_color}", f"stroke:{new_color}", content, flags=re.IGNORECASE
)
# 4. Remove white/light backgrounds
content = re.sub(r'<rect[^>]*fill="#FAFAFA"[^>]*>', "", content)
content = re.sub(r'<rect[^>]*fill="white"[^>]*>', "", content)
content = re.sub(r'<rect[^>]*fill="#FFFFFF"[^>]*>', "", content)
content = re.sub(r'<rect[^>]*fill="#ffffff"[^>]*>', "", content)
# 5. Fix stroke widths for visibility
content = re.sub(r'stroke-width="1"', 'stroke-width="2"', content)
content = re.sub(r'stroke-width="0\.5"', 'stroke-width="2"', content)
# 6. Fix font weights
content = re.sub(r'font-weight="bold"', 'font-weight="700"', content)
# 7. Fix font families for better rendering
content = re.sub(
r'font-family="[^"]*"',
'font-family="system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, sans-serif"',
content,
)
# 8. Ensure arrows and markers are visible
content = re.sub(
r'<path d="M0,0 L0,6 L9,3 z" fill="#888"/>',
'<path d="M0,0 L0,6 L9,3 z" fill="#374151"/>',
content,
)
# 9. Add slight padding to the viewBox if needed
viewbox_match = re.search(r'viewBox="(\d+)\s+(\d+)\s+(\d+)\s+(\d+)"', content)
if viewbox_match:
x, y, width, height = map(int, viewbox_match.groups())
# Add 20px padding
new_viewbox = f'viewBox="{x - 20} {y - 20} {width + 40} {height + 40}"'
content = re.sub(r'viewBox="[^"]*"', new_viewbox, content, count=1)
return content
def process_all_svgs():
"""Process all SVG files in the docs directory"""
docs_dir = Path("docs")
svg_files = list(docs_dir.glob("**/*.svg"))
# Filter out font files
svg_files = [
f
for f in svg_files
if "fontawesome" not in str(f).lower() and "favicon" not in str(f).lower()
]
print(f"Found {len(svg_files)} SVG files to fix")
print("=" * 60)
fixed = 0
errors = 0
for svg_file in svg_files:
try:
print(f"Processing: {svg_file.relative_to(docs_dir)}")
# Read the file
with open(svg_file, "r", encoding="utf-8") as f:
content = f.read()
# Apply fixes
fixed_content = fix_svg_content(content, svg_file.name)
# Write back
with open(svg_file, "w", encoding="utf-8") as f:
f.write(fixed_content)
print(f" ✓ Fixed successfully")
fixed += 1
except Exception as e:
print(f" ✗ Error: {e}")
errors += 1
print()
print("=" * 60)
print(f"COMPLETED: {fixed} files fixed, {errors} errors")
print()
print("Improvements applied:")
print("• All text now ≥16px (readable on mobile)")
print("• High contrast colors (dark text, no light grays)")
print("• 100% responsive width")
print("• Removed white backgrounds")
print("• Enhanced stroke widths")
print("• Added padding to prevent cutoff")
print("=" * 60)
if __name__ == "__main__":
print("=" * 60)
print("SVG READABILITY FIXER")
print("Making all diagrams actually readable!")
print("=" * 60)
print()
process_all_svgs()