system_fonts_win.cc 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. // Copyright 2019 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ui/gfx/system_fonts_win.h"
  5. #include <windows.h>
  6. #include "base/containers/flat_map.h"
  7. #include "base/logging.h"
  8. #include "base/no_destructor.h"
  9. #include "base/numerics/safe_conversions.h"
  10. #include "base/strings/sys_string_conversions.h"
  11. #include "base/trace_event/trace_event.h"
  12. #include "base/win/scoped_gdi_object.h"
  13. #include "base/win/scoped_hdc.h"
  14. #include "base/win/scoped_select_object.h"
  15. #include "ui/gfx/platform_font.h"
  16. namespace gfx {
  17. namespace win {
  18. namespace {
  19. class SystemFonts {
  20. public:
  21. const gfx::Font& GetFont(SystemFont system_font) {
  22. if (!IsInitialized())
  23. Initialize();
  24. auto it = system_fonts_.find(system_font);
  25. DCHECK(it != system_fonts_.end())
  26. << "System font #" << static_cast<int>(system_font) << " not found!";
  27. return it->second;
  28. }
  29. static SystemFonts* Instance() {
  30. static base::NoDestructor<SystemFonts> instance;
  31. return instance.get();
  32. }
  33. SystemFonts(const SystemFonts&) = delete;
  34. SystemFonts& operator=(const SystemFonts&) = delete;
  35. void ResetForTesting() {
  36. SystemFonts::is_initialized_ = false;
  37. SystemFonts::adjust_font_callback_ = nullptr;
  38. SystemFonts::get_minimum_font_size_callback_ = nullptr;
  39. system_fonts_.clear();
  40. }
  41. static int AdjustFontSize(int lf_height, int size_delta) {
  42. // Extract out the sign of |lf_height| - we'll add it back later.
  43. const int lf_sign = lf_height < 0 ? -1 : 1;
  44. lf_height = std::abs(lf_height);
  45. // Apply the size adjustment.
  46. lf_height += size_delta;
  47. // Make sure |lf_height| is not smaller than allowed min allowed font size.
  48. int min_font_size = 0;
  49. if (get_minimum_font_size_callback_) {
  50. min_font_size = get_minimum_font_size_callback_();
  51. DCHECK_GE(min_font_size, 0);
  52. }
  53. lf_height = std::max(min_font_size, lf_height);
  54. // Add back the sign.
  55. return lf_sign * lf_height;
  56. }
  57. static void AdjustLOGFONT(const FontAdjustment& font_adjustment,
  58. LOGFONT* logfont) {
  59. DCHECK_GT(font_adjustment.font_scale, 0.0);
  60. LONG new_height =
  61. base::ClampRound<LONG>(logfont->lfHeight * font_adjustment.font_scale);
  62. if (logfont->lfHeight && !new_height)
  63. new_height = logfont->lfHeight > 0 ? 1 : -1;
  64. logfont->lfHeight = new_height;
  65. if (!font_adjustment.font_family_override.empty()) {
  66. auto result = wcscpy_s(logfont->lfFaceName,
  67. font_adjustment.font_family_override.c_str());
  68. DCHECK_EQ(0, result) << "Font name "
  69. << font_adjustment.font_family_override
  70. << " cannot be copied into LOGFONT structure.";
  71. }
  72. }
  73. static Font GetFontFromLOGFONT(const LOGFONT& logfont) {
  74. // Finds a matching font by triggering font mapping. The font mapper finds
  75. // the closest physical font for a given logical font.
  76. base::win::ScopedHFONT font(::CreateFontIndirect(&logfont));
  77. base::win::ScopedGetDC screen_dc(NULL);
  78. base::win::ScopedSelectObject scoped_font(screen_dc, font.get());
  79. DCHECK(font.get()) << "Font for '"
  80. << base::SysWideToUTF8(logfont.lfFaceName)
  81. << "' has an invalid handle.";
  82. // Retrieve the name and height of the mapped font (physical font).
  83. LOGFONT mapped_font_info;
  84. GetObject(font.get(), sizeof(mapped_font_info), &mapped_font_info);
  85. std::string font_name = base::SysWideToUTF8(mapped_font_info.lfFaceName);
  86. TEXTMETRIC mapped_font_metrics;
  87. GetTextMetrics(screen_dc, &mapped_font_metrics);
  88. const int font_size =
  89. std::max<int>(1, mapped_font_metrics.tmHeight -
  90. mapped_font_metrics.tmInternalLeading);
  91. Font system_font =
  92. Font(PlatformFont::CreateFromNameAndSize(font_name, font_size));
  93. // System fonts may have different styles when they are manually changed by
  94. // the users (see crbug.com/989476).
  95. Font::FontStyle style = logfont.lfItalic == 0 ? Font::FontStyle::NORMAL
  96. : Font::FontStyle::ITALIC;
  97. Font::Weight weight = logfont.lfWeight == 0
  98. ? Font::Weight::NORMAL
  99. : static_cast<Font::Weight>(logfont.lfWeight);
  100. if (style != Font::FontStyle::NORMAL || weight != Font::Weight::NORMAL)
  101. system_font = system_font.Derive(0, style, weight);
  102. return system_font;
  103. }
  104. static void SetGetMinimumFontSizeCallback(
  105. GetMinimumFontSizeCallback callback) {
  106. DCHECK(!SystemFonts::IsInitialized());
  107. get_minimum_font_size_callback_ = callback;
  108. }
  109. static void SetAdjustFontCallback(AdjustFontCallback callback) {
  110. DCHECK(!SystemFonts::IsInitialized());
  111. adjust_font_callback_ = callback;
  112. }
  113. private:
  114. friend base::NoDestructor<SystemFonts>;
  115. SystemFonts() {}
  116. void Initialize() {
  117. TRACE_EVENT0("fonts", "gfx::SystemFonts::Initialize");
  118. NONCLIENTMETRICS metrics = {};
  119. metrics.cbSize = sizeof(metrics);
  120. const bool success = !!SystemParametersInfo(SPI_GETNONCLIENTMETRICS,
  121. metrics.cbSize, &metrics, 0);
  122. DCHECK(success);
  123. // NOTE(dfried): When rendering Chrome, we do all of our own font scaling
  124. // based on a number of factors, but what Windows reports to us has some
  125. // (but not all) of these factors baked in, and not in a way that is
  126. // display-consistent.
  127. //
  128. // For example, if your system DPI is 192 (200%) but you connect a monitor
  129. // with a standard DPI (100%) then even if Chrome starts on the second
  130. // monitor, we will be told the system font is 24pt instead of 12pt.
  131. // Conversely, if the system DPI is set to 96 (100%) but all of our monitors
  132. // are currently at 150%, Windows will still report 12pt fonts.
  133. //
  134. // The same is true with Text Zoom (a new accessibility feature). If zoom is
  135. // set to 150%, then Windows will report a font size of 18pt. But again, we
  136. // already take Text Zoom into account when rendering, so we want to account
  137. // for that.
  138. //
  139. // Our system fonts are in DIPs, so we must always take what Windows gives
  140. // us, figure out which adjustments it's making (and undo them), make our
  141. // own adjustments for localization (for example, we always render Hindi 25%
  142. // larger for readability), and only then can we store (and report) the
  143. // system fonts.
  144. // Factor in/out scale adjustment that fall outside what we can access here.
  145. // This includes l10n adjustments and those we have to ask UWP or other COM
  146. // interfaces for (since we don't have dependencies on that code from this
  147. // module, and don't want to implicitly invoke COM for testing purposes if
  148. // we don't have to).
  149. FontAdjustment font_adjustment;
  150. if (adjust_font_callback_) {
  151. adjust_font_callback_(&font_adjustment);
  152. }
  153. // Factor out system DPI scale that Windows will include in reported font
  154. // sizes. Note that these are (sadly) system-wide and do not reflect
  155. // specific displays' DPI.
  156. double system_scale = GetSystemScale();
  157. font_adjustment.font_scale /= system_scale;
  158. // Grab each of the fonts from the NONCLIENTMETRICS block, adjust it
  159. // appropriately, and store it in the font table.
  160. AddFont(SystemFont::kCaption, font_adjustment, &metrics.lfCaptionFont);
  161. AddFont(SystemFont::kSmallCaption, font_adjustment,
  162. &metrics.lfSmCaptionFont);
  163. AddFont(SystemFont::kMenu, font_adjustment, &metrics.lfMenuFont);
  164. AddFont(SystemFont::kMessage, font_adjustment, &metrics.lfMessageFont);
  165. AddFont(SystemFont::kStatus, font_adjustment, &metrics.lfStatusFont);
  166. is_initialized_ = true;
  167. }
  168. static bool IsInitialized() { return is_initialized_; }
  169. void AddFont(SystemFont system_font,
  170. const FontAdjustment& font_adjustment,
  171. LOGFONT* logfont) {
  172. TRACE_EVENT0("fonts", "gfx::SystemFonts::AddFont");
  173. // Make adjustments to the font as necessary.
  174. AdjustLOGFONT(font_adjustment, logfont);
  175. // Cap at minimum font size.
  176. logfont->lfHeight = AdjustFontSize(logfont->lfHeight, 0);
  177. system_fonts_.emplace(system_font, GetFontFromLOGFONT(*logfont));
  178. }
  179. // Returns the system DPI scale (standard DPI being 1.0).
  180. // TODO(dfried): move dpi.[h|cc] somewhere in base/win so we can share this
  181. // logic. However, note that the similar function in dpi.h is used many places
  182. // it ought not to be.
  183. static double GetSystemScale() {
  184. constexpr double kDefaultDPI = 96.0;
  185. base::win::ScopedGetDC screen_dc(nullptr);
  186. return ::GetDeviceCaps(screen_dc, LOGPIXELSY) / kDefaultDPI;
  187. }
  188. // Use a flat map for faster lookups.
  189. base::flat_map<SystemFont, gfx::Font> system_fonts_;
  190. static bool is_initialized_;
  191. // Font adjustment callback.
  192. static AdjustFontCallback adjust_font_callback_;
  193. // Minimum size callback.
  194. static GetMinimumFontSizeCallback get_minimum_font_size_callback_;
  195. };
  196. // static
  197. bool SystemFonts::is_initialized_ = false;
  198. // static
  199. AdjustFontCallback SystemFonts::adjust_font_callback_ = nullptr;
  200. // static
  201. GetMinimumFontSizeCallback SystemFonts::get_minimum_font_size_callback_ =
  202. nullptr;
  203. } // namespace
  204. void SetGetMinimumFontSizeCallback(GetMinimumFontSizeCallback callback) {
  205. SystemFonts::SetGetMinimumFontSizeCallback(callback);
  206. }
  207. void SetAdjustFontCallback(AdjustFontCallback callback) {
  208. SystemFonts::SetAdjustFontCallback(callback);
  209. }
  210. const Font& GetDefaultSystemFont() {
  211. // The message font is the closest font for a default system font from the
  212. // structure NONCLIENTMETRICS. The lfMessageFont field contains information
  213. // about the logical font used to display text in message boxes.
  214. return GetSystemFont(SystemFont::kMessage);
  215. }
  216. const Font& GetSystemFont(SystemFont system_font) {
  217. return SystemFonts::Instance()->GetFont(system_font);
  218. }
  219. NativeFont AdjustExistingSystemFont(NativeFont existing_font,
  220. const FontAdjustment& font_adjustment) {
  221. LOGFONT logfont;
  222. auto result = GetObject(existing_font, sizeof(logfont), &logfont);
  223. DCHECK(result);
  224. // Make the necessary adjustments.
  225. SystemFonts::AdjustLOGFONT(font_adjustment, &logfont);
  226. // Cap at minimum font size.
  227. logfont.lfHeight = SystemFonts::AdjustFontSize(logfont.lfHeight, 0);
  228. // Create the Font object.
  229. return ::CreateFontIndirect(&logfont);
  230. }
  231. int AdjustFontSize(int lf_height, int size_delta) {
  232. return SystemFonts::AdjustFontSize(lf_height, size_delta);
  233. }
  234. void AdjustLOGFONTForTesting(const FontAdjustment& font_adjustment,
  235. LOGFONT* logfont) {
  236. SystemFonts::AdjustLOGFONT(font_adjustment, logfont);
  237. }
  238. // Retrieve a FONT from a LOGFONT structure.
  239. Font GetFontFromLOGFONTForTesting(const LOGFONT& logfont) {
  240. return SystemFonts::GetFontFromLOGFONT(logfont);
  241. }
  242. void ResetSystemFontsForTesting() {
  243. SystemFonts::Instance()->ResetForTesting();
  244. }
  245. } // namespace win
  246. } // namespace gfx