흐림의 진짜 원인은 TextFormattingMode 였다 — 색 번짐만 재서 두 번 놓쳤다
전역 TextBlock 스타일이 TextOptions.TextFormattingMode="Ideal" 을 박고 있었다 (DesignerTheme.xaml:40). Ideal 은 WPF 기본값이고 글리프를 픽셀 격자에 맞추지 않아 11~14px UI 글자가 눌린 듯 흐려 보인다. ## 왜 두 번이나 못 잡았나 <b>ClearType 과 다른 축이다.</b> Ideal 이든 Display 든 색 번짐은 똑같이 생긴다 — 그래서 색 번짐을 세는 --cleartype 으로는 원리상 구분이 안 된다. 표면을 늘려 가며(카드 제목 → 본문 → 목록) 전부 "켜짐"을 확인했고 게이트는 초록불이었는데 사용자 눈에는 계속 흐렸다. 측정하고 있던 것이 물어야 할 것이 아니었다. 이번에는 픽셀이 아니라 <b>속성 값</b>으로 잠근다 — G·H·I 세 표면에서 GetTextFormattingMode() == Display 를 확인한다. ## 화면과 종이를 상속으로 가른다 - 창(ThemedWindow)에 Display. 상속 속성이라 TextBlock·TextBox·버튼·표 글자에 모두 닿는다. - 종이는 Ideal. PageView 의 용지 Grid 와 PrintService 의 캔버스가 각각 명시하고 그 안의 글자가 상속한다. 인쇄 지면 자릿수가 바뀌면 레거시와 다른 종이가 된다. 전역 TextBlock 스타일에서는 이 속성을 <b>빼야</b> 했다. 스타일 세터는 상속값을 이기므로, 거기에 값을 박으면 종이 안의 글자까지 그 값이 되어 용지 Grid 가 정한 Ideal 을 덮는다. 지금은 두 쪽 다 상속으로만 정한다. ## 종이가 끌려가지 않았는지 두 겹으로 확인 - --db-render P062 md5 8d683835f5d81e7bb41c79071d6bf954 불변 - --edit-smoke 에 '종이 글자는 Ideal 을 유지한다' 를 추가. md5 는 서식 하나만 보므로 속성 값으로도 못 박았다. ## 게이트 - dotnet test 323/323 - --cleartype 실패 0 (색번짐 3표면 + Display 판정 3건 신규) - --edit-smoke 실패 0 · --query-popup 실패 0 · --dialog-shots FAIL 0 - --db-render md5 불변 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
bbbc7f5a08
commit
1baf805f02
@@ -73,6 +73,14 @@ internal static class ClearTypeCheck
|
|||||||
if (target is { ActualWidth: > 0 })
|
if (target is { ActualWidth: > 0 })
|
||||||
{
|
{
|
||||||
samples.Add((label, target, true));
|
samples.Add((label, target, true));
|
||||||
|
// ── 흐림의 <b>두 번째 축</b> ──
|
||||||
|
// 색 번짐이 있어도(=ClearType 켜짐) TextFormattingMode 가 Ideal 이면
|
||||||
|
// 글리프가 픽셀 격자에 안 맞아 11~14px 글자가 눌린 듯 흐려 보인다.
|
||||||
|
// 색 번짐을 세는 방식으로는 두 값이 구분되지 않는다 — 그래서 두 번 놓쳤다.
|
||||||
|
// 이건 픽셀이 아니라 <b>속성 값</b>으로 잠근다.
|
||||||
|
var mode = TextOptions.GetTextFormattingMode(target);
|
||||||
|
Check($"{label} → 화면 글자는 Display 로 그린다",
|
||||||
|
mode == TextFormattingMode.Display, $"실제 {mode}");
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -79,6 +79,24 @@ public static class EditSmoke
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>시각 트리에서 첫 TextBlock — 없으면 null</summary>
|
||||||
|
private static System.Windows.Controls.TextBlock? FindFirstTextBlock(DependencyObject root)
|
||||||
|
{
|
||||||
|
if (root is System.Windows.Controls.TextBlock block)
|
||||||
|
{
|
||||||
|
return block;
|
||||||
|
}
|
||||||
|
var count = System.Windows.Media.VisualTreeHelper.GetChildrenCount(root);
|
||||||
|
for (var i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
if (FindFirstTextBlock(System.Windows.Media.VisualTreeHelper.GetChild(root, i)) is { } found)
|
||||||
|
{
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public static int Run(string reportPath)
|
public static int Run(string reportPath)
|
||||||
{
|
{
|
||||||
var lines = new List<string>();
|
var lines = new List<string>();
|
||||||
@@ -426,6 +444,37 @@ public static class EditSmoke
|
|||||||
$"요소 {livePages} → {liveAfterAdd} (안 늘면 생성자에서 한 번만 그린 것)");
|
$"요소 {livePages} → {liveAfterAdd} (안 늘면 생성자에서 한 번만 그린 것)");
|
||||||
live.Close();
|
live.Close();
|
||||||
|
|
||||||
|
// 종이 글자는 Display 로 바뀌지 않았는가 — 대조군이자 회귀 방어다.
|
||||||
|
// 화면 UI 를 Display 로 돌리면서 종이까지 끌려가면 글리프 자릿수가 달라져
|
||||||
|
// 레거시와 다른 지면이 된다. --db-render md5 가 잠그고 있지만
|
||||||
|
// 그건 서식 하나뿐이므로 여기서 속성 값으로도 못 박는다.
|
||||||
|
{
|
||||||
|
var paperPage = new FormDocument();
|
||||||
|
var paperOnly = new FormPage { Width = 720, Height = 856 };
|
||||||
|
paperPage.Pages.Add(paperOnly);
|
||||||
|
var paperLabel = new ControlElement
|
||||||
|
{
|
||||||
|
Type = "Label", Id = "종이글자",
|
||||||
|
Bounds = new LayoutRect { X = 20, Y = 20, W = 200, H = 20 },
|
||||||
|
};
|
||||||
|
paperLabel.Props.SetText("Text", "종이");
|
||||||
|
paperOnly.Controls.Add(paperLabel);
|
||||||
|
var paperVm = new DesignerViewModel(paperPage);
|
||||||
|
var paperVisual = (System.Windows.FrameworkElement)
|
||||||
|
Services.PrintService.BuildPageVisual(paperVm.Pages[0]);
|
||||||
|
paperVisual.Measure(new System.Windows.Size(720, 856));
|
||||||
|
paperVisual.Arrange(new Rect(0, 0, 720, 856));
|
||||||
|
paperVisual.UpdateLayout();
|
||||||
|
var paperBlock = FindFirstTextBlock(paperVisual);
|
||||||
|
Check("종이 글자는 Ideal 을 유지한다(화면 Display 와 분리)",
|
||||||
|
paperBlock is not null
|
||||||
|
&& System.Windows.Media.TextOptions.GetTextFormattingMode(paperBlock)
|
||||||
|
== System.Windows.Media.TextFormattingMode.Ideal,
|
||||||
|
paperBlock is null
|
||||||
|
? "종이에서 TextBlock 을 못 찾아 판정 불가"
|
||||||
|
: System.Windows.Media.TextOptions.GetTextFormattingMode(paperBlock).ToString());
|
||||||
|
}
|
||||||
|
|
||||||
// 태그가 종이에서 값이 되는가.
|
// 태그가 종이에서 값이 되는가.
|
||||||
// 지금까지 미리보기는 태그를 글자 그대로 찍었다 — PAT_이름 이 종이에 그렇게 인쇄됐다.
|
// 지금까지 미리보기는 태그를 글자 그대로 찍었다 — PAT_이름 이 종이에 그렇게 인쇄됐다.
|
||||||
// 해석기를 붙이면 값이 나오는 것은 값으로, 못 나오는 것은 <b>사유</b>로 바뀌어야 한다
|
// 해석기를 붙이면 값이 나오는 것은 값으로, 못 나오는 것은 <b>사유</b>로 바뀌어야 한다
|
||||||
|
|||||||
@@ -28,6 +28,18 @@
|
|||||||
<Setter Property="FontFamily" Value="Malgun Gothic" />
|
<Setter Property="FontFamily" Value="Malgun Gothic" />
|
||||||
<Setter Property="FontSize" Value="13" />
|
<Setter Property="FontSize" Value="13" />
|
||||||
<Setter Property="Foreground" Value="{DynamicResource B.Ink}" />
|
<Setter Property="Foreground" Value="{DynamicResource B.Ink}" />
|
||||||
|
<!--
|
||||||
|
화면 글자는 Display 로 그린다. WPF 기본값인 Ideal 은 글리프를 픽셀 격자에 맞추지 않고
|
||||||
|
소수 위치에 놓아서, 11~14px UI 글자가 눌린 듯 흐려 보인다.
|
||||||
|
<b>ClearType 과는 다른 축</b>이다 — Ideal 이든 Display 든 색 번짐은 그대로 생기므로
|
||||||
|
색 번짐을 세는 진단으로는 이 흐림을 잡을 수 없었다(그래서 두 번 놓쳤다).
|
||||||
|
|
||||||
|
상속 속성이라 여기 한 번 걸면 TextBlock·TextBox·버튼·표 글자에 모두 닿는다.
|
||||||
|
<b>종이는 예외다.</b> PageView 의 용지 Grid 와 PrintService 의 캔버스가 각각
|
||||||
|
Ideal 을 명시하고 그 안의 글자는 그것을 상속한다 — 인쇄 지면 자릿수가 바뀌면
|
||||||
|
레거시와 다른 종이가 된다(진단 db-render 의 md5 가 그것을 잠그고 있다).
|
||||||
|
-->
|
||||||
|
<Setter Property="TextOptions.TextFormattingMode" Value="Display" />
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<!-- 순수 Window 인스턴스용(파생 클래스에는 위 키 스타일을 직접 걸어야 한다) -->
|
<!-- 순수 Window 인스턴스용(파생 클래스에는 위 키 스타일을 직접 걸어야 한다) -->
|
||||||
@@ -37,7 +49,12 @@
|
|||||||
<Style TargetType="TextBlock">
|
<Style TargetType="TextBlock">
|
||||||
<Setter Property="Foreground" Value="{DynamicResource B.Ink}" />
|
<Setter Property="Foreground" Value="{DynamicResource B.Ink}" />
|
||||||
<Setter Property="FontFamily" Value="Malgun Gothic" />
|
<Setter Property="FontFamily" Value="Malgun Gothic" />
|
||||||
<Setter Property="TextOptions.TextFormattingMode" Value="Ideal" />
|
<!--
|
||||||
|
TextFormattingMode 을 여기서 정하지 않는다. <b>스타일 세터는 상속값을 이긴다</b> —
|
||||||
|
여기에 값을 박으면 종이 안의 글자까지 그 값이 되어 용지 Grid 가 정한 Ideal 을 덮는다.
|
||||||
|
전에 Ideal 이 박혀 있어서 화면 UI 글자가 전부 흐렸다.
|
||||||
|
지금은 창(Display)과 종이(Ideal)가 각각 상속으로 정한다.
|
||||||
|
-->
|
||||||
</Style>
|
</Style>
|
||||||
|
|
||||||
<!-- ===== 버튼 (기본: 은은한 다크 채움) ===== -->
|
<!-- ===== 버튼 (기본: 은은한 다크 채움) ===== -->
|
||||||
|
|||||||
Reference in New Issue
Block a user