Last active
February 23, 2026 18:25
-
-
Save htlin222/fda5fcea3726bb04b97913ca735bb99b to your computer and use it in GitHub Desktop.
Generate a grid overlay image for Google Slides (720x405) for designers — 6x6 big grid, 16x8 small cells, 7px unit, with position labels and dashed guides
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| from PIL import Image, ImageDraw, ImageFont | |
| # Scale factor | |
| scale = 5 | |
| # Parameters (original units) | |
| big_cols, big_rows = 6, 6 | |
| small_w, small_h = 16, 8 | |
| cell_px = 7 * scale | |
| # Total size | |
| width = big_cols * small_w * cell_px | |
| height = big_rows * small_h * cell_px | |
| # Add padding so border strokes aren't clipped | |
| pad = 2 * scale | |
| img = Image.new("RGB", (width + pad, height + pad), "white") | |
| draw = ImageDraw.Draw(img) | |
| # Draw column backgrounds (1 small cell padding on each side of each big block) | |
| col_bg = "#E8E8E8" | |
| big_step_x_px = small_w * cell_px | |
| for i in range(big_cols): | |
| x1 = i * big_step_x_px + cell_px # 1 cell padding from left | |
| x2 = (i + 1) * big_step_x_px - cell_px # 1 cell padding from right | |
| draw.rectangle([(x1, 0), (x2, height + pad)], fill=col_bg) # extend to full height incl pad | |
| # Draw small grid lines (light gray) | |
| for x in range(0, width + 1, cell_px): | |
| draw.line([(x, 0), (x, height)], fill="#D0D0D0", width=3) | |
| for y in range(0, height + 1, cell_px): | |
| draw.line([(0, y), (width, y)], fill="#D0D0D0", width=3) | |
| # Draw big grid lines (dark) | |
| for x in range(0, width + 1, small_w * cell_px): | |
| draw.line([(x, 0), (x, height)], fill="#AAAAAA", width=scale) | |
| for y in range(0, height + 1, small_h * cell_px): | |
| draw.line([(0, y), (width, y)], fill="#AAAAAA", width=scale) | |
| # Place grid centered on scaled canvas | |
| canvas_w, canvas_h = 720 * scale, 405 * scale | |
| canvas = Image.new("RGB", (canvas_w, canvas_h), "white") | |
| offset_x = (canvas_w - (width + pad)) // 2 | |
| offset_y = (canvas_h - (height + pad)) // 2 | |
| canvas.paste(img, (offset_x, offset_y)) | |
| # Extend column backgrounds to canvas top and bottom edges | |
| cdraw_bg = ImageDraw.Draw(canvas) | |
| for i in range(big_cols): | |
| x1 = offset_x + i * small_w * cell_px + cell_px | |
| x2 = offset_x + (i + 1) * small_w * cell_px - cell_px | |
| # Top extension | |
| cdraw_bg.rectangle([(x1, 0), (x2, offset_y)], fill=col_bg) | |
| # Bottom extension | |
| cdraw_bg.rectangle([(x1, offset_y + height + pad), (x2, canvas_h)], fill=col_bg) | |
| # Extend big grid lines as dashes to canvas edges | |
| cdraw = ImageDraw.Draw(canvas) | |
| dash_len = 10 | |
| dash_gap = 8 | |
| dash_color = "#BBBBBB" | |
| dash_w = 2 | |
| # Vertical dashes: top edge (0 to offset_y) and bottom edge (offset_y+height to canvas_h) | |
| for i in range(big_cols + 1): | |
| x = offset_x + i * small_w * cell_px | |
| # Top dashes | |
| y = 0 | |
| while y < offset_y: | |
| y2 = min(y + dash_len, offset_y) | |
| cdraw.line([(x, y), (x, y2)], fill=dash_color, width=dash_w) | |
| y += dash_len + dash_gap | |
| # Bottom dashes | |
| y = offset_y + height + pad | |
| while y < canvas_h: | |
| y2 = min(y + dash_len, canvas_h) | |
| cdraw.line([(x, y), (x, y2)], fill=dash_color, width=dash_w) | |
| y += dash_len + dash_gap | |
| # Horizontal dashes: left edge (0 to offset_x) and right edge (offset_x+width to canvas_w) | |
| for i in range(big_rows + 1): | |
| y = offset_y + i * small_h * cell_px | |
| # Left dashes | |
| x = 0 | |
| while x < offset_x: | |
| x2 = min(x + dash_len, offset_x) | |
| cdraw.line([(x, y), (x2, y)], fill=dash_color, width=dash_w) | |
| x += dash_len + dash_gap | |
| # Right dashes | |
| x = offset_x + width + pad | |
| while x < canvas_w: | |
| x2 = min(x + dash_len, canvas_w) | |
| cdraw.line([(x, y), (x2, y)], fill=dash_color, width=dash_w) | |
| x += dash_len + dash_gap | |
| # Label absolute positions (in original 720x405 coordinates) | |
| try: | |
| font = ImageFont.truetype("/System/Library/Fonts/Helvetica.ttc", 7 * scale) | |
| except: | |
| font = ImageFont.load_default() | |
| cdraw = ImageDraw.Draw(canvas) | |
| label_color = "#999999" | |
| margin = 2 * scale | |
| # Original offsets for labeling | |
| orig_offset_x = 23 | |
| orig_offset_y = 33 | |
| orig_step_x = small_w * 7 # 112 | |
| orig_step_y = small_h * 7 # 56 | |
| # X labels along the top | |
| big_step_x = small_w * cell_px | |
| for i in range(big_cols + 1): | |
| abs_x = offset_x + i * big_step_x | |
| orig_val = orig_offset_x + i * orig_step_x | |
| cdraw.text((abs_x + margin, offset_y - 12 * scale + 20), str(orig_val), fill=label_color, font=font) | |
| # Y labels along the left | |
| big_step_y = small_h * cell_px | |
| for i in range(big_rows + 1): | |
| abs_y = offset_y + i * big_step_y | |
| orig_val = orig_offset_y + i * orig_step_y | |
| cdraw.text((margin + 30, abs_y + margin), str(orig_val), fill=label_color, font=font) | |
| # X labels at bottom for column bilateral edges (left & right edge of each column background) | |
| try: | |
| edge_font = ImageFont.truetype("/System/Library/Fonts/Helvetica.ttc", 5 * scale) | |
| except: | |
| edge_font = ImageFont.load_default() | |
| edge_label_color = "#999999" | |
| bottom_label_y = offset_y + height + pad + 8 * scale - 35 # below the grid, moved up 35px | |
| for i in range(big_cols): | |
| # Left edge of column background: i * big_step_x + cell_px (1 cell inset) | |
| left_px = offset_x + i * big_step_x + cell_px | |
| left_orig = orig_offset_x + i * orig_step_x + 7 # +7 = 1 cell in original coords | |
| # Right edge of column background: (i+1) * big_step_x - cell_px | |
| right_px = offset_x + (i + 1) * big_step_x - cell_px | |
| right_orig = orig_offset_x + (i + 1) * orig_step_x - 7 # -7 = 1 cell in original coords | |
| # Draw left edge label (right-aligned to the edge) | |
| left_text = str(left_orig) | |
| left_bbox = cdraw.textbbox((0, 0), left_text, font=edge_font) | |
| left_tw = left_bbox[2] - left_bbox[0] | |
| cdraw.text((left_px - left_tw // 2, bottom_label_y), left_text, fill=edge_label_color, font=edge_font) | |
| # Draw right edge label (left-aligned to the edge) | |
| right_text = str(right_orig) | |
| right_bbox = cdraw.textbbox((0, 0), right_text, font=edge_font) | |
| right_tw = right_bbox[2] - right_bbox[0] | |
| cdraw.text((right_px - right_tw // 2, bottom_label_y), right_text, fill=edge_label_color, font=edge_font) | |
| canvas.save("/private/tmp/grid.png") | |
| print(f"Grid {width}x{height} on {canvas_w}x{canvas_h} canvas (5x scale, labels in original coords)") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment