Files
Cable/frame_screens.sh
2025-10-13 09:38:22 +02:00

276 lines
9.5 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
set -euo pipefail
FONT_COLOR="#3C3C3C" # color for light text
FONT_BOLD_COLOR="#B51700" # color for bold texto pipefail
# Inputs
SRC_ROOT="${1:-Shots/Screenshots}" # root folder with lang subfolders (de/, fr/, en/…)
BG_IMAGE="${2:-Shots/frame-bg.png}" # background image (portrait)
OUT_ROOT="${3:-Shots/Framed}" # output folder
FONT="./Shots/Fonts/Oswald-Light.ttf" # font for title text
FONT_BOLD="./Shots/Fonts/Oswald-SemiBold.ttf" # font for *emphasized* text
# Tweakables
CORNER_RADIUS="auto" # corner radius; "auto" picks a good value based on width
INSET=2 # inset (px) to shave off simulators black edge pixels
SHADOW_OPACITY=60 # 0100
SHADOW_BLUR=20 # blur radius
SHADOW_OFFSET_X=0 # px
SHADOW_OFFSET_Y=40 # px
CANVAS_MARGIN=190 # margin around the device on the background, px
TITLE_MARGIN=120 # margin above the device for title text, px
mkdir -p "$OUT_ROOT"
# Function to render mixed-font text (light + semi-bold for *text*)
render_mixed_font_title() {
local canvas="$1"
local title_text="$2"
local title_y="$3"
local output="$4"
if [[ "$title_text" == *"*"* ]]; then
# Get canvas dimensions
read -r canvas_w canvas_h <<<"$(identify -format "%w %h" "$canvas")"
# Create a temporary image to measure and render text parts
local temp_img
temp_img="$(mktemp /tmp/text_temp.XXXXXX_$$.png)"
cp "$canvas" "$temp_img"
# Parse text into segments with their font types
declare -a text_segments=()
declare -a font_types=()
local current_text=""
local in_bold=false
local i=0
while [ $i -lt ${#title_text} ]; do
local char="${title_text:$i:1}"
if [[ "$char" == "*" ]]; then
# Save current segment (even if empty, to handle cases like "**")
text_segments+=("$current_text")
if [[ "$in_bold" == true ]]; then
font_types+=("bold")
else
font_types+=("light")
fi
current_text=""
# Toggle bold state
if [[ "$in_bold" == true ]]; then
in_bold=false
else
in_bold=true
fi
else
current_text+="$char"
fi
i=$((i + 1))
done
# Handle remaining text
if [[ -n "$current_text" ]]; then
text_segments+=("$current_text")
if [[ "$in_bold" == true ]]; then
font_types+=("bold")
else
font_types+=("light")
fi
fi
# Debug: print segments (remove this later)
echo "DEBUG: Text segments:"
local debug_i=0
while [ $debug_i -lt ${#text_segments[@]} ]; do
echo " [$debug_i]: '${text_segments[$debug_i]}' (${font_types[$debug_i]})"
debug_i=$((debug_i + 1))
done
# Calculate total width
local total_width=0
local j=0
while [ $j -lt ${#text_segments[@]} ]; do
local segment="${text_segments[$j]}"
local font_type="${font_types[$j]}"
# Skip empty segments for width calculation
if [[ -n "$segment" ]]; then
local font_for_measurement="$FONT"
if [[ "$font_type" == "bold" ]]; then
font_for_measurement="$FONT_BOLD"
fi
# Replace leading/trailing spaces with non-breaking spaces for measurement
local segment_for_measurement="$segment"
segment_for_measurement="${segment_for_measurement/#/ }" # leading space
segment_for_measurement="${segment_for_measurement/%/ }" # trailing space
local part_width=$(magick -font "$font_for_measurement" -pointsize 148 -size x label:"$segment_for_measurement" -format "%w" info:)
total_width=$((total_width + part_width))
fi
j=$((j + 1))
done
# Calculate starting X position to center the entire text
local start_x=$(( (canvas_w - total_width) / 2 ))
# Render each segment
local x_offset=0
j=0
while [ $j -lt ${#text_segments[@]} ]; do
local segment="${text_segments[$j]}"
local font_type="${font_types[$j]}"
# Skip empty segments for rendering
if [[ -n "$segment" ]]; then
local font_to_use="$FONT"
local color_to_use="$FONT_COLOR"
if [[ "$font_type" == "bold" ]]; then
font_to_use="$FONT_BOLD"
color_to_use="$FONT_BOLD_COLOR"
fi
# Replace leading/trailing spaces with non-breaking spaces for rendering
local segment_for_rendering="$segment"
segment_for_rendering="${segment_for_rendering/#/ }" # leading space
segment_for_rendering="${segment_for_rendering/%/ }" # trailing space
magick "$temp_img" \
-font "$font_to_use" -pointsize 148 -fill "$color_to_use" \
-gravity northwest -annotate "+$((start_x + x_offset))+${title_y}" "$segment_for_rendering" \
"$temp_img"
# Calculate width of rendered text for next position (use same processed segment)
local text_width=$(magick -font "$font_to_use" -pointsize 148 -size x label:"$segment_for_rendering" -format "%w" info:)
x_offset=$((x_offset + text_width))
fi
j=$((j + 1))
done
cp "$temp_img" "$output"
rm -f "$temp_img"
else
# No asterisks, simple rendering
magick "$canvas" \
-font "$FONT" -pointsize 148 -fill "$FONT_COLOR" \
-gravity north -annotate "+0+${title_y}" "$title_text" \
"$output"
fi
}
# Function to get title from config file
get_title() {
local lang="$1"
local screenshot_name="$2"
local config_file="./Shots/Titles/${lang}.conf"
# Extract view name from filename format: 03-LoadEditorView_0_5EE662BD-84C7-41AC-806E-EB8C7340A037.png
# Remove .png extension, then extract the part after the first dash and before the first underscore
local base_name=$(basename "$screenshot_name" .png)
# Remove leading number and dash (e.g., "03-")
base_name=${base_name#*-}
# Remove everything from the first underscore onwards (e.g., "_0_5EE662BD...")
base_name=${base_name%%_*}
# Try to find title in config file
if [[ -f "$config_file" ]]; then
local title=$(grep "^${base_name}=" "$config_file" 2>/dev/null | cut -d'=' -f2-)
if [[ -n "$title" ]]; then
echo "$title"
return
fi
fi
# Fallback to default title
echo "***NOT SET***"
}
# Function to frame one screenshot
frame_one () {
local in="$1" # input screenshot (e.g., 1320x2868)
local out="$2" # output image
local bg="$3"
local lang="$4" # language code (e.g., "de", "en")
local screenshot_name="$5" # screenshot filename
# Read sizes
read -r W H <<<"$(identify -format "%w %h" "$in")"
# Determine corner radius
local R
if [[ "$CORNER_RADIUS" == "auto" ]]; then
# Heuristic: ~1/12 of width works well for iPhone 6.9" (≈110px for 1320px width)
R=$(( W / 12 ))
else
R=$CORNER_RADIUS
fi
# Create rounded-corner mask the same size as the screenshot
local mask
mask="$(mktemp /tmp/mask.XXXXXX_$$.png)"
magick -size "${W}x${H}" xc:black \
-fill white -draw "roundrectangle ${INSET},${INSET},$((W-1-INSET)),$((H-1-INSET)),$R,$R" \
"$mask"
# Apply rounded corners + make a soft drop shadow
# 1) Rounded PNG
local rounded
rounded="$(mktemp /tmp/rounded.XXXXXX_$$.png)"
magick "$in" -alpha set "$mask" -compose copyopacity -composite "$rounded"
# 2) Shadow from rounded image
local shadow
shadow="$(mktemp /tmp/shadow.XXXXXX_$$.png)"
magick "$rounded" \
\( +clone -background black -shadow ${SHADOW_OPACITY}x${SHADOW_BLUR}+${SHADOW_OFFSET_X}+${SHADOW_OFFSET_Y} \) \
+swap -background none -layers merge +repage "$shadow"
# Compose on the background, centered
# First, scale background to be at least screenshot+margin in both dimensions
read -r BW BH <<<"$(identify -format "%w %h" "$bg")"
local minW=$((W + 2*CANVAS_MARGIN))
local minH=$((H + 2*CANVAS_MARGIN + TITLE_MARGIN))
local canvas
canvas="$(mktemp /tmp/canvas.XXXXXX_$$.png)"
magick "$bg" -resize "${minW}x${minH}^" -gravity center -extent "${minW}x${minH}" "$canvas"
# Add title text above the screenshot
local title_text=$(get_title "$lang" "$screenshot_name")
local with_title
with_title="$(mktemp /tmp/with_title.XXXXXX_$$.png)"
# Calculate title position (center horizontally, positioned above the screenshot)
local title_y=$((TITLE_MARGIN - 10)) # 10px from top of title margin
# Render title with mixed fonts
render_mixed_font_title "$canvas" "$title_text" "$title_y" "$with_title"
# Now place shadow (which already includes the rounded image) positioned below the title
# Calculate the vertical offset to center the screenshot in the remaining space below the title
local screenshot_offset=$((TITLE_MARGIN*2))
local temp_result
temp_result="$(mktemp /tmp/temp_result.XXXXXX_$$.png)"
magick "$with_title" "$shadow" -gravity center -geometry "+0+${screenshot_offset}" -compose over -composite "$temp_result"
# Final step: scale to exact dimensions 1320 × 2868px
magick "$temp_result" -resize "1320x2868^" -gravity center -extent "1320x2868" "$out"
rm -f "$mask" "$rounded" "$shadow" "$canvas" "$with_title" "$temp_result"
}
# Process all screenshots in SRC_ROOT/*/*.png
shopt -s nullglob
for langdir in "$SRC_ROOT"/*; do
[[ -d "$langdir" ]] || continue
rel="$(basename "$langdir")"
mkdir -p "$OUT_ROOT/$rel"
for shot in "$langdir"/*.png; do
base="$(basename "$shot")"
frame_one "$shot" "$OUT_ROOT/$rel/$base" "$BG_IMAGE" "$rel" "$base"
echo "Framed: $rel/$base"
done
done
echo "Done. Framed images in: $OUT_ROOT/"