Auto convert SVG to PNG icons when creating icon theme from folder. Also allow specifying a license for the icon theme

This commit is contained in:
Kovid Goyal 2015-08-20 10:57:12 +05:30
parent a12182694a
commit 4484c2eb58

View File

@ -11,7 +11,7 @@ from io import BytesIO
from PyQt5.Qt import ( from PyQt5.Qt import (
QImageReader, QFormLayout, QVBoxLayout, QSplitter, QGroupBox, QListWidget, QImageReader, QFormLayout, QVBoxLayout, QSplitter, QGroupBox, QListWidget,
QLineEdit, QSpinBox, QTextEdit, QSize, QListWidgetItem, QIcon QLineEdit, QSpinBox, QTextEdit, QSize, QListWidgetItem, QIcon, QImage,
) )
from calibre import walk, fit_image from calibre import walk, fit_image
@ -28,21 +28,35 @@ IMAGE_EXTENSIONS = {'png', 'jpg', 'jpeg'}
THEME_COVER = 'icon-theme-cover.jpg' THEME_COVER = 'icon-theme-cover.jpg'
THEME_METADATA = 'metadata.json' THEME_METADATA = 'metadata.json'
def render_svg(filepath):
must_use_qt(headless=False)
pngpath = filepath[:-4] + '.png'
i = QImage(filepath)
i.save(pngpath)
def read_images_from_folder(path): def read_images_from_folder(path):
name_map = {} name_map = {}
path = os.path.abspath(path) path = os.path.abspath(path)
for filepath in walk(path): for filepath in walk(path):
name = os.path.relpath(filepath, path).replace(os.sep, '/').lower() name = os.path.relpath(filepath, path).replace(os.sep, '/').lower()
ext = name.rpartition('.')[-1] ext = name.rpartition('.')[-1]
bname = os.path.basename(name)
if bname.startswith('.') or bname.startswith('_'):
continue
if ext == 'svg':
render_svg(filepath)
ext = 'png'
filepath = filepath[:-4] + '.png'
name = name[:-4] + '.png'
if ext in IMAGE_EXTENSIONS: if ext in IMAGE_EXTENSIONS:
name_map[name] = filepath name_map[name] = filepath
return name_map return name_map
class Theme(object): class Theme(object):
def __init__(self, title='', author='', version=-1, description='', cover=None): def __init__(self, title='', author='', version=-1, description='', license='Unknown', cover=None):
self.title, self.author, self.version, self.description = title, author, version, description self.title, self.author, self.version, self.description = title, author, version, description
self.cover = cover self.license, self.cover = license, cover
class Report(object): class Report(object):
@ -50,6 +64,10 @@ class Report(object):
self.path, self.name_map, self.extra, self.missing, self.theme = path, name_map, extra, missing, theme self.path, self.name_map, self.extra, self.missing, self.theme = path, name_map, extra, missing, theme
self.bad = {} self.bad = {}
@property
def name(self):
return ascii_filename(self.theme.title).replace(' ', '_').lower()
def read_theme_from_folder(path): def read_theme_from_folder(path):
path = os.path.abspath(path) path = os.path.abspath(path)
current_image_map = read_images_from_folder(P('images', allow_user_override=False)) current_image_map = read_images_from_folder(P('images', allow_user_override=False))
@ -74,7 +92,8 @@ def read_theme_from_folder(path):
return int(x) return int(x)
except Exception: except Exception:
return -1 return -1
theme = Theme(metadata.get('title', ''), metadata.get('author', ''), safe_int(metadata.get('version', -1)), metadata.get('description', '')) g = lambda x, defval='': metadata.get(x, defval)
theme = Theme(g('title'), g('author'), safe_int(g('version', -1)), g('description'), g('license', 'Unknown'))
ans = Report(path, name_map, extra, missing, theme) ans = Report(path, name_map, extra, missing, theme)
try: try:
@ -176,6 +195,11 @@ class ThemeCreateDialog(Dialog):
self.version = v = QSpinBox(self) self.version = v = QSpinBox(self)
v.setMinimum(1), v.setMaximum(1000000) v.setMinimum(1), v.setMaximum(1000000)
l.addRow(_('&Version:'), v) l.addRow(_('&Version:'), v)
self.license = lc = QLineEdit(self)
l.addRow(_('&License:'), lc)
lc.setText(_(
'The license for the icons in this theme. Common choices are'
' Creative Commons or Public Domain.'))
self.description = QTextEdit(self) self.description = QTextEdit(self)
l.addRow(self.description) l.addRow(self.description)
self.refresh_button = rb = self.bb.addButton(_('&Refresh'), self.bb.ActionRole) self.refresh_button = rb = self.bb.addButton(_('&Refresh'), self.bb.ActionRole)
@ -189,6 +213,7 @@ class ThemeCreateDialog(Dialog):
@property @property
def metadata(self): def metadata(self):
self.report.theme.title = self.title.text().strip() # Needed for report.name to work
return { return {
'title': self.title.text().strip(), 'title': self.title.text().strip(),
'author': self.author.text().strip(), 'author': self.author.text().strip(),
@ -196,6 +221,8 @@ class ThemeCreateDialog(Dialog):
'description': self.description.toPlainText().strip(), 'description': self.description.toPlainText().strip(),
'number': len(self.report.name_map) - len(self.report.extra), 'number': len(self.report.name_map) - len(self.report.extra),
'date': utcnow().date().isoformat(), 'date': utcnow().date().isoformat(),
'name': self.report.name,
'license': self.license.text().strip() or 'Unknown',
} }
def save_metadata(self): def save_metadata(self):
@ -213,6 +240,7 @@ class ThemeCreateDialog(Dialog):
self.author.setText((theme.author or '').strip()) self.author.setText((theme.author or '').strip())
self.version.setValue(theme.version or 1) self.version.setValue(theme.version or 1)
self.description.setText((theme.description or '').strip()) self.description.setText((theme.description or '').strip())
self.license.setText((theme.license or 'Unknown').strip())
if self.report.missing: if self.report.missing:
title = _('%d icons missing in this theme') % len(self.report.missing) title = _('%d icons missing in this theme') % len(self.report.missing)
else: else:
@ -244,13 +272,13 @@ def create_themeball(report):
out = BytesIO() out = BytesIO()
compress(buf, out, level=9) compress(buf, out, level=9)
buf = BytesIO() buf = BytesIO()
prefix = ascii_filename(report.theme.title).replace(' ', '_').lower() prefix = report.name
with ZipFile(buf, 'w') as zf: with ZipFile(buf, 'w') as zf:
with open(os.path.join(report.path, THEME_METADATA), 'rb') as f: with open(os.path.join(report.path, THEME_METADATA), 'rb') as f:
zf.writestr(prefix + '/' + THEME_METADATA, f.read()) zf.writestr(prefix + '/' + THEME_METADATA, f.read())
zf.writestr(prefix + '/' + THEME_COVER, create_cover(report)) zf.writestr(prefix + '/' + THEME_COVER, create_cover(report))
zf.writestr(prefix + '/' + 'icons.zip.xz', out.getvalue(), compression=ZIP_STORED) zf.writestr(prefix + '/' + 'icons.zip.xz', out.getvalue(), compression=ZIP_STORED)
return buf.getvalue() return buf.getvalue(), prefix
def create_theme(folder=None, parent=None): def create_theme(folder=None, parent=None):
@ -264,10 +292,10 @@ def create_theme(folder=None, parent=None):
if d.exec_() != d.Accepted: if d.exec_() != d.Accepted:
return return
d.save_metadata() d.save_metadata()
raw = create_themeball(d.report) raw, prefix = create_themeball(d.report)
dest = choose_save_file(parent, 'create-icon-theme-dest', _( dest = choose_save_file(parent, 'create-icon-theme-dest', _(
'Choose destination for icon theme'), 'Choose destination for icon theme'),
[(_('ZIP files'), ['zip'])], initial_filename='my-calibre-icon-theme.zip') [(_('ZIP files'), ['zip'])], initial_filename=prefix + '.zip')
if dest: if dest:
with open(dest, 'wb') as f: with open(dest, 'wb') as f:
f.write(raw) f.write(raw)