CGraphicText, CGraphicFontTexture and CGraphicDib are the 40250 bodies verbatim; CGraphicTextInstance is verbatim except that its D3D draws become UI render commands (one image quad per glyph, Bar for cursor/underline). - platform/Win32Gdi.cpp: the GDI subset those files call, over vendored FreeType 2.13.3, following GDI's LOGFONT cell height, win metrics and gasp rules; system font lookup with substitute and CJK fallback faces - Win32Crt: MultiByteToWideChar / WideCharToMultiByte (UTF-8 and 1252 by hand, other code pages through iconv) - CGraphicImageTexture memory textures: glyph pages reach Godot as "mem:<id>@<revision>"; Metin2PythonHost.memory_texture + Canvas cache - Util.cpp code page / font face table, SetDefaultCodePage at boot, DefaultFont_* block, fnt resource type, GetMaxTextureWidth/Height - TextTag, StringCodec, Arabic, Vietnamese copied from 40250 - tests: port.text (Tahoma 12 metrics, alignment, CP949), port.app_loop and python_host_test.gd now check glyph quads instead of text commands Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
146 lines
4.8 KiB
C++
146 lines
4.8 KiB
C++
// 批次 2V0-g: the 40250 font path (CGraphicText → CGraphicFontTexture → CGraphicDib) over the GDI
|
|
// emulation in platform/Win32Gdi.cpp, and the code-page conversions CGraphicTextInstance runs text through.
|
|
//
|
|
// Needs a system Tahoma (macOS ships /System/Library/Fonts/Supplemental/Tahoma.ttf); exit 77 (ctest
|
|
// SKIP) without one, unless MT_ASSETS_STRICT=1.
|
|
#include "EterLib/StdAfx.h"
|
|
#include "EterLib/GrpText.h"
|
|
#include "EterLib/GrpTextInstance.h"
|
|
#include "EterLib/Util.h"
|
|
#include "EterLib/ResourceManager.h"
|
|
#include "../src/platform/EterLib/UIRenderCommands.h"
|
|
#include "../src/platform/UserInterface/UserInterface.h"
|
|
|
|
#include <cmath>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <string>
|
|
|
|
#include <unistd.h>
|
|
|
|
static int g_failures = 0;
|
|
#define CHECK(cond) \
|
|
do { \
|
|
if (!(cond)) { \
|
|
std::fprintf(stderr, "%s:%d: CHECK(%s)\n", __FILE__, __LINE__, #cond); \
|
|
++g_failures; \
|
|
} \
|
|
} while (0)
|
|
|
|
static void code_pages()
|
|
{
|
|
// CP949 "굴림" (B1BC B8B2) → U+AD74 U+B9BC, and back.
|
|
const char gulim[] = "\xb1\xbc\xb8\xb2";
|
|
WCHAR wide[8] = {};
|
|
CHECK(MultiByteToWideChar(949, 0, gulim, -1, nullptr, 0) == 3);
|
|
CHECK(MultiByteToWideChar(949, 0, gulim, -1, wide, 8) == 3);
|
|
CHECK(wide[0] == 0xAD74 && wide[1] == 0xB9BC && wide[2] == 0);
|
|
char narrow[8] = {};
|
|
CHECK(WideCharToMultiByte(949, 0, wide, -1, narrow, sizeof(narrow), nullptr, nullptr) == 5);
|
|
CHECK(std::string(narrow) == gulim);
|
|
|
|
// UTF-8 and the Windows size rules: a too-small buffer answers 0.
|
|
CHECK(MultiByteToWideChar(CP_UTF8, 0, "\xea\xb5\xb4", 3, wide, 8) == 1 && wide[0] == 0xAD74);
|
|
CHECK(MultiByteToWideChar(CP_UTF8, 0, "abc", 3, wide, 2) == 0);
|
|
// CP1252 0x80 is the euro sign.
|
|
CHECK(MultiByteToWideChar(1252, 0, "\x80", 1, wide, 8) == 1 && wide[0] == 0x20AC);
|
|
|
|
// 40250 Util.cpp face table.
|
|
CHECK(SetDefaultCodePage(949) && std::string(GetDefaultFontFace()) == "GulimChe");
|
|
CHECK(GetCharsetFromCodePage(949) == HANGUL_CHARSET);
|
|
CHECK(SetDefaultCodePage(1252) && std::string(GetDefaultFontFace()) == "Arial");
|
|
CHECK(GetDefaultCodePage() == 1252);
|
|
}
|
|
|
|
static int glyph_quads(float* min_x, float* max_x)
|
|
{
|
|
int count = 0;
|
|
*min_x = 1e9f;
|
|
*max_x = -1e9f;
|
|
for (const auto& command : UIRenderCommands())
|
|
{
|
|
if (command.kind != UIRenderCommand::Image || command.text.rfind("mem:", 0) != 0)
|
|
continue;
|
|
++count;
|
|
for (float x : command.qx)
|
|
{
|
|
*min_x = std::fmin(*min_x, x);
|
|
*max_x = std::fmax(*max_x, x);
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
int main()
|
|
{
|
|
const char* strict = std::getenv("MT_ASSETS_STRICT");
|
|
const bool is_strict = strict && std::string(strict) == "1";
|
|
|
|
PackSingletons();
|
|
CResourceManager resource_manager; // CResource::Release queues the resource here
|
|
code_pages();
|
|
|
|
if (access("/System/Library/Fonts/Supplemental/Tahoma.ttf", 0) != 0)
|
|
{
|
|
std::fprintf(stderr, "port_text_test: no system Tahoma\n");
|
|
return g_failures ? 1 : (is_strict ? 1 : 77);
|
|
}
|
|
|
|
// Not in any pack: CResource::Load falls through to OnLoad(0, NULL), as for every 40250 .fnt.
|
|
CGraphicText* text = new CGraphicText("Tahoma:12.fnt");
|
|
text->AddReference();
|
|
text->Load();
|
|
CHECK(!text->IsEmpty());
|
|
|
|
CGraphicTextInstance instance;
|
|
instance.SetTextPointer(text);
|
|
instance.SetValue("Hello");
|
|
instance.Update();
|
|
int width = 0, height = 0;
|
|
instance.GetTextSize(&width, &height);
|
|
// GDI lays a 12-pixel LOGFONT cell out as Tahoma's win ascent + descent at 10 ppem: 12 rows.
|
|
CHECK(height == 12);
|
|
CHECK(width > 20 && width < 40);
|
|
CHECK(instance.PixelPositionToCharacterPosition(0) == 0);
|
|
CHECK(instance.PixelPositionToCharacterPosition(width - 1) == 4);
|
|
CHECK(instance.PixelPositionToCharacterPosition(width + 5) == -1);
|
|
|
|
UIRenderSetSize(800, 600);
|
|
UIRenderBeginFrame();
|
|
instance.SetPosition(100.0f, 50.0f);
|
|
instance.Render();
|
|
float left = 0, right = 0;
|
|
CHECK(glyph_quads(&left, &right) == 5);
|
|
CHECK(std::fabs(left - 100.0f) <= 1.5f);
|
|
|
|
// Centred: the text straddles the position.
|
|
instance.SetHorizonalAlign(CGraphicTextInstance::HORIZONTAL_ALIGN_CENTER);
|
|
instance.Update();
|
|
UIRenderBeginFrame();
|
|
instance.Render();
|
|
CHECK(glyph_quads(&left, &right) == 5);
|
|
CHECK(left < 100.0f && right > 100.0f && std::fabs((left + right) * 0.5f - 100.0f) <= 2.0f);
|
|
|
|
// The glyph page has ink.
|
|
bool inked = false;
|
|
for (const auto& command : UIRenderCommands())
|
|
{
|
|
UIMemoryTexture page;
|
|
if (command.text.rfind("mem:", 0) == 0 && UIRenderMemoryTexture(command.text, &page))
|
|
for (std::uint32_t argb : page.argb)
|
|
inked |= (argb >> 24) != 0;
|
|
}
|
|
CHECK(inked);
|
|
|
|
instance.Destroy();
|
|
text->Release();
|
|
|
|
if (g_failures)
|
|
{
|
|
std::fprintf(stderr, "%d check(s) failed\n", g_failures);
|
|
return 1;
|
|
}
|
|
std::printf("port_text_test: ok\n");
|
|
return 0;
|
|
}
|