Cygwin: console: improve replacement char algorithm
Try various Unicode characters which may be used as a replacement character in case an invalid character has to be printed. Current list is 0xfffd "REPLACEMENT CHARACTER", 0x25a1 "WHITE SQUARE", and 0x2592 "MEDIUM SHADE" in that order. Additionally workaround a problem with some fonts (namely DejaVu Sans Mono) which are returned wit ha broken fontname with trailing stray characters. Signed-off-by: Corinna Vinschen <corinna@vinschen.de>
This commit is contained in:
		| @@ -640,6 +640,12 @@ LoadDLLfunc (LsaRegisterLogonProcess, 12, secur32) | ||||
|  | ||||
| LoadDLLfunc (SHGetDesktopFolder, 4, shell32) | ||||
|  | ||||
| LoadDLLfunc (CreateFontW, 56, gdi32) | ||||
| LoadDLLfunc (DeleteObject, 4, gdi32) | ||||
| LoadDLLfunc (EnumFontFamiliesExW, 20, gdi32) | ||||
| LoadDLLfunc (GetGlyphIndicesW, 20, gdi32) | ||||
| LoadDLLfunc (SelectObject, 8, gdi32) | ||||
|  | ||||
| LoadDLLfunc (CloseClipboard, 0, user32) | ||||
| LoadDLLfunc (CloseDesktop, 4, user32) | ||||
| LoadDLLfunc (CloseWindowStation, 4, user32) | ||||
| @@ -651,6 +657,7 @@ LoadDLLfunc (DispatchMessageW, 4, user32) | ||||
| LoadDLLfunc (EmptyClipboard, 0, user32) | ||||
| LoadDLLfunc (EnumWindows, 8, user32) | ||||
| LoadDLLfunc (GetClipboardData, 4, user32) | ||||
| LoadDLLfunc (GetDC, 4, user32) | ||||
| LoadDLLfunc (GetForegroundWindow, 0, user32) | ||||
| LoadDLLfunc (GetKeyboardLayout, 4, user32) | ||||
| LoadDLLfunc (GetMessageW, 16, user32) | ||||
|   | ||||
| @@ -1971,14 +1971,103 @@ bad_escape: | ||||
|     } | ||||
| } | ||||
|  | ||||
| #define NUM_REPLACEMENT_CHARS	3 | ||||
|  | ||||
| static const wchar_t replacement_char[NUM_REPLACEMENT_CHARS] = | ||||
| { | ||||
|   0xfffd, /* REPLACEMENT CHARACTER */ | ||||
|   0x25a1, /* WHITE SQUARE */ | ||||
|   0x2592  /* MEDIUM SHADE */ | ||||
| }; | ||||
| /* nFont member is always 0 so we have to use the facename. */ | ||||
| static WCHAR cons_facename[LF_FACESIZE]; | ||||
| static int rp_char_idx; | ||||
| static HDC cdc; | ||||
|  | ||||
| static int CALLBACK | ||||
| enum_proc (const LOGFONTW *lf, const TEXTMETRICW *tm, | ||||
| 	   DWORD FontType, LPARAM lParam) | ||||
| { | ||||
|   int *done = (int *) lParam; | ||||
|   *done = 1; | ||||
|   return 0; | ||||
| } | ||||
|  | ||||
| static void | ||||
| check_font (HANDLE hdl) | ||||
| { | ||||
|   CONSOLE_FONT_INFOEX cfi; | ||||
|   LOGFONTW lf; | ||||
|  | ||||
|   cfi.cbSize = sizeof cfi; | ||||
|   if (!GetCurrentConsoleFontEx (hdl, 0, &cfi)) | ||||
|     return; | ||||
|   /* Switched font? */ | ||||
|   if (wcscmp (cons_facename, cfi.FaceName) == 0) | ||||
|     return; | ||||
|   if (!cdc && !(cdc = GetDC (GetConsoleWindow ()))) | ||||
|     return; | ||||
|   /* Some FaceNames like DejaVu Sans Mono are sometimes returned with stray | ||||
|      trailing chars.  Fix it. */ | ||||
|   lf.lfCharSet = ANSI_CHARSET; | ||||
|   lf.lfPitchAndFamily = FIXED_PITCH | FF_DONTCARE; | ||||
|   wchar_t *cp = wcpcpy (lf.lfFaceName, cfi.FaceName) - 1; | ||||
|   int done = 0; | ||||
|   do | ||||
|     { | ||||
|       EnumFontFamiliesExW (cdc, &lf, enum_proc, (LPARAM) &done, 0); | ||||
|       if (!done && cp > lf.lfFaceName) | ||||
| 	*cp-- = L'\0'; | ||||
|     } | ||||
|   while (!done); | ||||
|   /* Yes.  Check for the best replacement char. */ | ||||
|   HFONT f = CreateFontW (0, 0, 0, 0, | ||||
| 			 cfi.FontWeight, FALSE, FALSE, FALSE, | ||||
| 			 ANSI_CHARSET, OUT_DEFAULT_PRECIS, | ||||
| 			 CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, | ||||
| 			 FIXED_PITCH | FF_DONTCARE, lf.lfFaceName); | ||||
|   if (!f) | ||||
|     return; | ||||
|  | ||||
|   HFONT old_f = (HFONT) SelectObject(cdc, f); | ||||
|   if (old_f) | ||||
|     { | ||||
|       WORD glyph_idx[NUM_REPLACEMENT_CHARS]; | ||||
|  | ||||
|       if (GetGlyphIndicesW (cdc, replacement_char, | ||||
| 			    NUM_REPLACEMENT_CHARS, glyph_idx, | ||||
| 			    GGI_MARK_NONEXISTING_GLYPHS) != GDI_ERROR) | ||||
| 	{ | ||||
| 	  int i; | ||||
|  | ||||
| 	  for (i = 0; i < NUM_REPLACEMENT_CHARS; ++i) | ||||
| 	    if (glyph_idx[i] != 0xffff) | ||||
| 	      break; | ||||
| 	  if (i == NUM_REPLACEMENT_CHARS) | ||||
| 	    i = 0; | ||||
| 	  rp_char_idx = i; | ||||
| 	  /* Note that we copy the original name returned by | ||||
| 	     GetCurrentConsoleFontEx, even if it was broken. | ||||
| 	     This allows an early return, rather than to store | ||||
| 	     the fixed name and then having to enum font families | ||||
| 	     all over again. */ | ||||
| 	  wcscpy (cons_facename, cfi.FaceName); | ||||
| 	} | ||||
|       SelectObject (cdc, old_f); | ||||
|     } | ||||
|   DeleteObject (f); | ||||
| } | ||||
|  | ||||
| /* This gets called when we found an invalid input character. | ||||
|    Print Unicode REPLACEMENT CHARACTER (UTF 0xfffd). */ | ||||
|    Print one of the above Unicode chars as replacement char. */ | ||||
| inline void | ||||
| fhandler_console::write_replacement_char () | ||||
| { | ||||
|   static const wchar_t replacement_char = 0xfffd; /* REPLACEMENT CHARACTER */ | ||||
|   check_font (get_output_handle ()); | ||||
|  | ||||
|   DWORD done; | ||||
|   WriteConsoleW (get_output_handle (), &replacement_char, 1, &done, 0); | ||||
|   WriteConsoleW (get_output_handle (), &replacement_char[rp_char_idx], 1, | ||||
| 		 &done, 0); | ||||
| } | ||||
|  | ||||
| const unsigned char * | ||||
|   | ||||
		Reference in New Issue
	
	Block a user