diff options
Diffstat (limited to 'src/tools/icons/export.py')
-rw-r--r-- | src/tools/icons/export.py | 213 |
1 files changed, 150 insertions, 63 deletions
diff --git a/src/tools/icons/export.py b/src/tools/icons/export.py index 6943b18fd7..bf8f0baa39 100644 --- a/src/tools/icons/export.py +++ b/src/tools/icons/export.py @@ -2,7 +2,7 @@ ############################################################################ # -# Copyright (C) 2016 The Qt Company Ltd. +# Copyright (C) 2020 The Qt Company Ltd. # Contact: https://www.qt.io/licensing/ # # This file is part of Qt Creator. @@ -27,70 +27,157 @@ # This script calls Inkscape to rasterize several images into png files. # 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. -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 -scriptDir = os.path.dirname(os.path.abspath(sys.argv[0])) + '/' - -# QTC_SRC is expected to point to the source root of Qt Creator -qtcSourceRoot = os.getenv('QTC_SRC', os.path.abspath(scriptDir + '../../..')) \ - .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) - -# The svg element IDs of images to export. They correspond to the -# path and base name of each image in the Qt Creator sources. -svgIDs = [] -svgTree = ET.ElementTree() -svgTree.parse(scriptDir + "qtcreatoricons.svg") -svgTreeRoot = svgTree.getroot() -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: + +def qtcRoot(): + return os.path.abspath( + os.path.join(os.path.dirname(sys.argv[0]), '../../..')).replace('\\', '/') + + +def svgIDs(svgFile, svgElementFilter): + # The svg element IDs of images to export. They correspond to the + # path and base name of each image in the Qt Creator sources. + svgIDs = [] + svgTree = ET.ElementTree() + svgTree.parse(os.path.join(qtcRoot(), svgFile)) + svgTreeRoot = svgTree.getroot() + pattern = re.compile(svgElementFilter) + for svgElement in svgTreeRoot.iter(): + try: + svgElementID = svgElement.attrib['id'] + if '/' in svgElementID and pattern.match(svgElementID): svgIDs.append(svgElementID) - except: - pass - -for id in svgIDs: - pngFile = qtcSourceRoot + id + ".png" - pngAt2XFile = qtcSourceRoot + id + "@2x.png" - 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 -optipngExecutable = spawn.find_executable("optipng") -if not optipngExecutable: - 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: - subprocess.call(["optipng", "-o7", "-strip", "all", pngFile]) + except Exception: + pass + + print("\n==== {} elements found which match {}" + .format(len(svgIDs), svgElementFilter)) + return svgIDs + + +def pngName(svgID, scale): + # File name is relative to qtcRoot() + return svgID + ("" if scale == 1 else "@{}x".format(scale)) + ".png" + + +def checkDirectories(svgIDs): + 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()) |