1// Copyright (c) 2012 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.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
14namespace {
15
16// Parses font description into |font_names|, |font_style| and |font_size|.
17void ParseFontDescriptionString(const std::string& font_description_string,
18                                std::vector<std::string>* font_names,
19                                int* font_style,
20                                int* font_size) {
21  base::SplitString(font_description_string, ',', font_names);
22  DCHECK_GT(font_names->size(), 1U);
23
24  // The last item is [STYLE_OPTIONS] SIZE.
25  std::vector<std::string> styles_size;
26  base::SplitString(font_names->back(), ' ', &styles_size);
27  DCHECK(!styles_size.empty());
28  base::StringToInt(styles_size.back(), font_size);
29  DCHECK_GT(*font_size, 0);
30  font_names->pop_back();
31
32  // Font supports BOLD and ITALIC; underline is supported via RenderText.
33  *font_style = 0;
34  for (size_t i = 0; i < styles_size.size() - 1; ++i) {
35    // Styles are separated by white spaces. base::SplitString splits styles
36    // by space, and it inserts empty string for continuous spaces.
37    if (styles_size[i].empty())
38      continue;
39    if (!styles_size[i].compare("Bold"))
40      *font_style |= gfx::Font::BOLD;
41    else if (!styles_size[i].compare("Italic"))
42      *font_style |= gfx::Font::ITALIC;
43    else
44      NOTREACHED();
45  }
46}
47
48// Returns the font style and size as a string.
49std::string FontStyleAndSizeToString(int font_style, int font_size) {
50  std::string result;
51  if (font_style & gfx::Font::BOLD)
52    result += "Bold ";
53  if (font_style & gfx::Font::ITALIC)
54    result += "Italic ";
55  result += base::IntToString(font_size);
56  result += "px";
57  return result;
58}
59
60// Returns font description from |font_names|, |font_style|, and |font_size|.
61std::string BuildFontDescription(const std::vector<std::string>& font_names,
62                                 int font_style,
63                                 int font_size) {
64  std::string description = JoinString(font_names, ',');
65  description += "," + FontStyleAndSizeToString(font_style, font_size);
66  return description;
67}
68
69}  // namespace
70
71namespace gfx {
72
73FontList::FontList()
74    : common_height_(-1),
75      common_baseline_(-1),
76      font_style_(-1),
77      font_size_(-1) {
78  fonts_.push_back(Font());
79}
80
81FontList::FontList(const std::string& font_description_string)
82    : font_description_string_(font_description_string),
83      common_height_(-1),
84      common_baseline_(-1),
85      font_style_(-1),
86      font_size_(-1) {
87  DCHECK(!font_description_string.empty());
88  // DCHECK description string ends with "px" for size in pixel.
89  DCHECK(EndsWith(font_description_string, "px", true));
90}
91
92FontList::FontList(const std::vector<std::string>& font_names,
93                   int font_style,
94                   int font_size)
95    : font_description_string_(BuildFontDescription(font_names, font_style,
96                                                    font_size)),
97      common_height_(-1),
98      common_baseline_(-1),
99      font_style_(font_style),
100      font_size_(font_size) {
101  DCHECK(!font_names.empty());
102  DCHECK(!font_names[0].empty());
103}
104
105FontList::FontList(const std::vector<Font>& fonts)
106    : fonts_(fonts),
107      common_height_(-1),
108      common_baseline_(-1),
109      font_style_(-1),
110      font_size_(-1) {
111  DCHECK(!fonts.empty());
112  font_style_ = fonts[0].GetStyle();
113  font_size_ = fonts[0].GetFontSize();
114  if (DCHECK_IS_ON()) {
115    for (size_t i = 1; i < fonts.size(); ++i) {
116      DCHECK_EQ(fonts[i].GetStyle(), font_style_);
117      DCHECK_EQ(fonts[i].GetFontSize(), font_size_);
118    }
119  }
120}
121
122FontList::FontList(const Font& font)
123    : common_height_(-1),
124      common_baseline_(-1),
125      font_style_(-1),
126      font_size_(-1) {
127  fonts_.push_back(font);
128}
129
130FontList::~FontList() {
131}
132
133FontList FontList::DeriveFontList(int font_style) const {
134  return DeriveFontListWithSizeDeltaAndStyle(0, font_style);
135}
136
137FontList FontList::DeriveFontListWithSize(int size) const {
138  DCHECK_GT(size, 0);
139  return DeriveFontListWithSizeDeltaAndStyle(size - GetFontSize(),
140                                             GetFontStyle());
141}
142
143FontList FontList::DeriveFontListWithSizeDelta(int size_delta) const {
144  return DeriveFontListWithSizeDeltaAndStyle(size_delta, GetFontStyle());
145}
146
147FontList FontList::DeriveFontListWithSizeDeltaAndStyle(int size_delta,
148                                                       int style) const {
149  // If there is a font vector, derive from that.
150  if (!fonts_.empty()) {
151    std::vector<Font> fonts = fonts_;
152    for (size_t i = 0; i < fonts.size(); ++i)
153      fonts[i] = fonts[i].DeriveFont(size_delta, style);
154    return FontList(fonts);
155  }
156
157  // Otherwise, parse the font description string to derive from it.
158  std::vector<std::string> font_names;
159  int old_size;
160  int old_style;
161  ParseFontDescriptionString(font_description_string_, &font_names,
162                             &old_style, &old_size);
163  int size = old_size + size_delta;
164  DCHECK_GT(size, 0);
165  return FontList(font_names, style, size);
166}
167
168int FontList::GetHeight() const {
169  if (common_height_ == -1)
170    CacheCommonFontHeightAndBaseline();
171  return common_height_;
172}
173
174int FontList::GetBaseline() const {
175  if (common_baseline_ == -1)
176    CacheCommonFontHeightAndBaseline();
177  return common_baseline_;
178}
179
180int FontList::GetStringWidth(const base::string16& text) const {
181  // Rely on the primary font metrics for the time being.
182  // TODO(yukishiino): Not only the first font, all the fonts in the list should
183  // be taken into account to compute the pixels needed to display |text|.
184  // Also this method, including one in Font class, should be deprecated and
185  // client code should call Canvas::GetStringWidth(text, font_list) directly.
186  // Our plan is as follows:
187  //   1. Introduce the FontList version of Canvas::GetStringWidth().
188  //   2. Make client code call Canvas::GetStringWidth().
189  //   3. Retire {Font,FontList}::GetStringWidth().
190  return GetPrimaryFont().GetStringWidth(text);
191}
192
193int FontList::GetExpectedTextWidth(int length) const {
194  // Rely on the primary font metrics for the time being.
195  return GetPrimaryFont().GetExpectedTextWidth(length);
196}
197
198int FontList::GetFontStyle() const {
199  if (font_style_ == -1)
200    CacheFontStyleAndSize();
201  return font_style_;
202}
203
204const std::string& FontList::GetFontDescriptionString() const {
205  if (font_description_string_.empty()) {
206    DCHECK(!fonts_.empty());
207    for (size_t i = 0; i < fonts_.size(); ++i) {
208      std::string name = fonts_[i].GetFontName();
209      font_description_string_ += name;
210      font_description_string_ += ',';
211    }
212    // All fonts have the same style and size.
213    font_description_string_ +=
214        FontStyleAndSizeToString(fonts_[0].GetStyle(), fonts_[0].GetFontSize());
215  }
216  return font_description_string_;
217}
218
219int FontList::GetFontSize() const {
220  if (font_size_ == -1)
221    CacheFontStyleAndSize();
222  return font_size_;
223}
224
225const std::vector<Font>& FontList::GetFonts() const {
226  if (fonts_.empty()) {
227    DCHECK(!font_description_string_.empty());
228
229    std::vector<std::string> font_names;
230    ParseFontDescriptionString(font_description_string_, &font_names,
231                               &font_style_, &font_size_);
232    for (size_t i = 0; i < font_names.size(); ++i) {
233      DCHECK(!font_names[i].empty());
234
235      Font font(font_names[i], font_size_);
236      if (font_style_ == Font::NORMAL)
237        fonts_.push_back(font);
238      else
239        fonts_.push_back(font.DeriveFont(0, font_style_));
240    }
241  }
242  return fonts_;
243}
244
245const Font& FontList::GetPrimaryFont() const {
246  return GetFonts()[0];
247}
248
249void FontList::CacheCommonFontHeightAndBaseline() const {
250  int ascent = 0;
251  int descent = 0;
252  const std::vector<Font>& fonts = GetFonts();
253  for (std::vector<Font>::const_iterator i = fonts.begin();
254       i != fonts.end(); ++i) {
255    ascent = std::max(ascent, i->GetBaseline());
256    descent = std::max(descent, i->GetHeight() - i->GetBaseline());
257  }
258  common_height_ = ascent + descent;
259  common_baseline_ = ascent;
260}
261
262void FontList::CacheFontStyleAndSize() const {
263  if (!fonts_.empty()) {
264    font_style_ = fonts_[0].GetStyle();
265    font_size_ = fonts_[0].GetFontSize();
266  } else {
267    std::vector<std::string> font_names;
268    ParseFontDescriptionString(font_description_string_, &font_names,
269                               &font_style_, &font_size_);
270  }
271}
272
273}  // namespace gfx
274