Icons: Adapt export.py to Inkscape 1.0 (+cleanup)

The "--shell" mode changed in Inkscape from version 0.9 to 1.0, so that
the export.py had to be adapted.

While doing that, it was also time to split up this Spaghetti pile
into functions.

Change-Id: I6346a6eb9b4be546333ee60fa1d34c2ba8e1a347
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
Alessandro Portale
2020-06-05 18:41:02 +02:00
parent b978af12ae
commit 00bb6ea958

View File

@@ -2,7 +2,7 @@
############################################################################ ############################################################################
# #
# Copyright (C) 2016 The Qt Company Ltd. # Copyright (C) 2020 The Qt Company Ltd.
# Contact: https://www.qt.io/licensing/ # Contact: https://www.qt.io/licensing/
# #
# This file is part of Qt Creator. # This file is part of Qt Creator.
@@ -27,70 +27,157 @@
# This script calls Inkscape to rasterize several images into png files. # This script calls Inkscape to rasterize several images into png files.
# The images end up in the final position of the source tree. # The images end up in the final position of the source tree.
# Each images is generated as normal and high resolution variant. # Each image is generated as normal and high resolution variant.
# Each png file is afterwards optimized with optipng. # Each png file is afterwards optimized with optipng.
import sys, os, subprocess, re, xml.etree.ElementTree as ET import argparse
import os
import re
import subprocess
import sys
import xml.etree.ElementTree as ET
from distutils import spawn from distutils import spawn
scriptDir = os.path.dirname(os.path.abspath(sys.argv[0])) + '/'
# QTC_SRC is expected to point to the source root of Qt Creator def qtcRoot():
qtcSourceRoot = os.getenv('QTC_SRC', os.path.abspath(scriptDir + '../../..')) \ return os.path.abspath(
.replace('\\', '/') + '/' os.path.join(os.path.dirname(sys.argv[0]), '../../..')).replace('\\', '/')
svgElementFilter = ""
if len(sys.argv) > 1:
svgElementFilter = sys.argv[1]
# Inkscape is required by this script def svgIDs(svgFile, svgElementFilter):
inkscapeExecutable = spawn.find_executable("inkscape") # The svg element IDs of images to export. They correspond to the
if not inkscapeExecutable: # path and base name of each image in the Qt Creator sources.
sys.stderr.write("Inkscape was not found in PATH\n") svgIDs = []
sys.exit(1) svgTree = ET.ElementTree()
svgTree.parse(os.path.join(qtcRoot(), svgFile))
# The svg element IDs of images to export. They correspond to the svgTreeRoot = svgTree.getroot()
# path and base name of each image in the Qt Creator sources. pattern = re.compile(svgElementFilter)
svgIDs = [] for svgElement in svgTreeRoot.iter():
svgTree = ET.ElementTree() try:
svgTree.parse(scriptDir + "qtcreatoricons.svg") svgElementID = svgElement.attrib['id']
svgTreeRoot = svgTree.getroot() if '/' in svgElementID and pattern.match(svgElementID):
for svgElement in svgTreeRoot.iter():
try:
svgElementID = svgElement.attrib['id']
if svgElementID.startswith(('src/', 'share/')):
if svgElementFilter != "":
pattern = re.compile(svgElementFilter)
if pattern.match(svgElementID):
svgIDs.append(svgElementID)
else:
svgIDs.append(svgElementID) svgIDs.append(svgElementID)
except: except Exception:
pass pass
for id in svgIDs: print("\n==== {} elements found which match {}"
pngFile = qtcSourceRoot + id + ".png" .format(len(svgIDs), svgElementFilter))
pngAt2XFile = qtcSourceRoot + id + "@2x.png" return svgIDs
if not (os.path.isfile(pngFile) or os.path.isfile(pngAt2XFile)):
sys.stderr.write(id + " has not yet been exported as .png.\n")
# The shell mode of Inkscape is used to execute several export commands
# with one launch of Inkscape.
inkscapeShellCommands = ""
pngFiles = []
for id in svgIDs:
for scale in [1, 2]:
pngFile = qtcSourceRoot + id + ("" if scale is 1 else "@%dx" % scale) + ".png"
pngFiles.append(pngFile)
inkscapeShellCommands += "qtcreatoricons.svg --export-id=" + id + " --export-id-only --export-png=" + pngFile + " --export-dpi=%d\n" % (scale * 96)
inkscapeShellCommands += "quit\n"
inkscapeProcess = subprocess.Popen(['inkscape', '--shell'], stdin=subprocess.PIPE, shell=True, cwd=scriptDir)
inkscapeProcess.communicate(input=inkscapeShellCommands.encode())
# Optimizing pngs via optipng def pngName(svgID, scale):
optipngExecutable = spawn.find_executable("optipng") # File name is relative to qtcRoot()
if not optipngExecutable: return svgID + ("" if scale == 1 else "@{}x".format(scale)) + ".png"
sys.stderr.write("optipng was not found in PATH. Please do not push the unoptimized .pngs to the main repository.\n")
else:
for pngFile in pngFiles: def checkDirectories(svgIDs):
subprocess.call(["optipng", "-o7", "-strip", "all", pngFile]) invalidDirectories = []
for id in svgIDs:
if not os.path.isdir(os.path.join(qtcRoot(), id, '../')):
invalidDirectories.append(id)
if invalidDirectories:
print("\n==== {} IDs for which the output directory is missing:"
.format(len(invalidDirectories)))
print("\n".join(invalidDirectories))
sys.exit("Output directories are missing.")
def printOutUnexported(svgIDs, scaleFactors):
unexported = []
partiallyExported = []
for id in svgIDs:
exportedCount = 0
for scaleFactor in scaleFactors:
if os.path.isfile(os.path.join(qtcRoot(), pngName(id, scaleFactor))):
exportedCount += 1
if exportedCount == 0:
unexported.append(id)
elif (exportedCount < len(scaleFactors)):
partiallyExported.append(id)
if partiallyExported:
print("\n==== {} IDs for which not each .png is exported:"
.format(len(partiallyExported)))
print("\n".join(partiallyExported))
if unexported:
print("\n==== {} IDs for which all .pngs are missing:"
.format(len(unexported)))
print("\n".join(unexported))
if partiallyExported or unexported:
input("\nPress Enter to continue...")
def exportPngs(svgIDs, svgFile, scaleFactors, inkscape):
inkscapeProcess = subprocess.Popen([inkscape, '--shell'],
stdin=subprocess.PIPE,
cwd=qtcRoot())
actions = ["file-open:" + svgFile]
for id in svgIDs:
for scale in scaleFactors:
actions += [
"export-id:{}".format(id),
"export-id-only",
"export-dpi:{}".format(scale * 96),
"export-filename:{}".format(pngName(id, scale)),
"export-do"
]
actions += ["quit-inkscape"]
actionLine = "; ".join(actions) + "\n"
print("Exporting pngs for {} Ids in {} scale factors."
.format(len(svgIDs), len(scaleFactors)))
inkscapeProcess.communicate(input=actionLine.encode())
def optimizePngs(svgIDs, scaleFactors, optipng):
for id in svgIDs:
for scale in scaleFactors:
png = pngName(id, scale)
print("Optimizing: {}".format(png))
try:
subprocess.check_call([optipng,
"-o7",
"-strip", "all",
png],
stderr=subprocess.DEVNULL,
cwd=qtcRoot())
except subprocess.CalledProcessError:
sys.exit("Failed to optimize {}.".format(png))
def export(svgFile, filter, scaleFactors, inkscape, optipng):
ids = svgIDs(svgFile, filter)
if not ids:
sys.exit("{} does not match any Id.".format(filter))
checkDirectories(ids)
printOutUnexported(ids, scaleFactors)
exportPngs(ids, svgFile, scaleFactors, inkscape)
optimizePngs(ids, scaleFactors, optipng)
def main():
parser = argparse.ArgumentParser(description='Export svg elements to .png '
'files and optimize the png. '
'Requires Inkscape 1.x and optipng in Path.')
parser.add_argument('filter',
help='a RegExp filter for svg element Ids, e.g.: .*device.*')
args = parser.parse_args()
inkscape = spawn.find_executable("inkscape")
if inkscape is None:
sys.exit("Inkscape was not found in Path.")
optipng = spawn.find_executable("optipng")
if optipng is None:
sys.exit("Optipng was not found in Path.")
export("src/tools/icons/qtcreatoricons.svg", args.filter, [1, 2],
inkscape, optipng)
return 0
if __name__ == '__main__':
sys.exit(main())