mirror of
https://github.com/penpot/penpot.git
synced 2026-05-28 11:23:42 +00:00
WIP
This commit is contained in:
parent
7d36bc4025
commit
baa90ea8bf
@ -379,6 +379,23 @@
|
|||||||
(->> (rx/from added)
|
(->> (rx/from added)
|
||||||
(rx/map process-wasm-object)))))))
|
(rx/map process-wasm-object)))))))
|
||||||
|
|
||||||
|
(when render-wasm?
|
||||||
|
(->> stream
|
||||||
|
(rx/filter (ptk/type? :wasm/position-data))
|
||||||
|
(rx/map deref)
|
||||||
|
(rx/filter
|
||||||
|
(fn [{:keys [position-data]}]
|
||||||
|
(some? position-data)))
|
||||||
|
(rx/map
|
||||||
|
(fn [{:keys [id position-data]}]
|
||||||
|
(prn "???" id position-data)
|
||||||
|
(dwsh/update-shapes
|
||||||
|
[id]
|
||||||
|
(fn [shape]
|
||||||
|
(.log js/console (clj->js shape))
|
||||||
|
(assoc shape :position-data position-data))
|
||||||
|
{:ignore-wasm? true})))))
|
||||||
|
|
||||||
(->> stream
|
(->> stream
|
||||||
(rx/filter dch/commit?)
|
(rx/filter dch/commit?)
|
||||||
(rx/map deref)
|
(rx/map deref)
|
||||||
|
|||||||
@ -50,7 +50,8 @@
|
|||||||
([ids update-fn] (update-shapes ids update-fn nil))
|
([ids update-fn] (update-shapes ids update-fn nil))
|
||||||
([ids update-fn
|
([ids update-fn
|
||||||
{:keys [reg-objects? save-undo? stack-undo? attrs ignore-tree page-id
|
{:keys [reg-objects? save-undo? stack-undo? attrs ignore-tree page-id
|
||||||
ignore-touched undo-group with-objects? changed-sub-attr]
|
ignore-touched undo-group with-objects? changed-sub-attr
|
||||||
|
ignore-wasm?]
|
||||||
:or {reg-objects? false
|
:or {reg-objects? false
|
||||||
save-undo? true
|
save-undo? true
|
||||||
stack-undo? false
|
stack-undo? false
|
||||||
@ -89,6 +90,7 @@
|
|||||||
:ignore-tree ignore-tree
|
:ignore-tree ignore-tree
|
||||||
:ignore-touched ignore-touched
|
:ignore-touched ignore-touched
|
||||||
:with-objects? with-objects?})
|
:with-objects? with-objects?})
|
||||||
|
(assoc :ignore-wasm? ignore-wasm?)
|
||||||
(cond-> undo-group
|
(cond-> undo-group
|
||||||
(pcb/set-undo-group undo-group)))
|
(pcb/set-undo-group undo-group)))
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
(ns app.main.ui.workspace.viewport.debug
|
(ns app.main.ui.workspace.viewport.debug
|
||||||
(:require
|
(:require
|
||||||
|
[app.render-wasm.api :as wasm.api]
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.files.helpers :as cfh]
|
[app.common.files.helpers :as cfh]
|
||||||
@ -275,3 +276,29 @@
|
|||||||
:y2 (:y end-p)
|
:y2 (:y end-p)
|
||||||
:style {:stroke "red"
|
:style {:stroke "red"
|
||||||
:stroke-width (/ 1 zoom)}}]))]))))
|
:stroke-width (/ 1 zoom)}}]))]))))
|
||||||
|
|
||||||
|
(mf/defc debug-text-position-data
|
||||||
|
{::mf/wrap-props false}
|
||||||
|
[props]
|
||||||
|
(let [objects (unchecked-get props "objects")
|
||||||
|
zoom (unchecked-get props "zoom")
|
||||||
|
selected-shapes (unchecked-get props "selected-shapes")
|
||||||
|
|
||||||
|
selected-text
|
||||||
|
(when (and (= (count selected-shapes) 1) (= :text (-> selected-shapes first :type)))
|
||||||
|
(first selected-shapes))
|
||||||
|
|
||||||
|
position-data
|
||||||
|
(when selected-text
|
||||||
|
(wasm.api/calculate-position-data selected-text))]
|
||||||
|
|
||||||
|
(for [{:keys [x y width height]} position-data]
|
||||||
|
[:rect {:x x
|
||||||
|
:y y
|
||||||
|
:width width
|
||||||
|
:height height
|
||||||
|
:fill "none"
|
||||||
|
:strokeWidth 1
|
||||||
|
:stroke "red"}]
|
||||||
|
|
||||||
|
)))
|
||||||
|
|||||||
@ -635,6 +635,10 @@
|
|||||||
:hover-top-frame-id @hover-top-frame-id
|
:hover-top-frame-id @hover-top-frame-id
|
||||||
:zoom zoom}])
|
:zoom zoom}])
|
||||||
|
|
||||||
|
[:& wvd/debug-text-position-data {:selected-shapes selected-shapes
|
||||||
|
:objects base-objects
|
||||||
|
:zoom zoom}]
|
||||||
|
|
||||||
(when show-selection-handlers?
|
(when show-selection-handlers?
|
||||||
[:g.selection-handlers {:clipPath "url(#clip-handlers)"}
|
[:g.selection-handlers {:clipPath "url(#clip-handlers)"}
|
||||||
(when-not text-editing?
|
(when-not text-editing?
|
||||||
|
|||||||
@ -7,6 +7,9 @@
|
|||||||
(ns app.render-wasm.api
|
(ns app.render-wasm.api
|
||||||
"A WASM based render API"
|
"A WASM based render API"
|
||||||
(:require
|
(:require
|
||||||
|
[potok.v2.core :as ptk]
|
||||||
|
[app.main.data.helpers :as dsh]
|
||||||
|
[app.main.ui.shapes.text]
|
||||||
["react-dom/server" :as rds]
|
["react-dom/server" :as rds]
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
@ -127,10 +130,18 @@
|
|||||||
(render ts)))))
|
(render ts)))))
|
||||||
|
|
||||||
(declare get-text-dimensions)
|
(declare get-text-dimensions)
|
||||||
|
(declare calculate-position-data)
|
||||||
|
|
||||||
(defn update-text-rect!
|
(defn update-text-rect!
|
||||||
[id]
|
[id]
|
||||||
(when wasm/context-initialized?
|
(when wasm/context-initialized?
|
||||||
|
(let [objects (dsh/lookup-page-objects @st/state)
|
||||||
|
shape (get objects id)
|
||||||
|
position-data (calculate-position-data shape)]
|
||||||
|
(.log js/console (:name shape) (clj->js position-data))
|
||||||
|
(st/emit!
|
||||||
|
(ptk/data-event :wasm/position-data {:id id :position-data position-data})))
|
||||||
|
|
||||||
(mw/emit!
|
(mw/emit!
|
||||||
{:cmd :index/update-text-rect
|
{:cmd :index/update-text-rect
|
||||||
:page-id (:current-page-id @st/state)
|
:page-id (:current-page-id @st/state)
|
||||||
@ -988,10 +999,7 @@
|
|||||||
(run!
|
(run!
|
||||||
(fn [id]
|
(fn [id]
|
||||||
(f/update-text-layout id)
|
(f/update-text-layout id)
|
||||||
(mw/emit! {:cmd :index/update-text-rect
|
(update-text-rect! id)))))
|
||||||
:page-id (:current-page-id @st/state)
|
|
||||||
:shape-id id
|
|
||||||
:dimensions (get-text-dimensions id)})))))
|
|
||||||
|
|
||||||
(defn process-pending
|
(defn process-pending
|
||||||
([shapes thumbnails full on-complete]
|
([shapes thumbnails full on-complete]
|
||||||
@ -1347,6 +1355,58 @@
|
|||||||
(h/call wasm/internal-module "_end_temp_objects")
|
(h/call wasm/internal-module "_end_temp_objects")
|
||||||
content)))
|
content)))
|
||||||
|
|
||||||
|
(def POSITION-DATA-U8-SIZE 36)
|
||||||
|
(def POSITION-DATA-U32-SIZE (/ POSITION-DATA-U8-SIZE 4))
|
||||||
|
|
||||||
|
(defn calculate-position-data
|
||||||
|
[shape]
|
||||||
|
(use-shape (:id shape))
|
||||||
|
(let [heapf32 (mem/get-heap-f32)
|
||||||
|
heapu32 (mem/get-heap-u32)
|
||||||
|
offset (-> (h/call wasm/internal-module "_calc_position_data")
|
||||||
|
(mem/->offset-32))
|
||||||
|
length (aget heapu32 offset)
|
||||||
|
|
||||||
|
max-offset (+ offset 1 (* length POSITION-DATA-U32-SIZE))
|
||||||
|
|
||||||
|
result
|
||||||
|
(loop [result (transient [])
|
||||||
|
offset (inc offset)]
|
||||||
|
(if (< offset max-offset)
|
||||||
|
(let [entry (dr/read-position-data-entry heapu32 heapf32 offset)]
|
||||||
|
(recur (conj! result entry)
|
||||||
|
(+ offset POSITION-DATA-U32-SIZE)))
|
||||||
|
(persistent! result)))
|
||||||
|
|
||||||
|
result
|
||||||
|
(->> result
|
||||||
|
(mapv
|
||||||
|
(fn [{:keys [paragraph span start-pos end-pos direction x y width height]}]
|
||||||
|
(let [content (:content shape)
|
||||||
|
element (-> content :children
|
||||||
|
(get 0) :children ;; paragraph-set
|
||||||
|
(get paragraph) :children ;; paragraph
|
||||||
|
(get span))
|
||||||
|
text (subs (:text element) start-pos end-pos)]
|
||||||
|
|
||||||
|
{:x x
|
||||||
|
:y y
|
||||||
|
:width width
|
||||||
|
:height height
|
||||||
|
:direction direction
|
||||||
|
:font-family (get element :font-family)
|
||||||
|
:font-size (get element :font-size)
|
||||||
|
:font-weight (get element :font-weight)
|
||||||
|
:text-transform (get element :text-transform)
|
||||||
|
:text-decoration (get element :text-decoration)
|
||||||
|
:letter-spacing (get element :letter-spacing)
|
||||||
|
:font-style (get element :font-style)
|
||||||
|
:fills (get element :fills)
|
||||||
|
:text text}))))]
|
||||||
|
(mem/free)
|
||||||
|
|
||||||
|
result))
|
||||||
|
|
||||||
(defn init-wasm-module
|
(defn init-wasm-module
|
||||||
[module]
|
[module]
|
||||||
(let [default-fn (unchecked-get module "default")
|
(let [default-fn (unchecked-get module "default")
|
||||||
|
|||||||
@ -45,4 +45,23 @@
|
|||||||
:center (gpt/point cx cy)
|
:center (gpt/point cx cy)
|
||||||
:transform (gmt/matrix a b c d e f)}))
|
:transform (gmt/matrix a b c d e f)}))
|
||||||
|
|
||||||
|
(defn read-position-data-entry
|
||||||
|
[heapu32 heapf32 offset]
|
||||||
|
(let [paragraph (aget heapu32 (+ offset 0))
|
||||||
|
span (aget heapu32 (+ offset 1))
|
||||||
|
start-pos (aget heapu32 (+ offset 2))
|
||||||
|
end-pos (aget heapu32 (+ offset 3))
|
||||||
|
x (aget heapf32 (+ offset 4))
|
||||||
|
y (aget heapf32 (+ offset 5))
|
||||||
|
width (aget heapf32 (+ offset 6))
|
||||||
|
height (aget heapf32 (+ offset 7))
|
||||||
|
direction (aget heapu32 (+ offset 8))]
|
||||||
|
{:paragraph paragraph
|
||||||
|
:span span
|
||||||
|
:start-pos start-pos
|
||||||
|
:end-pos end-pos
|
||||||
|
:x x
|
||||||
|
:y y
|
||||||
|
:width width
|
||||||
|
:height height
|
||||||
|
:direction direction}))
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
(ns debug
|
(ns debug
|
||||||
(:require
|
(:require
|
||||||
|
[app.render-wasm.api :as wasm.api]
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.data.macros :as dm]
|
[app.common.data.macros :as dm]
|
||||||
[app.common.files.repair :as cfr]
|
[app.common.files.repair :as cfr]
|
||||||
@ -456,3 +457,10 @@
|
|||||||
(defn ^:export network-averages
|
(defn ^:export network-averages
|
||||||
[]
|
[]
|
||||||
(.log js/console (clj->js @http/network-averages)))
|
(.log js/console (clj->js @http/network-averages)))
|
||||||
|
|
||||||
|
(defn ^:export tmp
|
||||||
|
[]
|
||||||
|
(let [objects (dsh/lookup-page-objects @st/state)
|
||||||
|
shape (->> (get-selected @st/state) (first) (get objects))]
|
||||||
|
(wasm.api/calculate-position-data shape))
|
||||||
|
)
|
||||||
|
|||||||
@ -847,6 +847,8 @@ impl RenderState {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// text::render_position_data(self, fills_surface_id, &shape, &text_content);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
|||||||
@ -4,6 +4,7 @@ use crate::{
|
|||||||
shapes::{
|
shapes::{
|
||||||
merge_fills, set_paint_fill, ParagraphBuilderGroup, Stroke, StrokeKind, TextContent,
|
merge_fills, set_paint_fill, ParagraphBuilderGroup, Stroke, StrokeKind, TextContent,
|
||||||
VerticalAlign,
|
VerticalAlign,
|
||||||
|
calc_position_data
|
||||||
},
|
},
|
||||||
utils::{get_fallback_fonts, get_font_collection},
|
utils::{get_fallback_fonts, get_font_collection},
|
||||||
};
|
};
|
||||||
@ -504,6 +505,29 @@ pub fn render_as_path(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub fn render_position_data(
|
||||||
|
render_state: &mut RenderState,
|
||||||
|
surface_id: SurfaceId,
|
||||||
|
shape: &Shape,
|
||||||
|
text_content: &TextContent
|
||||||
|
) {
|
||||||
|
let position_data = calc_position_data(shape, text_content);
|
||||||
|
|
||||||
|
let mut paint = skia::Paint::default();
|
||||||
|
paint.set_style(skia::PaintStyle::Stroke);
|
||||||
|
paint.set_color(skia::Color::from_argb(255, 255, 0, 0));
|
||||||
|
paint.set_stroke_width(2.);
|
||||||
|
|
||||||
|
for pd in position_data {
|
||||||
|
let rect = Rect::from_xywh(pd.x, pd.y, pd.width, pd.height);
|
||||||
|
render_state.surfaces
|
||||||
|
.canvas(surface_id)
|
||||||
|
.draw_rect(rect, &paint);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// How to use it?
|
// How to use it?
|
||||||
// Type::Text(text_content) => {
|
// Type::Text(text_content) => {
|
||||||
// self.surfaces
|
// self.surfaces
|
||||||
|
|||||||
@ -204,6 +204,49 @@ fn intersects(paragraph: &skia_safe::textlayout::Paragraph, x: f32, y: f32) -> b
|
|||||||
rects.iter().any(|r| r.rect.contains(&Point::new(x, y)))
|
rects.iter().any(|r| r.rect.contains(&Point::new(x, y)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Performs a text auto layout without width limits.
|
||||||
|
/// This should be the same as text_auto_layout.
|
||||||
|
pub fn build_paragraphs_from_paragraph_builders(
|
||||||
|
paragraph_builders: &mut [ParagraphBuilderGroup],
|
||||||
|
width: f32,
|
||||||
|
) -> Vec<Vec<skia::textlayout::Paragraph>> {
|
||||||
|
let paragraphs = paragraph_builders
|
||||||
|
.iter_mut()
|
||||||
|
.map(|builders| {
|
||||||
|
builders
|
||||||
|
.iter_mut()
|
||||||
|
.map(|builder| {
|
||||||
|
let mut paragraph = builder.build();
|
||||||
|
// For auto-width, always layout with infinite width first to get intrinsic width
|
||||||
|
paragraph.layout(width);
|
||||||
|
paragraph
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
paragraphs
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calculate the normalized line height from paragraph builders
|
||||||
|
pub fn calculate_normalized_line_height(
|
||||||
|
paragraph_builders: &mut [ParagraphBuilderGroup],
|
||||||
|
width: f32,
|
||||||
|
) -> f32 {
|
||||||
|
let mut normalized_line_height = 0.0;
|
||||||
|
for paragraph_builder_group in paragraph_builders.iter_mut() {
|
||||||
|
for paragraph_builder in paragraph_builder_group.iter_mut() {
|
||||||
|
let mut paragraph = paragraph_builder.build();
|
||||||
|
paragraph.layout(width);
|
||||||
|
let baseline = paragraph.ideographic_baseline();
|
||||||
|
if baseline > normalized_line_height {
|
||||||
|
normalized_line_height = baseline;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
normalized_line_height
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone)]
|
#[derive(Debug, PartialEq, Clone)]
|
||||||
pub struct TextContent {
|
pub struct TextContent {
|
||||||
pub paragraphs: Vec<Paragraph>,
|
pub paragraphs: Vec<Paragraph>,
|
||||||
@ -440,59 +483,15 @@ impl TextContent {
|
|||||||
paragraph_group
|
paragraph_group
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Performs a text auto layout without width limits.
|
|
||||||
/// This should be the same as text_auto_layout.
|
|
||||||
fn build_paragraphs_from_paragraph_builders(
|
|
||||||
&self,
|
|
||||||
paragraph_builders: &mut [ParagraphBuilderGroup],
|
|
||||||
width: f32,
|
|
||||||
) -> Vec<Vec<skia::textlayout::Paragraph>> {
|
|
||||||
let paragraphs = paragraph_builders
|
|
||||||
.iter_mut()
|
|
||||||
.map(|builders| {
|
|
||||||
builders
|
|
||||||
.iter_mut()
|
|
||||||
.map(|builder| {
|
|
||||||
let mut paragraph = builder.build();
|
|
||||||
// For auto-width, always layout with infinite width first to get intrinsic width
|
|
||||||
paragraph.layout(width);
|
|
||||||
paragraph
|
|
||||||
})
|
|
||||||
.collect()
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
paragraphs
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Calculate the normalized line height from paragraph builders
|
|
||||||
fn calculate_normalized_line_height(
|
|
||||||
&self,
|
|
||||||
paragraph_builders: &mut [ParagraphBuilderGroup],
|
|
||||||
width: f32,
|
|
||||||
) -> f32 {
|
|
||||||
let mut normalized_line_height = 0.0;
|
|
||||||
for paragraph_builder_group in paragraph_builders.iter_mut() {
|
|
||||||
for paragraph_builder in paragraph_builder_group.iter_mut() {
|
|
||||||
let mut paragraph = paragraph_builder.build();
|
|
||||||
paragraph.layout(width);
|
|
||||||
let baseline = paragraph.ideographic_baseline();
|
|
||||||
if baseline > normalized_line_height {
|
|
||||||
normalized_line_height = baseline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
normalized_line_height
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Performs an Auto Width text layout.
|
/// Performs an Auto Width text layout.
|
||||||
fn text_layout_auto_width(&self) -> TextContentLayoutResult {
|
fn text_layout_auto_width(&self) -> TextContentLayoutResult {
|
||||||
let mut paragraph_builders = self.paragraph_builder_group_from_text(None);
|
let mut paragraph_builders = self.paragraph_builder_group_from_text(None);
|
||||||
|
|
||||||
let normalized_line_height =
|
let normalized_line_height =
|
||||||
self.calculate_normalized_line_height(&mut paragraph_builders, f32::MAX);
|
calculate_normalized_line_height(&mut paragraph_builders, f32::MAX);
|
||||||
|
|
||||||
let paragraphs =
|
let paragraphs =
|
||||||
self.build_paragraphs_from_paragraph_builders(&mut paragraph_builders, f32::MAX);
|
build_paragraphs_from_paragraph_builders(&mut paragraph_builders, f32::MAX);
|
||||||
|
|
||||||
let (width, height) =
|
let (width, height) =
|
||||||
paragraphs
|
paragraphs
|
||||||
@ -521,10 +520,10 @@ impl TextContent {
|
|||||||
let mut paragraph_builders = self.paragraph_builder_group_from_text(None);
|
let mut paragraph_builders = self.paragraph_builder_group_from_text(None);
|
||||||
|
|
||||||
let normalized_line_height =
|
let normalized_line_height =
|
||||||
self.calculate_normalized_line_height(&mut paragraph_builders, width);
|
calculate_normalized_line_height(&mut paragraph_builders, width);
|
||||||
|
|
||||||
let paragraphs =
|
let paragraphs =
|
||||||
self.build_paragraphs_from_paragraph_builders(&mut paragraph_builders, width);
|
build_paragraphs_from_paragraph_builders(&mut paragraph_builders, width);
|
||||||
let height = paragraphs
|
let height = paragraphs
|
||||||
.iter()
|
.iter()
|
||||||
.flatten()
|
.flatten()
|
||||||
@ -546,10 +545,10 @@ impl TextContent {
|
|||||||
let mut paragraph_builders = self.paragraph_builder_group_from_text(None);
|
let mut paragraph_builders = self.paragraph_builder_group_from_text(None);
|
||||||
|
|
||||||
let normalized_line_height =
|
let normalized_line_height =
|
||||||
self.calculate_normalized_line_height(&mut paragraph_builders, width);
|
calculate_normalized_line_height(&mut paragraph_builders, width);
|
||||||
|
|
||||||
let paragraphs =
|
let paragraphs =
|
||||||
self.build_paragraphs_from_paragraph_builders(&mut paragraph_builders, width);
|
build_paragraphs_from_paragraph_builders(&mut paragraph_builders, width);
|
||||||
let paragraph_height = paragraphs
|
let paragraph_height = paragraphs
|
||||||
.iter()
|
.iter()
|
||||||
.flatten()
|
.flatten()
|
||||||
@ -577,7 +576,7 @@ impl TextContent {
|
|||||||
pub fn get_height(&self, width: f32) -> f32 {
|
pub fn get_height(&self, width: f32) -> f32 {
|
||||||
let mut paragraph_builders = self.paragraph_builder_group_from_text(None);
|
let mut paragraph_builders = self.paragraph_builder_group_from_text(None);
|
||||||
let paragraphs =
|
let paragraphs =
|
||||||
self.build_paragraphs_from_paragraph_builders(&mut paragraph_builders, width);
|
build_paragraphs_from_paragraph_builders(&mut paragraph_builders, width);
|
||||||
let paragraph_height = paragraphs
|
let paragraph_height = paragraphs
|
||||||
.iter()
|
.iter()
|
||||||
.flatten()
|
.flatten()
|
||||||
@ -734,7 +733,7 @@ impl TextContent {
|
|||||||
let width = self.width();
|
let width = self.width();
|
||||||
let mut paragraph_builders = self.paragraph_builder_group_from_text(None);
|
let mut paragraph_builders = self.paragraph_builder_group_from_text(None);
|
||||||
let paragraphs =
|
let paragraphs =
|
||||||
self.build_paragraphs_from_paragraph_builders(&mut paragraph_builders, width);
|
build_paragraphs_from_paragraph_builders(&mut paragraph_builders, width);
|
||||||
|
|
||||||
paragraphs
|
paragraphs
|
||||||
.iter()
|
.iter()
|
||||||
@ -1045,3 +1044,121 @@ impl TextSpan {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PositionData {
|
||||||
|
pub paragraph: u32, // 4
|
||||||
|
pub span: u32, // 4
|
||||||
|
pub start_pos: u32, // 4
|
||||||
|
pub end_pos: u32, // 4
|
||||||
|
pub x: f32, // 4
|
||||||
|
pub y: f32, // 4
|
||||||
|
pub width: f32, // 4
|
||||||
|
pub height: f32, // 4
|
||||||
|
pub direction: u32 // 4, u32 to align with 32 bytes
|
||||||
|
}
|
||||||
|
|
||||||
|
fn direction_to_int(direction: TextDirection) -> u32 {
|
||||||
|
match direction {
|
||||||
|
TextDirection::RTL => 0,
|
||||||
|
TextDirection::LTR => 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//fn get_unicode_substring(full_text: &str, start: usize, end: usize) -> String {
|
||||||
|
// let chars: Vec<char> = full_text.chars().collect();
|
||||||
|
// chars[start..end].iter().collect()
|
||||||
|
//}
|
||||||
|
|
||||||
|
pub fn calc_position_data(
|
||||||
|
shape: &Shape,
|
||||||
|
text_content: &TextContent
|
||||||
|
) -> Vec<PositionData> {
|
||||||
|
let mut result: Vec<PositionData> = Vec::default();
|
||||||
|
let mut text_content = text_content.clone();
|
||||||
|
text_content.update_layout(shape.selrect);
|
||||||
|
let rect = text_content.content_rect(&shape.selrect, shape.vertical_align);
|
||||||
|
|
||||||
|
let x = rect.x();
|
||||||
|
let mut y = rect.y();
|
||||||
|
|
||||||
|
let fonts = get_font_collection();
|
||||||
|
let fallback_fonts = get_fallback_fonts();
|
||||||
|
|
||||||
|
for (paragraph_index, paragraph) in text_content.paragraphs().iter().enumerate() {
|
||||||
|
let mut paragraph_text = String::default();
|
||||||
|
let paragraph_style = paragraph.paragraph_to_style();
|
||||||
|
let mut builder = ParagraphBuilder::new(¶graph_style, fonts);
|
||||||
|
|
||||||
|
let mut span_ranges: Vec<(usize, usize, usize)> = vec![];
|
||||||
|
let mut cur = 0;
|
||||||
|
|
||||||
|
for (span_index, span) in paragraph.children().iter().enumerate() {
|
||||||
|
let text_style = span.to_style(
|
||||||
|
&text_content.bounds(),
|
||||||
|
fallback_fonts,
|
||||||
|
false,
|
||||||
|
paragraph.line_height(),
|
||||||
|
);
|
||||||
|
let text: String = span.apply_text_transform();
|
||||||
|
builder.push_style(&text_style);
|
||||||
|
builder.add_text(&text);
|
||||||
|
|
||||||
|
span_ranges.push((cur, cur + text.len(), span_index));
|
||||||
|
cur += text.len();
|
||||||
|
|
||||||
|
paragraph_text += &text;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut p = builder.build();
|
||||||
|
p.layout(shape.selrect.width());
|
||||||
|
|
||||||
|
for (start, end, span_index) in span_ranges {
|
||||||
|
let rects = p.get_rects_for_range(
|
||||||
|
start .. end,
|
||||||
|
RectHeightStyle::Tight,
|
||||||
|
RectWidthStyle::Tight,
|
||||||
|
);
|
||||||
|
|
||||||
|
for textbox in rects {
|
||||||
|
let direction = textbox.direct;
|
||||||
|
let mut rect = textbox.rect;
|
||||||
|
let cy = rect.top + rect.height() / 2.0;
|
||||||
|
|
||||||
|
let start_pos = p
|
||||||
|
.get_glyph_position_at_coordinate((rect.left + 0.1, cy))
|
||||||
|
.position as usize;
|
||||||
|
|
||||||
|
let end_pos = p
|
||||||
|
.get_glyph_position_at_coordinate((rect.right - 0.1, cy))
|
||||||
|
.position as usize;
|
||||||
|
|
||||||
|
// start_pos and end_pos are relative to the paragraph but we
|
||||||
|
// want it relative to the span
|
||||||
|
|
||||||
|
let start_pos = start_pos - start;
|
||||||
|
let end_pos = end_pos - start;
|
||||||
|
|
||||||
|
rect.offset((x, y));
|
||||||
|
|
||||||
|
result.push(PositionData {
|
||||||
|
paragraph: paragraph_index as u32,
|
||||||
|
span: span_index as u32,
|
||||||
|
start_pos: start_pos as u32,
|
||||||
|
end_pos: end_pos as u32,
|
||||||
|
x: rect.x(),
|
||||||
|
y: rect.y(),
|
||||||
|
width: rect.width(),
|
||||||
|
height: rect.height(),
|
||||||
|
direction: direction_to_int(direction)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
y += p.height();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|||||||
@ -2,13 +2,13 @@ use macros::ToJs;
|
|||||||
|
|
||||||
use super::{fills::RawFillData, fonts::RawFontStyle};
|
use super::{fills::RawFillData, fonts::RawFontStyle};
|
||||||
use crate::math::{Matrix, Point};
|
use crate::math::{Matrix, Point};
|
||||||
use crate::mem;
|
use crate::mem::{self, SerializableResult};
|
||||||
use crate::shapes::{
|
use crate::shapes::{
|
||||||
self, GrowType, Shape, TextAlign, TextDecoration, TextDirection, TextTransform, Type,
|
self, GrowType, Shape, TextAlign, TextDecoration, TextDirection, TextTransform, Type,
|
||||||
};
|
};
|
||||||
use crate::utils::{uuid_from_u32, uuid_from_u32_quartet};
|
use crate::utils::{uuid_from_u32, uuid_from_u32_quartet};
|
||||||
use crate::{
|
use crate::{
|
||||||
with_current_shape_mut, with_state, with_state_mut, with_state_mut_current_shape, STATE,
|
with_current_shape, with_current_shape_mut, with_state, with_state_mut, with_state_mut_current_shape, STATE,
|
||||||
};
|
};
|
||||||
|
|
||||||
const RAW_SPAN_DATA_SIZE: usize = std::mem::size_of::<RawTextSpan>();
|
const RAW_SPAN_DATA_SIZE: usize = std::mem::size_of::<RawTextSpan>();
|
||||||
@ -411,3 +411,37 @@ pub extern "C" fn get_caret_position_at(x: f32, y: f32) -> i32 {
|
|||||||
});
|
});
|
||||||
-1
|
-1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const RAW_POSITION_DATA_SIZE: usize = size_of::<shapes::PositionData>();
|
||||||
|
|
||||||
|
impl SerializableResult for shapes::PositionData {
|
||||||
|
type BytesType = [u8; RAW_POSITION_DATA_SIZE];
|
||||||
|
|
||||||
|
fn from_bytes(bytes: Self::BytesType) -> Self {
|
||||||
|
unsafe { std::mem::transmute(bytes) }
|
||||||
|
}
|
||||||
|
fn as_bytes(&self) -> Self::BytesType {
|
||||||
|
let ptr = self as *const shapes::PositionData as *const u8;
|
||||||
|
let bytes: &[u8] = unsafe { std::slice::from_raw_parts(ptr, RAW_POSITION_DATA_SIZE) };
|
||||||
|
let mut result = [0; RAW_POSITION_DATA_SIZE];
|
||||||
|
result.copy_from_slice(bytes);
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
// The generic trait doesn't know the size of the array. This is why the
|
||||||
|
// clone needs to be here even if it could be generic.
|
||||||
|
fn clone_to_slice(&self, slice: &mut [u8]) {
|
||||||
|
slice.clone_from_slice(&self.as_bytes());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn calc_position_data() -> *mut u8 {
|
||||||
|
let mut result = Vec::<shapes::PositionData>::default();
|
||||||
|
with_current_shape!(state, |shape: &Shape| {
|
||||||
|
if let Type::Text(text_content) = &shape.shape_type {
|
||||||
|
result = shapes::calc_position_data(shape, &text_content);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
mem::write_vec(result)
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user