diff --git a/frontend/src/app/render_wasm/api.cljs b/frontend/src/app/render_wasm/api.cljs index 748771327..d69a8a04e 100644 --- a/frontend/src/app/render_wasm/api.cljs +++ b/frontend/src/app/render_wasm/api.cljs @@ -18,6 +18,7 @@ [app.main.refs :as refs] [app.main.render :as render] [app.render-wasm.api.fonts :as f] + [app.render-wasm.api.texts :as t] [app.render-wasm.deserializers :as dr] [app.render-wasm.helpers :as h] [app.render-wasm.mem :as mem] @@ -30,7 +31,6 @@ [app.util.http :as http] [app.util.webapi :as wapi] [beicon.v2.core :as rx] - [cuerdas.core :as str] [promesa.core :as p] [rumext.v2 :as mf])) @@ -328,8 +328,6 @@ (h/call wasm/internal-module "stringToUTF8" content offset size) (h/call wasm/internal-module "_set_shape_svg_raw_content"))) - - (defn set-shape-blend-mode [blend-mode] ;; These values correspond to skia::BlendMode representation @@ -597,47 +595,19 @@ (h/call wasm/internal-module "_add_shape_shadow" rgba blur spread x y (sr/translate-shadow-style style) hidden) (recur (inc index))))))) -(defn utf8->buffer [text] - (let [encoder (js/TextEncoder.)] - (.encode encoder text))) - -(defn- add-text-leaf [leaf] - (let [text (dm/get-prop leaf :text)] - (when (and text (not (str/blank? text))) - (let [font-id (f/serialize-font-id (dm/get-prop leaf :font-id)) - font-style (f/serialize-font-style (dm/get-prop leaf :font-style)) - font-weight (f/serialize-font-weight (dm/get-prop leaf :font-weight)) - font-size (js/Number (dm/get-prop leaf :font-size)) - buffer (utf8->buffer text) - size (.-byteLength buffer) - offset (mem/alloc-bytes size) - heap (mem/get-heap-u8) - mem (js/Uint8Array. (.-buffer heap) offset size)] - (.set mem buffer) - (h/call wasm/internal-module "_add_text_leaf" - (aget font-id 0) - (aget font-id 1) - (aget font-id 2) - (aget font-id 3) - font-weight font-style font-size))))) - -(defn set-shape-text-content [content] +(defn set-shape-text-content + [content] (h/call wasm/internal-module "_clear_shape_text") (let [paragraph-set (first (dm/get-prop content :children)) paragraphs (dm/get-prop paragraph-set :children) - fonts (fonts/get-content-fonts content) - total-paragraphs (count paragraphs)] + fonts (fonts/get-content-fonts content)] (loop [index 0] - (when (< index total-paragraphs) + (when (< index (count paragraphs)) (let [paragraph (nth paragraphs index) leaves (dm/get-prop paragraph :children)] (when (seq leaves) - (h/call wasm/internal-module "_add_text_paragraph") - (loop [leaf-index 0] - (when (< leaf-index (count leaves)) - (add-text-leaf (nth leaves leaf-index)) - (recur (inc leaf-index)))))) - (recur (inc index)))) + (t/write-shape-text leaves paragraph) + (recur (inc index)))))) (f/store-fonts fonts))) (defn set-view-box diff --git a/frontend/src/app/render_wasm/api/fonts.cljs b/frontend/src/app/render_wasm/api/fonts.cljs index 4c13618bf..1caf62202 100644 --- a/frontend/src/app/render_wasm/api/fonts.cljs +++ b/frontend/src/app/render_wasm/api/fonts.cljs @@ -175,3 +175,4 @@ :style style :weight weight}] (store-font-id font-data asset-id))) fonts)) + diff --git a/frontend/src/app/render_wasm/api/texts.cljs b/frontend/src/app/render_wasm/api/texts.cljs new file mode 100644 index 000000000..996e01967 --- /dev/null +++ b/frontend/src/app/render_wasm/api/texts.cljs @@ -0,0 +1,105 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC + +(ns app.render-wasm.api.texts + (:require + [app.render-wasm.api.fonts :as f] + [app.render-wasm.helpers :as h] + [app.render-wasm.mem :as mem] + [app.render-wasm.serializers :as sr] + [app.render-wasm.wasm :as wasm])) + +(defn utf8->buffer [text] + (let [encoder (js/TextEncoder.)] + (.encode encoder text))) + +(defn write-shape-text + ;; buffer has the following format: + ;; [ ] + [leaves paragraph] + (let [num-leaves (count leaves) + paragraph-attr-size 48 + leaf-attr-size 52 + metadata-size (+ 1 paragraph-attr-size (* num-leaves leaf-attr-size)) + text (apply str (map :text leaves)) + text-buffer (utf8->buffer text) + text-size (.-byteLength text-buffer) + buffer (js/ArrayBuffer. (+ metadata-size text-size)) + dview (js/DataView. buffer)] + + (.setUint32 dview 0 num-leaves) + + ;; Serialize paragraph attributes + (let [text-align (sr/serialize-text-align (:text-align paragraph)) + text-direction (sr/serialize-text-direction (:text-direction paragraph)) + text-decoration (sr/serialize-text-decoration (:text-decoration paragraph)) + text-transform (sr/serialize-text-transform (:text-transform paragraph)) + line-height (:line-height paragraph) + letter-spacing (:letter-spacing paragraph) + typography-ref-file (sr/serialize-uuid (:typography-ref-file paragraph)) + typography-ref-id (sr/serialize-uuid (:typography-ref-id paragraph))] + + (.setUint8 dview 4 text-align) + (.setUint8 dview 5 text-direction) + (.setUint8 dview 6 text-decoration) + (.setUint8 dview 7 text-transform) + + (.setFloat32 dview 8 line-height) + (.setFloat32 dview 12 letter-spacing) + + (.setUint32 dview 16 (aget typography-ref-file 0)) + (.setUint32 dview 20 (aget typography-ref-file 1)) + (.setUint32 dview 24 (aget typography-ref-file 2)) + (.setInt32 dview 28 (aget typography-ref-file 3)) + + (.setUint32 dview 32 (aget typography-ref-id 0)) + (.setUint32 dview 36 (aget typography-ref-id 1)) + (.setUint32 dview 40 (aget typography-ref-id 2)) + (.setInt32 dview 44 (aget typography-ref-id 3))) + + ;; Serialize leaves attributes + (loop [index 0 offset (+ 1 paragraph-attr-size)] + (when (< index num-leaves) + (let [leaf (nth leaves index) + font-style (f/serialize-font-style (:font-style leaf)) + font-size (:font-size leaf) + font-weight (:font-weight leaf) + font-id (f/serialize-font-id (:font-id leaf)) + font-family (hash (:font-family leaf)) + font-variant-id (sr/serialize-uuid (:font-variant-id leaf)) + text-length (count (:text leaf))] + + (.setUint8 dview offset font-style) + (.setFloat32 dview (+ offset 4) font-size) + (.setUint32 dview (+ offset 8) font-weight) + (.setUint32 dview (+ offset 12) (aget font-id 0)) + (.setUint32 dview (+ offset 16) (aget font-id 1)) + (.setUint32 dview (+ offset 20) (aget font-id 2)) + (.setInt32 dview (+ offset 24) (aget font-id 3)) + + (.setInt32 dview (+ offset 28) font-family) + + (.setUint32 dview (+ offset 32) (aget font-variant-id 0)) + (.setUint32 dview (+ offset 36) (aget font-variant-id 1)) + (.setUint32 dview (+ offset 40) (aget font-variant-id 2)) + (.setInt32 dview (+ offset 44) (aget font-variant-id 3)) + + (.setInt32 dview (+ offset 48) text-length) + + (recur (inc index) (+ offset leaf-attr-size))))) + + ;; Add text content to buffer + (let [text-offset metadata-size + buffer-u8 (js/Uint8Array. buffer)] + (.set buffer-u8 (js/Uint8Array. text-buffer) text-offset)) + + ;; Allocate memory and set buffer + (let [total-size (.-byteLength buffer) + metadata-offset (mem/alloc-bytes total-size) + heap (mem/get-heap-u8)] + (.set heap (js/Uint8Array. buffer) metadata-offset))) + + (h/call wasm/internal-module "_set_shape_text_content")) diff --git a/frontend/src/app/render_wasm/serializers.cljs b/frontend/src/app/render_wasm/serializers.cljs index e42ff8c45..ecd881444 100644 --- a/frontend/src/app/render_wasm/serializers.cljs +++ b/frontend/src/app/render_wasm/serializers.cljs @@ -44,6 +44,13 @@ (aset u32-arr 3 (aget buffer 3)) (js/Uint8Array. (.-buffer u32-arr)))) +(defn serialize-uuid + [id] + (if (nil? id) + [uuid/zero] + (let [as-uuid (uuid/uuid id)] + (uuid/get-u32 as-uuid)))) + (defn heapu32-set-u32 [value heap offset] (aset heap offset value)) @@ -266,9 +273,28 @@ :inner-shadow 1 0)) - (defn translate-structure-modifier-type [type] (case type :remove-children 1 :add-children 2)) + +(defn- serialize-enum + [value enum-map] + (get enum-map value 0)) + +(defn serialize-text-align + [text-align] + (serialize-enum text-align {"left" 0 "center" 1 "right" 2 "justify" 3})) + +(defn serialize-text-transform + [text-transform] + (serialize-enum text-transform {"none" 0 "uppercase" 1 "lowercase" 2 "capitalize" 3})) + +(defn serialize-text-decoration + [text-decoration] + (serialize-enum text-decoration {"none" 0 "underline" 1 "line-through" 2 "overline" 3})) + +(defn serialize-text-direction + [text-direction] + (serialize-enum text-direction {"ltr" 0 "rtl" 1})) diff --git a/render-wasm/src/shapes.rs b/render-wasm/src/shapes.rs index 5b44806b8..75cd72360 100644 --- a/render-wasm/src/shapes.rs +++ b/render-wasm/src/shapes.rs @@ -699,25 +699,10 @@ impl Shape { } } - pub fn add_text_leaf( - &mut self, - text_str: String, - font_family: FontFamily, - font_size: f32, - ) -> Result<(), String> { + pub fn add_paragraph(&mut self, paragraph: Paragraph) -> Result<(), String> { match self.shape_type { Type::Text(ref mut text) => { - text.add_leaf(text_str, font_family, font_size)?; - Ok(()) - } - _ => Err("Shape is not a text".to_string()), - } - } - - pub fn add_text_paragraph(&mut self) -> Result<(), String> { - match self.shape_type { - Type::Text(ref mut text) => { - text.add_paragraph(); + text.add_paragraph(paragraph); Ok(()) } _ => Err("Shape is not a text".to_string()), diff --git a/render-wasm/src/shapes/text.rs b/render-wasm/src/shapes/text.rs index 4a5d37af1..1ece55e8c 100644 --- a/render-wasm/src/shapes/text.rs +++ b/render-wasm/src/shapes/text.rs @@ -8,6 +8,8 @@ use skia_safe::{ }; use super::FontFamily; +use crate::utils::uuid_from_u32; +use crate::Uuid; #[derive(Debug, PartialEq, Clone)] pub struct TextContent { @@ -41,44 +43,24 @@ impl TextContent { self.bounds.y() } - pub fn add_paragraph(&mut self) { - let p = Paragraph::default(); - self.paragraphs.push(p); - } - - pub fn add_leaf( - &mut self, - text: String, - font_family: FontFamily, - font_size: f32, - ) -> Result<(), String> { - let paragraph = self - .paragraphs - .last_mut() - .ok_or("No paragraph to add text leaf to")?; - - paragraph.add_leaf(TextLeaf::new(text, font_family, font_size)); - - Ok(()) + pub fn add_paragraph(&mut self, paragraph: Paragraph) { + self.paragraphs.push(paragraph); } pub fn to_paragraphs(&self, fonts: &FontCollection) -> Vec { - let mut paragraph_style = ParagraphStyle::default(); - // TODO: read text direction, align, etc. from the shape - paragraph_style.set_text_direction(skia::textlayout::TextDirection::LTR); - self.paragraphs .iter() .map(|p| { + let paragraph_style = p.paragraph_to_style(); let mut builder = ParagraphBuilder::new(¶graph_style, fonts); - for leaf in &p.children { - let text_style = leaf.to_style(); + let text_style = leaf.to_style(&p); + let text = leaf.apply_text_transform(p.text_transform); + builder.push_style(&text_style); - builder.add_text(&leaf.text); + builder.add_text(&text); builder.pop(); } - builder.build() }) .collect() @@ -96,19 +78,90 @@ impl Default for TextContent { #[derive(Debug, PartialEq, Clone)] pub struct Paragraph { + text_align: u8, + text_decoration: u8, + text_direction: u8, + text_transform: u8, + line_height: f32, + letter_spacing: f32, + typography_ref_file: Uuid, + typography_ref_id: Uuid, children: Vec, } impl Default for Paragraph { fn default() -> Self { - Self { children: vec![] } + Self { + text_align: 0, + text_decoration: 0, + text_direction: 0, + text_transform: 0, + line_height: 1.0, + letter_spacing: 0.0, + typography_ref_file: Uuid::nil(), + typography_ref_id: Uuid::nil(), + children: vec![], + } } } impl Paragraph { + pub fn new( + text_align: u8, + text_decoration: u8, + text_direction: u8, + text_transform: u8, + line_height: f32, + letter_spacing: f32, + typography_ref_file: Uuid, + typography_ref_id: Uuid, + children: Vec, + ) -> Self { + Self { + text_align, + text_decoration, + text_direction, + text_transform, + line_height, + letter_spacing, + typography_ref_file, + typography_ref_id, + children, + } + } + + #[allow(dead_code)] + fn set_children(&mut self, children: Vec) { + self.children = children; + } + + #[allow(dead_code)] + pub fn get_children(&self) -> &Vec { + &self.children + } + + #[allow(dead_code)] fn add_leaf(&mut self, leaf: TextLeaf) { self.children.push(leaf); } + + pub fn paragraph_to_style(&self) -> ParagraphStyle { + let mut style = ParagraphStyle::default(); + style.set_text_align(match self.text_align { + 0 => skia::textlayout::TextAlign::Left, + 1 => skia::textlayout::TextAlign::Center, + 2 => skia::textlayout::TextAlign::Right, + 3 => skia::textlayout::TextAlign::Justify, + _ => skia::textlayout::TextAlign::Left, + }); + style.set_height(self.line_height); + style.set_text_direction(match self.text_direction { + 0 => skia::textlayout::TextDirection::LTR, + 1 => skia::textlayout::TextDirection::RTL, + _ => skia::textlayout::TextDirection::LTR, + }); + style + } } #[derive(Debug, PartialEq, Clone)] @@ -116,30 +169,250 @@ pub struct TextLeaf { text: String, font_family: FontFamily, font_size: f32, + font_style: u8, + font_weight: i32, + font_variant_id: Uuid, } impl TextLeaf { - pub fn new(text: String, font_family: FontFamily, font_size: f32) -> Self { + pub fn new( + text: String, + font_family: FontFamily, + font_size: f32, + font_style: u8, + font_weight: i32, + font_variant_id: Uuid, + ) -> Self { Self { text, font_family, font_size, + font_style, + font_weight, + font_variant_id, } } - pub fn to_style(&self) -> skia::textlayout::TextStyle { + pub fn to_style(&self, paragraph: &Paragraph) -> skia::textlayout::TextStyle { let mut style = skia::textlayout::TextStyle::default(); style.set_color(skia::Color::BLACK); style.set_font_size(self.font_size); + style.set_letter_spacing(paragraph.letter_spacing); + style.set_height(paragraph.line_height); + style.set_height_override(true); + style.set_decoration_type(match paragraph.text_decoration { + 0 => skia::textlayout::TextDecoration::NO_DECORATION, + 1 => skia::textlayout::TextDecoration::UNDERLINE, + 2 => skia::textlayout::TextDecoration::LINE_THROUGH, + 3 => skia::textlayout::TextDecoration::OVERLINE, + _ => skia::textlayout::TextDecoration::NO_DECORATION, + }); style.set_font_families(&[ self.serialized_font_family(), default_font(), DEFAULT_EMOJI_FONT.to_string(), ]); + style } fn serialized_font_family(&self) -> String { format!("{}", self.font_family) } + + pub fn apply_text_transform(&self, transform: u8) -> String { + match transform { + 1 => self.text.to_uppercase(), + 2 => self.text.to_lowercase(), + 3 => self + .text + .split_whitespace() + .map(|word| { + let mut chars = word.chars(); + match chars.next() { + Some(first) => first.to_uppercase().collect::() + chars.as_str(), + None => String::new(), + } + }) + .collect::>() + .join(" "), + _ => self.text.clone(), + } + } +} + +pub const RAW_PARAGRAPH_DATA_SIZE: usize = 48; +pub const RAW_LEAF_DATA_SIZE: usize = 52; + +#[repr(C)] +#[derive(Debug)] +pub struct RawTextLeafData { + font_style: u8, + font_size: f32, + font_weight: i32, + font_id: [u32; 4], + font_family: [u8; 4], + font_variant_id: [u32; 4], + text_length: u32, +} + +#[repr(C)] +#[derive(Debug)] +pub struct RawParagraphData { + text_align: u8, + text_transform: u8, + text_decoration: u8, + text_direction: u8, + line_height: f32, + letter_spacing: f32, + typography_ref_file: [u32; 4], + typography_ref_id: [u32; 4], +} + +impl From<[u8; RAW_PARAGRAPH_DATA_SIZE]> for RawParagraphData { + fn from(bytes: [u8; RAW_PARAGRAPH_DATA_SIZE]) -> Self { + Self { + text_align: bytes[4], + text_direction: bytes[5], + text_decoration: bytes[6], + text_transform: bytes[7], + line_height: f32::from_be_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]), + letter_spacing: f32::from_be_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]), + typography_ref_file: [ + u32::from_be_bytes([bytes[16], bytes[17], bytes[18], bytes[19]]), + u32::from_be_bytes([bytes[20], bytes[21], bytes[22], bytes[23]]), + u32::from_be_bytes([bytes[24], bytes[25], bytes[26], bytes[27]]), + u32::from_be_bytes([bytes[28], bytes[29], bytes[30], bytes[31]]), + ], + typography_ref_id: [ + u32::from_be_bytes([bytes[32], bytes[33], bytes[34], bytes[35]]), + u32::from_be_bytes([bytes[36], bytes[37], bytes[38], bytes[39]]), + u32::from_be_bytes([bytes[40], bytes[41], bytes[42], bytes[43]]), + u32::from_be_bytes([bytes[44], bytes[45], bytes[46], bytes[47]]), + ], + } + } +} + +pub struct RawTextData { + pub paragraph: Paragraph, +} + +impl RawTextData { + fn leaves_attrs_from_bytes(buffer: &[u8], num_leaves: usize) -> Vec { + let mut attrs = Vec::new(); + for i in 0..num_leaves { + let start = i * RAW_LEAF_DATA_SIZE; + let end = start + RAW_LEAF_DATA_SIZE; + let bytes = &buffer[start..end]; + let array: [u8; RAW_LEAF_DATA_SIZE] = bytes.try_into().expect("Slice length mismatch"); + let leaf_attrs = RawTextLeafData::from(array); + attrs.push(leaf_attrs); + } + attrs + } + + fn paragraph_attrs_from_bytes(buffer: &[u8]) -> RawParagraphData { + let bytes: [u8; RAW_PARAGRAPH_DATA_SIZE] = buffer[..RAW_PARAGRAPH_DATA_SIZE] + .try_into() + .expect("Slice length mismatch for paragraph attributes"); + RawParagraphData::from(bytes) + } + + fn text_from_bytes(buffer: &[u8], offset: usize, text_length: u32) -> (String, usize) { + let text_length = text_length as usize; + let text_end = offset + text_length; + + if text_end > buffer.len() { + panic!( + "Invalid text range: offset={}, text_end={}, buffer_len={}", + offset, + text_end, + buffer.len() + ); + } + + let text_utf8 = buffer[offset..text_end].to_vec(); + let text = String::from_utf8(text_utf8).expect("Invalid UTF-8 text"); + + (text, text_end) + } +} + +impl From<[u8; RAW_LEAF_DATA_SIZE]> for RawTextLeafData { + fn from(bytes: [u8; RAW_LEAF_DATA_SIZE]) -> Self { + Self { + font_style: bytes[0], + font_size: f32::from_be_bytes([bytes[4], bytes[5], bytes[6], bytes[7]]), + font_weight: i32::from_be_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]), + font_id: [ + u32::from_be_bytes([bytes[12], bytes[13], bytes[14], bytes[15]]), + u32::from_be_bytes([bytes[16], bytes[17], bytes[18], bytes[19]]), + u32::from_be_bytes([bytes[20], bytes[21], bytes[22], bytes[23]]), + u32::from_be_bytes([bytes[24], bytes[25], bytes[26], bytes[27]]), + ], + font_family: [bytes[28], bytes[29], bytes[30], bytes[31]], + font_variant_id: [ + u32::from_be_bytes([bytes[32], bytes[33], bytes[34], bytes[35]]), + u32::from_be_bytes([bytes[36], bytes[37], bytes[38], bytes[39]]), + u32::from_be_bytes([bytes[40], bytes[41], bytes[42], bytes[43]]), + u32::from_be_bytes([bytes[44], bytes[45], bytes[46], bytes[47]]), + ], + text_length: u32::from_be_bytes([bytes[48], bytes[49], bytes[50], bytes[51]]), + } + } +} + +impl From<&Vec> for RawTextData { + fn from(bytes: &Vec) -> Self { + let num_leaves = u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]) as usize; + + let paragraph_attrs = + RawTextData::paragraph_attrs_from_bytes(&bytes[..RAW_PARAGRAPH_DATA_SIZE]); + let leaves_attrs = + RawTextData::leaves_attrs_from_bytes(&bytes[1 + RAW_PARAGRAPH_DATA_SIZE..], num_leaves); + + let metadata_size = 1 + RAW_PARAGRAPH_DATA_SIZE + num_leaves * RAW_LEAF_DATA_SIZE; + let text_start = metadata_size; + let mut offset = text_start; + let mut text_leaves: Vec = Vec::new(); + + for attrs in leaves_attrs { + let (text, new_offset) = RawTextData::text_from_bytes(bytes, offset, attrs.text_length); + offset = new_offset; + + let font_id = uuid_from_u32(attrs.font_id); + let font_variant_id = uuid_from_u32(attrs.font_variant_id); + + let font_family = + FontFamily::new(font_id, attrs.font_weight as u32, attrs.font_style.into()); + + let text_leaf = TextLeaf::new( + text, + font_family, + attrs.font_size, + attrs.font_style, + attrs.font_weight, + font_variant_id, + ); + text_leaves.push(text_leaf); + } + + let typography_ref_file = uuid_from_u32(paragraph_attrs.typography_ref_file); + let typography_ref_id = uuid_from_u32(paragraph_attrs.typography_ref_id); + + let paragraph = Paragraph::new( + paragraph_attrs.text_align, + paragraph_attrs.text_decoration, + paragraph_attrs.text_direction, + paragraph_attrs.text_transform, + paragraph_attrs.line_height, + paragraph_attrs.letter_spacing, + typography_ref_file, + typography_ref_id, + text_leaves.clone(), + ); + + Self { paragraph } + } } diff --git a/render-wasm/src/utils.rs b/render-wasm/src/utils.rs index 96062002f..3b51efba0 100644 --- a/render-wasm/src/utils.rs +++ b/render-wasm/src/utils.rs @@ -14,3 +14,7 @@ pub fn uuid_to_u32_quartet(id: &Uuid) -> (u32, u32, u32, u32) { let lolo32 = lo as u32; (hihi32, hilo32, lohi32, lolo32) } + +pub fn uuid_from_u32(id: [u32; 4]) -> Uuid { + uuid_from_u32_quartet(id[0], id[1], id[2], id[3]) +} diff --git a/render-wasm/src/wasm/text.rs b/render-wasm/src/wasm/text.rs index 8ab9062ea..1fb3fd401 100644 --- a/render-wasm/src/wasm/text.rs +++ b/render-wasm/src/wasm/text.rs @@ -1,6 +1,5 @@ use crate::mem; -use crate::shapes::FontFamily; -use crate::utils::uuid_from_u32_quartet; +use crate::shapes::RawTextData; use crate::with_current_shape; use crate::STATE; @@ -12,37 +11,14 @@ pub extern "C" fn clear_shape_text() { } #[no_mangle] -pub extern "C" fn add_text_paragraph() { - with_current_shape!(state, |shape: &mut Shape| { - let res = shape.add_text_paragraph(); - if let Err(err) = res { - eprintln!("{}", err); - } - }); -} - -#[no_mangle] -pub extern "C" fn add_text_leaf( - a: u32, - b: u32, - c: u32, - d: u32, - weight: u32, - style: u8, - font_size: f32, -) { - let font_id = uuid_from_u32_quartet(a, b, c, d); - let font_family = FontFamily::new(font_id, weight, style.into()); +pub extern "C" fn set_shape_text_content() { let bytes = mem::bytes(); - - let text = unsafe { - String::from_utf8_unchecked(bytes) // TODO: handle this error - }; - with_current_shape!(state, |shape: &mut Shape| { - let res = shape.add_text_leaf(text, font_family, font_size); - if let Err(err) = res { - eprintln!("{}", err); - } + let raw_text_data = RawTextData::from(&bytes); + shape + .add_paragraph(raw_text_data.paragraph) + .expect("Failed to add paragraph"); }); + + mem::free_bytes(); }