1 /* 2 * PROJECT: ReactOS api tests 3 * LICENSE: GPL - See COPYING in the top level directory 4 * PURPOSE: Test for AddFontMemResourceEx 5 * PROGRAMMERS: Mark Jansen 6 * 7 * PanosePitchTest + TTCTestV by Katayama Hirofumi MZ, licensed under CC BY 8 * Shadows_Into_Light by Kimberly Geswein, licensed under OFL 9 * Captured from firefox, embedded on reactos.org 10 */ 11 12 #include "precomp.h" 13 14 typedef struct _fnt_res 15 { 16 const char* FontName; 17 TEXTMETRICA tm; 18 } fnt_res; 19 20 typedef struct _fnt_test 21 { 22 const char* ResourceName; 23 int NumFaces; 24 fnt_res res[4]; 25 } fnt_test; 26 27 28 29 static fnt_test test_data[] = 30 { 31 { 32 /* .ResourceName = */ "PanosePitchTest.ttf", 33 /* .NumFaces = */ 2, 34 /* .res = */ 35 { 36 { 37 /* .FontName = */ "PanosePitchTest", 38 { 39 /* .tm.tmHeight = */ 11, 40 /* .tm.tmAscent = */ 11, 41 /* .tm.tmDescent = */ 0, 42 /* .tm.tmInternalLeading = */ -5, 43 /* .tm.tmExternalLeading = */ 1, 44 /* .tm.tmAveCharWidth = */ 8, 45 /* .tm.tmMaxCharWidth = */ 11, 46 /* .tm.tmWeight = */ FW_NORMAL, 47 /* .tm.tmOverhang = */ 0, 48 /* .tm.tmDigitizedAspectX = */ 96, 49 /* .tm.tmDigitizedAspectY = */ 96, 50 /* .tm.tmFirstChar = */ 63, 51 /* .tm.tmLastChar = */ 65, 52 /* .tm.tmDefaultChar = */ 165, 53 /* .tm.tmBreakChar = */ 65, 54 /* .tm.tmItalic = */ 0, 55 /* .tm.tmUnderlined = */ 0, 56 /* .tm.tmStruckOut = */ 0, 57 /* .tm.tmPitchAndFamily = */ TMPF_TRUETYPE | TMPF_VECTOR, 58 /* .tm.tmCharSet = */ SHIFTJIS_CHARSET, 59 } 60 }, 61 { 62 /* .FontName = */ "@PanosePitchTest", 63 { 64 /* .tm.tmHeight = */ 11, 65 /* .tm.tmAscent = */ 11, 66 /* .tm.tmDescent = */ 0, 67 /* .tm.tmInternalLeading = */ -5, 68 /* .tm.tmExternalLeading = */ 1, 69 /* .tm.tmAveCharWidth = */ 8, 70 /* .tm.tmMaxCharWidth = */ 11, 71 /* .tm.tmWeight = */ FW_NORMAL, 72 /* .tm.tmOverhang = */ 0, 73 /* .tm.tmDigitizedAspectX = */ 96, 74 /* .tm.tmDigitizedAspectY = */ 96, 75 /* .tm.tmFirstChar = */ 63, 76 /* .tm.tmLastChar = */ 65, 77 /* .tm.tmDefaultChar = */ 165, 78 /* .tm.tmBreakChar = */ 65, 79 /* .tm.tmItalic = */ 0, 80 /* .tm.tmUnderlined = */ 0, 81 /* .tm.tmStruckOut = */ 0, 82 /* .tm.tmPitchAndFamily = */ TMPF_TRUETYPE | TMPF_VECTOR, 83 /* .tm.tmCharSet = */ SHIFTJIS_CHARSET, 84 } 85 }, 86 }, 87 }, 88 { 89 /* .ResourceName = */ "TTCTestV.ttc", 90 /* .NumFaces = */ 3, 91 /* .res = */ 92 { 93 { 94 /* .FontName = */ "No1Of3in1", 95 { 96 /* .tm.tmHeight = */ 12, 97 /* .tm.tmAscent = */ 12, 98 /* .tm.tmDescent = */ 0, 99 /* .tm.tmInternalLeading = */ -4, 100 /* .tm.tmExternalLeading = */ 1, 101 /* .tm.tmAveCharWidth = */ -525, 102 /* .tm.tmMaxCharWidth = */ 6, 103 /* .tm.tmWeight = */ FW_NORMAL, 104 /* .tm.tmOverhang = */ 0, 105 /* .tm.tmDigitizedAspectX = */ 96, 106 /* .tm.tmDigitizedAspectY = */ 96, 107 /* .tm.tmFirstChar = */ 63, 108 /* .tm.tmLastChar = */ 65, 109 /* .tm.tmDefaultChar = */ 64, 110 /* .tm.tmBreakChar = */ 65, 111 /* .tm.tmItalic = */ 0, 112 /* .tm.tmUnderlined = */ 0, 113 /* .tm.tmStruckOut = */ 0, 114 /* .tm.tmPitchAndFamily = */ TMPF_TRUETYPE | TMPF_VECTOR | TMPF_FIXED_PITCH, 115 /* .tm.tmCharSet = */ ANSI_CHARSET, 116 } 117 }, 118 { 119 /* .FontName = */ "No2Of3in1", 120 { 121 /* .tm.tmHeight = */ 12, 122 /* .tm.tmAscent = */ 12, 123 /* .tm.tmDescent = */ 0, 124 /* .tm.tmInternalLeading = */ -4, 125 /* .tm.tmExternalLeading = */ 1, 126 /* .tm.tmAveCharWidth = */ 8, 127 /* .tm.tmMaxCharWidth = */ 7, 128 /* .tm.tmWeight = */ FW_NORMAL, 129 /* .tm.tmOverhang = */ 0, 130 /* .tm.tmDigitizedAspectX = */ 96, 131 /* .tm.tmDigitizedAspectY = */ 96, 132 /* .tm.tmFirstChar = */ 63, 133 /* .tm.tmLastChar = */ 65, 134 /* .tm.tmDefaultChar = */ 64, 135 /* .tm.tmBreakChar = */ 65, 136 /* .tm.tmItalic = */ 0, 137 /* .tm.tmUnderlined = */ 0, 138 /* .tm.tmStruckOut = */ 0, 139 /* .tm.tmPitchAndFamily = */ TMPF_TRUETYPE | TMPF_VECTOR | TMPF_FIXED_PITCH, 140 /* .tm.tmCharSet = */ ANSI_CHARSET, 141 } 142 }, 143 { 144 /* .FontName = */ "No3Of3in1V", 145 { 146 /* .tm.tmHeight = */ 12, 147 /* .tm.tmAscent = */ 12, 148 /* .tm.tmDescent = */ 0, 149 /* .tm.tmInternalLeading = */ -4, 150 /* .tm.tmExternalLeading = */ 1, 151 /* .tm.tmAveCharWidth = */ 8, 152 /* .tm.tmMaxCharWidth = */ 13, 153 /* .tm.tmWeight = */ FW_NORMAL, 154 /* .tm.tmOverhang = */ 0, 155 /* .tm.tmDigitizedAspectX = */ 96, 156 /* .tm.tmDigitizedAspectY = */ 96, 157 /* .tm.tmFirstChar = */ 63, 158 /* .tm.tmLastChar = */ 65, 159 /* .tm.tmDefaultChar = */ 64, 160 /* .tm.tmBreakChar = */ 65, 161 /* .tm.tmItalic = */ 0, 162 /* .tm.tmUnderlined = */ 0, 163 /* .tm.tmStruckOut = */ 0, 164 /* .tm.tmPitchAndFamily = */ FF_MODERN | TMPF_TRUETYPE | TMPF_VECTOR, 165 /* .tm.tmCharSet = */ ANSI_CHARSET, 166 } 167 }, 168 }, 169 }, 170 { 171 /* .ResourceName = */ "Shadows_Into_Light.ttf", 172 /* .NumFaces = */ 1, 173 /* .res = */ 174 { 175 { 176 /* .FontName = */ "ufaXaAlLOxCUGYJ7KN51UP2Q==", 177 { 178 /* .tm.tmHeight = */ 26, 179 /* .tm.tmAscent = */ 19, 180 /* .tm.tmDescent = */ 7, 181 /* .tm.tmInternalLeading = */ 10, 182 /* .tm.tmExternalLeading = */ 0, 183 /* .tm.tmAveCharWidth = */ 7, 184 /* .tm.tmMaxCharWidth = */ 23, 185 /* .tm.tmWeight = */ FW_NORMAL, 186 /* .tm.tmOverhang = */ 0, 187 /* .tm.tmDigitizedAspectX = */ 96, 188 /* .tm.tmDigitizedAspectY = */ 96, 189 /* .tm.tmFirstChar = */ 30, 190 /* .tm.tmLastChar = */ 255, 191 /* .tm.tmDefaultChar = */ 31, 192 /* .tm.tmBreakChar = */ 32, 193 /* .tm.tmItalic = */ 0, 194 /* .tm.tmUnderlined = */ 0, 195 /* .tm.tmStruckOut = */ 0, 196 /* .tm.tmPitchAndFamily = */ TMPF_TRUETYPE | TMPF_VECTOR | TMPF_FIXED_PITCH, 197 /* .tm.tmCharSet = */ ANSI_CHARSET, 198 } 199 }, 200 }, 201 }, 202 }; 203 204 205 #define ok_int2(expression) \ 206 do { \ 207 int _value = (expression); \ 208 ok(_value == (res->expression), "Wrong value for '%s', expected: %d, got: %d for %s/%s\n", \ 209 #expression, (int)(res->expression), _value, test_name, res->FontName); \ 210 } while (0) 211 212 #define ok_hex2(expression) \ 213 do { \ 214 int _value = (expression); \ 215 ok(_value == (res->expression), "Wrong value for '%s', expected: 0x%x, got: 0x%x for %s/%s\n", \ 216 #expression, (int)(res->expression), _value, test_name, res->FontName); \ 217 } while (0) 218 219 220 static void test_font_caps(HDC hdc, int test_index) 221 { 222 HGDIOBJ old; 223 TEXTMETRICA tm = { 0 }; 224 char name[64]; 225 BOOL ret; 226 HFONT font; 227 int n; 228 const char* test_name = test_data[test_index].ResourceName; 229 230 for (n = 0; test_data[test_index].res[n].FontName; ++n) 231 { 232 fnt_res* res = test_data[test_index].res + n; 233 font = CreateFontA(0, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, DEFAULT_CHARSET, 234 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, res->FontName); 235 236 if (font) 237 { 238 old = SelectObject(hdc, font); 239 240 memset(&tm, 0xaa, sizeof(tm)); 241 ret = GetTextMetricsA(hdc, &tm); 242 ok(ret, "GetTextMetricsA() for %s/%s\n", test_name, res->FontName); 243 244 SetLastError(0xdeadbeef); 245 ret = GetTextFaceA(hdc, sizeof(name), name); 246 ok(ret, "GetTextFaceA error %lu for %s/%s\n", GetLastError(), test_name, res->FontName); 247 if (ret) 248 { 249 ok(!strcmp(name, res->FontName), "FontName was %s, expected %s for %s/%s", name, res->FontName, test_name, res->FontName); 250 } 251 252 ok_int2(tm.tmHeight); 253 ok_int2(tm.tmAscent); 254 ok_int2(tm.tmDescent); 255 ok_int2(tm.tmInternalLeading); 256 ok_int2(tm.tmExternalLeading); 257 ok_int2(tm.tmAveCharWidth); 258 ok_int2(tm.tmMaxCharWidth); 259 ok_int2(tm.tmWeight); 260 ok_int2(tm.tmOverhang); 261 ok_int2(tm.tmDigitizedAspectX); 262 ok_int2(tm.tmDigitizedAspectY); 263 ok_int2(tm.tmFirstChar); 264 ok_int2(tm.tmLastChar); 265 ok_int2(tm.tmDefaultChar); 266 ok_int2(tm.tmBreakChar); 267 ok_int2(tm.tmItalic); 268 ok_int2(tm.tmUnderlined); 269 ok_int2(tm.tmStruckOut); 270 ok_hex2(tm.tmPitchAndFamily); 271 ok_int2(tm.tmCharSet); 272 273 SelectObject(hdc, old); 274 DeleteObject(font); 275 } 276 } 277 } 278 279 280 /* Not working as of 2017-04-08 on ReactOS */ 281 static BOOL is_font_available(HDC hdc, const char* fontName) 282 { 283 char name[64]; 284 BOOL ret; 285 286 HFONT font = CreateFontA(0, 0, 0, 0, FW_DONTCARE, FALSE, FALSE, FALSE, DEFAULT_CHARSET, 287 OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, fontName); 288 HGDIOBJ old = SelectObject(hdc, font); 289 290 291 SetLastError(0xdeadbeef); 292 293 ret = GetTextFaceA(hdc, sizeof(name), name); 294 ok(ret, "GetTextFaceA error %lu for %s\n", GetLastError(), fontName); 295 SelectObject(hdc, old); 296 DeleteObject(font); 297 298 if (ret) 299 { 300 return !_strcmpi(name, fontName); 301 } 302 return FALSE; 303 } 304 305 306 307 START_TEST(AddFontMemResourceEx) 308 { 309 HMODULE mod; 310 HRSRC hRsrc; 311 312 HGLOBAL hTemplate; 313 DWORD dwSize, dwNumFonts; 314 LPVOID pFont; 315 316 HANDLE hFont; 317 fnt_test* data; 318 int n; 319 320 HDC hdc = CreateCompatibleDC(NULL); 321 BOOL is_font_available_broken = is_font_available(hdc, "Nonexisting font name here"); 322 323 ok(!is_font_available_broken, "Validating font is broken! (CORE-13053)!\n"); 324 325 for (n = 0; n < _countof(test_data); ++n) 326 { 327 data = test_data + n; 328 329 mod = GetModuleHandle(NULL); 330 hRsrc = FindResourceA(mod, data->ResourceName, MAKEINTRESOURCE(RT_RCDATA)); 331 332 hTemplate = LoadResource(mod, hRsrc); 333 dwSize = SizeofResource(mod, hRsrc); 334 pFont = LockResource(hTemplate); 335 336 dwNumFonts = 0; 337 hFont = AddFontMemResourceEx(pFont, dwSize, NULL, &dwNumFonts); 338 ok(dwNumFonts == data->NumFaces, "dwNumFonts was %lu, expected %d for %s\n", dwNumFonts, data->NumFaces, data->ResourceName); 339 ok(hFont != NULL, "Expected valid handle for %s\n", data->ResourceName); 340 341 if (hFont) 342 { 343 test_font_caps(hdc, n); 344 RemoveFontMemResourceEx(hFont); 345 if (!is_font_available_broken) 346 { 347 ok (!is_font_available(hdc, data->ResourceName), "Expected font to be unregistered again for %s\n", data->ResourceName); 348 } 349 else 350 { 351 skip("Font unregister test for %s\n", data->ResourceName); 352 } 353 } 354 355 UnlockResource(hTemplate); 356 FreeResource(hTemplate); 357 } 358 359 DeleteDC(hdc); 360 } 361 362