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 238