1 /* xftfont.c -- XFT font driver.
2 Copyright (C) 2006, 2007, 2008, 2009 Free Software Foundation, Inc.
3 Copyright (C) 2006, 2007, 2008, 2009
4 National Institute of Advanced Industrial Science and Technology (AIST)
5 Registration Number H13PRO009
7 This file is part of GNU Emacs.
9 GNU Emacs is free software: you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation, either version 3 of the License, or
12 (at your option) any later version.
14 GNU Emacs is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
19 You should have received a copy of the GNU General Public License
20 along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
26 #include <X11/Xft/Xft.h>
29 #include "dispextern.h"
32 #include "blockinput.h"
33 #include "character.h"
39 /* Xft font driver. */
41 static Lisp_Object Qxft
;
42 static Lisp_Object QChinting
, QCautohint
, QChintstyle
, QCrgba
, QCembolden
;
44 /* The actual structure for Xft font that can be casted to struct
50 /* The following five members must be here in this order to be
51 compatible with struct ftfont_info (in ftfont.c). */
53 int maybe_otf
; /* Flag to tell if this may be OTF or not. */
55 #endif /* HAVE_LIBOTF */
64 /* Structure pointed by (struct face *)->extra */
68 XftColor xft_fg
; /* color for face->foreground */
69 XftColor xft_bg
; /* color for face->background */
72 static void xftfont_get_colors
P_ ((FRAME_PTR
, struct face
*, GC gc
,
73 struct xftface_info
*,
74 XftColor
*fg
, XftColor
*bg
));
77 /* Setup foreground and background colors of GC into FG and BG. If
78 XFTFACE_INFO is not NULL, reuse the colors in it if possible. BG
82 xftfont_get_colors (f
, face
, gc
, xftface_info
, fg
, bg
)
86 struct xftface_info
*xftface_info
;
89 if (xftface_info
&& face
->gc
== gc
)
91 *fg
= xftface_info
->xft_fg
;
93 *bg
= xftface_info
->xft_bg
;
98 int fg_done
= 0, bg_done
= 0;
101 XGetGCValues (FRAME_X_DISPLAY (f
), gc
,
102 GCForeground
| GCBackground
, &xgcv
);
105 if (xgcv
.foreground
== face
->foreground
)
106 *fg
= xftface_info
->xft_fg
, fg_done
= 1;
107 else if (xgcv
.foreground
== face
->background
)
108 *fg
= xftface_info
->xft_bg
, fg_done
= 1;
111 else if (xgcv
.background
== face
->background
)
112 *bg
= xftface_info
->xft_bg
, bg_done
= 1;
113 else if (xgcv
.background
== face
->foreground
)
114 *bg
= xftface_info
->xft_fg
, bg_done
= 1;
117 if (fg_done
+ bg_done
< 2)
121 colors
[0].pixel
= fg
->pixel
= xgcv
.foreground
;
123 colors
[1].pixel
= bg
->pixel
= xgcv
.background
;
124 XQueryColors (FRAME_X_DISPLAY (f
), FRAME_X_COLORMAP (f
), colors
,
126 fg
->color
.alpha
= 0xFFFF;
127 fg
->color
.red
= colors
[0].red
;
128 fg
->color
.green
= colors
[0].green
;
129 fg
->color
.blue
= colors
[0].blue
;
132 bg
->color
.alpha
= 0xFFFF;
133 bg
->color
.red
= colors
[1].red
;
134 bg
->color
.green
= colors
[1].green
;
135 bg
->color
.blue
= colors
[1].blue
;
143 static Lisp_Object xftfont_list
P_ ((Lisp_Object
, Lisp_Object
));
144 static Lisp_Object xftfont_match
P_ ((Lisp_Object
, Lisp_Object
));
145 static Lisp_Object xftfont_open
P_ ((FRAME_PTR
, Lisp_Object
, int));
146 static void xftfont_close
P_ ((FRAME_PTR
, struct font
*));
147 static int xftfont_prepare_face
P_ ((FRAME_PTR
, struct face
*));
148 static void xftfont_done_face
P_ ((FRAME_PTR
, struct face
*));
149 static int xftfont_has_char
P_ ((Lisp_Object
, int));
150 static unsigned xftfont_encode_char
P_ ((struct font
*, int));
151 static int xftfont_text_extents
P_ ((struct font
*, unsigned *, int,
152 struct font_metrics
*));
153 static int xftfont_draw
P_ ((struct glyph_string
*, int, int, int, int, int));
154 static int xftfont_end_for_frame
P_ ((FRAME_PTR f
));
156 struct font_driver xftfont_driver
;
159 xftfont_list (frame
, spec
)
163 Lisp_Object list
= ftfont_driver
.list (frame
, spec
), tail
;
165 for (tail
= list
; CONSP (tail
); tail
= XCDR (tail
))
166 ASET (XCAR (tail
), FONT_TYPE_INDEX
, Qxft
);
171 xftfont_match (frame
, spec
)
175 Lisp_Object entity
= ftfont_driver
.match (frame
, spec
);
178 ASET (entity
, FONT_TYPE_INDEX
, Qxft
);
182 extern Lisp_Object ftfont_font_format
P_ ((FcPattern
*, Lisp_Object
));
183 extern FcCharSet
*ftfont_get_fc_charset
P_ ((Lisp_Object
));
184 extern Lisp_Object QCantialias
;
186 static FcChar8 ascii_printable
[95];
189 xftfont_fix_match (pat
, match
)
190 FcPattern
*pat
, *match
;
192 /* These values are not used for matching (except antialias), but for
193 rendering, so make sure they are carried over to the match.
194 We also put antialias here because most fonts are antialiased, so
195 the match will have antialias true. */
201 FcPatternGetBool (pat
, FC_ANTIALIAS
, 0, &b
);
204 FcPatternDel (match
, FC_ANTIALIAS
);
205 FcPatternAddBool (match
, FC_ANTIALIAS
, FcFalse
);
207 FcPatternGetBool (pat
, FC_HINTING
, 0, &b
);
210 FcPatternDel (match
, FC_HINTING
);
211 FcPatternAddBool (match
, FC_HINTING
, FcFalse
);
213 if (FcResultMatch
== FcPatternGetInteger (pat
, FC_HINT_STYLE
, 0, &i
))
215 FcPatternDel (match
, FC_HINT_STYLE
);
216 FcPatternAddInteger (match
, FC_HINT_STYLE
, i
);
218 #ifndef FC_LCD_FILTER
219 /* Older fontconfig versions don't have FC_LCD_FILTER. */
220 #define FC_LCD_FILTER "lcdfilter"
222 if (FcResultMatch
== FcPatternGetInteger (pat
, FC_LCD_FILTER
, 0, &i
))
224 FcPatternDel (match
, FC_LCD_FILTER
);
225 FcPatternAddInteger (match
, FC_LCD_FILTER
, i
);
227 if (FcResultMatch
== FcPatternGetInteger (pat
, FC_RGBA
, 0, &i
))
229 FcPatternDel (match
, FC_RGBA
);
230 FcPatternAddInteger (match
, FC_RGBA
, i
);
232 if (FcResultMatch
== FcPatternGetDouble (pat
, FC_DPI
, 0, &dpi
))
234 FcPatternDel (match
, FC_DPI
);
235 FcPatternAddDouble (match
, FC_DPI
, dpi
);
240 xftfont_open (f
, entity
, pixel_size
)
246 Display
*display
= FRAME_X_DISPLAY (f
);
247 Lisp_Object val
, filename
, index
, tail
, font_object
;
248 FcPattern
*pat
= NULL
, *match
;
249 struct xftfont_info
*xftfont_info
= NULL
;
252 XftFont
*xftfont
= NULL
;
260 val
= assq_no_quit (QCfont_entity
, AREF (entity
, FONT_EXTRA_INDEX
));
264 filename
= XCAR (val
);
266 size
= XINT (AREF (entity
, FONT_SIZE_INDEX
));
269 pat
= FcPatternCreate ();
270 FcPatternAddInteger (pat
, FC_WEIGHT
, FONT_WEIGHT_NUMERIC (entity
));
271 i
= FONT_SLANT_NUMERIC (entity
) - 100;
273 FcPatternAddInteger (pat
, FC_SLANT
, i
);
274 FcPatternAddInteger (pat
, FC_WIDTH
, FONT_WIDTH_NUMERIC (entity
));
275 FcPatternAddDouble (pat
, FC_PIXEL_SIZE
, pixel_size
);
276 val
= AREF (entity
, FONT_FAMILY_INDEX
);
278 FcPatternAddString (pat
, FC_FAMILY
, (FcChar8
*) SDATA (SYMBOL_NAME (val
)));
279 val
= AREF (entity
, FONT_FOUNDRY_INDEX
);
281 FcPatternAddString (pat
, FC_FOUNDRY
, (FcChar8
*) SDATA (SYMBOL_NAME (val
)));
282 val
= AREF (entity
, FONT_SPACING_INDEX
);
284 FcPatternAddInteger (pat
, FC_SPACING
, XINT (val
));
285 val
= AREF (entity
, FONT_DPI_INDEX
);
288 double dbl
= XINT (val
);
290 FcPatternAddDouble (pat
, FC_DPI
, dbl
);
292 val
= AREF (entity
, FONT_AVGWIDTH_INDEX
);
293 if (INTEGERP (val
) && XINT (val
) == 0)
294 FcPatternAddBool (pat
, FC_SCALABLE
, FcTrue
);
295 /* This is necessary to identify the exact font (e.g. 10x20.pcf.gz
296 over 10x20-ISO8859-1.pcf.gz). */
297 FcPatternAddCharSet (pat
, FC_CHARSET
, ftfont_get_fc_charset (entity
));
299 for (tail
= AREF (entity
, FONT_EXTRA_INDEX
); CONSP (tail
); tail
= XCDR (tail
))
301 Lisp_Object key
, val
;
303 key
= XCAR (XCAR (tail
)), val
= XCDR (XCAR (tail
));
304 if (EQ (key
, QCantialias
))
305 FcPatternAddBool (pat
, FC_ANTIALIAS
, NILP (val
) ? FcFalse
: FcTrue
);
306 else if (EQ (key
, QChinting
))
307 FcPatternAddBool (pat
, FC_HINTING
, NILP (val
) ? FcFalse
: FcTrue
);
308 else if (EQ (key
, QCautohint
))
309 FcPatternAddBool (pat
, FC_AUTOHINT
, NILP (val
) ? FcFalse
: FcTrue
);
310 else if (EQ (key
, QChintstyle
))
313 FcPatternAddInteger (pat
, FC_HINT_STYLE
, XINT (val
));
315 else if (EQ (key
, QCrgba
))
318 FcPatternAddInteger (pat
, FC_RGBA
, XINT (val
));
321 else if (EQ (key
, QCembolden
))
322 FcPatternAddBool (pat
, FC_EMBOLDEN
, NILP (val
) ? FcFalse
: FcTrue
);
326 FcPatternAddString (pat
, FC_FILE
, (FcChar8
*) SDATA (filename
));
327 FcPatternAddInteger (pat
, FC_INDEX
, XINT (index
));
331 /* Make sure that the Xrender extension is added before the Xft one.
332 Otherwise, the close-display hook set by Xft is called after the
333 one for Xrender, and the former tries to re-add the latter. This
334 results in inconsistency of internal states and leads to X
335 protocol error when one reconnects to the same X server.
338 int event_base
, error_base
;
339 XRenderQueryExtension (display
, &event_base
, &error_base
);
342 /* Substitute in values from X resources and XftDefaultSet. */
343 XftDefaultSubstitute (display
, FRAME_X_SCREEN_NUMBER (f
), pat
);
344 match
= XftFontMatch (display
, FRAME_X_SCREEN_NUMBER (f
), pat
, &result
);
345 xftfont_fix_match (pat
, match
);
347 FcPatternDestroy (pat
);
348 xftfont
= XftFontOpenPattern (display
, match
);
352 XftPatternDestroy (match
);
355 ft_face
= XftLockFace (xftfont
);
358 /* We should not destroy PAT here because it is kept in XFTFONT and
359 destroyed automatically when XFTFONT is closed. */
360 font_object
= font_make_object (VECSIZE (struct xftfont_info
), entity
, size
);
361 ASET (font_object
, FONT_TYPE_INDEX
, Qxft
);
362 len
= font_unparse_xlfd (entity
, size
, name
, 256);
364 ASET (font_object
, FONT_NAME_INDEX
, make_string (name
, len
));
365 len
= font_unparse_fcname (entity
, size
, name
, 256);
367 ASET (font_object
, FONT_FULLNAME_INDEX
, make_string (name
, len
));
369 ASET (font_object
, FONT_FULLNAME_INDEX
,
370 AREF (font_object
, FONT_NAME_INDEX
));
371 ASET (font_object
, FONT_FILE_INDEX
, filename
);
372 ASET (font_object
, FONT_FORMAT_INDEX
,
373 ftfont_font_format (xftfont
->pattern
, filename
));
374 font
= XFONT_OBJECT (font_object
);
375 font
->pixel_size
= pixel_size
;
376 font
->driver
= &xftfont_driver
;
377 font
->encoding_charset
= font
->repertory_charset
= -1;
379 xftfont_info
= (struct xftfont_info
*) font
;
380 xftfont_info
->display
= display
;
381 xftfont_info
->screen
= FRAME_X_SCREEN_NUMBER (f
);
382 xftfont_info
->xftfont
= xftfont
;
383 /* This means that there's no need of transformation. */
384 xftfont_info
->matrix
.xx
= 0;
385 if (FcPatternGetMatrix (xftfont
->pattern
, FC_MATRIX
, 0, &matrix
)
388 xftfont_info
->matrix
.xx
= 0x10000L
* matrix
->xx
;
389 xftfont_info
->matrix
.yy
= 0x10000L
* matrix
->yy
;
390 xftfont_info
->matrix
.xy
= 0x10000L
* matrix
->xy
;
391 xftfont_info
->matrix
.yx
= 0x10000L
* matrix
->yx
;
393 font
->pixel_size
= size
;
394 font
->driver
= &xftfont_driver
;
395 if (INTEGERP (AREF (entity
, FONT_SPACING_INDEX
)))
396 spacing
= XINT (AREF (entity
, FONT_SPACING_INDEX
));
398 spacing
= FC_PROPORTIONAL
;
399 if (! ascii_printable
[0])
402 for (i
= 0; i
< 95; i
++)
403 ascii_printable
[i
] = ' ' + i
;
406 if (spacing
!= FC_PROPORTIONAL
)
408 font
->min_width
= font
->average_width
= font
->space_width
409 = xftfont
->max_advance_width
;
410 XftTextExtents8 (display
, xftfont
, ascii_printable
+ 1, 94, &extents
);
414 XftTextExtents8 (display
, xftfont
, ascii_printable
, 1, &extents
);
415 font
->space_width
= extents
.xOff
;
416 if (font
->space_width
<= 0)
417 /* dirty workaround */
418 font
->space_width
= pixel_size
;
419 XftTextExtents8 (display
, xftfont
, ascii_printable
+ 1, 94, &extents
);
420 font
->average_width
= (font
->space_width
+ extents
.xOff
) / 95;
424 font
->ascent
= xftfont
->ascent
;
425 font
->descent
= xftfont
->descent
;
428 /* The above condition is a dirty workaround because
429 XftTextExtents8 behaves strangely for some fonts
430 (e.g. "Dejavu Sans Mono") when pixel_size is less than 5. */
431 if (font
->ascent
< extents
.y
)
432 font
->ascent
= extents
.y
;
433 if (font
->descent
< extents
.height
- extents
.y
)
434 font
->descent
= extents
.height
- extents
.y
;
436 font
->height
= font
->ascent
+ font
->descent
;
438 if (XINT (AREF (entity
, FONT_SIZE_INDEX
)) == 0)
440 int upEM
= ft_face
->units_per_EM
;
442 font
->underline_position
= -ft_face
->underline_position
* size
/ upEM
;
443 font
->underline_thickness
= ft_face
->underline_thickness
* size
/ upEM
;
444 if (font
->underline_thickness
> 2)
445 font
->underline_position
-= font
->underline_thickness
/ 2;
449 font
->underline_position
= -1;
450 font
->underline_thickness
= 0;
453 xftfont_info
->maybe_otf
= ft_face
->face_flags
& FT_FACE_FLAG_SFNT
;
454 xftfont_info
->otf
= NULL
;
455 #endif /* HAVE_LIBOTF */
456 xftfont_info
->ft_size
= ft_face
->size
;
458 /* Unfortunately Xft doesn't provide a way to get minimum char
459 width. So, we use space_width instead. */
460 font
->min_width
= font
->space_width
;
462 font
->baseline_offset
= 0;
463 font
->relative_compose
= 0;
464 font
->default_ascent
= 0;
465 font
->vertical_centering
= 0;
467 if (! (ft_face
->face_flags
& FT_FACE_FLAG_SFNT
))
471 if (FT_Get_BDF_Property (ft_face
, "_MULE_BASELINE_OFFSET", &rec
) == 0
472 && rec
.type
== BDF_PROPERTY_TYPE_INTEGER
)
473 font
->baseline_offset
= rec
.u
.integer
;
474 if (FT_Get_BDF_Property (ft_face
, "_MULE_RELATIVE_COMPOSE", &rec
) == 0
475 && rec
.type
== BDF_PROPERTY_TYPE_INTEGER
)
476 font
->relative_compose
= rec
.u
.integer
;
477 if (FT_Get_BDF_Property (ft_face
, "_MULE_DEFAULT_ASCENT", &rec
) == 0
478 && rec
.type
== BDF_PROPERTY_TYPE_INTEGER
)
479 font
->default_ascent
= rec
.u
.integer
;
487 xftfont_close (f
, font
)
491 struct xftfont_info
*xftfont_info
= (struct xftfont_info
*) font
;
494 if (xftfont_info
->otf
)
495 OTF_close (xftfont_info
->otf
);
498 XftUnlockFace (xftfont_info
->xftfont
);
499 XftFontClose (xftfont_info
->display
, xftfont_info
->xftfont
);
504 xftfont_prepare_face (f
, face
)
508 struct xftface_info
*xftface_info
;
511 /* This doesn't work if face->ascii_face doesn't use an Xft font. */
512 if (face
!= face
->ascii_face
)
514 face
->extra
= face
->ascii_face
->extra
;
519 xftface_info
= malloc (sizeof (struct xftface_info
));
522 xftfont_get_colors (f
, face
, face
->gc
, NULL
,
523 &xftface_info
->xft_fg
, &xftface_info
->xft_bg
);
524 face
->extra
= xftface_info
;
529 xftfont_done_face (f
, face
)
533 struct xftface_info
*xftface_info
;
536 /* This doesn't work if face->ascii_face doesn't use an Xft font. */
537 if (face
!= face
->ascii_face
542 xftface_info
= (struct xftface_info
*) face
->extra
;
550 extern Lisp_Object Qja
, Qko
;
553 xftfont_has_char (font
, c
)
557 struct xftfont_info
*xftfont_info
;
558 struct charset
*cs
= NULL
;
560 if (EQ (AREF (font
, FONT_ADSTYLE_INDEX
), Qja
)
561 && charset_jisx0208
>= 0)
562 cs
= CHARSET_FROM_ID (charset_jisx0208
);
563 else if (EQ (AREF (font
, FONT_ADSTYLE_INDEX
), Qko
)
564 && charset_ksc5601
>= 0)
565 cs
= CHARSET_FROM_ID (charset_ksc5601
);
567 return (ENCODE_CHAR (cs
, c
) != CHARSET_INVALID_CODE (cs
));
569 if (FONT_ENTITY_P (font
))
570 return ftfont_driver
.has_char (font
, c
);
571 xftfont_info
= (struct xftfont_info
*) XFONT_OBJECT (font
);
572 return (XftCharExists (xftfont_info
->display
, xftfont_info
->xftfont
,
573 (FcChar32
) c
) == FcTrue
);
577 xftfont_encode_char (font
, c
)
581 struct xftfont_info
*xftfont_info
= (struct xftfont_info
*) font
;
582 unsigned code
= XftCharIndex (xftfont_info
->display
, xftfont_info
->xftfont
,
585 return (code
? code
: FONT_INVALID_CODE
);
589 xftfont_text_extents (font
, code
, nglyphs
, metrics
)
593 struct font_metrics
*metrics
;
595 struct xftfont_info
*xftfont_info
= (struct xftfont_info
*) font
;
599 XftGlyphExtents (xftfont_info
->display
, xftfont_info
->xftfont
, code
, nglyphs
,
604 metrics
->lbearing
= - extents
.x
;
605 metrics
->rbearing
= - extents
.x
+ extents
.width
;
606 metrics
->width
= extents
.xOff
;
607 metrics
->ascent
= extents
.y
;
608 metrics
->descent
= extents
.height
- extents
.y
;
614 xftfont_get_xft_draw (f
)
617 XftDraw
*xft_draw
= font_get_frame_data (f
, &xftfont_driver
);
622 xft_draw
= XftDrawCreate (FRAME_X_DISPLAY (f
),
625 FRAME_X_COLORMAP (f
));
629 font_put_frame_data (f
, &xftfont_driver
, xft_draw
);
635 xftfont_draw (s
, from
, to
, x
, y
, with_background
)
636 struct glyph_string
*s
;
637 int from
, to
, x
, y
, with_background
;
640 struct face
*face
= s
->face
;
641 struct xftfont_info
*xftfont_info
= (struct xftfont_info
*) s
->font
;
642 struct xftface_info
*xftface_info
= NULL
;
643 XftDraw
*xft_draw
= xftfont_get_xft_draw (f
);
649 if (s
->font
== face
->font
)
650 xftface_info
= (struct xftface_info
*) face
->extra
;
651 xftfont_get_colors (f
, face
, s
->gc
, xftface_info
,
652 &fg
, with_background
? &bg
: NULL
);
654 if (s
->num_clips
> 0)
655 XftDrawSetClipRectangles (xft_draw
, 0, 0, s
->clip
, s
->num_clips
);
657 XftDrawSetClip (xft_draw
, NULL
);
660 XftDrawRect (xft_draw
, &bg
,
661 x
, y
- face
->font
->ascent
, s
->width
, face
->font
->height
);
662 code
= alloca (sizeof (FT_UInt
) * len
);
663 for (i
= 0; i
< len
; i
++)
664 code
[i
] = ((XCHAR2B_BYTE1 (s
->char2b
+ from
+ i
) << 8)
665 | XCHAR2B_BYTE2 (s
->char2b
+ from
+ i
));
668 for (i
= 0; i
< len
; i
++)
669 XftDrawGlyphs (xft_draw
, &fg
, xftfont_info
->xftfont
,
670 x
+ i
, y
, code
+ i
, 1);
672 XftDrawGlyphs (xft_draw
, &fg
, xftfont_info
->xftfont
,
680 xftfont_end_for_frame (f
)
685 /* Don't do anything if display is dead */
686 if (FRAME_X_DISPLAY (f
) == NULL
) return 0;
688 xft_draw
= font_get_frame_data (f
, &xftfont_driver
);
693 XftDrawDestroy (xft_draw
);
695 font_put_frame_data (f
, &xftfont_driver
, NULL
);
703 DEFSYM (Qxft
, "xft");
704 DEFSYM (QChinting
, ":hinting");
705 DEFSYM (QCautohint
, ":autohint");
706 DEFSYM (QChintstyle
, ":hintstyle");
707 DEFSYM (QCrgba
, ":rgba");
708 DEFSYM (QCembolden
, ":embolden");
710 xftfont_driver
= ftfont_driver
;
711 xftfont_driver
.type
= Qxft
;
712 xftfont_driver
.get_cache
= xfont_driver
.get_cache
;
713 xftfont_driver
.list
= xftfont_list
;
714 xftfont_driver
.match
= xftfont_match
;
715 xftfont_driver
.open
= xftfont_open
;
716 xftfont_driver
.close
= xftfont_close
;
717 xftfont_driver
.prepare_face
= xftfont_prepare_face
;
718 xftfont_driver
.done_face
= xftfont_done_face
;
719 xftfont_driver
.has_char
= xftfont_has_char
;
720 xftfont_driver
.encode_char
= xftfont_encode_char
;
721 xftfont_driver
.text_extents
= xftfont_text_extents
;
722 xftfont_driver
.draw
= xftfont_draw
;
723 xftfont_driver
.end_for_frame
= xftfont_end_for_frame
;
725 register_font_driver (&xftfont_driver
, NULL
);
728 /* arch-tag: 64ec61bf-7c8e-4fe6-b953-c6a85d5e1605
729 (do not change this comment) */