servo / servo/font-kit

`font.outline()` renders a glyph in reversed shape

Open
#247 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
Rust
Stars
840
Forks
137
Avg merge
4h 56m
Merged PRs (30d)
1

Description

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?

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the provided font.outline call and the TextPathBuilder coordinate conversions, especially calc_y, then compare the resulting path with raqote's coordinate expectations. Verify the example using the NotoSansCJK-Medium.otf glyph for '9'; done means determining whether the inverted y transformation is the correct fix and documenting or correcting the behavior as needed.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
computer-graphics
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.