From 6948858ffd8cc9d8a04e9080e40f0d2b307619fc Mon Sep 17 00:00:00 2001 From: DeltaRazero Date: Sat, 4 May 2024 22:32:58 +0200 Subject: [PATCH 1/2] Use product icons instead of 'hardcoded' icons - Add a product icon font instead of icons that are unchangable by product icon themes - Add a script using FontForge scripting API for generating product icon font for future use - Remove redundant icons that are already part of codicons --- package.json | 133 +++++++----- res/dark/build-icon.svg | 8 - res/dark/clean-configure-icon.svg | 27 --- res/dark/clean-icon.svg | 39 ---- res/dark/configure-icon.svg | 4 - res/dark/json-icon.svg | 4 - res/dark/settings-icon.svg | 12 -- res/light/build-icon.svg | 8 - res/light/json-icon.svg | 4 - res/light/settings-icon.svg | 12 -- res/product-icons.woff2 | Bin 0 -> 1772 bytes res/product-icons/uE001_build-icon.svg | 1 + .../uE002_clean-configure-icon.svg} | 0 .../uE003_clean-icon.svg} | 0 .../uE004_cmake-view-icon-1.svg} | 0 .../uE005_cmake-view-icon-2.svg} | 0 .../uE006_configure-icon.svg} | 0 .../product-icon-font-generator/.eslintrc.cjs | 159 +++++++++++++++ .../product-icon-font-generator/package.json | 24 +++ .../product-icon-font-generator/src/index.ts | 192 ++++++++++++++++++ .../product-icon-font-generator/tsconfig.json | 12 ++ 21 files changed, 467 insertions(+), 172 deletions(-) delete mode 100644 res/dark/build-icon.svg delete mode 100644 res/dark/clean-configure-icon.svg delete mode 100644 res/dark/clean-icon.svg delete mode 100644 res/dark/configure-icon.svg delete mode 100644 res/dark/json-icon.svg delete mode 100644 res/dark/settings-icon.svg delete mode 100644 res/light/build-icon.svg delete mode 100644 res/light/json-icon.svg delete mode 100644 res/light/settings-icon.svg create mode 100644 res/product-icons.woff2 create mode 100644 res/product-icons/uE001_build-icon.svg rename res/{light/clean-configure-icon.svg => product-icons/uE002_clean-configure-icon.svg} (100%) rename res/{light/clean-icon.svg => product-icons/uE003_clean-icon.svg} (100%) rename res/{cmake-view-icon.svg => product-icons/uE004_cmake-view-icon-1.svg} (100%) rename res/{cmake-view-icon2.svg => product-icons/uE005_cmake-view-icon-2.svg} (100%) rename res/{light/configure-icon.svg => product-icons/uE006_configure-icon.svg} (100%) create mode 100644 tools/product-icon-font-generator/.eslintrc.cjs create mode 100644 tools/product-icon-font-generator/package.json create mode 100644 tools/product-icon-font-generator/src/index.ts create mode 100644 tools/product-icon-font-generator/tsconfig.json diff --git a/package.json b/package.json index 3b71af67bd..9e38de2ee8 100644 --- a/package.json +++ b/package.json @@ -67,6 +67,69 @@ ], "main": "./dist/main", "contributes": { + "languages": [ + { + "id": "cmake", + "extensions": [ + ".cmake" + ], + "filenames": ["CMakeLists.txt"], + "aliases": [ + "CMake" + ] + }, + { + "id": "cmake-cache", + "filenames": ["CMakeCache.txt"], + "aliases": [ + "CMake Cache" + ] + } + ], + "icons": { + "cmake-tools-build": { + "description": "CMake build icon", + "default": { + "fontPath": "./res/product-icons.woff2", + "fontCharacter": "\\E001" + } + }, + "cmake-tools-clean-configure": { + "description": "CMake clean configure icon", + "default": { + "fontPath": "./res/product-icons.woff2", + "fontCharacter": "\\E002" + } + }, + "cmake-tools-clean": { + "description": "CMake clean icon", + "default": { + "fontPath": "./res/product-icons.woff2", + "fontCharacter": "\\E003" + } + }, + "cmake-tools-cmake-view-1": { + "description": "CMake view icon 1", + "default": { + "fontPath": "./res/product-icons.woff2", + "fontCharacter": "\\E004" + } + }, + "cmake-tools-cmake-view-2": { + "description": "CMake view icon 2", + "default": { + "fontPath": "./res/product-icons.woff2", + "fontCharacter": "\\E005" + } + }, + "cmake-tools-configure": { + "description": "CMake configure icon", + "default": { + "fontPath": "./res/product-icons.woff2", + "fontCharacter": "\\E006" + } + } + }, "commands": [ { "command": "cmake.openCMakePresets", @@ -299,18 +362,12 @@ { "command": "cmake.outline.configure", "title": "%cmake-tools.command.cmake.configure.title%", - "icon": { - "dark": "res/dark/configure-icon.svg", - "light": "res/light/configure-icon.svg" - } + "icon": "$(cmake-tools-configure)" }, { "command": "cmake.projectStatus.configure", "title": "%cmake-tools.command.cmake.configure.title%", - "icon": { - "dark": "res/dark/configure-icon.svg", - "light": "res/light/configure-icon.svg" - }, + "icon": "$(cmake-tools-configure)", "when": "cmake:enableFullFeatureSet", "category": "CMake" }, @@ -339,10 +396,7 @@ { "command": "cmake.outline.configureAll", "title": "%cmake-tools.command.cmake.configureAll.title%", - "icon": { - "dark": "res/dark/configure-icon.svg", - "light": "res/light/configure-icon.svg" - } + "icon": "$(cmake-tools-configure)" }, { "command": "cmake.outline.configureAllWithDebugger", @@ -360,28 +414,19 @@ "command": "cmake.outline.build", "title": "%cmake-tools.command.cmake.build.title%", "when": "cmake:enableFullFeatureSet", - "icon": { - "dark": "res/dark/build-icon.svg", - "light": "res/light/build-icon.svg" - } + "icon": "$(cmake-tools-build)" }, { "command": "cmake.projectStatus.build", "title": "%cmake-tools.command.cmake.build.title%", "when": "cmake:enableFullFeatureSet", - "icon": { - "dark": "res/dark/build-icon.svg", - "light": "res/light/build-icon.svg" - } + "icon": "$(cmake-tools-build)" }, { "command": "cmake.outline.buildTarget", "title": "%cmake-tools.command.cmake.build.title%", "when": "cmake:enableFullFeatureSet", - "icon": { - "dark": "res/dark/build-icon.svg", - "light": "res/light/build-icon.svg" - } + "icon": "$(cmake-tools-build)" }, { "command": "cmake.showBuildCommand", @@ -399,29 +444,20 @@ "command": "cmake.outline.buildAll", "title": "%cmake-tools.command.cmake.buildAll.title%", "when": "cmake:enableFullFeatureSet", - "icon": { - "dark": "res/dark/build-icon.svg", - "light": "res/light/build-icon.svg" - } + "icon": "$(cmake-tools-build)" }, { "command": "cmake.compileFile", "title": "%cmake-tools.command.cmake.compileFile.title%", "category": "CMake", "when": "cmake:enableFullFeatureSet", - "icon": { - "dark": "res/dark/build-icon.svg", - "light": "res/light/build-icon.svg" - } + "icon": "$(cmake-tools-build)" }, { "command": "cmake.outline.compileFile", "title": "%cmake-tools.command.cmake.outline.compileFile.title%", "when": "cmake:enableFullFeatureSet", - "icon": { - "dark": "res/dark/build-icon.svg", - "light": "res/light/build-icon.svg" - } + "icon": "$(cmake-tools-build)" }, { "command": "cmake.install", @@ -473,30 +509,21 @@ "title": "%cmake-tools.command.cmake.projectStatus.cleanConfigure.title%", "when": "cmake:enableFullFeatureSet", "category": "CMake", - "icon": { - "dark": "res/dark/clean-configure-icon.svg", - "light": "res/light/clean-configure-icon.svg" - } + "icon": "$(cmake-tools-clean-configure)" }, { "command": "cmake.projectStatus.openSettings", "title": "%cmake-tools.command.cmake.openSettings.title%", "when": "cmake:enableFullFeatureSet", "category": "CMake", - "icon": { - "dark": "res/dark/settings-icon.svg", - "light": "res/light/settings-icon.svg" - } + "icon": "$(settings-gear)" }, { "command": "cmake.projectStatus.openVisibilitySettings", "title": "%cmake-tools.command.cmake.projectStatus.openVisibilitySettings.title%", "when": "cmake:enabelFullFeatureSet", "category": "CMake", - "icon": { - "dark": "res/dark/json-icon.svg", - "light": "res/light/json-icon.svg" - } + "icon": "$(json)" }, { "command": "cmake.cleanConfigureWithDebugger", @@ -539,10 +566,7 @@ "command": "cmake.outline.clean", "when": "cmake:enableFullFeatureSet", "title": "%cmake-tools.command.cmake.clean.title%", - "icon": { - "dark": "res/dark/clean-icon.svg", - "light": "res/light/clean-icon.svg" - } + "icon": "$(cmake-tools-clean)" }, { "command": "cmake.cleanAll", @@ -3620,7 +3644,7 @@ { "id": "cmake-view", "title": "CMake", - "icon": "res/cmake-view-icon2.svg", + "icon": "$(cmake-tools-cmake-view-2)", "when": "cmake:enableFullFeatureSet" } ] @@ -3699,7 +3723,8 @@ "extensionTestsSuccessfulBuild": "yarn run pretest && node ./out/test/extension-tests/successful-build/runTest.js", "extensionTestsSingleRoot": "yarn run pretest && node ./out/test/extension-tests/single-root-UI/runTest.js", "extensionTestsMultiRoot": "yarn run pretest && node ./out/test/extension-tests/multi-root-UI/runTest.js", - "backendTests": "node ./node_modules/mocha/bin/_mocha -u tdd --timeout 999999 --colors -r ts-node/register -r tsconfig-paths/register ./test/backend-unit-tests/**/*.test.ts" + "backendTests": "node ./node_modules/mocha/bin/_mocha -u tdd --timeout 999999 --colors -r ts-node/register -r tsconfig-paths/register ./test/backend-unit-tests/**/*.test.ts", + "build-product-icon-font": "yarn --cwd ./tools/product-icon-font-generator/ install && yarn --cwd ./tools/product-icon-font-generator/ build && node ./tools/product-icon-font-generator/dist/index.js --source-directory ./res/product-icons/ --output-directory ./res/ --woff2" }, "devDependencies": { "@octokit/rest": "^18.1.1", diff --git a/res/dark/build-icon.svg b/res/dark/build-icon.svg deleted file mode 100644 index 2cd8a0c9ad..0000000000 --- a/res/dark/build-icon.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/res/dark/clean-configure-icon.svg b/res/dark/clean-configure-icon.svg deleted file mode 100644 index b501001917..0000000000 --- a/res/dark/clean-configure-icon.svg +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/res/dark/clean-icon.svg b/res/dark/clean-icon.svg deleted file mode 100644 index 5bd9fa9eb7..0000000000 --- a/res/dark/clean-icon.svg +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - diff --git a/res/dark/configure-icon.svg b/res/dark/configure-icon.svg deleted file mode 100644 index 031cc85f69..0000000000 --- a/res/dark/configure-icon.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/res/dark/json-icon.svg b/res/dark/json-icon.svg deleted file mode 100644 index 11e780cafb..0000000000 --- a/res/dark/json-icon.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/res/dark/settings-icon.svg b/res/dark/settings-icon.svg deleted file mode 100644 index 075129f205..0000000000 --- a/res/dark/settings-icon.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - \ No newline at end of file diff --git a/res/light/build-icon.svg b/res/light/build-icon.svg deleted file mode 100644 index 79a8a63339..0000000000 --- a/res/light/build-icon.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/res/light/json-icon.svg b/res/light/json-icon.svg deleted file mode 100644 index 79a1312724..0000000000 --- a/res/light/json-icon.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/res/light/settings-icon.svg b/res/light/settings-icon.svg deleted file mode 100644 index 2f58e54022..0000000000 --- a/res/light/settings-icon.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - \ No newline at end of file diff --git a/res/product-icons.woff2 b/res/product-icons.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..6af38d6d30cfec17a9ae04afc86d8bb61804a1d4 GIT binary patch literal 1772 zcmV=hqRpZ{?iWF0kYoB0vq+hP6H7@bybmFJz7b45ht?=R_6UNfP=~X z#Jp|`4Zv$){I-$-Hb)OpsCuF=4)>0Mk+B;AMy;v7dK2S87b%{TdnltKM9Hd- zEjngfTcI-bBrLlMUuSECs<-u@;nk}hT%-TMlP-b)tF{G7lV+8Tz<10*ia{ie94!cn z%9xQl2Dq<6Xa)I&nS@-*wE2Nl9b zJe3U+pv;P0ha1kr%Lgik7!-O$9%J3GA|B+@e2^G@0Xw1}Z^YUFI#5CVCWXm6nK55Q z9<4j7HLG;oGZ5q!yEilpE$N|SKnVIiNc{gFFm1vRlauX(P>+lfLB(WAlMK_Rz<;5> z9mA4R@(S!+qTB*}B4Xk^GQ844f)Z@9a)e4}FtI=qacli>q|1>nl`p_dvg5{+o+03i z#72^uDIE&3{uQtlDvT&>2gn|%ncE^7@PjrSpaUo9A`E&+f<97UfIJwY0G40}d*K56 z5d{a}1_u!Uhu{N;5dlXK14j`D$Ke4dkpZXR1*ee)XAlBs5d;^I0GD9{SC9o)-E#OA z6dWyvAXG>QLWdSd5C*ga1Yv@fh;mo}6s-+b0gNCYp-dq4ZGsw#=&I?FW-Uzivqb7rkEBXg z=@PmQ03m5hr3z&a(fxe>gF>DMUnx^#Q&UT0Lt0}?*ETKmu1->~R9Rx=KLFB1sb^i)ZOFTO>TaDw`iBgw7DOWEcIbV&4ALq1p{~O2f##eh zoXTW2>bAqe*I!S)x@6<(f~<<0tFyOUS@7kRRI7^dtE;jpZ!K)0t-;e42HrDOH2Fpr zp6l~?)21oX8fKrZ)THS4t>ip^Rr<>9{%+f^o8*yH*c^o$vop7Z)H(8~&v`IxdDKh2 z{<1PaH{`x>I?qhc7`#}JBCTTTTH(;k?HwzsCBisECo=1-*t7xhk_k_LUp3mXN;`;U<2rrq@hHO@5oCMV{?z$q`Su!>uy0>pT|V(| z;gY4rBDtdQZ(uf=d#cc8?#Y1z{fd?>FBQrZ>5mmF+{?E;>}0-9`d)c`cB5Z^cI3tE z)%N%~ZlY2NV;_)XWplXUSk9~3SpG=30H~*;(f?lz@Yf-p8x#z4LYT{(g0S zO!~*j_k<4dP1ju!Rufs2SE+>DEQe|W@%iMUX0<63T z{AtCxy_rj#aF889(H#L*J>3tY*<_%zX~N(zfmO}&<~Ba(Igxk@5w*ggaC!t))C;27 zyn!z08w^qZVYMY>czK&*7&bv5yp5w814pn}k90OOBP46@tZhbuXdoCSH``gaQGzfM z7>anv4nWbaOQBQ?wF0rgVuC3$n+c@a8&Ty1J*C$4#MTkqN&-hLz)C>yY&ew0aU22@ z)kAb_re+L*(!fJhGqj2k8KQFwbu_i9q(sGZuM7&|qYfCba$jVplfqLeEokudsl`*5 z>+wWtR+Q|;ww;VnOoC%IQmxXpL|aFwYC_Mt<#m~a30CqR7Xv8CimK^`Y1xkJ`5-U^ z3WFn%C^QC(!xM-kGKET`GeU-`v6GwRS2vc|uuV7?ZK7ADyrdYbY&Nxgb=O>bmD53X z8DeTz_c;#(v2KtQQ#MrQyXq+=WO*N&GAi11(N!N&(p$TD${m`3D|Wb&yc \ No newline at end of file diff --git a/res/light/clean-configure-icon.svg b/res/product-icons/uE002_clean-configure-icon.svg similarity index 100% rename from res/light/clean-configure-icon.svg rename to res/product-icons/uE002_clean-configure-icon.svg diff --git a/res/light/clean-icon.svg b/res/product-icons/uE003_clean-icon.svg similarity index 100% rename from res/light/clean-icon.svg rename to res/product-icons/uE003_clean-icon.svg diff --git a/res/cmake-view-icon.svg b/res/product-icons/uE004_cmake-view-icon-1.svg similarity index 100% rename from res/cmake-view-icon.svg rename to res/product-icons/uE004_cmake-view-icon-1.svg diff --git a/res/cmake-view-icon2.svg b/res/product-icons/uE005_cmake-view-icon-2.svg similarity index 100% rename from res/cmake-view-icon2.svg rename to res/product-icons/uE005_cmake-view-icon-2.svg diff --git a/res/light/configure-icon.svg b/res/product-icons/uE006_configure-icon.svg similarity index 100% rename from res/light/configure-icon.svg rename to res/product-icons/uE006_configure-icon.svg diff --git a/tools/product-icon-font-generator/.eslintrc.cjs b/tools/product-icon-font-generator/.eslintrc.cjs new file mode 100644 index 0000000000..795d0b79e4 --- /dev/null +++ b/tools/product-icon-font-generator/.eslintrc.cjs @@ -0,0 +1,159 @@ +module.exports = { + "env": { + "browser": true, + "es6": true, + "node": true + }, + "parser": "@typescript-eslint/parser", + "parserOptions": { + "project": "tsconfig.json", + "sourceType": "module" + }, + "plugins": [ + "@typescript-eslint", + "@typescript-eslint/tslint", + "eslint-plugin-jsdoc", + "@typescript-eslint/eslint-plugin-tslint", + "eslint-plugin-import", + ], + "rules": { + "@typescript-eslint/adjacent-overload-signatures": "error", + "@typescript-eslint/array-type": "error", + "camelcase": "off", + "@typescript-eslint/naming-convention": [ + "error", + { + "selector": "typeLike", + "format": ["PascalCase"] + } + ], + "@typescript-eslint/indent": "error", + "@typescript-eslint/member-delimiter-style": [ + "error", + { + "multiline": { + "delimiter": "semi", + "requireLast": true + }, + "singleline": { + "delimiter": "semi", + "requireLast": false + } + } + ], + "@typescript-eslint/no-for-in-array": "error", + "@typescript-eslint/no-misused-new": "error", + "@typescript-eslint/no-misused-promises": "error", + //"@typescript-eslint/no-namespace": "error", + //"@typescript-eslint/no-non-null-assertion": "error", + "@typescript-eslint/no-extra-non-null-assertion": "error", + "@typescript-eslint/no-this-alias": "error", + "@typescript-eslint/no-unnecessary-qualifier": "error", + "@typescript-eslint/no-unnecessary-type-arguments": "error", + //"@typescript-eslint/no-var-requires": "error", + "@typescript-eslint/prefer-function-type": "error", + "@typescript-eslint/prefer-namespace-keyword": "error", + "@typescript-eslint/semi": "error", + "@typescript-eslint/triple-slash-reference": "error", + "@typescript-eslint/type-annotation-spacing": "error", + //"@typescript-eslint/unified-signatures": "error", + "@typescript-eslint/no-floating-promises": "error", + "@typescript-eslint/await-thenable": "error", + "arrow-body-style": "error", + "brace-style": "error", + "comma-dangle": "error", + "constructor-super": "error", + "curly": "error", + "eol-last": "error", + "eqeqeq": [ + "error", + "always" + ], + //"import/no-default-export": "error", + "import/no-unassigned-import": "error", + "jsdoc/no-types": "error", + "new-parens": "error", + "no-bitwise": "error", + "no-caller": "error", + "no-cond-assign": "error", + //"no-debugger": "error", + "no-duplicate-case": "error", + //"no-duplicate-imports": "error", + "no-eval": "error", + "no-fallthrough": "error", + //"no-invalid-this": "error", + "no-irregular-whitespace": "error", + "no-multiple-empty-lines": ["error", { "max": 1, "maxEOF": 1, "maxBOF": 0 }], + "no-new-wrappers": "error", + "no-redeclare": "error", + "no-return-await": "error", + "no-sequences": "error", + "no-sparse-arrays": "error", + "no-trailing-spaces": "error", + "no-undef-init": "error", + "no-unsafe-finally": "error", + "no-unused-expressions": "off", + "no-unused-labels": "error", + "no-var": "error", + "one-var": [ + "error", + "never" + ], + "prefer-const": "error", + "prefer-object-spread": "error", + "space-in-parens": [ + "error", + "never" + ], + /*"spaced-comment": [ + "error", + "always" + ],*/ + "use-isnan": "error", + "valid-typeof": "error", + "yoda": "error", + "@typescript-eslint/tslint/config": [ + "error", + { + "rules": { + "encoding": true, + /*"file-header": [ + true, + ".*" + ],*/ + "import-spacing": true, + "match-default-export-name": true, + "no-boolean-literal-compare": true, + "no-mergeable-namespace": true, + "no-reference-import": true, + "no-unnecessary-callback-wrapper": false, + "number-literal-format": true, + "one-line": [ + true, + "check-catch", + "check-finally", + "check-else", + "check-open-brace", + "check-whitespace" + ], + "prefer-method-signature": true, + "prefer-while": true, + /*"typedef": [ + true, + "variable-declaration", + "call-signature", + "variable-declaration-ignore-function" + ],*/ + "whitespace": [ + true, + "check-branch", + "check-operator", + "check-separator", + "check-preblock", + "check-type" + ] + } + } + ] + } +}; diff --git a/tools/product-icon-font-generator/package.json b/tools/product-icon-font-generator/package.json new file mode 100644 index 0000000000..23e0a2be2e --- /dev/null +++ b/tools/product-icon-font-generator/package.json @@ -0,0 +1,24 @@ +{ + "name": "product-icon-font-generator", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "build": "tsc -p ./", + "clean": "rimraf ./dist", + "lint": "eslint src --ext ts --max-warnings 0" + }, + "dependencies": { + "fontagon": "^1.1.1", + "ttf2woff2": "^6" + }, + "devDependencies": { + "@types/fontagon": "^1.1.4", + "@types/node": "^20.13", + "commander": "^12.0.0", + "eslint": "^8.25.0", + "rimraf": "^3.0.2", + "ts-node": "^10.9.1", + "typescript": "^5.1.0" + } +} diff --git a/tools/product-icon-font-generator/src/index.ts b/tools/product-icon-font-generator/src/index.ts new file mode 100644 index 0000000000..8622a86f11 --- /dev/null +++ b/tools/product-icon-font-generator/src/index.ts @@ -0,0 +1,192 @@ +import * as path from "path"; +import * as fs from "fs"; + +import * as commander from "commander"; +import Fontagon from "fontagon"; + +type Optional = T | undefined; + +/** + * Supported font formats to output. + * + * @enum {number} + */ +enum FontFormat { + WOFF, + WOFF2, +} + +/** + * Struct to store icon file metadata. + */ +class SvgIcon { + public fp: string; + public glyphCode: number; + public name: string; + + constructor(args: {fp: string; glyph_code: number; name: Optional}) { + this.fp = args.fp; + this.glyphCode = args.glyph_code; + this.name = args.name || ''; + } +} + +/** + * Generator class for generating webfonts. + */ +class WebfontGenerator { + private readonly _FILE_MATCHER = new RegExp("^u(?[0-9a-f]{4})[-_]?(?.*)\\.svg$", "i"); + + private _svgIcons: SvgIcon[]; + private _enabledFormats: Set; + + constructor() { + this._svgIcons = []; + this._enabledFormats = new Set(); + } + + /** + * Enables a font output format. + * + * @public + * @param format Format to enable. + */ + public enableFormat(format: FontFormat) { + this._enabledFormats.add(format); + } + + /** + * Searches and adds all icons files recursively from a given base directory. + * + * @public + * @param baseDir Base directory to recursively process. + */ + public addFromDirectory(baseDir: string) { + baseDir = path.resolve(baseDir); + if (!fs.statSync(baseDir).isDirectory()) { + return; + } + + for (let fp of fs.readdirSync(baseDir)) { + // Full path, as well as replace backwards slashes as webfont requires forward slashes. + fp = path.join(baseDir, fp).replace(/\\/g, '/'); + const parsed_fp = path.parse(fp); + + const match = this._FILE_MATCHER.exec(parsed_fp.base); + if (match?.groups && fs.statSync(fp).isFile()) { + this._svgIcons.push(new SvgIcon({ + fp: fp, + glyph_code: Number.parseInt(`0x${match.groups['code']}`), + name: parsed_fp.name + })); + console.log(`Added icon: '${fp}'`); + console.log(Number.parseInt(`0x${match.groups['code']}`)); + // Assume a directory (checked in begin of function when recursing). + } else { + this.addFromDirectory(fp); + } + } + } + + /** + * Generates and outputs the font files from the added icons. + * + * @public + * @async + * @param outputDirectory Directory to output the font files. + * @param name Font name. + */ + public async generate(outputDirectory: string, name: Optional = undefined): Promise { + if (!this._enabledFormats.size) { + console.warn("No enabled font formats to output; nothing to generate."); + return; + } + + outputDirectory = path.resolve(outputDirectory); + if (!fs.existsSync(outputDirectory)) { + throw Error(`Output directory does not exist '${outputDirectory}'`); + } + + // Fontagon automatically cleans/removes directories, so make a temporary directory and move the files over + // after generation to the actual output directory. + const tmp_directory = path.join(outputDirectory, 'tmp'); + !fs.existsSync(tmp_directory) && fs.mkdirSync(tmp_directory); + + // Enable formats to output, alongside any optional format options. Furthmore, Fontagon also outputs intermediate + // font formats, so we'll keep track of the files by extension that we want to keep. + const format_options: Fontagon.FormatOptions = {}; + const extensions: string[] = []; + for (const format of this._enabledFormats) { + switch (format) { + case FontFormat.WOFF: + format_options['woff'] = {}; + extensions.push('.woff'); + break; + case FontFormat.WOFF2: + format_options['woff2'] = {}; + extensions.push('.woff2'); + break; + default: + throw Error(`Unsupported font format '${FontFormat[format]}'.`); + } + }; + + await Fontagon({ + files: this._svgIcons.map((svg_icon) => svg_icon.fp), + codepoints: this._svgIcons.reduce((a, svg_icon) => ({ ...a, [svg_icon.name]: svg_icon.glyphCode }), {}), + dist: tmp_directory, + fontName: name || path.basename(opts.sourceDirectory), + style: undefined, + formatOptions: { ...format_options, + // The SVG intermediate format has all the transformative options we can set. + svg: { + fontHeight: 1000, + normalize: false + } + } + }); + + // Move over all files from the temporary directory + for (const fp of fs.readdirSync(tmp_directory)) { + if (!extensions.includes(path.parse(fp).ext)) { + continue; + } + fs.renameSync( + path.resolve(tmp_directory, fp), + path.join(outputDirectory, path.basename(fp))); + } + fs.existsSync(tmp_directory) && fs.rmSync(tmp_directory, { recursive: true }); + } +} + +commander.program + .name("product-icon-generator") + .description("Takes SVG icons and generates it into a product icon font.") + // I/O arguments. + .requiredOption("--source-directory ", "The root directory where to recursively import icons from. Filenames must start with a hexadecimal glyph codepoint (e.g. '0xE001.svg')") + .option("-o, --output-directory ", "Directory where to output the webfont file. Defaults to the source directory if not set.") + .option("--name ", "(File)name of the font. Defaults to the directory name if this option is not set.") + // Font format generation arguments. Each format should be enabled explicitly by the user. + .option("--woff", "Enables WOFF font generation.") + .option("--woff2", "Enables WOFF2 font generation.") + .parse(); +const opts = commander.program.opts(); + +try { + const generator = new WebfontGenerator(); + + const register_format = (opt: boolean, format: FontFormat) => { + opt && generator.enableFormat(format); + }; + register_format(opts.woff , FontFormat.WOFF); + register_format(opts.woff2, FontFormat.WOFF2); + + generator.addFromDirectory(opts.sourceDirectory); + + await generator.generate( + opts.outputDirectory || opts.sourceDirectory, + opts.name + ); +} catch (e) { + console.error('Font creation failed.', e); +} diff --git a/tools/product-icon-font-generator/tsconfig.json b/tools/product-icon-font-generator/tsconfig.json new file mode 100644 index 0000000000..c2a6eb6e8e --- /dev/null +++ b/tools/product-icon-font-generator/tsconfig.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "outDir": "dist", + "rootDir": "./src", + "strict": true, + "module": "es2022", + "target": "es2022", + "moduleResolution": "Node", + "forceConsistentCasingInFileNames": true, + "esModuleInterop": true + } +} From e640ae4cf61d21e5ce6e15d425050b91250db021 Mon Sep 17 00:00:00 2001 From: DeltaRazero Date: Thu, 17 Oct 2024 18:59:03 +0200 Subject: [PATCH 2/2] Revert accidental addition of 'languages' contribution entry --- package.json | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/package.json b/package.json index 9e38de2ee8..bb1b2505fe 100644 --- a/package.json +++ b/package.json @@ -67,25 +67,6 @@ ], "main": "./dist/main", "contributes": { - "languages": [ - { - "id": "cmake", - "extensions": [ - ".cmake" - ], - "filenames": ["CMakeLists.txt"], - "aliases": [ - "CMake" - ] - }, - { - "id": "cmake-cache", - "filenames": ["CMakeCache.txt"], - "aliases": [ - "CMake Cache" - ] - } - ], "icons": { "cmake-tools-build": { "description": "CMake build icon",