Skip to content

Instantly share code, notes, and snippets.

@kirelagin
Last active July 19, 2020 04:41
Show Gist options
  • Save kirelagin/fe832d1c943c195d1ca527cd50293996 to your computer and use it in GitHub Desktop.
Save kirelagin/fe832d1c943c195d1ca527cd50293996 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
# SPDX-FileCopyrightText: 2020 Kirill Elagin <https://kir.elagin.me/>
# SPDX-License-Identifier: MPL-2.0
###
#
# Convert a Noun Project icon to a PNG of the right size.
#
# This simple tool takes a free “basic” SVG icon from The Noun Project
# <https://thenounproject.com/> and converts it to a PNG of the right size
# to be used in various services.
#
# To be more specific, before converting it to PNG, it resizes the canvas to
# be a square (by trimming the bottom part). In particular, this results in the
# removal of the attribution watermark.
#
# Therefore, YOU CANNOT REDISTRIBUTE THE RESULTING ICON. You can only use it for
# your own private purposes, e.g. in your private Notion workspace.
#
# Use:
#
# * Download this script and put it on your $PATH
# (or use its full path when invoking it)
#
# * Run:
#
# ```
# $ noun.py <desired_size> <path_to_svg>
# ```
#
# For example, download an SVG to ~/Downloads/noun_Duck.svg
# and then execute:
#
# ```
# $ ./noun.py Notion ~/Downloads/noun_Duck.svg
# Writing output to `/Users/username/Downloads/noun_Duck.png`
# ```
#
# Dependencies:
#
# * Python 3
# * CairoSVG
#
# e.g. `nix-shell -p 'python3.withPackages (pp: [pp.cairosvg])'`
#
# https://gist.github.com/kirelagin/fe832d1c943c195d1ca527cd50293996
###
import argparse
from cairosvg import svg2png
import os
from os.path import splitext
import xml.etree.ElementTree as ET
known_sizes = {
# This is recommended by Notion <https://www.notion.so/Page-icons-covers-be694b07c6284ee3800bd71dde495981#7289827d98af4d2fae028c405a89e1e1>
'Notion': (280, 280),
# Slack does not give any specific recommendations, but the internet seems to say so.
'Slack': (128, 128),
}
def output_size(size):
known = known_sizes.get(size)
if known is not None:
return known
else:
try:
(wstr, hstr) = size.split('x')
(w, h) = (int(wstr), int(hstr))
return (w, h)
except:
raise argparse.ArgumentTypeError('Invalid output size')
parser = argparse.ArgumentParser(description='Process a Noun Project icon')
parser.add_argument('desired_size', type=output_size,
help='desired resulting size: either WIDTHxHEIGHT or one of ' + ', '.join(known_sizes))
parser.add_argument('svg_file', type=argparse.FileType('r'),
help='path to the source icon svg (or `-` for stdin)')
if __name__ == '__main__':
args = parser.parse_args()
(width, height) = args.desired_size
tree = ET.parse(args.svg_file)
root = tree.getroot()
# Trim the image to make it square (and remove the whatermark)
# DO NOT REDISTRIBUTE THIS IMAGE WITHOUT THE WHATERMARK
# YOU CAN ONLY USE IT FOR YOUR OWN PRIVATE PURPOSES
bgattr = root.get('enable-background')
root.set('viewBox', bgattr[bgattr.find(' ') + 1:])
svg_fname = args.svg_file.name
if svg_fname == '<stdin>':
svg_fname = 'stdin.svg'
png_fname = os.path.splitext(svg_fname)[0] + '.png'
print(f'Writing output to `{png_fname}`')
svg2png(bytestring=ET.tostring(root), write_to=png_fname,
parent_width=width, parent_height=height)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment