| 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 |