1// Copyright 2014 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
5#include "ui/gfx/font_list_impl.h"
6
7#include <algorithm>
8
9#include "base/logging.h"
10#include "base/strings/string_number_conversions.h"
11#include "base/strings/string_split.h"
12#include "base/strings/string_util.h"
13#include "ui/gfx/font.h"
14
15namespace {
16
17// Parses font description into |font_names|, |font_style| and |font_size|.
18void ParseFontDescriptionString(const std::string& font_description_string,
19                                std::vector<std::string>* font_names,
20                                int* font_style,
21                                int* font_size) {
22  base::SplitString(font_description_string, ',', font_names);
23  DCHECK_GT(font_names->size(), 1U);
24
25  // The last item is [STYLE_OPTIONS] SIZE.
26  std::vector<std::string> styles_size;
27  base::SplitString(font_names->back(), ' ', &styles_size);
28  DCHECK(!styles_size.empty());
29  base::StringToInt(styles_size.back(), font_size);
30  DCHECK_GT(*font_size, 0);
31  font_names->pop_back();
32
33  // Font supports BOLD and ITALIC; underline is supported via RenderText.
34  *font_style = 0;
35  for (size_t i = 0; i < styles_size.size() - 1; ++i) {
36    // Styles are separated by white spaces. base::SplitString splits styles
37    // by space, and it inserts empty string for continuous spaces.
38    if (styles_size[i].empty())
39      continue;
40    if (!styles_size[i].compare("Bold"))
41      *font_style |= gfx::Font::BOLD;
42    else if (!styles_size[i].compare("Italic"))
43      *font_style |= gfx::Font::ITALIC;
44    else
45      NOTREACHED();
46  }
47}
48
49// Returns the font style and size as a string.
50std::string FontStyleAndSizeToString(int font_style, int font_size) {
51  std::string result;
52  if (font_style & gfx::Font::BOLD)
53    result += "Bold ";
54  if (font_style & gfx::Font::ITALIC)
55    result += "Italic ";
56  result += base::IntToString(font_size);
57  result += "px";
58  return result;
59}
60
61// Returns font description from |font_names|, |font_style|, and |font_size|.
62std::string BuildFontDescription(const std::vector<std::string>& font_names,
63                                 int font_style,
64                                 int font_size) {
65  std::string description = JoinString(font_names, ',');
66  description += "," + FontStyleAndSizeToString(font_style, font_size);
67  return description;
68}
69
70}  // namespace
71
72namespace gfx {
73
74FontListImpl::FontListImpl(const std::string& font_description_string)
75    : font_description_string_(font_description_string),
76      common_height_(-1),
77      common_baseline_(-1),
78      font_style_(-1),
79      font_size_(-1) {
80  DCHECK(!font_description_string.empty());
81  // DCHECK description string ends with "px" for size in pixel.
82  DCHECK(EndsWith(font_description_string, "px", true));
83}
84
85FontListImpl::FontListImpl(const std::vector<std::string>& font_names,
86                           int font_style,
87                           int font_size)
88    : font_description_string_(BuildFontDescription(font_names, font_style,
89                                                    font_size)),
90      common_height_(-1),
91      common_baseline_(-1),
92      font_style_(font_style),
93      font_size_(font_size) {
94  DCHECK(!font_names.empty());
95  DCHECK(!font_names[0].empty());
96}
97
98FontListImpl::FontListImpl(const std::vector<Font>& fonts)
99    : fonts_(fonts),
100      common_height_(-1),
101      common_baseline_(-1),
102      font_style_(-1),
103      font_size_(-1) {
104  DCHECK(!fonts.empty());
105  font_style_ = fonts[0].GetStyle();
106  font_size_ = fonts[0].GetFontSize();
107#if DCHECK_IS_ON
108  for (size_t i = 1; i < fonts.size(); ++i) {
109    DCHECK_EQ(fonts[i].GetStyle(), font_style_);
110    DCHECK_EQ(fonts[i].GetFontSize(), font_size_);
111  }
112#endif
113}
114
115FontListImpl::FontListImpl(const Font& font)
116    : common_height_(-1),
117      common_baseline_(-1),
118      font_style_(-1),
119      font_size_(-1) {
120  fonts_.push_back(font);
121}
122
123FontListImpl* FontListImpl::Derive(int size_delta, int font_style) const {
124  // If there is a font vector, derive from that.
125  if (!fonts_.empty()) {
126    std::vector<Font> fonts = fonts_;
127    for (size_t i = 0; i < fonts.size(); ++i)
128      fonts[i] = fonts[i].Derive(size_delta, font_style);
129    return new FontListImpl(fonts);
130  }
131
132  // Otherwise, parse the font description string to derive from it.
133  std::vector<std::string> font_names;
134  int old_size;
135  int old_style;
136  ParseFontDescriptionString(font_description_string_, &font_names,
137                             &old_style, &old_size);
138  const int size = std::max(1, old_size + size_delta);
139  return new FontListImpl(font_names, font_style, size);
140}
141
142int FontListImpl::GetHeight() const {
143  if (common_height_ == -1)
144    CacheCommonFontHeightAndBaseline();
145  return common_height_;
146}
147
148int FontListImpl::GetBaseline() const {
149  if (common_baseline_ == -1)
150    CacheCommonFontHeightAndBaseline();
151  return common_baseline_;
152}
153
154int FontListImpl::GetCapHeight() const {
155  // Assume the primary font is used to render Latin characters.
156  return GetPrimaryFont().GetCapHeight();
157}
158
159int FontListImpl::GetExpectedTextWidth(int length) const {
160  // Rely on the primary font metrics for the time being.
161  return GetPrimaryFont().GetExpectedTextWidth(length);
162}
163
164int FontListImpl::GetFontStyle() const {
165  if (font_style_ == -1)
166    CacheFontStyleAndSize();
167  return font_style_;
168}
169
170const std::string& FontListImpl::GetFontDescriptionString() const {
171  if (font_description_string_.empty()) {
172    DCHECK(!fonts_.empty());
173    for (size_t i = 0; i < fonts_.size(); ++i) {
174      std::string name = fonts_[i].GetFontName();
175      font_description_string_ += name;
176      font_description_string_ += ',';
177    }
178    // All fonts have the same style and size.
179    font_description_string_ +=
180        FontStyleAndSizeToString(fonts_[0].GetStyle(), fonts_[0].GetFontSize());
181  }
182  return font_description_string_;
183}
184
185int FontListImpl::GetFontSize() const {
186  if (font_size_ == -1)
187    CacheFontStyleAndSize();
188  return font_size_;
189}
190
191const std::vector<Font>& FontListImpl::GetFonts() const {
192  if (fonts_.empty()) {
193    DCHECK(!font_description_string_.empty());
194
195    std::vector<std::string> font_names;
196    // It's possible that gfx::Font::UNDERLINE is specified and it's already
197    // stored in |font_style_| but |font_description_string_| doesn't have the
198    // underline info.  So we should respect |font_style_| as long as it's
199    // valid.
200    int style = 0;
201    ParseFontDescriptionString(font_description_string_, &font_names,
202                               &style, &font_size_);
203    if (font_style_ == -1)
204      font_style_ = style;
205    for (size_t i = 0; i < font_names.size(); ++i) {
206      DCHECK(!font_names[i].empty());
207
208      Font font(font_names[i], font_size_);
209      if (font_style_ == Font::NORMAL)
210        fonts_.push_back(font);
211      else
212        fonts_.push_back(font.Derive(0, font_style_));
213    }
214  }
215  return fonts_;
216}
217
218const Font& FontListImpl::GetPrimaryFont() const {
219  return GetFonts()[0];
220}
221
222FontListImpl::~FontListImpl() {}
223
224void FontListImpl::CacheCommonFontHeightAndBaseline() const {
225  int ascent = 0;
226  int descent = 0;
227  const std::vector<Font>& fonts = GetFonts();
228  for (std::vector<Font>::const_iterator i = fonts.begin();
229       i != fonts.end(); ++i) {
230    ascent = std::max(ascent, i->GetBaseline());
231    descent = std::max(descent, i->GetHeight() - i->GetBaseline());
232  }
233  common_height_ = ascent + descent;
234  common_baseline_ = ascent;
235}
236
237void FontListImpl::CacheFontStyleAndSize() const {
238  if (!fonts_.empty()) {
239    font_style_ = fonts_[0].GetStyle();
240    font_size_ = fonts_[0].GetFontSize();
241  } else {
242    std::vector<std::string> font_names;
243    ParseFontDescriptionString(font_description_string_, &font_names,
244                               &font_style_, &font_size_);
245  }
246}
247
248}  // namespace gfx
249