diff --git a/CHANGES.md b/CHANGES.md index 77f4dd262b..6754a5045a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -33,6 +33,7 @@ - Fix allow negative spread values on shadow token creation [Taiga #13167](https://tree.taiga.io/project/penpot/issue/13167) - Fix spanish translations on import export token modal [Taiga #13171](https://tree.taiga.io/project/penpot/issue/13171) - Remove whitespaces from asset export filename [Github #8133](https://github.com/penpot/penpot/pull/8133) +- Fix exception on uploading large fonts [Github #8135](https://github.com/penpot/penpot/pull/8135) ## 2.12.1 diff --git a/backend/src/app/rpc/commands/fonts.clj b/backend/src/app/rpc/commands/fonts.clj index 05454d6698..8ca20eac49 100644 --- a/backend/src/app/rpc/commands/fonts.clj +++ b/backend/src/app/rpc/commands/fonts.clj @@ -27,7 +27,17 @@ [app.rpc.helpers :as rph] [app.rpc.quotes :as quotes] [app.storage :as sto] - [app.util.services :as sv])) + [app.storage.tmp :as tmp] + [app.util.services :as sv] + [datoteka.io :as io]) + (:import + java.io.InputStream + java.io.OutputStream + java.io.SequenceInputStream + java.util.Collections)) + +(set! *warn-on-reflection* true) + (def valid-weight #{100 200 300 400 500 600 700 800 900 950}) (def valid-style #{"normal" "italic"}) @@ -105,7 +115,7 @@ (defn create-font-variant [{:keys [::sto/storage ::db/conn]} {:keys [data] :as params}] - (letfn [(generate-missing! [data] + (letfn [(generate-missing [data] (let [data (media/run {:cmd :generate-fonts :input data})] (when (and (not (contains? data "font/otf")) (not (contains? data "font/ttf")) @@ -116,8 +126,26 @@ :hint "invalid font upload, unable to generate missing font assets")) data)) + (process-chunks [chunks] + (let [tmp (tmp/tempfile :prefix "penpot.tempfont." :suffix "") + streams (map io/input-stream chunks) + streams (Collections/enumeration streams)] + (with-open [^OutputStream output (io/output-stream tmp) + ^InputStream input (SequenceInputStream. streams)] + (io/copy input output)) + tmp)) + + (join-chunks [data] + (reduce-kv (fn [data mtype content] + (if (vector? content) + (assoc data mtype (process-chunks content)) + data)) + data + data)) + (prepare-font [data mtype] (when-let [resource (get data mtype)] + (let [hash (sto/calculate-hash resource) content (-> (sto/content resource) (sto/wrap-with-hash hash))] @@ -156,7 +184,8 @@ :otf-file-id (:id otf) :ttf-file-id (:id ttf)}))] - (let [data (generate-missing! data) + (let [data (join-chunks data) + data (generate-missing data) assets (persist-fonts-files! data) result (insert-font-variant! assets)] (vary-meta result assoc ::audit/replace-props (update params :data (comp vec keys)))))) diff --git a/frontend/src/app/main/data/fonts.cljs b/frontend/src/app/main/data/fonts.cljs index 4706e7c5c3..4efa40718f 100644 --- a/frontend/src/app/main/data/fonts.cljs +++ b/frontend/src/app/main/data/fonts.cljs @@ -24,6 +24,20 @@ [cuerdas.core :as str] [potok.v2.core :as ptk])) +(def ^:const default-chunk-size + (* 1024 1024 4)) ;; 4MiB + +(defn- chunk-array + [data chunk-size] + (let [total-size (alength data)] + (loop [offset 0 + chunks []] + (if (< offset total-size) + (let [end (min (+ offset chunk-size) total-size) + chunk (.subarray ^js data offset end)] + (recur end (conj chunks chunk))) + chunks)))) + ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; ;; General purpose events & IMPL ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -116,9 +130,9 @@ (not= hhea-descender win-descent) (and f-selection (or (not= hhea-ascender os2-ascent) - (not= hhea-descender os2-descent))))] - - {:content {:data (js/Uint8Array. data) + (not= hhea-descender os2-descent)))) + data (js/Uint8Array. data)] + {:content {:data (chunk-array data default-chunk-size) :name name :type type} :font-family (or family "")