forked from qt-creator/qt-creator
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:
@@ -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
|
|
||||||
inkscapeExecutable = spawn.find_executable("inkscape")
|
|
||||||
if not inkscapeExecutable:
|
|
||||||
sys.stderr.write("Inkscape was not found in PATH\n")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
|
def svgIDs(svgFile, svgElementFilter):
|
||||||
# The svg element IDs of images to export. They correspond to the
|
# The svg element IDs of images to export. They correspond to the
|
||||||
# path and base name of each image in the Qt Creator sources.
|
# path and base name of each image in the Qt Creator sources.
|
||||||
svgIDs = []
|
svgIDs = []
|
||||||
svgTree = ET.ElementTree()
|
svgTree = ET.ElementTree()
|
||||||
svgTree.parse(scriptDir + "qtcreatoricons.svg")
|
svgTree.parse(os.path.join(qtcRoot(), svgFile))
|
||||||
svgTreeRoot = svgTree.getroot()
|
svgTreeRoot = svgTree.getroot()
|
||||||
|
pattern = re.compile(svgElementFilter)
|
||||||
for svgElement in svgTreeRoot.iter():
|
for svgElement in svgTreeRoot.iter():
|
||||||
try:
|
try:
|
||||||
svgElementID = svgElement.attrib['id']
|
svgElementID = svgElement.attrib['id']
|
||||||
if svgElementID.startswith(('src/', 'share/')):
|
if '/' in svgElementID and pattern.match(svgElementID):
|
||||||
if svgElementFilter != "":
|
|
||||||
pattern = re.compile(svgElementFilter)
|
|
||||||
if pattern.match(svgElementID):
|
|
||||||
svgIDs.append(svgElementID)
|
svgIDs.append(svgElementID)
|
||||||
else:
|
except Exception:
|
||||||
svgIDs.append(svgElementID)
|
|
||||||
except:
|
|
||||||
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())
|
||||||
|
Reference in New Issue
Block a user