Created
July 22, 2020 20:29
-
-
Save dreness/6a23ef1d7183c7b98cee3978c9b5facc to your computer and use it in GitHub Desktop.
Export Keynote shapes to SVG
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 | |
# -*- coding: utf-8 -*- | |
import json # Native JSON Library | |
import argparse # Native Argument parser Library | |
# This is a slightly updated version of a script found on ye cyberwebs to | |
# add batch conversion support. Just don't specify an image ID to export the world. | |
# ==============================# | |
# IMPORTANT SCRIPT INFORMATION # | |
# ==============================# | |
SCRIPT_VERSION = "%(prog)s 1.0 (20w22a)" | |
SCRIPT_CREATOR = "donemanuelvuckovic@gmail.com" | |
SCRIPT_DESCRIPTION = """iShapeParser is a script to parse (and convert) () Apple's Keynote shape_library into useful svg files. | |
Usage: | |
To view all possible shapes: | |
python3 iShapeParser.py | |
To export the found shape using default svg options in the same dir where the script is with the name of the shape: | |
python3 iShapeParser.py -i <ID> -gs | |
I take no responsibility if something breaks while this script is being used. There is no warranty, you can use it as is etc. | |
Also feel free to copy, edit and share this script as long as you attribute me :) | |
""" | |
SCRIPT_EPILOG = f"© Don Vuckovic 2020. Contact {SCRIPT_CREATOR} for help!" | |
# ===================================# | |
# A R G U M E N T P A R S E R # | |
# ===================================# | |
parser = argparse.ArgumentParser( | |
formatter_class=argparse.RawDescriptionHelpFormatter, | |
description=SCRIPT_DESCRIPTION, | |
epilog=SCRIPT_EPILOG, | |
) | |
parser.add_argument("--version", action="version", version=SCRIPT_VERSION) | |
parser.add_argument( | |
"-d", | |
"--debug", | |
help="Use this for debugging, max. log level = 5", | |
action="count", | |
default=0, | |
) | |
parser.add_argument( | |
"-f", | |
"--file", | |
help="Which file to use for json shape extracting", | |
default="shape_library.json", | |
) | |
parser.add_argument( | |
"-i", "--id", help="Specify if you want a specific shape ID to be extracted only" | |
) | |
parser.add_argument( | |
"-g", "--generate", help="When active a svg will be generated", action="store_true" | |
) | |
parser.add_argument( | |
"-s", | |
"--save", | |
help="If a svg was generated, use this to save it to a .svg file", | |
action="store_true", | |
) | |
parser.add_argument("-p", "--path", help="Change the path of the save directory") | |
parser.add_argument("-n", "--name", help="Change the name of the exported svg file") | |
# SVG SPECIFIC VALUES (Change at own risk) | |
parser.add_argument( | |
"-w", | |
"--width", | |
help="Specify the width of the svg viewBox (Changing this can break the svg!) Default: 700.", | |
default=700, | |
) | |
parser.add_argument( | |
"-t", | |
"--height", # t is for tallness as h is already used for help! | |
help="Specify the width of the svg viewBox (Changing this can break the svg!) Default: 500.", | |
default=500, | |
) | |
parser.add_argument( | |
"-x", | |
"--xpos", | |
help="Specify the x of the svg file (Changing this can break the svg!) Default: 0.", | |
default=0, | |
) | |
parser.add_argument( | |
"-y", | |
"--ypos", | |
help="Specify the y of the svg file (Changing this can break the svg!) Default: 0.", | |
default=0, | |
) | |
parser.add_argument( | |
"-a", | |
"--minx", | |
help="Specify the min-x of the svg viewBox (Changing this can break the svg!) Default: 50.", | |
default=50, | |
) | |
parser.add_argument( | |
"-b", | |
"--miny", | |
help="Specify the min-y of the svg viewBox (Changing this can break the svg!) Default: 50.", | |
default=50, | |
) | |
parser.add_argument( | |
"-l", "--layer", help="Specify the layer name. Default: Layer_1.", default="Layer_1" | |
) | |
args = parser.parse_args() | |
# ===================# | |
# F u n c t i o n s # | |
# ===================# | |
# Simple function to print according to the debug level | |
def log(msg, level=3): | |
if args.debug >= level: | |
print(f"LEVEL_{level}: {msg}") | |
# Simple function to throw an error and exit | |
def throw(msg): | |
raise Exception(msg) from None | |
sys.exit() | |
def translateKeynoteVectorToSVG(SHAPEDATA, args): | |
svg = f"""<?xml version="1.0" encoding="utf-8"?> | |
<!-- Generated SVG File by Don Vuckovic's iShapeParser.py! Path taken from () Apple's Keynote shape_library.json --> | |
<svg id="{args.layer}" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="{args.xpos}px" y="{args.ypos}px" | |
viewBox="{args.minx} {args.miny} {args.width} {args.height}" xml:space="preserve"> | |
<g> | |
<path d=" | |
{SHAPEDATA} | |
"/> | |
</g> | |
</svg>""" | |
return svg | |
def saveSVG(svg, SHAPENAME, args): | |
savedir = "" | |
savename = SHAPENAME | |
if args.path is not None: | |
savedir = args.path + "/" | |
if args.name is not None: | |
savename = args.name | |
# some of the localized names may contain special characters... | |
savename = savename.replace("/", "-") | |
outpath = f"{savedir}{savename}.svg" | |
print(f"Creating svg file at {outpath}") | |
f = open(f"{outpath}", "w") | |
f.write(svg) | |
f.close() | |
print(f"Shape successfully exported to {outpath}.svg") | |
# ========================================================================================================# | |
# SCRIPT STARTS HERE - SCRIPT STARTS HERE - SCRIPT STARTS HERE - SCRIPT STARTS HERE - SCRIPT STARTS HERE # | |
# ========================================================================================================# | |
log(f"DEBUG LVL = {args.debug}", 1) | |
log(f"File to use = {args.file}", 1) | |
# Try to open the json file, exit on fail | |
try: | |
with open(args.file, "r") as read_file: | |
log("LOADING FILE...", 1) | |
data = json.load(read_file) | |
log("FILE LOADED!") | |
except: | |
throw("Unable to open the provided file!") | |
# Check the versionID, do this to make sure that you most probabbly have the correct file! | |
try: | |
versionid = data["versionID"] | |
except: | |
throw("No versionID found within the provided file! Wrong format!") | |
# Check if the versionID is the same as the one when the script was initially created! | |
# For now do this only to show a warning in case Apple changes formatting. | |
# TODO: save this to a variable, and if the script fails warn that it might be due to a version mismatch! | |
if data["versionID"] != "S3988C2995": | |
print( | |
"Version ID not the same as expected, please report this to %s" % SCRIPT_CREATOR | |
) | |
# If the shape ID is provided, get it's values | |
if args.id is not None: | |
# Try to get the shape name, exit on fail | |
try: | |
SHAPENAME = data["shapesByID"][args.id]["localizationKey"] | |
except: | |
throw("Shape ID probabbly doesn't exist!") | |
# Try to get the shape data, exit on fail | |
try: | |
SHAPEDATA = data["shapesByID"][args.id]["shapePath"] | |
except: | |
throw("Shape ID probabbly doesn't exist!") | |
# Print the shape data | |
print( | |
f""" | |
NAME = {SHAPENAME} | |
PATH | |
------------------------------------------------ | |
{SHAPEDATA} | |
""" | |
) | |
# If the generate argument is passed, generate the SVG format data | |
if args.generate: | |
svg = translateKeynoteVectorToSVG(SHAPEDATA, args) | |
# Print the generated SVG format data | |
print("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -") | |
print(svg) | |
print("- - - - - - - - - - - - - - - - - - - - - - - - - - - - - -") | |
# If the save argument is passed, save the generated SVG format data into the SVG file | |
if args.save: | |
saveSVG(svg, SHAPENAME, args) | |
# Save the SVG as a .svg file, exit on fail | |
# If the save argument is passed, but no svg has been generated explain the impossibility | |
elif args.save: | |
print( | |
"Please use -g/--generate with -s/--save as well to generate the file to save first!" | |
) | |
# If no shape id is provided make a list of all possible shapes | |
else: | |
# Try to get the shape list, exit on fail | |
try: | |
SHAPELIST = data["shapesByID"].keys() | |
except: | |
throw( | |
"I'm not even sure how you got here. Please report this to %s" | |
% SCRIPT_CREATOR | |
) | |
SHAPECOUNT = len(SHAPELIST) | |
# Print all shapes found | |
for key in SHAPELIST: | |
print(f"""{key} = {data["shapesByID"][key]["localizationKey"]}""") | |
print(f"Found {SHAPECOUNT} shapes.") | |
# If the generate argument is passed, generate everything | |
if args.generate: | |
print("Generating everything!") | |
for shapeID in SHAPELIST: | |
SHAPEDATA = data["shapesByID"][shapeID]["shapePath"] | |
SHAPENAME = data["shapesByID"][shapeID]["localizationKey"] | |
svg = translateKeynoteVectorToSVG(SHAPEDATA, args) | |
saveSVG(svg, SHAPENAME, args) | |
# If the save argument is passed, but no ID has been provided explain the impossibility | |
if args.save: | |
print( | |
"Unable to save a svg file without the -i/--id and -g/--generate arguments!" | |
) | |
# The end :) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Here's a better one that exports all built-in shapes as well as anything saved to the "my shapes" collection : https://gist.github.com/dreness/b5ebd1ebff092b124d343164c6217a98