Add SVG support

master
kirbylife 2024-04-02 01:15:59 -06:00
parent bb13ef5937
commit d9792187fd
2 changed files with 46 additions and 56 deletions

View File

@ -1,5 +1,7 @@
libsailfishapp-launcher libsailfishapp-launcher
sailfish-svg2png
pyotherside-qml-plugin-python3-qt5 pyotherside-qml-plugin-python3-qt5
python3-base python3-base
python3-imaging python3-imaging
python3-requests python3-requests
python3-lxml

View File

@ -4,34 +4,59 @@ from PIL.Image import Image as ImageType
from io import BytesIO from io import BytesIO
from glob import glob from glob import glob
from collections import Counter from collections import Counter
from subprocess import getstatusoutput
from urllib.parse import urlparse
from functools import lru_cache
import requests import requests
import base64 import base64
import os import os
import shutil import shutil
import logging import logging
import tempfile
ICON_SIZE = (256, 256)
logging_filename = os.path.join(os.path.expanduser("~"), logging_filename = os.path.join(os.path.expanduser("~"),
".local", ".local",
"share", "share",
"org.kirbylife", "dev.kirbylife",
"harbour-muchkin", "harbour-muchkin",
"muchkin.log") "muchkin.log")
os.makedirs(os.path.dirname(logging_filename), exist_ok=True) os.makedirs(os.path.dirname(logging_filename), exist_ok=True)
logging.basicConfig(filename=logging_filename, filemode="w", level=logging.DEBUG) logging.basicConfig(filename=logging_filename, filemode="w", level=logging.DEBUG)
@lru_cache(maxsize=50)
def download_icon(url) -> ImageType: def download_icon(url) -> ImageType:
response = requests.get(url) response = requests.get(url)
img = Image.open(BytesIO(response.content)) img = Image.open(BytesIO(response.content))
img.thumbnail(ICON_SIZE)
img = img.convert("RGBA") img = img.convert("RGBA")
return img return img
@lru_cache(maxsize=50)
def base64_to_img(base64_bytes: bytes) -> ImageType: def base64_to_img(base64_bytes: bytes) -> ImageType:
img_bytes = base64.b64decode(base64_bytes) img_bytes = base64.b64decode(base64_bytes)
img_buffer = BytesIO(img_bytes) img_buffer = BytesIO(img_bytes)
img = Image.open(img_buffer) img = Image.open(img_buffer)
img.thumbnail(ICON_SIZE)
img = img.convert("RGBA") img = img.convert("RGBA")
return img return img
@lru_cache(maxsize=50)
def svg_to_img(svg_url: str) -> ImageType:
response = requests.get(svg_url)
with tempfile.TemporaryDirectory() as d:
with open(os.path.join(d, "icon.svg"), "w") as f:
f.write(response.text)
status, output = getstatusoutput(f"sailfish_svg2png -z 16 {d} {d}")
logging.info("Converted svg to png")
logging.info(f"sailfish_svg2png status {status}: {output}")
img = Image.open(os.path.join(d, "icon.png"))
img.thumbnail(ICON_SIZE)
img = img.convert("RGBA")
return img
def avg_colors(img): def avg_colors(img):
counter = Counter() counter = Counter()
width, height = img.size width, height = img.size
@ -42,9 +67,12 @@ def avg_colors(img):
counter[pixel] += 1 counter[pixel] += 1
return counter.most_common(1)[0][0] return counter.most_common(1)[0][0]
def add_background(img: ImageType, bg_tuple: tuple) -> ImageType: def add_background(img: ImageType, bg_tuple: tuple, size: float) -> ImageType:
img = img.copy()
size = size / 100
bg = Image.new("RGBA", img.size, bg_tuple) bg = Image.new("RGBA", img.size, bg_tuple)
bg.paste(img, (0, 0), img) img.thumbnail((value * size for value in img.size))
bg.paste(img, ((bg.size[0] - img.size[0]) // 2, (bg.size[1] - img.size[1]) // 2), img)
return bg return bg
def crop_to_circle(im, template): def crop_to_circle(im, template):
@ -70,9 +98,7 @@ def parse_file(desktop_path):
output = {} output = {}
with open(desktop_path, "r") as f: with open(desktop_path, "r") as f:
for line in f: for line in f:
if not line: if not line or line.startswith("["):
continue
if line.startswith("["):
continue continue
key, value = line.split("=", 1) key, value = line.split("=", 1)
output[key] = value.strip() output[key] = value.strip()
@ -97,21 +123,23 @@ def get_web_icons():
return list(map(parse_file, apps)) return list(map(parse_file, apps))
def sailify(raw_str, pattern): def sailify(raw_str, pattern, size) -> str:
try: url_parsed = urlparse(raw_str)
# New versions of Sailfish has the url on the icons if url_parsed.scheme == "data":
img = download_icon(raw_str)
logging.info("Downloaded from url: " + raw_str)
except requests.exceptions.InvalidSchema:
# The old ones converted the icon to base64 # The old ones converted the icon to base64
base64_bytes = raw_str.replace("data:image/png;base64,", "", 1).encode() base64_bytes = raw_str.replace("data:image/png;base64,", "", 1).encode()
img = base64_to_img(base64_bytes) img = base64_to_img(base64_bytes)
logging.info("Converted from base64") logging.info("Converted from base64")
except Exception as e: # New versions of Sailfish has the url on the icons
logging.error(e) elif url_parsed.path.split("/")[-1].split(".")[-1].upper() == "SVG":
raise e img = svg_to_img(raw_str)
logging.info("Downloaded from url: " + raw_str)
else:
img = download_icon(raw_str)
logging.info("Downloaded from url: " + raw_str)
bg_tuple = avg_colors(img) bg_tuple = avg_colors(img)
img = add_background(img, bg_tuple) img = add_background(img, bg_tuple, size)
img = crop_to_circle(img, pattern) img = crop_to_circle(img, pattern)
output_buffer = BytesIO() output_buffer = BytesIO()
img.save(output_buffer, format="PNG") img.save(output_buffer, format="PNG")
@ -133,43 +161,3 @@ def save_icon(app):
f.write(new_content) f.write(new_content)
logging.info("Icon saved on: " + app["path"]) logging.info("Icon saved on: " + app["path"])
return True return True
def main():
import shutil
while True:
apps = get_web_icons()
while True:
for n, app in enumerate(apps):
print(f"{n}) {app['Name']}")
index = input("select an application (the index):\n>>> ")
index = int(index)
if index >= len(apps):
print("Select a valid index")
continue
break
app = apps[index]
if app["Icon"] == "icon-launcher-bookmark":
print("this webapp is using the generic icon of a bookmark and not a favicon so it is not possible to modify")
continue
while True:
pattern = input("Enter the pattern (0 = round, 1 = peak):\n>>> ")
if len(pattern) != 4:
print("it is necessary to enter 4 characters")
pattern = tuple(map(int, pattern))
new_icon = sailify(app.get("old_icon", app["Icon"]), pattern)
# Backup the original .desktop
shutil.copyfile(app["path"], app["path"] + "_backup")
app["old_icon"] = app.get("old_icon", app["Icon"])
app["Icon"] = new_icon
new_content = deparse_file(app)
with open(app["path"], "w") as f:
f.write(new_content)
print(f"{app['Name']} sailified correctly")
break
if __name__ == "__main__":
main()