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.
5 #include "ui/gfx/pango_util.h"
7 #include <cairo/cairo.h>
8 #include <pango/pango.h>
9 #include <pango/pangocairo.h>
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"
28 // Marker for accelerators in the text.
29 const gunichar kAcceleratorChar
= '&';
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
);
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();
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
);
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
;
59 NOTREACHED() << "Unhandled subpixel rendering type " << subpixel
;
60 cairo_font_options_set_subpixel_order(cairo_font_options
,
61 cairo_subpixel_order
);
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
);
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
;
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
);
85 return cairo_font_options
;
88 // Returns the DPI that should be used by Pango.
89 double GetPangoDPI() {
90 static double dpi
= -1.0;
92 const gfx::LinuxFontDelegate
* delegate
= gfx::LinuxFontDelegate::instance();
94 dpi
= delegate
->GetFontDPI();
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
;
111 void SetUpPangoLayout(
113 const base::string16
& text
,
114 const FontList
& font_list
,
115 base::i18n::TextDirection text_direction
,
117 cairo_font_options_t
* cairo_font_options
= CreateCairoFontOptions(
118 font_list
.GetPrimaryFont().GetFontRenderParams());
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
);
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
;
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
));
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
);
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
);
154 } else if (text_direction
== base::i18n::RIGHT_TO_LEFT
) {
155 pango_layout_set_ellipsize(layout
, PANGO_ELLIPSIZE_END
);
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);
163 // Set the layout's resolution to match the resolution used to convert from
165 pango_cairo_context_set_resolution(pango_layout_get_context(layout
),
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
,
175 strlen(escaped_text
),
176 kAcceleratorChar
, NULL
);
177 g_free(escaped_text
);
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
),
189 utf8
= base::UTF16ToUTF8(accelerator_removed
);
191 utf8
= base::UTF16ToUTF8(text
);
194 pango_layout_set_text(layout
, utf8
.data(), utf8
.size());
197 ScopedPangoFontDescription
desc(pango_font_description_from_string(
198 font_list
.GetFontDescriptionString().c_str()));
199 pango_layout_set_font_description(layout
, desc
.get());
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
;
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);
213 PangoFontMetrics
* GetPangoFontMetrics(PangoFontDescription
* desc
) {
214 static std::map
<int, PangoFontMetrics
*>* desc_to_metrics
= NULL
;
215 static PangoContext
* context
= NULL
;
218 context
= GetPangoContext();
219 pango_context_set_language(context
, pango_language_get_default());
222 if (!desc_to_metrics
)
223 desc_to_metrics
= new std::map
<int, PangoFontMetrics
*>();
225 const int desc_hash
= pango_font_description_hash(desc
);
226 std::map
<int, PangoFontMetrics
*>::iterator i
=
227 desc_to_metrics
->find(desc_hash
);
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
));