( &mut self, glyph: &OutlineGlyph<'_>, size: f32, normalized_coords: &[NormalizedCoord], glyph_offset: DVec2, style_skew: Option<DAffine2>, skew: DAffine2, per_glyph_items: bool, )
| 53 | |
| 54 | #[allow(clippy::too_many_arguments)] |
| 55 | fn draw_glyph( |
| 56 | &mut self, |
| 57 | glyph: &OutlineGlyph<'_>, |
| 58 | size: f32, |
| 59 | normalized_coords: &[NormalizedCoord], |
| 60 | glyph_offset: DVec2, |
| 61 | style_skew: Option<DAffine2>, |
| 62 | skew: DAffine2, |
| 63 | per_glyph_items: bool, |
| 64 | ) -> bool { |
| 65 | let location_ref = LocationRef::new(normalized_coords); |
| 66 | let settings = DrawSettings::unhinted(Size::new(size), location_ref); |
| 67 | glyph.draw(settings, self).unwrap(); |
| 68 | let has_geometry = !self.glyph_subpaths.is_empty(); |
| 69 | |
| 70 | // Apply transforms in correct order: style-based skew first, then user-requested skew |
| 71 | // This ensures font synthesis (italic) is applied before user transformations |
| 72 | for glyph_subpath in &mut self.glyph_subpaths { |
| 73 | if let Some(style_skew) = style_skew { |
| 74 | glyph_subpath.apply_transform(style_skew); |
| 75 | } |
| 76 | |
| 77 | glyph_subpath.apply_transform(skew); |
| 78 | } |
| 79 | |
| 80 | let glyph_bbox = subpaths_bounding_box(&self.glyph_subpaths); |
| 81 | |
| 82 | if per_glyph_items { |
| 83 | // Frame in item-local space: top-left at `-glyph_offset` so the item transform cancels it |
| 84 | // back to the layer-local frame origin, regardless of which glyph survived |
| 85 | let frame_in_item_local = DAffine2::from_scale_angle_translation(self.text_frame_size, 0., -glyph_offset); |
| 86 | |
| 87 | let item = Item::new_from_element(Vector::from_subpaths(core::mem::take(&mut self.glyph_subpaths), false)) |
| 88 | .with_attribute(ATTR_TRANSFORM, DAffine2::from_translation(glyph_offset)) |
| 89 | .with_attribute(ATTR_EDITOR_TEXT_FRAME, frame_in_item_local); |
| 90 | self.vector_list.push(item); |
| 91 | |
| 92 | // Defer click target creation to `finalize()` where adjacent AABBs get widened |
| 93 | self.per_glyph_bboxes.push(glyph_bbox); |
| 94 | } else { |
| 95 | for subpath in self.glyph_subpaths.drain(..) { |
| 96 | // Unwrapping here is ok because `self.vector_list` is initialized with a single `List<Vector>` item |
| 97 | self.vector_list.element_mut(0).unwrap().append_subpath(subpath, false); |
| 98 | } |
| 99 | if let Some(bbox) = glyph_bbox { |
| 100 | self.merged_click_target_bboxes.push(bbox); |
| 101 | self.merged_click_target_baselines.push(glyph_offset.y); |
| 102 | } |
| 103 | } |
| 104 | |
| 105 | has_geometry |
| 106 | } |
| 107 | |
| 108 | pub fn render_glyph_run(&mut self, glyph_run: &GlyphRun<'_, ()>, letter_tilt: f64, per_glyph_items: bool, x_offset: f32, space_extra: f32) { |
| 109 | let mut run_x = glyph_run.offset() + x_offset; |
no test coverage detected