forked from SixLabors/Fonts
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathFont.cs
More file actions
322 lines (288 loc) · 13 KB
/
Copy pathFont.cs
File metadata and controls
322 lines (288 loc) · 13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
// Copyright (c) Six Labors.
// Licensed under the Six Labors Split License.
using System.Diagnostics.CodeAnalysis;
using System.Numerics;
using SixLabors.Fonts.Unicode;
namespace SixLabors.Fonts;
/// <summary>
/// Defines a particular format for text, including font face, size, and style attributes.
/// This class cannot be inherited.
/// </summary>
public sealed class Font
{
private readonly Lazy<FontMetrics?> metrics;
private readonly Lazy<string> fontName;
/// <summary>
/// Initializes a new instance of the <see cref="Font"/> class.
/// </summary>
/// <param name="family">The font family.</param>
/// <param name="size">The size of the font in PT units.</param>
public Font(FontFamily family, float size)
: this(family, size, FontStyle.Regular)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Font"/> class.
/// </summary>
/// <param name="family">The font family.</param>
/// <param name="size">The size of the font in PT units.</param>
/// <param name="style">The font style.</param>
public Font(FontFamily family, float size, FontStyle style)
{
if (family == default)
{
throw new ArgumentException("Cannot use the default value type instance to create a font.", nameof(family));
}
this.Family = family;
this.RequestedStyle = style;
this.Size = size;
this.metrics = new Lazy<FontMetrics?>(this.LoadInstanceInternal, true);
this.fontName = new Lazy<string>(this.LoadFontName, true);
}
/// <summary>
/// Initializes a new instance of the <see cref="Font"/> class.
/// </summary>
/// <param name="prototype">The prototype.</param>
/// <param name="style">The font style.</param>
public Font(Font prototype, FontStyle style)
: this(prototype?.Family ?? throw new ArgumentNullException(nameof(prototype)), prototype.Size, style)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Font"/> class.
/// </summary>
/// <param name="prototype">The prototype.</param>
/// <param name="size">The size of the font in PT units.</param>
/// <param name="style">The font style.</param>
public Font(Font prototype, float size, FontStyle style)
: this(prototype?.Family ?? throw new ArgumentNullException(nameof(prototype)), size, style)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="Font"/> class.
/// </summary>
/// <param name="prototype">The prototype.</param>
/// <param name="size">The size of the font in PT units.</param>
public Font(Font prototype, float size)
: this(prototype.Family, size, prototype.RequestedStyle)
{
}
/// <summary>
/// Gets the family.
/// </summary>
public FontFamily Family { get; }
/// <summary>
/// Gets the name.
/// </summary>
public string Name => this.fontName.Value;
/// <summary>
/// Gets the size of the font in PT units.
/// </summary>
public float Size { get; }
/// <summary>
/// Gets the font metrics.
/// </summary>
/// <exception cref="FontException">Font instance not found.</exception>
public FontMetrics FontMetrics => this.metrics.Value ?? throw new FontException("Font instance not found.");
/// <summary>
/// Gets a value indicating whether this <see cref="Font"/> is bold.
/// </summary>
public bool IsBold => (this.FontMetrics.Description.Style & FontStyle.Bold) == FontStyle.Bold;
/// <summary>
/// Gets a value indicating whether this <see cref="Font"/> is italic.
/// </summary>
public bool IsItalic => (this.FontMetrics.Description.Style & FontStyle.Italic) == FontStyle.Italic;
/// <summary>
/// Gets the requested style.
/// </summary>
internal FontStyle RequestedStyle { get; }
/// <summary>
/// Gets the filesystem path to the font family source.
/// </summary>
/// <param name="path">
/// When this method returns, contains the filesystem path to the font family source,
/// if the path exists; otherwise, the default value for the type of the path parameter.
/// This parameter is passed uninitialized.
/// </param>
/// <returns>
/// <see langword="true" /> if the <see cref="Font" /> was created via a filesystem path; otherwise, <see langword="false" />.
/// </returns>
public bool TryGetPath([NotNullWhen(true)] out string? path)
{
if (this == default)
{
FontsThrowHelper.ThrowDefaultInstance();
}
if (this.FontMetrics is FileFontMetrics fileMetrics)
{
path = fileMetrics.Path;
return true;
}
path = null;
return false;
}
/// <summary>
/// Gets the glyphs for the given codepoint.
/// </summary>
/// <param name="codePoint">The code point of the character.</param>
/// <param name="glyphs">
/// When this method returns, contains the glyphs for the given codepoint if the glyphs
/// are found; otherwise the default value. This parameter is passed uninitialized.
/// </param>
/// <returns>
/// <see langword="true"/> if the face contains glyphs for the specified codepoint; otherwise, <see langword="false"/>.
/// </returns>
public bool TryGetGlyphs(CodePoint codePoint, [NotNullWhen(true)] out IReadOnlyList<Glyph>? glyphs)
=> this.TryGetGlyphs(codePoint, TextAttributes.None, ColorFontSupport.None, out glyphs);
/// <summary>
/// Gets the glyphs for the given codepoint.
/// </summary>
/// <param name="codePoint">The code point of the character.</param>
/// <param name="support">Options for enabling color font support during layout and rendering.</param>
/// <param name="glyphs">
/// When this method returns, contains the glyphs for the given codepoint and color support if the glyphs
/// are found; otherwise the default value. This parameter is passed uninitialized.
/// </param>
/// <returns>
/// <see langword="true"/> if the face contains glyphs for the specified codepoint; otherwise, <see langword="false"/>.
/// </returns>
public bool TryGetGlyphs(CodePoint codePoint, ColorFontSupport support, [NotNullWhen(true)] out IReadOnlyList<Glyph>? glyphs)
=> this.TryGetGlyphs(codePoint, TextAttributes.None, support, out glyphs);
/// <summary>
/// Gets the glyphs for the given codepoint.
/// </summary>
/// <param name="codePoint">The code point of the character.</param>
/// <param name="textAttributes">The text attributes to apply to the glyphs.</param>
/// <param name="support">Options for enabling color font support during layout and rendering.</param>
/// <param name="glyphs">
/// When this method returns, contains the glyphs for the given codepoint, attributes, and color support if the glyphs
/// are found; otherwise the default value. This parameter is passed uninitialized.
/// </param>
/// <returns>
/// <see langword="true"/> if the face contains glyphs for the specified codepoint; otherwise, <see langword="false"/>.
/// </returns>
public bool TryGetGlyphs(
CodePoint codePoint,
TextAttributes textAttributes,
ColorFontSupport support,
[NotNullWhen(true)] out IReadOnlyList<Glyph>? glyphs)
=> this.TryGetGlyphs(codePoint, textAttributes, TextDecorations.None, LayoutMode.HorizontalTopBottom, support, out glyphs);
/// <summary>
/// Gets the glyphs for the given codepoint.
/// </summary>
/// <param name="codePoint">The code point of the character.</param>
/// <param name="textAttributes">The text attributes to apply to the glyphs.</param>
/// <param name="layoutMode">The layout mode to apply to the glyphs.</param>
/// <param name="support">Options for enabling color font support during layout and rendering.</param>
/// <param name="glyphs">
/// When this method returns, contains the glyphs for the given codepoint, attributes, and color support if the glyphs
/// are found; otherwise the default value. This parameter is passed uninitialized.
/// </param>
/// <returns>
/// <see langword="true"/> if the face contains glyphs for the specified codepoint; otherwise, <see langword="false"/>.
/// </returns>
public bool TryGetGlyphs(
CodePoint codePoint,
TextAttributes textAttributes,
LayoutMode layoutMode,
ColorFontSupport support,
[NotNullWhen(true)] out IReadOnlyList<Glyph>? glyphs)
=> this.TryGetGlyphs(codePoint, textAttributes, TextDecorations.None, layoutMode, support, out glyphs);
/// <summary>
/// Gets the glyphs for the given codepoint.
/// </summary>
/// <param name="codePoint">The code point of the character.</param>
/// <param name="textAttributes">The text attributes to apply to the glyphs.</param>
/// <param name="textDecorations">The text decorations to apply to the glyphs.</param>
/// <param name="layoutMode">The layout mode to apply to the glyphs.</param>
/// <param name="support">Options for enabling color font support during layout and rendering.</param>
/// <param name="glyphs">
/// When this method returns, contains the glyphs for the given codepoint, attributes, and color support if the glyphs
/// are found; otherwise the default value. This parameter is passed uninitialized.
/// </param>
/// <returns>
/// <see langword="true"/> if the face contains glyphs for the specified codepoint; otherwise, <see langword="false"/>.
/// </returns>
public bool TryGetGlyphs(
CodePoint codePoint,
TextAttributes textAttributes,
TextDecorations textDecorations,
LayoutMode layoutMode,
ColorFontSupport support,
[NotNullWhen(true)] out IReadOnlyList<Glyph>? glyphs)
{
TextRun textRun = new() { Start = 0, End = 1, Font = this, TextAttributes = textAttributes, TextDecorations = textDecorations };
if (this.FontMetrics.TryGetGlyphMetrics(codePoint, textAttributes, textDecorations, layoutMode, support, out IReadOnlyList<GlyphMetrics>? metrics))
{
List<Glyph> g = new();
foreach (GlyphMetrics metric in metrics)
{
g.Add(new Glyph(metric.CloneForRendering(textRun), this.Size));
}
glyphs = g;
return true;
}
glyphs = default;
return false;
}
/// <summary>
/// Gets the amount, in px units, the <paramref name="current"/> glyph should be offset if it is followed by
/// the <paramref name="next"/> glyph.
/// </summary>
/// <param name="current">The current glyph.</param>
/// <param name="next">The next glyph.</param>
/// <param name="dpi">The DPI (Dots Per Inch) to render/measure the kerning offset at.</param>
/// <param name="vector">
/// When this method returns, contains the offset, in font units, that should be applied to the
/// <paramref name="current"/> glyph, if the offset is found; otherwise the default vector value.
/// This parameter is passed uninitialized.
/// </param>
/// <returns>
/// <see langword="true"/> if the face contains and offset for the glyph combination; otherwise, <see langword="false"/>.
/// </returns>
public bool TryGetKerningOffset(Glyph current, Glyph next, float dpi, out Vector2 vector)
{
if (this.FontMetrics.TryGetKerningOffset(current.GlyphMetrics.GlyphId, next.GlyphMetrics.GlyphId, out vector))
{
// Scale the result
Vector2 scale = new Vector2(this.Size * dpi) / next.GlyphMetrics.ScaleFactor;
vector *= scale;
return true;
}
return false;
}
private string LoadFontName()
=> this.metrics.Value?.Description.FontName(this.Family.Culture) ?? string.Empty;
private FontMetrics? LoadInstanceInternal()
{
if (this.Family.TryGetMetrics(this.RequestedStyle, out FontMetrics? metrics))
{
return metrics;
}
if ((this.RequestedStyle & FontStyle.Italic) == FontStyle.Italic)
{
// Can't find style requested and they want one that's at least partial italic.
// Try the regular italic.
if (this.Family.TryGetMetrics(FontStyle.Italic, out metrics))
{
return metrics;
}
}
if ((this.RequestedStyle & FontStyle.Bold) == FontStyle.Bold)
{
// Can't find style requested and they want one that's at least partial bold.
// Try the regular bold.
if (this.Family.TryGetMetrics(FontStyle.Bold, out metrics))
{
return metrics;
}
}
// Can't find style requested so let's just try returning the default.
IEnumerable<FontStyle>? styles = this.Family.GetAvailableStyles();
FontStyle defaultStyle = styles.Contains(FontStyle.Regular)
? FontStyle.Regular
: styles.First();
this.Family.TryGetMetrics(defaultStyle, out metrics);
return metrics;
}
}