Repository: typst/typst
File: crates/typst-svg/src/lib.rs (Rust)

Code before:
        glyph_id: GlyphId,
        x_offset: f64,
        scale: f64,
    ) -> Option<()> {
        let scale = Ratio::new(scale);
        let path = convert_outline_glyph_to_path(&text.font, glyph_id, scale)?;
        let hash = hash128(&(&text.font, glyph_id, scale));
        let id = self.glyphs.insert_with(hash, || RenderedGlyph::Path(path));

        let glyph_size = text.font.ttf().glyph_bounding_box(glyph_id)?;
        let width = glyph_size.width() as f64 * scale.get();
        let height = glyph_size.height() as f64 * scale.get();

        self.xml.start_element("use");
        self.xml.write_attribute_fmt("xlink:href", format_args!("#{id}"));
        self.xml.write_attribute_fmt("x", format_args!("{x_offset}"));
        self.write_fill(
            &text.fill,
            Size::new(Abs::pt(width), Abs::pt(height)),
            self.text_paint_transform(state, &text.fill),
        );
        if let Some(stroke) = &text.stroke {
            self.write_stroke(
                stroke,
                Size::new(Abs::pt(width), Abs::pt(height)),
                self.text_paint_transform(state, &text.fill),
            );
            self.xml.write_attribute("paint-order", "stroke"); // make sure the stroke is rendered on top of the fill
        }
        self.xml.end_element();

        Some(())
    }

    fn text_paint_transform(&self, state: State, paint: &Paint) -> Transform {
        match paint {
            Paint::Solid(_) => Transform::identity(),
            Paint::Gradient(gradient) => match gradient.unwrap_relative(true) {
                RelativeTo::Self_ => Transform::identity(),
                RelativeTo::Parent => Transform::scale(
                    Ratio::new(state.size.x.to_pt()),
                    Ratio::new(state.size.y.to_pt()),
                )
                .post_concat(state.transform.invert().unwrap()),
            },
            Paint::Pattern(pattern) => match pattern.unwrap_relative(true) {
                RelativeTo::Self_ => Transform::identity(),
                RelativeTo::Parent => state.transform.invert().unwrap(),
            },
        }
    }

Review comment (bug):
Be careful, here it should be `&stroke.paint` instead of `&text.fill` otherwise the transform is wrong!

Code after:
        glyph_id: GlyphId,
        x_offset: f64,
        scale: f64,
    ) -> Option<()> {
        let scale = Ratio::new(scale);
        let path = convert_outline_glyph_to_path(&text.font, glyph_id, scale)?;
        let hash = hash128(&(&text.font, glyph_id, scale));
        let id = self.glyphs.insert_with(hash, || RenderedGlyph::Path(path));

        let glyph_size = text.font.ttf().glyph_bounding_box(glyph_id)?;
        let width = glyph_size.width() as f64 * scale.get();
        let height = glyph_size.height() as f64 * scale.get();

        self.xml.start_element("use");
        self.xml.write_attribute_fmt("xlink:href", format_args!("#{id}"));
        self.xml.write_attribute_fmt("x", format_args!("{x_offset}"));
        self.write_fill(
            &text.fill,
            Size::new(Abs::pt(width), Abs::pt(height)),
            self.text_paint_transform(state, &text.fill),
        );
        if let Some(stroke) = &text.stroke {
            self.write_stroke(
                stroke,
                Size::new(Abs::pt(width), Abs::pt(height)),
                self.text_paint_transform(state, &stroke.paint),
            );
        }
        self.xml.end_element();

        Some(())
    }

    fn text_paint_transform(&self, state: State, paint: &Paint) -> Transform {
        match paint {
            Paint::Solid(_) => Transform::identity(),
            Paint::Gradient(gradient) => match gradient.unwrap_relative(true) {
                RelativeTo::Self_ => Transform::identity(),
                RelativeTo::Parent => Transform::scale(
                    Ratio::new(state.size.x.to_pt()),
                    Ratio::new(state.size.y.to_pt()),
                )
                .post_concat(state.transform.invert().unwrap()),
            },
            Paint::Pattern(pattern) => match pattern.unwrap_relative(true) {
                RelativeTo::Self_ => Transform::identity(),
                RelativeTo::Parent => state.transform.invert().unwrap(),
            },
        }
    }