Add serialization support for PathData

For transit and fressian
This commit is contained in:
Andrey Antukh 2025-03-26 20:32:43 +01:00
parent e62231cfed
commit fac93e4ff8
2 changed files with 64 additions and 9 deletions

View file

@ -6,7 +6,9 @@
(ns app.common.types.shape.path
(:require
[app.common.schema :as sm])
#?(:clj [app.common.fressian :as fres])
[app.common.schema :as sm]
[app.common.transit :as t])
(:import
#?(:cljs [goog.string StringBuffer]
:clj [java.nio ByteBuffer])))
@ -74,7 +76,8 @@
(def ^:const SEGMENT-BYTE-SIZE 28)
(defprotocol IPathData
(-write-to [_ buffer offset] "write the content to the specified buffer"))
(-write-to [_ buffer offset] "write the content to the specified buffer")
(-bytes [_] "get path data as byte array"))
(defrecord PathSegment [command params])
@ -225,7 +228,15 @@
default))
clojure.lang.Counted
(count [_] size))
(count [_] size)
IPathData
(-write-to [_ _ _]
(throw (RuntimeException. "not implemented")))
(-bytes [_]
(.array ^ByteBuffer buffer)))
:cljs
(deftype PathData [size buffer dview]
@ -240,18 +251,21 @@
mem (js/Uint32Array. into-buffer offset size)]
(.set mem (js/Uint32Array. buffer))))
(-bytes [_]
(js/Uint8Array. buffer))
cljs.core/ISequential
cljs.core/IEquiv
(-equiv [_ other]
(if (instance? PathData other)
(let [obuffer (.-buffer other)
osize (.-byteLength obuffer)
csize (.-byteLength buffer)]
(if (= osize csize)
(let [obuffer (.-buffer other)]
(if (= (.-byteLength obuffer)
(.-byteLength buffer))
(let [cb (js/Uint32Array. buffer)
ob (js/Uint32Array. obuffer)]
ob (js/Uint32Array. obuffer)
sz (alength cb)]
(loop [i 0]
(if (< i osize)
(if (< i sz)
(if (= (aget ob i)
(aget cb i))
(recur (inc i))
@ -341,6 +355,9 @@
count (long (/ size SEGMENT-BYTE-SIZE))]
(PathData. count buffer dview))
(instance? js/Uint8Array buffer)
(from-bytes (.-buffer buffer))
:else
(throw (js/Error. "invalid data provided")))))
@ -429,3 +446,23 @@
:else
(from-bytes data)))
(t/add-handlers!
{:id "penpot/path-data"
:class PathData
:wfn (fn [^PathData pdata]
(-bytes pdata))
:rfn path-data})
#?(:clj
(fres/add-handlers!
{:name "penpot/path-data"
:class PathData
:wfn (fn [n w o]
(fres/write-tag! w n 1)
(let [buffer (.-buffer ^PathData o)
bytes (.array ^ByteBuffer buffer)]
(fres/write-bytes! w bytes)))
:rfn (fn [r]
(let [^bytes bytes (fres/read-object! r)]
(path-data (ByteBuffer/wrap bytes))))}))

View file

@ -6,9 +6,11 @@
(ns common-tests.types.shape-path-data-test
(:require
#?(:clj [app.common.fressian :as fres])
[app.common.data :as d]
[app.common.math :as mth]
[app.common.pprint :as pp]
[app.common.transit :as trans]
[app.common.types.shape.path :as path]
[clojure.test :as t]))
@ -57,3 +59,19 @@
(mapv path/map->PathSegment))
(vec pdata)))))
(t/deftest path-data-transit-roundtrip
(let [pdata (path/path-data sample-content)
result1 (trans/encode-str pdata)
expected "[\"~#penpot/path-data\",\"~bAAEAAAAAAAAAAAAAAAAAAAAAAABD8AAARFHAAAACAAAAAAAAAAAAAAAAAAAAAAAAQ9uAAERIgAAAAwAAQ7gAAEQ4QABDmwAARCpAAEOEAABEHoAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==\"]"
result2 (trans/decode-str result1)]
(t/is (= expected result1))
(t/is (= pdata result2))))
#?(:clj
(t/deftest path-data-fresian
(let [pdata (path/path-data sample-content)
result1 (fres/encode pdata)
result2 (fres/decode result1)]
(t/is (= pdata result2)))))