Skip to content

font.outline() renders a glyph in reversed shape #247

Description

@choyunjin

I'm using this library with raqote for text rendering. I wrote the code below:

use std::fs::File;
use font_kit::font::Font;
use font_kit::hinting::HintingOptions;
use font_kit::outline::OutlineSink;
use raqote::{
    Path, PathBuilder,
    Point, Source, SolidSource,
    StrokeStyle, LineCap, LineJoin,
    DrawTarget, DrawOptions
};
use pathfinder_geometry::vector::Vector2F;
use pathfinder_geometry::line_segment::LineSegment2F;

pub struct TextPathBuilder {
    pb: PathBuilder,
    font_scale: f32,
    start: Point
}

impl TextPathBuilder {
    pub fn new(font_size: f32, start: Point, units_per_em: u32) -> Self {
        return Self {
            pb: PathBuilder::new(),
            font_scale: font_size / units_per_em as f32,
            start
        };
    }

    fn calc_point(&self, point: f32) -> f32 {
        return point * self.font_scale;
    }

    fn calc_x(&self, point: f32) -> f32 {
        return self.calc_point(point) + self.start.x;
    }

    fn calc_y(&self, point: f32) -> f32 {
        return self.calc_point(point) + self.start.y;
    }

    pub fn finish(self) -> Path {
        return self.pb.finish();
    }
}

impl OutlineSink for TextPathBuilder {
    fn move_to(&mut self, to: Vector2F) {
        self.pb.move_to(self.calc_x(to.x()), self.calc_y(to.y()));
    }

    fn line_to(&mut self, to: Vector2F) {
        self.pb.line_to(self.calc_x(to.x()), self.calc_y(to.y()));
    }

    fn quadratic_curve_to(&mut self, ctrl: Vector2F, to: Vector2F) {
        self.pb.quad_to(self.calc_x(ctrl.x()), self.calc_y(ctrl.y()), self.calc_x(to.x()), self.calc_y(to.y()));
    }

    fn cubic_curve_to(&mut self, ctrl: LineSegment2F, to: Vector2F) {
        self.pb.cubic_to(self.calc_x(ctrl.from().x()), self.calc_y(ctrl.from().y()), self.calc_x(ctrl.to().x()), self.calc_y(ctrl.to().y()), self.calc_x(to.x()), self.calc_y(to.y()));
    }

    fn close(&mut self) {
        self.pb.close();
    }
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut file = File::open("tmp/NotoSansCJK-Medium.otf")?;
    let font = Font::from_file(&mut file, 0)?;

    let mut tpb = TextPathBuilder::new(70.0, Point::new(10.0, 10.0), font.metrics().units_per_em);
    font.outline(font.glyph_for_char('9').unwrap(), HintingOptions::None, &mut tpb)?;
    let path = tpb.finish();

    let mut dt = DrawTarget::new(100, 100);

    let color1 = Source::Solid(SolidSource::from_unpremultiplied_argb(255, 255, 255, 255));
    let color2 = Source::Solid(SolidSource::from_unpremultiplied_argb(255, 0x2a, 0xbd, 0x71));
    let stroke = StrokeStyle {
        cap: LineCap::Butt,
        join: LineJoin::Round,
        width: 10.0,
        miter_limit: 10.0,
        ..Default::default()
    };
    let draw_options = DrawOptions {
        ..Default::default()
    };

    dt.stroke(&path, &color1, &stroke, &draw_options);
    dt.fill(&path, &color2, &draw_options);

    dt.write_png("example.png")?;

    return Ok(());
}

However, the output is reversed "9".

I fixed TextPathBuilder like this:

pub struct TextPathBuilder {
    pb: PathBuilder,
    font_scale: f32,
    font_size: f32,
    start: Point
}

impl TextPathBuilder {
    pub fn new(font_size: f32, start: Point, units_per_em: u32) -> Self {
        return Self {
            pb: PathBuilder::new(),
            font_scale: font_size / units_per_em as f32,
            font_size,
            start
        };
    }

    fn calc_point(&self, point: f32) -> f32 {
        return point * self.font_scale;
    }

    fn calc_x(&self, point: f32) -> f32 {
        return self.calc_point(point) + self.start.x;
    }

    fn calc_y(&self, point: f32) -> f32 {
        return -self.calc_point(point) + self.font_size + self.start.y;
    }

    pub fn finish(self) -> Path {
        return self.pb.finish();
    }
}

and the image is generated correctly.

Did I fix the problem correctly?

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions