From f450c9dbe337a357691e23163f7f72e41b60e89b Mon Sep 17 00:00:00 2001 From: Dalai Felinto Date: Thu, 13 Mar 2025 15:15:30 +0000 Subject: [PATCH] :tada: Add support for WEBP format on shape export It is very convenient to be able to export WEBP right from penpot. Otherwise users have to first download to PNG then convert it locally. --- Playwright only supports JPEG and PNG. So in order to support WEBP I had to first generate a PNG and then convert it afterwards. Signed-off-by: Dalai Felinto --- CHANGES.md | 6 ++++++ common/src/app/common/types/shape/export.cljc | 2 +- docs/user-guide/exporting/index.njk | 2 +- exporter/src/app/renderer.cljs | 3 ++- exporter/src/app/renderer/bitmap.cljs | 6 +++++- exporter/src/app/util/mime.cljs | 4 +++- frontend/src/app/main/data/exports/assets.cljs | 4 ++-- frontend/src/app/main/ui/inspect/exports.cljs | 3 ++- frontend/src/app/main/ui/inspect/exports.scss | 6 +++--- .../main/ui/workspace/sidebar/options/menus/exports.cljs | 3 ++- .../main/ui/workspace/sidebar/options/menus/exports.scss | 6 +++--- frontend/src/app/plugins/format.cljs | 2 +- frontend/src/app/plugins/parser.cljs | 2 +- 13 files changed, 32 insertions(+), 17 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index fd01e4c3b..09ea41efc 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,5 +1,11 @@ # CHANGELOG +## 2.5.4 (Unreleased) + +### :sparkles: New features + +- Add support for WEBP format on shape export [Github #6053](https://github.com/penpot/penpot/pull/6053) and [Github #6074](https://github.com/penpot/penpot/pull/6074) + ## 2.5.3 ### :bug: Bugs fixed diff --git a/common/src/app/common/types/shape/export.cljc b/common/src/app/common/types/shape/export.cljc index bd2bee0a5..feb7f8dec 100644 --- a/common/src/app/common/types/shape/export.cljc +++ b/common/src/app/common/types/shape/export.cljc @@ -8,7 +8,7 @@ (:require [app.common.schema :as sm])) -(def types #{:png :jpeg :svg :pdf}) +(def types #{:png :jpeg :webp :svg :pdf}) (def schema:export [:map {:title "ShapeExport"} diff --git a/docs/user-guide/exporting/index.njk b/docs/user-guide/exporting/index.njk index 63f9d9fb2..d685a6683 100644 --- a/docs/user-guide/exporting/index.njk +++ b/docs/user-guide/exporting/index.njk @@ -27,7 +27,7 @@ title: 07ยท Exporting objects

Exporting multiple elements

diff --git a/exporter/src/app/renderer.cljs b/exporter/src/app/renderer.cljs index b4abaca4b..4a7cf8af7 100644 --- a/exporter/src/app/renderer.cljs +++ b/exporter/src/app/renderer.cljs @@ -15,7 +15,7 @@ (s/def ::name ::us/string) (s/def ::suffix ::us/string) -(s/def ::type #{:jpeg :png :pdf :svg}) +(s/def ::type #{:png :jpeg :webp :pdf :svg}) (s/def ::page-id ::us/uuid) (s/def ::file-id ::us/uuid) (s/def ::share-id ::us/uuid) @@ -40,6 +40,7 @@ (case type :png (rb/render params on-object) :jpeg (rb/render params on-object) + :webp (rb/render params on-object) :pdf (rp/render params on-object) :svg (rs/render params on-object))) diff --git a/exporter/src/app/renderer/bitmap.cljs b/exporter/src/app/renderer/bitmap.cljs index 38022db73..750102af7 100644 --- a/exporter/src/app/renderer/bitmap.cljs +++ b/exporter/src/app/renderer/bitmap.cljs @@ -34,7 +34,11 @@ (bw/wait-for node) (case type :png (bw/screenshot node {:omit-background? true :type type :path path}) - :jpeg (bw/screenshot node {:omit-background? false :type type :path path})) + :jpeg (bw/screenshot node {:omit-background? false :type type :path path}) + :webp (p/let [png-path (sh/tempfile :prefix "penpot.tmp.render.bitmap." :suffix ".png")] + ;; playwright only supports jpg and png, we need to convert it afterwards + (bw/screenshot node {:omit-background? true :type :png :path png-path}) + (sh/run-cmd! (str "convert " png-path " -quality 100 WEBP:" path)))) (on-object (assoc object :path path)))) (render [uri page] diff --git a/exporter/src/app/util/mime.cljs b/exporter/src/app/util/mime.cljs index 18601cbd0..7bb0ce77c 100644 --- a/exporter/src/app/util/mime.cljs +++ b/exporter/src/app/util/mime.cljs @@ -15,6 +15,7 @@ (case type :png ".png" :jpeg ".jpg" + :webp ".webp" :svg ".svg" :pdf ".pdf" :zip ".zip")) @@ -26,6 +27,7 @@ :pdf "application/pdf" :svg "image/svg+xml" :jpeg "image/jpeg" - :png "image/png")) + :png "image/png" + :webp "image/webp")) diff --git a/frontend/src/app/main/data/exports/assets.cljs b/frontend/src/app/main/data/exports/assets.cljs index 1e800aee6..b1f029399 100644 --- a/frontend/src/app/main/data/exports/assets.cljs +++ b/frontend/src/app/main/data/exports/assets.cljs @@ -266,10 +266,10 @@ (defn export-shapes-event [exports origin] (let [types (reduce (fn [counts {:keys [type]}] - (if (#{:png :pdf :svg :jpeg} type) + (if (#{:png :jpeg :webp :svg :pdf} type) (update counts type inc) counts)) - {:png 0, :pdf 0, :svg 0, :jpeg 0} + {:png 0, :jpeg 0, :webp 0, :pdf 0, :svg 0} exports)] (ptk/event ::ev/event (merge types diff --git a/frontend/src/app/main/ui/inspect/exports.cljs b/frontend/src/app/main/ui/inspect/exports.cljs index 8c5495ad1..5f1aa2118 100644 --- a/frontend/src/app/main/ui/inspect/exports.cljs +++ b/frontend/src/app/main/ui/inspect/exports.cljs @@ -37,7 +37,7 @@ scale-enabled? (mf/use-callback (fn [export] - (#{:png :jpeg} (:type export)))) + (#{:png :jpeg :webp} (:type export)))) in-progress? (:in-progress xstate) @@ -123,6 +123,7 @@ format-options [{:value "png" :label "PNG"} {:value "jpeg" :label "JPG"} + {:value "webp" :label "WEBP"} {:value "svg" :label "SVG"} {:value "pdf" :label "PDF"}]] diff --git a/frontend/src/app/main/ui/inspect/exports.scss b/frontend/src/app/main/ui/inspect/exports.scss index 8244d9e23..f7365067c 100644 --- a/frontend/src/app/main/ui/inspect/exports.scss +++ b/frontend/src/app/main/ui/inspect/exports.scss @@ -51,7 +51,7 @@ .element-group { display: grid; - grid-template-columns: repeat(8, 1fr); + grid-template-columns: repeat(9, 1fr); column-gap: $s-4; .action-btn { @extend .button-tertiary; @@ -64,13 +64,13 @@ } .input-wrapper { - grid-column: span 7; + grid-column: span 8; display: grid; grid-template-columns: subgrid; } .format-select { - grid-column: span 2; + grid-column: span 3; padding: 0; .dropdown-upwards { diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/exports.cljs b/frontend/src/app/main/ui/workspace/sidebar/options/menus/exports.cljs index dd8908d76..415d0295a 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/exports.cljs +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/exports.cljs @@ -53,7 +53,7 @@ scale-enabled? (mf/use-fn (fn [export] - (#{:png :jpeg} (:type export)))) + (#{:png :jpeg :webp} (:type export)))) on-download (mf/use-fn @@ -173,6 +173,7 @@ format-options [{:value "png" :label "PNG"} {:value "jpeg" :label "JPG"} + {:value "webp" :label "WEBP"} {:value "svg" :label "SVG"} {:value "pdf" :label "PDF"}]] diff --git a/frontend/src/app/main/ui/workspace/sidebar/options/menus/exports.scss b/frontend/src/app/main/ui/workspace/sidebar/options/menus/exports.scss index 6bf3152dd..0f9aae670 100644 --- a/frontend/src/app/main/ui/workspace/sidebar/options/menus/exports.scss +++ b/frontend/src/app/main/ui/workspace/sidebar/options/menus/exports.scss @@ -32,18 +32,18 @@ .element-group { display: grid; - grid-template-columns: repeat(8, 1fr); + grid-template-columns: repeat(9, 1fr); column-gap: $s-4; } .input-wrapper { - grid-column: span 7; + grid-column: span 8; display: grid; grid-template-columns: subgrid; } .format-select { - grid-column: span 2; + grid-column: span 3; padding: 0; .dropdown-upwards { diff --git a/frontend/src/app/plugins/format.cljs b/frontend/src/app/plugins/format.cljs index 829476da0..6555f6a36 100644 --- a/frontend/src/app/plugins/format.cljs +++ b/frontend/src/app/plugins/format.cljs @@ -261,7 +261,7 @@ :hidden hidden}))) ;; export interface Export { -;; type: 'png' | 'jpeg' | 'svg' | 'pdf'; +;; type: 'png' | 'jpeg' | 'webp' | 'svg' | 'pdf'; ;; scale: number; ;; suffix: string; ;; } diff --git a/frontend/src/app/plugins/parser.cljs b/frontend/src/app/plugins/parser.cljs index 8d884350d..0ce6aad41 100644 --- a/frontend/src/app/plugins/parser.cljs +++ b/frontend/src/app/plugins/parser.cljs @@ -243,7 +243,7 @@ ;; export interface Export { -;; type: 'png' | 'jpeg' | 'svg' | 'pdf'; +;; type: 'png' | 'jpeg' | 'webp' | 'svg' | 'pdf'; ;; scale: number; ;; suffix: string; ;; }