mirror of
https://github.com/penpot/penpot.git
synced 2025-04-29 18:16:26 +02:00
🔧 Parse text leaves all at once
This commit is contained in:
parent
18dea6c3a3
commit
83f72f3e41
8 changed files with 457 additions and 117 deletions
|
@ -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
|
||||
|
|
|
@ -175,3 +175,4 @@
|
|||
:style style
|
||||
:weight weight}]
|
||||
(store-font-id font-data asset-id))) fonts))
|
||||
|
||||
|
|
105
frontend/src/app/render_wasm/api/texts.cljs
Normal file
105
frontend/src/app/render_wasm/api/texts.cljs
Normal file
|
@ -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:
|
||||
;; [<num-leaves> <paragraph_attributes> <leaves_attributes> <text>]
|
||||
[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"))
|
|
@ -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}))
|
||||
|
|
|
@ -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()),
|
||||
|
|
|
@ -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<skia::textlayout::Paragraph> {
|
||||
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<TextLeaf>,
|
||||
}
|
||||
|
||||
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<TextLeaf>,
|
||||
) -> 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<TextLeaf>) {
|
||||
self.children = children;
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn get_children(&self) -> &Vec<TextLeaf> {
|
||||
&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::<String>() + chars.as_str(),
|
||||
None => String::new(),
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
.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<RawTextLeafData> {
|
||||
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<u8>> for RawTextData {
|
||||
fn from(bytes: &Vec<u8>) -> 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<TextLeaf> = 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 }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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])
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue