레거시 서식생성기(VB.NET WinForms) 대체용 C#/.NET 10 WPF 디자이너. 기준선: 실DB 활성 디자인 1,271건 왕복 의미론 diff 0 / 예외 0, 단위 테스트 49/49. 이 커밋에 함께 포함된 자격증명 분리: - appsettings.json 을 __HOST__/__PASSWORD__ 플레이스홀더로 전환 - 실접속 정보는 appsettings.Development.json 으로 분리(.gitignore 제외, csproj Debug 조건부 복사라 Release 산출물에 실리지 않음) - ConfigLoader 를 환경변수 > Development > appsettings 순 레이어링으로 변경, 미치환 플레이스홀더는 '미설정'으로 간주 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
182 lines
6.3 KiB
C#
182 lines
6.3 KiB
C#
using System.Globalization;
|
|
|
|
namespace SheetMe.Core.Serialization;
|
|
|
|
/// <summary>
|
|
/// 레거시 TypeConverter invariant 문자열 포맷 도우미.
|
|
/// 읽기는 관용적으로, 쓰기는 레거시 원문 형식("X, Y" / "이름, 11.25pt, style=Bold")과 동일하게.
|
|
/// </summary>
|
|
public static class LegacyFormat
|
|
{
|
|
#region Point / Size
|
|
/// <summary>"336, 1064" 형식 파싱 — 실패 시 (0,0)</summary>
|
|
public static (double X, double Y) ParsePoint(string? text)
|
|
{
|
|
if (TryParsePair(text, out var x, out var y))
|
|
{
|
|
return (x, y);
|
|
}
|
|
return (0, 0);
|
|
}
|
|
|
|
/// <summary>"174, 18" 형식 파싱 — 실패 시 (0,0)</summary>
|
|
public static (double W, double H) ParseSize(string? text) => ParsePoint(text);
|
|
|
|
/// <summary>Point/Size invariant 문자열 생성 — 레거시와 동일한 "X, Y" (정수 반올림)</summary>
|
|
public static string FormatPair(double a, double b)
|
|
=> string.Create(CultureInfo.InvariantCulture, $"{(int)Math.Round(a)}, {(int)Math.Round(b)}");
|
|
|
|
private static bool TryParsePair(string? text, out double a, out double b)
|
|
{
|
|
a = 0;
|
|
b = 0;
|
|
if (string.IsNullOrWhiteSpace(text))
|
|
{
|
|
return false;
|
|
}
|
|
var parts = text.Split(',');
|
|
if (parts.Length != 2)
|
|
{
|
|
return false;
|
|
}
|
|
return double.TryParse(parts[0].Trim(), NumberStyles.Number, CultureInfo.InvariantCulture, out a)
|
|
&& double.TryParse(parts[1].Trim(), NumberStyles.Number, CultureInfo.InvariantCulture, out b);
|
|
}
|
|
#endregion
|
|
|
|
#region Font
|
|
/// <summary>
|
|
/// 레거시 FontConverter invariant 문자열 파싱 — "굴림, 11.25pt, style=Bold, Underline".
|
|
/// 실패 시 기본(굴림 9pt) 반환.
|
|
/// </summary>
|
|
public static LegacyFont ParseFont(string? text)
|
|
{
|
|
var font = new LegacyFont();
|
|
if (string.IsNullOrWhiteSpace(text))
|
|
{
|
|
return font;
|
|
}
|
|
|
|
var parts = text.Split(',');
|
|
if (parts.Length > 0)
|
|
{
|
|
font.Family = parts[0].Trim();
|
|
}
|
|
|
|
for (var i = 1; i < parts.Length; i++)
|
|
{
|
|
var part = parts[i].Trim();
|
|
if (part.EndsWith("pt", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
if (double.TryParse(part[..^2], NumberStyles.Number, CultureInfo.InvariantCulture, out var size))
|
|
{
|
|
font.SizePt = size;
|
|
}
|
|
}
|
|
else if (part.StartsWith("style=", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
ApplyStyle(font, part[6..]);
|
|
}
|
|
else
|
|
{
|
|
// "style=Bold, Italic" 처럼 콤마로 이어진 후속 스타일 토큰
|
|
ApplyStyle(font, part);
|
|
}
|
|
}
|
|
return font;
|
|
}
|
|
|
|
/// <summary>레거시 FontConverter invariant 형식으로 생성 — "이름, 11.25pt[, style=Bold, Italic]"</summary>
|
|
public static string FormatFont(LegacyFont font)
|
|
{
|
|
var size = font.SizePt.ToString("0.##", CultureInfo.InvariantCulture);
|
|
var styles = new List<string>(4);
|
|
if (font.Bold) { styles.Add("Bold"); }
|
|
if (font.Italic) { styles.Add("Italic"); }
|
|
if (font.Underline) { styles.Add("Underline"); }
|
|
if (font.Strikeout) { styles.Add("Strikeout"); }
|
|
|
|
return styles.Count == 0
|
|
? $"{font.Family}, {size}pt"
|
|
: $"{font.Family}, {size}pt, style={string.Join(", ", styles)}";
|
|
}
|
|
|
|
private static void ApplyStyle(LegacyFont font, string token)
|
|
{
|
|
switch (token.Trim())
|
|
{
|
|
case "Bold": font.Bold = true; break;
|
|
case "Italic": font.Italic = true; break;
|
|
case "Underline": font.Underline = true; break;
|
|
case "Strikeout": font.Strikeout = true; break;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Color
|
|
/// <summary>
|
|
/// 레거시 ColorConverter invariant 문자열 파싱 — "224, 224, 224" / "255, 0, 0, 0"(ARGB) / "White" / "ControlDarkDark".
|
|
/// 반환: (A,R,G,B). 이름 색은 System.Drawing 으로 해석, 실패 시 검정.
|
|
/// </summary>
|
|
public static (byte A, byte R, byte G, byte B) ParseColor(string? text)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(text))
|
|
{
|
|
return (255, 0, 0, 0);
|
|
}
|
|
|
|
var parts = text.Split(',');
|
|
if (parts.Length is 3 or 4 && byte.TryParse(parts[0].Trim(), out _))
|
|
{
|
|
var values = new byte[parts.Length];
|
|
for (var i = 0; i < parts.Length; i++)
|
|
{
|
|
if (!byte.TryParse(parts[i].Trim(), NumberStyles.Integer, CultureInfo.InvariantCulture, out values[i]))
|
|
{
|
|
return (255, 0, 0, 0);
|
|
}
|
|
}
|
|
return parts.Length == 3
|
|
? ((byte)255, values[0], values[1], values[2])
|
|
: (values[0], values[1], values[2], values[3]);
|
|
}
|
|
|
|
// 명명색/시스템색 — System.Drawing 의 KnownColor 해석 사용(레거시와 동일 의미론)
|
|
var named = System.Drawing.Color.FromName(text.Trim());
|
|
if (named.IsKnownColor || named.A != 0 || named.R != 0 || named.G != 0 || named.B != 0)
|
|
{
|
|
return (named.A, named.R, named.G, named.B);
|
|
}
|
|
return (255, 0, 0, 0);
|
|
}
|
|
|
|
/// <summary>ColorConverter invariant 형식으로 생성 — 불투명 색은 "R, G, B", 반투명은 "A, R, G, B"</summary>
|
|
public static string FormatColor(byte a, byte r, byte g, byte b)
|
|
=> a == 255
|
|
? string.Create(CultureInfo.InvariantCulture, $"{r}, {g}, {b}")
|
|
: string.Create(CultureInfo.InvariantCulture, $"{a}, {r}, {g}, {b}");
|
|
#endregion
|
|
}
|
|
|
|
/// <summary>레거시 폰트 표현 — WinForms Font invariant 문자열과 1:1</summary>
|
|
public sealed class LegacyFont
|
|
{
|
|
/// <summary>글꼴 패밀리명</summary>
|
|
public string Family { get; set; } = "굴림";
|
|
|
|
/// <summary>크기(pt)</summary>
|
|
public double SizePt { get; set; } = 9;
|
|
|
|
/// <summary>굵게</summary>
|
|
public bool Bold { get; set; }
|
|
|
|
/// <summary>기울임</summary>
|
|
public bool Italic { get; set; }
|
|
|
|
/// <summary>밑줄</summary>
|
|
public bool Underline { get; set; }
|
|
|
|
/// <summary>취소선</summary>
|
|
public bool Strikeout { get; set; }
|
|
}
|