Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
<PackageVersion Include="SpaceWizards.Sdl" Version="1.0.0" />
<PackageVersion Include="SpaceWizards.SharpFont" Version="1.1.0" />
<PackageVersion Include="SpaceWizards.Sodium" Version="0.2.1" />
<PackageVersion Include="SpaceWizards.Fontconfig.Interop" Version="1.0.0" />
<PackageVersion Include="libsodium" Version="1.0.20.1" />
<PackageVersion Include="System.Management" Version="9.0.8" />
<PackageVersion Include="TerraFX.Interop.Windows" Version="10.0.26100.1" />
Expand Down
3 changes: 3 additions & 0 deletions RELEASE-NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,9 @@ END TEMPLATE-->
* Sandbox:
* Exposed `System.Reflection.Metadata.MetadataUpdateHandlerAttribute`.
* Exposed more overloads on `StringBuilder`.
* The engine can now load system fonts.
* At the moment only available on Windows.
* See `ISystemFontManager` for API.

### Bugfixes

Expand Down
5 changes: 5 additions & 0 deletions Robust.Client/ClientIoC.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using Robust.Client.GameStates;
using Robust.Client.Graphics;
using Robust.Client.Graphics.Clyde;
using Robust.Client.Graphics.FontManagement;
using Robust.Client.HWId;
using Robust.Client.Input;
using Robust.Client.Localization;
Expand Down Expand Up @@ -121,6 +122,8 @@ public static void RegisterIoC(GameController.DisplayMode mode, IDependencyColle
deps.Register<IInputManager, InputManager>();
deps.Register<IFileDialogManager, DummyFileDialogManager>();
deps.Register<IUriOpener, UriOpenerDummy>();
deps.Register<ISystemFontManager, SystemFontManagerFallback>();
deps.Register<ISystemFontManagerInternal, SystemFontManagerFallback>();
break;
case GameController.DisplayMode.Clyde:
deps.Register<IClyde, Clyde>();
Expand All @@ -131,6 +134,8 @@ public static void RegisterIoC(GameController.DisplayMode mode, IDependencyColle
deps.Register<IInputManager, ClydeInputManager>();
deps.Register<IFileDialogManager, FileDialogManager>();
deps.Register<IUriOpener, UriOpener>();
deps.Register<ISystemFontManager, SystemFontManager>();
deps.Register<ISystemFontManagerInternal, SystemFontManager>();
break;
default:
throw new ArgumentOutOfRangeException();
Expand Down
2 changes: 2 additions & 0 deletions Robust.Client/GameController/GameController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ internal sealed partial class GameController : IGameControllerInternal
[Dependency] private readonly IReflectionManager _reflectionManager = default!;
[Dependency] private readonly IReloadManager _reload = default!;
[Dependency] private readonly ILocalizationManager _loc = default!;
[Dependency] private readonly ISystemFontManagerInternal _systemFontManager = default!;

private IWebViewManagerHook? _webViewHook;

Expand Down Expand Up @@ -143,6 +144,7 @@ internal bool StartupContinue(DisplayMode displayMode)
_taskManager.Initialize();
_parallelMgr.Initialize();
_fontManager.SetFontDpi((uint)_configurationManager.GetCVar(CVars.DisplayFontDpi));
_systemFontManager.Initialize();

// Load optional Robust modules.
LoadOptionalRobustModules(displayMode, _resourceManifest!);
Expand Down
76 changes: 76 additions & 0 deletions Robust.Client/Graphics/Font.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,12 @@ public VectorFont(FontResource res, int size)
Handle = IoCManager.Resolve<IFontManagerInternal>().MakeInstance(res.FontFaceHandle, size);
}

internal VectorFont(IFontInstanceHandle handle, int size)
{
Size = size;
Handle = handle;
}

public override int GetAscent(float scale) => Handle.GetAscent(scale);
public override int GetHeight(float scale) => Handle.GetHeight(scale);
public override int GetDescent(float scale) => Handle.GetDescent(scale);
Expand Down Expand Up @@ -222,4 +228,74 @@ public override float DrawChar(DrawingHandleBase handle, Rune rune, Vector2 base
return null;
}
}

/// <summary>
/// Possible values for font weights. Larger values have thicker font strokes.
/// </summary>
/// <remarks>
/// <para>
/// These values are based on the <c>usWeightClass</c> property of the OpenType specification:
/// https://learn.microsoft.com/en-us/typography/opentype/spec/os2#usweightclass
/// </para>
/// </remarks>
/// <seealso cref="ISystemFontFace.Weight"/>
public enum FontWeight : ushort
{
Thin = 100,
ExtraLight = 200,
UltraLight = ExtraLight,
Light = 300,
SemiLight = 350,
Normal = 400,
Regular = Normal,
Medium = 500,
SemiBold = 600,
DemiBold = SemiBold,
Bold = 700,
ExtraBold = 800,
UltraBold = ExtraBold,
Black = 900,
Heavy = Black,
ExtraBlack = 950,
UltraBlack = ExtraBlack,
}

/// <summary>
/// Possible slant values for fonts.
/// </summary>
/// <seealso cref="ISystemFontFace.Slant"/>
public enum FontSlant : byte
{
// NOTE: Enum values correspond to DWRITE_FONT_STYLE.
Normal = 0,
Oblique = 1,

// FUN FACT: they're called "italics" because they look like the Leaning Tower of Pisa.
// Don't fact-check that.
Italic = 2
}

/// <summary>
/// Possible values for font widths. Larger values are proportionally wider.
/// </summary>
/// <remarks>
/// <para>
/// These values are based on the <c>usWidthClass</c> property of the OpenType specification:
/// https://learn.microsoft.com/en-us/typography/opentype/spec/os2#uswidthclass
/// </para>
/// </remarks>
/// <seealso cref="ISystemFontFace.Width"/>
public enum FontWidth : ushort
{
UltraCondensed = 1,
ExtraCondensed = 2,
Condensed = 3,
SemiCondensed = 4,
Normal = 5,
Medium = Normal,
SemiExpanded = 6,
Expanded = 7,
ExtraExpanded = 8,
UltraExpanded = 9,
}
}
15 changes: 15 additions & 0 deletions Robust.Client/Graphics/FontManagement/SystemFontDebug.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using Robust.Shared.Console;

namespace Robust.Client.Graphics.FontManagement;

internal sealed class SystemFontDebugCommand : IConsoleCommand
{
public string Command => "system_font_debug";
public string Description => "";
public string Help => "";

public void Execute(IConsoleShell shell, string argStr, string[] args)
{
new SystemFontDebugWindow().OpenCentered();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<DefaultWindow xmlns="https://spacestation14.io"
Title="System font debug">
<SplitContainer Orientation="Horizontal" MinSize="800 600">
<ScrollContainer HScrollEnabled="False">
<BoxContainer Name="SelectorContainer" Orientation="Vertical" />
</ScrollContainer>
<ScrollContainer HScrollEnabled="False">
<BoxContainer Orientation="Vertical">
<Label Name="FamilyLabel" />
<BoxContainer Orientation="Vertical" Name="FaceContainer" />
</BoxContainer>
</ScrollContainer>
</SplitContainer>
</DefaultWindow>
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
using System.Linq;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Utility;

namespace Robust.Client.Graphics.FontManagement;

[GenerateTypedNameReferences]
internal sealed partial class SystemFontDebugWindow : DefaultWindow
{
private static readonly int[] ExampleFontSizes = [8, 12, 16, 24, 36];
private const string ExampleString = "The quick brown fox jumps over the lazy dog";

[Dependency] private readonly ISystemFontManager _systemFontManager = default!;

public SystemFontDebugWindow()
{
IoCManager.InjectDependencies(this);
RobustXamlLoader.Load(this);

var buttonGroup = new ButtonGroup();

foreach (var group in _systemFontManager.SystemFontFaces.GroupBy(k => k.FamilyName).OrderBy(k => k.Key))
{
var fonts = group.ToArray();
SelectorContainer.AddChild(new Selector(this, buttonGroup, group.Key, fonts));
}
}

private void SelectFontFamily(ISystemFontFace[] fonts)
{
FamilyLabel.Text = fonts[0].FamilyName;

FaceContainer.RemoveAllChildren();

foreach (var font in fonts)
{
var exampleContainer = new BoxContainer
{
Orientation = BoxContainer.LayoutOrientation.Vertical,
Margin = new Thickness(8)
};

foreach (var size in ExampleFontSizes)
{
var fontInstance = font.Load(size);

var richTextLabel = new RichTextLabel
{
Stylesheet = new Stylesheet([
StylesheetHelpers.Element<RichTextLabel>().Prop("font", fontInstance)
]),
};
richTextLabel.SetMessage(FormattedMessage.FromUnformatted(ExampleString));
exampleContainer.AddChild(richTextLabel);
}

FaceContainer.AddChild(new BoxContainer
{
Orientation = BoxContainer.LayoutOrientation.Vertical,
Children =
{
new RichTextLabel
{
Text = $"""
{font.FullName}
Family: "{font.FamilyName}", face: "{font.FaceName}", PostScript = "{font.PostscriptName}"
Weight: {font.Weight} ({(int) font.Weight}), slant: {font.Slant} ({(int) font.Slant}), width: {font.Width} ({(int) font.Width})
""",
},
exampleContainer
},
Margin = new Thickness(0, 0, 0, 8)
});
}
}

private sealed class Selector : Control
{
public Selector(SystemFontDebugWindow window, ButtonGroup group, string family, ISystemFontFace[] fonts)
{
var button = new Button
{
Text = family,
Group = group,
ToggleMode = true
};
AddChild(button);

button.OnPressed += _ => window.SelectFontFamily(fonts);
}
}
}
Loading
Loading