Files
SheetMe/src/SheetMe.Core/Serialization/LegacyFormat.cs
T
MsystechandClaude Fable 5 16c07f48dc 초기 커밋: SheetMe 서식생성기 (P0~P5 완료 상태)
레거시 서식생성기(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>
2026-08-11 17:20:22 +09:00

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; }
}