From f2c35d7e465be155bc5f0063455379396863bc37 Mon Sep 17 00:00:00 2001 From: Cosimo Lupo Date: Mon, 7 Aug 2023 12:54:05 +0100 Subject: [PATCH] glyphs2fontir: support translating qcurves to IR paths --- glyphs-reader/src/font.rs | 6 ++ glyphs2fontir/src/source.rs | 17 ++++ glyphs2fontir/src/toir.rs | 3 + resources/testdata/glyphs2/QCurve.glyphs | 87 ++++++++++++++++ resources/testdata/glyphs3/QCurve.glyphs | 121 +++++++++++++++++++++++ 5 files changed, 234 insertions(+) create mode 100644 resources/testdata/glyphs2/QCurve.glyphs create mode 100644 resources/testdata/glyphs3/QCurve.glyphs diff --git a/glyphs-reader/src/font.rs b/glyphs-reader/src/font.rs index 2878420b..04ca7bff 100644 --- a/glyphs-reader/src/font.rs +++ b/glyphs-reader/src/font.rs @@ -269,6 +269,8 @@ pub enum NodeType { OffCurve, Curve, CurveSmooth, + QCurve, + QCurveSmooth, } #[derive(Clone, Debug, FromPlist, PartialEq)] @@ -440,12 +442,16 @@ impl std::str::FromStr for NodeType { "OFFCURVE" => Ok(NodeType::OffCurve), "CURVE" => Ok(NodeType::Curve), "CURVE SMOOTH" => Ok(NodeType::CurveSmooth), + "QCURVE" => Ok(NodeType::QCurve), + "QCURVE SMOOTH" => Ok(NodeType::QCurveSmooth), // Glyphs 3 style "l" => Ok(NodeType::Line), "ls" => Ok(NodeType::LineSmooth), "o" => Ok(NodeType::OffCurve), "c" => Ok(NodeType::Curve), "cs" => Ok(NodeType::CurveSmooth), + "q" => Ok(NodeType::QCurve), + "qs" => Ok(NodeType::QCurveSmooth), _ => Err(format!("unknown node type {s}")), } } diff --git a/glyphs2fontir/src/source.rs b/glyphs2fontir/src/source.rs index b31d60a2..aa96069c 100644 --- a/glyphs2fontir/src/source.rs +++ b/glyphs2fontir/src/source.rs @@ -1348,4 +1348,21 @@ mod tests { ) ); } + + #[test] + fn build_glyph_contour_ir_containing_qcurves() { + let glyph_name = "i"; + let expected = "M302,584 Q328,584 346,602 Q364,620 364,645 Q364,670 346,687.5 Q328,705 302,705 Q276,705 257.5,687.5 Q239,670 239,645 Q239,620 257.5,602 Q276,584 302,584 Z"; + for test_dir in &[glyphs2_dir(), glyphs3_dir()] { + let (source, context) = build_static_metadata(test_dir.join("QCurve.glyphs")); + build_glyphs(&source, &context, &[&glyph_name.into()]).unwrap(); + let glyph = context.glyphs.get(&WorkId::Glyph(glyph_name.into())); + let default_instance = glyph + .sources() + .get(context.static_metadata.get().default_location()) + .unwrap(); + let first_contour = default_instance.contours.first().unwrap(); + assert_eq!(first_contour.to_svg(), expected); + } + } } diff --git a/glyphs2fontir/src/toir.rs b/glyphs2fontir/src/toir.rs index 1da5d37a..ffe4b6e3 100644 --- a/glyphs2fontir/src/toir.rs +++ b/glyphs2fontir/src/toir.rs @@ -85,6 +85,9 @@ fn to_ir_path(glyph_name: GlyphName, src_path: &Path) -> Result path_builder .offcurve((node.pt.x, node.pt.y)) .map_err(WorkError::PathConversionError)?, + NodeType::QCurve | NodeType::QCurveSmooth => path_builder + .qcurve_to((node.pt.x, node.pt.y)) + .map_err(WorkError::PathConversionError)?, } } diff --git a/resources/testdata/glyphs2/QCurve.glyphs b/resources/testdata/glyphs2/QCurve.glyphs new file mode 100644 index 00000000..eccb67f3 --- /dev/null +++ b/resources/testdata/glyphs2/QCurve.glyphs @@ -0,0 +1,87 @@ +{ +.appVersion = "3208"; +DisplayStrings = ( +"", +i +); +customParameters = ( +{ +name = "Disable Last Change"; +value = 1; +} +); +date = "2023-07-20 13:49:39 +0000"; +familyName = "New Font"; +fontMaster = ( +{ +alignmentZones = ( +"{800, 16}", +"{700, 16}", +"{500, 16}", +"{0, -16}", +"{-200, -16}" +); +ascender = 800; +capHeight = 700; +descender = -200; +id = m01; +xHeight = 500; +} +); +glyphs = ( +{ +glyphname = i; +layers = ( +{ +layerId = m01; +paths = ( +{ +closed = 1; +nodes = ( +"328 584 OFFCURVE", +"364 620 OFFCURVE", +"364 645 QCURVE SMOOTH", +"364 670 OFFCURVE", +"328 705 OFFCURVE", +"302 705 QCURVE SMOOTH", +"276 705 OFFCURVE", +"239 670 OFFCURVE", +"239 645 QCURVE SMOOTH", +"239 620 OFFCURVE", +"276 584 OFFCURVE", +"302 584 QCURVE SMOOTH" +); +}, +{ +closed = 1; +nodes = ( +"241 0 LINE", +"354 0 LINE", +"354 500 LINE", +"241 500 LINE" +); +} +); +width = 600; +} +); +unicode = 0069; +}, +{ +glyphname = space; +layers = ( +{ +layerId = m01; +width = 200; +} +); +unicode = 0020; +} +); +unitsPerEm = 1000; +userData = { +GSDontShowVersionAlert = 1; +}; +versionMajor = 1; +versionMinor = 0; +} diff --git a/resources/testdata/glyphs3/QCurve.glyphs b/resources/testdata/glyphs3/QCurve.glyphs new file mode 100644 index 00000000..b4116a9b --- /dev/null +++ b/resources/testdata/glyphs3/QCurve.glyphs @@ -0,0 +1,121 @@ +{ +.appVersion = "3208"; +.formatVersion = 3; +DisplayStrings = ( +"", +i +); +customParameters = ( +{ +name = "Write lastChange"; +value = 0; +} +); +date = "2023-07-20 13:49:39 +0000"; +familyName = "New Font"; +fontMaster = ( +{ +id = m01; +metricValues = ( +{ +over = 16; +pos = 800; +}, +{ +over = 16; +pos = 700; +}, +{ +over = 16; +pos = 500; +}, +{ +over = -16; +}, +{ +over = -16; +pos = -200; +}, +{ +} +); +name = Regular; +} +); +glyphs = ( +{ +glyphname = i; +layers = ( +{ +layerId = m01; +shapes = ( +{ +closed = 1; +nodes = ( +(328,584,o), +(364,620,o), +(364,645,qs), +(364,670,o), +(328,705,o), +(302,705,qs), +(276,705,o), +(239,670,o), +(239,645,qs), +(239,620,o), +(276,584,o), +(302,584,qs) +); +}, +{ +closed = 1; +nodes = ( +(241,0,l), +(354,0,l), +(354,500,l), +(241,500,l) +); +} +); +width = 600; +} +); +unicode = 105; +}, +{ +glyphname = space; +layers = ( +{ +layerId = m01; +width = 200; +} +); +unicode = 32; +} +); +metrics = ( +{ +type = ascender; +}, +{ +type = "cap height"; +}, +{ +type = "x-height"; +}, +{ +type = baseline; +}, +{ +type = descender; +}, +{ +type = "italic angle"; +} +); +unitsPerEm = 1000; +userData = { +GSDontShowVersionAlert = 1; +}; +versionMajor = 1; +versionMinor = 0; +}