diff --git a/examples/swash_render/src/main.rs b/examples/swash_render/src/main.rs index bb5c1b1..bc7f72d 100644 --- a/examples/swash_render/src/main.rs +++ b/examples/swash_render/src/main.rs @@ -20,7 +20,7 @@ use zeno::{Format, Vector}; fn main() { // The text we are going to style and lay out let text = String::from( - "Some text here. Let's make it a bit longer so that line wrapping kicks in 😊. And also some اللغة العربية arabic text.", + "Some text here. Let's make it a bit longer so that line wrapping kicks in 😊. And also some اللغة العربية arabic text.\nThis is underline and strikethrough text", ); // The display scale for HiDPI rendering @@ -48,6 +48,8 @@ fn main() { let brush_style = StyleProperty::Brush(text_color); let font_stack = FontStack::from("system-ui"); let bold_style = StyleProperty::FontWeight(FontWeight::new(600.0)); + let underline_style = StyleProperty::Underline(true); + let strikethrough_style = StyleProperty::Strikethrough(true); let mut layout = if std::env::args().any(|arg| arg == "--tree") { // TREE BUILDER @@ -87,7 +89,18 @@ fn main() { height: 30.0, }); - builder.push_text(&text[50..]); + builder.push_text(&text[50..141]); + + // Set the underline style + builder.push_style_modification_span(&[underline_style]); + builder.push_text(&text[141..150]); + + builder.pop_style_span(); + builder.push_text(&text[150..155]); + + // Set the strikethrough style + builder.push_style_modification_span(&[strikethrough_style]); + builder.push_text(&text[155..168]); // Build the builder into a Layout // let mut layout: Layout = builder.build(&text); @@ -111,6 +124,10 @@ fn main() { // Set the first 4 characters to bold builder.push(bold_style, 0..4); + // Set the underline & stoked style + builder.push(underline_style, 141..150); + builder.push(strikethrough_style, 155..168); + builder.push_inline_box(InlineBox { id: 0, index: 40, @@ -217,6 +234,41 @@ fn render_glyph_run( render_glyph(img, &mut scaler, color, glyph, glyph_x, glyph_y); } + + // Draw decorations: underline & strikethrough + let style = glyph_run.style(); + let run_metrics = run.metrics(); + if let Some(decoration) = &style.underline { + let offset = decoration.offset.unwrap_or(run_metrics.underline_offset); + let size = decoration.size.unwrap_or(run_metrics.underline_size); + render_decoration(img, glyph_run, decoration.brush, offset, size, padding); + } + if let Some(decoration) = &style.strikethrough { + let offset = decoration + .offset + .unwrap_or(run_metrics.strikethrough_offset); + let size = decoration.size.unwrap_or(run_metrics.strikethrough_size); + render_decoration(img, glyph_run, decoration.brush, offset, size, padding); + } +} + +fn render_decoration( + img: &mut RgbaImage, + glyph_run: &GlyphRun, + color: Color, + offset: f32, + width: f32, + padding: u32, +) { + let y = glyph_run.baseline() - offset; + let color = Rgba([color.r, color.g, color.b, color.a]); + for pixel_y in y as u32..(y + width) as u32 { + for pixel_x in glyph_run.offset() as u32..(glyph_run.offset() + glyph_run.advance()) as u32 + { + img.get_pixel_mut(pixel_x + padding, pixel_y + padding) + .blend(&color); + } + } } fn render_glyph( diff --git a/examples/tiny_skia_render/src/main.rs b/examples/tiny_skia_render/src/main.rs index 565a2e5..0c448a2 100644 --- a/examples/tiny_skia_render/src/main.rs +++ b/examples/tiny_skia_render/src/main.rs @@ -25,7 +25,7 @@ use tiny_skia::{ fn main() { // The text we are going to style and lay out let text = String::from( - "Some text here. Let's make it a bit longer so that line wrapping kicks in 😊. And also some اللغة العربية arabic text.", + "Some text here. Let's make it a bit longer so that line wrapping kicks in 😊. And also some اللغة العربية arabic text.\nThis is underline and strikethrough text", ); // The display scale for HiDPI rendering @@ -64,6 +64,10 @@ fn main() { let bold = FontWeight::new(600.0); builder.push(StyleProperty::FontWeight(bold), 0..4); + // Set the underline & stoked style + builder.push(StyleProperty::Underline(true), 141..150); + builder.push(StyleProperty::Strikethrough(true), 155..168); + builder.push_inline_box(InlineBox { id: 0, index: 40, @@ -158,12 +162,43 @@ fn render_glyph_run(glyph_run: &GlyphRun, pen: &mut TinySkiaPen<'_> run_x += glyph.advance; let glyph_id = GlyphId::from(glyph.id); - let glyph_outline = outlines.get(glyph_id).unwrap(); + if let Some(glyph_outline) = outlines.get(glyph_id) { + pen.set_origin(glyph_x, glyph_y); + pen.set_color(to_tiny_skia(color)); + pen.draw_glyph(&glyph_outline, font_size, &normalized_coords); + } + } - pen.set_origin(glyph_x, glyph_y); - pen.set_color(to_tiny_skia(color)); - pen.draw_glyph(&glyph_outline, font_size, &normalized_coords); + // Draw decorations: underline & strikethrough + let style = glyph_run.style(); + let run_metrics = run.metrics(); + if let Some(decoration) = &style.underline { + let offset = decoration.offset.unwrap_or(run_metrics.underline_offset); + let size = decoration.size.unwrap_or(run_metrics.underline_size); + render_decoration(pen, glyph_run, decoration.brush, offset, size, padding); } + if let Some(decoration) = &style.strikethrough { + let offset = decoration + .offset + .unwrap_or(run_metrics.strikethrough_offset); + let size = decoration.size.unwrap_or(run_metrics.strikethrough_size); + render_decoration(pen, glyph_run, decoration.brush, offset, size, padding); + } +} + +fn render_decoration( + pen: &mut TinySkiaPen<'_>, + glyph_run: &GlyphRun, + color: PenikoColor, + offset: f32, + width: f32, + padding: u32, +) { + let y = glyph_run.baseline() - offset + padding as f32; + let x = glyph_run.offset() + padding as f32; + pen.set_color(to_tiny_skia(color)); + pen.set_origin(x, y); + pen.fill_rect(glyph_run.advance(), width); } struct TinySkiaPen<'a> {