Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add sanity check for outlines, add point pen #56

Merged
merged 9 commits into from
Nov 11, 2020
Merged

Conversation

madig
Copy link
Collaborator

@madig madig commented Oct 3, 2020

As detailed on https://unifiedfontobject.org/versions/ufo3/glyphs/glif/#point.

I'm not quite sure about off-curves follows by a move, so I opened unified-font-object/ufo-spec#148.

Also added a GlyphPointPen for programmatically drawing glyphs, like https://fonttools.readthedocs.io/en/latest/pens/pointPen.html.

@madig madig changed the title Add sanity check outline Add sanity check for outlines Oct 3, 2020
@madig madig changed the title Add sanity check for outlines Add sanity check for outlines, add point pen Oct 4, 2020
Copy link
Member

@cmyr cmyr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks reasonable to me, a few comments inline!

@madig madig marked this pull request as draft October 7, 2020 12:24
@madig madig force-pushed the add-sanity-check-outline branch 2 times, most recently from 7949332 to 87648a5 Compare October 21, 2020 22:23
@madig
Copy link
Collaborator Author

madig commented Oct 31, 2020

There are a bunch of eprintln in use in the parser code. Shouldn't they be the usual ErrorKinds instead?

Copy link
Member

@cmyr cmyr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly this all looks great to me! A few questions and comments inline, but no major concerns.

Copy link
Member

@cmyr cmyr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Had an opportunity to give this a bit more thought; some comments inline.

I had an idea for an adjustment to the API, which I am going to call the 'meta-builder pattern'. The basic idea is that some methods on the builder take as arguments closures that are passed another builder that can be used to build the component in question, like a guideline or an anchor. In use this would look something like,

let mut pen = Pen::new("test", GlifVersion::V2);
pen.unicode("x")
    .guideline(|builder| builder.identifier("accent-2").line(Line::Horizontal(840.0)))?
    .outline(|builder| builder.add_point((173.0, 422.0), PointType::Line, false, None, None)
        .add_point((193.0, 300.0), PointType::Line, false, None, None)
        .end_path()
        .add_component("hallo", None, "some-ident"))?;

The signature for guideline would then look something like,

fn guideline(&mut self, build_fn: impl FnMut(&mut GuidelineBuilder)) -> Result<&mut Self, Error> {
    let mut builder = GuidelineBuilder::new();
    build_fn(&mut builder);
    let guildeline = builder.finish()?;
    // insert guideline
    Ok(self)
}

It's very possible this doesn't make sense; in particular there's a sort of funny mismatch between the old API we use for parsing and this new Pen stuff. We could definitely consider rethinking that; one option would be that GlifParser goes away, and all those parse methods become just free functions; instead of mutating the pen inside the GlifParser they would just return the appropriate type, so we'd have things like,

fn parse_outline(reader: &mut Reader<[u8]>, buf: &mut Vec<u8>) -> Result<Outline, Error>;
fn parse_contour(reader: &mut Reader<[u8]>, buf: &mut Vec<u8>) -> Result<Contour, Error>;
// etc

Let me know what you think? This is all just brainstorming, but it feels like you've been fighting a bit to cram the pen into the existing parsing, and maybe it makes sense for it to live outside of that?

@madig
Copy link
Collaborator Author

madig commented Nov 3, 2020

I like the sub-builder approach and will try to come up with something! I think it may work well enough in GlifParser? I want the best API first, then I can make it work with GlifParser somehow.

What do you mean by funny mismatch? How would free standing parse functions work together?

cmyr added a commit that referenced this pull request Nov 4, 2020
This came up in the discussion of #56. It should no longer be
possible to create invalid identifiers.
cmyr added a commit that referenced this pull request Nov 4, 2020
This came up in the discussion of #56. It should no longer be
possible to create invalid identifiers.
cmyr added a commit that referenced this pull request Nov 5, 2020
This came up in the discussion of #56. It should no longer be
possible to create invalid identifiers.
cmyr added a commit that referenced this pull request Nov 6, 2020
This came up in the discussion of #56. It should no longer be
possible to create invalid identifiers.
@madig madig force-pushed the add-sanity-check-outline branch from 2997d60 to a5934a1 Compare November 8, 2020 19:50
@madig madig force-pushed the add-sanity-check-outline branch from a5934a1 to a8845ab Compare November 8, 2020 21:45
@madig
Copy link
Collaborator Author

madig commented Nov 8, 2020

So, I tried the sub-builder approach. It's ok for constructing things directly but clashes with GlifParser. E.g. to have an outline builder, closures are awkward because you ideally do everything at once in the closure, but outline parsing is strewn across 4 functions. I think this needs more thinking and more use-cases to flesh out. Might revisit when I know more.

@madig madig marked this pull request as ready for review November 8, 2020 23:14
@madig madig force-pushed the add-sanity-check-outline branch from 240f420 to 40950f3 Compare November 10, 2020 21:37
@madig
Copy link
Collaborator Author

madig commented Nov 11, 2020

It was suggested to me to call the pen GlyphBuilder instead to avoid confusion with fontTools' Pen concept.

Copy link
Member

@cmyr cmyr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did a quick pass, and this seems reasonable. There are definitely some ways I might consider doing this differently, as discussed, but it's hard for me to be helpful without actually trying to play around with the implementation itself, more. Happy to discuss in any case, though!

Line::Angle { x, y, degrees } => (Some(x), Some(y), Some(degrees)),
Line::Angle { x, y, degrees } => {
if !(0.0..=360.0).contains(&degrees) {
return Err(ser::Error::custom("angle must be between 0 and 360 degrees."));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as with Identifier, my preference here would be to make illegal values impossible to construct. This could be follow-up work, though.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was my thinking. At which level though? making a special Degrees newtype?

Copy link
Member

@cmyr cmyr left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Happy to get this in as a checkpoint, on the understanding that we're going to keep iterating. :)

@madig madig force-pushed the add-sanity-check-outline branch from 9e78428 to 69b2cb6 Compare November 11, 2020 22:58
@madig madig merged commit cfb7355 into master Nov 11, 2020
@madig madig deleted the add-sanity-check-outline branch November 11, 2020 23:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants