OLD | NEW |
| (Empty) |
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/pango_util.h" | |
6 | |
7 #include <cairo/cairo.h> | |
8 #include <pango/pango.h> | |
9 #include <pango/pangocairo.h> | |
10 #include <string> | |
11 | |
12 #include <algorithm> | |
13 #include <map> | |
14 | |
15 #include "base/logging.h" | |
16 #include "base/strings/utf_string_conversions.h" | |
17 #include "ui/gfx/canvas.h" | |
18 #include "ui/gfx/font_list.h" | |
19 #include "ui/gfx/font_render_params.h" | |
20 #include "ui/gfx/linux_font_delegate.h" | |
21 #include "ui/gfx/platform_font_pango.h" | |
22 #include "ui/gfx/text_utils.h" | |
23 | |
24 namespace gfx { | |
25 | |
26 namespace { | |
27 | |
28 // Marker for accelerators in the text. | |
29 const gunichar kAcceleratorChar = '&'; | |
30 | |
31 // Creates and returns a PangoContext. The caller owns the context. | |
32 PangoContext* GetPangoContext() { | |
33 PangoFontMap* font_map = pango_cairo_font_map_get_default(); | |
34 return pango_font_map_create_context(font_map); | |
35 } | |
36 | |
37 // Creates a new cairo_font_options_t based on |params|. | |
38 cairo_font_options_t* CreateCairoFontOptions(const FontRenderParams& params) { | |
39 cairo_font_options_t* cairo_font_options = cairo_font_options_create(); | |
40 | |
41 FontRenderParams::SubpixelRendering subpixel = params.subpixel_rendering; | |
42 if (!params.antialiasing) { | |
43 cairo_font_options_set_antialias(cairo_font_options, CAIRO_ANTIALIAS_NONE); | |
44 } else if (subpixel == FontRenderParams::SUBPIXEL_RENDERING_NONE) { | |
45 cairo_font_options_set_antialias(cairo_font_options, CAIRO_ANTIALIAS_GRAY); | |
46 } else { | |
47 cairo_font_options_set_antialias(cairo_font_options, | |
48 CAIRO_ANTIALIAS_SUBPIXEL); | |
49 cairo_subpixel_order_t cairo_subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT; | |
50 if (subpixel == FontRenderParams::SUBPIXEL_RENDERING_RGB) | |
51 cairo_subpixel_order = CAIRO_SUBPIXEL_ORDER_RGB; | |
52 else if (subpixel == FontRenderParams::SUBPIXEL_RENDERING_BGR) | |
53 cairo_subpixel_order = CAIRO_SUBPIXEL_ORDER_BGR; | |
54 else if (subpixel == FontRenderParams::SUBPIXEL_RENDERING_VRGB) | |
55 cairo_subpixel_order = CAIRO_SUBPIXEL_ORDER_VRGB; | |
56 else if (subpixel == FontRenderParams::SUBPIXEL_RENDERING_VBGR) | |
57 cairo_subpixel_order = CAIRO_SUBPIXEL_ORDER_VBGR; | |
58 else | |
59 NOTREACHED() << "Unhandled subpixel rendering type " << subpixel; | |
60 cairo_font_options_set_subpixel_order(cairo_font_options, | |
61 cairo_subpixel_order); | |
62 } | |
63 | |
64 if (params.hinting == FontRenderParams::HINTING_NONE || | |
65 params.subpixel_positioning) { | |
66 cairo_font_options_set_hint_style(cairo_font_options, | |
67 CAIRO_HINT_STYLE_NONE); | |
68 cairo_font_options_set_hint_metrics(cairo_font_options, | |
69 CAIRO_HINT_METRICS_OFF); | |
70 } else { | |
71 cairo_hint_style_t cairo_hint_style = CAIRO_HINT_STYLE_DEFAULT; | |
72 if (params.hinting == FontRenderParams::HINTING_SLIGHT) | |
73 cairo_hint_style = CAIRO_HINT_STYLE_SLIGHT; | |
74 else if (params.hinting == FontRenderParams::HINTING_MEDIUM) | |
75 cairo_hint_style = CAIRO_HINT_STYLE_MEDIUM; | |
76 else if (params.hinting == FontRenderParams::HINTING_FULL) | |
77 cairo_hint_style = CAIRO_HINT_STYLE_FULL; | |
78 else | |
79 NOTREACHED() << "Unhandled hinting style " << params.hinting; | |
80 cairo_font_options_set_hint_style(cairo_font_options, cairo_hint_style); | |
81 cairo_font_options_set_hint_metrics(cairo_font_options, | |
82 CAIRO_HINT_METRICS_ON); | |
83 } | |
84 | |
85 return cairo_font_options; | |
86 } | |
87 | |
88 // Returns the DPI that should be used by Pango. | |
89 double GetPangoDPI() { | |
90 static double dpi = -1.0; | |
91 if (dpi < 0.0) { | |
92 const gfx::LinuxFontDelegate* delegate = gfx::LinuxFontDelegate::instance(); | |
93 if (delegate) | |
94 dpi = delegate->GetFontDPI(); | |
95 if (dpi <= 0.0) | |
96 dpi = 96.0; | |
97 } | |
98 return dpi; | |
99 } | |
100 | |
101 // Returns the number of pixels in a point. | |
102 // - multiply a point size by this to get pixels ("device units") | |
103 // - divide a pixel size by this to get points | |
104 double GetPixelsInPoint() { | |
105 static double pixels_in_point = GetPangoDPI() / 72.0; // 72 points in an inch | |
106 return pixels_in_point; | |
107 } | |
108 | |
109 } // namespace | |
110 | |
111 void SetUpPangoLayout( | |
112 PangoLayout* layout, | |
113 const base::string16& text, | |
114 const FontList& font_list, | |
115 base::i18n::TextDirection text_direction, | |
116 int flags) { | |
117 cairo_font_options_t* cairo_font_options = CreateCairoFontOptions( | |
118 font_list.GetPrimaryFont().GetFontRenderParams()); | |
119 | |
120 // If we got an explicit request to turn off subpixel rendering, disable it. | |
121 if ((flags & Canvas::NO_SUBPIXEL_RENDERING) && | |
122 (cairo_font_options_get_antialias(cairo_font_options) == | |
123 CAIRO_ANTIALIAS_SUBPIXEL)) | |
124 cairo_font_options_set_antialias(cairo_font_options, CAIRO_ANTIALIAS_GRAY); | |
125 | |
126 // This needs to be done early on; it has no effect when called just before | |
127 // pango_cairo_show_layout(). | |
128 pango_cairo_context_set_font_options( | |
129 pango_layout_get_context(layout), cairo_font_options); | |
130 cairo_font_options_destroy(cairo_font_options); | |
131 cairo_font_options = NULL; | |
132 | |
133 // Set Pango's base text direction explicitly from |text_direction|. | |
134 pango_layout_set_auto_dir(layout, FALSE); | |
135 pango_context_set_base_dir(pango_layout_get_context(layout), | |
136 (text_direction == base::i18n::RIGHT_TO_LEFT ? | |
137 PANGO_DIRECTION_RTL : PANGO_DIRECTION_LTR)); | |
138 | |
139 if (flags & Canvas::TEXT_ALIGN_CENTER) { | |
140 // We don't support center aligned w/ eliding. | |
141 DCHECK(gfx::Canvas::NO_ELLIPSIS); | |
142 pango_layout_set_alignment(layout, PANGO_ALIGN_CENTER); | |
143 } else if (flags & Canvas::TEXT_ALIGN_RIGHT) { | |
144 pango_layout_set_alignment(layout, PANGO_ALIGN_RIGHT); | |
145 } | |
146 | |
147 if (flags & Canvas::NO_ELLIPSIS) { | |
148 pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_NONE); | |
149 if (flags & Canvas::MULTI_LINE) { | |
150 pango_layout_set_wrap(layout, | |
151 (flags & Canvas::CHARACTER_BREAK) ? | |
152 PANGO_WRAP_WORD_CHAR : PANGO_WRAP_WORD); | |
153 } | |
154 } else if (text_direction == base::i18n::RIGHT_TO_LEFT) { | |
155 pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_END); | |
156 } else { | |
157 // Fading the text will be handled in the draw operation. | |
158 // Ensure that the text is only on one line. | |
159 pango_layout_set_ellipsize(layout, PANGO_ELLIPSIZE_NONE); | |
160 pango_layout_set_width(layout, -1); | |
161 } | |
162 | |
163 // Set the layout's resolution to match the resolution used to convert from | |
164 // points to pixels. | |
165 pango_cairo_context_set_resolution(pango_layout_get_context(layout), | |
166 GetPangoDPI()); | |
167 | |
168 // Set text and accelerator character if needed. | |
169 if (flags & Canvas::SHOW_PREFIX) { | |
170 // Escape the text string to be used as markup. | |
171 std::string utf8 = base::UTF16ToUTF8(text); | |
172 gchar* escaped_text = g_markup_escape_text(utf8.c_str(), utf8.size()); | |
173 pango_layout_set_markup_with_accel(layout, | |
174 escaped_text, | |
175 strlen(escaped_text), | |
176 kAcceleratorChar, NULL); | |
177 g_free(escaped_text); | |
178 } else { | |
179 std::string utf8; | |
180 | |
181 // Remove the ampersand character. A double ampersand is output as | |
182 // a single ampersand. | |
183 if (flags & Canvas::HIDE_PREFIX) { | |
184 DCHECK_EQ(1, g_unichar_to_utf8(kAcceleratorChar, NULL)); | |
185 base::string16 accelerator_removed = | |
186 RemoveAcceleratorChar(text, | |
187 static_cast<base::char16>(kAcceleratorChar), | |
188 NULL, NULL); | |
189 utf8 = base::UTF16ToUTF8(accelerator_removed); | |
190 } else { | |
191 utf8 = base::UTF16ToUTF8(text); | |
192 } | |
193 | |
194 pango_layout_set_text(layout, utf8.data(), utf8.size()); | |
195 } | |
196 | |
197 ScopedPangoFontDescription desc(pango_font_description_from_string( | |
198 font_list.GetFontDescriptionString().c_str())); | |
199 pango_layout_set_font_description(layout, desc.get()); | |
200 } | |
201 | |
202 int GetPangoFontSizeInPixels(PangoFontDescription* pango_font) { | |
203 // If the size is absolute, then it's in Pango units rather than points. There | |
204 // are PANGO_SCALE Pango units in a device unit (pixel). | |
205 if (pango_font_description_get_size_is_absolute(pango_font)) | |
206 return pango_font_description_get_size(pango_font) / PANGO_SCALE; | |
207 | |
208 // Otherwise, we need to convert from points. | |
209 return static_cast<int>(GetPixelsInPoint() * | |
210 pango_font_description_get_size(pango_font) / PANGO_SCALE + 0.5); | |
211 } | |
212 | |
213 PangoFontMetrics* GetPangoFontMetrics(PangoFontDescription* desc) { | |
214 static std::map<int, PangoFontMetrics*>* desc_to_metrics = NULL; | |
215 static PangoContext* context = NULL; | |
216 | |
217 if (!context) { | |
218 context = GetPangoContext(); | |
219 pango_context_set_language(context, pango_language_get_default()); | |
220 } | |
221 | |
222 if (!desc_to_metrics) | |
223 desc_to_metrics = new std::map<int, PangoFontMetrics*>(); | |
224 | |
225 const int desc_hash = pango_font_description_hash(desc); | |
226 std::map<int, PangoFontMetrics*>::iterator i = | |
227 desc_to_metrics->find(desc_hash); | |
228 | |
229 if (i == desc_to_metrics->end()) { | |
230 PangoFontMetrics* metrics = pango_context_get_metrics(context, desc, NULL); | |
231 desc_to_metrics->insert(std::make_pair(desc_hash, metrics)); | |
232 return metrics; | |
233 } | |
234 return i->second; | |
235 } | |
236 | |
237 } // namespace gfx | |
OLD | NEW |