Skip to content

Commit eb679d1

Browse files
remove width and height attributes from SVG (#2)
* simplify switch * remove width and height * rework the service * adjust namespaces * remove usings * extract minify
1 parent ec251a7 commit eb679d1

File tree

10 files changed

+165
-148
lines changed

10 files changed

+165
-148
lines changed
Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
using CommandLine;
22
using MyNihongo.KanjiVG.Animation;
3-
using MyNihongo.KanjiVG.Animation.Utils.Extensions;
4-
using MyNihongo.KanjiVG.Animator.Services;
3+
using MyNihongo.KanjiVG.Animation.Services;
54

65
await Parser.Default.ParseArguments<Args>(args)
7-
.WithParsedAsync(async x =>
8-
{
9-
var svgParams = x.ToSvgParams();
10-
await new KanjiAnimatorService().GenerateAsync(svgParams);
11-
});
6+
.WithParsedAsync(new KanjiAnimationCreator().CreateAsync);
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
using MyNihongo.KanaDetector.Extensions;
2+
using MyNihongo.KanjiVG.Animation.Utils;
3+
using MyNihongo.KanjiVG.Animation.Utils.Extensions;
4+
using MyNihongo.KanjiVG.Animator.Services;
5+
using System.Text;
6+
7+
namespace MyNihongo.KanjiVG.Animation.Services;
8+
9+
internal sealed class KanjiAnimationCreator
10+
{
11+
private static readonly UnicodeEncoding UnicodeEncoding = new();
12+
private readonly IKanjiAnimatorService _animatorService = new KanjiAnimatorService();
13+
14+
public Task CreateAsync(Args args)
15+
{
16+
if (!Directory.Exists(args.SourceDirectory))
17+
throw new InvalidOperationException($"{nameof(args.SourceDirectory)} does not exist");
18+
19+
if (!Directory.Exists(args.DestinationDirectory))
20+
throw new InvalidOperationException($"{nameof(args.DestinationDirectory)} does not exist");
21+
22+
var svgParams = args.ToSvgParams();
23+
var tasks = GetResourcePaths(args.SourceDirectory)
24+
.Select<string, Task>(async (x, i) =>
25+
{
26+
if (!TryGetFileName(x, out var fileName))
27+
return;
28+
29+
var kanjiChar = GetKanjiChar(fileName);
30+
if (!kanjiChar.IsKanaOrKanji())
31+
return;
32+
33+
var svgString = await GetXmlDocumentAsync(x)
34+
.ConfigureAwait(false);
35+
36+
svgString = _animatorService.Generate(svgString, fileName, svgParams);
37+
38+
await svgString.WriteTo(Path.Combine(args.DestinationDirectory, $"{fileName}.svg"))
39+
.ConfigureAwait(false);
40+
41+
Console.Write($"\r{i + 1}");
42+
});
43+
44+
return Task.WhenAll(tasks);
45+
}
46+
47+
private static IEnumerable<string> GetResourcePaths(string folderPath) =>
48+
Directory.EnumerateFiles(folderPath, "*svg");
49+
50+
private static bool TryGetFileName(string path, out string fileName)
51+
{
52+
fileName = Path.GetFileNameWithoutExtension(path);
53+
54+
// Skip modified kanji files
55+
if (fileName.Contains('-'))
56+
return false;
57+
58+
fileName = fileName.PadLeft(6, '0');
59+
if (fileName.StartsWith("00"))
60+
fileName = fileName[2..];
61+
62+
return true;
63+
}
64+
65+
public static char GetKanjiChar(string fileName)
66+
{
67+
var bytes = GetHexBytes(fileName);
68+
var str = UnicodeEncoding.GetString(bytes);
69+
70+
if (str.Length == 1)
71+
return str[0];
72+
73+
Console.WriteLine("Skipping: {0}", fileName);
74+
return '\0';
75+
76+
static byte[] GetHexBytes(string hexString)
77+
{
78+
if (hexString.StartsWith("00"))
79+
hexString = hexString[2..];
80+
81+
var bytes = new byte[hexString.Length / 2];
82+
for (var i = 0; i < hexString.Length; i += 2)
83+
{
84+
var str = hexString.Substring(i, 2);
85+
var index = (hexString.Length - i) / 2 - 1;
86+
bytes[index] = Convert.ToByte(str, 16);
87+
}
88+
89+
return bytes;
90+
}
91+
}
92+
93+
private static async Task<string> GetXmlDocumentAsync(string filePath)
94+
{
95+
await using var stream = FileUtils.AsyncStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);
96+
using var reader = new StreamReader(stream);
97+
98+
return await reader.ReadToEndAsync()
99+
.ConfigureAwait(false);
100+
}
101+
}

src/MyNihongo.KanjiVG.Animation/Utils/Extensions/ArgsEx.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ namespace MyNihongo.KanjiVG.Animation.Utils.Extensions;
55
internal static class ArgsEx
66
{
77
public static SvgParams ToSvgParams(this Args @this) =>
8-
new(@this.SourceDirectory, @this.DestinationDirectory)
8+
new()
99
{
1010
Rounding = @this.Rounding,
1111
OuterStroke = @this.OuterStroke,
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using MyNihongo.KanjiVG.Animator.Utils;
2+
3+
namespace MyNihongo.KanjiVG.Animation.Utils.Extensions;
4+
5+
internal static class StringEx
6+
{
7+
public static async Task WriteTo(this string @this, string path)
8+
{
9+
await using var stream = FileUtils.AsyncStream(path, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
10+
await using var writer = new StreamWriter(stream);
11+
12+
await writer.WriteAsync(@this)
13+
.ConfigureAwait(false);
14+
15+
await writer.FlushAsync()
16+
.ConfigureAwait(false);
17+
}
18+
}

src/MyNihongo.KanjiVG.Animator/Utils/FileUtils.cs renamed to src/MyNihongo.KanjiVG.Animation/Utils/FileUtils.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
namespace MyNihongo.KanjiVG.Animator.Utils;
1+
namespace MyNihongo.KanjiVG.Animation.Utils;
22

33
internal static class FileUtils
44
{

src/MyNihongo.KanjiVG.Animator/Services/IKanjiAnimatorService.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22

33
public interface IKanjiAnimatorService
44
{
5-
Task GenerateAsync(SvgParams svgParams);
5+
string Generate(string text, string fileName, SvgParams svgParams);
66
}

src/MyNihongo.KanjiVG.Animator/Services/KanjiAnimatorService.cs

Lines changed: 17 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
1-
using MyNihongo.KanaDetector.Extensions;
2-
using MyNihongo.KanjiVG.Animator.Resources.Const;
3-
using MyNihongo.KanjiVG.Animator.Utils;
1+
using MyNihongo.KanjiVG.Animator.Resources.Const;
42
using MyNihongo.KanjiVG.Animator.Utils.Extensions;
5-
using NUglify;
6-
using NUglify.Html;
73
using System.Xml;
84
using System.Xml.Linq;
95

@@ -13,69 +9,12 @@ public sealed class KanjiAnimatorService : IKanjiAnimatorService
139
{
1410
private const string DefaultNamespace = "http://www.w3.org/2000/svg";
1511

16-
private static readonly HtmlSettings MinifySettings = new()
12+
public string Generate(string text, string fileName, SvgParams svgParams)
1713
{
18-
RemoveAttributeQuotes = false
19-
};
14+
var xmlDoc = XDocument.Parse(text);
15+
xmlDoc = CreateAnimatedDocument(xmlDoc, fileName, svgParams);
2016

21-
public Task GenerateAsync(SvgParams svgParams)
22-
{
23-
if (!Directory.Exists(svgParams.SourceDirectory))
24-
throw new InvalidOperationException($"{nameof(svgParams.SourceDirectory)} does not exist");
25-
26-
if (!Directory.Exists(svgParams.DestinationDirectory))
27-
throw new InvalidOperationException($"{nameof(svgParams.DestinationDirectory)} does not exist");
28-
29-
var tasks = GetResourcePaths(svgParams.SourceDirectory)
30-
.Select<string, Task>(async (x, i) =>
31-
{
32-
if (!x.TryGetFileName(out var fileName))
33-
return;
34-
35-
var kanjiChar = fileName.GetKanjiChar();
36-
if (!kanjiChar.IsKanaOrKanji())
37-
return;
38-
39-
var xmlDoc = await GetXmlDocument(x)
40-
.ConfigureAwait(false);
41-
42-
xmlDoc = CreateAnimatedDocument(xmlDoc, fileName, svgParams);
43-
Console.Write($"\r{i + 1}");
44-
45-
var xmlString = MinifyXmlDoc(xmlDoc);
46-
47-
await xmlString.WriteTo(Path.Combine(svgParams.DestinationDirectory, $"{fileName}.svg"))
48-
.ConfigureAwait(false);
49-
});
50-
51-
return Task.WhenAll(tasks);
52-
}
53-
54-
private static IEnumerable<string> GetResourcePaths(string folderPath) =>
55-
Directory.EnumerateFiles(folderPath, "*svg");
56-
57-
private static async Task<XDocument> GetXmlDocument(string filePath)
58-
{
59-
string text;
60-
61-
await using (var stream = FileUtils.AsyncStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read))
62-
using (var reader = new StreamReader(stream))
63-
{
64-
text = await reader.ReadToEndAsync()
65-
.ConfigureAwait(false);
66-
}
67-
68-
return XDocument.Parse(text);
69-
}
70-
71-
private static string MinifyXmlDoc(XDocument xmlDoc)
72-
{
73-
var xmlString = xmlDoc.ToString();
74-
var result = Uglify.Html(xmlString, MinifySettings);
75-
if (result.HasErrors)
76-
throw new InvalidOperationException("Cannot minify the SVG");
77-
78-
return result.Code;
17+
return xmlDoc.Minify();
7918
}
8019

8120
private static XDocument CreateAnimatedDocument(XDocument src, string fileName, SvgParams svgParams)
@@ -94,18 +33,18 @@ private static XDocument CreateAnimatedDocument(XDocument src, string fileName,
9433
continue;
9534
}
9635

97-
if (node is not XElement element || !HandleAttributes(element, svgParams, out var elementId))
36+
if (node is not XElement element)
9837
continue;
9938

100-
if (element.Name.LocalName == SvgElements.Svg)
101-
{
102-
element.SetAttributeValue(XNames.Id, fileName);
103-
svgElement = element;
104-
continue;
105-
}
106-
39+
HandleAttributes(element, svgParams, out var elementId);
10740
switch (element.Name.LocalName)
10841
{
42+
case SvgElements.Svg:
43+
{
44+
element.SetAttributeValue(XNames.Id, fileName);
45+
svgElement = element;
46+
break;
47+
}
10948
case SvgElements.Path:
11049
{
11150
var animatedPath = element.Copy();
@@ -122,8 +61,6 @@ private static XDocument CreateAnimatedDocument(XDocument src, string fileName,
12261
svgElement = null;
12362
break;
12463
}
125-
default:
126-
continue;
12764
}
12865
}
12966

@@ -135,7 +72,7 @@ private static XDocument CreateAnimatedDocument(XDocument src, string fileName,
13572
return src;
13673
}
13774

138-
private static bool HandleAttributes(XElement element, SvgParams svgParams, out string id)
75+
private static void HandleAttributes(XElement element, SvgParams svgParams, out string id)
13976
{
14077
id = string.Empty;
14178
var attrs = element.Attributes()
@@ -149,7 +86,7 @@ private static bool HandleAttributes(XElement element, SvgParams svgParams, out
14986
if (attr.Value.Contains("StrokeNumbers"))
15087
{
15188
element.Remove();
152-
return false;
89+
return;
15390
}
15491
else
15592
{
@@ -180,11 +117,11 @@ private static bool HandleAttributes(XElement element, SvgParams svgParams, out
180117
case "radical":
181118
case "position":
182119
case "part":
120+
case "width":
121+
case "height":
183122
attr.Remove();
184123
break;
185124
}
186-
187-
return true;
188125
}
189126

190127
private static void SetInnerGraphic(XElement graphicElement, SvgParams svgParams)

src/MyNihongo.KanjiVG.Animator/SvgParams.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
namespace MyNihongo.KanjiVG.Animator;
22

3-
public sealed record SvgParams(string SourceDirectory, string DestinationDirectory)
3+
public sealed record SvgParams
44
{
55
public int Rounding { get; init; }
66

src/MyNihongo.KanjiVG.Animator/Utils/Extensions/StringEx.cs

Lines changed: 0 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -4,20 +4,6 @@ namespace MyNihongo.KanjiVG.Animator.Utils.Extensions;
44

55
internal static class StringEx
66
{
7-
private static readonly UnicodeEncoding UnicodeEncoding = new();
8-
9-
public static async Task WriteTo(this string @this, string path)
10-
{
11-
await using var stream = FileUtils.AsyncStream(path, FileMode.Create, FileAccess.ReadWrite, FileShare.ReadWrite);
12-
await using var writer = new StreamWriter(stream);
13-
14-
await writer.WriteAsync(@this)
15-
.ConfigureAwait(false);
16-
17-
await writer.FlushAsync()
18-
.ConfigureAwait(false);
19-
}
20-
217
public static IReadOnlyDictionary<string, string> ParseStyleAttrs(this string @this)
228
{
239
var dictionary = new Dictionary<string, string>();
@@ -34,47 +20,4 @@ public static IReadOnlyDictionary<string, string> ParseStyleAttrs(this string @t
3420

3521
return dictionary;
3622
}
37-
38-
public static bool TryGetFileName(this string path, out string fileName)
39-
{
40-
fileName = Path.GetFileNameWithoutExtension(path);
41-
42-
// Skip modified kanji files
43-
if (fileName.Contains('-'))
44-
return false;
45-
46-
fileName = fileName.PadLeft(6, '0');
47-
if (fileName.StartsWith("00"))
48-
fileName = fileName[2..];
49-
50-
return true;
51-
}
52-
53-
public static char GetKanjiChar(this string fileName)
54-
{
55-
var bytes = fileName.GetHexBytes();
56-
var str = UnicodeEncoding.GetString(bytes);
57-
58-
if (str.Length == 1)
59-
return str[0];
60-
61-
Console.WriteLine("Skipping: {0}", fileName);
62-
return '\0';
63-
}
64-
65-
private static byte[] GetHexBytes(this string hexString)
66-
{
67-
if (hexString.StartsWith("00"))
68-
hexString = hexString[2..];
69-
70-
var bytes = new byte[hexString.Length / 2];
71-
for (var i = 0; i < hexString.Length; i += 2)
72-
{
73-
var str = hexString.Substring(i, 2);
74-
var index = (hexString.Length - i) / 2 - 1;
75-
bytes[index] = Convert.ToByte(str, 16);
76-
}
77-
78-
return bytes;
79-
}
8023
}

0 commit comments

Comments
 (0)