#font #glif #parser #ufo-format

glifparser

A parser and writer for UFO .glif files

7 releases (stable)

2.0.1 2023年7月15日
1.2.8 2022年12月21日
1.2.7 2022年12月19日
1.2.5 2022年12月13日

#642 in 解析器实现


用于 4 crates

Apache-2.0

215KB
5K SLoC

glifparser v2.0.1 (⏫︎2023-06-01)

(c) 2020–2023 Fredrick R. Brennan and MFEK Authors

A parser and writer for UFO .glif files.

glifparser is a core MFEK library. Almost all modules and non-core libraries are expected to in some way rely on glifparser and its types.

glifparser supports the entire .glif spec as of 12 April 2021 (0a79aa7), when components were added with full validation support (components may not include themselves, nor may they include a component which somewhere in its inclusion tree includes any parent glyph). glifparser supports images completely, including colored images, and can generate the colored to-spec UFO bitmaps for you.

glifparser is meant to be tuned for situation using Cargo's features mechanism. See the § "Discretionary features" for non-default features, and default features which can be disabled to make it smaller.

API

glifparser's main type is Glif<PD: PointData>. You can get this type by calling glifparser::read_from_filename.

Toplevel type: Glif

/// A UFO .glif
///
/// TODO: use different generic types on Anchor and Guideline, making this declaration
/// `Glif<PD,GD,AD>`
#[derive(Clone, Debug, Default, PartialEq)]
pub struct Glif<PD: PointData> {
    pub outline: Option<Outline<PD>>,
    pub order: OutlineType,
    pub anchors: Vec<Anchor<PD>>,
    /// Note that these components are not yet parsed or checked for infinite loops. You need to
    /// call either ``GlifComponent::to_component_of`` on each of these, or ``Glif::flatten``.
    pub components: GlifComponents,
    /// .glif guidelines. Note: glif may have more guidelines, not listed here. It will also have
    /// an asecender and a descender, not listed here. You can get this info from `norad`, reading
    /// the parent UFO and telling it not to read glif's (via UfoDataRequest) since you're using
    /// this for that.
    // Command line MFEK programs can also get it from MFEKmetadata.
    pub guidelines: Vec<Guideline<PD>>,
    /// glifparser does support reading the data of images and guessing their format, but in order
    /// to allow you to handle possibly erroneous files we don't do so by default. You need to call
    /// ``GlifImage::to_image_of`` to get an ``Image`` with data.
    #[cfg(feature = "glifimage")]
    pub images: Vec<GlifImage>,
    pub width: Option<u64>,
    pub unicode: Vec<char>,
    pub name: String,
    /// This is an arbitrary glyph comment, exactly like the comment field in FontForge SFD.
    pub note: Option<String>,
    /// It's up to the API consumer to set this.
    pub filename: Option<path::PathBuf>,
    /// glif private library
    pub lib: Option<plist::Dictionary>,
}

If you do not need to associate data with points, it behooves you to stub out the generic type with () as early as you can…

fn main() {
    let mut input_glif: glifparser::Glif<()> =
        glifparser::read_from_filename(&path).expect("Failed to read .glif file!"))
            .expect("glifparser couldn't parse input path glif. Invalid glif?");
}

PointData

API consumers may put any clonable type as an associated type to Glif, which will appear along with each Point. You could use this to implement, e.g., hyperbeziers. The Glif Point's would still represent a Bézier curve, but you could put hyperbezier info along with the Point.

Note that anchors and guidelines receive the same type. So, if you wanted to put different data along with each, you would need to make an enum like

use glifparser::{Point, PointData};

#[derive(Debug, Copy, Clone, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
pub enum MyPointData {
    Point(bool),
    Guideline(u8),
    Anchor { good: bool },
}
impl Default for MyPointData {
    fn default() -> Self {
        Self::Point(false)
    }
}
impl PointData for MyPointData {}

fn testing() {
    let mut point = Point::default();
    point.data = Some(MyPointData::Point(true));
}

Comparison with Norad

首先,glifparser 与 Norad 的目标不同。glifparser 旨在仅支持 UFO 规范中的 .glif 部分,而 Norad 则打算支持整个 UFO 规范。此外,glifparser 旨在可选地序列化/反序列化 MFEK 项目使用的 <lib/> 元素(当编译时使用 --features=mfek)——因此,在例如 src/glif/mfek.rs 中可以找到许多非 UFO .glif 类型。

该库只实现 .glif 的原因是它认为 .glif 文件可能是从 .ufo 文件“分离的” (“未关联的 .glif”)。当编写这个库的时候,Norad 并没有将未关联的 .glif 文件视为合法的,但后来增加了一些支持:[链接](https://github.com/linebender/norad/blob/5f0cc9c9b6f923b18c6eddfa481ef9eb9d72335e/src/glyph/mod.rs#L65)。尽管如此,然而,许多 Norad 的函数在未关联的 .glif 上可能无法按预期工作。glifparser 则相反,它认为所有的 .glif 都可能是未关联的,直到证明不是这样。这意味着所有依赖其他字型文件的类型有两个版本:以 Glif 前缀的版本表示它是 .glif XML 的近似表示。例如,GlifImage 提供了一个近似表示的 <image> 元素,而如果你将其升级到常规的 Image,(如果 .glif 是未关联的,则可能会失败),那么这个新类型将包含在父 UFO 中实际存在的数据。

同样的情况也适用于 GlifComponent(一个可能存在或可能不存在的组件的未关联 .glif 文件)与 Component(从 GlifComponent 获取的已验证的组件)。glifparser 还可以展平组件(即应用它们的矩阵并将它们的点插入到父 .glif),并打印表示基本/组件关系的任意深度的树。

glifparser 与 Norad 的另一个重大区别是,glifparser 返回 Skia 友好的点。它的立方贝塞尔 Point 有两个手柄,ab,并且 glifparser 解析曲线上的点和离曲线点来得出这个结果。(二次贝塞尔样条使用相同的 Point 类型,但始终将手柄 b 设置为 Handle::Colocated。)然而,Norad 不进行轮廓解析,只是将点提供给你自行解析。

有用特性

对于 glifparser 类型,有许多有用的特性尚未在此实现,而是在其他 MFEK 库中。例如,MFEKmath::PolarCoordinates 实现了在 Point 上的极坐标处理,允许通过极坐标以及笛卡尔坐标(在极坐标模式下,点为原点)来获取/设置点句柄。

您还可以在 MFEK/math.rlib 中找到可以转换为 glifparser 的 Glif<PD>Outline<PD> 类型的分段样条类型。

可选特性

所有可选特性都可以通过名为 fat 的元特性启用。默认情况下,启用的特性包括图像格式支持(glifimage)和所有类型的 Serde(反)序列化(glifserde;这也在我们使用的库中启用了 Serde,例如 Kurbo 和 IntegerOrFloat)。

特性 skiaOutlineContour 类型添加了与 SkPath 之间的转换。

特性 mfek 添加了许多类型,这些类型在 .glif 文件中以它们的 <lib/> 形式表示。

两者 skiamfek 都需要 default

最后,more-image-formats 启用了 GIF、JPEG、WEBP、BMP 和 TIFF 对 .glif 的支持——请注意,由于规范中只有 PNG,只有 PNG 会被其他软件“看到”,此特性仅存在于“草案”/“背景”图像中,而不是您在运行 例如 fontmake 在包含 .glif glifparser 放置的非 PNG 图像的 UFO 上时预期的最终字体中的图像。

许可证

Copyright 2020–2023 Fredrick R. Brennan and MFEK Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

  http://apache.ac.cn/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

依赖项

~6–11MB
~165K SLoC