Merge pull request #7698 from penpot/elenatorro-fix-word-breaking-different-browsers

🔧 Fix cross-browser text issues
This commit is contained in:
Alejandro Alonso
2025-11-06 12:34:22 +01:00
committed by GitHub
11 changed files with 117 additions and 10 deletions

View File

@@ -95,7 +95,6 @@ macro_rules! with_state_mut_current_shape {
};
}
/// This is called from JS after the WebGL context has been created.
#[no_mangle]
pub extern "C" fn init(width: i32, height: i32) {
let state_box = Box::new(State::new(width, height));
@@ -104,6 +103,13 @@ pub extern "C" fn init(width: i32, height: i32) {
}
}
#[no_mangle]
pub extern "C" fn set_browser(browser: u8) {
with_state_mut!(state, {
state.set_browser(browser);
});
}
#[no_mangle]
pub extern "C" fn clean_up() {
with_state_mut!(state, {

View File

@@ -1,6 +1,7 @@
use crate::{
math::{Bounds, Matrix, Rect},
render::{default_font, DEFAULT_EMOJI_FONT},
utils::Browser,
};
use core::f32;
@@ -19,6 +20,7 @@ use crate::math::Point;
use crate::shapes::{self, merge_fills, Shape, VerticalAlign};
use crate::utils::{get_fallback_fonts, get_font_collection};
use crate::Uuid;
use crate::STATE;
// TODO: maybe move this to the wasm module?
pub type ParagraphBuilderGroup = Vec<ParagraphBuilder>;
@@ -607,6 +609,7 @@ impl Paragraph {
style.set_text_align(self.text_align);
style.set_text_direction(self.text_direction);
style.set_replace_tab_characters(true);
style.set_apply_rounding_hack(true);
style.set_text_height_behavior(skia::textlayout::TextHeightBehavior::All);
style
}
@@ -711,7 +714,7 @@ impl TextSpan {
style.set_font_families(&font_families);
style.set_font_size(self.font_size);
style.set_letter_spacing(self.letter_spacing);
style.set_half_leading(false);
style.set_half_leading(true);
style
}
@@ -753,15 +756,26 @@ impl TextSpan {
format!("{}", self.font_family)
}
fn remove_ignored_chars(text: &str) -> String {
fn process_ignored_chars(text: &str, browser: u8) -> String {
text.chars()
.filter(|&c| c >= '\u{0020}' && c != '\u{2028}' && c != '\u{2029}')
.filter_map(|c| {
if c < '\u{0020}' || c == '\u{2028}' || c == '\u{2029}' {
if browser == Browser::Firefox as u8 {
None
} else {
Some(' ')
}
} else {
Some(c)
}
})
.collect()
}
pub fn apply_text_transform(&self) -> String {
let text = Self::remove_ignored_chars(&self.text);
match self.text_transform {
let browser = crate::with_state!(state, { state.current_browser });
let text = Self::process_ignored_chars(&self.text, browser);
let transformed_text = match self.text_transform {
Some(TextTransform::Uppercase) => text.to_uppercase(),
Some(TextTransform::Lowercase) => text.to_lowercase(),
Some(TextTransform::Capitalize) => text
@@ -776,7 +790,9 @@ impl TextSpan {
.collect::<Vec<_>>()
.join(" "),
None => text,
}
};
transformed_text.replace("/", "/\u{200B}")
}
pub fn scale_content(&mut self, value: f32) {

View File

@@ -22,6 +22,7 @@ pub(crate) struct State<'a> {
pub render_state: RenderState,
pub text_editor_state: TextEditorState,
pub current_id: Option<Uuid>,
pub current_browser: u8,
pub shapes: ShapesPool<'a>,
}
@@ -31,6 +32,7 @@ impl<'a> State<'a> {
render_state: RenderState::new(width, height),
text_editor_state: TextEditorState::new(),
current_id: None,
current_browser: 0,
shapes: ShapesPool::new(),
}
}
@@ -124,6 +126,10 @@ impl<'a> State<'a> {
self.render_state.set_background_color(color);
}
pub fn set_browser(&mut self, browser: u8) {
self.current_browser = browser;
}
/// Sets the parent for the current shape and updates the parent's extended rectangle
///
/// When a shape is assigned a new parent, the parent's extended rectangle needs to be

View File

@@ -36,3 +36,25 @@ pub fn get_fallback_fonts() -> &'static HashSet<String> {
pub fn get_font_collection() -> &'static FontCollection {
with_state_mut!(state, { state.font_collection() })
}
#[derive(Debug, Clone, Copy)]
#[repr(u8)]
pub enum Browser {
Firefox = 0,
Chrome = 1,
Safari = 2,
Edge = 3,
Unknown = 4,
}
impl From<u8> for Browser {
fn from(value: u8) -> Self {
match value {
0 => Browser::Firefox,
1 => Browser::Chrome,
2 => Browser::Safari,
3 => Browser::Edge,
_ => Browser::Unknown,
}
}
}