Warning, file /graphics/glaxnimate/data/plugins/dotLottie/dotlottie.py was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 # SPDX-FileCopyrightText: 2019-2023 Mattia Basaglia <dev@dragon.best> 0002 # SPDX-License-Identifier: GPL-3.0-or-later 0003 import io 0004 import re 0005 import os 0006 import json 0007 import string 0008 import base64 0009 import zipfile 0010 from PIL import Image 0011 0012 import glaxnimate 0013 0014 0015 def asset_data(asset): 0016 if asset["e"]: 0017 m = re.match("data:[^/]+/([^;,]+);base64,(.*)", asset["p"]) 0018 if not m: 0019 return None, None 0020 return m.group(1), base64.b64decode(m.group(2)) 0021 0022 path = os.path.join(asset["u"], asset["p"]) 0023 if not os.path.isfile(path): 0024 return None, None 0025 0026 with open(path, "rb") as imgfile: 0027 return os.path.splitext(path)[1][1:], imgfile.read() 0028 0029 0030 def save_dotlottie(window, document, file, fname, import_export, settings): 0031 files = {} 0032 0033 if settings["append"]: 0034 with zipfile.ZipFile(fname, "r") as zf: 0035 with zf.open("manifest.json") as manifest: 0036 meta = json.load(manifest) 0037 meta["custom"].update(document.metadata) 0038 0039 for name in zf.namelist(): 0040 if name != "manifest.json": 0041 files[name] = zf.read(name) 0042 else: 0043 meta = { 0044 "generator": "Glaxnimate " + glaxnimate.__version__, 0045 "version": 1.0, 0046 "revision": 1, 0047 "author": str(document.metadata.get("author", "")), 0048 "animations": [], 0049 "custom": document.metadata, 0050 "author": document.info.author, 0051 } 0052 0053 try: 0054 meta["revision"] = int(document.metadata["revision"]) 0055 except (KeyError, ValueError): 0056 pass 0057 0058 id = settings["id"] 0059 if not id: 0060 if document.main.name: 0061 idok = string.ascii_letters + string.digits + "_-" 0062 id = "".join(filter(lambda x: x in idok, document.main.name.replace(" ", "_"))) 0063 if not id: 0064 id = "animation_%s" % len(meta["animations"]) 0065 0066 meta["animations"].append({ 0067 "id": id, 0068 "speed": settings["speed"], 0069 "themeColor": settings["theme_color"], 0070 "loop": settings["loop"], 0071 }) 0072 0073 lottie_str = glaxnimate.io.registry["lottie"].save(document) 0074 lottie_dict = json.loads(lottie_str) 0075 0076 if settings["pack_assets"]: 0077 image_no = 0 0078 for asset in lottie_dict.get("assets", []): 0079 if "e" not in asset or "p" not in asset: 0080 continue 0081 0082 ext, data = asset_data(asset) 0083 if not ext: 0084 continue 0085 0086 pathname = "images/" 0087 while True: 0088 basename = "image_%s.%s" % (image_no, ext) 0089 image_no += 1 0090 if pathname+basename not in files: 0091 break 0092 files[pathname+basename] = data 0093 asset["u"] = pathname 0094 asset["p"] = basename 0095 asset["e"] = 0 0096 0097 files["manifest.json"] = json.dumps(meta) 0098 files["animations/%s.json" % id] = json.dumps(lottie_dict) 0099 0100 with open(fname, "wb") as fd: 0101 with zipfile.ZipFile(fd, "w") as zf: 0102 for name, data in files.items(): 0103 zf.writestr(name, data) 0104 0105 0106 def load_asset(asset, file): 0107 image = Image.open(file) 0108 asset["u"] = "" 0109 asset["p"] = "" 0110 format = (image.format or "png").lower() 0111 asset["w"], asset["h"] = image.size 0112 output = io.BytesIO() 0113 image.save(output, format=format) 0114 asset["p"] = "data:image/%s;base64,%s" % ( 0115 format, 0116 base64.b64encode(output.getvalue()).decode("ascii") 0117 ) 0118 asset["e"] = 1 0119 0120 def open_dotlottie(window, document, file, fname, import_export, settings): 0121 file.open("rb") 0122 with zipfile.ZipFile(file, "r") as zf: 0123 with zf.open("manifest.json") as manifest: 0124 meta = json.load(manifest) 0125 0126 animations = {} 0127 ids = {} 0128 for anim in meta["animations"]: 0129 anim_id = anim["id"] 0130 info = zf.getinfo("animations/%s.json" % anim_id) 0131 with zf.open(info) as animfile: 0132 lottie = json.load(animfile) 0133 0134 animations[anim_id] = lottie 0135 ids[lottie.get("nm", anim_id)] = anim_id 0136 0137 chosen_id = None 0138 if len(meta["animations"]) > 1: 0139 chosen_id = window.choose_option("Animation", ids, "Open dotLottie") 0140 if chosen_id is None: 0141 chosen_id = meta["animations"][0]["id"] 0142 0143 lottie = animations[chosen_id] 0144 0145 for asset in lottie.get("assets", []): 0146 # .lottie is very werid on how it handles assets so we can't 0147 # rely on `e` or `p`... 0148 if asset.get("p", "").startswith("data:"): 0149 continue 0150 0151 asset_fname = os.path.join(asset["u"], asset["p"]) 0152 if asset_fname not in zf.namelist() and "images/" + asset_fname in zf.namelist(): 0153 asset_fname = "images/" + asset_fname 0154 0155 if asset_fname in zf.namelist(): 0156 with zf.open(asset_fname) as asset_file: 0157 load_asset(asset, asset_file) 0158 0159 glaxnimate.io.registry["lottie"].load(document, json.dumps(lottie).encode("utf-8")) 0160 0161 custom_meta = meta.get("custom", {}) 0162 if "author" in meta: 0163 custom_meta["author"] = meta["author"] 0164 if "revision" in meta: 0165 custom_meta["revision"] = meta["revision"] 0166 document.metadata = custom_meta