Merge pull request #6573 from penpot/elenatorro-11021-text-fixes

🔧 Fix text parsing and transformation
This commit is contained in:
Elena Torró 2025-05-29 09:33:05 +02:00 committed by GitHub
commit fe60016124
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 52 additions and 17 deletions

View file

@ -19,7 +19,7 @@ use options::RenderOptions;
use surfaces::{SurfaceId, Surfaces}; use surfaces::{SurfaceId, Surfaces};
use crate::performance; use crate::performance;
use crate::shapes::{modified_children_ids, Corners, Shape, StructureEntry, Type}; use crate::shapes::{modified_children_ids, Corners, Shape, StrokeKind, StructureEntry, Type};
use crate::tiles::{self, TileRect, TileViewbox, TileWithDistance}; use crate::tiles::{self, TileRect, TileViewbox, TileWithDistance};
use crate::uuid::Uuid; use crate::uuid::Uuid;
use crate::view::Viewbox; use crate::view::Viewbox;
@ -423,9 +423,17 @@ impl RenderState {
} }
Type::Text(text_content) => { Type::Text(text_content) => {
self.surfaces.apply_mut(&[SurfaceId::Fills], |s| { self.surfaces.apply_mut(
&[
SurfaceId::Fills,
SurfaceId::Strokes,
SurfaceId::DropShadows,
SurfaceId::InnerShadows,
],
|s| {
s.canvas().concat(&matrix); s.canvas().concat(&matrix);
}); },
);
let text_content = text_content.new_bounds(shape.selrect()); let text_content = text_content.new_bounds(shape.selrect());
let paragraphs = text_content.get_skia_paragraphs(self.fonts.font_collection()); let paragraphs = text_content.get_skia_paragraphs(self.fonts.font_collection());
@ -440,6 +448,13 @@ impl RenderState {
self.fonts.font_collection(), self.fonts.font_collection(),
); );
shadows::render_text_drop_shadows(self, &shape, &stroke_paragraphs, antialias); shadows::render_text_drop_shadows(self, &shape, &stroke_paragraphs, antialias);
if stroke.kind == StrokeKind::Inner {
// Inner strokes must be rendered on the Fills surface because their blend modes
// (e.g., SrcATop, DstOver) rely on the text fill already being present underneath.
// Rendering them on a separate surface would break this blending and result in incorrect visuals as
// black color background.
text::render(self, &shape, &stroke_paragraphs, None, None);
} else {
text::render( text::render(
self, self,
&shape, &shape,
@ -447,6 +462,7 @@ impl RenderState {
Some(SurfaceId::Strokes), Some(SurfaceId::Strokes),
None, None,
); );
}
shadows::render_text_inner_shadows(self, &shape, &stroke_paragraphs, antialias); shadows::render_text_inner_shadows(self, &shape, &stroke_paragraphs, antialias);
} }

View file

@ -8,13 +8,19 @@ pub fn render(
surface_id: Option<SurfaceId>, surface_id: Option<SurfaceId>,
paint: Option<skia::Paint>, paint: Option<skia::Paint>,
) { ) {
let use_save_layer = paint.is_some();
let mask_paint = paint.unwrap_or_default(); let mask_paint = paint.unwrap_or_default();
let mask = SaveLayerRec::default().paint(&mask_paint); let mask = SaveLayerRec::default().paint(&mask_paint);
let canvas = render_state let canvas = render_state
.surfaces .surfaces
.canvas(surface_id.unwrap_or(SurfaceId::Fills)); .canvas(surface_id.unwrap_or(SurfaceId::Fills));
// Skip save_layer when no custom paint is provided to avoid isolating content unnecessarily.
// This ensures inner strokes and fills can blend correctly on the same surface.
if use_save_layer {
canvas.save_layer(&mask); canvas.save_layer(&mask);
}
for group in paragraphs { for group in paragraphs {
let mut offset_y = 0.0; let mut offset_y = 0.0;
for skia_paragraph in group { for skia_paragraph in group {
@ -23,5 +29,7 @@ pub fn render(
offset_y += skia_paragraph.height(); offset_y += skia_paragraph.height();
} }
} }
if use_save_layer {
canvas.restore(); canvas.restore();
}
} }

View file

@ -197,8 +197,8 @@ impl Default for TextContent {
pub struct Paragraph { pub struct Paragraph {
num_leaves: u32, num_leaves: u32,
text_align: u8, text_align: u8,
text_decoration: u8,
text_direction: u8, text_direction: u8,
text_decoration: u8,
text_transform: u8, text_transform: u8,
line_height: f32, line_height: f32,
letter_spacing: f32, letter_spacing: f32,
@ -212,8 +212,8 @@ impl Default for Paragraph {
Self { Self {
num_leaves: 0, num_leaves: 0,
text_align: 0, text_align: 0,
text_decoration: 0,
text_direction: 0, text_direction: 0,
text_decoration: 0,
text_transform: 0, text_transform: 0,
line_height: 1.0, line_height: 1.0,
letter_spacing: 0.0, letter_spacing: 0.0,
@ -229,8 +229,8 @@ impl Paragraph {
pub fn new( pub fn new(
num_leaves: u32, num_leaves: u32,
text_align: u8, text_align: u8,
text_decoration: u8,
text_direction: u8, text_direction: u8,
text_decoration: u8,
text_transform: u8, text_transform: u8,
line_height: f32, line_height: f32,
letter_spacing: f32, letter_spacing: f32,
@ -241,8 +241,8 @@ impl Paragraph {
Self { Self {
num_leaves, num_leaves,
text_align, text_align,
text_decoration,
text_direction, text_direction,
text_decoration,
text_transform, text_transform,
line_height, line_height,
letter_spacing, letter_spacing,
@ -352,6 +352,8 @@ impl TextLeaf {
3 => skia::textlayout::TextDecoration::OVERLINE, 3 => skia::textlayout::TextDecoration::OVERLINE,
_ => skia::textlayout::TextDecoration::NO_DECORATION, _ => skia::textlayout::TextDecoration::NO_DECORATION,
}); });
// FIXME
style.set_decoration_color(paint.color());
style.set_font_families(&[ style.set_font_families(&[
self.serialized_font_family(), self.serialized_font_family(),
@ -369,6 +371,15 @@ impl TextLeaf {
) -> skia::textlayout::TextStyle { ) -> skia::textlayout::TextStyle {
let mut style = self.to_style(paragraph, &Rect::default()); let mut style = self.to_style(paragraph, &Rect::default());
style.set_foreground_paint(stroke_paint); style.set_foreground_paint(stroke_paint);
style.set_font_size(self.font_size);
style.set_letter_spacing(paragraph.letter_spacing);
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 style
} }