diff --git a/upptst/Unicode/Unicode.upp b/upptst/Unicode/Unicode.upp index 50f8eef06..29520a009 100644 --- a/upptst/Unicode/Unicode.upp +++ b/upptst/Unicode/Unicode.upp @@ -3,6 +3,7 @@ uses file UnicodeData.txt, + Win32.cpp, main.cpp; mainconfig diff --git a/upptst/Unicode/Win32.cpp b/upptst/Unicode/Win32.cpp new file mode 100644 index 000000000..902e27365 --- /dev/null +++ b/upptst/Unicode/Win32.cpp @@ -0,0 +1,149 @@ +#include + +using namespace Upp; + +#ifdef PLATFORM_WIN32 + +namespace Upp { +HFONT GetWin32Font(Font fnt, int angle); +}; + +int GlyphIndex(HDC hdc, int ch) +{ + WString h = ToUtf16(ch); + GCP_RESULTSW gcp_results = { sizeof(GCP_RESULTS) }; + gcp_results.nGlyphs = 4; + wchar gi[4]; + gcp_results.lpGlyphs = gi; + GetCharacterPlacementW(hdc, ~h, h.GetCount(), 0, &gcp_results, GCP_GLYPHSHAPE); + return (word)gi[0]; +} + +int GlyphIndex(Font fnt, int ch) +{ + RTIMING("GlyphIndex"); + static HDC hdc = CreateIC("DISPLAY", NULL, NULL, NULL); + static Font current = Null; + if(current == ch) + return GlyphIndex(hdc, ch); + HFONT hfont = GetWin32Font(fnt, 0); + if(hfont) { + current = fnt; + ::SelectObject(hdc, hfont); + return GlyphIndex(hdc, ch); + } + return -1; +} + +bool HasCodepoint(Font fnt, int ch) +{ + static Vector tofu; + int& q = tofu.At(fnt.GetFace(), Null); + if(IsNull(q)) + q = GlyphIndex(fnt, 1); // this is 99.99% to produce tofu + return ch == ' ' || GlyphIndex(fnt, ch) != q; +} + +int GetAdvanceWidth(Font fnt, int ch) +{ + TIMING("Glyph"); + HFONT hfont = GetWin32Font(fnt, 0); + VERIFY(hfont); + int r = -1; + if(hfont) { + HDC hdc = CreateIC("DISPLAY", NULL, NULL, NULL); + HFONT ohfont = (HFONT) ::SelectObject(hdc, hfont); + GLYPHMETRICS gm; + memset(&gm, 0, sizeof(gm)); + MAT2 m_matrix; + memset8(&m_matrix, 0, sizeof(m_matrix)); + m_matrix.eM11.value = 1; + m_matrix.eM22.value = 1; + ch = GlyphIndex(hdc, ch); + int gsz = GetGlyphOutlineW(hdc, ch, GGO_NATIVE|GGO_METRICS|GGO_GLYPH_INDEX, &gm, 0, NULL, &m_matrix); + if(gsz != GDI_ERROR && gm.gmCellIncX != 75) + r = gm.gmCellIncX; + ::SelectObject(hdc, ohfont); + ::DeleteDC(hdc); + } + return r; +} + +double fx_to_dbl(const FIXED& p) { + return double(p.value) + double(p.fract) * (1.0 / 65536.0); +} + +Pointf fx_to_dbl(const Pointf& pp, const POINTFX& p) { + return Pointf(pp.x + fx_to_dbl(p.x), pp.y - fx_to_dbl(p.y)); +} + +void RenderCharPath2(const char* gbuf, unsigned total_size, FontGlyphConsumer& sw, double xx, double yy) +{ + const char* cur_glyph = gbuf; + const char* end_glyph = gbuf + total_size; + Pointf pp(xx, yy); + while(cur_glyph < end_glyph) { + const TTPOLYGONHEADER* th = (TTPOLYGONHEADER*)cur_glyph; + const char* end_poly = cur_glyph + th->cb; + const char* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER); + sw.Move(fx_to_dbl(pp, th->pfxStart)); + while(cur_poly < end_poly) { + const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly; + if (pc->wType == TT_PRIM_LINE) + for(int i = 0; i < pc->cpfx; i++) + sw.Line(fx_to_dbl(pp, pc->apfx[i])); + if (pc->wType == TT_PRIM_QSPLINE) + for(int u = 0; u < pc->cpfx - 1; u++) { + Pointf b = fx_to_dbl(pp, pc->apfx[u]); + Pointf c = fx_to_dbl(pp, pc->apfx[u + 1]); + if (u < pc->cpfx - 2) + c = Mid(b, c); + sw.Quadratic(b, c); + } + cur_poly += sizeof(WORD) * 2 + sizeof(POINTFX) * pc->cpfx; + } + sw.Close(); + cur_glyph += th->cb; + } +} + +void RenderCodepoint(FontGlyphConsumer& sw, double x, double y, int ch, Font fnt) +{ + HFONT hfont = GetWin32Font(fnt, 0); + VERIFY(hfont); + if(hfont) { + HDC hdc = CreateIC("DISPLAY", NULL, NULL, NULL); + HFONT ohfont = (HFONT) ::SelectObject(hdc, hfont); + GLYPHMETRICS gm; + MAT2 m_matrix; + memset8(&m_matrix, 0, sizeof(m_matrix)); + m_matrix.eM11.value = 1; + m_matrix.eM22.value = 1; + ch = GlyphIndex(hdc, ch); + int gsz = GetGlyphOutlineW(hdc, ch, GGO_NATIVE|GGO_UNHINTED|GGO_GLYPH_INDEX, &gm, 0, NULL, &m_matrix); + if(gsz >= 0) { + StringBuffer gb(gsz); + gsz = GetGlyphOutlineW(hdc, ch, GGO_NATIVE|GGO_UNHINTED|GGO_GLYPH_INDEX, &gm, gsz, ~gb, &m_matrix); + if(gsz >= 0) + RenderCharPath2(~gb, gsz, sw, x, y + fnt.GetAscent()); + } + ::SelectObject(hdc, ohfont); + ::DeleteDC(hdc); + } +} + +void DrawCodepoint(Draw& w, int x, int y, int ch, Font font) +{ + if(SystemDraw *sw = dynamic_cast(&w)) { + WString h = ToUtf16(ch); + HFONT hfont = GetWin32Font(font, 0); + HDC hdc = sw->BeginGdi(); + HGDIOBJ orgfont = ::SelectObject(hdc, hfont); + ::SetTextColor(hdc, sw->GetColor(Black())); + ::ExtTextOutW(hdc, 100, 200, 0, NULL, ~h, 2, NULL); + ::SelectObject(hdc, orgfont); + sw->EndGdi(); + } +} + +#endif diff --git a/upptst/Unicode/main.cpp b/upptst/Unicode/main.cpp index 287670784..83812b36f 100644 --- a/upptst/Unicode/main.cpp +++ b/upptst/Unicode/main.cpp @@ -2,138 +2,13 @@ using namespace Upp; -namespace Upp { -HFONT GetWin32Font(Font fnt, int angle); -}; - -int GlyphIndex(HDC hdc, int ch) -{ - WString h = ToUtf16(ch); - GCP_RESULTSW gcp_results = { sizeof(GCP_RESULTS) }; - gcp_results.nGlyphs = 4; - wchar gi[4]; - gcp_results.lpGlyphs = gi; -// RTIMING("GetCharacterPlacement"); - GetCharacterPlacementW(hdc, ~h, h.GetCount(), 0, &gcp_results, GCP_GLYPHSHAPE); - return (word)gi[0]; -} - -int GlyphIndex(int ch, Font fnt) -{ - RTIMING("GlyphIndex"); - int r = -1; - HFONT hfont = GetWin32Font(fnt, 0); - VERIFY(hfont); - if(hfont) { - static HDC hdc = CreateIC("DISPLAY", NULL, NULL, NULL); - HFONT ohfont; - { - RTIMING("Select"); - ohfont = (HFONT) ::SelectObject(hdc, hfont); - } - r = GlyphIndex(hdc, ch); -// ::SelectObject(hdc, ohfont); -// ::DeleteDC(hdc); - } - return r; -} - -bool HasCodepoint(int ch, Font fnt) -{ - static Vector tofu; - int& q = tofu.At(fnt.GetFace(), Null); - if(IsNull(q)) - q = GlyphIndex(1, fnt); - return ch == ' ' || GlyphIndex(ch, fnt) != q; -} - -int GetAdvanceWidth(int ch, Font fnt) -{ - TIMING("Glyph"); - HFONT hfont = GetWin32Font(fnt, 0); - VERIFY(hfont); - int r = -1; - if(hfont) { - HDC hdc = CreateIC("DISPLAY", NULL, NULL, NULL); - HFONT ohfont = (HFONT) ::SelectObject(hdc, hfont); - GLYPHMETRICS gm; - memset(&gm, 0, sizeof(gm)); - MAT2 m_matrix; - memset8(&m_matrix, 0, sizeof(m_matrix)); - m_matrix.eM11.value = 1; - m_matrix.eM22.value = 1; - ch = GlyphIndex(hdc, ch); - int gsz = GetGlyphOutlineW(hdc, ch, GGO_NATIVE|GGO_METRICS|GGO_GLYPH_INDEX, &gm, 0, NULL, &m_matrix); - if(gsz != GDI_ERROR && gm.gmCellIncX != 75) - r = gm.gmCellIncX; - ::SelectObject(hdc, ohfont); - ::DeleteDC(hdc); - } - return r; -} - -double fx_to_dbl(const FIXED& p) { - return double(p.value) + double(p.fract) * (1.0 / 65536.0); -} - -Pointf fx_to_dbl(const Pointf& pp, const POINTFX& p) { - return Pointf(pp.x + fx_to_dbl(p.x), pp.y - fx_to_dbl(p.y)); -} - -void RenderCharPath2(const char* gbuf, unsigned total_size, FontGlyphConsumer& sw, double xx, double yy) -{ - const char* cur_glyph = gbuf; - const char* end_glyph = gbuf + total_size; - Pointf pp(xx, yy); - while(cur_glyph < end_glyph) { - const TTPOLYGONHEADER* th = (TTPOLYGONHEADER*)cur_glyph; - const char* end_poly = cur_glyph + th->cb; - const char* cur_poly = cur_glyph + sizeof(TTPOLYGONHEADER); - sw.Move(fx_to_dbl(pp, th->pfxStart)); - while(cur_poly < end_poly) { - const TTPOLYCURVE* pc = (const TTPOLYCURVE*)cur_poly; - if (pc->wType == TT_PRIM_LINE) - for(int i = 0; i < pc->cpfx; i++) - sw.Line(fx_to_dbl(pp, pc->apfx[i])); - if (pc->wType == TT_PRIM_QSPLINE) - for(int u = 0; u < pc->cpfx - 1; u++) { - Pointf b = fx_to_dbl(pp, pc->apfx[u]); - Pointf c = fx_to_dbl(pp, pc->apfx[u + 1]); - if (u < pc->cpfx - 2) - c = Mid(b, c); - sw.Quadratic(b, c); - } - cur_poly += sizeof(WORD) * 2 + sizeof(POINTFX) * pc->cpfx; - } - sw.Close(); - cur_glyph += th->cb; - } -} - -void RenderGlyph2(FontGlyphConsumer& sw, double x, double y, int ch, Font fnt) -{ - HFONT hfont = GetWin32Font(fnt, 0); - VERIFY(hfont); - if(hfont) { - HDC hdc = CreateIC("DISPLAY", NULL, NULL, NULL); - HFONT ohfont = (HFONT) ::SelectObject(hdc, hfont); - GLYPHMETRICS gm; - MAT2 m_matrix; - memset8(&m_matrix, 0, sizeof(m_matrix)); - m_matrix.eM11.value = 1; - m_matrix.eM22.value = 1; - ch = GlyphIndex(hdc, ch); - int gsz = GetGlyphOutlineW(hdc, ch, GGO_NATIVE|GGO_UNHINTED|GGO_GLYPH_INDEX, &gm, 0, NULL, &m_matrix); - if(gsz >= 0) { - StringBuffer gb(gsz); - gsz = GetGlyphOutlineW(hdc, ch, GGO_NATIVE|GGO_UNHINTED|GGO_GLYPH_INDEX, &gm, gsz, ~gb, &m_matrix); - if(gsz >= 0) - RenderCharPath2(~gb, gsz, sw, x, y + fnt.GetAscent()); - } - ::SelectObject(hdc, ohfont); - ::DeleteDC(hdc); - } -} +int GlyphIndex(Font fnt, int ch); +bool HasCodepoint(Font fnt, int ch); +int GetAdvanceWidth(Font fnt, int ch); +double fx_to_dbl(const FIXED& p); +Pointf fx_to_dbl(const Pointf& pp, const POINTFX& p); +void RenderCodepoint(FontGlyphConsumer& sw, double x, double y, int ch, Font fnt); +void DrawCodepoint(Draw& w, int x, int y, int ch, Font font); struct MyApp : TopWindow { SplitterFrame sf; @@ -144,47 +19,28 @@ struct MyApp : TopWindow { virtual void Paint(Draw& w) { Font font = Font(~face, 100); // Font().Height(100).FaceName("MingLiU-ExtB"); + font.Italic(); // Font font = Font().Height(100).FaceName("MingLiU-ExtB"); w.DrawRect(GetSize(), White()); int x = 0; int y = 40; int ch = ScanInt(~~code, NULL, 16); if(SystemDraw *sw = dynamic_cast(&w)) { + int cx = GetAdvanceWidth(font, ch); + w.DrawRect(100, 200, cx, 20, WhiteGray()); + + DrawCodepoint(w, 100, 200, ch, font); + WString h = ToUtf16(ch); String f; for(wchar ch : h) f << Format("%x ", ch); - // Get glyph indices for the string - - DDUMP(ch); - int cx = GetAdvanceWidth(ch, font); - w.DrawRect(100, 200, cx, 20, WhiteGray()); - - HFONT hfont = GetWin32Font(font, 0); - DDUMP(hfont); - HDC hdc = sw->BeginGdi(); - DDUMP(hdc); - HGDIOBJ orgfont = ::SelectObject(hdc, hfont); - DDUMP(GetLastErrorMessage()); - DDUMP(h.GetCount()); - ::SetTextColor(hdc, sw->GetColor(Black())); - ::ExtTextOutW(hdc, 100, 200, 0, NULL, ~h, 2, NULL); - DDUMP(GetLastErrorMessage()); - - GCP_RESULTSW gcp_results = { sizeof(GCP_RESULTS) }; - gcp_results.nGlyphs = 2; - wchar gi[4]; - gcp_results.lpGlyphs = gi; - GetCharacterPlacementW(hdc, ~h, h.GetCount(), 0, &gcp_results, GCP_GLYPHSHAPE); - f << ", glyph: " << (int)gi[0]; + f << ", glyph: " << GlyphIndex(font, ch); - if(HasCodepoint(ch, font)) + if(HasCodepoint(font, ch)) f << " EXISTS"; - ::SelectObject(hdc, orgfont); - DDUMP(GetLastErrorMessage()); - sw->EndGdi(); DDUMP(GetLastErrorMessage()); w.DrawText(20, 300, f); @@ -214,7 +70,7 @@ struct MyApp : TopWindow { ip.Clear(Yellow()); PaintCharPath h; h.sw = &ip; - RenderGlyph2(h, 0, 0, ch, font); + RenderCodepoint(h, 0, 0, ch, font); ip.Fill(Black()); w.DrawImage(100, 400, ip); @@ -251,10 +107,19 @@ struct MyApp : TopWindow { face << [=] { DLOG("================"); - for(int i = 0; i < 0x2ffff; i++) { + int i = 32; + int pi = 0; + while(i < 0x30000) { Font font = Font(~face, 100); - if(HasCodepoint(i, font)) - DDUMPHEX(i); + if(HasCodepoint(font, i)) { + int i0 = i; + while(HasCodepoint(font, i)) + i++; + DLOG(i0 << " " << i0 - pi << " " << i - i0); + pi = i; + } + else + i++; } };