"""SaiCore Tool Palette, Extra Palette, Erase Palette, Background Palette, Cursors, and misc icons.""" import os from PIL import Image, ImageDraw, ImageFont BASE = os.path.join(os.path.dirname(__file__), "..", "resources", "images") DARK = (250, 250, 249) # cream-white for dark bg INDIGO = (220, 38, 38) # red-600 INDIGO_LT = (248, 113, 113) # red-400 WHITE = (255, 255, 255) RED = (239, 68, 68) GREEN = (16, 185, 129) AMBER = (245, 158, 11) GRAY = (82, 82, 91) LIGHT_GRAY = (229, 231, 235) T = (0, 0, 0, 0) def font(sz): for n in ['C:/Windows/Fonts/segoeuib.ttf', 'C:/Windows/Fonts/arialbd.ttf']: try: return ImageFont.truetype(n, sz) except: pass return ImageFont.load_default() def new(sz=42): img = Image.new('RGBA', (sz, sz), T) return img, ImageDraw.Draw(img) def save(path, img): img.save(path, 'PNG') print(f" {os.path.basename(path)}") def svg(path, body, w=42, h=42): with open(path, 'w', encoding='utf-8') as f: f.write(f'\n\n{body}\n') print(f" {os.path.basename(path)}") def hx(c): return f'#{c[0]:02x}{c[1]:02x}{c[2]:02x}' def gen_tool_palette(): print("=== Tool Palette ===") d = os.path.join(BASE, "toolPalette") sz = 42 # Compass img, dr = new(sz) dr.line([21,8,12,34], fill=DARK, width=2) dr.line([21,8,30,34], fill=DARK, width=2) dr.ellipse([18,5,24,11], outline=DARK, width=2) dr.ellipse([9,31,15,37], fill=DARK) dr.line([12,34,30,34], fill=DARK, width=1) save(os.path.join(d, "compassTool.png"), img) svg(os.path.join(d, "compassTool.svg"), '') # Ruler img, dr = new(sz) dr.polygon([(6,28),(34,6),(36,10),(8,32)], fill=DARK) for i in range(5): x1, y1 = 10+i*5, 30-i*4 dr.line([(x1,y1),(x1+2,y1-3)], fill=WHITE, width=1) save(os.path.join(d, "rulerTool.png"), img) # Protractor img, dr = new(sz) dr.arc([6,6,36,36], 0, 180, fill=DARK, width=2) dr.line([6,21,36,21], fill=DARK, width=2) dr.arc([12,12,30,30], 0, 180, fill=DARK, width=1) save(os.path.join(d, "protractorTool.png"), img) # Triangle img, dr = new(sz) dr.polygon([(6,34),(34,34),(6,6)], outline=DARK, width=2) save(os.path.join(d, "triangleTool.png"), img) # Axes img, dr = new(sz) dr.line([21,6,21,36], fill=DARK, width=2) dr.line([6,21,36,21], fill=DARK, width=2) dr.polygon([(21,6),(18,10),(24,10)], fill=DARK) dr.polygon([(36,21),(32,18),(32,24)], fill=DARK) save(os.path.join(d, "axesTool.png"), img) # Aristo (L-shape) img, dr = new(sz) dr.polygon([(6,6),(6,34),(34,34)], outline=DARK, width=2) save(os.path.join(d, "aristoTool.png"), img) # Magnifier img, dr = new(sz) dr.ellipse([8,8,28,28], outline=DARK, width=3) dr.line([26,26,36,36], fill=DARK, width=3) save(os.path.join(d, "magnifierTool.png"), img) # Cache img, dr = new(sz) dr.rounded_rectangle([8,8,34,34], radius=4, outline=DARK, width=2) dr.line([8,8,34,34], fill=DARK, width=1) save(os.path.join(d, "cacheTool.png"), img) # Mask img, dr = new(sz) dr.rounded_rectangle([8,8,34,34], radius=4, fill=DARK) dr.rounded_rectangle([14,14,28,28], radius=2, fill=T[:3]+(0,)) save(os.path.join(d, "maskTool.png"), img) # Podcast svg(os.path.join(d, "podcast.svg"), '') svg(os.path.join(d, "podcastOn.svg"), '') def gen_extra_palette(): print("=== Extra Palette ===") d = os.path.join(BASE, "extraPalette") sz = 42 for name, suffix, color, fill_c in [ ("blackout","",DARK,None),("blackoutOn","",INDIGO,None), ("compass","",DARK,None),("compassOn","",INDIGO,None), ("keyboard","",DARK,None),("keyboardOn","",INDIGO,None), ("mask","",DARK,None),("maskOn","",INDIGO,None), ("oEmbed","",DARK,None), ("podcast","",DARK,None),("podcastOn","",INDIGO,None), ("protractor","",DARK,None),("protractorOn","",INDIGO,None), ("ruler","",DARK,None),("rulerOn","",INDIGO,None), ("trapFlash","",DARK,None), ("webTrap","",DARK,None),("webTrapEnabled","",INDIGO,None), ]: img, dr = new(sz) if "blackout" in name: dr.rounded_rectangle([8,8,34,34], radius=4, fill=color) elif "compass" in name: dr.line([21,8,12,34], fill=color, width=2) dr.line([21,8,30,34], fill=color, width=2) dr.ellipse([18,5,24,11], outline=color, width=2) elif "keyboard" in name: dr.rounded_rectangle([6,14,36,28], radius=2, outline=color, width=2) for i in range(5): for j in range(2): dr.rectangle([10+j*5+i*5, 16, 13+j*5+i*5, 19], fill=color) elif "mask" in name: dr.rounded_rectangle([8,8,34,34], radius=4, fill=color) elif "podcast" in name: dr.ellipse([9,9,33,33], outline=color, width=2) dr.ellipse([17,17,25,25], fill=color) elif "protractor" in name: dr.arc([6,6,36,36], 0, 180, fill=color, width=2) dr.line([6,21,36,21], fill=color, width=2) elif "ruler" in name: dr.polygon([(6,28),(34,6),(36,10),(8,32)], fill=color) elif "oEmbed" in name: dr.rounded_rectangle([8,14,34,28], radius=2, outline=color, width=2) dr.polygon([(18,18),(18,24),(24,21)], fill=color) elif "trapFlash" in name: dr.ellipse([8,8,34,34], outline=color, width=2) dr.line([8,8,34,34], fill=color, width=1) elif "webTrap" in name: dr.ellipse([8,8,34,34], outline=color, width=2) dr.line([8,8,34,34], fill=color, width=1) dr.line([21,8,21,34], fill=color, width=1) dr.line([8,21,34,21], fill=color, width=1) save(os.path.join(d, f"{name}{suffix}.png"), img) def gen_erase_palette(): print("=== Erase Palette ===") d = os.path.join(BASE, "erasePalette") for name, color in [("eraseAll",DARK),("eraseAnnotation",DARK),("eraseBackground",DARK),("eraseItem",DARK)]: svg(os.path.join(d, f"{name}.svg"), f'') # eraseAll - X with circle svg(os.path.join(d, "eraseAll.svg"), '') # eraseAnnotation - eraser with lines svg(os.path.join(d, "eraseAnnotation.svg"), '') # eraseBackground - grid with X svg(os.path.join(d, "eraseBackground.svg"), '') # eraseItem - box with X svg(os.path.join(d, "eraseItem.svg"), '') def gen_background_palette(): print("=== Background Palette ===") d = os.path.join(BASE, "backgroundPalette") bgs = [ ("background1", "plain"), ("background2", "grid"), ("background3", "lines"), ("background4", "dots"), ("background5", "cross"), ("background6", "ruler"), ("background7", "dark"), ("background8", "light"), ] for name, bg_type in bgs: for suffix, color in [("", DARK), ("On", INDIGO)]: c = hx(color) if bg_type == "plain": body = f'' elif bg_type == "grid": body = f'' elif bg_type == "lines": body = f'' elif bg_type == "dots": body = f'' + ''.join(f'' for x in range(12,36,6) for y in range(12,36,6)) elif bg_type == "cross": body = f'' + ''.join(f'' for x in range(12,36,6) for y in range(12,36,6)) elif bg_type == "ruler": body = f'' + ''.join(f'' for x in range(12,36,3)) elif bg_type == "dark": body = f'' elif bg_type == "light": body = f'' svg(os.path.join(d, f"{name}{suffix}.svg"), body) # Reset grid size svg(os.path.join(d, "resetDefaultGridSize.svg"), '') def gen_cursors(): print("=== Cursors ===") d = os.path.join(BASE, "cursors") sz = 32 for name, fn in [ ("drawCompass", lambda dr: (dr.line([16,4,8,28], fill=DARK, width=2), dr.line([16,4,24,28], fill=DARK, width=2), dr.ellipse([13,1,19,7], outline=DARK, width=2))), ("drawRulerLine", lambda dr: (dr.line([4,28,28,4], fill=DARK, width=2), dr.ellipse([2,26,8,32], fill=DARK), dr.ellipse([26,2,32,8], fill=DARK))), ("eraser", lambda dr: dr.rounded_rectangle([6,10,26,22], radius=3, fill=DARK)), ("laser", lambda dr: (dr.ellipse([12,12,20,20], fill=RED), dr.line([16,16,28,28], fill=RED, width=2))), ("marker", lambda dr: (dr.line([6,26,26,6], fill=DARK, width=5), dr.rounded_rectangle([23,4,29,10], radius=2, fill=DARK))), ("play", lambda dr: dr.polygon([(8,6),(8,26),(26,16)], fill=DARK)), ("resize", lambda dr: (dr.line([4,16,28,16], fill=DARK, width=2), dr.polygon([(4,16),(8,12),(8,20)], fill=DARK), dr.polygon([(28,16),(24,12),(24,20)], fill=DARK))), ("rotate", lambda dr: (dr.arc([6,6,26,26], 0, 270, fill=DARK, width=2), dr.polygon([(26,6),(22,6),(26,10)], fill=DARK))), ("zoomIn", lambda dr: (dr.ellipse([6,6,22,22], outline=DARK, width=2), dr.line([20,20,28,28], fill=DARK, width=2), dr.line([10,14,18,14], fill=DARK, width=2), dr.line([14,10,14,18], fill=DARK, width=2))), ("zoomOut", lambda dr: (dr.ellipse([6,6,22,22], outline=DARK, width=2), dr.line([20,20,28,28], fill=DARK, width=2), dr.line([10,14,18,14], fill=DARK, width=2))), ]: img, dr = new(sz) fn(dr) save(os.path.join(d, f"{name}.png"), img) def gen_misc(): print("=== Misc Icons ===") sz = 42 # Main SVG icons d = BASE for name, body in [ ("close", ''), ("closeDisabled", ''), ("closeTool", ''), ("color", ''), ("circle", ''), ("duplicate", ''), ("duplicateDisabled", ''), ("eyeClosed", ''), ("eyeOpened", ''), ("flash", ''), ("font", 'A'), ("frozen", ''), ("unfrozen", ''), ("groupItems", ''), ("ungroupItems", ''), ("home", ''), ("locked", ''), ("unlocked", ''), ("menu", ''), ("menuDisabled", ''), ("minus", ''), ("plus", ''), ("pin", ''), ("unpin", ''), ("play", ''), ("pause", ''), ("stop", ''), ("reload", ''), ("rotate", ''), ("rotateProtractor", ''), ("roundeRrectangle", ''), ("save", ''), ("setAsBackground", ''), ("soundOff", ''), ("soundOn", ''), ("hflipTool", ''), ("vflipTool", ''), ("moveDown", ''), ("moveDownDisabled", ''), ("moveUp", ''), ("moveUpDisabled", ''), ("moveTool", ''), ("resetAngle", ''), ("resetTool", ''), ("resizeTool", ''), ("resizeBottom", ''), ("resizeLeft", ''), ("resizeRight", ''), ("resizeTop", ''), ("resizeButton", ''), ("resizeCompass", ''), ("resizeRuler", ''), ("numbersTool", '123'), ("angleMarker", ''), ("centerAligned", ''), ("leftAligned", ''), ("rightAligned", ''), ("notAligned", ''), ("z_layer_up", ''), ("z_layer_down", ''), ("increase", ''), ("decrease", ''), ("resize", ''), ("rotate", ''), ("addItemToCurrentPage", ''), ("addItemToLibrary", ''), ("addItemToNewPage", ''), ("addToFavorites", ''), ("removeFromFavorites", ''), ("folder", ''), ("myFolder", ''), ]: svg(os.path.join(d, f"{name}.svg"), body) # PNG-only misc icons for name, fn in [ ("addToFavorites", lambda dr: dr.polygon([(21,6),(26,16),(37,17),(28,25),(31,36),(21,30),(11,36),(14,25),(5,17),(16,16)], outline=DARK, width=2)), ("removeFromFavorites", lambda dr: dr.polygon([(21,6),(26,16),(37,17),(28,25),(31,36),(21,30),(11,36),(14,25),(5,17),(16,16)], fill=INDIGO)), ("asc", lambda dr: (dr.line([8,30,34,30], fill=DARK, width=2), dr.line([8,20,34,20], fill=DARK, width=2), dr.line([8,10,34,10], fill=DARK, width=2), dr.polygon([(21,4),(16,10),(26,10)], fill=DARK))), ("desc", lambda dr: (dr.line([8,30,34,30], fill=DARK, width=2), dr.line([8,20,34,20], fill=DARK, width=2), dr.line([8,10,34,10], fill=DARK, width=2), dr.polygon([(21,36),(16,30),(26,30)], fill=DARK))), ("cache_circle", lambda dr: dr.ellipse([6,6,36,36], outline=DARK, width=2)), ("cache_close", lambda dr: (dr.ellipse([6,6,36,36], outline=DARK, width=2), dr.line([14,14,28,28], fill=RED, width=2), dr.line([28,14,14,28], fill=RED, width=2))), ("cache_open", lambda dr: (dr.ellipse([6,6,36,36], outline=DARK, width=2), dr.line([14,14,28,28], fill=GREEN, width=2))), ("cache_square", lambda dr: dr.rounded_rectangle([8,8,34,34], radius=2, outline=DARK, width=2)), ("collapse-all", lambda dr: (dr.line([6,10,36,10], fill=DARK, width=2), dr.polygon([(10,6),(6,10),(10,14)], fill=DARK), dr.line([6,21,36,21], fill=DARK, width=2), dr.polygon([(10,17),(6,21),(10,25)], fill=DARK), dr.line([6,32,36,32], fill=DARK, width=2), dr.polygon([(10,28),(6,32),(10,36)], fill=DARK))), ("expand-all", lambda dr: (dr.line([6,10,36,10], fill=DARK, width=2), dr.polygon([(6,6),(10,10),(6,14)], fill=DARK), dr.line([6,21,36,21], fill=DARK, width=2), dr.polygon([(6,17),(10,21),(6,25)], fill=DARK), dr.line([6,32,36,32], fill=DARK, width=2), dr.polygon([(6,28),(10,32),(6,36)], fill=DARK))), ("currentDocument", lambda dr: (dr.rounded_rectangle([8,6,34,36], radius=2, outline=DARK, width=2), dr.line([12,12,30,12], fill=DARK, width=1), dr.line([12,16,30,16], fill=DARK, width=1), dr.line([12,20,24,20], fill=DARK, width=1))), ("defaultWidgetIcon", lambda dr: (dr.rounded_rectangle([8,8,34,34], radius=4, outline=DARK, width=2), dr.ellipse([16,16,26,26], fill=INDIGO))), ("download_close", lambda dr: (dr.line([8,8,34,34], fill=RED, width=2), dr.line([34,8,8,34], fill=RED, width=2))), ("download_open", lambda dr: (dr.line([8,21,34,21], fill=GREEN, width=2), dr.polygon([(14,16),(14,26),(8,21)], fill=GREEN), dr.polygon([(28,16),(28,26),(34,21)], fill=GREEN))), ("edit-mode", lambda dr: (dr.line([8,34,28,14], fill=DARK, width=3), dr.ellipse([25,11,33,19], outline=DARK, width=2))), ("favorites", lambda dr: dr.polygon([(21,6),(26,16),(37,17),(28,25),(31,36),(21,30),(11,36),(14,25),(5,17),(16,16)], outline=DARK, width=2)), ("folder", lambda dr: (dr.rounded_rectangle([6,12,36,34], radius=3, fill=(220,38,38,40), outline=DARK, width=2), dr.polygon([(6,12),(16,12),(20,8),(28,8)], fill=DARK, outline=DARK, width=1), dr.rounded_rectangle([6,10,28,14], radius=2, fill=(220,38,38,60), outline=DARK, width=1))), ("library_close", lambda dr: (dr.rounded_rectangle([6,6,36,36], radius=4, outline=DARK, width=2), dr.line([14,14,28,28], fill=RED, width=2))), ("library_open", lambda dr: (dr.rounded_rectangle([6,6,36,36], radius=4, outline=DARK, width=2), dr.line([14,21,28,21], fill=GREEN, width=2))), ("myContent", lambda dr: (dr.rounded_rectangle([6,8,36,34], radius=2, outline=DARK, width=2), dr.line([6,16,36,16], fill=DARK, width=1))), ("myFolder", lambda dr: (dr.rounded_rectangle([6,12,36,34], radius=3, fill=(220,38,38,40), outline=DARK, width=2), dr.polygon([(6,12),(16,12),(20,8),(28,8)], fill=DARK, outline=DARK, width=1), dr.rounded_rectangle([6,10,28,14], radius=2, fill=(220,38,38,60), outline=DARK, width=1))), ("navig_arrow", lambda dr: dr.polygon([(8,8),(34,21),(8,34)], fill=DARK)), ("online", lambda dr: (dr.ellipse([8,8,34,34], outline=GREEN, width=2), dr.line([14,21,19,26], fill=GREEN, width=3), dr.line([19,26,30,15], fill=GREEN, width=3))), ("pages_close", lambda dr: (dr.rounded_rectangle([6,6,36,36], radius=4, outline=DARK, width=2), dr.line([14,14,28,28], fill=DARK, width=2), dr.line([28,14,14,28], fill=DARK, width=2))), ("pages_open", lambda dr: (dr.rounded_rectangle([6,6,36,36], radius=4, outline=DARK, width=2), dr.line([14,18,28,18], fill=DARK, width=2), dr.line([14,24,28,24], fill=DARK, width=2))), ("paletteLibrary", lambda dr: (dr.rounded_rectangle([6,6,36,36], radius=4, outline=DARK, width=2), dr.line([6,16,36,16], fill=DARK, width=1), dr.line([16,6,16,36], fill=DARK, width=1))), ("paletteNavigator", lambda dr: (dr.rounded_rectangle([6,6,36,36], radius=4, outline=DARK, width=2), dr.ellipse([14,14,28,28], outline=INDIGO, width=2))), ("save", lambda dr: (dr.rounded_rectangle([8,6,34,36], radius=2, outline=DARK, width=2), dr.rectangle([14,6,28,14], fill=DARK), dr.rectangle([14,22,28,34], outline=DARK, width=1))), ("shape", lambda dr: (dr.ellipse([6,6,24,24], outline=DARK, width=2), dr.polygon([(20,18),(36,18),(28,36)], outline=DARK, width=2))), ("tab_mask", lambda dr: dr.rounded_rectangle([2,8,40,34], radius=2, outline=DARK, width=2)), ("tools", lambda dr: (dr.line([8,34,24,18], fill=DARK, width=3), dr.ellipse([22,14,32,24], outline=DARK, width=2))), ("trash", lambda dr: (dr.line([6,8,36,8], fill=DARK, width=2), dr.rounded_rectangle([10,8,32,36], radius=2, outline=DARK, width=2), dr.line([16,4,26,4], fill=DARK, width=2))), ("trash-delete-document", lambda dr: (dr.line([6,8,36,8], fill=DARK, width=2), dr.rounded_rectangle([10,8,32,36], radius=2, outline=DARK, width=2), dr.rounded_rectangle([16,14,26,30], radius=2, outline=RED, width=1))), ("trash-delete-folder", lambda dr: (dr.line([6,8,36,8], fill=DARK, width=2), dr.rounded_rectangle([10,8,32,36], radius=2, outline=DARK, width=2), dr.rounded_rectangle([14,14,28,28], radius=2, fill=(220,38,38,40), outline=RED, width=1), dr.polygon([(14,14),(20,14),(22,10),(26,10)], fill=RED), dr.line([16,18,26,26], fill=RED, width=2), dr.line([26,18,16,26], fill=RED, width=2))), ("trash-document-page", lambda dr: (dr.line([6,8,36,8], fill=DARK, width=2), dr.rounded_rectangle([10,8,32,36], radius=2, outline=DARK, width=2), dr.line([16,14,26,14], fill=RED, width=1), dr.line([16,20,26,20], fill=RED, width=1))), ("trash-document", lambda dr: (dr.line([6,8,36,8], fill=DARK, width=2), dr.rounded_rectangle([10,8,32,36], radius=2, outline=DARK, width=2), dr.rounded_rectangle([16,14,26,30], radius=2, outline=DARK, width=1))), ("trash-empty", lambda dr: (dr.line([6,8,36,8], fill=DARK, width=2), dr.rounded_rectangle([10,8,32,36], radius=2, outline=DARK, width=2))), ("trash-folder", lambda dr: (dr.line([6,8,36,8], fill=DARK, width=2), dr.rounded_rectangle([10,8,32,36], radius=2, outline=DARK, width=2), dr.rounded_rectangle([14,14,28,28], radius=2, fill=(220,38,38,40), outline=RED, width=1), dr.polygon([(14,14),(20,14),(22,10),(26,10)], fill=RED))), ("trash-my-documents", lambda dr: (dr.line([6,8,36,8], fill=DARK, width=2), dr.rounded_rectangle([10,8,32,36], radius=2, outline=DARK, width=2), dr.ellipse([16,14,22,20], outline=DARK, width=1), dr.ellipse([24,14,30,20], outline=DARK, width=1))), ("up_arrow", lambda dr: dr.polygon([(21,6),(8,30),(34,30)], fill=DARK)), ("down_arrow", lambda dr: dr.polygon([(21,36),(8,12),(34,12)], fill=DARK)), ("left_arrow", lambda dr: dr.polygon([(6,21),(30,8),(30,34)], fill=DARK)), ("right_arrow", lambda dr: dr.polygon([(36,21),(12,8),(12,34)], fill=DARK)), ("edit-mode", lambda dr: (dr.line([8,34,28,14], fill=DARK, width=3), dr.ellipse([25,11,33,19], outline=DARK, width=2))), ("banner", lambda dr: dr.rounded_rectangle([0,0,42,42], radius=0, fill=INDIGO)), ("banner_linear", lambda dr: dr.rounded_rectangle([0,0,42,42], radius=0, fill=INDIGO)), ]: img, dr = new(sz) fn(dr) save(os.path.join(d, f"{name}.png"), img) # Ch.SaiCore.SaiCore.svg svg(os.path.join(d, "ch.SaiCore.SaiCore.svg"), 'S') def gen_libpalette(): print("=== Lib Palette ===") d = os.path.join(BASE, "libpalette") if not os.path.exists(d): os.makedirs(d) # Check what's there and regenerate for f in os.listdir(d): path = os.path.join(d, f) if f.endswith('.png'): try: img = Image.open(path) img = img.convert('RGBA') # Recolor: replace green/colored with red, dark pixels with cream pixels = img.load() for y in range(img.height): for x in range(img.width): r, g, b, a = pixels[x, y] if a > 0 and g > r and g > b: pixels[x, y] = (INDIGO[0], INDIGO[1], INDIGO[2], a) elif a > 0 and r == g == b and r < 128: pixels[x, y] = (DARK[0], DARK[1], DARK[2], a) elif a > 0 and r > 100 and g < 100 and b < 100: pixels[x, y] = (INDIGO[0], INDIGO[1], INDIGO[2], a) img.save(path, 'PNG') print(f" recolored: {f}") except: pass def gen_print(): print("=== Print ===") d = os.path.join(BASE, "print") if not os.path.exists(d): os.makedirs(d) for f in os.listdir(d): path = os.path.join(d, f) if f.endswith('.png'): try: img = Image.open(path).convert('RGBA') pixels = img.load() for y in range(img.height): for x in range(img.width): r, g, b, a = pixels[x, y] if a > 0 and g > r and g > b: pixels[x, y] = (INDIGO[0], INDIGO[1], INDIGO[2], a) img.save(path, 'PNG') print(f" recolored: {f}") except: pass def run(): gen_tool_palette() gen_extra_palette() gen_erase_palette() gen_background_palette() gen_cursors() gen_misc() gen_libpalette() gen_print() if __name__ == "__main__": run()