diff --git a/src/Common/Font.cs b/src/Common/Font.cs
index 86e7776..3c576f5 100644
--- a/src/Common/Font.cs
+++ b/src/Common/Font.cs
@@ -1,6 +1,8 @@
using System;
+using System.ComponentModel;
using System.Collections.Concurrent;
using System.Collections.Generic;
+using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
@@ -8,14 +10,29 @@
namespace GeneXus.Drawing;
+[TypeConverter(typeof(FontConverter))]
[Serializable]
public class Font : IDisposable, ICloneable
{
internal readonly FontFamily m_family;
internal readonly float m_size;
+ internal readonly string m_original;
- internal readonly string m_original = null;
-
+ ///
+ /// Initializes a new System.Drawing.Font that uses the specified existing
+ /// and enumeration.
+ ///
+ /// The existing from which to create the new
+ ///
+ /// The to apply to the new S. Multiple
+ /// values of the enumeration can be combined with the OR operator.
+ ///
+ public Font(Font prototype, FontStyle newStyle)
+ {
+ m_family = prototype.FontFamily;
+ // TODO set newStyle
+ }
+
///
/// Initializes a new using the specified and size.
///
@@ -29,12 +46,20 @@ public Font(FontFamily family, float size = 12)
/// Initializes a new using the specified family name and size.
///
public Font(string familyName, float size = 12)
- : this(SystemFonts.Select(f => f.m_family).FirstOrDefault(f => f.MatchFamily(familyName))
- ?? throw new ArgumentException("missing font family", nameof(familyName)), size)
+ : this(SystemFonts.Select(f => f.m_family).FirstOrDefault(ff => ff is SkiaFontFamily sff && sff.MatchFamily(familyName)) ?? new UnknownFontFamily(familyName), size)
{
m_original = familyName;
}
+ ///
+ /// Initializes a new using the specified family name, size, styl.
+ ///
+ public Font(string familyName, float size, FontStyle style, GraphicsUnit unit)
+ : this(familyName, size)
+ {
+ // TODO set style and unit
+ }
+
///
/// Cleans up resources for this .
///
@@ -45,7 +70,7 @@ public Font(string familyName, float size = 12)
///
public override string ToString()
{
- string suffix = m_family.m_index > 0 ? $"#{m_family.m_index}" : string.Empty;
+ string suffix = m_family is SkiaFontFamily { m_index: > 0 } sff ? $"#{sff.m_index}" : string.Empty;
return $"[{GetType().Name}: Name={Name}{suffix}, Size={Size}]";
}
@@ -69,16 +94,9 @@ public void Dispose()
#region IClonable
///
- /// Creates an exact copy of this .
+ /// Creates an exact copy of this .
///
- public object Clone()
- {
- var index = m_family.m_index;
- var bytes = m_family.m_data.ToArray();
- var data = SKData.CreateCopy(bytes);
- var family = new FontFamily(data, index);
- return new Font(family);
- }
+ public object Clone() => new Font((FontFamily)m_family.Clone());
#endregion
@@ -88,19 +106,17 @@ public object Clone()
///
/// Creates a with the coordinates of the specified .
///
- public static explicit operator SKFont(Font font) => font.m_family.GetFont(font.m_size);
+ public static explicit operator SKFont(Font font) => (font.m_family as SkiaFontFamily)?.GetFont(font.m_size);
#endregion
#region Properties
- private SKFont m_font => m_family.GetFont(m_size);
-
///
/// Gets the face name of this .
///
- public string Name => $"{m_family.Name} {m_family.Face}";
+ public string Name => m_family is SkiaFontFamily sff ? $"{sff.Name} {sff.Face}" : m_family.Name;
///
/// Gets the name of the originally specified.
@@ -176,22 +192,24 @@ public FontStyle Style
///
/// Gets a value that indicates whether this has the italic style applied.
///
- public bool Italic => m_family.m_typeface.IsItalic;
+ public bool Italic => m_family.IsItalic;
///
/// Gets a value that indicates whether this is bold.
///
- public bool Bold => m_family.m_typeface.IsBold;
+ public bool Bold => m_family.IsBold;
+ private SKFontMetrics Metrics => m_family is SkiaFontFamily sff ? sff.GetFont(m_size).Metrics : new SKFontMetrics();
+
///
/// Gets a value indicating whether this is underlined.
///
- public bool Underline => m_font.Metrics.UnderlineThickness > 0 && m_font.Metrics.UnderlinePosition == 0f;
+ public bool Underline => Metrics is { UnderlineThickness: > 0, UnderlinePosition: 0f };
///
/// Gets a value indicating whether this is strikeout (has a line through it).
///
- public bool Strikeout => m_font.Metrics.StrikeoutThickness > 0 && m_font.Metrics.StrikeoutPosition == 0f;
+ public bool Strikeout => Metrics is { StrikeoutThickness: > 0, StrikeoutPosition: 0f };
///
/// Gets a value indicating whether the is a member of SystemFonts.
@@ -217,8 +235,29 @@ public static ICollection SystemFonts
}
}
- private static ICollection s_SystemFonts = null;
+ private static ICollection s_SystemFonts;
+ ///
+ /// Gets the em-size, in points, of this .
+ ///
+ /// The em-size, in points, of this
+ [Browsable(false)]
+ public float SizeInPoints
+ {
+ get
+ {
+ // TODO calculate it for other units
+ Debug.Assert(Unit == GraphicsUnit.Point);
+ return Size;
+ }
+ }
+
+ ///
+ /// Gets the unit of measure for this .
+ ///
+ /// A that represents the unit of measure for this .
+ public GraphicsUnit Unit => GraphicsUnit.Point;
+
#endregion
@@ -227,7 +266,7 @@ public static ICollection SystemFonts
///
/// Returns the line spacing of this .
///
- public float GetHeight() => m_font.Metrics.Descent - m_font.Metrics.Ascent + m_font.Metrics.Leading;
+ public float GetHeight() => Metrics.Descent - Metrics.Ascent + Metrics.Leading;
///
/// Returns a collection in the specified location.
@@ -240,7 +279,7 @@ public static ICollection GetFonts(string location)
{
if (FONT_EXTENSIONS.Contains(Path.GetExtension(fontFile)))
{
- var family = new FontFamily(fontFile);
+ var family = FontFamilyFactory.Create(fontFile);
var font = new Font(family);
fonts.Add(font);
}
diff --git a/src/Common/FontConverter.cs b/src/Common/FontConverter.cs
new file mode 100644
index 0000000..f487661
--- /dev/null
+++ b/src/Common/FontConverter.cs
@@ -0,0 +1,527 @@
+using System;
+using System.Collections;
+using System.ComponentModel;
+using System.ComponentModel.Design.Serialization;
+using System.Globalization;
+using System.Linq;
+using System.Reflection;
+
+namespace GeneXus.Drawing;
+
+///
+/// Converts System.Drawing.Font objects from one data type to another.
+///
+public class FontConverter : TypeConverter
+{
+ internal class UnitName
+ {
+ internal readonly string m_name;
+ internal readonly GraphicsUnit m_unit;
+
+ internal static readonly UnitName[] Names = {
+ new("world", GraphicsUnit.World),
+ new("display", GraphicsUnit.Display),
+ new("px", GraphicsUnit.Pixel),
+ new("pt", GraphicsUnit.Point),
+ new("in", GraphicsUnit.Inch),
+ new("doc", GraphicsUnit.Document),
+ new("mm", GraphicsUnit.Millimeter)
+ };
+
+ private UnitName(string name, GraphicsUnit unit)
+ {
+ m_name = name;
+ m_unit = unit;
+ }
+ }
+
+ ///
+ /// is a type converter that is used
+ /// to convert a font name to and from various other representations.
+ ///
+ private sealed class FontNameConverter : TypeConverter
+ {
+ private StandardValuesCollection _values;
+
+ ///
+ /// Determines if this converter can convert an object in the given source type to
+ /// the native type of the converter.
+ ///
+ ///
+ /// An that can be used to extract additional
+ /// information about the environment this converter is being invoked from. This
+ /// may be null, so you should always check. Also, properties on the context object
+ /// may return null.
+ ///
+ /// The type you wish to convert from.
+ /// true if the converter can perform the conversion; otherwise, false.
+ public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
+ {
+ return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
+ }
+
+ ///
+ /// Converts the given object to the converter's native type.
+ ///
+ ///
+ /// An that can be used to extract additional
+ /// information about the environment this converter is being invoked from. This
+ /// may be null, so you should always check. Also, properties on the context object
+ /// may return null.
+ ///
+ /// A to use to perform the conversion
+ /// The object to convert.
+ /// The converted object.
+ /// The conversion cannot be completed.
+ public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
+ {
+ if (value is string s)
+ return MatchFontName(s, context);
+
+ return base.ConvertFrom(context, culture, value);
+ }
+
+ ///
+ /// Retrieves a collection containing a set of standard values for the data type
+ /// this converter is designed for.
+ ///
+ ///
+ /// An that can be used to extract additional
+ /// information about the environment this converter is being invoked from. This
+ /// may be null, so you should always check. Also, properties on the context object
+ /// may return null.
+ ///
+ /// A collection containing a standard set of valid values, or null. The default is null.
+ public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
+ {
+ if (_values != null)
+ return _values;
+
+ FontFamily[] families = FontFamily.Families;
+ Hashtable hashtable = new();
+ foreach (FontFamily fontFamily in families)
+ {
+ string name = fontFamily.Name;
+ hashtable[name.ToLower(CultureInfo.InvariantCulture)] = name;
+ }
+
+ object[] array = new object[hashtable.Values.Count];
+ hashtable.Values.CopyTo(array, 0);
+ Array.Sort(array, Comparer.Default);
+ _values = new StandardValuesCollection(array);
+
+ return _values;
+ }
+
+ ///
+ /// Determines if the list of standard values returned from the Overload:System.Drawing.FontConverter.FontNameConverter.GetStandardValues
+ /// method is an exclusive list.
+ ///
+ ///
+ /// An that can be used to extract additional
+ /// information about the environment this converter is being invoked from. This
+ /// may be null, so you should always check. Also, properties on the context object
+ /// may return null.
+ ///
+ /// true if the collection returned from
+ /// is an exclusive list of possible values; otherwise, false. The default is false.
+ public override bool GetStandardValuesExclusive(ITypeDescriptorContext context)
+ {
+ return false;
+ }
+
+ ///
+ /// Determines if this object supports a standard set of values that can be picked from a list.
+ ///
+ ///
+ /// An that can be used to extract additional
+ /// information about the environment this converter is being invoked from. This
+ /// may be null, so you should always check. Also, properties on the context object
+ /// may return null.
+ ///
+ ///
+ /// true if Overload:System.Drawing.FontConverter.FontNameConverter.GetStandardValues
+ /// should be called to find a common set of values the object supports; otherwise,
+ /// false.
+ ///
+ public override bool GetStandardValuesSupported(ITypeDescriptorContext context)
+ {
+ return true;
+ }
+
+ private string MatchFontName(string name, ITypeDescriptorContext context)
+ {
+ string candidate = null;
+ string lowerName = name.ToLower(CultureInfo.InvariantCulture);
+ foreach (string fontName in GetStandardValues(context))
+ {
+ string lowerFontName = fontName.ToLower(CultureInfo.InvariantCulture);
+ if (lowerFontName.Equals(lowerName))
+ return fontName;
+
+ if (lowerFontName.StartsWith(name) && (candidate == null || lowerFontName.Length <= candidate.Length))
+ candidate = fontName;
+ }
+
+ return candidate ?? name;
+ }
+
+ private void OnInstalledFontsChanged(object sender, EventArgs e)
+ {
+ _values = null;
+ }
+ }
+
+ ///
+ /// Converts font units to and from other unit types.
+ ///
+ private class FontUnitConverter : EnumConverter
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public FontUnitConverter()
+ : base(typeof(GraphicsUnit))
+ {
+ }
+
+ ///
+ /// Returns a collection of standard values valid for the type.
+ ///
+ /// An that provides a format context.
+ /// A collection containing a standard set of valid values, or null. The default is null.
+ public override StandardValuesCollection GetStandardValues(ITypeDescriptorContext context)
+ {
+ if (base.Values == null)
+ {
+ base.GetStandardValues(context);
+ ArrayList arrayList = new(base.Values);
+ arrayList.Remove(GraphicsUnit.Display);
+ base.Values = new StandardValuesCollection(arrayList);
+ }
+
+ return base.Values;
+ }
+ }
+
+ private FontNameConverter _fontNameConverter;
+
+ ///
+ /// Determines whether this converter can convert an object in the specified source
+ /// type to the native type of the converter.
+ ///
+ ///
+ /// A formatter context. This object can be used to get additional information about
+ /// the environment this converter is being called from. This may be null, so you
+ /// should always check. Also, properties on the context object may also return null.
+ ///
+ /// The type you want to convert from
+ /// This method returns true if this object can perform the conversion.
+ public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
+ {
+ return sourceType == typeof(string) || base.CanConvertFrom(context, sourceType);
+ }
+
+ ///
+ /// Gets a value indicating whether this converter can convert an object to the given
+ /// destination type using the context.
+ ///
+ /// An object that provides a format context.
+ /// A object that represents the type you want to convert to.
+ /// This method returns true if this converter can perform the conversion; otherwise, false.
+ public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
+ {
+ return destinationType == typeof(string) || base.CanConvertTo(context, destinationType);
+ }
+
+ ///
+ /// Converts the specified object to the native type of the converter.
+ ///
+ ///
+ /// A formatter context. This object can be used to get additional information about
+ /// the environment this converter is being called from. This may be null, so you
+ /// should always check. Also, properties on the context object may also return null.
+ ///
+ /// A object that specifies the culture used to represent the font.
+ /// The object to convert.
+ /// The converted object.
+ /// The conversion could not be performed.
+ public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
+ {
+ if (value is not string text)
+ return base.ConvertFrom(context, culture, value);
+
+ string trimmedText = text.Trim();
+ if (trimmedText.Length == 0)
+ return null;
+
+ culture ??= CultureInfo.CurrentCulture;
+
+ char separator = culture.TextInfo.ListSeparator[0];
+ string familyName = trimmedText;
+ float emSize = 8.25f;
+ FontStyle fontStyle = FontStyle.Regular;
+ GraphicsUnit unit = GraphicsUnit.Point;
+
+ int separatorIndex = trimmedText.IndexOf(separator);
+ if (separatorIndex > 0)
+ {
+ familyName = trimmedText.Substring(0, separatorIndex);
+ if (separatorIndex < trimmedText.Length - 1)
+ {
+ string sizeAndUnit;
+ string style = null;
+
+ int styleIndex = trimmedText.IndexOf("style=");
+ if (styleIndex != -1)
+ {
+ style = trimmedText.Substring(styleIndex, trimmedText.Length - styleIndex);
+ if (!style.StartsWith("style="))
+ throw GetFormatException(trimmedText, separator);
+
+ sizeAndUnit = trimmedText.Substring(separatorIndex + 1, styleIndex - separatorIndex - 1);
+ }
+ else
+ {
+ sizeAndUnit = trimmedText.Substring(separatorIndex + 1, trimmedText.Length - separatorIndex - 1);
+ }
+
+ ParseSizeTokens(sizeAndUnit, separator, out string sizeText, out string unitText);
+ if (sizeText != null)
+ {
+ try
+ {
+ emSize = (float)TypeDescriptor.GetConverter(typeof(float)).ConvertFromString(context, culture, sizeText);
+ }
+ catch
+ {
+ throw GetFormatException(trimmedText, separator);
+ }
+ }
+
+ if (unitText != null)
+ {
+ unit = ParseGraphicsUnits(unitText);
+ }
+
+ if (style != null)
+ {
+ int indexEqual = style.IndexOf("=");
+ style = style.Substring(indexEqual + 1, style.Length - "style=".Length);
+ foreach (string stylePart in style.Split(separator))
+ {
+ try
+ {
+ fontStyle |= (FontStyle)Enum.Parse(typeof(FontStyle), stylePart.Trim(), ignoreCase: true);
+ }
+ catch (Exception ex)
+ {
+ if (ex is InvalidEnumArgumentException)
+ throw;
+
+ throw GetFormatException(trimmedText, separator);
+ }
+
+ const FontStyle validFontStyle = FontStyle.Bold | FontStyle.Italic | FontStyle.Underline | FontStyle.Strikeout;
+ if ((fontStyle | validFontStyle) != validFontStyle)
+ {
+ throw new InvalidEnumArgumentException("style", (int)fontStyle, typeof(FontStyle));
+ }
+ }
+ }
+ }
+ }
+
+ _fontNameConverter ??= new FontNameConverter();
+ familyName = (string)_fontNameConverter.ConvertFrom(context, culture, familyName);
+ return new Font(familyName, emSize, fontStyle, unit);
+ }
+
+ ///
+ /// Converts the specified object to another type.
+ ///
+ ///
+ /// A formatter context. This object can be used to get additional information about
+ /// the environment this converter is being called from. This may be null, so you
+ /// should always check. Also, properties on the context object may also return null.
+ ///
+ ///
+ /// A object that specifies the culture used to represent the object.
+ ///
+ /// The object to convert.
+ /// The data type to convert the object to.
+ /// The converted object.
+ /// The conversion was not successful.
+ public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
+ {
+ if (destinationType == null)
+ throw new ArgumentNullException(nameof(destinationType));
+
+ if (destinationType == typeof(string))
+ {
+ if (value is not Font font)
+ return string.Empty;
+
+ culture ??= CultureInfo.CurrentCulture;
+
+ string separator = culture.TextInfo.ListSeparator + " ";
+ int parts = 2;
+ if (font.Style != 0)
+ parts++;
+
+ string[] array = new string[parts];
+ array[0] = font.Name;
+ array[1] = TypeDescriptor.GetConverter(font.Size).ConvertToString(context, culture, font.Size) + GetGraphicsUnitText(font.Unit);
+ if (font.Style != 0)
+ array[2] = "style=" + font.Style.ToString("G");
+
+ return string.Join(separator, array);
+ }
+
+ return base.ConvertTo(context, culture, value, destinationType);
+ }
+
+ ///
+ /// Creates an object of this type by using a specified set of property values for the object.
+ ///
+ /// A type descriptor through which additional context can be provided.
+ ///
+ /// A dictionary of new property values. The dictionary contains a series of name-value
+ /// pairs, one for each property returned from the
+ /// method.
+ ///
+ ///
+ /// The newly created object, or null if the object could not be created. The default
+ /// implementation returns null. Useful for creating non-changeable objects that have changeable properties.
+ ///
+ public override object CreateInstance(ITypeDescriptorContext context, IDictionary propertyValues)
+ {
+ if (propertyValues == null)
+ throw new ArgumentNullException(nameof(propertyValues));
+
+ object familyObj = propertyValues["Name"] ?? "Tahoma";
+ object sizeObj = propertyValues["Size"] ?? 8f;
+ object unitObj = propertyValues["Unit"] ?? GraphicsUnit.Point;
+ object isBoldObj = propertyValues["Bold"] ?? false;
+ object isItalicObj = propertyValues["Italic"] ?? false;
+ object isStrikeoutObj = propertyValues["Strikeout"] ?? false;
+ object isUnderlineObj = propertyValues["Underline"] ?? false;
+ if (familyObj is not string family
+ || sizeObj is not float size
+ || unitObj is not GraphicsUnit unit
+ || isBoldObj is not bool isBold
+ || isItalicObj is not bool isItalic
+ || isStrikeoutObj is not bool isStrikeout
+ || isUnderlineObj is not bool isUnderline)
+ {
+ throw new ArgumentException("Property value with incorrect type");
+ }
+
+ FontStyle style = FontStyle.Regular;
+ if (isBold)
+ style |= FontStyle.Bold;
+
+ if (isItalic)
+ style |= FontStyle.Italic;
+
+ if (isStrikeout)
+ style |= FontStyle.Strikeout;
+
+ if (isUnderline)
+ style |= FontStyle.Underline;
+
+ return new Font(family, size, style, unit);
+ }
+
+ ///
+ /// Determines whether changing a value on this object should require a call to the
+ /// method to create a new value.
+ ///
+ /// A type descriptor through which additional context can be provided.
+ ///
+ /// This method returns true if the CreateInstance object should be called when a
+ /// change is made to one or more properties of this object; otherwise, false.
+ ///
+ public override bool GetCreateInstanceSupported(ITypeDescriptorContext context)
+ {
+ return true;
+ }
+
+ private static ArgumentException GetFormatException(string text, char separator)
+ {
+ string validFormat = string.Format(CultureInfo.CurrentCulture, "name{0} size[units[{0} style=style1[{0} style2{0} ...]]]", separator);
+ return new ArgumentException($"Failed to parse font text, input: {text}, valid format: {validFormat}");
+ }
+
+ private static string GetGraphicsUnitText(GraphicsUnit units)
+ {
+ return UnitName.Names.FirstOrDefault(n => n.m_unit == units)?.m_name ?? "";
+ }
+
+ ///
+ /// Retrieves the set of properties for this type. By default, a type does not have
+ /// any properties to return.
+ ///
+ /// A type descriptor through which additional context can be provided.
+ /// The value of the object to get the properties for.
+ /// An array of objects that describe the properties.
+ ///
+ /// The set of properties that should be exposed for this data type. If no properties
+ /// should be exposed, this may return null. The default implementation always returns
+ /// null. An easy implementation of this method can call the
+ /// method for the correct data type.
+ ///
+ public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
+ {
+ PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(Font), attributes);
+ return properties.Sort(new[] { "Name", "Size", "Unit", "Weight" });
+ }
+
+ ///
+ /// Determines whether this object supports properties. The default is false.
+ ///
+ /// A type descriptor through which additional context can be provided.
+ ///
+ /// This method returns true if the System.Drawing.FontConverter.GetPropertiesSupported(System.ComponentModel.ITypeDescriptorContext)
+ /// method should be called to find the properties of this object; otherwise, false.
+ ///
+ public override bool GetPropertiesSupported(ITypeDescriptorContext context)
+ {
+ return true;
+ }
+
+ private static void ParseSizeTokens(string text, char separator, out string size, out string unit)
+ {
+ size = null;
+ unit = null;
+ text = text.Trim();
+ int length = text.Length;
+ if (length <= 0)
+ return;
+
+ int index = 0;
+ while (index < length && !char.IsLetter(text[index]))
+ index++;
+
+ char[] trimChars = { separator, ' ' };
+ if (index > 0)
+ {
+ size = text.Substring(0, index);
+ size = size.Trim(trimChars);
+ }
+
+ if (index < length)
+ {
+ unit = text.Substring(index);
+ unit = unit.TrimEnd(trimChars);
+ }
+ }
+
+ private static GraphicsUnit ParseGraphicsUnits(string units)
+ {
+ UnitName unitName = UnitName.Names.FirstOrDefault(un => string.Equals(un.m_name, units, StringComparison.OrdinalIgnoreCase));
+ if (unitName == null)
+ throw new ArgumentException(nameof(units));
+ return unitName.m_unit;
+ }
+}
diff --git a/src/Common/FontFamily.cs b/src/Common/FontFamily.cs
index 4d18dbf..47e7710 100644
--- a/src/Common/FontFamily.cs
+++ b/src/Common/FontFamily.cs
@@ -7,59 +7,179 @@
namespace GeneXus.Drawing;
-public class FontFamily : IDisposable
+public abstract class FontFamily : IDisposable, ICloneable
{
- internal readonly int m_index;
- internal readonly SKData m_data;
- internal readonly SKTypeface m_typeface;
+ ///
+ /// Creates a human-readable string that represents this .
+ ///
+ public override string ToString() => $"[{GetType().Name}: Name={Name}]";
+
+ public virtual void Dispose() { }
+
+ #region Properties
- internal FontFamily(SKData skData, int index)
- {
- m_index = index;
- m_data = skData;
- m_typeface = SKTypeface.FromData(m_data, m_index);
- if (m_typeface == null) throw new ArgumentException("file does not exist or is an invalid font file");
- }
+ ///
+ /// Gets the name of this .
+ ///
+ public abstract string Name { get; }
///
- /// Initializes a new instance of the class from the specified filename name
- /// and optional index for font collection.
+ /// Gets the Face associated with this .
///
- public FontFamily(string name, int index = 0)
- : this(File.Exists(name) ? SKData.Create(name) : new Font(name).FontFamily.m_data, index) { }
+ public abstract string Face { get; }
///
- /// Initializes a new instance of the class from the specified stream name
- /// and optional index for font collection.
+ /// Gets a value that indicates whether this has the italic style applied.
///
- public FontFamily(Stream stream, int index = 0)
- : this(SKData.Create(stream), index) { }
+ public abstract bool IsItalic { get; }
///
- /// Initializes a new instance of the class in the specified
- /// and with the specified name.
+ /// Gets a value that indicates whether this is bold.
///
- public FontFamily(string name, FontCollection fontCollection)
- : this(fontCollection.Families.FirstOrDefault(ff => ff.MatchFamily(name))?.m_data
- ?? throw new ArgumentException($"missing family from collection", nameof(name)), 0)
- { }
+ public abstract bool IsBold { get; }
+
+ ///
+ /// Gets the weight of this .
+ ///
+ public abstract int Weight { get; }
+
+ ///
+ /// Gets the width of this .
+ ///
+ public abstract int Width { get; }
+
+ ///
+ /// Gets the slant of this .
+ ///
+ public abstract SlantType Slant { get; }
+
+ #endregion
+
+ #region Methods
///
- /// Initializes a new instance of the class from the specified generic font family.
+ /// Returns the cell ascent of this .
///
- public FontFamily(GenericFontFamilies genericFamily)
- : this(GetGenericFontFamily(genericFamily).m_data, 0) { }
+ public abstract int GetCellAscent();
///
- /// Cleans up resources for this .
+ /// Returns the cell descent of this .
///
- ~FontFamily() => Dispose(false);
+ public abstract int GetCellDescent();
///
- /// Creates a human-readable string that represents this .
+ /// Gets the height, of the em square of this .
+ ///
+ public abstract int GetEmHeight();
+
+ ///
+ /// Returns the distance between two consecutive lines of text for this
+ /// with the specified .
+ ///
+ public abstract int GetLineSpacing();
+
+ ///
+ /// Returns the name of this in the specified language.
+ ///
+ public abstract string GetName(int language);
+
+ ///
+ /// Indicates whether the specified is available.
+ ///
+ public abstract bool IsStyleAvailable(FontStyle style);
+
+ ///
+ /// Creates an exact copy of this .
+ ///
+ public abstract object Clone();
+
+ #endregion
+
+ #region Generic Font Families
+
+ ///
+ /// Returns an array that contains all of the objects associated with the current
+ /// graphics context.
+ ///
+ public static FontFamily[] Families => new InstalledFontCollection().Families;
+
+ ///
+ /// Gets a generic monospace .
+ ///
+ public static FontFamily GenericMonospace => new SkiaFontFamily(GenericFontFamilies.Monospace);
+
+ ///
+ /// Gets a generic SansSerif .
+ ///
+ public static FontFamily GenericSansSerif => new SkiaFontFamily(GenericFontFamilies.SansSerif);
+
+ ///
+ /// Gets a generic Serif .
+ ///
+ public static FontFamily GenericSerif => new SkiaFontFamily(GenericFontFamilies.Serif);
+
+ #endregion
+}
+
+public static class FontFamilyFactory
+{
+ ///
+ /// Initializes a new instance of the class from the specified filename name
+ /// and optional index for font collection.
+ ///
+ public static FontFamily Create(string fontPath, int index = 0) => new SkiaFontFamily(fontPath, index);
+
+ ///
+ /// Initializes a new instance of the class from the specified stream name
+ /// and optional index for font collection.
+ ///
+ public static FontFamily Create(Stream stream, int index = 0) => new SkiaFontFamily(stream, index);
+
+ ///
+ /// Initializes a new instance of the class in the specified
+ /// and with the specified name.
+ ///
+ public static FontFamily Create(string name, FontCollection fontCollection) => new SkiaFontFamily(name, fontCollection);
+
+ ///
+ /// Initializes a new instance of the class from the specified generic font family.
///
- public override string ToString() => $"[{GetType().Name}: Name={m_typeface.FamilyName}]";
+ public static FontFamily Create(GenericFontFamilies genericFamily) => new SkiaFontFamily(genericFamily);
+}
+internal sealed class SkiaFontFamily : FontFamily
+{
+ internal readonly int m_index;
+ private readonly SKData m_data;
+ private readonly SKTypeface m_typeface;
+
+ private SkiaFontFamily(SKData skData, int index)
+ {
+ m_index = index;
+ m_data = skData;
+ m_typeface = SKTypeface.FromData(m_data, m_index);
+ if (m_typeface == null) throw new ArgumentException("file does not exist or is an invalid font file");
+ }
+
+ public SkiaFontFamily(string fontPath, int index = 0)
+ : this(File.Exists(fontPath) ? SKData.Create(fontPath) : throw new FileNotFoundException(fontPath), index) { }
+
+ public SkiaFontFamily(Stream stream, int index = 0)
+ : this(SKData.Create(stream), index) { }
+
+ public SkiaFontFamily(string name, FontCollection fontCollection)
+ : this((fontCollection.Families.FirstOrDefault(ff => ff is SkiaFontFamily sff && sff.MatchFamily(name)) as SkiaFontFamily)?.m_data
+ ?? throw new ArgumentException($"missing family from collection", nameof(name)), 0)
+ { }
+
+ public SkiaFontFamily(GenericFontFamilies genericFamily)
+ : this(GetGenericFontFamily(genericFamily).m_data, 0) { }
+
+ ///
+ /// Cleans up resources for this .
+ ///
+ ~SkiaFontFamily() => Dispose(false);
+
#region IEqualitable
@@ -67,12 +187,12 @@ public FontFamily(GenericFontFamilies genericFamily)
/// Indicates whether the specified object is a and is identical to this .
///
public override bool Equals(object obj)
- => obj is FontFamily ff
+ => obj is SkiaFontFamily ff
&& ff.m_typeface.FamilyName.Equals(m_typeface.FamilyName, StringComparison.OrdinalIgnoreCase)
&& ff.m_index == m_index;
///
- /// Gets a hash code for this .
+ /// Gets a hash code for this .
///
public override int GetHashCode() => Name.GetHashCode();
@@ -82,15 +202,15 @@ public override bool Equals(object obj)
#region IDisposable
///
- /// Cleans up resources for this .
+ /// Cleans up resources for this .
///
- public void Dispose()
+ public override void Dispose()
{
GC.SuppressFinalize(this);
Dispose(true);
}
- protected virtual void Dispose(bool disposing)
+ private void Dispose(bool disposing)
{
m_typeface.Dispose();
m_data.Dispose();
@@ -102,9 +222,9 @@ protected virtual void Dispose(bool disposing)
#region Operators
///
- /// Creates a with the coordinates of the specified .
+ /// Creates a with the coordinates of the specified .
///
- public static explicit operator SKTypeface(FontFamily fontFamily) => fontFamily.m_typeface;
+ public static explicit operator SKTypeface(SkiaFontFamily fontFamily) => fontFamily.m_typeface;
#endregion
@@ -114,91 +234,51 @@ protected virtual void Dispose(bool disposing)
///
/// Gets the name of this .
///
- public string Name => m_typeface.FamilyName;
-
- ///
- /// Returns an array that contains all of the objects associated with the current
- /// graphics context.
- ///
- public static FontFamily[] Families => new InstalledFontCollection().Families;
-
- ///
- /// Gets a generic monospace .
- ///
- public static FontFamily GenericMonospace => new(GenericFontFamilies.Monospace);
-
- ///
- /// Gets a generic SansSerif .
- ///
- public static FontFamily GenericSansSerif => new(GenericFontFamilies.SansSerif);
-
- ///
- /// Gets a generic Serif .
- ///
- public static FontFamily GenericSerif => new(GenericFontFamilies.Serif);
+ public override string Name => m_typeface.FamilyName;
#endregion
#region Methods
- internal SKFont m_font => GetFont(10);
+ private SKFont m_font => GetFont(10);
internal SKFont GetFont(float size) => m_typeface.ToFont(size);
- ///
- /// Returns the cell ascent of this .
- ///
- public int GetCellAscent() => (int)Math.Abs(m_font.Metrics.Ascent * GetEmHeight() / m_font.Size);
-
- ///
- /// Returns the cell descent of this .
- ///
- public int GetCellDescent() => (int)Math.Abs(m_font.Metrics.Descent * GetEmHeight() / m_font.Size);
-
- ///
- /// Gets the height, of the em square of this .
- ///
- public int GetEmHeight() => m_typeface.UnitsPerEm;
-
- /// Returns the distance between two consecutive lines of text for this with the
- /// specified .
- ///
- public int GetLineSpacing() => (int)Math.Abs(m_font.Spacing * GetEmHeight() / m_font.Size);
-
- ///
- /// Returns the name of this in the specified language.
- ///
- public string GetName(int language) => Name; // NOTE: Language is not suppored in SkiaSharp
-
- ///
- /// Indicates whether the specified is available.
- ///
- public bool IsStyleAvailable(FontStyle style) => (new Font(this).Style & style) == style;
+ public override int GetCellAscent() => (int)Math.Abs(m_font.Metrics.Ascent * GetEmHeight() / m_font.Size);
+ public override int GetCellDescent() => (int)Math.Abs(m_font.Metrics.Descent * GetEmHeight() / m_font.Size);
+ public override int GetEmHeight() => m_typeface.UnitsPerEm;
+ public override int GetLineSpacing() => (int)Math.Abs(m_font.Spacing * GetEmHeight() / m_font.Size);
+ public override string GetName(int language) => Name; // NOTE: Language is not supported in SkiaSharp
+ public override bool IsStyleAvailable(FontStyle style) => (new Font(this).Style & style) == style;
+ public override object Clone() => new SkiaFontFamily(SKData.CreateCopy(m_data.ToArray()), m_index);
#endregion
#region Utilities
- internal int Weight => m_typeface.FontWeight;
+ public override bool IsItalic => m_typeface.IsItalic;
+ public override bool IsBold => m_typeface.IsBold;
+
+ public override int Weight => m_typeface.FontWeight;
private string WeightName => Weight switch
{
- int n when n < 100 => "Extra Thin",
- int n when n < 275 => "Thin",
- int n when n < 300 => "Extra Light",
- int n when n < 350 => "Light",
- int n when n < 400 => "Semi Light",
- int n when n < 500 => "Normal",
- int n when n < 600 => "Medium",
- int n when n < 700 => "Semi Bold",
- int n when n < 800 => "Bold",
- int n when n < 900 => "Extra Bold",
- int n when n < 950 => "Black",
- int n when n < 999 => "Extra Black",
+ < 100 => "Extra Thin",
+ < 275 => "Thin",
+ < 300 => "Extra Light",
+ < 350 => "Light",
+ < 400 => "Semi Light",
+ < 500 => "Normal",
+ < 600 => "Medium",
+ < 700 => "Semi Bold",
+ < 800 => "Bold",
+ < 900 => "Extra Bold",
+ < 950 => "Black",
+ < 999 => "Extra Black",
_ => string.Empty
};
- internal int Width => m_typeface.FontWidth;
+ public override int Width => m_typeface.FontWidth;
private string WidthName => Width switch
{
1 => "Ultra Condensed",
@@ -213,7 +293,7 @@ protected virtual void Dispose(bool disposing)
_ => string.Empty
};
- internal SlantType Slant => m_typeface.FontSlant switch
+ public override SlantType Slant => m_typeface.FontSlant switch
{
SKFontStyleSlant.Oblique => SlantType.Oblique,
SKFontStyleSlant.Italic => SlantType.Italic,
@@ -228,7 +308,7 @@ protected virtual void Dispose(bool disposing)
_ => string.Empty
};
- internal string Face
+ public override string Face
{
get
{
@@ -247,8 +327,8 @@ void AppendStyle(string value)
return faceName.Append(faceName.Length > 0 ? string.Empty : "Regular").ToString();
}
}
-
- private static FontFamily GetGenericFontFamily(GenericFontFamilies genericFamily)
+
+ private static SkiaFontFamily GetGenericFontFamily(GenericFontFamilies genericFamily)
{
var candidates = genericFamily switch // NOTE: Define a set of predefined fonts
{
@@ -257,15 +337,34 @@ private static FontFamily GetGenericFontFamily(GenericFontFamilies genericFamily
GenericFontFamilies.Serif => new[] { "Times New Roman", "Georgia", "Garamond", "Palatino", "Book Antiqua", "Baskerville" },
_ => throw new ArgumentException($"invalid generic font value {genericFamily}", nameof(genericFamily))
};
- foreach (var candidate in candidates)
+ foreach (string candidate in candidates)
if (Font.SystemFonts.FirstOrDefault(f => f.FamilyName.Equals(candidate, StringComparison.OrdinalIgnoreCase)) is Font font)
- return font.FontFamily;
+ return font.FontFamily as SkiaFontFamily;
throw new ArgumentException($"invalid generic font family", nameof(genericFamily));
}
internal bool MatchFamily(string familyName) // TODO: Improve this code
- => new string[] { Name, $"{Name} {Face}", $"{Name}-{Face}" }.Any(candidateName
+ => new[] { Name, $"{Name} {Face}", $"{Name}-{Face}" }.Any(candidateName
=> candidateName.Equals(familyName, StringComparison.OrdinalIgnoreCase));
#endregion
}
+
+internal sealed class UnknownFontFamily : FontFamily
+{
+ public UnknownFontFamily(string name) { Name = name; }
+ public override string Name { get; }
+ public override string Face => string.Empty;
+ public override bool IsItalic => false;
+ public override bool IsBold => false;
+ public override int Weight => 0;
+ public override int Width => 0;
+ public override SlantType Slant => SlantType.Normal;
+ public override int GetCellAscent() => 0;
+ public override int GetCellDescent() => 0;
+ public override int GetEmHeight() => 0;
+ public override int GetLineSpacing() => 0;
+ public override string GetName(int language) => Name;
+ public override bool IsStyleAvailable(FontStyle style) => false;
+ public override object Clone() => new UnknownFontFamily(Name);
+}
diff --git a/src/Common/GraphicsUnit.cs b/src/Common/GraphicsUnit.cs
new file mode 100644
index 0000000..f22a2de
--- /dev/null
+++ b/src/Common/GraphicsUnit.cs
@@ -0,0 +1,43 @@
+namespace GeneXus.Drawing;
+
+///
+/// Specifies the unit of measure for the given data
+///
+public enum GraphicsUnit
+{
+ ///
+ /// Specifies the world coordinate system unit as the unit of measure.
+ ///
+ World,
+
+ ///
+ /// Specifies the unit of measure of the display device. Typically pixels for video
+ /// displays, and 1/100 inch for printers.
+ ///
+ Display,
+
+ ///
+ /// Specifies a device pixel as the unit of measure.
+ ///
+ Pixel,
+
+ ///
+ /// Specifies a printer's point (1/72 inch) as the unit of measure.
+ ///
+ Point,
+
+ ///
+ /// Specifies the inch as the unit of measure.
+ ///
+ Inch,
+
+ ///
+ /// Specifies the document unit (1/300 inch) as the unit of measure.
+ ///
+ Document,
+
+ ///
+ /// Specifies the millimeter as the unit of measure.
+ ///
+ Millimeter
+}
diff --git a/src/Common/Point.cs b/src/Common/Point.cs
index 9dd4ba9..05bd35c 100644
--- a/src/Common/Point.cs
+++ b/src/Common/Point.cs
@@ -16,7 +16,7 @@ private Point(SKPoint point)
///
/// Initializes a new instance of the class with the specified coordinates.
///
- public Point(float x, float y)
+ public Point(int x, int y)
: this(new SKPoint(x, y)) { }
///
@@ -113,18 +113,18 @@ public Point(int dw)
///
/// Gets the x-coordinate of this .
///
- public float X
+ public int X
{
- readonly get => m_point.X;
+ readonly get => (int)m_point.X;
set => m_point.X = value;
}
///
/// Gets the y-coordinate of this .
///
- public float Y
+ public int Y
{
- readonly get => m_point.Y;
+ readonly get => (int)m_point.Y;
set => m_point.Y = value;
}
@@ -148,25 +148,10 @@ public float Y
///
public static Point Subtract(Point pt, Size sz) => new(pt.m_point - sz.m_size);
- ///
- /// Converts a by performing a ceiling operation on all the coordinates.
- ///
- public static Point Ceiling(Point value) => new(unchecked((int)Math.Ceiling(value.X)), unchecked((int)Math.Ceiling(value.Y)));
-
- ///
- /// Converts a by performing a truncate operation on all the coordinates.
- ///
- public static Point Truncate(Point value) => new(unchecked((int)value.X), unchecked((int)value.Y));
-
- ///
- /// Converts a by performing a round operation on all the coordinates.
- ///
- public static Point Round(Point value) => new(unchecked((int)Math.Round(value.X)), unchecked((int)Math.Round(value.Y)));
-
///
/// Translates this by the specified amount.
///
- public void Offset(float dx, float dy) => m_point.Offset(dx, dy);
+ public void Offset(int dx, int dy) => m_point.Offset(dx, dy);
///
/// Translates this by the specified amount.
diff --git a/src/Common/PointF.cs b/src/Common/PointF.cs
new file mode 100644
index 0000000..b009356
--- /dev/null
+++ b/src/Common/PointF.cs
@@ -0,0 +1,177 @@
+using System;
+using SkiaSharp;
+
+namespace GeneXus.Drawing;
+
+[Serializable]
+public struct PointF : IEquatable
+{
+ internal SKPoint m_point;
+
+ private PointF(SKPoint point)
+ {
+ m_point = point;
+ }
+
+ ///
+ /// Initializes a new instance of the class with the specified coordinates.
+ ///
+ public PointF(float x, float y)
+ : this(new SKPoint(x, y)) { }
+
+ ///
+ /// Initializes a new instance of the class from a .
+ ///
+ public PointF(SizeF sz)
+ : this(sz.Width, sz.Height) { }
+
+ ///
+ /// Initializes a new instance of the class using coordinates specified by an integer value.
+ ///
+ public PointF(int dw)
+ : this(unchecked((short)((dw >> 0) & 0xFFFF)), unchecked((short)((dw >> 16) & 0xFFFF))) { }
+
+ ///
+ /// Creates a human-readable string that represents this .
+ ///
+ public override readonly string ToString() => $"{{X={X},Y={Y}}}";
+
+
+ #region Operators
+
+ ///
+ /// Creates a with the coordinates of the specified .
+ ///
+ public static explicit operator SKPoint(PointF point) => point.m_point;
+
+ ///
+ /// Creates a with the coordinates of the specified .
+ ///
+ public static explicit operator SizeF(PointF p) => new(p.X, p.Y);
+
+ ///
+ /// Compares two objects. The result specifies whether the values of the
+ /// and properties of the two
+ /// objects are equal.
+ ///
+ public static bool operator ==(PointF left, PointF right) => left.m_point == right.m_point;
+
+ ///
+ /// Compares two objects. The result specifies whether the values of the
+ /// or properties of the two
+ /// objects are unequal.
+ ///
+ public static bool operator !=(PointF left, PointF right) => left.m_point != right.m_point;
+
+ ///
+ /// Translates a by a given .
+ ///
+ public static PointF operator +(PointF pt, SizeF sz) => Add(pt, sz);
+
+ ///
+ /// Translates a by the negative of a given .
+ ///
+ public static PointF operator -(PointF pt, SizeF sz) => Subtract(pt, sz);
+
+ #endregion
+
+
+ #region IEquatable
+
+ ///
+ /// Tests whether a has the same coordinates
+ /// as this Point.
+ ///
+ public readonly bool Equals(PointF other) => m_point == other.m_point;
+
+ ///
+ /// Tests whether is a with the same coordinates
+ /// as this Point.
+ ///
+ public override readonly bool Equals(object obj) => m_point.Equals(obj);
+
+ ///
+ /// Returns a hash code.
+ ///
+ public override readonly int GetHashCode() => m_point.GetHashCode();
+
+ #endregion
+
+
+ #region Fields
+
+ ///
+ /// Creates a new instance of the class with member data left uninitialized.
+ ///
+ public static readonly PointF Empty = default;
+
+ #endregion
+
+
+ #region Properties
+
+ ///
+ /// Gets the x-coordinate of this .
+ ///
+ public float X
+ {
+ readonly get => m_point.X;
+ set => m_point.X = value;
+ }
+
+ ///
+ /// Gets the y-coordinate of this .
+ ///
+ public float Y
+ {
+ readonly get => m_point.Y;
+ set => m_point.Y = value;
+ }
+
+ ///
+ /// Gets a value indicating whether this is empty.
+ ///
+ public readonly bool IsEmpty => m_point.IsEmpty;
+
+ #endregion
+
+
+ #region Methods
+
+ ///
+ /// Translates a by a given .
+ ///
+ public static PointF Add(PointF pt, SizeF sz) => new(pt.m_point + sz.m_size);
+
+ ///
+ /// Translates a by the negative of a given .
+ ///
+ public static PointF Subtract(PointF pt, SizeF sz) => new(pt.m_point - sz.m_size);
+
+ ///
+ /// Converts a by performing a ceiling operation on all the coordinates.
+ ///
+ public static PointF Ceiling(PointF value) => new(unchecked((int)Math.Ceiling(value.X)), unchecked((int)Math.Ceiling(value.Y)));
+
+ ///
+ /// Converts a by performing a truncate operation on all the coordinates.
+ ///
+ public static PointF Truncate(PointF value) => new(unchecked((int)value.X), unchecked((int)value.Y));
+
+ ///
+ /// Converts a by performing a round operation on all the coordinates.
+ ///
+ public static PointF Round(PointF value) => new(unchecked((int)Math.Round(value.X)), unchecked((int)Math.Round(value.Y)));
+
+ ///
+ /// Translates this by the specified amount.
+ ///
+ public void Offset(float dx, float dy) => m_point.Offset(dx, dy);
+
+ ///
+ /// Translates this by the specified amount.
+ ///
+ public void Offset(PointF p) => Offset(p.X, p.Y);
+
+ #endregion
+}
diff --git a/src/Common/Rectangle.cs b/src/Common/Rectangle.cs
index cc447ab..6ae6bd9 100644
--- a/src/Common/Rectangle.cs
+++ b/src/Common/Rectangle.cs
@@ -17,7 +17,7 @@ internal Rectangle(SKRect rect)
/// Initializes a new instance of the struct with the
/// specified location and size.
///
- public Rectangle(float x, float y, float width, float height)
+ public Rectangle(int x, int y, int width, int height)
: this(new SKRect(x, y, width + x, height + y)) { }
///
@@ -29,13 +29,13 @@ public Rectangle(Point location, Size size)
///
/// Initializes a new instance of the struct with the specified location (x, y) and size.
///
- public Rectangle(float x, float y, Size size)
+ public Rectangle(int x, int y, Size size)
: this(x, y, size.Width, size.Height) { }
///
/// Initializes a new instance of the struct with the specified location and size (width, height).
///
- public Rectangle(Point location, float width, float height)
+ public Rectangle(Point location, int width, int height)
: this(location.X, location.Y, width, height) { }
///
@@ -99,41 +99,41 @@ public Rectangle(Point location, float width, float height)
/// Gets the x-coordinate of the upper-left corner of the rectangular region defined by this
/// .
///
- public readonly float Left => m_rect.Left;
+ public readonly int Left => (int)m_rect.Left;
///
/// Gets the x-coordinate of the lower-right corner of the rectangular region defined by this
/// .
///
- public readonly float Right => m_rect.Right;
+ public readonly int Right => (int)m_rect.Right;
///
/// Gets the y-coordinate of the upper-left corner of the rectangular region defined by this
/// .
///
- public readonly float Top => m_rect.Top;
+ public readonly int Top => (int)m_rect.Top;
///
/// Gets the y-coordinate of the lower-right corner of the rectangular region defined by this
/// .
///
- public readonly float Bottom => m_rect.Bottom;
+ public readonly int Bottom => (int)m_rect.Bottom;
///
/// Gets or sets the width of the rectangular region defined by this .
///
- public float Width
+ public int Width
{
- readonly get => m_rect.Width;
+ readonly get => (int)m_rect.Width;
set => m_rect.Right = m_rect.Left + value;
}
///
/// Gets or sets the width of the rectangular region defined by this .
///
- public float Height
+ public int Height
{
- readonly get => m_rect.Height;
+ readonly get => (int)m_rect.Height;
set => m_rect.Bottom = m_rect.Top + value;
}
@@ -141,7 +141,7 @@ public float Height
/// Gets or sets the x-coordinate of the upper-left corner of the rectangular region defined by this
/// .
///
- public float X
+ public int X
{
readonly get => Left;
set
@@ -155,7 +155,7 @@ public float X
/// Gets or sets the y-coordinate of the upper-left corner of the rectangular region defined by this
/// .
///
- public float Y
+ public int Y
{
readonly get => Top;
set
@@ -170,7 +170,7 @@ public float Y
///
public Size Size
{
- get => new Size(Width, Height);
+ get => new(Width, Height);
set => (Width, Height) = (value.Width, value.Height);
}
@@ -180,7 +180,7 @@ public Size Size
///
public Point Location
{
- get => new Point(X, Y);
+ get => new(X, Y);
set => (X, Y) = (value.X, value.Y);
}
@@ -198,7 +198,7 @@ public Point Location
///
/// Creates a new with the specified location and size.
///
- public static Rectangle FromLTRB(float left, float top, float right, float bottom) => new(left, top, unchecked(right - left), unchecked(bottom - top));
+ public static Rectangle FromLTRB(int left, int top, int right, int bottom) => new(left, top, unchecked(right - left), unchecked(bottom - top));
#endregion
@@ -215,7 +215,7 @@ public Point Location
/// Determines if the specified point is contained within the rectangular region defined by this
/// .
///
- public readonly bool Contains(float x, float y) => m_rect.Contains(x, y);
+ public readonly bool Contains(int x, int y) => m_rect.Contains(x, y);
///
/// Determines if the specified point is contained within the rectangular region defined by this
@@ -260,7 +260,7 @@ public static Rectangle Union(Rectangle a, Rectangle b)
///
/// Inflates this by the specified amount.
///
- public void Inflate(float width, float height) => m_rect.Inflate(width, height);
+ public void Inflate(int width, int height) => m_rect.Inflate(width, height);
///
/// Inflates this by the specified amount.
@@ -270,7 +270,7 @@ public static Rectangle Union(Rectangle a, Rectangle b)
///
/// Creates a that is inflated by the specified amount.
///
- public static Rectangle Inflate(Rectangle rect, float x, float y)
+ public static Rectangle Inflate(Rectangle rect, int x, int y)
{
var ret = SKRect.Inflate(rect.m_rect, x, y);
return new Rectangle(ret);
@@ -279,7 +279,7 @@ public static Rectangle Inflate(Rectangle rect, float x, float y)
///
/// Adjusts the location of this by the specified amount.
///
- public void Offset(float x, float y) => m_rect.Offset(x, y);
+ public void Offset(int x, int y) => m_rect.Offset(x, y);
///
/// Adjusts the location of this by the specified amount.
diff --git a/src/Common/RectangleF.cs b/src/Common/RectangleF.cs
new file mode 100644
index 0000000..56c5ff2
--- /dev/null
+++ b/src/Common/RectangleF.cs
@@ -0,0 +1,290 @@
+using System;
+using SkiaSharp;
+
+namespace GeneXus.Drawing;
+
+[Serializable]
+public struct RectangleF : IEquatable
+{
+ internal SKRect m_rect;
+
+ internal RectangleF(SKRect rect)
+ {
+ m_rect = rect;
+ }
+
+ ///
+ /// Initializes a new instance of the struct with the
+ /// specified location and size.
+ ///
+ public RectangleF(float x, float y, float width, float height)
+ : this(new SKRect(x, y, width + x, height + y)) { }
+
+ ///
+ /// Initializes a new instance of the struct with the specified location and size.
+ ///
+ public RectangleF(PointF location, SizeF size)
+ : this(location.X, location.Y, size.Width, size.Height) { }
+
+ ///
+ /// Initializes a new instance of the struct with the specified location (x, y) and size.
+ ///
+ public RectangleF(float x, float y, SizeF size)
+ : this(x, y, size.Width, size.Height) { }
+
+ ///
+ /// Initializes a new instance of the struct with the specified location and size (width, height).
+ ///
+ public RectangleF(PointF location, float width, float height)
+ : this(location.X, location.Y, width, height) { }
+
+ ///
+ /// Creates a human-readable string that represents this .
+ ///
+ public override readonly string ToString() => $"{{X={X},Y={Y},Width={Width},Height={Height}}}";
+
+
+ #region Operators
+
+ public static explicit operator SKRect(RectangleF rect) => rect.m_rect;
+
+ ///
+ /// Tests whether two objects have equal location and size.
+ ///
+ public static bool operator ==(RectangleF left, RectangleF right) => left.m_rect == right.m_rect;
+
+ ///
+ /// Tests whether two objects differ in location or size.
+ ///
+ public static bool operator !=(RectangleF left, RectangleF right) => left.m_rect != right.m_rect;
+
+ #endregion
+
+
+ #region IEquatable
+
+ ///
+ /// Tests whether a has the same location
+ /// and size of this Rectangle.
+ ///
+ public readonly bool Equals(RectangleF other) => m_rect == other.m_rect;
+
+ ///
+ /// Tests whether is a with the same location
+ /// and size as this Rectangle.
+ ///
+ public override readonly bool Equals(object obj) => m_rect.Equals(obj);
+
+ ///
+ /// Returns a hash code.
+ ///
+ public override readonly int GetHashCode() => m_rect.GetHashCode();
+
+ #endregion
+
+
+ #region Fields
+
+ ///
+ /// Initializes a new instance of the struct.
+ ///
+ public static readonly RectangleF Empty = default;
+
+ #endregion
+
+
+ #region Properties
+
+ ///
+ /// Gets the x-coordinate of the upper-left corner of the rectangular region defined by this
+ /// .
+ ///
+ public readonly float Left => m_rect.Left;
+
+ ///
+ /// Gets the x-coordinate of the lower-right corner of the rectangular region defined by this
+ /// .
+ ///
+ public readonly float Right => m_rect.Right;
+
+ ///
+ /// Gets the y-coordinate of the upper-left corner of the rectangular region defined by this
+ /// .
+ ///
+ public readonly float Top => m_rect.Top;
+
+ ///
+ /// Gets the y-coordinate of the lower-right corner of the rectangular region defined by this
+ /// .
+ ///
+ public readonly float Bottom => m_rect.Bottom;
+
+ ///
+ /// Gets or sets the width of the rectangular region defined by this .
+ ///
+ public float Width
+ {
+ readonly get => m_rect.Width;
+ set => m_rect.Right = m_rect.Left + value;
+ }
+
+ ///
+ /// Gets or sets the width of the rectangular region defined by this .
+ ///
+ public float Height
+ {
+ readonly get => m_rect.Height;
+ set => m_rect.Bottom = m_rect.Top + value;
+ }
+
+ ///
+ /// Gets or sets the x-coordinate of the upper-left corner of the rectangular region defined by this
+ /// .
+ ///
+ public float X
+ {
+ readonly get => Left;
+ set
+ {
+ m_rect.Right += value - m_rect.Left;
+ m_rect.Left = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the y-coordinate of the upper-left corner of the rectangular region defined by this
+ /// .
+ ///
+ public float Y
+ {
+ readonly get => Top;
+ set
+ {
+ m_rect.Bottom += value - m_rect.Top;
+ m_rect.Top = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the size of this .
+ ///
+ public SizeF Size
+ {
+ get => new(Width, Height);
+ set => (Width, Height) = (value.Width, value.Height);
+ }
+
+ ///
+ /// Gets or sets the coordinates of the upper-left corner of the rectangular region represented by this
+ /// .
+ ///
+ public PointF Location
+ {
+ get => new(X, Y);
+ set => (X, Y) = (value.X, value.Y);
+ }
+
+ ///
+ /// Tests whether this has a
+ /// or a of 0.
+ ///
+ public readonly bool IsEmpty => m_rect.IsEmpty;
+
+ #endregion
+
+
+ #region Factory
+
+ ///
+ /// Creates a new with the specified location and size.
+ ///
+ public static RectangleF FromLTRB(float left, float top, float right, float bottom) => new(left, top, unchecked(right - left), unchecked(bottom - top));
+
+ #endregion
+
+
+ #region Methods
+
+ ///
+ /// Determines if the rectangular region represented by is entirely contained within the
+ /// rectangular region represented by this .
+ ///
+ public readonly bool Contains(RectangleF rect) => m_rect.Contains(rect.m_rect);
+
+ ///
+ /// Determines if the specified point is contained within the rectangular region defined by this
+ /// .
+ ///
+ public readonly bool Contains(float x, float y) => m_rect.Contains(x, y);
+
+ ///
+ /// Determines if the specified point is contained within the rectangular region defined by this
+ /// .
+ ///
+ public readonly bool Contains(PointF pt) => Contains(pt.X, pt.Y);
+
+ ///
+ /// Creates a that represents the intersection between this Rectangle and rect.
+ ///
+ public void Intersect(RectangleF rect) => m_rect.Intersect(rect.m_rect);
+
+ ///
+ /// Creates a that represents the intersection between a and b. If there
+ /// is no intersection, an empty rectangle is returned.
+ ///
+ public static RectangleF Intersect(RectangleF a, RectangleF b)
+ {
+ var ret = SKRect.Intersect(a.m_rect, b.m_rect);
+ return new RectangleF(ret);
+ }
+
+ ///
+ /// Determines if this intersects with rect.
+ ///
+ public readonly bool IntersectsWith(RectangleF rect) => m_rect.IntersectsWith(rect.m_rect);
+
+ ///
+ /// Creates a that represents the union between this Rectangle and rect.
+ ///
+ public void Union(RectangleF rect) => m_rect.Union(rect.m_rect);
+
+ ///
+ /// Creates a that represents the union between a and b.
+ ///
+ public static RectangleF Union(RectangleF a, RectangleF b)
+ {
+ var ret = SKRect.Union(a.m_rect, b.m_rect);
+ return new RectangleF(ret);
+ }
+
+ ///
+ /// Inflates this by the specified amount.
+ ///
+ public void Inflate(float width, float height) => m_rect.Inflate(width, height);
+
+ ///
+ /// Inflates this by the specified amount.
+ ///
+ public void Inflate(SizeF size) => Inflate(size.Width, size.Height);
+
+ ///
+ /// Creates a that is inflated by the specified amount.
+ ///
+ public static RectangleF Inflate(RectangleF rect, float x, float y)
+ {
+ var ret = SKRect.Inflate(rect.m_rect, x, y);
+ return new RectangleF(ret);
+ }
+
+ ///
+ /// Adjusts the location of this by the specified amount.
+ ///
+ public void Offset(float x, float y) => m_rect.Offset(x, y);
+
+ ///
+ /// Adjusts the location of this by the specified amount.
+ ///
+ public void Offset(PointF pos) => Offset(pos.X, pos.Y);
+
+ #endregion
+}
diff --git a/src/Common/Size.cs b/src/Common/Size.cs
index 15f15c0..e80727f 100644
--- a/src/Common/Size.cs
+++ b/src/Common/Size.cs
@@ -16,7 +16,7 @@ private Size(SKSize size)
///
/// Initializes a new instance of the class from the specified dimensions.
///
- public Size(float width, float height)
+ public Size(int width, int height)
: this(new SKSize(width, height)) { }
///
@@ -65,28 +65,28 @@ public Size(Point pt)
public static Size operator -(Size sz1, Size sz2) => Subtract(sz1, sz2);
///
- /// Multiplies by an producing .
+ /// Multiplies by an producing .
///
/// Multiplicand of type .
- /// Multiplier of type .
+ /// Multiplier of type .
/// Product of type .
- public static Size operator *(Size left, float right) => Multiply(left, right);
+ public static Size operator *(Size left, int right) => Multiply(left, right);
///
- /// Multiplies a by an producing .
+ /// Multiplies a by an producing .
///
- /// Multiplier of type .
+ /// Multiplier of type .
/// Multiplicand of type .
/// Product of type .
- public static Size operator *(float left, Size right) => Multiply(right, left);
+ public static Size operator *(int left, Size right) => Multiply(right, left);
///
- /// Divides by an producing .
+ /// Divides by an producing .
///
/// Dividend of type .
- /// Divisor of type .
+ /// Divisor of type .
/// Result of type .
- public static Size operator /(Size left, float right) => Divide(left, right);
+ public static Size operator /(Size left, int right) => Divide(left, right);
#endregion
@@ -128,18 +128,18 @@ public Size(Point pt)
///
/// Represents the horizontal component of this .
///
- public float Width
+ public int Width
{
- readonly get => m_size.Width;
+ readonly get => (int)m_size.Width;
set => m_size.Width = value;
}
///
/// Represents the vertical component of this .
///
- public float Height
+ public int Height
{
- readonly get => m_size.Height;
+ readonly get => (int)m_size.Height;
set => m_size.Height = value;
}
@@ -159,40 +159,25 @@ public float Height
public static Size Add(Size sz1, Size sz2) => new(sz1.m_size + sz2.m_size);
///
- /// Performs vector substraction of two objects.
+ /// Performs vector subtraction of two objects.
///
public static Size Subtract(Size sz1, Size sz2) => new(sz1.m_size - sz2.m_size);
///
- /// Converts a by performing a ceiling operation on all the coordinates.
- ///
- public static Size Ceiling(Size value) => new(unchecked((int)Math.Ceiling(value.Width)), unchecked((int)Math.Ceiling(value.Height)));
-
- ///
- /// Converts a by performing a truncate operation on all the coordinates.
- ///
- public static Size Truncate(Size value) => new(unchecked((int)value.Width), unchecked((int)value.Height));
-
- ///
- /// Converts a by performing a round operation on all the coordinates.
- ///
- public static Size Round(Size value) => new(unchecked((int)Math.Round(value.Width)), unchecked((int)Math.Round(value.Height)));
-
- ///
- /// Multiplies by an producing .
+ /// Multiplies by an producing .
///
/// Multiplicand of type .
- /// Multiplier of type .
+ /// Multiplier of type .
/// Product of type .
- public static Size Multiply(Size size, float multiplier) => new(unchecked(size.Width * multiplier), unchecked(size.Height * multiplier));
+ public static Size Multiply(Size size, int multiplier) => new(size.Width * multiplier, size.Height * multiplier);
///
- /// Divides by an producing .
+ /// Divides by an producing .
///
- /// Divident of type .
- /// Denominator of type .
+ /// Dividend of type .
+ /// Divisor of type .
/// Quotient of type .
- public static Size Divide(Size size, float denominator) => new(unchecked(size.Width / denominator), unchecked(size.Height / denominator));
+ public static Size Divide(Size size, int divisor) => new(size.Width / divisor, size.Height / divisor);
#endregion
}
diff --git a/src/Common/SizeF.cs b/src/Common/SizeF.cs
new file mode 100644
index 0000000..434f34e
--- /dev/null
+++ b/src/Common/SizeF.cs
@@ -0,0 +1,198 @@
+using System;
+using SkiaSharp;
+
+namespace GeneXus.Drawing;
+
+[Serializable]
+public struct SizeF : IEquatable
+{
+ internal SKSize m_size;
+
+ private SizeF(SKSize size)
+ {
+ m_size = size;
+ }
+
+ ///
+ /// Initializes a new instance of the class from the specified dimensions.
+ ///
+ public SizeF(float width, float height)
+ : this(new SKSize(width, height)) { }
+
+ ///
+ /// Initializes a new instance of the class from the specified
+ /// .
+ ///
+ public SizeF(PointF pt)
+ : this(pt.X, pt.Y) { }
+
+ ///
+ /// Creates a human-readable string that represents this .
+ ///
+ public override readonly string ToString() => $"{{Width={Width},Height={Height}}}";
+
+
+ #region Operators
+
+ ///
+ /// Converts the specified to a .
+ ///
+ public static explicit operator SKSize(SizeF point) => point.m_size;
+
+ ///
+ /// Converts the specified to a .
+ ///
+ public static explicit operator PointF(SizeF size) => new(size.Width, size.Height);
+
+ ///
+ /// Tests whether two objects are identical.
+ ///
+ public static bool operator ==(SizeF sz1, SizeF sz2) => sz1.m_size == sz2.m_size;
+
+ ///
+ /// Tests whether two objects are different.
+ ///
+ public static bool operator !=(SizeF sz1, SizeF sz2) => sz1.m_size != sz2.m_size;
+
+ ///
+ /// Performs vector addition of two objects.
+ ///
+ public static SizeF operator +(SizeF sz1, SizeF sz2) => Add(sz1, sz2);
+
+ ///
+ /// Contracts a by another
+ ///
+ public static SizeF operator -(SizeF sz1, SizeF sz2) => Subtract(sz1, sz2);
+
+ ///
+ /// Multiplies by an producing .
+ ///
+ /// Multiplicand of type .
+ /// Multiplier of type .
+ /// Product of type .
+ public static SizeF operator *(SizeF left, float right) => Multiply(left, right);
+
+ ///
+ /// Multiplies a by an producing .
+ ///
+ /// Multiplier of type .
+ /// Multiplicand of type .
+ /// Product of type .
+ public static SizeF operator *(float left, SizeF right) => Multiply(right, left);
+
+ ///
+ /// Divides by an producing .
+ ///
+ /// Dividend of type .
+ /// Divisor of type .
+ /// Result of type .
+ public static SizeF operator /(SizeF left, float right) => Divide(left, right);
+
+ #endregion
+
+
+ #region IEquatable
+
+ ///
+ /// Tests whether a has the same dimensions
+ /// as this Size.
+ ///
+ public readonly bool Equals(SizeF other) => m_size == other.m_size;
+
+ ///
+ /// Tests whether is a with the same dimensions
+ /// as this Size.
+ ///
+ public override readonly bool Equals(object obj) => m_size.Equals(obj);
+
+ ///
+ /// Returns a hash code.
+ ///
+ public override readonly int GetHashCode() => m_size.GetHashCode();
+
+ #endregion
+
+
+ #region Fields
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public static readonly SizeF Empty = default;
+
+ #endregion
+
+
+ #region Properties
+
+ ///
+ /// Represents the horizontal component of this .
+ ///
+ public float Width
+ {
+ readonly get => m_size.Width;
+ set => m_size.Width = value;
+ }
+
+ ///
+ /// Represents the vertical component of this .
+ ///
+ public float Height
+ {
+ readonly get => m_size.Height;
+ set => m_size.Height = value;
+ }
+
+ ///
+ /// Tests whether this has zero width and height.
+ ///
+ public readonly bool IsEmpty => m_size.IsEmpty;
+
+ #endregion
+
+
+ #region Methods
+
+ ///
+ /// Performs vector addition of two objects.
+ ///
+ public static SizeF Add(SizeF sz1, SizeF sz2) => new(sz1.m_size + sz2.m_size);
+
+ ///
+ /// Performs vector subtraction of two objects.
+ ///
+ public static SizeF Subtract(SizeF sz1, SizeF sz2) => new(sz1.m_size - sz2.m_size);
+
+ ///
+ /// Converts a by performing a ceiling operation on all the coordinates.
+ ///
+ public static SizeF Ceiling(SizeF value) => new(unchecked((int)Math.Ceiling(value.Width)), unchecked((int)Math.Ceiling(value.Height)));
+
+ ///
+ /// Converts a by performing a truncate operation on all the coordinates.
+ ///
+ public static SizeF Truncate(SizeF value) => new(unchecked((int)value.Width), unchecked((int)value.Height));
+
+ ///
+ /// Converts a by performing a round operation on all the coordinates.
+ ///
+ public static SizeF Round(SizeF value) => new(unchecked((int)Math.Round(value.Width)), unchecked((int)Math.Round(value.Height)));
+
+ ///
+ /// Multiplies by an producing .
+ ///
+ /// Multiplicand of type .
+ /// Multiplier of type .
+ /// Product of type .
+ public static SizeF Multiply(SizeF size, float multiplier) => new(unchecked(size.Width * multiplier), unchecked(size.Height * multiplier));
+
+ ///
+ /// Divides by an producing .
+ ///
+ /// Dividend of type .
+ /// Divisor of type .
+ /// Quotient of type .
+ public static SizeF Divide(SizeF size, float divisor) => new(unchecked(size.Width / divisor), unchecked(size.Height / divisor));
+
+ #endregion
+}
diff --git a/src/Common/Text/PrivateFontCollection.cs b/src/Common/Text/PrivateFontCollection.cs
index 17e59c2..e6ab86b 100644
--- a/src/Common/Text/PrivateFontCollection.cs
+++ b/src/Common/Text/PrivateFontCollection.cs
@@ -9,15 +9,15 @@ public class PrivateFontCollection : FontCollection
///
/// Initializes a new instance of the class.
///
- public PrivateFontCollection() : base()
+ public PrivateFontCollection()
{ }
///
/// Adds a font from the specified file to this .
///
- public void AddFontFile(string filename)
+ public void AddFontFile(string filePath)
{
- var fontFamily = new FontFamily(filename);
+ var fontFamily = FontFamilyFactory.Create(filePath);
m_families.Add(fontFamily);
}
@@ -31,7 +31,7 @@ public void AddMemoryFont(IntPtr memory, int length)
using var stream = new MemoryStream(fontData);
- var fontFamily = new FontFamily(stream);
+ var fontFamily = FontFamilyFactory.Create(stream);
m_families.Add(fontFamily);
}
}
diff --git a/test/Common/FontConverterTest.cs b/test/Common/FontConverterTest.cs
new file mode 100644
index 0000000..9b1194c
--- /dev/null
+++ b/test/Common/FontConverterTest.cs
@@ -0,0 +1,27 @@
+using System;
+using System.ComponentModel;
+
+namespace GeneXus.Drawing.Test;
+
+internal class FontConverterTest
+{
+ [Test]
+ public void Method_ConvertFromString()
+ {
+ Font font = TypeDescriptor.GetConverter(typeof(Font)).ConvertFromString("Verdana,12") as Font;
+ Assert.Multiple(() =>
+ {
+ Assert.That(font?.Name, Is.EqualTo("Verdana"));
+ Assert.That(Math.Abs(font.Size - 12f), Is.LessThan(0.01));
+ Assert.That(font.Unit, Is.EqualTo(GraphicsUnit.Point));
+ });
+ }
+
+ [Test]
+ public void Method_ConvertToString()
+ {
+ Font font = new("Verdana"); // default size is 12
+ string text = TypeDescriptor.GetConverter(typeof(Font)).ConvertToString(font);
+ Assert.That(text, Is.EqualTo("Verdana, 12pt"));
+ }
+}
diff --git a/test/Common/FontFamilyUnitTest.cs b/test/Common/FontFamilyUnitTest.cs
index 0471b66..6cc1a00 100644
--- a/test/Common/FontFamilyUnitTest.cs
+++ b/test/Common/FontFamilyUnitTest.cs
@@ -32,7 +32,7 @@ public void Setup()
public void Constructor_FileName(string fileName, string familyName, int ascent, int descent, int lineSpacing, int emHeight, int fontIndex = 0)
{
var fontPath = Path.Combine(FONT_PATH, fileName);
- using var family = new FontFamily(fontPath, fontIndex);
+ using var family = FontFamilyFactory.Create(fontPath, fontIndex);
Assert.Multiple(() =>
{
Assert.That(family.Name, Is.EqualTo(familyName));
@@ -60,7 +60,7 @@ public void Constructor_Stream(string fileName, string familyName, int ascent, i
{
var fontPath = Path.Combine(FONT_PATH, fileName);
using var fontStream = File.OpenRead(fontPath);
- using var family = new FontFamily(fontStream, fontIndex);
+ using var family = FontFamilyFactory.Create(fontStream, fontIndex);
Assert.Multiple(() =>
{
Assert.That(family.Name, Is.EqualTo(familyName));
@@ -81,7 +81,7 @@ public void Constructor_FontCollection()
Assert.That(pfc.Families, Has.Length.EqualTo(3));
using var font = new Font(pfc.Families[1]);
- using var family = new FontFamily(font.Name, pfc);
+ using var family = FontFamilyFactory.Create(font.Name, pfc);
Assert.Multiple(() =>
{
Assert.That(family.Name, Is.EqualTo("Montserrat"));
@@ -94,13 +94,13 @@ public void Constructor_FontCollection()
[Test]
public void Constructor_GenericFont()
{
- using var monospace = new FontFamily(GenericFontFamilies.Monospace);
+ using var monospace = FontFamilyFactory.Create(GenericFontFamilies.Monospace);
Assert.That(monospace.Name, Is.AnyOf("Courier New", "Consolas", "Courier", "Menlo", "Monaco", "Lucida Console").IgnoreCase);
- using var sanserif = new FontFamily(GenericFontFamilies.SansSerif);
+ using var sanserif = FontFamilyFactory.Create(GenericFontFamilies.SansSerif);
Assert.That(sanserif.Name, Is.AnyOf("Arial", "Helvetica", "Verdana", "Tahoma", "Trebuchet MS", "Gill Sans").IgnoreCase);
- using var justserif = new FontFamily(GenericFontFamilies.Serif);
+ using var justserif = FontFamilyFactory.Create(GenericFontFamilies.Serif);
Assert.That(justserif.Name, Is.AnyOf("Times New Roman", "Georgia", "Garamond", "Palatino", "Book Antiqua", "Baskerville").IgnoreCase);
}
}
diff --git a/test/Common/FontUnitTest.cs b/test/Common/FontUnitTest.cs
index 4136b00..c17c638 100644
--- a/test/Common/FontUnitTest.cs
+++ b/test/Common/FontUnitTest.cs
@@ -31,7 +31,7 @@ public void Setup()
public void Constructor_Family(string fileName, string familyName, string faceName, int fontWeight, int fontWidth, int fontHeight, SlantType fontSlant, FontStyle fontStyle, int fontIndex = 0)
{
var fontPath = Path.Combine(FONT_PATH, fileName);
- using var family = new FontFamily(fontPath, fontIndex);
+ using var family = FontFamilyFactory.Create(fontPath, fontIndex);
using var font = new Font(family);
Assert.Multiple(() =>
@@ -98,8 +98,8 @@ public void Property_IsSystemFont()
[Test]
public void Method_Clone()
{
- var fontpath = Path.Combine(FONT_PATH, "Montserrat-Regular.ttf");
- using var family = new FontFamily(fontpath);
+ var fontPath = Path.Combine(FONT_PATH, "Montserrat-Regular.ttf");
+ using var family = FontFamilyFactory.Create(fontPath);
using var font1 = new Font(family);
using var font2 = font1.Clone() as Font;
@@ -130,5 +130,4 @@ public void Extra_GetFontCount_Stream()
var count = Font.GetFontCount(stream);
Assert.That(count, Is.EqualTo(12));
}
-
}
diff --git a/test/Common/PointFUnitTest.cs b/test/Common/PointFUnitTest.cs
new file mode 100644
index 0000000..e2c0140
--- /dev/null
+++ b/test/Common/PointFUnitTest.cs
@@ -0,0 +1,188 @@
+namespace GeneXus.Drawing.Test;
+
+internal class PointFUnitTest
+{
+ [SetUp]
+ public void Setup()
+ {
+ }
+
+ [Test]
+ public void Constructor_None()
+ {
+ var point = new PointF();
+ Assert.Multiple(() =>
+ {
+ Assert.That(point.X, Is.EqualTo(0f));
+ Assert.That(point.Y, Is.EqualTo(0f));
+ });
+ }
+
+ [Test]
+ public void Constructor_Properties()
+ {
+ float x = 10f, y = 20f;
+ var point = new PointF { X = x, Y = y };
+ Assert.Multiple(() =>
+ {
+ Assert.That(point.X, Is.EqualTo(x));
+ Assert.That(point.Y, Is.EqualTo(y));
+ });
+ }
+
+ [Test]
+ public void Constructor_Float()
+ {
+ float x = 10f, y = -20f;
+ var point = new PointF(x, y);
+ Assert.Multiple(() =>
+ {
+ Assert.That(point.X, Is.EqualTo(x));
+ Assert.That(point.Y, Is.EqualTo(y));
+ });
+ }
+
+ [Test]
+ public void Constructor_Size()
+ {
+ var size = new SizeF(30f, 40f);
+ var point = new PointF(size);
+ Assert.Multiple(() =>
+ {
+ Assert.That(point.X, Is.EqualTo(size.Width));
+ Assert.That(point.Y, Is.EqualTo(size.Height));
+ });
+ }
+
+ [Test]
+ public void Constructor_Int()
+ {
+ int x = 100, y = 200;
+ int dw = unchecked(y << 16) | unchecked(x << 0);
+ var point = new PointF(dw);
+ Assert.Multiple(() =>
+ {
+ Assert.That(point.X, Is.EqualTo(x));
+ Assert.That(point.Y, Is.EqualTo(y));
+ });
+ }
+
+ [Test]
+ public void Operator_Equality()
+ {
+ var point1 = new PointF(10f, 20f);
+ var point2 = new PointF(10f, 20f);
+ Assert.That(point1 == point2, Is.True);
+ }
+
+ [Test]
+ public void Operator_Inequality()
+ {
+ var point1 = new PointF(10f, 20f);
+ var point2 = new PointF(20f, 30f);
+ Assert.That(point1 != point2, Is.True);
+ }
+
+ [Test]
+ public void Operator_Addition()
+ {
+ var point = new PointF(10f, 20f);
+ var size = new SizeF(30f, 40f);
+ var result = point + size;
+ Assert.Multiple(() =>
+ {
+ Assert.That(result.X, Is.EqualTo(40f));
+ Assert.That(result.Y, Is.EqualTo(60f));
+ });
+ }
+
+ [Test]
+ public void Operator_Subtraction()
+ {
+ var point = new PointF(50f, 60f);
+ var size = new SizeF(20f, 30f);
+ var result = point - size;
+ Assert.Multiple(() =>
+ {
+ Assert.That(result.X, Is.EqualTo(30f));
+ Assert.That(result.Y, Is.EqualTo(30f));
+ });
+ }
+
+ [Test]
+ public void Method_Equals()
+ {
+ var point1 = new PointF(10f, 20f);
+ var point2 = new PointF(10f, 20f);
+ Assert.That(point1.Equals(point2), Is.True);
+ }
+
+ [Test]
+ public void Method_GetHashCode()
+ {
+ var point1 = new PointF(10f, 20f);
+ var point2 = new PointF(10f, 20f);
+ Assert.That(point2.GetHashCode(), Is.EqualTo(point1.GetHashCode()));
+ }
+
+ [Test]
+ public void Method_Offset()
+ {
+ var point = new PointF(10f, 20f);
+ point.Offset(5f, -5f);
+ Assert.Multiple(() =>
+ {
+ Assert.That(point.X, Is.EqualTo(15f));
+ Assert.That(point.Y, Is.EqualTo(15f));
+ });
+ }
+
+ [Test]
+ public void Method_Offset_Point()
+ {
+ var point = new PointF(10f, 20f);
+ var offsetPoint = new PointF(5f, -5f);
+ point.Offset(offsetPoint);
+ Assert.Multiple(() =>
+ {
+ Assert.That(point.X, Is.EqualTo(15f));
+ Assert.That(point.Y, Is.EqualTo(15f));
+ });
+ }
+
+ [Test]
+ public void Static_Method_Ceiling()
+ {
+ var point = new PointF(10.4f, 20.6f);
+ var result = PointF.Ceiling(point);
+ Assert.Multiple(() =>
+ {
+ Assert.That(result.X, Is.EqualTo(11f));
+ Assert.That(result.Y, Is.EqualTo(21f));
+ });
+ }
+
+ [Test]
+ public void Static_Method_Truncate()
+ {
+ var point = new PointF(10.9f, 20.6f);
+ var result = PointF.Truncate(point);
+ Assert.Multiple(() =>
+ {
+ Assert.That(result.X, Is.EqualTo(10f));
+ Assert.That(result.Y, Is.EqualTo(20f));
+ });
+ }
+
+ [Test]
+ public void Static_Method_Round()
+ {
+ var point = new PointF(10.4f, 20.6f);
+ var result = PointF.Round(point);
+ Assert.Multiple(() =>
+ {
+ Assert.That(result.X, Is.EqualTo(10f));
+ Assert.That(result.Y, Is.EqualTo(21f));
+ });
+ }
+}
diff --git a/test/Common/PointUnitTest.cs b/test/Common/PointUnitTest.cs
index 79271fc..4e0fcec 100644
--- a/test/Common/PointUnitTest.cs
+++ b/test/Common/PointUnitTest.cs
@@ -21,8 +21,9 @@ public void Constructor_None()
[Test]
public void Constructor_Properties()
{
- float x = 10f, y = 20f;
- var point = new Point() { X = x, Y = y };
+ const int x = 10;
+ const int y = 20;
+ Point point = new() { X = x, Y = y };
Assert.Multiple(() =>
{
Assert.That(point.X, Is.EqualTo(x));
@@ -33,8 +34,9 @@ public void Constructor_Properties()
[Test]
public void Constructor_Float()
{
- float x = 10f, y = -20f;
- var point = new Point(x, y);
+ const int x = 10;
+ const int y = -20;
+ Point point = new(x, y);
Assert.Multiple(() =>
{
Assert.That(point.X, Is.EqualTo(x));
@@ -45,8 +47,8 @@ public void Constructor_Float()
[Test]
public void Constructor_Size()
{
- var size = new Size(30f, 40f);
- var point = new Point(size);
+ Size size = new(30, 40);
+ Point point = new(size);
Assert.Multiple(() =>
{
Assert.That(point.X, Is.EqualTo(size.Width));
@@ -57,9 +59,10 @@ public void Constructor_Size()
[Test]
public void Constructor_Int()
{
- int x = 100, y = 200;
- int dw = unchecked(y << 16) | unchecked(x << 0);
- var point = new Point(dw);
+ const int x = 100;
+ const int y = 200;
+ const int dw = y << 16 | x << 0;
+ Point point = new(dw);
Assert.Multiple(() =>
{
Assert.That(point.X, Is.EqualTo(x));
@@ -70,38 +73,38 @@ public void Constructor_Int()
[Test]
public void Operator_Equality()
{
- var point1 = new Point(10f, 20f);
- var point2 = new Point(10f, 20f);
+ Point point1 = new(10, 20);
+ Point point2 = new(10, 20);
Assert.That(point1 == point2, Is.True);
}
[Test]
public void Operator_Inequality()
{
- var point1 = new Point(10f, 20f);
- var point2 = new Point(20f, 30f);
- Assert.That(point1 != point2, Is.True);
+ Point point1 = new(10, 20);
+ Point point2 = new(20, 30);
+ Assert.That(point1, Is.Not.EqualTo(point2));
}
[Test]
public void Operator_Addition()
{
- var point = new Point(10f, 20f);
- var size = new Size(30f, 40f);
- var result = point + size;
+ Point point = new(10, 20);
+ Size size = new(30, 40);
+ Point result = point + size;
Assert.Multiple(() =>
{
- Assert.That(result.X, Is.EqualTo(40f));
- Assert.That(result.Y, Is.EqualTo(60f));
+ Assert.That(result.X, Is.EqualTo(40));
+ Assert.That(result.Y, Is.EqualTo(60));
});
}
[Test]
public void Operator_Subtraction()
{
- var point = new Point(50f, 60f);
- var size = new Size(20f, 30f);
- var result = point - size;
+ Point point = new(50, 60);
+ Size size = new(20, 30);
+ Point result = point - size;
Assert.Multiple(() =>
{
Assert.That(result.X, Is.EqualTo(30f));
@@ -112,24 +115,24 @@ public void Operator_Subtraction()
[Test]
public void Method_Equals()
{
- var point1 = new Point(10f, 20f);
- var point2 = new Point(10f, 20f);
+ Point point1 = new(10, 20);
+ Point point2 = new(10, 20);
Assert.That(point1.Equals(point2), Is.True);
}
[Test]
public void Method_GetHashCode()
{
- var point1 = new Point(10f, 20f);
- var point2 = new Point(10f, 20f);
+ Point point1 = new(10, 20);
+ Point point2 = new(10, 20);
Assert.That(point2.GetHashCode(), Is.EqualTo(point1.GetHashCode()));
}
[Test]
public void Method_Offset()
{
- var point = new Point(10f, 20f);
- point.Offset(5f, -5f);
+ Point point = new(10, 20);
+ point.Offset(5, -5);
Assert.Multiple(() =>
{
Assert.That(point.X, Is.EqualTo(15f));
@@ -140,8 +143,8 @@ public void Method_Offset()
[Test]
public void Method_Offset_Point()
{
- var point = new Point(10f, 20f);
- var offsetPoint = new Point(5f, -5f);
+ Point point = new(10, 20);
+ Point offsetPoint = new(5, -5);
point.Offset(offsetPoint);
Assert.Multiple(() =>
{
@@ -149,40 +152,4 @@ public void Method_Offset_Point()
Assert.That(point.Y, Is.EqualTo(15f));
});
}
-
- [Test]
- public void Static_Method_Ceiling()
- {
- var point = new Point(10.4f, 20.6f);
- var result = Point.Ceiling(point);
- Assert.Multiple(() =>
- {
- Assert.That(result.X, Is.EqualTo(11f));
- Assert.That(result.Y, Is.EqualTo(21f));
- });
- }
-
- [Test]
- public void Static_Method_Truncate()
- {
- var point = new Point(10.9f, 20.6f);
- var result = Point.Truncate(point);
- Assert.Multiple(() =>
- {
- Assert.That(result.X, Is.EqualTo(10f));
- Assert.That(result.Y, Is.EqualTo(20f));
- });
- }
-
- [Test]
- public void Static_Method_Round()
- {
- var point = new Point(10.4f, 20.6f);
- var result = Point.Round(point);
- Assert.Multiple(() =>
- {
- Assert.That(result.X, Is.EqualTo(10f));
- Assert.That(result.Y, Is.EqualTo(21f));
- });
- }
}
\ No newline at end of file
diff --git a/test/Common/RectangleFUnitTest.cs b/test/Common/RectangleFUnitTest.cs
new file mode 100644
index 0000000..e3a8c2a
--- /dev/null
+++ b/test/Common/RectangleFUnitTest.cs
@@ -0,0 +1,315 @@
+namespace GeneXus.Drawing.Test;
+
+internal class RectangleFUnitTest
+{
+ [SetUp]
+ public void Setup()
+ {
+ }
+
+ [Test]
+ public void Constructor_Float()
+ {
+ float x = 5f, y = 10f, w = 100f, h = 200f;
+ var rect = new RectangleF(x, y, w, h);
+ Assert.Multiple(() =>
+ {
+ Assert.That(rect.X, Is.EqualTo(x));
+ Assert.That(rect.Y, Is.EqualTo(y));
+ Assert.That(rect.Width, Is.EqualTo(w));
+ Assert.That(rect.Height, Is.EqualTo(h));
+ });
+ }
+
+ [Test]
+ public void Constructor_PointSize()
+ {
+ float x = 5f, y = 10f, w = 100f, h = 200f;
+ var point = new PointF(x, y);
+ var size = new SizeF(w, h);
+ var rect = new RectangleF(point, size);
+ Assert.Multiple(() =>
+ {
+ Assert.That(rect.X, Is.EqualTo(x));
+ Assert.That(rect.Y, Is.EqualTo(y));
+ Assert.That(rect.Width, Is.EqualTo(w));
+ Assert.That(rect.Height, Is.EqualTo(h));
+ });
+ }
+
+ [Test]
+ public void Constructor_FloatSize()
+ {
+ float x = 5f, y = 10f, w = 100f, h = 200f;
+ var size = new SizeF(w, h);
+ var rect = new RectangleF(x, y, size);
+ Assert.Multiple(() =>
+ {
+ Assert.That(rect.X, Is.EqualTo(x));
+ Assert.That(rect.Y, Is.EqualTo(y));
+ Assert.That(rect.Width, Is.EqualTo(w));
+ Assert.That(rect.Height, Is.EqualTo(h));
+ });
+ }
+
+ [Test]
+ public void Constructor_PointFloat()
+ {
+ float x = 5f, y = 10f, w = 100f, h = 200f;
+ var point = new PointF(x, y);
+ var rect = new RectangleF(point, w, h);
+ Assert.Multiple(() =>
+ {
+ Assert.That(rect.X, Is.EqualTo(x));
+ Assert.That(rect.Y, Is.EqualTo(y));
+ Assert.That(rect.Width, Is.EqualTo(w));
+ Assert.That(rect.Height, Is.EqualTo(h));
+ });
+ }
+
+ [Test]
+ public void Operator_Equality()
+ {
+ var rect1 = new RectangleF(5f, 10f, 100f, 200f);
+ var rect2 = new RectangleF(5f, 10f, 100f, 200f);
+ Assert.That(rect1 == rect2, Is.True);
+ }
+
+ [Test]
+ public void Operator_Inequality()
+ {
+ var rect1 = new RectangleF(5f, 10f, 100f, 200f);
+ var rect2 = new RectangleF(10f, 5f, 200f, 100f);
+ Assert.That(rect1 != rect2, Is.True);
+ }
+
+ [Test]
+ public void Property_X()
+ {
+ float x = 5f, y = 10f, w = 100f, h = 200f;
+ var rect = new RectangleF(x, y, w, h);
+ rect.X += 5;
+ Assert.Multiple(() =>
+ {
+ Assert.That(rect.X, Is.EqualTo(x + 5));
+ Assert.That(rect.Y, Is.EqualTo(y));
+ Assert.That(rect.Width, Is.EqualTo(w));
+ Assert.That(rect.Height, Is.EqualTo(h));
+ });
+ }
+
+ [Test]
+ public void Property_Y()
+ {
+ float x = 5f, y = 10f, w = 100f, h = 200f;
+ var rect = new RectangleF(x, y, w, h);
+ rect.Y += 5f;
+ Assert.Multiple(() =>
+ {
+ Assert.That(rect.X, Is.EqualTo(x));
+ Assert.That(rect.Y, Is.EqualTo(y + 5f));
+ Assert.That(rect.Width, Is.EqualTo(w));
+ Assert.That(rect.Height, Is.EqualTo(h));
+ });
+ }
+
+ [Test]
+ public void Property_Width()
+ {
+ float x = 5f, y = 10f, w = 100f, h = 200f;
+ var rect = new RectangleF(x, y, w, h);
+ rect.Width += 50;
+ Assert.Multiple(() =>
+ {
+ Assert.That(rect.X, Is.EqualTo(x));
+ Assert.That(rect.Y, Is.EqualTo(y));
+ Assert.That(rect.Width, Is.EqualTo(w + 50f));
+ Assert.That(rect.Height, Is.EqualTo(h));
+ });
+ }
+
+ [Test]
+ public void Property_Height()
+ {
+ float x = 5f, y = 10f, w = 100f, h = 200f;
+ var rect = new RectangleF(x, y, w, h);
+ rect.Height += 50f;
+ Assert.Multiple(() =>
+ {
+ Assert.That(rect.X, Is.EqualTo(x));
+ Assert.That(rect.Y, Is.EqualTo(y));
+ Assert.That(rect.Width, Is.EqualTo(w));
+ Assert.That(rect.Height, Is.EqualTo(h + 50f));
+ });
+ }
+
+ [Test]
+ public void Property_Left()
+ {
+ float x = 5f, y = 10f, w = 100f, h = 200f;
+ var rect = new RectangleF(x, y, w, h);
+ Assert.That(rect.Left, Is.EqualTo(x));
+ }
+
+ [Test]
+ public void Property_Right()
+ {
+ float x = 5f, y = 10f, w = 100f, h = 200f;
+ var rect = new RectangleF(x, y, w, h);
+ Assert.That(rect.Right, Is.EqualTo(x + w));
+ }
+
+ [Test]
+ public void Property_Top()
+ {
+ float x = 5f, y = 10f, w = 100f, h = 200f;
+ var rect = new RectangleF(x, y, w, h);
+ Assert.That(rect.Top, Is.EqualTo(y));
+ }
+
+ [Test]
+ public void Property_Bottom()
+ {
+ float x = 5f, y = 10f, w = 100f, h = 200f;
+ var rect = new RectangleF(x, y, w, h);
+ Assert.That(rect.Bottom, Is.EqualTo(y + h));
+ }
+
+ [Test]
+ public void Property_Location()
+ {
+ float x = 5f, y = 10f, w = 100f, h = 200f;
+ var rect = new RectangleF(x, y, w, h);
+ Assert.Multiple(() =>
+ {
+ Assert.That(rect.Location.X, Is.EqualTo(x));
+ Assert.That(rect.Location.Y, Is.EqualTo(y));
+ });
+ }
+
+ [Test]
+ public void Property_Size()
+ {
+ float x = 5f, y = 10f, w = 100f, h = 200f;
+ var rect = new RectangleF(x, y, w, h);
+ Assert.Multiple(() =>
+ {
+ Assert.That(rect.Size.Width, Is.EqualTo(w));
+ Assert.That(rect.Size.Height, Is.EqualTo(h));
+ });
+ }
+
+ [Test]
+ public void Method_FromLTRB()
+ {
+ float l = 5f, t = 10f, r = 105f, b = 210f;
+ var rect = RectangleF.FromLTRB(l, t, r, b);
+ Assert.Multiple(() =>
+ {
+ Assert.That(rect.Left, Is.EqualTo(l));
+ Assert.That(rect.Top, Is.EqualTo(t));
+ Assert.That(rect.Right, Is.EqualTo(r));
+ Assert.That(rect.Bottom, Is.EqualTo(b));
+ });
+ }
+
+ [Test]
+ public void Method_Contains()
+ {
+ var rect1 = new RectangleF(10f, 20f, 100f, 50f);
+ var rect2 = new RectangleF(20f, 30f, 50f, 20f);
+ Assert.That(rect1.Contains(rect2), Is.True);
+ }
+
+ [Test]
+ public void Method_Contains_Point()
+ {
+ var rect = new RectangleF(10f, 20f, 100f, 50f);
+ Assert.Multiple(() =>
+ {
+ Assert.That(rect.Contains(50f, 40f), Is.True);
+ Assert.That(rect.Contains(5f, 15f), Is.False);
+ });
+ }
+
+ [Test]
+ public void Method_IntersectWith()
+ {
+ var rect1 = new RectangleF(10f, 20f, 100f, 50f);
+ var rect2 = new RectangleF(50f, 30f, 80f, 70f);
+ Assert.That(rect1.IntersectsWith(rect2), Is.True);
+ }
+
+ [Test]
+ public void Method_Union()
+ {
+ var rect1 = new RectangleF(10f, 20f, 100f, 50f);
+ var rect2 = new RectangleF(50f, 30f, 80f, 70f);
+ rect1.Union(rect2);
+ Assert.Multiple(() =>
+ {
+ Assert.That(rect1.Left, Is.EqualTo(10f));
+ Assert.That(rect1.Top, Is.EqualTo(20f));
+ Assert.That(rect1.Right, Is.EqualTo(130f));
+ Assert.That(rect1.Bottom, Is.EqualTo(100f));
+ });
+ }
+
+ [Test]
+ public void Method_Inflate()
+ {
+ var rect = new RectangleF(10f, 20f, 100f, 50f);
+ rect.Inflate(5f, 10f);
+ Assert.Multiple(() =>
+ {
+ Assert.That(rect.Left, Is.EqualTo(5f));
+ Assert.That(rect.Top, Is.EqualTo(10f));
+ Assert.That(rect.Right, Is.EqualTo(115f));
+ Assert.That(rect.Bottom, Is.EqualTo(80f));
+ });
+ }
+
+ [Test]
+ public void Method_Inflate_Size()
+ {
+ var size = new SizeF(5f, 18);
+ var rect = new RectangleF(10f, 20f, 100f, 50f);
+ rect.Inflate(size);
+ Assert.Multiple(() =>
+ {
+ Assert.That(rect.Left, Is.EqualTo(5f));
+ Assert.That(rect.Top, Is.EqualTo(2f));
+ Assert.That(rect.Right, Is.EqualTo(115f));
+ Assert.That(rect.Bottom, Is.EqualTo(88f));
+ });
+ }
+
+ [Test]
+ public void Method_Offset()
+ {
+ var rect = new RectangleF(10f, 20f, 100f, 50f);
+ rect.Offset(5f, 10f);
+ Assert.Multiple(() =>
+ {
+ Assert.That(rect.Left, Is.EqualTo(15f));
+ Assert.That(rect.Top, Is.EqualTo(30f));
+ Assert.That(rect.Right, Is.EqualTo(115f));
+ Assert.That(rect.Bottom, Is.EqualTo(80f));
+ });
+ }
+
+ [Test]
+ public void Method_Offset_Point()
+ {
+ var point = new PointF(5f, 10f);
+ var rect = new RectangleF(10f, 20f, 100f, 50f);
+ rect.Offset(point);
+ Assert.Multiple(() =>
+ {
+ Assert.That(rect.Left, Is.EqualTo(15f));
+ Assert.That(rect.Top, Is.EqualTo(30f));
+ Assert.That(rect.Right, Is.EqualTo(115f));
+ Assert.That(rect.Bottom, Is.EqualTo(80f));
+ });
+ }
+}
diff --git a/test/Common/SizeFUnitTest.cs b/test/Common/SizeFUnitTest.cs
new file mode 100644
index 0000000..2442302
--- /dev/null
+++ b/test/Common/SizeFUnitTest.cs
@@ -0,0 +1,186 @@
+namespace GeneXus.Drawing.Test;
+
+internal class SizeFUnitTest
+{
+ [SetUp]
+ public void Setup()
+ {
+ }
+
+ [Test]
+ public void Constructor_None()
+ {
+ var size = new SizeF();
+ Assert.Multiple(() =>
+ {
+ Assert.That(size.Width, Is.EqualTo(0f));
+ Assert.That(size.Height, Is.EqualTo(0f));
+ });
+ }
+
+ [Test]
+ public void Constructor_Properties()
+ {
+ float w = 10f, h = 20f;
+ var size = new SizeF { Width = w, Height = h };
+ Assert.Multiple(() =>
+ {
+ Assert.That(size.Width, Is.EqualTo(w));
+ Assert.That(size.Height, Is.EqualTo(h));
+ });
+ }
+
+ [Test]
+ public void Constructor_Float()
+ {
+ float w = 10f, h = 20f;
+ var size = new SizeF(w, h);
+ Assert.Multiple(() =>
+ {
+ Assert.That(size.Width, Is.EqualTo(w));
+ Assert.That(size.Height, Is.EqualTo(h));
+ });
+ }
+
+ [Test]
+ public void Constructor_Point()
+ {
+ var point = new PointF(30f, 40f);
+ var size = new SizeF(point);
+ Assert.Multiple(() =>
+ {
+ Assert.That(size.Width, Is.EqualTo(point.X));
+ Assert.That(size.Height, Is.EqualTo(point.Y));
+ });
+ }
+
+ [Test]
+ public void Operator_Equality()
+ {
+ var size1 = new SizeF(10f, 20f);
+ var size2 = new SizeF(10f, 20f);
+ Assert.That(size1 == size2, Is.True);
+ }
+
+ [Test]
+ public void Operator_Inequality()
+ {
+ var size1 = new SizeF(10f, 20f);
+ var size2 = new SizeF(20f, 30f);
+ Assert.That(size1 != size2, Is.True);
+ }
+
+ [Test]
+ public void Operator_Addition()
+ {
+ var size1 = new SizeF(10f, 20f);
+ var size2 = new SizeF(5f, 10f);
+ var result = size1 + size2;
+ Assert.Multiple(() =>
+ {
+ Assert.That(result.Width, Is.EqualTo(15f));
+ Assert.That(result.Height, Is.EqualTo(30f));
+ });
+ }
+
+ [Test]
+ public void Operator_Subtraction()
+ {
+ var size1 = new SizeF(10f, 20f);
+ var size2 = new SizeF(5f, 10f);
+ var result = size1 - size2;
+ Assert.Multiple(() =>
+ {
+ Assert.That(result.Width, Is.EqualTo(5f));
+ Assert.That(result.Height, Is.EqualTo(10f));
+ });
+ }
+
+ [Test]
+ public void Operator_Multiplication_Right()
+ {
+ var size = new SizeF(10f, 20f);
+ var result = size * 2;
+ Assert.Multiple(() =>
+ {
+ Assert.That(result.Width, Is.EqualTo(20f));
+ Assert.That(result.Height, Is.EqualTo(40f));
+ });
+ }
+
+ [Test]
+ public void Operator_Multiplication_Left()
+ {
+ var size = new SizeF(10f, 20f);
+ var result = 2 * size;
+ Assert.Multiple(() =>
+ {
+ Assert.That(result.Width, Is.EqualTo(20f));
+ Assert.That(result.Height, Is.EqualTo(40f));
+ });
+ }
+
+ [Test]
+ public void Operator_Division()
+ {
+ var size1 = new SizeF(10f, 20f);
+ var result = size1 / 2;
+ Assert.Multiple(() =>
+ {
+ Assert.That(result.Width, Is.EqualTo(5f));
+ Assert.That(result.Height, Is.EqualTo(10f));
+ });
+ }
+
+ [Test]
+ public void Operator_Round()
+ {
+ var size1 = new SizeF(10.2f, 20.8f);
+ var result = SizeF.Round(size1);
+ Assert.Multiple(() =>
+ {
+ Assert.That(result.Width, Is.EqualTo(10f));
+ Assert.That(result.Height, Is.EqualTo(21f));
+ });
+ }
+
+ [Test]
+ public void Operator_Truncate()
+ {
+ var size1 = new SizeF(10.9f, 20.1f);
+ var result = SizeF.Truncate(size1);
+ Assert.Multiple(() =>
+ {
+ Assert.That(result.Width, Is.EqualTo(10f));
+ Assert.That(result.Height, Is.EqualTo(20f));
+ });
+ }
+
+ [Test]
+ public void Operator_Ceiling()
+ {
+ var size1 = new SizeF(10.2f, 20.6f);
+ var result = SizeF.Ceiling(size1);
+ Assert.Multiple(() =>
+ {
+ Assert.That(result.Width, Is.EqualTo(11f));
+ Assert.That(result.Height, Is.EqualTo(21f));
+ });
+ }
+
+ [Test]
+ public void Method_Equals()
+ {
+ var size1 = new SizeF(10f, 20f);
+ var size2 = new SizeF(10f, 20f);
+ Assert.That(size1.Equals(size2), Is.True);
+ }
+
+ [Test]
+ public void Method_GetHashCode()
+ {
+ var size1 = new SizeF(10f, 20f);
+ var size2 = new SizeF(10f, 20f);
+ Assert.That(size2.GetHashCode(), Is.EqualTo(size1.GetHashCode()));
+ }
+}
diff --git a/test/Common/SizeUnitTest.cs b/test/Common/SizeUnitTest.cs
index 77b3130..c0db5d0 100644
--- a/test/Common/SizeUnitTest.cs
+++ b/test/Common/SizeUnitTest.cs
@@ -21,8 +21,9 @@ public void Constructor_None()
[Test]
public void Constructor_Properties()
{
- float w = 10f, h = 20f;
- var size = new Size() { Width = w, Height = h };
+ const int w = 10;
+ const int h = 20;
+ Size size = new() { Width = w, Height = h };
Assert.Multiple(() =>
{
Assert.That(size.Width, Is.EqualTo(w));
@@ -33,8 +34,9 @@ public void Constructor_Properties()
[Test]
public void Constructor_Float()
{
- float w = 10f, h = 20f;
- var size = new Size(w, h);
+ const int w = 10;
+ const int h = 20;
+ Size size = new(w, h);
Assert.Multiple(() =>
{
Assert.That(size.Width, Is.EqualTo(w));
@@ -45,8 +47,8 @@ public void Constructor_Float()
[Test]
public void Constructor_Point()
{
- var point = new Point(30f, 40f);
- var size = new Size(point);
+ Point point = new(30, 40);
+ Size size = new(point);
Assert.Multiple(() =>
{
Assert.That(size.Width, Is.EqualTo(point.X));
@@ -57,130 +59,94 @@ public void Constructor_Point()
[Test]
public void Operator_Equality()
{
- var size1 = new Size(10f, 20f);
- var size2 = new Size(10f, 20f);
+ Size size1 = new(10, 20);
+ Size size2 = new(10, 20);
Assert.That(size1 == size2, Is.True);
}
[Test]
public void Operator_Inequality()
{
- var size1 = new Size(10f, 20f);
- var size2 = new Size(20f, 30f);
+ Size size1 = new(10, 20);
+ Size size2 = new(20, 30);
Assert.That(size1 != size2, Is.True);
}
[Test]
public void Operator_Addition()
{
- var size1 = new Size(10f, 20f);
- var size2 = new Size(5f, 10f);
- var result = size1 + size2;
+ Size size1 = new(10, 20);
+ Size size2 = new(5, 10);
+ Size result = size1 + size2;
Assert.Multiple(() =>
{
- Assert.That(result.Width, Is.EqualTo(15f));
- Assert.That(result.Height, Is.EqualTo(30f));
+ Assert.That(result.Width, Is.EqualTo(15));
+ Assert.That(result.Height, Is.EqualTo(30));
});
}
[Test]
public void Operator_Substraction()
{
- var size1 = new Size(10f, 20f);
- var size2 = new Size(5f, 10f);
- var result = size1 - size2;
+ Size size1 = new(10, 20);
+ Size size2 = new(5, 10);
+ Size result = size1 - size2;
Assert.Multiple(() =>
{
- Assert.That(result.Width, Is.EqualTo(5f));
- Assert.That(result.Height, Is.EqualTo(10f));
+ Assert.That(result.Width, Is.EqualTo(5));
+ Assert.That(result.Height, Is.EqualTo(10));
});
}
[Test]
public void Operator_Multiplication_Right()
{
- var size = new Size(10f, 20f);
- var result = size * 2;
+ Size size = new(10, 20);
+ Size result = size * 2;
Assert.Multiple(() =>
{
- Assert.That(result.Width, Is.EqualTo(20f));
- Assert.That(result.Height, Is.EqualTo(40f));
+ Assert.That(result.Width, Is.EqualTo(20));
+ Assert.That(result.Height, Is.EqualTo(40));
});
}
[Test]
public void Operator_Multiplication_Left()
{
- var size = new Size(10f, 20f);
- var result = 2 * size;
+ Size size = new(10, 20);
+ Size result = 2 * size;
Assert.Multiple(() =>
{
- Assert.That(result.Width, Is.EqualTo(20f));
- Assert.That(result.Height, Is.EqualTo(40f));
+ Assert.That(result.Width, Is.EqualTo(20));
+ Assert.That(result.Height, Is.EqualTo(40));
});
}
[Test]
public void Operator_Division()
{
- var size1 = new Size(10f, 20f);
- var result = size1 / 2;
+ Size size1 = new(10, 20);
+ Size result = size1 / 2;
Assert.Multiple(() =>
{
- Assert.That(result.Width, Is.EqualTo(5f));
- Assert.That(result.Height, Is.EqualTo(10f));
- });
- }
-
- [Test]
- public void Operator_Round()
- {
- var size1 = new Size(10.2f, 20.8f);
- var result = Size.Round(size1);
- Assert.Multiple(() =>
- {
- Assert.That(result.Width, Is.EqualTo(10f));
- Assert.That(result.Height, Is.EqualTo(21f));
- });
- }
-
- [Test]
- public void Operator_Truncate()
- {
- var size1 = new Size(10.9f, 20.1f);
- var result = Size.Truncate(size1);
- Assert.Multiple(() =>
- {
- Assert.That(result.Width, Is.EqualTo(10f));
- Assert.That(result.Height, Is.EqualTo(20f));
- });
- }
-
- [Test]
- public void Operator_Ceiling()
- {
- var size1 = new Size(10.2f, 20.6f);
- var result = Size.Ceiling(size1);
- Assert.Multiple(() =>
- {
- Assert.That(result.Width, Is.EqualTo(11f));
- Assert.That(result.Height, Is.EqualTo(21f));
+ Assert.That(result.Width, Is.EqualTo(5));
+ Assert.That(result.Height, Is.EqualTo(10));
});
}
[Test]
public void Method_Equals()
{
- var size1 = new Size(10f, 20f);
- var size2 = new Size(10f, 20f);
- Assert.That(size1.Equals(size2), Is.True);
+ Size size1 = new(10, 20);
+ Size size2 = new(10, 20);
+ Assert.That(size1, Is.EqualTo(size2));
}
[Test]
public void Method_GetHashCode()
{
- var size1 = new Size(10f, 20f);
- var size2 = new Size(10f, 20f);
+ Size size1 = new(10, 20);
+ Size size2 = new(10, 20);
Assert.That(size2.GetHashCode(), Is.EqualTo(size1.GetHashCode()));
}
}