diff --git a/src/data_request.rs b/src/data_request.rs index cb168a75..4d7a5ac7 100644 --- a/src/data_request.rs +++ b/src/data_request.rs @@ -2,21 +2,54 @@ /// A type that describes which components of a UFO should be loaded. /// -/// By default, we load all components of the UFO file; however if you only -/// need some subset of these, you can pass this struct to [`Ufo::with_fields`] -/// in order to only load the fields specified in this object. This can help a -/// lot with performance with large UFO files if you don't need the glyph data. +/// By default, all components of the UFO file are loaded; however, if you only +/// need a subset of them, you can pass this struct to [`Ufo::with_fields`] in +/// order to only load the fields you specify. This can improve performance in +/// large projects. +/// +/// # Examples +/// +/// A [DataRequest] that excludes all layer, glyph and kerning data: +/// +/// ``` +/// use norad::DataRequest; +/// +/// let datareq = DataRequest::default().layers(false).kerning(false); +/// ``` +/// +/// A [DataRequest] that excludes all UFO data and images: +/// +/// ``` +/// use norad::DataRequest; +/// +/// let datareq = DataRequest::default().data(false).images(false); +/// ``` +/// +/// A [DataRequest] that only includes parsed lib.plist data: +/// +/// ``` +/// use norad::DataRequest; +/// +/// let datareq = DataRequest::none().lib(true); +/// ``` /// /// [`Ufo::with_fields`]: struct.Ufo.html#method.with_fields #[derive(Clone, Copy, Debug, PartialEq)] #[non_exhaustive] pub struct DataRequest { + /// Load and parse all layers and glyphs. pub layers: bool, + /// Load parsed lib.plist data pub lib: bool, + /// Load parsed groups.plist data pub groups: bool, + /// Load parsed kerning.plist data pub kerning: bool, + /// Load Adobe .fea format feature file data pub features: bool, + /// Load data pub data: bool, + /// Load images pub images: bool, } @@ -25,17 +58,17 @@ impl DataRequest { DataRequest { layers: b, lib: b, groups: b, kerning: b, features: b, data: b, images: b } } - /// Returns a `DataRequest` requesting all UFO data. + /// Returns a [`DataRequest`] requesting all UFO data. pub fn all() -> Self { DataRequest::from_bool(true) } - /// Returns a `DataRequest` requesting no UFO data. + /// Returns a [`DataRequest`] requesting no UFO data. pub fn none() -> Self { DataRequest::from_bool(false) } - /// Request that returned UFO data include the glyph layers and points. + /// Request that returned UFO data include layers and their glyph data. pub fn layers(mut self, b: bool) -> Self { self.layers = b; self @@ -59,8 +92,8 @@ impl DataRequest { self } - /// Request that returned UFO data include OpenType Layout features in Adobe - /// .fea format. + /// Request that returned UFO data include [OpenType Layout features in Adobe + /// .fea format](https://unifiedfontobject.org/versions/ufo3/features.fea/). pub fn features(mut self, b: bool) -> Self { self.features = b; self diff --git a/src/datastore.rs b/src/datastore.rs index ca12bc2b..d2c765ff 100644 --- a/src/datastore.rs +++ b/src/datastore.rs @@ -24,7 +24,9 @@ use crate::Error; /// file. Images must always be in a flat directory. The paths are always relative to /// a UFO's data directory. /// -/// # Example +/// This type supports partial equality testing that is based on path comparisons. +/// +/// # Examples /// /// Consider a UFO on disk with the following structure: /// @@ -75,6 +77,7 @@ pub struct Store { #[doc(hidden)] pub struct Data; +/// Lazy access to the contents of the UFO's `data` directory. pub type DataStore = Store; /// Implements custom behavior for the images store. @@ -82,6 +85,7 @@ pub type DataStore = Store; #[doc(hidden)] pub struct Image; +/// Lazy access to the contents of the UFO's `images` directory. pub type ImageStore = Store; /// Defines custom behavior for data and images stores. @@ -122,7 +126,7 @@ where } } -/// Implements partial equality testing by just comparing paths. +/// Implements path testing-based partial equality for [Store]. impl PartialEq for Store { fn eq(&self, other: &Self) -> bool { self.items.len() == other.items.len() @@ -275,7 +279,7 @@ impl Store { self.items.is_empty() } - /// An iterator visiting all paths in arbitrary order. + /// Returns an iterator visiting all paths in arbitrary order. pub fn keys(&self) -> impl Iterator { self.items.keys() } @@ -317,7 +321,7 @@ impl Store { } } - /// Try to insert data for this path. Overwrites an existing data. + /// Try to insert data for this path. Overwrites existing data. /// /// Does not return the overwritten data, use [`Self::get`] first to get it if you need /// it. diff --git a/src/error.rs b/src/error.rs index 1ae13c87..5677cc96 100644 --- a/src/error.rs +++ b/src/error.rs @@ -17,35 +17,65 @@ pub enum Error { /// An error returned when trying to save a Glyph that contains a `public.objectLibs` /// lib key already (the key is automatically managed by Norad). PreexistingPublicObjectLibsKey, + /// An error returned when there is no default layer in the UFO directory. MissingDefaultLayer, + /// An error returned when an expected layer is missing. MissingLayer(String), + /// An error returned when a layer is duplicated. DuplicateLayer(String), + /// An error returned when there is an invalid color definition. InvalidColor(InvalidColorString), + /// An error returned when there is a duplicate glyph. DuplicateGlyph { + /// The layer name. layer: String, + /// The glyph name. glyph: String, }, + /// An error returned when there is a missing expected glyph MissingGlyph { + /// The layer name. layer: String, + /// The glyph name. glyph: String, }, + /// An error returned when there is an input/output problem during processing IoError(IoError), + /// An error returned when there is an XML parsing problem. ParseError(XmlError), + /// An error that wraps a [GlifError]. Glif(GlifError), + /// An error that wraps a [GlifWriteError]. GlifWrite(GlifWriteError), + /// An error that wraps a [PlistError]. PlistError(PlistError), + /// An error returned when there is invalid fontinfo.plist data. InvalidFontInfo, + /// An error returned when there is a problem during fontinfo.plist version up-conversion. FontInfoUpconversion, + /// An error returned when there is invalid groups.plist data. InvalidGroups(GroupsValidationError), + /// An error returned when there is a problem during groups.plist version up-conversion. GroupsUpconversionFailure(GroupsValidationError), - // the string is the key + /// An error returned when there is a problem parsing plist data into + /// [`plist::Dictionary`] types. + /// + /// The string is the dictionary key. ExpectedPlistDictionary(String), + /// An error returned when there is an unexpected plist string. ExpectedPlistString, + /// An error returned when there is an inappropriate negative sign on a value. ExpectedPositiveValue, + /// An error returned when there is a problem with kurbo contour conversion. #[cfg(feature = "kurbo")] ConvertContour(ErrorKind), + /// An error returned when there is a missing mandatory file. MissingFile(String), + /// An error returned when the requested UFO directory path is not present. MissingUfoDir(String), + /// An error returned when there is an invalid entry in an image or data store. + /// + /// This error wraps a [`StoreError`] type and provides additional path data. InvalidStoreEntry(PathBuf, StoreError), } @@ -74,15 +104,23 @@ pub enum StoreError { /// An error representing a failure to validate UFO groups. #[derive(Debug)] pub enum GroupsValidationError { + /// An error returned when there is an invalid groups name. InvalidName, - OverlappingKerningGroups { glyph_name: String, group_name: String }, + /// An error returned when there are overlapping kerning groups. + OverlappingKerningGroups { + /// The glyph name. + glyph_name: String, + /// The group name. + group_name: String, + }, } -/// A [`Color`] string was invalid. +/// An error representing an invalid [`Color`] string. /// /// [`Color`]: crate::Color #[derive(Debug)] pub struct InvalidColorString { + /// The source string that caused the error. source: String, } @@ -92,15 +130,19 @@ impl InvalidColorString { } } -/// An error that occurs while parsing a .glif file +/// An error representing a failure during .glif file parsing. #[derive(Debug)] pub struct GlifError { + /// The glif file path. pub path: Option, + /// The buffer position. pub position: usize, + /// The kind of error. pub kind: ErrorKind, } -/// An error when attempting to write a .glif file +/// An error representing a failure during .glif file serialization. This +/// error wraps [GlyphName] and [WriteError] types. #[derive(Debug)] pub struct GlifWriteError { /// The name of the glif where the error occured. @@ -113,6 +155,7 @@ pub struct GlifWriteError { /// out a .glif type. #[derive(Debug)] pub enum WriteError { + /// XML serialzation error. Wraps a [XmlError]. Xml(XmlError), /// When writing out the 'lib' section, we use the plist crate to generate /// the plist xml, and then strip the preface and closing tag. @@ -120,12 +163,14 @@ pub enum WriteError { /// If for some reason the implementation of that crate changes, we could /// be affected, although this is very unlikely. InternalLibWriteError, + /// Generic serialization error. Wraps an [IoError]. IoError(IoError), + /// Plist serialization error. Wraps a [PlistError]. Plist(PlistError), } /// Errors that happen when parsing `glif` files. This is converted into either -/// `Error::Xml` or `Error::Glif` at the parse boundary. +/// `Error::ParseError` or `Error::Glif` at the parse boundary. #[derive(Debug)] pub(crate) enum GlifErrorInternal { /// A problem with the xml data. @@ -137,38 +182,71 @@ pub(crate) enum GlifErrorInternal { /// The reason for a glif parse failure. #[derive(Debug, Clone, Copy)] pub enum ErrorKind { + /// The glif version is not supported by this library. UnsupportedGlifVersion, + /// An unknown point type. UnknownPointType, + /// The first XML element of a glif file is invalid. WrongFirstElement, + /// Missing a close tag. MissingCloseTag, + /// Has an unexpected tag. UnexpectedTag, + /// Has an invalid hexadecimal value. BadHexValue, + /// Has an invalid numeric value. BadNumber, + /// Has an invalid color value. BadColor, + /// Has an invalid anchor definition. BadAnchor, + /// Has an invalid point definition. BadPoint, + /// Has an invalid guideline definition. BadGuideline, + /// Has an invalid component definition. BadComponent, + /// Has an invalid image definition. BadImage, + /// Has an invalid identifier. BadIdentifier, + /// Has an invalid lib. BadLib, + /// Has an unexected duplicate value. UnexpectedDuplicate, + /// Has an unexpected move definition. UnexpectedMove, + /// Has an unexpected smooth definition. UnexpectedSmooth, + /// Has an unexpected element definition. UnexpectedElement, + /// Has an unexpected attribute definition. UnexpectedAttribute, + /// Has an unexpected end of file definition. UnexpectedEof, + /// Has an unexpected point following an off curve point definition. UnexpectedPointAfterOffCurve, + /// Has too many off curve points in sequence. TooManyOffCurves, + /// The contour pen path was not started PenPathNotStarted, + /// Has trailing off curve points defined. TrailingOffCurves, + /// Has duplicate identifiers. DuplicateIdentifier, + /// Has unexepected drawing data. UnexpectedDrawing, + /// Has incomplete drawing data. UnfinishedDrawing, + /// Has an unexpected point field. UnexpectedPointField, + /// Has an unexpected component field. UnexpectedComponentField, + /// Has an unexpected anchor field. UnexpectedAnchorField, + /// Has an unexpected guideline field. UnexpectedGuidelineField, + /// Has an unexpected image field. UnexpectedImageField, } @@ -268,7 +346,7 @@ impl std::fmt::Display for ErrorKind { match self { ErrorKind::UnsupportedGlifVersion => write!(f, "Unsupported glif version"), ErrorKind::UnknownPointType => write!(f, "Unknown point type"), - ErrorKind::WrongFirstElement => write!(f, "Wrong first element"), + ErrorKind::WrongFirstElement => write!(f, "Wrong first XML element in glif file"), ErrorKind::MissingCloseTag => write!(f, "Missing close tag"), ErrorKind::UnexpectedTag => write!(f, "Unexpected tag"), ErrorKind::BadHexValue => write!(f, "Bad hex value"), diff --git a/src/font.rs b/src/font.rs index 17501e08..28e957a3 100644 --- a/src/font.rs +++ b/src/font.rs @@ -32,18 +32,44 @@ pub(crate) static DATA_DIR: &str = "data"; pub(crate) static IMAGES_DIR: &str = "images"; /// A Unified Font Object. +/// +/// See the [UFO specification] for a description of the underlying data. +/// +/// [UFO specification]: https://unifiedfontobject.org/versions/ufo3/ #[derive(Clone, Debug, Default, PartialEq)] #[non_exhaustive] pub struct Font { + /// [metainfo.plist][mi] parsed data field + /// + /// [mi]: https://unifiedfontobject.org/versions/ufo3/metainfo.plist/ pub meta: MetaInfo, + /// [fontinfo.plist][fi] parsed data field + /// + /// [fi]: https://unifiedfontobject.org/versions/ufo3/fontinfo.plist/ pub font_info: FontInfo, + /// font layers, each containing an independent set of glyphs. pub layers: LayerSet, + /// [lib.plist][l] parsed data field + /// + /// [l]: https://unifiedfontobject.org/versions/ufo3/lib.plist/ pub lib: Plist, + /// [groups.plist][g] parsed data field + /// + /// [g]: https://unifiedfontobject.org/versions/ufo3/groups.plist/ pub groups: Groups, + /// [kerning.plist][k] parsed data field + /// + /// [k]: https://unifiedfontobject.org/versions/ufo3/kerning.plist/ pub kerning: Kerning, + /// [features.fea][fea] file data field + /// + /// [fea]: https://unifiedfontobject.org/versions/ufo3/features.fea/ pub features: String, + /// [`DataRequest`] field pub data_request: DataRequest, + /// [`DataStore`] field pub data: DataStore, + /// [`ImageStore`] field pub images: ImageStore, } @@ -53,8 +79,11 @@ pub struct Font { #[derive(Debug, Clone, Copy, Serialize_repr, Deserialize_repr, PartialEq)] #[repr(u8)] pub enum FormatVersion { + /// UFO specification major version 1. Only reading (and upconversion) is supported. V1 = 1, + /// UFO specfication major version 2. Only reading (and upconversion) is supported. V2 = 2, + /// UFO specification major version 3 V3 = 3, } @@ -64,8 +93,11 @@ pub enum FormatVersion { #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] #[serde(rename_all = "camelCase")] pub struct MetaInfo { + /// Creator field pub creator: Option, + /// UFO specification major version field pub format_version: FormatVersion, + /// UFO specification minor version field #[serde(default, skip_serializing_if = "is_zero")] pub format_version_minor: u32, } @@ -85,28 +117,75 @@ impl Default for MetaInfo { } impl Font { - /// Create a new, empty `Font` object. + /// Returns a new, empty [`Font`] object. pub fn new() -> Self { Font::default() } - /// Attempt to load a font object from a file. + /// Returns a [`Font`] object with data from a UFO directory `path`. /// /// `path` must point to a directory with the structure described in /// [v3 of the Unified Font Object][v3] spec. /// - /// # Note + /// # Examples + /// + /// ```no_run + /// use norad::Font; + /// + /// let ufo = Font::load("path/to/font.ufo").expect("failed to load"); + /// ``` /// - /// This will consume the `public.objectLibs` key in the global lib + /// Note: This will consume the `public.objectLibs` key in the global lib /// and in glyph libs and assign object libs found therein to global /// guidelines and glyph objects with the matching identifier, respectively. /// + /// See [Font::load_requested_data] for a load method that supports customization + /// of the data inclusion / exclusion criteria. + /// /// [v3]: http://unifiedfontobject.org/versions/ufo3/ pub fn load>(path: P) -> Result { Self::load_requested_data(path, &DataRequest::default()) } - /// Attempt to load the requested elements of a font object from a file. + /// Returns a [`Font`] object with custom data inclusion/exclusion + /// criteria from a UFO directory `path`. + /// + /// UFO data inclusion and exclusion criteria are defined with a [`DataRequest`] parameter. + /// + /// # Examples + /// + /// A font object that excludes all layer, glyph and kerning data: + /// + /// ```no_run + /// use norad::DataRequest; + /// use norad::Font; + /// + /// let datareq = DataRequest::default().layers(false).kerning(false); + /// + /// let ufo = Font::load_requested_data("path/to/font.ufo", &datareq).expect("failed to load"); + /// ``` + /// + /// A font object that excludes all data and images: + /// + /// ```no_run + /// use norad::DataRequest; + /// use norad::Font; + /// + /// let datareq = DataRequest::default().data(false).images(false); + /// + /// let ufo = Font::load_requested_data("path/to/font.ufo", &datareq).expect("failed to load"); + /// ``` + /// + /// A font object that includes only parsed lib.plist data: + /// + /// ```no_run + /// use norad::DataRequest; + /// use norad::Font; + /// + /// let datareq = DataRequest::none().lib(true); + /// + /// let ufo = Font::load_requested_data("path/to/font.ufo", &datareq).expect("failed to load"); + /// ``` pub fn load_requested_data( path: impl AsRef, request: &DataRequest, @@ -217,20 +296,103 @@ impl Font { }) } - /// Attempt to save this UFO to the given path, overriding any existing contents. + /// Serialize a [`Font`] to the given `path`, overwriting any existing contents. /// - /// This may fail; instead of saving directly to the target path, it is a good + /// # Examples + /// + /// With a [`Font`] object such as: + /// + /// ```no_run + /// use norad::Font; + /// + /// let ufo = Font::load("path/to/in-font.ufo").expect("failed to load"); + /// # ufo.save("path/to/out-font.ufo").expect("failed to save"); + /// ``` + /// + /// do things with the [`Font`], then serialize to disk with: + /// + /// ```no_run + /// # use norad::Font; + /// # let ufo = Font::load("path/to/in-font.ufo").expect("failed to load"); + /// ufo.save("path/to/out-font.ufo").expect("failed to save"); + /// ``` + /// + /// Note: This may fail; instead of saving directly to the target path, it is a good /// idea to save to a temporary location and then move that to the target path /// if the save is successful. /// /// This _will_ fail if either the global or any glyph lib contains the - /// `public.objectLibs` key, as object lib management is done automatically. + /// `public.objectLibs` key, as object lib management must currently be done + /// by norad. pub fn save(&self, path: impl AsRef) -> Result<(), Error> { let path = path.as_ref(); self.save_impl(path, &Default::default()) } - /// Attempt to save the UFO, using the provided [`WriteOptions`]. + /// Serialize a [`Font`] to the given `path`, overwriting any existing contents, + /// with custom [`WriteOptions`] serialization format settings. + /// + /// # Examples + /// + /// With a [`Font`] object: + /// + /// ```no_run + /// use norad::{Font, QuoteChar, WriteOptions}; + /// + /// let ufo = Font::load("path/to/in-font.ufo").expect("failed to load"); + /// ``` + /// + /// define the serialization format with a [`WriteOptions`] type: + /// + /// ```no_run + /// # use norad::{Font, QuoteChar, WriteOptions}; + /// # let ufo = Font::load("path/to/in-font.ufo").expect("failed to load"); + /// let single_tab = WriteOptions::default(); + /// + /// let two_tabs = WriteOptions::default() + /// .whitespace("\t\t"); + /// + /// let spaces = WriteOptions::default() + /// .whitespace(" "); + /// + /// let spaces_and_singlequotes = WriteOptions::default() + /// .whitespace(" ") + /// .quote_char(QuoteChar::Single); + /// ``` + /// + /// and serialize to disk with the respective [`WriteOptions`] configuration: + /// + /// ```no_run + /// # use norad::{Font, QuoteChar, WriteOptions}; + /// # let ufo = Font::load("path/to/in-font.ufo").expect("failed to load"); + /// # let single_tab = WriteOptions::default(); + /// # let two_tabs = WriteOptions::default() + /// # .whitespace("\t\t"); + /// # let spaces = WriteOptions::default() + /// # .whitespace(" "); + /// # let spaces_and_singlequotes = WriteOptions::default() + /// # .whitespace(" ") + /// # .quote_char(QuoteChar::Single); + /// // with single tab indentation (default) + /// ufo.save_with_options("path/to/out-font1.ufo", &single_tab); + /// + /// // with two tab indentation + /// ufo.save_with_options("path/to/out-font2.ufo", &two_tabs); + /// + /// // with two space indentation + /// ufo.save_with_options("path/to/out-font3.ufo", &spaces); + /// + /// // with two space indentation and single quote XML declarations + /// ufo.save_with_options("path/to/out-font4.ufo", &spaces_and_singlequotes); + /// ``` + /// + /// Note: This may fail; instead of saving directly to the target path, it is a good + /// idea to save to a temporary location and then move that to the target path + /// if the save is successful. + /// + /// This _will_ fail if either the global or any glyph lib contains the + /// `public.objectLibs` key, as object lib management must currently be done + /// by norad. pub fn save_with_options( &self, path: impl AsRef, @@ -363,12 +525,13 @@ impl Font { self.layers.iter() } - /// Returns an iterator over all the glyphs in the default layer. + /// Returns an iterator over all the glyph names _in the default layer_. pub fn iter_names(&self) -> impl Iterator + '_ { self.layers.default_layer().glyphs.keys().cloned() } - /// Returns a reference to the glyph with the given name (in the default layer). + /// Returns a reference to the glyph with the given name _in the default + /// layer_. pub fn get_glyph(&self, key: &K) -> Option<&Arc> where GlyphName: Borrow, @@ -377,8 +540,8 @@ impl Font { self.default_layer().get_glyph(key) } - /// Returns a mutable reference to the glyph with the given name, - /// IN THE DEFAULT LAYER, if it exists. + /// Returns a mutable reference to the glyph with the given name + /// _in the default layer_, if it exists. pub fn get_glyph_mut(&mut self, key: &K) -> Option<&mut Glyph> where GlyphName: Borrow, @@ -387,7 +550,7 @@ impl Font { self.default_layer_mut().get_glyph_mut(key) } - /// Returns the total number of glyphs in the default layer. + /// Returns the total number of glyphs _in the default layer_. pub fn glyph_count(&self) -> usize { self.default_layer().len() } diff --git a/src/fontinfo.rs b/src/fontinfo.rs index 8ed3c348..53a0d9b7 100644 --- a/src/fontinfo.rs +++ b/src/fontinfo.rs @@ -25,148 +25,270 @@ use crate::{Error, FormatVersion, Guideline, Identifier, Plist}; pub struct FontInfo { // INFO: Keep this struct sorted alphabetically, serde serializes it in the order you see // here and Plist files should be sorted. + /// Ascender value (ascender). pub ascender: Option, + /// Cap height value (capHeight). pub cap_height: Option, + /// Copyright statement (copyright). pub copyright: Option, + /// Descender value (descender). pub descender: Option, + /// Family name (familyName). pub family_name: Option, + /// Guideline definitions that apply to all glyphs in + /// all layers (guidelines). pub guidelines: Option>, + /// Italic angle in counter-clockwise degrees (italicAngle). pub italic_angle: Option, + /// Family ID number (macintoshFONDFamilyID). #[serde(rename = "macintoshFONDFamilyID")] pub macintosh_fond_family_id: Option, + /// Font name for the FOND resource (macintoshFONDName). #[serde(rename = "macintoshFONDName")] pub macintosh_fond_name: Option, + /// Arbitrary note (note). pub note: Option, + /// A collection of gasp Range Records (openTypeGaspRangeRecords). pub open_type_gasp_range_records: Option>, + /// Creation date (openTypeHeadCreated). pub open_type_head_created: Option, + /// head table flags (openTypeHeadFlags). pub open_type_head_flags: Option, + /// Smallest readable size in pixels (openTypeHeadLowestRecPPEM). #[serde(rename = "openTypeHeadLowestRecPPEM")] pub open_type_head_lowest_rec_ppem: Option, + /// Ascender value (openTypeHheaAscender). pub open_type_hhea_ascender: Option, + /// Caret offset value (openTypeHheaCaretOffset). pub open_type_hhea_caret_offset: Option, + /// Caret slope rise value (openTypeHheaCaretSlopeRise). pub open_type_hhea_caret_slope_rise: Option, + /// Caret slope run value (openTypeHheaCaretSlopeRun) pub open_type_hhea_caret_slope_run: Option, + /// Descender value (openTypeHheaDescender). pub open_type_hhea_descender: Option, + /// Line gap value (openTypeHheaLineGap). pub open_type_hhea_line_gap: Option, + /// Compatible full name (openTypeNameCompatibleFullName). pub open_type_name_compatible_full_name: Option, + /// Description of the font (openTypeNameDescription). pub open_type_name_description: Option, + /// URL for the designer (openTypeNameDesignerURL). #[serde(rename = "openTypeNameDesignerURL")] pub open_type_name_designer_url: Option, + /// Designer name (openTypeNameDesigner). pub open_type_name_designer: Option, + /// License text (openTypeNameLicense). pub open_type_name_license: Option, + /// License URL (openTypeNameLicenseURL). #[serde(rename = "openTypeNameLicenseURL")] pub open_type_name_license_url: Option, + /// Manufacturer name (openTypeNameManufacturer). pub open_type_name_manufacturer: Option, + /// Manufacturer URL (openTypeNameManufacturerURL). #[serde(rename = "openTypeNameManufacturerURL")] pub open_type_name_manufacturer_url: Option, + /// Preferred family name (openTypeNamePreferredFamilyName). pub open_type_name_preferred_family_name: Option, + /// Preferred sub-family name (openTypeNamePreferredSubfamilyName). pub open_type_name_preferred_subfamily_name: Option, + /// A collection of name records (openTypeNameRecords). pub open_type_name_records: Option>, + /// Sample text (openTypeNameSampleText). pub open_type_name_sample_text: Option, + /// Unique ID string (openTypeNameUniqueID). #[serde(rename = "openTypeNameUniqueID")] pub open_type_name_unique_id: Option, + /// Version string (openTypeNameVersion). pub open_type_name_version: Option, + /// WWS family name (openTypeNameWWSFamilyName). #[serde(rename = "openTypeNameWWSFamilyName")] pub open_type_name_wws_family_name: Option, + /// WWS sub-family name (openTypeNameWWSSubfamilyName). #[serde(rename = "openTypeNameWWSSubfamilyName")] pub open_type_name_wws_subfamily_name: Option, + /// Bit flags that represent code page ranges present in the font + /// (openTypeOS2CodePageRanges). #[serde(rename = "openTypeOS2CodePageRanges")] pub open_type_os2_code_page_ranges: Option, + /// Font class and sub-class (openTypeOS2FamilyClass). #[serde(rename = "openTypeOS2FamilyClass")] pub open_type_os2_family_class: Option, + /// Panose specification settings (openTypeOS2Panose). #[serde(rename = "openTypeOS2Panose")] pub open_type_os2_panose: Option, + /// fsSelection bit settings (openTypeOS2Selection). #[serde(rename = "openTypeOS2Selection")] pub open_type_os2_selection: Option, + /// Strikeout position (openTypeOS2StrikeoutPosition). #[serde(rename = "openTypeOS2StrikeoutPosition")] pub open_type_os2_strikeout_position: Option, + /// Strikeout size (openTypeOS2StrikeoutSize). #[serde(rename = "openTypeOS2StrikeoutSize")] pub open_type_os2_strikeout_size: Option, + /// Subscript x offset (openTypeOS2SubscriptXOffset). #[serde(rename = "openTypeOS2SubscriptXOffset")] pub open_type_os2_subscript_x_offset: Option, + /// Subscript horizontal size (openTypeOS2SubscriptXSize). #[serde(rename = "openTypeOS2SubscriptXSize")] pub open_type_os2_subscript_x_size: Option, + /// Subscript y offset (openTypeOS2SubscriptYOffset). #[serde(rename = "openTypeOS2SubscriptYOffset")] pub open_type_os2_subscript_y_offset: Option, + /// Subscript vertical size (openTypeOS2SubscriptYSize). #[serde(rename = "openTypeOS2SubscriptYSize")] pub open_type_os2_subscript_y_size: Option, + /// Superscript x offset (openTypeOS2SuperscriptXOffset). #[serde(rename = "openTypeOS2SuperscriptXOffset")] pub open_type_os2_superscript_x_offset: Option, + /// Superscript horizontal size (openTypeOS2SuperscriptXSize). #[serde(rename = "openTypeOS2SuperscriptXSize")] pub open_type_os2_superscript_x_size: Option, + /// Superscript y offset (openTypeOS2SuperscriptYOffset). #[serde(rename = "openTypeOS2SuperscriptYOffset")] pub open_type_os2_superscript_y_offset: Option, + /// Superscript vertical size (openTypeOS2SuperscriptYSize). #[serde(rename = "openTypeOS2SuperscriptYSize")] pub open_type_os2_superscript_y_size: Option, + /// Bit flags indicating the embedding type (openTypeOS2Type). #[serde(rename = "openTypeOS2Type")] pub open_type_os2_type: Option, + /// Ascender value (openTypeOS2TypoAscender). #[serde(rename = "openTypeOS2TypoAscender")] pub open_type_os2_typo_ascender: Option, + /// Descender value (openTypeOS2TypoDescender). #[serde(rename = "openTypeOS2TypoDescender")] pub open_type_os2_typo_descender: Option, + /// Line gap value (openTypeOS2TypoLineGap). #[serde(rename = "openTypeOS2TypoLineGap")] pub open_type_os2_typo_line_gap: Option, + /// Bit flags that represent Unicode ranges present in + /// the font (openTypeOS2UnicodeRanges). #[serde(rename = "openTypeOS2UnicodeRanges")] pub open_type_os2_unicode_ranges: Option, + /// Four character vendor ID (openTypeOS2VendorID). #[serde(rename = "openTypeOS2VendorID")] pub open_type_os2_vendor_id: Option, + /// OS/2 weight class (openTypeOS2WeightClass). #[serde(rename = "openTypeOS2WeightClass")] pub open_type_os2_weight_class: Option, + /// OS/2 width class (openTypeOS2WidthClass). #[serde(rename = "openTypeOS2WidthClass")] pub open_type_os2_width_class: Option, + /// Ascender value (openTypeOS2WinAscent). #[serde(rename = "openTypeOS2WinAscent")] pub open_type_os2_win_ascent: Option, + /// Descender value (openTypeOS2WinDescent). #[serde(rename = "openTypeOS2WinDescent")] pub open_type_os2_win_descent: Option, + /// Caret offset value (openTypeVheaCaretOffset). pub open_type_vhea_caret_offset: Option, + /// Caret slope rise value (openTypeVheaCaretSlopeRise). pub open_type_vhea_caret_slope_rise: Option, + /// Caret slope run value (openTypeVheaCaretSlopeRun). pub open_type_vhea_caret_slope_run: Option, + /// Ascender value (openTypeVheaVertTypoAscender). pub open_type_vhea_vert_typo_ascender: Option, + /// Descender value (openTypeVheaVertTypoDescender). pub open_type_vhea_vert_typo_descender: Option, + /// Line gap value (openTypeVheaVertTypoLineGap). pub open_type_vhea_vert_typo_line_gap: Option, + /// Postscript BlueFuzz value (postscriptBlueFuzz). pub postscript_blue_fuzz: Option, + /// Postscript BlueScale value (postscriptBlueScale). pub postscript_blue_scale: Option, + /// Postscript BlueShift value (postscriptBlueShift). pub postscript_blue_shift: Option, + /// A collection of values that should be in the + /// Type 1/CFF BlueValues field (postscriptBlueValues). pub postscript_blue_values: Option>, + /// Name of default glyph in PFM files (postscriptDefaultCharacter). pub postscript_default_character: Option, + /// Default glyph width (postscriptDefaultWidthX). pub postscript_default_width_x: Option, + /// A collection of values that should be in the Type 1/CFF + /// FamilyBlues field (postscriptFamilyBlues). pub postscript_family_blues: Option>, + /// A collection of values that should be in the Type 1/CFF + /// FamilyOtherBlues field (postscriptFamilyOtherBlues). pub postscript_family_other_blues: Option>, + /// Type 1/CFF table FontName field (postscriptFontName). pub postscript_font_name: Option, + /// Boolean value that indicates how Type 1/CFF ForceBold should + /// be set (postscriptForceBold). pub postscript_force_bold: Option, + /// Type 1/CFF table FullName field (postscriptFullName). pub postscript_full_name: Option, + /// Boolean that indicates if a font is monospaced + /// (postscriptIsFixedPitch). pub postscript_is_fixed_pitch: Option, + /// Glyph nominal width (postscriptNominalWidthX). pub postscript_nominal_width_x: Option, + /// A collection of values that should be in the Type 1/CFF + /// OtherBlues field (postscriptOtherBlues). pub postscript_other_blues: Option>, + /// Slant angle in counter-clockwise degrees from the vertical + /// (postscriptSlantAngle). pub postscript_slant_angle: Option, + /// A collection of horizontal stems sorted in the order specified + /// in the Type 1/CFF specification (postscriptStemSnapH). pub postscript_stem_snap_h: Option>, + /// A collection of vertical stems sorted in the order specified + /// in the Type 1/CFF specification (postscriptStemSnapV). pub postscript_stem_snap_v: Option>, + /// Underline position value (postscriptUnderlinePosition). pub postscript_underline_position: Option, + /// Underline thickness value (postscriptUnderlineThickness). pub postscript_underline_thickness: Option, + /// Unique ID as specified by the Type 1/CFF specification + /// (postscriptUniqueID). #[serde(rename = "postscriptUniqueID")] pub postscript_unique_id: Option, + /// Overall font weight (postscriptWeightName). pub postscript_weight_name: Option, + /// Windows character set (postscriptWindowsCharacterSet). pub postscript_windows_character_set: Option, + /// Family name in bold, italic, bold italic style mapping + /// (styleMapFamilyName). pub style_map_family_name: Option, + /// Style map style (styleMapStyleName). pub style_map_style_name: Option, + /// Style name (styleName). pub style_name: Option, + /// Trademark statement (trademark). pub trademark: Option, + /// Units per em (unitsPerEm). pub units_per_em: Option, + /// Major version number (versionMajor). pub version_major: Option, + /// Minor version number (versionMinor). pub version_minor: Option, + /// Major version number (woffMajorVersion). pub woff_major_version: Option, + /// Font copyright (woffMetadataCopyright). pub woff_metadata_copyright: Option, + /// Font credits (woffMetadataCredits). pub woff_metadata_credits: Option, + /// Font description (woffMetadataDescription). pub woff_metadata_description: Option, + /// A collection of metadata extension records (woffMetadataExtensions). pub woff_metadata_extensions: Option>, + /// Font license (woffMetadataLicense). pub woff_metadata_license: Option, + /// Font licensee (woffMetadataLicensee). pub woff_metadata_licensee: Option, + /// Font trademark (woffMetadataTrademark). pub woff_metadata_trademark: Option, + /// Font unique ID (woffMetadataUniqueID). #[serde(rename = "woffMetadataUniqueID")] pub woff_metadata_unique_id: Option, + /// Font vendor (woffMetadataVendor). pub woff_metadata_vendor: Option, + /// Minor version number (woffMinorVersion). pub woff_minor_version: Option, + /// x-height value (xHeight). pub x_height: Option, + /// Year that the font was created (year). pub year: Option, } @@ -325,7 +447,7 @@ struct FontInfoV1 { } impl FontInfo { - /// Create FontInfo from a file, upgrading from the supplied format_version to the highest + /// Returns [`FontInfo`] from a file, upgrading from the supplied `format_version` to the highest /// internally supported version. /// /// The conversion follows what ufoLib and defcon are doing, e.g. various fields that were @@ -610,7 +732,7 @@ impl FontInfo { } } - /// Returns `false` if this FontInfo has any non-default value, and `true` otherwise. + /// Returns `false` if this [`FontInfo`] has any non-default value, and `true` otherwise. pub fn is_empty(&self) -> bool { self == &Self::default() } @@ -794,7 +916,7 @@ impl FontInfo { Ok(()) } - /// Dump guideline libs into a Plist. + /// Dump guideline libs into a [`Plist`]. pub(crate) fn dump_object_libs(&self) -> Plist { let mut object_libs = Plist::default(); @@ -816,8 +938,10 @@ impl FontInfo { #[serde(deny_unknown_fields)] #[serde(rename_all = "camelCase")] pub struct GaspRangeRecord { + /// Range max PPEM. #[serde(rename = "rangeMaxPPEM")] pub range_max_ppem: NonNegativeInteger, + /// Range gasp behavior. pub range_gasp_behavior: Vec, } @@ -825,9 +949,13 @@ pub struct GaspRangeRecord { #[derive(Debug, Clone, Copy, Serialize_repr, Deserialize_repr, PartialEq)] #[repr(u8)] pub enum GaspBehavior { + /// Use grid fitting. Gridfit = 0, + /// Use gray scale rendering. DoGray = 1, + /// Use grid fitting with symmetric smoothing. SymmetricGridfit = 2, + /// Use multi-axis smoothing. SymmetricSmoothing = 3, } @@ -835,36 +963,53 @@ pub enum GaspBehavior { #[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)] #[serde(deny_unknown_fields)] pub struct NameRecord { + /// Name ID. #[serde(rename = "nameID")] pub name_id: NonNegativeInteger, + /// Platform ID. #[serde(rename = "platformID")] pub platform_id: NonNegativeInteger, + /// Platform encoding ID. #[serde(rename = "encodingID")] pub encoding_id: NonNegativeInteger, + /// Language ID. #[serde(rename = "languageID")] pub language_id: NonNegativeInteger, + /// Name record string value. pub string: String, } -/// Corresponds to the allowed values for [openTypeOS2WidthClass](http://unifiedfontobject.org/versions/ufo3/fontinfo.plist/#opentype-os2-table-fields). +/// Corresponds to the allowed values for +/// [openTypeOS2WidthClass](http://unifiedfontobject.org/versions/ufo3/fontinfo.plist/#opentype-os2-table-fields). #[derive(Debug, Clone, Copy, Serialize_repr, Deserialize_repr, PartialEq)] #[repr(u8)] pub enum Os2WidthClass { + /// Ultra-condensed width. UltraCondensed = 1, + /// Extra-condensed width. ExtraCondensed = 2, + /// Condensed width. Condensed = 3, + /// Semi-condensed width. SemiCondensed = 4, + /// Medium (normal) width. Normal = 5, + /// Semi-expanded width. SemiExpanded = 6, + /// Expanded width. Expanded = 7, + /// Extra-expanded width. ExtraExpanded = 8, + /// Ultra-expanded width. UltraExpanded = 9, } /// Corresponds to [openTypeOS2FamilyClass](http://unifiedfontobject.org/versions/ufo3/fontinfo.plist/#opentype-os2-table-fields). #[derive(Debug, Clone, Default, PartialEq)] pub struct Os2FamilyClass { + /// Class ID. pub class_id: u8, + /// Sub-class ID. pub subclass_id: u8, } @@ -907,15 +1052,25 @@ impl<'de> Deserialize<'de> for Os2FamilyClass { /// Corresponds to [openTypeOS2Panose](http://unifiedfontobject.org/versions/ufo3/fontinfo.plist/#opentype-os2-table-fields). #[derive(Debug, Clone, Default, PartialEq)] pub struct Os2Panose { + /// Panose family type. pub family_type: NonNegativeInteger, + /// Panose serif style. pub serif_style: NonNegativeInteger, + /// Panose weight. pub weight: NonNegativeInteger, + /// Panose proportion. pub proportion: NonNegativeInteger, + /// Panose contrast. pub contrast: NonNegativeInteger, + /// Panose stroke variation. pub stroke_variation: NonNegativeInteger, + /// Panose arm style. pub arm_style: NonNegativeInteger, + /// Panose letterform. pub letterform: NonNegativeInteger, + /// Panose midline. pub midline: NonNegativeInteger, + /// Panose x-height. pub x_height: NonNegativeInteger, } @@ -1030,128 +1185,193 @@ impl<'de> Deserialize<'de> for Os2PanoseV2 { #[derive(Debug, Clone, Copy, Serialize_repr, Deserialize_repr, PartialEq)] #[repr(u8)] pub enum PostscriptWindowsCharacterSet { + /// ANSI character set. Ansi = 1, + /// Default character set. Default = 2, + /// Symbol character set. Symbol = 3, + /// Macintosh character set. Macintosh = 4, + /// Shift JIS character set. ShiftJis = 5, + /// Hangul character set. Hangul = 6, + /// Hangul (Johab) character set. HangulJohab = 7, + /// GB2312 character set. Gb2312 = 8, + /// Chinese BIG5 character set. ChineseBig5 = 9, + /// Greek character set. Greek = 10, + /// Turkish character set. Turkish = 11, + /// Vietnamese character set. Vietnamese = 12, + /// Hebrew character set. Hebrew = 13, + /// Arabic character set. Arabic = 14, + /// Baltic character set. Baltic = 15, + /// Bitstream character set. Bitstream = 16, + /// Cyrillic character set. Cyrillic = 17, + /// Thai character set. Thai = 18, + /// Eastern European character set. EasternEuropean = 19, + /// OEM character set. Oem = 20, } /// Corresponds to woffMetadataCopyright in [WOFF Data](http://unifiedfontobject.org/versions/ufo3/fontinfo.plist/#woff-data). #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] pub struct WoffMetadataCopyright { + /// WOFF Metadata Copyright Record pub text: Vec, } /// Corresponds to woffMetadataCredits in [WOFF Data](http://unifiedfontobject.org/versions/ufo3/fontinfo.plist/#woff-data). #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] pub struct WoffMetadataCredits { + /// A collection of WOFF Metadata Credit Records pub credits: Vec, } +/// A WOFF Metadata Credits Record data structure. #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] pub struct WoffMetadataCredit { + /// Name. pub name: String, + /// URL. pub url: Option, + /// Role. pub role: Option, + /// Writing direction. pub dir: Option, + /// Class. pub class: Option, } /// Corresponds to woffMetadataDescription in [WOFF Data](http://unifiedfontobject.org/versions/ufo3/fontinfo.plist/#woff-data). #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] pub struct WoffMetadataDescription { + /// Description URL. pub url: Option, + /// Description Text. pub text: Vec, } +/// A WOFF Metadata Text Record data structure. #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] pub struct WoffMetadataTextRecord { + /// Text. pub text: String, + /// Language. pub language: Option, + /// Writing direction. pub dir: Option, + /// Class. pub class: Option, } +/// A WOFF Metadata Extension Record data structure. #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] pub struct WoffMetadataExtensionRecord { + /// Identifier. pub id: Option, + /// Extension names. pub names: Vec, + /// Extension items. pub items: Vec, } +/// A WOFF Metadata Extension Name Record data structure. #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] pub struct WoffMetadataExtensionNameRecord { + /// Text. pub text: String, + /// Language. pub language: Option, + /// Writing direction. pub dir: Option, + /// Class. pub class: Option, } +/// A WOFF Metadata Extension Item Record data structure. #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] pub struct WoffMetadataExtensionItemRecord { + /// Identifier. pub id: Option, // XXX: Spec does not specify if required, assume optional. + /// Extension names. pub names: Vec, + /// Extension values. pub values: Vec, } +/// A WOFF Metadata Extension Value Record data structure. #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] pub struct WoffMetadataExtensionValueRecord { + /// Text. pub text: String, + /// Language. pub language: Option, + /// Writing direction. pub dir: Option, + /// Class. pub class: Option, } /// Corresponds to woffMetadataLicense in [WOFF Data](http://unifiedfontobject.org/versions/ufo3/fontinfo.plist/#woff-data). #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] pub struct WoffMetadataLicense { + /// License URL. pub url: Option, + /// License identifier. pub id: Option, + /// License text. pub text: Vec, } /// Corresponds to woffMetadataLicensee in [WOFF Data](http://unifiedfontobject.org/versions/ufo3/fontinfo.plist/#woff-data). #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] pub struct WoffMetadataLicensee { + /// Licensee name. pub name: String, + /// Writing direction. pub dir: Option, + /// Class. pub class: Option, } /// Corresponds to woffMetadataTrademark in [WOFF Data](http://unifiedfontobject.org/versions/ufo3/fontinfo.plist/#woff-data). #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] pub struct WoffMetadataTrademark { + /// Trademark text. pub text: Vec, } /// Corresponds to woffMetadataUniqueID in [WOFF Data](http://unifiedfontobject.org/versions/ufo3/fontinfo.plist/#woff-data). #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] pub struct WoffMetadataUniqueId { + /// Unique identifier. pub id: String, } /// Corresponds to woffMetadataVendor in [WOFF Data](http://unifiedfontobject.org/versions/ufo3/fontinfo.plist/#woff-data). #[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq)] pub struct WoffMetadataVendor { + /// Vendor name. pub name: String, + /// Vendor URL. pub url: String, + /// Writing direction. pub dir: Option, + /// Class. pub class: Option, } @@ -1159,7 +1379,9 @@ pub struct WoffMetadataVendor { /// If present, is either "ltr" or "rtl". #[derive(Debug, Eq, PartialEq, Clone)] pub enum WoffAttributeDirection { + /// Left to Right writing direction. LeftToRight, + /// Right to Left writing direction. RightToLeft, } @@ -1193,9 +1415,13 @@ impl<'de> Deserialize<'de> for WoffAttributeDirection { /// If present, is either "regular", "italic", "bold" or "bold italic". #[derive(Debug, Eq, PartialEq, Clone)] pub enum StyleMapStyle { + /// Regular style. Regular, + /// Italic style. Italic, + /// Bold style. Bold, + /// Bold Italic style. BoldItalic, } diff --git a/src/glyph/builder.rs b/src/glyph/builder.rs index cc6a1b82..a448ff33 100644 --- a/src/glyph/builder.rs +++ b/src/glyph/builder.rs @@ -28,7 +28,7 @@ use crate::{ /// constraints about how often a Glyph field or "element" can appear in a `.glif` file. For /// example, calling `outline()` twice results in an error. /// -/// # Example +/// # Examples /// /// ```ignore /// use std::str::FromStr; diff --git a/src/glyph/mod.rs b/src/glyph/mod.rs index 480bafce..30724569 100644 --- a/src/glyph/mod.rs +++ b/src/glyph/mod.rs @@ -26,24 +26,40 @@ pub type GlyphName = Arc; #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "druid", derive(Lens))] pub struct Glyph { + /// Glyph name. + /// + /// Must be at least one character long. Names must not contain control characters. pub name: GlyphName, + /// Glif file format version. pub format: GlifVersion, + /// Glyph height. pub height: f64, + /// Glyph width. pub width: f64, + /// A collection of glyph Unicode code points. + /// + /// The first entry defines the primary Unicode value for this glyph. pub codepoints: Vec, + /// Arbitrary glyph note. pub note: Option, + /// A collection of glyph guidelines. pub guidelines: Vec, + /// A collection of glyph anchors. pub anchors: Vec, + /// A collection of glyph components. pub components: Vec, + /// A collection of glyph contours. pub contours: Vec, + /// Glyph image data. pub image: Option, + /// Glyph library data. pub lib: Plist, } impl Glyph { - /// Load the glyph at this path. + /// Returns a [`Glyph`] at this `path`. /// - /// When loading glyphs in bulk, `load_with_names` should be preferred, + /// When loading glyphs in bulk, [`Glyph::load_with_names`] should be preferred, /// since it will allow glyph names (in glyphs and components) to be shared /// between instances. pub fn load(path: impl AsRef) -> Result { @@ -52,6 +68,8 @@ impl Glyph { Glyph::load_with_names(path, &names) } + /// Returns a [`Glyph`] at this `path` with glyph and component glyph name sharing + /// between instances. pub fn load_with_names(path: &Path, names: &NameList) -> Result { let data = std::fs::read(path)?; parse::GlifParser::from_xml(&data, Some(names)).map_err(|e| match e { @@ -82,7 +100,7 @@ impl Glyph { Ok(()) } - /// Create a new glyph with the given name. + /// Returns a new, "empty" [`Glyph`] with the given `name`. pub fn new_named>(name: S) -> Self { Glyph::new(name.into(), GlifVersion::V2) } @@ -104,24 +122,23 @@ impl Glyph { } } - /// Returns boolean value indicating whether [`Glyph`] is defined with one or - /// more [`Component`]. + /// Returns true if [`Glyph`] contains one or more [`Component`]s. pub fn has_component(&self) -> bool { !self.components.is_empty() } - /// Returns a usize that represents the number of [`Component`] defined on the Glyph. + /// Returns the number of [`Component`]s in the Glyph. pub fn component_count(&self) -> usize { self.components.len() } - /// Returns boolean indicating the presence of one or more [`Component`] with base + /// Returns true if the Glyph contains one or more [`Component`]s with base /// glyph name `basename`. pub fn has_component_with_base(&self, basename: &str) -> bool { self.components.iter().any(|x| *x.base == *basename) } - /// Return a iterator over immutable [`Component`] references filtered by base glyph name. + /// Returns an iterator over immutable [`Component`] references filtered by base glyph name. pub fn get_components_with_base<'b, 'a: 'b>( &'a self, basename: &'b str, @@ -241,50 +258,62 @@ impl Data for Glyph { #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "druid", derive(Data))] pub enum GlifVersion { + /// Glif file format version 1. Saving this version is not supported. V1 = 1, + /// Glif file format version 2. V2 = 2, } -/// An reference position in a glyph, such as for attaching accents. +/// A reference position in a glyph, such as for attaching accents. /// /// See the [Anchor section] of the UFO spec for more information. /// /// [Anchor section]: https://unifiedfontobject.org/versions/ufo3/glyphs/glif/#anchor #[derive(Debug, Clone, PartialEq)] pub struct Anchor { + /// Anchor x coordinate value. pub x: f64, + /// Anchor y coordinate value. pub y: f64, - /// An arbitrary name for the anchor. + /// Optional arbitrary name for the anchor. pub name: Option, + /// Optional anchor color. pub color: Option, - /// Unique identifier for the anchor within the glyph. This attribute is only required - /// when a lib is present and should otherwise only be added as needed. + /// Optional unique identifier for the anchor within the glyph. + /// + /// This attribute is only required when a lib is present and should otherwise only be added as needed. identifier: Option, - /// The anchor's lib for arbitary data. + /// Optional anchor lib for arbitary data. lib: Option, } /// A reference to another glyph, to be included in this glyph's outline. #[derive(Debug, Clone, PartialEq)] pub struct Component { - /// The name of the base glyph. + /// The name of the base glyph used in the component. pub base: GlyphName, + /// Component affine transormation definition. pub transform: AffineTransform, - /// Unique identifier for the component within the glyph. This attribute is only required - /// when a lib is present and should otherwise only be added as needed. + /// Optional unique identifier for the component within the glyph. + /// + /// This attribute is only required when a lib is present and should otherwise only + /// be added as needed. identifier: Option, - /// The component's lib for arbitary data. + /// Optional lib for arbitary component data. lib: Option, } /// A single open or closed bezier path segment. #[derive(Debug, Clone, Default, PartialEq)] pub struct Contour { + /// A collection of contour points. pub points: Vec, - /// Unique identifier for the contour within the glyph. This attribute is only required - /// when a lib is present and should otherwise only be added as needed. + /// Unique identifier for the contour within the glyph. + /// + /// This attribute is only required when a lib is present and should otherwise only + /// be added as needed. identifier: Option, - /// The contour's lib for arbitary data. + /// Optional lib for arbitary contour data. lib: Option, } @@ -348,15 +377,21 @@ impl Contour { /// A single point in a [`Contour`]. #[derive(Debug, Clone, PartialEq)] pub struct ContourPoint { + /// Contour point x coordinate value. pub x: f64, + /// Contour point y coordinate value. pub y: f64, + /// Contour point type. pub typ: PointType, + /// Whether a smooth curvature should be maintained at this point. Must not be set for off-curve points. pub smooth: bool, + /// Optional contour point name. pub name: Option, - /// Unique identifier for the point within the glyph. This attribute is only required - /// when a lib is present and should otherwise only be added as needed. + /// Optional unique identifier for the point within the glyph. + /// + /// This attribute is only required when a lib is present and should otherwise only be added as needed. identifier: Option, - /// The point's lib for arbitary data. + /// Optional lib for arbitary contour point data. lib: Option, } @@ -423,15 +458,22 @@ impl std::fmt::Display for PointType { #[derive(Debug, Clone, Copy, PartialEq)] #[cfg_attr(feature = "druid", derive(Data))] pub struct AffineTransform { + /// x-scale value. pub x_scale: f64, + /// xy-scale value. pub xy_scale: f64, + /// yx-scale value. pub yx_scale: f64, + /// y-scale value. pub y_scale: f64, + /// x-offset value. pub x_offset: f64, + /// y-offset value. pub y_offset: f64, } impl Anchor { + /// Returns a new [`Anchor`] given `x` and `y` coordinate values. pub fn new( x: f64, y: f64, @@ -450,7 +492,7 @@ impl Anchor { this } - /// Returns an immutable reference to the anchor's lib. + /// Returns a reference to the anchor's lib. pub fn lib(&self) -> Option<&Plist> { self.lib.as_ref() } @@ -474,7 +516,7 @@ impl Anchor { self.lib.take() } - /// Returns an immutable reference to the anchor's identifier. + /// Returns a reference to the anchor's identifier. pub fn identifier(&self) -> Option<&Identifier> { self.identifier.as_ref() } @@ -487,6 +529,7 @@ impl Anchor { } impl Contour { + /// Returns a new [`Contour`] given a vector of contour points. pub fn new( points: Vec, identifier: Option, @@ -502,7 +545,7 @@ impl Contour { this } - /// Returns an immutable reference to the contour's lib. + /// Returns a reference to the contour's lib. pub fn lib(&self) -> Option<&Plist> { self.lib.as_ref() } @@ -526,7 +569,7 @@ impl Contour { self.lib.take() } - /// Returns an immutable reference to the contour's identifier. + /// Returns a reference to the contour's identifier. pub fn identifier(&self) -> Option<&Identifier> { self.identifier.as_ref() } @@ -539,6 +582,8 @@ impl Contour { } impl ContourPoint { + /// Returns a new [`ContourPoint`] given an `x` coordinate value, + /// `y` coordinate value, point type, and smooth definition. pub fn new( x: f64, y: f64, @@ -558,7 +603,7 @@ impl ContourPoint { this } - /// Returns an immutable reference to the contour's lib. + /// Returns a reference to the contour's lib. pub fn lib(&self) -> Option<&Plist> { self.lib.as_ref() } @@ -582,7 +627,7 @@ impl ContourPoint { self.lib.take() } - /// Returns an immutable reference to the contour's identifier. + /// Returns a reference to the contour's identifier. pub fn identifier(&self) -> Option<&Identifier> { self.identifier.as_ref() } @@ -609,6 +654,7 @@ impl ContourPoint { } impl Component { + /// Returns a new [`Component`] given a base glyph name and affine transformation definition. pub fn new( base: GlyphName, transform: AffineTransform, @@ -625,7 +671,7 @@ impl Component { this } - /// Returns an immutable reference to the component's lib. + /// Returns a reference to the component's lib. pub fn lib(&self) -> Option<&Plist> { self.lib.as_ref() } @@ -649,7 +695,7 @@ impl Component { self.lib.take() } - /// Returns an immutable reference to the component's identifier. + /// Returns a reference to the component's identifier. pub fn identifier(&self) -> Option<&Identifier> { self.identifier.as_ref() } @@ -684,9 +730,11 @@ impl std::default::Default for AffineTransform { /// An image included in a glyph. #[derive(Debug, Clone, PartialEq)] pub struct Image { - /// Not an absolute / relative path, but the name of the image file. + /// The name of the image file. Must be a base file name, no subdirectories involved. pub file_name: PathBuf, + /// Optional image color. pub color: Option, + /// Affine transformation. pub transform: AffineTransform, } diff --git a/src/glyph/serialize.rs b/src/glyph/serialize.rs index 243e8e45..9aebe878 100644 --- a/src/glyph/serialize.rs +++ b/src/glyph/serialize.rs @@ -27,6 +27,11 @@ impl Glyph { self.encode_xml_with_options(&options) } + /// Serialize the glyph into an XML byte stream with custom string formatting. + /// + /// The order of elements and attributes follows [ufonormalizer] where possible. + /// + /// [ufonormalizer]: https://github.com/unified-font-object/ufoNormalizer/ pub fn encode_xml_with_options(&self, opts: &WriteOptions) -> Result, GlifWriteError> { self.encode_xml_impl(opts) .map_err(|inner| GlifWriteError { name: self.name.clone(), inner }) diff --git a/src/groups.rs b/src/groups.rs index 72483419..9cb8fd34 100644 --- a/src/groups.rs +++ b/src/groups.rs @@ -5,7 +5,7 @@ use crate::GlyphName; /// A map of group name to a list of glyph names. /// -/// We use a BTreeMap because we need sorting for serialization. +/// We use a [`BTreeMap`] because we need sorting for serialization. pub type Groups = BTreeMap>; /// Validate the contents of the groups.plist file according to the rules in the diff --git a/src/guideline.rs b/src/guideline.rs index 65d0fa6f..bb4c4831 100644 --- a/src/guideline.rs +++ b/src/guideline.rs @@ -27,13 +27,21 @@ pub enum Line { Vertical(f64), /// A horizontal line, passing through a given `y` coordinate. Horizontal(f64), - /// An angled line passing through `(x, y)` at `degrees` degrees counteer-clockwise + /// An angled line passing through `(x, y)` at `degrees` degrees counter-clockwise /// to the horizontal. // TODO: make a Degrees newtype that checks `0 <= degrees <= 360`. - Angle { x: f64, y: f64, degrees: f64 }, + Angle { + /// x coordinate. + x: f64, + /// y coordinate. + y: f64, + /// angle degrees. + degrees: f64, + }, } impl Guideline { + /// Returns a new [`Guideline`] struct. pub fn new( line: Line, name: Option, @@ -51,7 +59,7 @@ impl Guideline { this } - /// Returns an immutable reference to the Guideline's lib. + /// Returns a reference to the Guideline's lib. pub fn lib(&self) -> Option<&Plist> { self.lib.as_ref() } @@ -75,7 +83,7 @@ impl Guideline { self.lib.take() } - /// Returns an immutable reference to the Guideline's identifier. + /// Returns a reference to the Guideline's identifier. pub fn identifier(&self) -> Option<&Identifier> { self.identifier.as_ref() } diff --git a/src/identifier.rs b/src/identifier.rs index b4d8a88d..62c3dcdd 100644 --- a/src/identifier.rs +++ b/src/identifier.rs @@ -19,7 +19,7 @@ use crate::error::ErrorKind; pub struct Identifier(Arc); impl Identifier { - /// Create a new `Identifier` from a `String`, if it is valid. + /// Create a new [`Identifier`] from a [`String`], if it is valid. /// /// A valid identifier must have between 0 and 100 characters, and each /// character must be in the printable ASCII range, 0x20 to 0x7E. @@ -32,6 +32,7 @@ impl Identifier { } } + /// Create a new [`Identifier`] from a UUID v4 identifier. pub fn from_uuidv4() -> Self { Self::new(uuid::Uuid::new_v4().to_string()).unwrap() } diff --git a/src/kerning.rs b/src/kerning.rs index 0826afdc..d475c690 100644 --- a/src/kerning.rs +++ b/src/kerning.rs @@ -9,12 +9,12 @@ use serde::Serialize; /// to the second half of a pair (glyph name or group name), which maps to the kerning value /// (high-level view: (first, second) => value). /// -/// We use a `BTreeMap` because we need sorting for serialization. +/// We use a [`BTreeMap`] because we need sorting for serialization. pub type Kerning = BTreeMap>; /// A helper for serializing kerning values. /// -/// KerningSerializer is a crutch to serialize kerning values as integers if they are +/// `KerningSerializer` is a crutch to serialize kerning values as integers if they are /// integers rather than floats. This spares us having to use a wrapper type like /// `IntegerOrFloat` for kerning values. pub(crate) struct KerningSerializer<'a> { diff --git a/src/layer.rs b/src/layer.rs index 4fbdf87e..bd7e4072 100644 --- a/src/layer.rs +++ b/src/layer.rs @@ -28,13 +28,13 @@ pub type LayerName = Arc; /// layers. #[derive(Debug, Clone, PartialEq)] pub struct LayerSet { - // The first layer is always the 'default layer'. + /// A collection of [`Layer`]s. The first [`Layer`] is the default. layers: Vec, } #[allow(clippy::len_without_is_empty)] // never empty impl LayerSet { - /// Load the layers from the provided path. + /// Returns a [`LayerSet`] from the provided `path`. /// /// If a `layercontents.plist` file exists, it will be used, otherwise /// we will assume the pre-UFOv3 behaviour, and expect a single glyphs dir. @@ -67,7 +67,7 @@ impl LayerSet { Ok(LayerSet { layers }) } - /// Create a new `LayerSet`. + /// Returns a new [`LayerSet`] from a `layers` collection. /// /// Will panic if `layers` is empty. pub fn new(mut layers: Vec) -> Self { @@ -76,25 +76,25 @@ impl LayerSet { LayerSet { layers } } - /// The number of layers in the set. + /// Returns the number of layers in the set. /// - /// This should be non-zero. + /// This is always non-zero. #[allow(clippy::len_without_is_empty)] pub fn len(&self) -> usize { self.layers.len() } - /// Get a reference to a layer, by name. + /// Returns a reference to a layer, by name. pub fn get(&self, name: &str) -> Option<&Layer> { self.layers.iter().find(|l| &*l.name == name) } - /// Get a mutable reference to a layer, by name. + /// Returns a mutable reference to a layer, by name. pub fn get_mut(&mut self, name: &str) -> Option<&mut Layer> { self.layers.iter_mut().find(|l| &*l.name == name) } - /// Get a mutable reference to a layer, by name, or create it if it doesn't exist. + /// Returns a mutable reference to a layer, by name, or create it if it doesn't exist. pub fn get_or_create(&mut self, name: &str) -> &mut Layer { if let Some(index) = self.layers.iter().position(|l| &*l.name == name) { self.layers.get_mut(index).unwrap() @@ -105,29 +105,29 @@ impl LayerSet { } } - /// A reference to the default layer. + /// Returns a reference to the default layer. pub fn default_layer(&self) -> &Layer { debug_assert!(self.layers[0].path() == Path::new(DEFAULT_GLYPHS_DIRNAME)); &self.layers[0] } - /// A mutable reference to the default layer. + /// Returns a mutable reference to the default layer. pub fn default_layer_mut(&mut self) -> &mut Layer { debug_assert!(self.layers[0].path() == Path::new(DEFAULT_GLYPHS_DIRNAME)); &mut self.layers[0] } - /// Iterate over all layers. + /// Returns an iterator over all layers. pub fn iter(&self) -> impl Iterator { self.layers.iter() } - /// Iterate over the names of all layers. + /// Returns an iterator over the names of all layers. pub fn names(&self) -> impl Iterator { self.layers.iter().map(|l| &l.name) } - /// Create a new layer with the given name. + /// Returns a new layer with the given name. pub fn new_layer(&mut self, name: &str) -> Result<(), Error> { if self.layers.iter().any(|l| &*l.name == name) { Err(Error::DuplicateLayer(name.into())) @@ -179,23 +179,25 @@ impl Default for LayerSet { } } -/// A [layer], corresponding to a 'glyphs' directory. +/// A [UFO layer], corresponding to a 'glyphs' sub-directory. /// /// Conceptually, a layer is just a collection of glyphs. /// -/// [layer]: http://unifiedfontobject.org/versions/ufo3/glyphs/ +/// [UFO layer]: http://unifiedfontobject.org/versions/ufo3/glyphs/ #[derive(Debug, Clone, PartialEq)] pub struct Layer { pub(crate) glyphs: BTreeMap>, pub(crate) name: LayerName, pub(crate) path: PathBuf, contents: BTreeMap, + /// Color field. pub color: Option, + /// lib field. pub lib: Plist, } impl Layer { - /// Create a new layer with the provided name and path. + /// Returns a new [`Layer`] with the provided `name` and `path`. /// /// The `path` argument, if provided, will be the directory within the UFO /// that the layer is saved. If it is not provided, it will be derived from @@ -216,12 +218,11 @@ impl Layer { } } - /// Load the layer at this path. + /// Returns a new [`Layer`] that is loaded from `path` with the provided `name`. /// /// Internal callers should use `load_impl` directly, so that glyph names /// can be reused between layers. /// - /// /// You generally shouldn't need this; instead prefer to load all layers /// with [`LayerSet::load`] and then get the layer you need from there. pub fn load(path: impl AsRef, name: LayerName) -> Result { @@ -230,7 +231,7 @@ impl Layer { Layer::load_impl(path, name, &names) } - /// the actual loading logic. + /// The actual loading logic. /// /// `names` is a map of glyphnames; we pass it throughout parsing /// so that we reuse the same Arc for identical names. @@ -328,7 +329,8 @@ impl Layer { crate::write::write_plist_value_to_file(&path.join(LAYER_INFO_FILE), &dict.into(), options) } - /// Attempt to write this layer to the given path. + /// Serialize this layer to the given path with the default + /// [`WriteOptions`] serialization format configuration. /// /// The path should not exist. pub fn save(&self, path: impl AsRef) -> Result<(), Error> { @@ -336,6 +338,10 @@ impl Layer { self.save_with_options(path.as_ref(), &options) } + /// Serialize this layer to the given `path` with a custom + /// [`WriteOptions`] serialization format configuration. + /// + /// The path should not exist. pub fn save_with_options(&self, path: &Path, opts: &WriteOptions) -> Result<(), Error> { fs::create_dir(&path)?; crate::write::write_xml_to_file(&path.join(CONTENTS_FILE), &self.contents, opts)?; @@ -353,7 +359,7 @@ impl Layer { }) } - /// The number of [`Glyph`]s in the layer. + /// Returns the number of [`Glyph`]s in the layer. pub fn len(&self) -> usize { self.glyphs.len() } @@ -363,14 +369,14 @@ impl Layer { self.glyphs.is_empty() } - /// The name of the layer. + /// Returns the name of the layer. /// /// This can only be mutated through the [`LayerSet`]. pub fn name(&self) -> &LayerName { &self.name } - /// The directory name of this layer. + /// Returns the directory path of this layer. /// /// This cannot be mutated; it is either provided when the layer /// is loaded, or we will create it for you. Maybe this is bad? We can talk @@ -379,7 +385,7 @@ impl Layer { &self.path } - /// Returns a reference the glyph with the given name, if it exists. + /// Returns a reference to the glyph with the given name, if it exists. pub fn get_glyph(&self, glyph: &K) -> Option<&Arc> where GlyphName: Borrow, @@ -397,7 +403,7 @@ impl Layer { self.glyphs.get_mut(glyph).map(|g| Arc::make_mut(g)) } - /// Returns `true` if this layer contains a glyph with this name. + /// Returns `true` if this layer contains a glyph with this `name`. pub fn contains_glyph(&self, name: &str) -> bool { self.glyphs.contains_key(name) } @@ -447,17 +453,17 @@ impl Layer { } } - /// Iterate over the glyphs in this layer. + /// Returns an iterator over the glyphs in this layer. pub fn iter(&self) -> impl Iterator> + '_ { self.glyphs.values() } - /// Iterate over the glyphs in this layer, mutably. + /// Returns an iterator over the glyphs in this layer, mutably. pub fn iter_mut(&mut self) -> impl Iterator { self.glyphs.values_mut().map(Arc::make_mut) } - /// Returns the path to the .glif file of a given glyph name. + /// Returns the path to the .glif file of a given glyph `name`. /// /// The returned path is relative to the path of the current layer. pub fn get_path(&self, name: &str) -> Option<&Path> { diff --git a/src/lib.rs b/src/lib.rs index 3a16bbcc..68426e37 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,18 +4,65 @@ //! //! [ufo]: http://unifiedfontobject.org/versions/ufo3 //! -//! # Basic usage: +//! # Basic Usage +//! +//! Instantiate a UFO font object with a [`Font`] struct like this: //! //! ```no_run //! use norad::Font; //! -//! let path = "RoflsExtraDim.ufo"; -//! let mut font_obj = Font::load(path).expect("failed to load font"); +//! let inpath = "RoflsExtraDim.ufo"; +//! let mut font_obj = Font::load(inpath).expect("failed to load font"); +//! # let layer = font_obj.default_layer(); +//! # let glyph_a = layer.get_glyph("A").expect("missing glyph"); +//! # assert_eq!(glyph_a.name.as_ref(), "A"); +//! # let outpath = "RoflsSemiDim.ufo"; +//! # font_obj.save(outpath); +//! ``` +//! +//! The API may be used to access and modify data in the [`Font`]: +//! +//!```no_run +//! # use norad::Font; +//! # let inpath = "RoflsExtraDim.ufo"; +//! # let mut font_obj = Font::load(inpath).expect("failed to load font"); //! let layer = font_obj.default_layer(); //! let glyph_a = layer.get_glyph("A").expect("missing glyph"); //! assert_eq!(glyph_a.name.as_ref(), "A"); +//! # let outpath = "RoflsSemiDim.ufo"; +//! # font_obj.save(outpath); +//! ``` +//! +//! Serialize the [`Font`] to UFO files on disk with the [`Font::save`] method: +//! +//!```no_run +//! # use norad::Font; +//! # let inpath = "RoflsExtraDim.ufo"; +//! # let mut font_obj = Font::load(inpath).expect("failed to load font"); +//! # let layer = font_obj.default_layer(); +//! # let glyph_a = layer.get_glyph("A").expect("missing glyph"); +//! # assert_eq!(glyph_a.name.as_ref(), "A"); +//! let outpath = "RoflsSemiDim.ufo"; +//! font_obj.save(outpath); //! ``` +//! +//! Refer to the [`examples` directory of the source repository](https://github.com/linebender/norad/tree/master/examples) +//! for additional source code examples. +//! +//! # API Documentation +//! +//! Details on the full API for working with UFO fonts are available in these docs. +//! +//! # License +//! +//! norad is licensed under the [MIT](https://github.com/linebender/norad/blob/master/LICENSE-MIT) +//! and [Apache v2.0](https://github.com/linebender/norad/blob/master/LICENSE-APACHE) licenses. +//! +//! # Source +//! +//! Source files are available at . +#![warn(missing_docs)] #![deny(rustdoc::broken_intra_doc_links, unsafe_code)] #[macro_use] diff --git a/src/shared_types.rs b/src/shared_types.rs index 65121f6f..ebe3cd13 100644 --- a/src/shared_types.rs +++ b/src/shared_types.rs @@ -17,13 +17,17 @@ pub static PUBLIC_OBJECT_LIBS_KEY: &str = "public.objectLibs"; /// A Plist dictionary. pub type Plist = plist::Dictionary; -/// A color. +/// A color in RGBA (Red-Green-Blue-Alpha) format. #[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "druid", derive(Data))] pub struct Color { + /// Red channel value. Must be in the range 0 to 1, inclusive. pub red: f64, + /// Green channel value. Must be in the range 0 to 1, inclusive. pub green: f64, + /// Blue channel value. Must be in the range 0 to 1, inclusive. pub blue: f64, + /// Alpha (transparency) channel value. Must be in the range 0 to 1, inclusive. pub alpha: f64, } @@ -76,23 +80,27 @@ pub type Bitlist = Vec; /// A number that may be either an integer or float. /// -/// It should serialize to an integer if it effectively represents one. +/// It serializes to an integer if it effectively represents one. #[derive(Debug, Clone, Copy, PartialEq)] pub struct IntegerOrFloat(f64); impl IntegerOrFloat { + /// Returns a new [`IntegerOrFloat`] with the given `value`. pub fn new(value: f64) -> Self { IntegerOrFloat(value) } + /// Returns the value. pub fn get(&self) -> f64 { self.0 } + /// Sets the value. pub fn set(&mut self, value: f64) { self.0 = value } + /// Returns `true` if the value is an integer. pub fn is_integer(&self) -> bool { (self.0 - self.round()).abs() < std::f64::EPSILON } @@ -143,11 +151,13 @@ impl<'de> Deserialize<'de> for IntegerOrFloat { /// A number that can be a non-negative integer or float. /// -/// It should serialize to an integer if it effectively represents one. +/// It serializes to an integer if it effectively represents one. #[derive(Debug, Clone, Copy, PartialEq)] pub struct NonNegativeIntegerOrFloat(f64); impl NonNegativeIntegerOrFloat { + /// Returns a new [`NonNegativeIntegerOrFloat`] with the given `value` or `None` + /// if the value is less than or equal to zero. pub fn new(value: f64) -> Option { if value.is_sign_positive() { Some(NonNegativeIntegerOrFloat(value)) @@ -156,10 +166,14 @@ impl NonNegativeIntegerOrFloat { } } + /// Returns the value. pub fn get(&self) -> f64 { self.0 } + /// Sets the value. + /// + /// An error is raised if `value` is less than or equal to zero. pub fn try_set(&mut self, value: f64) -> Result<(), Error> { if value.is_sign_positive() { self.0 = value; @@ -169,6 +183,7 @@ impl NonNegativeIntegerOrFloat { } } + /// Returns `true` if the value is an integer. pub fn is_integer(&self) -> bool { (self.0 - self.round()).abs() < std::f64::EPSILON } diff --git a/src/util.rs b/src/util.rs index a2003bcc..80c125e1 100644 --- a/src/util.rs +++ b/src/util.rs @@ -14,13 +14,13 @@ pub fn recursive_sort_plist_keys(plist: &mut plist::Dictionary) { //NOTE: this is hacky, and intended mostly as a placeholder. It was adapted from // https://github.com/unified-font-object/ufoLib/blob/master/Lib/ufoLib/filenames.py -/// given a glyph name, compute an appropriate file name. +/// Given a glyph `name`, return an appropriate file name. pub fn default_file_name_for_glyph_name(name: impl AsRef) -> String { let name = name.as_ref(); user_name_to_file_name(name, "", ".glif") } -/// given a layer name, compute an appropriate file name. +/// Given a layer `name`, return an appropriate file name. pub fn default_file_name_for_layer_name(name: &str) -> String { user_name_to_file_name(name, "glyphs.", "") } diff --git a/src/write.rs b/src/write.rs index 4d199ad5..c97f6bd8 100644 --- a/src/write.rs +++ b/src/write.rs @@ -64,8 +64,8 @@ impl WriteOptions { /// not contain multiple different characters. As an example, "\t\t" is /// fine, but "\t \t" is not, because it contains both tabs and spaces. /// - /// This is not good API, but is a work around for the fact that the quick-xml - /// and plist crates both represent whitespace in different ways. + // This is not good API, but is a work around for the fact that the quick-xml + // and plist crates both represent whitespace in different ways. /// /// # Panics /// @@ -109,7 +109,7 @@ pub enum QuoteChar { Double, } -/// Write a `plist::Value` to file, providing custom options. +/// Write a [`plist::Value`] to file, providing custom options. pub fn write_plist_value_to_file( path: &Path, value: &plist::Value, @@ -137,6 +137,7 @@ pub fn write_xml_to_file( Ok(()) } +/// Write XML declarations with custom quote formatting options. pub fn write_quote_style(file: &File, options: &WriteOptions) -> Result<(), Error> { // Optionally modify the XML declaration quote style match options.quote_style {