OLD | NEW |
(Empty) | |
| 1 /* -*- mode: C; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ |
| 2 /* vim:set et sts=4: */ |
| 3 /* ibus - The Input Bus |
| 4 * Copyright (C) 2017 Takao Fujiwara <takao.fujiwara1@gmail.com> |
| 5 * Copyright (C) 2017 Red Hat, Inc. |
| 6 * |
| 7 * This library is free software; you can redistribute it and/or |
| 8 * modify it under the terms of the GNU Lesser General Public |
| 9 * License as published by the Free Software Foundation; either |
| 10 * version 2.1 of the License, or (at your option) any later version. |
| 11 * |
| 12 * This library is distributed in the hope that it will be useful, |
| 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 15 * Lesser General Public License for more details. |
| 16 * |
| 17 * You should have received a copy of the GNU Lesser General Public |
| 18 * License along with this library; if not, write to the Free Software |
| 19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 |
| 20 * USA |
| 21 */ |
| 22 |
| 23 #include <cairo-ft.h> |
| 24 #include <fontconfig/fontconfig.h> |
| 25 #include <ft2build.h> |
| 26 #include FT_FREETYPE_H |
| 27 #include <glib.h> |
| 28 #include <hb-ot.h> |
| 29 #include <pango/pango.h> |
| 30 |
| 31 #include "ibusfontset.h" |
| 32 |
| 33 #define XPAD 2 |
| 34 #define YPAD 2 |
| 35 #define UNKNOWN_FONT_SIZE 7 |
| 36 #define IBUS_FONTSET_GET_PRIVATE(o) \ |
| 37 (G_TYPE_INSTANCE_GET_PRIVATE ((o), IBUS_TYPE_FONTSET, IBusFontSetPrivate)) |
| 38 |
| 39 |
| 40 static FT_Library m_ftlibrary; |
| 41 static FcFontSet *m_fcfontset; |
| 42 static gchar *m_family; |
| 43 static guint m_size; |
| 44 static gchar *m_language; |
| 45 static GHashTable *m_scaled_font_table; |
| 46 static GHashTable *m_hb_font_table; |
| 47 |
| 48 enum { |
| 49 PROP_0, |
| 50 PROP_FAMILY, |
| 51 PROP_SIZE, |
| 52 PROP_LANGUAGE |
| 53 }; |
| 54 |
| 55 typedef struct { |
| 56 gunichar ch; |
| 57 FcPattern *fcfont; |
| 58 } FontPerChar; |
| 59 |
| 60 struct _IBusFontSetPrivate { |
| 61 gchar *family; |
| 62 guint size; |
| 63 gchar *language; |
| 64 }; |
| 65 |
| 66 static GObject * ibus_fontset_constructor (GType type, |
| 67 guint n, |
| 68 GObjectConstructParam *args); |
| 69 static void ibus_fontset_destroy (IBusFontSet *fontset); |
| 70 static void ibus_fontset_set_property (IBusFontSet *fontset, |
| 71 guint prop_id, |
| 72 const GValue *value, |
| 73 GParamSpec *pspec); |
| 74 static void ibus_fontset_get_property (IBusFontSet *fontset, |
| 75 guint prop_id, |
| 76 GValue *value, |
| 77 GParamSpec *pspec); |
| 78 static cairo_scaled_font_t * |
| 79 ibus_fontset_cairo_scaled_font_new_with_font |
| 80 (const gchar *family, |
| 81 guint size); |
| 82 |
| 83 G_DEFINE_BOXED_TYPE (IBusCairoLine, |
| 84 ibus_cairo_line, |
| 85 ibus_cairo_line_copy, |
| 86 ibus_cairo_line_free); |
| 87 G_DEFINE_BOXED_TYPE (IBusRequisitionEx, |
| 88 ibus_requisition_ex, |
| 89 ibus_requisition_ex_copy, |
| 90 ibus_requisition_ex_free); |
| 91 G_DEFINE_TYPE (IBusFontSet, ibus_fontset, IBUS_TYPE_OBJECT) |
| 92 |
| 93 static void |
| 94 ibus_fontset_class_init (IBusFontSetClass *class) |
| 95 { |
| 96 GObjectClass *gobject_class = G_OBJECT_CLASS (class); |
| 97 IBusObjectClass *object_class = IBUS_OBJECT_CLASS (class); |
| 98 cairo_glyph_t dummy; |
| 99 IBusGlyph dummy2; |
| 100 |
| 101 gobject_class->constructor = ibus_fontset_constructor; |
| 102 gobject_class->get_property = |
| 103 (GObjectGetPropertyFunc) ibus_fontset_get_property; |
| 104 gobject_class->set_property = |
| 105 (GObjectSetPropertyFunc) ibus_fontset_set_property; |
| 106 object_class->destroy = (IBusObjectDestroyFunc) ibus_fontset_destroy; |
| 107 |
| 108 /* install properties */ |
| 109 /** |
| 110 * IBusFontSet:family: |
| 111 * |
| 112 * Font family of this IBusFontSet. |
| 113 */ |
| 114 g_object_class_install_property (gobject_class, |
| 115 PROP_FAMILY, |
| 116 g_param_spec_string ("family", |
| 117 "family", |
| 118 "family", |
| 119 "", |
| 120 G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); |
| 121 |
| 122 /** |
| 123 * IBusFontSet:size: |
| 124 * |
| 125 * Font size of this IBusFontSet. |
| 126 */ |
| 127 g_object_class_install_property (gobject_class, |
| 128 PROP_SIZE, |
| 129 g_param_spec_uint ("size", |
| 130 "size", |
| 131 "size", |
| 132 0, G_MAXUINT16, 0, |
| 133 G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); |
| 134 |
| 135 /** |
| 136 * IBusFontSet:language: |
| 137 * |
| 138 * Font language of this IBusFontSet. |
| 139 */ |
| 140 g_object_class_install_property (gobject_class, |
| 141 PROP_LANGUAGE, |
| 142 g_param_spec_string ("language", |
| 143 "language", |
| 144 "language", |
| 145 "", |
| 146 G_PARAM_READWRITE | G_PARAM_CONSTRUCT)); |
| 147 |
| 148 g_type_class_add_private (class, sizeof (IBusFontSetPrivate)); |
| 149 FT_Init_FreeType (&m_ftlibrary); |
| 150 m_scaled_font_table = g_hash_table_new_full ( |
| 151 g_str_hash, g_str_equal, |
| 152 g_free, |
| 153 (GDestroyNotify) cairo_scaled_font_destroy); |
| 154 m_hb_font_table = g_hash_table_new_full ( |
| 155 g_str_hash, g_str_equal, |
| 156 g_free, |
| 157 (GDestroyNotify) hb_font_destroy); |
| 158 |
| 159 /* hb_glyph_t is not available in Vala so override it with IBusGlyph. */ |
| 160 g_assert (sizeof (dummy) == sizeof (dummy2)); |
| 161 g_assert (sizeof (dummy.index) == sizeof (dummy2.index)); |
| 162 g_assert (sizeof (dummy.x) == sizeof (dummy2.x)); |
| 163 g_assert (sizeof (dummy.y) == sizeof (dummy2.y)); |
| 164 } |
| 165 |
| 166 static void |
| 167 ibus_fontset_init (IBusFontSet *fontset) |
| 168 { |
| 169 fontset->priv = IBUS_FONTSET_GET_PRIVATE (fontset); |
| 170 } |
| 171 |
| 172 |
| 173 static GObject * |
| 174 ibus_fontset_constructor (GType type, |
| 175 guint n, |
| 176 GObjectConstructParam *args) |
| 177 { |
| 178 GObject *object; |
| 179 IBusFontSet *fontset; |
| 180 const gchar *family; |
| 181 guint size; |
| 182 |
| 183 object = G_OBJECT_CLASS (ibus_fontset_parent_class)->constructor ( |
| 184 type, n ,args); |
| 185 fontset = IBUS_FONTSET (object); |
| 186 family = ibus_fontset_get_family (fontset); |
| 187 size = ibus_fontset_get_size (fontset); |
| 188 ibus_fontset_update_fcfontset (fontset); |
| 189 if (family != NULL && size > 0) { |
| 190 /* cache the font */ |
| 191 ibus_fontset_cairo_scaled_font_new_with_font (family, |
| 192 size); |
| 193 } |
| 194 return object; |
| 195 } |
| 196 |
| 197 static void |
| 198 ibus_fontset_destroy (IBusFontSet *fontset) |
| 199 { |
| 200 g_clear_pointer (&fontset->priv->family, g_free); |
| 201 g_clear_pointer (&fontset->priv->language, g_free); |
| 202 } |
| 203 |
| 204 static void |
| 205 ibus_fontset_set_property (IBusFontSet *fontset, |
| 206 guint prop_id, |
| 207 const GValue *value, |
| 208 GParamSpec *pspec) |
| 209 { |
| 210 switch (prop_id) { |
| 211 case PROP_FAMILY: |
| 212 ibus_fontset_set_family (fontset, g_value_get_string (value)); |
| 213 break; |
| 214 case PROP_SIZE: |
| 215 ibus_fontset_set_size (fontset, g_value_get_uint (value)); |
| 216 break; |
| 217 case PROP_LANGUAGE: |
| 218 ibus_fontset_set_language (fontset, g_value_get_string (value)); |
| 219 break; |
| 220 default: |
| 221 G_OBJECT_WARN_INVALID_PROPERTY_ID (fontset, prop_id, pspec); |
| 222 } |
| 223 } |
| 224 |
| 225 static void |
| 226 ibus_fontset_get_property (IBusFontSet *fontset, |
| 227 guint prop_id, |
| 228 GValue *value, |
| 229 GParamSpec *pspec) |
| 230 { |
| 231 switch (prop_id) { |
| 232 case PROP_FAMILY: |
| 233 g_value_set_string (value, ibus_fontset_get_family (fontset)); |
| 234 break; |
| 235 case PROP_SIZE: |
| 236 g_value_set_uint (value, ibus_fontset_get_size (fontset)); |
| 237 break; |
| 238 case PROP_LANGUAGE: |
| 239 g_value_set_string (value, ibus_fontset_get_language (fontset)); |
| 240 break; |
| 241 default: |
| 242 G_OBJECT_WARN_INVALID_PROPERTY_ID (fontset, prop_id, pspec); |
| 243 } |
| 244 } |
| 245 |
| 246 static cairo_scaled_font_t * |
| 247 ibus_fontset_cairo_scaled_font_new_with_font (const gchar *family, |
| 248 guint size) |
| 249 { |
| 250 gchar *font_name; |
| 251 cairo_scaled_font_t *scaled_font = NULL; |
| 252 FcPattern *pattern, *resolved; |
| 253 FcResult result; |
| 254 cairo_font_options_t *font_options; |
| 255 double pixel_size = 0.; |
| 256 FcMatrix fc_matrix, *fc_matrix_val; |
| 257 cairo_font_face_t *cairo_face = NULL; |
| 258 cairo_matrix_t font_matrix; |
| 259 cairo_matrix_t ctm; |
| 260 int i; |
| 261 |
| 262 g_return_val_if_fail (family != NULL, NULL); |
| 263 g_return_val_if_fail (m_scaled_font_table != NULL, NULL); |
| 264 |
| 265 font_name = g_strdup_printf ("%s %u", family, size); |
| 266 scaled_font = g_hash_table_lookup (m_scaled_font_table, font_name); |
| 267 if (scaled_font != NULL) { |
| 268 g_free (font_name); |
| 269 return scaled_font; |
| 270 } |
| 271 pattern = FcPatternCreate (); |
| 272 FcPatternAddString (pattern, FC_FAMILY, (FcChar8*) family); |
| 273 FcPatternAddDouble (pattern, FC_SIZE, (double) size); |
| 274 FcPatternAddDouble (pattern, FC_DPI, 96); |
| 275 FcConfigSubstitute(NULL, pattern, FcMatchPattern); |
| 276 font_options = cairo_font_options_create (); |
| 277 cairo_ft_font_options_substitute (font_options, pattern); |
| 278 FcDefaultSubstitute (pattern); |
| 279 resolved = FcFontMatch (NULL, pattern, &result); |
| 280 FcPatternDestroy (pattern); |
| 281 FcPatternGetDouble (resolved, FC_PIXEL_SIZE, 0, &pixel_size); |
| 282 if (pixel_size == 0.) |
| 283 g_warning ("Failed to scaled the font: %s %u", family, size); |
| 284 cairo_face = cairo_ft_font_face_create_for_pattern (resolved); |
| 285 FcMatrixInit (&fc_matrix); |
| 286 for (i = 0; |
| 287 FcPatternGetMatrix (resolved, FC_MATRIX, i, &fc_matrix_val) |
| 288 == FcResultMatch; |
| 289 i++) { |
| 290 FcMatrixMultiply (&fc_matrix, &fc_matrix, fc_matrix_val); |
| 291 } |
| 292 FcPatternDestroy (resolved); |
| 293 cairo_matrix_init (&font_matrix, |
| 294 fc_matrix.xx, -fc_matrix.yx, |
| 295 -fc_matrix.xy, fc_matrix.yy, |
| 296 0., 0.); |
| 297 if (pixel_size != 0.) |
| 298 cairo_matrix_scale (&font_matrix, pixel_size, pixel_size); |
| 299 cairo_matrix_init_identity (&ctm); |
| 300 scaled_font = cairo_scaled_font_create (cairo_face, |
| 301 &font_matrix, &ctm, |
| 302 font_options); |
| 303 cairo_font_face_destroy (cairo_face); |
| 304 if (font_name) |
| 305 g_hash_table_insert(m_scaled_font_table, font_name, scaled_font); |
| 306 |
| 307 return scaled_font; |
| 308 } |
| 309 |
| 310 static hb_font_t * |
| 311 ibus_fontset_hb_font_new_with_font_path (const gchar *font_path) |
| 312 { |
| 313 hb_font_t *hb_font; |
| 314 GError *error = NULL; |
| 315 GMappedFile *mf; |
| 316 char *font_data = NULL; |
| 317 gsize len; |
| 318 hb_blob_t *hb_blob; |
| 319 hb_face_t *hb_face; |
| 320 |
| 321 g_return_val_if_fail (font_path != NULL, NULL); |
| 322 g_return_val_if_fail (m_hb_font_table != NULL, NULL); |
| 323 |
| 324 hb_font = g_hash_table_lookup (m_hb_font_table, font_path); |
| 325 if (hb_font != NULL) |
| 326 return hb_font; |
| 327 |
| 328 mf = g_mapped_file_new (font_path, FALSE, &error); |
| 329 if (mf == NULL) { |
| 330 g_warning ("Not found font %s", font_path); |
| 331 return NULL; |
| 332 } |
| 333 font_data = g_mapped_file_get_contents (mf); |
| 334 len = g_mapped_file_get_length (mf); |
| 335 if (len == 0) { |
| 336 g_warning ("zero size font %s", font_path); |
| 337 g_mapped_file_unref (mf); |
| 338 return NULL; |
| 339 } |
| 340 hb_blob = hb_blob_create (font_data, len, |
| 341 HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE, |
| 342 mf, (hb_destroy_func_t)g_mapped_file_unref); |
| 343 hb_face = hb_face_create (hb_blob, 0); |
| 344 hb_blob_destroy (hb_blob); |
| 345 hb_font = hb_font_create (hb_face); |
| 346 unsigned int upem = hb_face_get_upem (hb_face); |
| 347 hb_font_set_scale (hb_font, upem, upem); |
| 348 hb_face_destroy (hb_face); |
| 349 hb_ot_font_set_funcs (hb_font); |
| 350 g_hash_table_insert (m_hb_font_table, g_strdup (font_path), hb_font); |
| 351 |
| 352 return hb_font; |
| 353 } |
| 354 |
| 355 static void |
| 356 get_font_extents_with_scaled_font (cairo_scaled_font_t *scaled_font, |
| 357 PangoRectangle *font_rect) |
| 358 { |
| 359 cairo_font_extents_t font_extents; |
| 360 |
| 361 g_assert (scaled_font != NULL && font_rect != NULL); |
| 362 |
| 363 cairo_scaled_font_extents (scaled_font, &font_extents); |
| 364 font_rect->x = 0; |
| 365 font_rect->y = - pango_units_from_double (font_extents.ascent); |
| 366 font_rect->width = 0; |
| 367 font_rect->height = pango_units_from_double ( |
| 368 font_extents.ascent + font_extents.descent); |
| 369 } |
| 370 |
| 371 static void |
| 372 get_glyph_extents_with_scaled_hb_font (const gchar *str, |
| 373 cairo_scaled_font_t *scaled_font, |
| 374 hb_font_t *hb_font, |
| 375 PangoRectangle *font_rect, |
| 376 IBusCairoLine **cairo_lines, |
| 377 FcChar8 *fallback_family) |
| 378 { |
| 379 gboolean has_unknown_glyph = FALSE; |
| 380 hb_buffer_t *hb_buffer; |
| 381 unsigned int len, n, i; |
| 382 hb_glyph_info_t *info; |
| 383 hb_glyph_position_t *pos; |
| 384 double x; |
| 385 cairo_glyph_t *glyph; |
| 386 cairo_text_extents_t text_extents = { 0, }; |
| 387 |
| 388 g_return_if_fail (str != NULL); |
| 389 |
| 390 hb_buffer = hb_buffer_create (); |
| 391 hb_buffer_add_utf8 (hb_buffer, str, -1, 0, -1); |
| 392 hb_buffer_guess_segment_properties (hb_buffer); |
| 393 for (n = 0; *cairo_lines && (*cairo_lines)[n].scaled_font; n++); |
| 394 if (n == 0) |
| 395 *cairo_lines = g_new0 (IBusCairoLine, 2); |
| 396 else |
| 397 *cairo_lines = g_renew (IBusCairoLine, *cairo_lines, n + 2); |
| 398 (*cairo_lines)[n + 1].scaled_font = NULL; |
| 399 (*cairo_lines)[n + 1].num_glyphs = 0; |
| 400 (*cairo_lines)[n + 1].glyphs = NULL; |
| 401 hb_shape (hb_font, hb_buffer, NULL, 0); |
| 402 len = hb_buffer_get_length (hb_buffer); |
| 403 info = hb_buffer_get_glyph_infos (hb_buffer, NULL); |
| 404 pos = hb_buffer_get_glyph_positions (hb_buffer, NULL); |
| 405 (*cairo_lines)[n].scaled_font = scaled_font; |
| 406 (*cairo_lines)[n].num_glyphs = len; |
| 407 (*cairo_lines)[n].glyphs = (IBusGlyph*) cairo_glyph_allocate (len + 1); |
| 408 x = 0.; |
| 409 for (i = 0; i < len; i++) { |
| 410 hb_codepoint_t c = info[i].codepoint; |
| 411 if (c) { |
| 412 (*cairo_lines)[n].glyphs[i].index = info[i].codepoint; |
| 413 (*cairo_lines)[n].glyphs[i].x = x; |
| 414 (*cairo_lines)[n].glyphs[i].y = -font_rect->y / PANGO_SCALE; |
| 415 glyph = (cairo_glyph_t *) &((*cairo_lines)[n].glyphs[i]); |
| 416 cairo_scaled_font_glyph_extents (scaled_font, glyph, |
| 417 1, &text_extents); |
| 418 x += text_extents.width; |
| 419 } else { |
| 420 has_unknown_glyph = TRUE; |
| 421 c = g_utf8_get_char (str); |
| 422 (*cairo_lines)[n].glyphs[i].index = PANGO_GET_UNKNOWN_GLYPH (c); |
| 423 (*cairo_lines)[n].glyphs[i].x = x; |
| 424 (*cairo_lines)[n].glyphs[i].y = -font_rect->y / PANGO_SCALE; |
| 425 glyph = (cairo_glyph_t *) &((*cairo_lines)[n].glyphs[i]); |
| 426 cairo_scaled_font_glyph_extents (scaled_font, glyph, |
| 427 1, &text_extents); |
| 428 x += 10; |
| 429 } |
| 430 } |
| 431 (*cairo_lines)[n].glyphs[i].index = -1; |
| 432 (*cairo_lines)[n].glyphs[i].x = 0; |
| 433 (*cairo_lines)[n].glyphs[i].y = 0; |
| 434 glyph = (cairo_glyph_t *) (*cairo_lines)[n].glyphs; |
| 435 cairo_scaled_font_glyph_extents (scaled_font, glyph, |
| 436 len, &text_extents); |
| 437 if (text_extents.width) { |
| 438 font_rect->width = pango_units_from_double (text_extents.width); |
| 439 } else { |
| 440 font_rect->width = font_rect->height; |
| 441 } |
| 442 if (has_unknown_glyph && fallback_family != NULL) { |
| 443 cairo_scaled_font_t *unknown_font; |
| 444 unknown_font = ibus_fontset_cairo_scaled_font_new_with_font ( |
| 445 (const gchar *) fallback_family, |
| 446 UNKNOWN_FONT_SIZE); |
| 447 (*cairo_lines)[n].scaled_font = unknown_font; |
| 448 } |
| 449 hb_buffer_destroy (hb_buffer); |
| 450 } |
| 451 |
| 452 static void |
| 453 get_string_extents_with_font (const gchar *str, |
| 454 FontPerChar *buff, |
| 455 cairo_rectangle_int_t *rect, |
| 456 IBusCairoLine **cairo_lines) |
| 457 { |
| 458 FcChar8 *family = NULL; |
| 459 FcChar8 *font_path = NULL; |
| 460 guint size = 0; |
| 461 cairo_scaled_font_t *scaled_font = NULL; |
| 462 PangoRectangle font_rect = { 0, }; |
| 463 hb_font_t *hb_font; |
| 464 |
| 465 g_return_if_fail (str != NULL); |
| 466 g_return_if_fail (buff != NULL && buff->fcfont != NULL); |
| 467 |
| 468 FcPatternGetString (buff->fcfont, FC_FAMILY, 0, &family); |
| 469 g_return_if_fail (family != NULL); |
| 470 size = m_size; |
| 471 if (size == 0) { |
| 472 g_warning ("Font size is not right for font %s.", family); |
| 473 size = 14; |
| 474 } |
| 475 scaled_font = ibus_fontset_cairo_scaled_font_new_with_font ( |
| 476 (const gchar *) family, |
| 477 size); |
| 478 g_return_if_fail (scaled_font != NULL); |
| 479 get_font_extents_with_scaled_font (scaled_font, &font_rect); |
| 480 |
| 481 FcPatternGetString (buff->fcfont, FC_FILE, 0, &font_path); |
| 482 g_return_if_fail (font_path != NULL); |
| 483 hb_font = ibus_fontset_hb_font_new_with_font_path ( |
| 484 (const gchar *) font_path); |
| 485 if (hb_font == NULL) |
| 486 return; |
| 487 get_glyph_extents_with_scaled_hb_font (str, |
| 488 scaled_font, |
| 489 hb_font, |
| 490 &font_rect, |
| 491 cairo_lines, |
| 492 family); |
| 493 rect->width += font_rect.width / PANGO_SCALE; |
| 494 rect->height += font_rect.height / PANGO_SCALE; |
| 495 } |
| 496 |
| 497 static FT_Face |
| 498 ibus_fontset_get_ftface_from_fcfont (IBusFontSet *fontset, |
| 499 FcPattern *fcfont) |
| 500 { |
| 501 FcChar8 *font_file = NULL; |
| 502 FT_Face ft_face; |
| 503 guint size = ibus_fontset_get_size (fontset); |
| 504 |
| 505 g_return_val_if_fail (IBUS_IS_FONTSET (fontset), NULL); |
| 506 g_return_val_if_fail (fcfont != NULL, NULL); |
| 507 |
| 508 size = ibus_fontset_get_size (fontset); |
| 509 FcPatternGetString (fcfont, FC_FILE, 0, &font_file); |
| 510 FT_New_Face (m_ftlibrary, (const gchar *) font_file, 0, &ft_face); |
| 511 FT_Set_Pixel_Sizes (ft_face, size, size); |
| 512 return ft_face; |
| 513 } |
| 514 |
| 515 void |
| 516 _cairo_show_unknown_glyphs (cairo_t *cr, |
| 517 const cairo_glyph_t *glyphs, |
| 518 guint num_glyphs, |
| 519 guint width, |
| 520 guint height) |
| 521 { |
| 522 gunichar ch; |
| 523 gboolean invalid_input; |
| 524 int rows = 2; |
| 525 int cols; |
| 526 int row, col; |
| 527 char buf[7]; |
| 528 double cx = 0.; |
| 529 double cy; |
| 530 const double box_descent = 3.; |
| 531 double x0; |
| 532 double y0; |
| 533 const double digit_width = 5.; |
| 534 const double digit_height= 6.; |
| 535 char hexbox_string[2] = {0, 0}; |
| 536 |
| 537 g_assert (glyphs != NULL); |
| 538 g_assert (num_glyphs > 0); |
| 539 |
| 540 ch = glyphs[0].index & ~PANGO_GLYPH_UNKNOWN_FLAG; |
| 541 invalid_input = G_UNLIKELY (glyphs[0].index == PANGO_GLYPH_INVALID_INPUT || |
| 542 ch > 0x10FFFF); |
| 543 if (G_UNLIKELY (invalid_input)) { |
| 544 g_warning ("Unsupported U+%06X", ch); |
| 545 return; |
| 546 } |
| 547 |
| 548 cairo_save (cr); |
| 549 |
| 550 cols = (ch > 0xffff ? 6 : 4) / rows; |
| 551 g_snprintf (buf, sizeof(buf), (ch > 0xffff) ? "%06X" : "%04X", ch); |
| 552 cy = (double) height; |
| 553 x0 = cx + box_descent + XPAD / 2; |
| 554 y0 = cy - box_descent - YPAD / 2; |
| 555 |
| 556 for (row = 0; row < rows; row++) { |
| 557 double y = y0 - (rows - 1 - row) * (digit_height + YPAD); |
| 558 for (col = 0; col < cols; col++) { |
| 559 double x = x0 + col * (digit_width + XPAD); |
| 560 cairo_move_to (cr, x, y); |
| 561 hexbox_string[0] = buf[row * cols + col]; |
| 562 cairo_show_text (cr, hexbox_string); |
| 563 } |
| 564 } |
| 565 cairo_move_to (cr, XPAD, YPAD); |
| 566 cairo_line_to (cr, width - XPAD, YPAD); |
| 567 cairo_line_to (cr, width - XPAD, |
| 568 height - YPAD); |
| 569 cairo_line_to (cr, XPAD, height - YPAD); |
| 570 cairo_line_to (cr, XPAD, YPAD); |
| 571 cairo_set_line_width (cr, 1.); |
| 572 cairo_stroke (cr); |
| 573 |
| 574 cairo_restore (cr); |
| 575 } |
| 576 |
| 577 IBusCairoLine * |
| 578 ibus_cairo_line_copy (IBusCairoLine *cairo_lines) |
| 579 { |
| 580 IBusCairoLine *ret; |
| 581 guint n, i, j, num_glyphs; |
| 582 if (!cairo_lines) |
| 583 return NULL; |
| 584 |
| 585 for (n = 0; cairo_lines[n].scaled_font; n++); |
| 586 ret = g_new0 (IBusCairoLine, n + 1); |
| 587 for (i = 0; i < n; i++) { |
| 588 ret[i].scaled_font = cairo_lines[i].scaled_font; |
| 589 num_glyphs = cairo_lines[i].num_glyphs; |
| 590 ret[i].num_glyphs = num_glyphs; |
| 591 ret[i].glyphs = (IBusGlyph *) cairo_glyph_allocate (num_glyphs + 1); |
| 592 for (j = 0; j < num_glyphs; j++) { |
| 593 ret[i].glyphs[j] = cairo_lines[i].glyphs[j]; |
| 594 } |
| 595 ret[i].glyphs[j].index = -1; |
| 596 ret[i].glyphs[j].x = 0; |
| 597 ret[i].glyphs[j].y = 0; |
| 598 } |
| 599 ret[i].scaled_font = NULL; |
| 600 ret[i].num_glyphs = 0; |
| 601 ret[i].glyphs = NULL; |
| 602 return ret; |
| 603 } |
| 604 |
| 605 void |
| 606 ibus_cairo_line_free (IBusCairoLine *cairo_lines) |
| 607 { |
| 608 guint i; |
| 609 if (!cairo_lines) |
| 610 return; |
| 611 for (i = 0; cairo_lines[i].scaled_font; i++) { |
| 612 g_free (cairo_lines[i].glyphs); |
| 613 } |
| 614 g_free (cairo_lines); |
| 615 } |
| 616 |
| 617 IBusRequisitionEx * |
| 618 ibus_requisition_ex_copy (IBusRequisitionEx *req) |
| 619 { |
| 620 IBusRequisitionEx *ret; |
| 621 if (!req) |
| 622 return NULL; |
| 623 ret = g_new0 (IBusRequisitionEx, 1); |
| 624 ret->width = req->width; |
| 625 ret->height = req->height; |
| 626 ret->cairo_lines = ibus_cairo_line_copy (req->cairo_lines); |
| 627 return ret; |
| 628 } |
| 629 |
| 630 void |
| 631 ibus_requisition_ex_free (IBusRequisitionEx *req) |
| 632 { |
| 633 if (!req) |
| 634 return; |
| 635 g_clear_pointer (&req->cairo_lines, ibus_cairo_line_free); |
| 636 g_free (req); |
| 637 } |
| 638 |
| 639 IBusFontSet * |
| 640 ibus_fontset_new (const gchar *first_property_name, ...) |
| 641 { |
| 642 va_list var_args; |
| 643 IBusFontSet *fontset; |
| 644 |
| 645 g_assert (first_property_name); |
| 646 |
| 647 va_start (var_args, first_property_name); |
| 648 fontset = (IBusFontSet *)g_object_new_valist (IBUS_TYPE_FONTSET, |
| 649 first_property_name, |
| 650 var_args); |
| 651 va_end (var_args); |
| 652 g_assert (fontset->priv->family); |
| 653 g_assert (fontset->priv->language); |
| 654 return fontset; |
| 655 } |
| 656 |
| 657 IBusFontSet * |
| 658 ibus_fontset_new_with_font (const gchar *family, |
| 659 guint size, |
| 660 const gchar *language) |
| 661 { |
| 662 return ibus_fontset_new ("family", family, |
| 663 "size", size, |
| 664 "language", language, |
| 665 NULL); |
| 666 } |
| 667 |
| 668 void |
| 669 ibus_fontset_exit () |
| 670 { |
| 671 g_clear_pointer (&m_ftlibrary, FT_Done_FreeType); |
| 672 } |
| 673 |
| 674 const gchar * |
| 675 ibus_fontset_get_family (IBusFontSet *fontset) |
| 676 { |
| 677 g_return_val_if_fail (IBUS_IS_FONTSET (fontset), NULL); |
| 678 return fontset->priv->family; |
| 679 } |
| 680 |
| 681 void |
| 682 ibus_fontset_set_family (IBusFontSet *fontset, |
| 683 const gchar *family) |
| 684 { |
| 685 g_return_if_fail (IBUS_IS_FONTSET (fontset)); |
| 686 g_free (fontset->priv->family); |
| 687 fontset->priv->family = g_strdup (family); |
| 688 } |
| 689 |
| 690 guint |
| 691 ibus_fontset_get_size (IBusFontSet *fontset) |
| 692 { |
| 693 g_return_val_if_fail (IBUS_IS_FONTSET (fontset), 0); |
| 694 return fontset->priv->size; |
| 695 } |
| 696 |
| 697 void |
| 698 ibus_fontset_set_size (IBusFontSet *fontset, |
| 699 guint size) |
| 700 { |
| 701 g_return_if_fail (IBUS_IS_FONTSET (fontset)); |
| 702 fontset->priv->size = size; |
| 703 } |
| 704 |
| 705 const gchar * |
| 706 ibus_fontset_get_language (IBusFontSet *fontset) |
| 707 { |
| 708 g_return_val_if_fail (IBUS_IS_FONTSET (fontset), NULL); |
| 709 return fontset->priv->language; |
| 710 } |
| 711 |
| 712 void |
| 713 ibus_fontset_set_language (IBusFontSet *fontset, |
| 714 const gchar *language) |
| 715 { |
| 716 g_return_if_fail (IBUS_IS_FONTSET (fontset)); |
| 717 g_free (fontset->priv->language); |
| 718 fontset->priv->language = g_strdup (language); |
| 719 } |
| 720 |
| 721 gboolean |
| 722 ibus_fontset_update_fcfontset (IBusFontSet *fontset) |
| 723 { |
| 724 FcPattern *pattern; |
| 725 const gchar *family; |
| 726 guint size; |
| 727 const gchar *language; |
| 728 gboolean update_fontset = FALSE; |
| 729 FcResult result; |
| 730 |
| 731 g_return_val_if_fail (IBUS_IS_FONTSET (fontset), FALSE); |
| 732 |
| 733 pattern = FcPatternCreate (); |
| 734 family = fontset->priv->family; |
| 735 size = fontset->priv->size; |
| 736 language = fontset->priv->language; |
| 737 |
| 738 if (g_strcmp0 (m_family, family)) { |
| 739 g_free (m_family); |
| 740 m_family = g_strdup (family); |
| 741 update_fontset = TRUE; |
| 742 } |
| 743 if (m_size != size) { |
| 744 m_size = size; |
| 745 update_fontset = TRUE; |
| 746 } |
| 747 if (g_strcmp0 (m_language, language)) { |
| 748 g_free (m_language); |
| 749 m_language = g_strdup (language); |
| 750 update_fontset = TRUE; |
| 751 } |
| 752 if (!update_fontset && m_fcfontset != NULL) |
| 753 return FALSE; |
| 754 |
| 755 if (m_fcfontset) |
| 756 g_clear_pointer (&m_fcfontset, FcFontSetDestroy); |
| 757 |
| 758 if (g_strcmp0 (family, "")) |
| 759 FcPatternAddString (pattern, FC_FAMILY, (const FcChar8*) family); |
| 760 if (size > 0) |
| 761 FcPatternAddDouble (pattern, FC_SIZE, (double) size); |
| 762 if (g_strcmp0 (language, "")) |
| 763 FcPatternAddString (pattern, FC_LANG, (const FcChar8*) language); |
| 764 FcPatternAddInteger (pattern, FC_WEIGHT, FC_WEIGHT_NORMAL); |
| 765 FcPatternAddInteger (pattern, FC_WIDTH, FC_WIDTH_NORMAL); |
| 766 FcPatternAddInteger (pattern, FC_DPI, 96); |
| 767 FcConfigSubstitute (NULL, pattern, FcMatchPattern); |
| 768 FcConfigSubstitute (NULL, pattern, FcMatchFont); |
| 769 FcDefaultSubstitute (pattern); |
| 770 m_fcfontset = FcFontSort (NULL, pattern, FcTrue, NULL, &result); |
| 771 FcPatternDestroy (pattern); |
| 772 if (result == FcResultNoMatch || m_fcfontset->nfont == 0) { |
| 773 g_warning ("No FcFontSet for %s", family ? family : "(null)"); |
| 774 return FALSE; |
| 775 } |
| 776 return TRUE; |
| 777 } |
| 778 |
| 779 void |
| 780 ibus_fontset_unref (IBusFontSet *fontset) |
| 781 { |
| 782 g_object_unref (fontset); |
| 783 } |
| 784 |
| 785 IBusRequisitionEx * |
| 786 ibus_fontset_get_preferred_size_hb (IBusFontSet *fontset, |
| 787 const gchar *text, |
| 788 cairo_rectangle_int_t *widest) |
| 789 { |
| 790 gchar *copied_text; |
| 791 gchar *p; |
| 792 FontPerChar *buff; |
| 793 IBusCairoLine *cairo_lines = NULL; |
| 794 IBusRequisitionEx *req = NULL; |
| 795 GString *str = NULL; |
| 796 int text_length; |
| 797 int i, n = 0; |
| 798 |
| 799 g_return_val_if_fail (IBUS_IS_FONTSET (fontset), NULL); |
| 800 g_return_val_if_fail (m_fcfontset != NULL, NULL); |
| 801 |
| 802 copied_text = g_strdup (text); |
| 803 text_length = g_utf8_strlen (text, -1); |
| 804 buff = g_slice_alloc0 (sizeof (FontPerChar) * text_length); |
| 805 str = g_string_new (NULL); |
| 806 |
| 807 for (p = copied_text; *p != '\0'; p = g_utf8_next_char (p)) { |
| 808 gunichar c = g_utf8_get_char (p); |
| 809 gboolean has_glyphs = FALSE; |
| 810 buff[n].ch = c; |
| 811 if ((c == 0xfe0eu || c == 0xfe0fu) && n > 0) { |
| 812 buff[n].fcfont = buff[n-1].fcfont; |
| 813 ++n; |
| 814 continue; |
| 815 } |
| 816 for (i = 0; i < m_fcfontset->nfont; i++) { |
| 817 if (g_unichar_iscntrl (c) && !g_unichar_isspace (c)) |
| 818 break; |
| 819 FT_Face ft_face = ibus_fontset_get_ftface_from_fcfont ( |
| 820 fontset, |
| 821 m_fcfontset->fonts[i]); |
| 822 if (FT_Get_Char_Index (ft_face, c) != 0) { |
| 823 buff[n].fcfont = m_fcfontset->fonts[i]; |
| 824 if (n > 0 && buff[n - 1].fcfont != buff[n].fcfont) { |
| 825 get_string_extents_with_font (str->str, |
| 826 &buff[n - 1], |
| 827 widest, |
| 828 &cairo_lines); |
| 829 g_string_free (str, TRUE); |
| 830 str = g_string_new (NULL); |
| 831 g_string_append_unichar (str, c); |
| 832 } else { |
| 833 g_string_append_unichar (str, c); |
| 834 } |
| 835 ++n; |
| 836 has_glyphs = TRUE; |
| 837 FT_Done_Face (ft_face); |
| 838 break; |
| 839 } |
| 840 FT_Done_Face (ft_face); |
| 841 } |
| 842 if (!has_glyphs) { |
| 843 if (n > 0) { |
| 844 buff[n].fcfont = buff[n - 1].fcfont; |
| 845 } else { |
| 846 /* Search a font for non-glyph char to draw the code points |
| 847 * likes Pango. |
| 848 */ |
| 849 for (i = 0; i < m_fcfontset->nfont; i++) { |
| 850 FT_Face ft_face = ibus_fontset_get_ftface_from_fcfont ( |
| 851 fontset, |
| 852 m_fcfontset->fonts[i]); |
| 853 /* Check alphabets instead of space or digits |
| 854 * because 'Noto Emoji Color' font's digits are |
| 855 * white color and cannot change the font color. |
| 856 * the font does not have alphabets. |
| 857 */ |
| 858 if (FT_Get_Char_Index (ft_face, 'A') != 0) { |
| 859 buff[n].fcfont = m_fcfontset->fonts[i]; |
| 860 FT_Done_Face (ft_face); |
| 861 has_glyphs = TRUE; |
| 862 break; |
| 863 } |
| 864 FT_Done_Face (ft_face); |
| 865 } |
| 866 if (!has_glyphs) { |
| 867 buff[n].fcfont = m_fcfontset->fonts[0]; |
| 868 g_warning ("Not found fonts for unicode %04X at %d in %s", |
| 869 c, n, text); |
| 870 } |
| 871 } |
| 872 n++; |
| 873 g_string_append_unichar (str, c); |
| 874 } |
| 875 } |
| 876 if (str->str) { |
| 877 get_string_extents_with_font (str->str, |
| 878 &buff[n - 1], |
| 879 widest, |
| 880 &cairo_lines); |
| 881 g_string_free (str, TRUE); |
| 882 } |
| 883 g_slice_free1 (sizeof (FontPerChar) * text_length, buff); |
| 884 g_free (copied_text); |
| 885 widest->width += XPAD * 2; |
| 886 widest->height += YPAD * 2; |
| 887 req = g_new0 (IBusRequisitionEx, 1); |
| 888 req->width = widest->width; |
| 889 req->height = widest->height; |
| 890 req->cairo_lines = cairo_lines; |
| 891 return req; |
| 892 } |
| 893 |
| 894 void |
| 895 ibus_fontset_draw_cairo_with_requisition_ex (IBusFontSet *fontset, |
| 896 cairo_t *cr, |
| 897 IBusRequisitionEx *ex) |
| 898 { |
| 899 IBusCairoLine *cairo_lines; |
| 900 int i; |
| 901 |
| 902 g_return_if_fail (IBUS_IS_FONTSET (fontset)); |
| 903 g_return_if_fail (cr != NULL); |
| 904 g_return_if_fail (ex != NULL); |
| 905 |
| 906 cairo_lines = ex->cairo_lines; |
| 907 g_return_if_fail (cairo_lines != NULL); |
| 908 |
| 909 for (i = 0; cairo_lines[i].scaled_font; i++) { |
| 910 const cairo_glyph_t *glyphs = (cairo_glyph_t *) cairo_lines[i].glyphs; |
| 911 guint num_glyphs = cairo_lines[i].num_glyphs; |
| 912 |
| 913 cairo_ft_scaled_font_lock_face (cairo_lines[i].scaled_font); |
| 914 cairo_set_scaled_font (cr, cairo_lines[i].scaled_font); |
| 915 if (num_glyphs > 0 && glyphs[0].index & PANGO_GLYPH_UNKNOWN_FLAG) { |
| 916 _cairo_show_unknown_glyphs (cr, glyphs, num_glyphs, |
| 917 ex->width, ex->height); |
| 918 } else { |
| 919 cairo_show_glyphs (cr, glyphs, num_glyphs); |
| 920 } |
| 921 cairo_ft_scaled_font_unlock_face (cairo_lines[i].scaled_font); |
| 922 } |
| 923 } |
OLD | NEW |