Last active
March 14, 2022 21:46
-
-
Save kpmiller/662710e480da44b744f7664c09d6157a to your computer and use it in GitHub Desktop.
Create a PNG from a SF Symbol for a Stream Deck icon, Mac only (requires pyobjc bridge), tested on MacOS Monterey and python 3.8.9
This file contains 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
#!/usr/bin/env python3 | |
import Cocoa #from PyObjC, https://pyobjc.readthedocs.io/ | |
import argparse | |
import sys, re | |
#taken from https://stackoverflow.com/questions/29643352/converting-hex-to-rgb-value-in-python | |
def hex_to_rgb(hx, hsl=False): | |
"""Converts a HEX code into RGB or HSL. | |
Args: | |
hx (str): Takes both short as well as long HEX codes. | |
hsl (bool): Converts the given HEX code into HSL value if True. | |
Return: | |
Tuple of length 3 consisting of either int or float values. | |
Raise: | |
ValueError: If given value is not a valid HEX code.""" | |
if re.compile(r'#[a-fA-F0-9]{3}(?:[a-fA-F0-9]{3})?$').match(hx): | |
div = 255.0 if hsl else 0 | |
if len(hx) <= 4: | |
return tuple(int(hx[i]*2, 16) / div if div else | |
int(hx[i]*2, 16) for i in (1, 2, 3)) | |
return tuple(int(hx[i:i+2], 16) / div if div else | |
int(hx[i:i+2], 16) for i in (1, 3, 5)) | |
raise ValueError(f'"{hx}" is not a valid HEX code.') | |
parser = argparse.ArgumentParser(description='Make a PNG file from the name of a sf symbol') | |
parser.add_argument('--size', default=128.0, type=float, help='PNG file will be square and this resolution') | |
parser.add_argument('--symbol', default="applelogo", help='Name of the SF Symbol') | |
parser.add_argument('--result', default=None, help='name of the output file') | |
parser.add_argument('--color', default='#19324B', help='Color for the symbol in HTML format') | |
parser.add_argument('--bgcolor', default=None, help='If present, a round rect of this HTML color will be drawn behind the SF Symbol') | |
parser.add_argument('--print-size', action='store_true', help='If specified, script will print the default size of the symbol and exit.') | |
opts = parser.parse_args() | |
if opts.result == None: | |
filename = opts.symbol+'.png' | |
else: | |
filename = os.path.abspath(os.path.expanduser(opts.result)) | |
#make the SF symbol NSImage and apply color | |
r,g,b = hex_to_rgb(opts.color) | |
imagesf = Cocoa.NSImage.imageWithSystemSymbolName_accessibilityDescription_(opts.symbol, None) | |
sfconfig = Cocoa.NSImageSymbolConfiguration.configurationWithHierarchicalColor_(Cocoa.NSColor.colorWithRed_green_blue_alpha_(r/255.0, g/255.0, b/255.0, 1.0)) | |
image = imagesf.imageWithSymbolConfiguration_(sfconfig) | |
sfsymbolsize = image.size() | |
if opts.print_size: | |
print(f'Size of {opts.symbol} is width={sfsymbolsize.width} height={sfsymbolsize.height}') | |
sys.exit(0) | |
#create the bitmap context | |
bmrep = Cocoa.NSBitmapImageRep.alloc() | |
bmrep.initWithBitmapDataPlanes_pixelsWide_pixelsHigh_bitsPerSample_samplesPerPixel_hasAlpha_isPlanar_colorSpaceName_bytesPerRow_bitsPerPixel_(None, opts.size, opts.size, 8, 4, Cocoa.YES, Cocoa.NO, Cocoa.NSCalibratedRGBColorSpace, 0, 0) | |
gfxctx = Cocoa.NSGraphicsContext.graphicsContextWithBitmapImageRep_(bmrep) | |
Cocoa.NSGraphicsContext.setCurrentContext_(gfxctx) | |
#figure out how big to make the symbol | |
nw = opts.size | |
nh = opts.size | |
x = 0.0 | |
y = 0.0 | |
if sfsymbolsize.width > sfsymbolsize.height: | |
#wider than tall | |
scale = opts.size / sfsymbolsize.width | |
nh = sfsymbolsize.height * scale | |
y = (opts.size - nh) / 2.0 | |
elif sfsymbolsize.height > sfsymbolsize.width: | |
#taller than wide | |
scale = opts.size / sfsymbolsize.height | |
nw = sfsymbolsize.width * scale | |
x = (opts.size - nw) / 2.0 | |
#if there's a background color, draw a rounded rect for a stream deck button | |
if opts.bgcolor != None: | |
r,g,b = hex_to_rgb(opts.bgcolor) | |
color = Cocoa.NSColor.colorWithRed_green_blue_alpha_(r/255.0, g/255.0, b/255.0, 1.0) | |
color.set() | |
bb = Cocoa.NSMakeRect(0.0, 0.0, opts.size, opts.size) | |
bp = Cocoa.NSBezierPath.bezierPathWithRoundedRect_xRadius_yRadius_(bb, opts.size * 0.1, opts.size * 0.1) | |
bp.fill() | |
#draw the symbol | |
r = Cocoa.NSMakeRect(x, y, nw, nh) | |
image.drawInRect_fromRect_operation_fraction_(r, Cocoa.NSZeroRect, Cocoa.NSCompositingOperationSourceOver, 1.0) | |
#write the output file | |
pngData = bmrep.representationUsingType_properties_(Cocoa.NSBitmapImageFileTypePNG, {}) | |
pngData.writeToFile_atomically_(filename, Cocoa.YES) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment