Arabic icon drawing support
This commit is contained in:
Valentin 2020-10-10 13:52:36 +02:00 committed by GitHub
commit 1b93086307
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 145 additions and 45 deletions

View File

@ -45,7 +45,7 @@ namespace FModel.Creator.Bases
if (Description.Equals(DisplayName)) Description = string.Empty;
if (!string.IsNullOrEmpty(Description))
{
Height += (int)descriptionPaint.TextSize * Helper.SplitLines(Description, descriptionPaint, Width - Margin).Length;
Height += (int)descriptionPaint.TextSize * Helper.SplitLines(Description, descriptionPaint, Width - Margin).Count;
Height += (int)descriptionPaint.TextSize;
}
}
@ -78,7 +78,7 @@ namespace FModel.Creator.Bases
s.Description = Text.GetTextPropertyBase(aDescription) ?? "";
if (!string.IsNullOrEmpty(Description))
{
s.Height += (int)descriptionPaint.TextSize * Helper.SplitLines(s.Description, descriptionPaint, Width - Margin).Length;
s.Height += (int)descriptionPaint.TextSize * Helper.SplitLines(s.Description, descriptionPaint, Width - Margin).Count;
s.Height += (int)descriptionPaint.TextSize * 3;
}
}

View File

@ -40,7 +40,7 @@ namespace FModel.Creator.Bases
OptionDescription = Text.GetTextPropertyBase(optionDescription);
if (!string.IsNullOrEmpty(OptionDescription))
{
Height += (int)descriptionPaint.TextSize * Helper.SplitLines(OptionDescription, descriptionPaint, Width - Margin).Length;
Height += (int)descriptionPaint.TextSize * Helper.SplitLines(OptionDescription, descriptionPaint, Width - Margin).Count;
Height += (int)descriptionPaint.TextSize;
}
}

View File

@ -4,6 +4,10 @@ using System;
using System.Collections.Generic;
using System.Text;
using FModel.Properties;
using SkiaSharp.HarfBuzz;
namespace FModel.Creator.Texts
{
static class Helper
@ -19,33 +23,56 @@ namespace FModel.Creator.Texts
public static void DrawCenteredMultilineText(SKCanvas canvas, string text, int maxLineCount, int size, int margin, ETextSide side, SKRect area, SKPaint paint)
{
float lineHeight = paint.TextSize * 1.2f;
Line[] lines = SplitLines(text, paint, area.Width - margin);
List<Line> lines = SplitLines(text, paint, area.Width - margin);
if (lines == null)
return;
if (lines.Length <= maxLineCount)
maxLineCount = lines.Length;
if (lines.Count <= maxLineCount)
maxLineCount = lines.Count;
float height = maxLineCount * lineHeight;
float y = area.MidY - height / 2;
SKShaper shaper = (ELanguage)Settings.Default.AssetsLanguage == ELanguage.Arabic ? new SKShaper(paint.Typeface) : null;
for (int i = 0; i < maxLineCount; i++)
{
Line line = lines[i];
y += lineHeight;
float x = side switch
{
ETextSide.Center => area.MidX - lines[i].Width / 2,
ETextSide.Right => size - margin - lines[i].Width,
ETextSide.Center => area.MidX - line.Width / 2,
ETextSide.Right => size - margin - line.Width,
ETextSide.Left => margin,
_ => area.MidX - lines[i].Width / 2
_ => area.MidX - line.Width / 2
};
canvas.DrawText(lines[i].Value.TrimEnd(), x, y, paint);
string lineText = line.Value.TrimEnd();
if (shaper == null)
{
canvas.DrawText(lineText, x, y, paint);
}
else
{
if (side == ETextSide.Center)
{
SKShaper.Result shapedText = shaper.Shape(lineText, paint);
float shapedTextWidth = shapedText.Points[^1].X + paint.TextSize / 2f;
canvas.DrawShapedText(shaper, lineText, (area.Width - shapedTextWidth) / 2f, y, paint);
}
else
{
canvas.DrawShapedText(shaper, lineText, x, y, paint);
}
}
}
}
public static void DrawMultilineText(SKCanvas canvas, string text, int size, int margin, ETextSide side, SKRect area, SKPaint paint, out int yPos)
{
float lineHeight = paint.TextSize * 1.2f;
Line[] lines = SplitLines(text, paint, area.Width);
List<Line> lines = SplitLines(text, paint, area.Width);
if (lines == null)
{
yPos = (int)area.Top;
@ -53,22 +80,45 @@ namespace FModel.Creator.Texts
}
float y = area.Top;
for (int i = 0; i < lines.Length; i++)
SKShaper shaper = (ELanguage)Settings.Default.AssetsLanguage == ELanguage.Arabic ? new SKShaper(paint.Typeface) : null;
for (int i = 0; i < lines.Count; i++)
{
var line = lines[i];
float x = side switch
{
ETextSide.Center => area.MidX - lines[i].Width / 2,
ETextSide.Right => size - margin - lines[i].Width,
ETextSide.Center => area.MidX - line.Width / 2,
ETextSide.Right => size - margin - line.Width,
ETextSide.Left => area.Left,
_ => area.MidX - lines[i].Width / 2
_ => area.MidX - line.Width / 2
};
canvas.DrawText(lines[i].Value.TrimEnd(), x, y, paint);
string lineText = line.Value.TrimEnd();
if (shaper == null)
{
canvas.DrawText(lineText, x, y, paint);
}
else
{
if (side == ETextSide.Center)
{
SKShaper.Result shapedText = shaper.Shape(lineText, paint);
float shapedTextWidth = shapedText.Points[^1].X + paint.TextSize / 2f;
canvas.DrawShapedText(shaper, lineText, (area.Width - shapedTextWidth) / 2f, y, paint);
}
else
{
canvas.DrawShapedText(shaper, lineText, x, y, paint);
}
}
y += lineHeight;
}
yPos = (int)area.Top + ((int)lineHeight * lines.Length);
yPos = (int)area.Top + ((int)lineHeight * lines.Count);
}
public static Line[] SplitLines(string text, SKPaint paint, float maxWidth)
public static List<Line> SplitLines(string text, SKPaint paint, float maxWidth)
{
if (string.IsNullOrEmpty(text))
return null;
@ -84,7 +134,7 @@ namespace FModel.Creator.Texts
float width = 0;
var lineResult = new StringBuilder();
string[] words = lines[i].Split(' ', StringSplitOptions.None);
string[] words = lines[i].Split(' ');
foreach (var word in words)
{
float wordWidth = paint.MeasureText(word);
@ -105,7 +155,7 @@ namespace FModel.Creator.Texts
}
ret.Add(new Line { Value = lineResult.ToString(), Width = width });
}
return ret.ToArray();
return ret;
}
}
}

View File

@ -1,10 +1,16 @@
using FModel.Creator.Bases;
using System;
using System.Collections.Generic;
using FModel.Creator.Bases;
using FModel.Properties;
using PakReader.Pak;
using PakReader.Parsers.Class;
using PakReader.Parsers.Objects;
using PakReader.Parsers.PropertyTagData;
using SkiaSharp;
using System.Collections.Generic;
using SkiaSharp.HarfBuzz;
namespace FModel.Creator.Texts
{
@ -17,7 +23,7 @@ namespace FModel.Creator.Texts
public static string GetTextPropertyBase(TextProperty t)
{
if (t.Value is FText text)
if (t.Value is { } text)
if (text.Text is FTextHistory.None n)
return n.CultureInvariantString;
else if (text.Text is FTextHistory.Base b)
@ -51,7 +57,7 @@ namespace FModel.Creator.Texts
public static (string, string, string) GetTextPropertyBases(TextProperty t)
{
if (t.Value is FText text && text.Text is FTextHistory.Base b)
if (t.Value is { } text && text.Text is FTextHistory.Base b)
return (b.Namespace, b.Key, b.SourceString);
return (string.Empty, string.Empty, string.Empty);
}
@ -62,7 +68,8 @@ namespace FModel.Creator.Texts
{
if (o1.TryGetValue("Value", out var c) && c is FloatProperty value && value.Value != -1) // old way
return $"MaxStackSize : {value.Value}";
else if (
if (
o1.TryGetValue("Curve", out var c1) && c1 is StructProperty curve && curve.Value is UObject o2 &&
o2.TryGetValue("CurveTable", out var c2) && c2 is ObjectProperty curveTable &&
o2.TryGetValue("RowName", out var c3) && c3 is NameProperty rowName) // new way
@ -75,7 +82,7 @@ namespace FModel.Creator.Texts
{
if (table.TryGetValue(rowName.Value.String, out var v1) && v1 is UObject maxStackAmount &&
maxStackAmount.TryGetValue("Keys", out var v2) && v2 is ArrayProperty keys &&
keys.Value.Length > 0 && (keys.Value[0] as StructProperty).Value is FSimpleCurveKey amount &&
keys.Value.Length > 0 && (keys.Value[0] as StructProperty)?.Value is FSimpleCurveKey amount &&
amount.KeyValue != -1)
{
return $"MaxStackSize : {amount.KeyValue}";
@ -104,7 +111,7 @@ namespace FModel.Creator.Texts
{
if (table.TryGetValue(rowName.Value.String, out var v1) && v1 is UObject maxStackAmount &&
maxStackAmount.TryGetValue("Keys", out var v2) && v2 is ArrayProperty keys &&
keys.Value.Length > 0 && (keys.Value[0] as StructProperty).Value is FSimpleCurveKey amount &&
keys.Value.Length > 0 && (keys.Value[0] as StructProperty)?.Value is FSimpleCurveKey amount &&
amount.KeyValue != -1)
{
return $"{amount.KeyValue} Xp";
@ -118,14 +125,14 @@ namespace FModel.Creator.Texts
public static void DrawBackground(SKCanvas c, IBase icon)
{
switch ((EIconDesign)Properties.Settings.Default.AssetsIconDesign)
switch ((EIconDesign)Settings.Default.AssetsIconDesign)
{
case EIconDesign.Flat:
{
var pathBottom = new SKPath { FillType = SKPathFillType.EvenOdd };
pathBottom.MoveTo(icon.Margin, icon.Height - icon.Margin);
pathBottom.LineTo(icon.Margin, icon.Height - icon.Margin - (icon.Height / 17 * 2.5f));
pathBottom.LineTo(icon.Width - icon.Margin, icon.Height - icon.Margin - (icon.Height / 17 * 4.5f));
pathBottom.LineTo(icon.Margin, icon.Height - icon.Margin - icon.Height / 17 * 2.5f);
pathBottom.LineTo(icon.Width - icon.Margin, icon.Height - icon.Margin - icon.Height / 17 * 4.5f);
pathBottom.LineTo(icon.Width - icon.Margin, icon.Height - icon.Margin);
pathBottom.Close();
c.DrawPath(pathBottom, new SKPaint
@ -158,12 +165,12 @@ namespace FModel.Creator.Texts
SKTextAlign side = SKTextAlign.Center;
int x = icon.Width / 2;
int y = _STARTER_TEXT_POSITION + _NAME_TEXT_SIZE;
switch ((EIconDesign)Properties.Settings.Default.AssetsIconDesign)
switch ((EIconDesign)Settings.Default.AssetsIconDesign)
{
case EIconDesign.Mini:
{
_NAME_TEXT_SIZE = 47;
text = text.ToUpper();
text = text.ToUpperInvariant();
break;
}
case EIconDesign.Flat:
@ -182,16 +189,41 @@ namespace FModel.Creator.Texts
Typeface = TypeFaces.DisplayNameTypeface,
TextSize = _NAME_TEXT_SIZE,
Color = SKColors.White,
TextAlign = side,
TextAlign = side
};
// resize if too long
while (namePaint.MeasureText(text) > (icon.Width - (icon.Margin * 2)))
if ((ELanguage)Settings.Default.AssetsLanguage == ELanguage.Arabic)
{
namePaint.TextSize = _NAME_TEXT_SIZE -= 2;
}
SKShaper shaper = new SKShaper(namePaint.Typeface);
float shapedTextWidth;
c.DrawText(text, x, y, namePaint);
while (true)
{
SKShaper.Result shapedText = shaper.Shape(text, namePaint);
shapedTextWidth = shapedText.Points[^1].X + namePaint.TextSize / 2f;
if (shapedTextWidth > icon.Width - icon.Margin * 2)
{
namePaint.TextSize = _NAME_TEXT_SIZE -= 1;
}
else
{
break;
}
}
c.DrawShapedText(shaper, text, (icon.Width - shapedTextWidth) / 2f, y, namePaint);
}
else
{
// resize if too long
while (namePaint.MeasureText(text) > icon.Width - icon.Margin * 2)
{
namePaint.TextSize = _NAME_TEXT_SIZE -= 1;
}
c.DrawText(text, x, y, namePaint);
}
}
public static void DrawDescription(SKCanvas c, IBase icon)
@ -200,7 +232,7 @@ namespace FModel.Creator.Texts
_BOTTOM_TEXT_SIZE = 15;
string text = icon.Description;
ETextSide side = ETextSide.Center;
switch ((EIconDesign)Properties.Settings.Default.AssetsIconDesign)
switch ((EIconDesign)Settings.Default.AssetsIconDesign)
{
case EIconDesign.Mini:
{
@ -243,7 +275,23 @@ namespace FModel.Creator.Texts
TextAlign = side == ETextSide.Left ? SKTextAlign.Left : SKTextAlign.Right,
};
c.DrawText(text, side == ETextSide.Left ? icon.Margin * 2.5f : icon.Size - (icon.Margin * 2.5f), icon.Size - (icon.Margin * 2.5f), shortDescriptionPaint);
if (side == ETextSide.Left)
{
if ((ELanguage)Settings.Default.AssetsLanguage == ELanguage.Arabic)
{
shortDescriptionPaint.TextSize -= 4f;
SKShaper shaper = new SKShaper(shortDescriptionPaint.Typeface);
c.DrawShapedText(shaper, text, icon.Margin * 2.5f, icon.Size - icon.Margin * 2.5f - shortDescriptionPaint.TextSize * .5f /* ¯\_(ツ)_/¯ */, shortDescriptionPaint);
}
else
{
c.DrawText(text, icon.Margin * 2.5f, icon.Size - icon.Margin * 2.5f, shortDescriptionPaint);
}
}
else
{
c.DrawText(text, icon.Size - icon.Margin * 2.5f, icon.Size - icon.Margin * 2.5f, shortDescriptionPaint);
}
}
}
}

View File

@ -144,6 +144,7 @@
<PackageReference Include="NVorbis" Version="0.10.1" />
<PackageReference Include="Ookii.Dialogs.Wpf" Version="1.1.0" />
<PackageReference Include="SkiaSharp" Version="2.80.2" />
<PackageReference Include="SkiaSharp.HarfBuzz" Version="2.80.2" />
<PackageReference Include="ToastNotifications" Version="2.5.1" />
<PackageReference Include="ToastNotifications.Messages" Version="2.5.1" />
<PackageReference Include="WriteableBitmapEx" Version="1.6.7" />

View File

@ -18,7 +18,7 @@ namespace FModel.Grabber.Paks
{
static class PaksGrabber
{
private static readonly Regex _pakFileRegex = new Regex(@"^FortniteGame/Content/Paks/.+\.pak$", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase);
private static readonly Regex _pakFileRegex = new Regex(@"^FortniteGame/Content/Paks/.+\.pak$", RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
public static async Task PopulateMenu()
{
@ -41,9 +41,10 @@ namespace FModel.Grabber.Paks
}
// Add Pak Files
if (Properties.Settings.Default.PakPath.EndsWith(".manifest") && await ManifestGrabber.TryGetLatestManifestInfo().ConfigureAwait(false) is ManifestInfo manifestInfo)
if (Properties.Settings.Default.PakPath.EndsWith(".manifest"))
{
var manifestData = await manifestInfo.DownloadManifestDataAsync();
ManifestInfo manifestInfo = await ManifestGrabber.TryGetLatestManifestInfo().ConfigureAwait(false);
byte[] manifestData = await manifestInfo.DownloadManifestDataAsync().ConfigureAwait(false);
Manifest manifest = new Manifest(manifestData, new ManifestOptions
{
ChunkBaseUri = new Uri("http://download.epicgames.com/Builds/Fortnite/CloudDir/ChunksV3/", UriKind.Absolute),

View File

@ -65,8 +65,8 @@ namespace FModel.ViewModels.ImageBox
{
Title = vm.Name,
WindowStartupLocation = WindowStartupLocation.CenterScreen,
Width = vm.Image.Width,
Height = vm.Image.Height
Width = vm.Image.Width + 16,
Height = vm.Image.Height + 39
};
win.SetValue(TextOptions.TextFormattingModeProperty, TextFormattingMode.Display);
if (vm.Image.Height > 1000)