From ea0ca889eb518f428e747278c5d643fa1a5bdacd Mon Sep 17 00:00:00 2001 From: Quentin Stoeckel Date: Tue, 25 Oct 2022 15:39:08 +0200 Subject: [PATCH] Add ability to download icons as PNG files --- package.json | 1 + src/App.vue | 57 ++++++++++++++++++++++++++++++++++++++-------- src/enums.ts | 2 +- src/helpers/img.ts | 17 ++++++++++++++ yarn.lock | 44 +++++++++++++++++++++++++++++++++++ 5 files changed, 110 insertions(+), 11 deletions(-) create mode 100644 src/helpers/img.ts diff --git a/package.json b/package.json index f693f4e..bddba9f 100644 --- a/package.json +++ b/package.json @@ -12,6 +12,7 @@ "lint": "vue-cli-service lint" }, "dependencies": { + "canvg": "^4.0.1", "core-js": "^3.6.5", "material-scss-colors": "^1.1.2", "vue": "^3.0.0", diff --git a/src/App.vue b/src/App.vue index 4a7c05e..46cc3ac 100644 --- a/src/App.vue +++ b/src/App.vue @@ -228,10 +228,34 @@ - + +
+ +
+ {{ actionLabels['download-png'] }}
+ {{ activeIcon && activeIcon.name }}.png +
+
+
+ +
+ {{ actionLabels['download-svg'] }}
+ {{ activeIcon && activeIcon.name }}.png +
+
+ +
@@ -262,6 +286,7 @@ import {objectChunk} from '@/helpers/array'; import {getWeight} from '@/helpers/search'; import Toast from '@/components/Toast.vue'; import {prefersDarkColorScheme} from '@/helpers/theme'; +import {svgToPng} from '@/helpers/img'; const SETTINGS = { ACCENT_COLOR: 'color-accent', @@ -287,6 +312,7 @@ const ACTIONS_LABELS = { 'svg-path': 'Copy SVG path', 'markdown-preview': 'Copy markdown preview', 'download-svg': 'Download SVG', + 'download-png': 'Download PNG', } as Record; const searchReplaceRegex = new RegExp('-', 'g'); @@ -440,9 +466,11 @@ export default defineComponent({ }, doAction(action: Action): void { if (action === 'download-svg') { - this.downloadSvg(); - } else { - this.copy(action); + this.download('svg'); + } else if (action === 'download-png') { + this.download('png') + } { + this.copy(action as Copy); } }, copy(what: Copy): void { @@ -486,7 +514,7 @@ export default defineComponent({ this.toast('Copied to clipboard'); }, - downloadSvg(): void { + async download(format: 'svg'|'png'): Promise { if (this.activeIcon === null) { return; } @@ -503,8 +531,15 @@ export default defineComponent({ } const blob = new Blob([svg], {type: "image/svg+xml"}); - const url = URL.createObjectURL(blob); - const filename = this.activeIcon.name+'.svg'; + + let url; + if (format === 'png') { + url = await svgToPng(svg); + } else { + url = URL.createObjectURL(blob); + } + + const filename = `${this.activeIcon.name}.${format}`; const browserApi = getBrowserInstance(); browserApi && browserApi.downloads.download({ @@ -513,6 +548,8 @@ export default defineComponent({ }); this.toast(`Downloaded ${filename}`); + // Close overflow menu once done + this.openOverflowMenu && this.openOverflowMenu.close(); }, selectText(e: Event): void { // Find .icon-usage diff --git a/src/enums.ts b/src/enums.ts index 126a410..0cf26f1 100644 --- a/src/enums.ts +++ b/src/enums.ts @@ -1,3 +1,3 @@ export type Copy = 'svg'|'svg-path'|'name'|'markdown-preview'|'desktop-font-icon'|'codepoint'; -export type Action = Copy|'download-svg'; +export type Action = Copy|'download-svg'|'download-png'; diff --git a/src/helpers/img.ts b/src/helpers/img.ts new file mode 100644 index 0000000..1dbd604 --- /dev/null +++ b/src/helpers/img.ts @@ -0,0 +1,17 @@ +/** + * Img helper + */ + +import {Canvg} from 'canvg'; + +export const svgToPng = async (svg: string, size = 512): Promise => { + const canvas = document.createElement('canvas'); + canvas.width = canvas.height = size; + const ctx = canvas.getContext('2d'); + if (ctx === null) { + throw new Error(); + } + const v = await Canvg.fromString(ctx, svg); + await v.render(); + return canvas.toDataURL("image/png"); +}; diff --git a/yarn.lock b/yarn.lock index 27d4796..abfef92 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1132,6 +1132,11 @@ resolved "https://registry.yarnpkg.com/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz#d3357479a0fdfdd5907fe67e17e0a85c906e1301" integrity sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw== +"@types/offscreencanvas@^2019.6.4": + version "2019.7.0" + resolved "https://registry.yarnpkg.com/@types/offscreencanvas/-/offscreencanvas-2019.7.0.tgz#e4a932069db47bb3eabeb0b305502d01586fa90d" + integrity sha512-PGcyveRIpL1XIqK8eBsmRBt76eFgtzuPiSTyKHZxnGemp2yzGzWpjYKAfK3wIMiU7eH+851yEpiuP8JZerTmWg== + "@types/parse-json@^4.0.0": version "4.0.0" resolved "https://registry.yarnpkg.com/@types/parse-json/-/parse-json-4.0.0.tgz#2f8bb441434d163b35fb8ffdccd7138927ffb8c0" @@ -1147,6 +1152,11 @@ resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== +"@types/raf@^3.4.0": + version "3.4.0" + resolved "https://registry.yarnpkg.com/@types/raf/-/raf-3.4.0.tgz#2b72cbd55405e071f1c4d29992638e022b20acc2" + integrity sha512-taW5/WYqo36N7V39oYyHP9Ipfd5pNFvGTIQsNGj86xV88YQ7GnI30/yMfKDF7Zgin0m3e+ikX88FvImnK4RjGw== + "@types/range-parser@*": version "1.2.4" resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" @@ -2576,6 +2586,18 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30001109, caniuse-lite@^1.0.30001254: resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001256.tgz#182410b5f024e0ab99c72ec648f234a9986bd548" integrity sha512-QirrvMLmB4txNnxiaG/xbm6FSzv9LqOZ3Jp9VtCYb3oPIfCHpr/oGn38pFq0udwlkctvXQgPthaXqJ76DaYGnA== +canvg@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/canvg/-/canvg-4.0.1.tgz#122532dffeff6fc36074582af2cd37c454ae9ffa" + integrity sha512-5gD/d6SiCCT7baLnVr0hokYe93DfcHW2rSqdKOuOQD84YMlyfttnZ8iQsThTdX6koYam+PROz/FuQTo500zqGw== + dependencies: + "@types/offscreencanvas" "^2019.6.4" + "@types/raf" "^3.4.0" + raf "^3.4.1" + rgbcolor "^1.0.1" + stackblur-canvas "^2.0.0" + svg-pathdata "^6.0.3" + case-sensitive-paths-webpack-plugin@^2.3.0: version "2.4.0" resolved "https://registry.yarnpkg.com/case-sensitive-paths-webpack-plugin/-/case-sensitive-paths-webpack-plugin-2.4.0.tgz#db64066c6422eed2e08cc14b986ca43796dbc6d4" @@ -7374,6 +7396,13 @@ queue-microtask@^1.2.2: resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== +raf@^3.4.1: + version "3.4.1" + resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" + integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA== + dependencies: + performance-now "^2.1.0" + randombytes@^2.0.0, randombytes@^2.0.1, randombytes@^2.0.5, randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -7669,6 +7698,11 @@ rgba-regex@^1.0.0: resolved "https://registry.yarnpkg.com/rgba-regex/-/rgba-regex-1.0.0.tgz#43374e2e2ca0968b0ef1523460b7d730ff22eeb3" integrity sha1-QzdOLiyglosO8VI0YLfXMP8i7rM= +rgbcolor@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/rgbcolor/-/rgbcolor-1.0.1.tgz#d6505ecdb304a6595da26fa4b43307306775945d" + integrity sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw== + rimraf@2.6.3: version "2.6.3" resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.3.tgz#b2d104fe0d8fb27cf9e0a1cda8262dd3833c6cab" @@ -8210,6 +8244,11 @@ stable@^0.1.8: resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== +stackblur-canvas@^2.0.0: + version "2.5.0" + resolved "https://registry.yarnpkg.com/stackblur-canvas/-/stackblur-canvas-2.5.0.tgz#aa87bbed1560fdcd3138fff344fc6a1c413ebac4" + integrity sha512-EeNzTVfj+1In7aSLPKDD03F/ly4RxEuF/EX0YcOG0cKoPXs+SLZxDawQbexQDBzwROs4VKLWTOaZQlZkGBFEIQ== + stackframe@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/stackframe/-/stackframe-1.2.0.tgz#52429492d63c62eb989804c11552e3d22e779303" @@ -8418,6 +8457,11 @@ svg-inline-loader@^0.8.2: object-assign "^4.0.1" simple-html-tokenizer "^0.1.1" +svg-pathdata@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/svg-pathdata/-/svg-pathdata-6.0.3.tgz#80b0e0283b652ccbafb69ad4f8f73e8d3fbf2cac" + integrity sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw== + svg-tags@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/svg-tags/-/svg-tags-1.0.0.tgz#58f71cee3bd519b59d4b2a843b6c7de64ac04764"