1 /* font.c - Font API and font file loader. */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2003,2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc.
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
20 #include <grub/bufio.h>
22 #include <grub/file.h>
23 #include <grub/font.h>
24 #include <grub/misc.h>
26 #include <grub/types.h>
27 #include <grub/video.h>
28 #include <grub/bitmap.h>
29 #include <grub/charset.h>
30 #include <grub/unicode.h>
31 #include <grub/fontformat.h>
34 GRUB_MOD_LICENSE ("GPLv3+");
44 struct char_index_entry
47 grub_uint8_t storage_flags
;
50 /* Glyph if loaded, or NULL otherwise. */
51 struct grub_font_glyph
*glyph
;
54 #define FONT_WEIGHT_NORMAL 100
55 #define FONT_WEIGHT_BOLD 200
56 #define ASCII_BITMAP_SIZE 16
58 /* Definition of font registry. */
59 struct grub_font_node
*grub_font_list
;
61 static int register_font (grub_font_t font
);
62 static void font_init (grub_font_t font
);
63 static void free_font (grub_font_t font
);
64 static void remove_font (grub_font_t font
);
66 struct font_file_section
68 /* The file this section is in. */
71 /* FOURCC name of the section. */
74 /* Length of the section contents. */
77 /* Set by open_section() on EOF. */
81 /* Replace unknown glyphs with a rounded question mark. */
82 static grub_uint8_t unknown_glyph_bitmap
[] = {
102 /* The "unknown glyph" glyph, used as a last resort. */
103 static struct grub_font_glyph
*unknown_glyph
;
105 /* The font structure used when no other font is loaded. This functions
106 as a "Null Object" pattern, so that code everywhere does not have to
107 check for a NULL grub_font_t to avoid dereferencing a null pointer. */
108 static struct grub_font null_font
;
110 /* Flag to ensure module is initialized only once. */
111 static grub_uint8_t font_loader_initialized
;
114 static struct grub_font_glyph
*ascii_font_glyph
[0x80];
117 static struct grub_font_glyph
*
118 ascii_glyph_lookup (grub_uint32_t code
)
121 static int ascii_failback_initialized
= 0;
126 if (ascii_failback_initialized
== 0)
129 for (current
= 0; current
< 0x80; current
++)
131 ascii_font_glyph
[current
] =
132 grub_malloc (sizeof (struct grub_font_glyph
) + ASCII_BITMAP_SIZE
);
134 ascii_font_glyph
[current
]->width
= 8;
135 ascii_font_glyph
[current
]->height
= 16;
136 ascii_font_glyph
[current
]->offset_x
= 0;
137 ascii_font_glyph
[current
]->offset_y
= -2;
138 ascii_font_glyph
[current
]->device_width
= 8;
139 ascii_font_glyph
[current
]->font
= NULL
;
141 grub_memcpy (ascii_font_glyph
[current
]->bitmap
,
142 &ascii_bitmaps
[current
* ASCII_BITMAP_SIZE
],
146 ascii_failback_initialized
= 1;
149 return ascii_font_glyph
[code
];
157 grub_font_loader_init (void)
159 /* Only initialize font loader once. */
160 if (font_loader_initialized
)
163 /* Make glyph for unknown glyph. */
164 unknown_glyph
= grub_malloc (sizeof (struct grub_font_glyph
)
165 + sizeof (unknown_glyph_bitmap
));
169 unknown_glyph
->width
= 8;
170 unknown_glyph
->height
= 16;
171 unknown_glyph
->offset_x
= 0;
172 unknown_glyph
->offset_y
= -3;
173 unknown_glyph
->device_width
= 8;
174 grub_memcpy (unknown_glyph
->bitmap
,
175 unknown_glyph_bitmap
, sizeof (unknown_glyph_bitmap
));
177 /* Initialize the null font. */
178 font_init (&null_font
);
179 /* FIXME: Fix this slightly improper cast. */
180 null_font
.name
= (char *) "<No Font>";
181 null_font
.ascent
= unknown_glyph
->height
- 3;
182 null_font
.descent
= 3;
183 null_font
.max_char_width
= unknown_glyph
->width
;
184 null_font
.max_char_height
= unknown_glyph
->height
;
186 font_loader_initialized
= 1;
189 /* Initialize the font object with initial default values. */
191 font_init (grub_font_t font
)
196 font
->point_size
= 0;
199 /* Default leading value, not in font file yet. */
202 font
->max_char_width
= 0;
203 font
->max_char_height
= 0;
207 font
->char_index
= 0;
211 /* Open the next section in the file.
213 On success, the section name is stored in section->name and the length in
214 section->length, and 0 is returned. On failure, 1 is returned and
215 grub_errno is set appropriately with an error message.
217 If 1 is returned due to being at the end of the file, then section->eof is
218 set to 1; otherwise, section->eof is set to 0. */
220 open_section (grub_file_t file
, struct font_file_section
*section
)
223 grub_uint32_t raw_length
;
225 section
->file
= file
;
228 /* Read the FOURCC section name. */
229 retval
= grub_file_read (file
, section
->name
, 4);
230 if (retval
>= 0 && retval
< 4)
232 /* EOF encountered. */
242 /* Read the big-endian 32-bit section length. */
243 retval
= grub_file_read (file
, &raw_length
, 4);
244 if (retval
>= 0 && retval
< 4)
246 /* EOF encountered. */
256 /* Convert byte-order and store in *length. */
257 section
->length
= grub_be_to_cpu32 (raw_length
);
262 /* Size in bytes of each character index (CHIX section)
263 entry in the font file. */
264 #define FONT_CHAR_INDEX_ENTRY_SIZE (4 + 1 + 4)
266 /* Load the character index (CHIX) section contents from the font file. This
267 presumes that the position of FILE is positioned immediately after the
268 section length for the CHIX section (i.e., at the start of the section
269 contents). Returns 0 upon success, nonzero for failure (in which case
270 grub_errno is set appropriately). */
272 load_font_index (grub_file_t file
, grub_uint32_t sect_length
, struct
276 grub_uint32_t last_code
;
279 grub_dprintf ("font", "load_font_index(sect_length=%d)\n", sect_length
);
282 /* Sanity check: ensure section length is divisible by the entry size. */
283 if ((sect_length
% FONT_CHAR_INDEX_ENTRY_SIZE
) != 0)
285 grub_error (GRUB_ERR_BAD_FONT
,
286 "font file format error: character index length %d "
287 "is not a multiple of the entry size %d",
288 sect_length
, FONT_CHAR_INDEX_ENTRY_SIZE
);
292 /* Calculate the number of characters. */
293 font
->num_chars
= sect_length
/ FONT_CHAR_INDEX_ENTRY_SIZE
;
295 /* Allocate the character index array. */
296 font
->char_index
= grub_malloc (font
->num_chars
297 * sizeof (struct char_index_entry
));
298 if (!font
->char_index
)
300 font
->bmp_idx
= grub_malloc (0x10000 * sizeof (grub_uint16_t
));
303 grub_memset (font
->bmp_idx
, 0xff, 0x10000 * sizeof (grub_uint16_t
));
307 grub_dprintf ("font", "num_chars=%d)\n", font
->num_chars
);
312 /* Load the character index data from the file. */
313 for (i
= 0; i
< font
->num_chars
; i
++)
315 struct char_index_entry
*entry
= &font
->char_index
[i
];
317 /* Read code point value; convert to native byte order. */
318 if (grub_file_read (file
, &entry
->code
, 4) != 4)
320 entry
->code
= grub_be_to_cpu32 (entry
->code
);
322 /* Verify that characters are in ascending order. */
323 if (i
!= 0 && entry
->code
<= last_code
)
325 grub_error (GRUB_ERR_BAD_FONT
,
326 "font characters not in ascending order: %u <= %u",
327 entry
->code
, last_code
);
331 if (entry
->code
< 0x10000)
332 font
->bmp_idx
[entry
->code
] = i
;
334 last_code
= entry
->code
;
336 /* Read storage flags byte. */
337 if (grub_file_read (file
, &entry
->storage_flags
, 1) != 1)
340 /* Read glyph data offset; convert to native byte order. */
341 if (grub_file_read (file
, &entry
->offset
, 4) != 4)
343 entry
->offset
= grub_be_to_cpu32 (entry
->offset
);
345 /* No glyph loaded. Will be loaded on demand and cached thereafter. */
349 /* Print the 1st 10 characters. */
351 grub_dprintf ("font", "c=%d o=%d\n", entry
->code
, entry
->offset
);
358 /* Read the contents of the specified section as a string, which is
359 allocated on the heap. Returns 0 if there is an error. */
361 read_section_as_string (struct font_file_section
*section
)
366 str
= grub_malloc (section
->length
+ 1);
370 ret
= grub_file_read (section
->file
, str
, section
->length
);
371 if (ret
< 0 || ret
!= (grub_ssize_t
) section
->length
)
377 str
[section
->length
] = '\0';
381 /* Read the contents of the current section as a 16-bit integer value,
382 which is stored into *VALUE.
383 Returns 0 upon success, nonzero upon failure. */
385 read_section_as_short (struct font_file_section
*section
,
386 grub_int16_t
* value
)
388 grub_uint16_t raw_value
;
390 if (section
->length
!= 2)
392 grub_error (GRUB_ERR_BAD_FONT
,
393 "font file format error: section %c%c%c%c length "
394 "is %d but should be 2",
395 section
->name
[0], section
->name
[1],
396 section
->name
[2], section
->name
[3], section
->length
);
399 if (grub_file_read (section
->file
, &raw_value
, 2) != 2)
402 *value
= grub_be_to_cpu16 (raw_value
);
406 /* Load a font and add it to the beginning of the global font list.
407 Returns 0 upon success, nonzero upon failure. */
409 grub_font_load (const char *filename
)
411 grub_file_t file
= 0;
412 struct font_file_section section
;
414 grub_font_t font
= 0;
417 grub_dprintf ("font", "add_font(%s)\n", filename
);
420 if (filename
[0] == '(' || filename
[0] == '/' || filename
[0] == '+')
421 file
= grub_buffile_open (filename
, GRUB_FILE_TYPE_FONT
, 1024);
422 else if (grub_strncmp(filename
, "mem:", 4) == 0)
423 file
= grub_buffile_open (filename
, GRUB_FILE_TYPE_FONT
, 1024);
426 const char *prefix
= grub_env_get ("prefix");
427 char *fullname
, *ptr
;
430 grub_error (GRUB_ERR_FILE_NOT_FOUND
, N_("variable `%s' isn't set"),
434 fullname
= grub_malloc (grub_strlen (prefix
) + grub_strlen (filename
) + 1
435 + sizeof ("/fonts/") + sizeof (".pf2"));
438 ptr
= grub_stpcpy (fullname
, prefix
);
439 ptr
= grub_stpcpy (ptr
, "/fonts/");
440 ptr
= grub_stpcpy (ptr
, filename
);
441 ptr
= grub_stpcpy (ptr
, ".pf2");
443 file
= grub_buffile_open (fullname
, GRUB_FILE_TYPE_FONT
, 1024);
444 grub_free (fullname
);
450 grub_dprintf ("font", "file opened\n");
453 /* Read the FILE section. It indicates the file format. */
454 if (open_section (file
, §ion
) != 0)
458 grub_dprintf ("font", "opened FILE section\n");
460 if (grub_memcmp (section
.name
, FONT_FORMAT_SECTION_NAMES_FILE
,
461 sizeof (FONT_FORMAT_SECTION_NAMES_FILE
) - 1) != 0)
463 grub_error (GRUB_ERR_BAD_FONT
,
464 "font file format error: 1st section must be FILE");
469 grub_dprintf ("font", "section name ok\n");
471 if (section
.length
!= 4)
473 grub_error (GRUB_ERR_BAD_FONT
,
474 "font file format error (file type ID length is %d "
475 "but should be 4)", section
.length
);
480 grub_dprintf ("font", "section length ok\n");
482 /* Check the file format type code. */
483 if (grub_file_read (file
, magic
, 4) != 4)
487 grub_dprintf ("font", "read magic ok\n");
490 if (grub_memcmp (magic
, FONT_FORMAT_PFF2_MAGIC
, 4) != 0)
492 grub_error (GRUB_ERR_BAD_FONT
, "invalid font magic %x %x %x %x",
493 magic
[0], magic
[1], magic
[2], magic
[3]);
498 grub_dprintf ("font", "compare magic ok\n");
501 /* Allocate the font object. */
502 font
= (grub_font_t
) grub_zalloc (sizeof (struct grub_font
));
510 grub_dprintf ("font", "allocate font ok; loading font info\n");
513 /* Load the font information. */
516 if (open_section (file
, §ion
) != 0)
519 break; /* Done reading the font file. */
525 grub_dprintf ("font", "opened section %c%c%c%c ok\n",
526 section
.name
[0], section
.name
[1],
527 section
.name
[2], section
.name
[3]);
530 if (grub_memcmp (section
.name
, FONT_FORMAT_SECTION_NAMES_FONT_NAME
,
531 sizeof (FONT_FORMAT_SECTION_NAMES_FONT_NAME
) - 1) == 0)
533 font
->name
= read_section_as_string (§ion
);
537 else if (grub_memcmp (section
.name
,
538 FONT_FORMAT_SECTION_NAMES_POINT_SIZE
,
539 sizeof (FONT_FORMAT_SECTION_NAMES_POINT_SIZE
) -
542 if (read_section_as_short (§ion
, &font
->point_size
) != 0)
545 else if (grub_memcmp (section
.name
, FONT_FORMAT_SECTION_NAMES_WEIGHT
,
546 sizeof (FONT_FORMAT_SECTION_NAMES_WEIGHT
) - 1)
550 wt
= read_section_as_string (§ion
);
553 /* Convert the weight string 'normal' or 'bold' into a number. */
554 if (grub_strcmp (wt
, "normal") == 0)
555 font
->weight
= FONT_WEIGHT_NORMAL
;
556 else if (grub_strcmp (wt
, "bold") == 0)
557 font
->weight
= FONT_WEIGHT_BOLD
;
560 else if (grub_memcmp (section
.name
,
561 FONT_FORMAT_SECTION_NAMES_MAX_CHAR_WIDTH
,
562 sizeof (FONT_FORMAT_SECTION_NAMES_MAX_CHAR_WIDTH
)
565 if (read_section_as_short (§ion
, &font
->max_char_width
) != 0)
568 else if (grub_memcmp (section
.name
,
569 FONT_FORMAT_SECTION_NAMES_MAX_CHAR_HEIGHT
,
570 sizeof (FONT_FORMAT_SECTION_NAMES_MAX_CHAR_HEIGHT
)
573 if (read_section_as_short (§ion
, &font
->max_char_height
) != 0)
576 else if (grub_memcmp (section
.name
,
577 FONT_FORMAT_SECTION_NAMES_ASCENT
,
578 sizeof (FONT_FORMAT_SECTION_NAMES_ASCENT
) - 1)
581 if (read_section_as_short (§ion
, &font
->ascent
) != 0)
584 else if (grub_memcmp (section
.name
, FONT_FORMAT_SECTION_NAMES_DESCENT
,
585 sizeof (FONT_FORMAT_SECTION_NAMES_DESCENT
) - 1)
588 if (read_section_as_short (§ion
, &font
->descent
) != 0)
591 else if (grub_memcmp (section
.name
,
592 FONT_FORMAT_SECTION_NAMES_CHAR_INDEX
,
593 sizeof (FONT_FORMAT_SECTION_NAMES_CHAR_INDEX
) -
596 if (load_font_index (file
, section
.length
, font
) != 0)
599 else if (grub_memcmp (section
.name
, FONT_FORMAT_SECTION_NAMES_DATA
,
600 sizeof (FONT_FORMAT_SECTION_NAMES_DATA
) - 1) == 0)
602 /* When the DATA section marker is reached, we stop reading. */
607 /* Unhandled section type, simply skip past it. */
609 grub_dprintf ("font", "Unhandled section type, skipping.\n");
611 grub_off_t section_end
= grub_file_tell (file
) + section
.length
;
612 if ((int) grub_file_seek (file
, section_end
) == -1)
619 grub_dprintf ("font", "Font has no name.\n");
620 font
->name
= grub_strdup ("Unknown");
624 grub_dprintf ("font", "Loaded font `%s'.\n"
625 "Ascent=%d Descent=%d MaxW=%d MaxH=%d Number of characters=%d.\n",
627 font
->ascent
, font
->descent
,
628 font
->max_char_width
, font
->max_char_height
, font
->num_chars
);
631 if (font
->max_char_width
== 0
632 || font
->max_char_height
== 0
633 || font
->num_chars
== 0
634 || font
->char_index
== 0 || font
->ascent
== 0 || font
->descent
== 0)
636 grub_error (GRUB_ERR_BAD_FONT
,
637 "invalid font file: missing some required data");
641 /* Add the font to the global font registry. */
642 if (register_font (font
) != 0)
649 grub_file_close (file
);
657 /* Read a 16-bit big-endian integer from FILE, convert it to native byte
658 order, and store it in *VALUE.
659 Returns 0 on success, 1 on failure. */
661 read_be_uint16 (grub_file_t file
, grub_uint16_t
* value
)
663 if (grub_file_read (file
, value
, 2) != 2)
665 *value
= grub_be_to_cpu16 (*value
);
670 read_be_int16 (grub_file_t file
, grub_int16_t
* value
)
672 /* For the signed integer version, use the same code as for unsigned. */
673 return read_be_uint16 (file
, (grub_uint16_t
*) value
);
676 /* Return a pointer to the character index entry for the glyph corresponding to
677 the codepoint CODE in the font FONT. If not found, return zero. */
678 static inline struct char_index_entry
*
679 find_glyph (const grub_font_t font
, grub_uint32_t code
)
681 struct char_index_entry
*table
;
686 table
= font
->char_index
;
688 /* Use BMP index if possible. */
689 if (code
< 0x10000 && font
->bmp_idx
)
691 if (font
->bmp_idx
[code
] == 0xffff)
693 return &table
[font
->bmp_idx
[code
]];
696 /* Do a binary search in `char_index', which is ordered by code point. */
698 hi
= font
->num_chars
- 1;
705 mid
= lo
+ (hi
- lo
) / 2;
706 if (code
< table
[mid
].code
)
708 else if (code
> table
[mid
].code
)
717 /* Get a glyph for the Unicode character CODE in FONT. The glyph is loaded
718 from the font file if has not been loaded yet.
719 Returns a pointer to the glyph if found, or 0 if it is not found. */
720 static struct grub_font_glyph
*
721 grub_font_get_glyph_internal (grub_font_t font
, grub_uint32_t code
)
723 struct char_index_entry
*index_entry
;
725 index_entry
= find_glyph (font
, code
);
728 struct grub_font_glyph
*glyph
= 0;
730 grub_uint16_t height
;
736 if (index_entry
->glyph
)
737 /* Return cached glyph. */
738 return index_entry
->glyph
;
741 /* No open file, can't load any glyphs. */
744 /* Make sure we can find glyphs for error messages. Push active
745 error message to error stack and reset error message. */
748 grub_file_seek (font
->file
, index_entry
->offset
);
750 /* Read the glyph width, height, and baseline. */
751 if (read_be_uint16 (font
->file
, &width
) != 0
752 || read_be_uint16 (font
->file
, &height
) != 0
753 || read_be_int16 (font
->file
, &xoff
) != 0
754 || read_be_int16 (font
->file
, &yoff
) != 0
755 || read_be_int16 (font
->file
, &dwidth
) != 0)
761 len
= (width
* height
+ 7) / 8;
762 glyph
= grub_malloc (sizeof (struct grub_font_glyph
) + len
);
770 glyph
->width
= width
;
771 glyph
->height
= height
;
772 glyph
->offset_x
= xoff
;
773 glyph
->offset_y
= yoff
;
774 glyph
->device_width
= dwidth
;
776 /* Don't try to read empty bitmaps (e.g., space characters). */
779 if (grub_file_read (font
->file
, glyph
->bitmap
, len
) != len
)
787 /* Restore old error message. */
790 /* Cache the glyph. */
791 index_entry
->glyph
= glyph
;
799 /* Free the memory used by FONT.
800 This should not be called if the font has been made available to
801 users (once it is added to the global font list), since there would
802 be the possibility of a dangling pointer. */
804 free_font (grub_font_t font
)
809 grub_file_close (font
->file
);
810 grub_free (font
->name
);
811 grub_free (font
->family
);
812 grub_free (font
->char_index
);
813 grub_free (font
->bmp_idx
);
818 /* Add FONT to the global font registry.
819 Returns 0 upon success, nonzero on failure
820 (the font was not registered). */
822 register_font (grub_font_t font
)
824 struct grub_font_node
*node
= 0;
826 node
= grub_malloc (sizeof (struct grub_font_node
));
831 node
->next
= grub_font_list
;
832 grub_font_list
= node
;
837 /* Remove the font from the global font list. We don't actually free the
838 font's memory since users could be holding references to the font. */
840 remove_font (grub_font_t font
)
842 struct grub_font_node
**nextp
, *cur
;
844 for (nextp
= &grub_font_list
, cur
= *nextp
;
845 cur
; nextp
= &cur
->next
, cur
= cur
->next
)
847 if (cur
->value
== font
)
851 /* Free the node, but not the font itself. */
859 /* Get a font from the list of loaded fonts. This function will return
860 another font if the requested font is not available. If no fonts are
861 loaded, then a special 'null font' is returned, which contains no glyphs,
862 but is not a null pointer so the caller may omit checks for NULL. */
864 grub_font_get (const char *font_name
)
866 struct grub_font_node
*node
;
868 for (node
= grub_font_list
; node
; node
= node
->next
)
870 grub_font_t font
= node
->value
;
871 if (grub_strcmp (font
->name
, font_name
) == 0)
875 /* If no font by that name is found, return the first font in the list
877 if (grub_font_list
&& grub_font_list
->value
)
878 return grub_font_list
->value
;
880 /* The null_font is a last resort. */
884 /* Get the full name of the font. */
886 grub_font_get_name (grub_font_t font
)
891 /* Get the maximum width of any character in the font in pixels. */
893 grub_font_get_max_char_width (grub_font_t font
)
895 return font
->max_char_width
;
898 /* Get the distance in pixels from the baseline to the lowest descenders
899 (for instance, in a lowercase 'y', 'g', etc.). */
901 grub_font_get_descent (grub_font_t font
)
903 return font
->descent
;
906 /* FIXME: not correct for all fonts. */
908 grub_font_get_xheight (grub_font_t font
)
910 return font
->ascent
/ 2;
913 /* Get the *standard leading* of the font in pixel, which is the spacing
914 between two lines of text. Specifically, it is the space between the
915 descent of one line and the ascent of the next line. This is included
916 in the *height* metric. */
918 grub_font_get_leading (grub_font_t font
)
920 return font
->leading
;
923 /* Get the distance in pixels between baselines of adjacent lines of text. */
925 grub_font_get_height (grub_font_t font
)
927 return font
->ascent
+ font
->descent
+ font
->leading
;
930 /* Get the glyph for FONT corresponding to the Unicode code point CODE.
931 Returns the ASCII glyph for the code if no other fonts are available.
932 The glyphs are cached once loaded. */
933 struct grub_font_glyph
*
934 grub_font_get_glyph (grub_font_t font
, grub_uint32_t code
)
936 struct grub_font_glyph
*glyph
= 0;
938 glyph
= grub_font_get_glyph_internal (font
, code
);
941 glyph
= ascii_glyph_lookup (code
);
947 /* Calculate a subject value representing "how similar" two fonts are.
948 This is used to prioritize the order that fonts are scanned for missing
949 glyphs. The object is to select glyphs from the most similar font
950 possible, for the best appearance.
951 The heuristic is crude, but it helps greatly when fonts of similar
952 sizes are used so that tiny 8 point glyphs are not mixed into a string
953 of 24 point text unless there is no other choice. */
955 get_font_diversity (grub_font_t a
, grub_font_t b
)
961 if (a
->ascent
&& b
->ascent
)
962 d
+= grub_abs (a
->ascent
- b
->ascent
) * 8;
964 /* Penalty for missing attributes. */
967 if (a
->max_char_height
&& b
->max_char_height
)
968 d
+= grub_abs (a
->max_char_height
- b
->max_char_height
) * 8;
970 /* Penalty for missing attributes. */
973 /* Weight is a minor factor. */
974 d
+= (a
->weight
!= b
->weight
) ? 5 : 0;
979 /* Get a glyph corresponding to the codepoint CODE. If FONT contains the
980 specified glyph, then it is returned. Otherwise, all other loaded fonts
981 are searched until one is found that contains a glyph for CODE.
982 If no glyph is available for CODE in the loaded fonts, then a glyph
983 representing an unknown character is returned.
984 This function never returns NULL.
985 The returned glyph is owned by the font manager and should not be freed
986 by the caller. The glyphs are cached. */
987 struct grub_font_glyph
*
988 grub_font_get_glyph_with_fallback (grub_font_t font
, grub_uint32_t code
)
990 struct grub_font_glyph
*glyph
;
991 struct grub_font_node
*node
;
992 /* Keep track of next node, in case there's an I/O error in
993 grub_font_get_glyph_internal() and the font is removed from the list. */
994 struct grub_font_node
*next
;
995 /* Information on the best glyph found so far, to help find the glyph in
996 the best matching to the requested one. */
998 struct grub_font_glyph
*best_glyph
;
1002 /* First try to get the glyph from the specified font. */
1003 glyph
= grub_font_get_glyph_internal (font
, code
);
1008 /* Otherwise, search all loaded fonts for the glyph and use the one from
1009 the font that best matches the requested font. */
1010 best_diversity
= 10000;
1013 for (node
= grub_font_list
; node
; node
= next
)
1015 grub_font_t curfont
;
1017 curfont
= node
->value
;
1020 glyph
= grub_font_get_glyph_internal (curfont
, code
);
1027 d
= get_font_diversity (curfont
, font
);
1028 if (d
< best_diversity
)
1040 static struct grub_font_glyph
*
1041 grub_font_dup_glyph (struct grub_font_glyph
*glyph
)
1043 static struct grub_font_glyph
*ret
;
1044 ret
= grub_malloc (sizeof (*ret
) + (glyph
->width
* glyph
->height
+ 7) / 8);
1047 grub_memcpy (ret
, glyph
, sizeof (*ret
)
1048 + (glyph
->width
* glyph
->height
+ 7) / 8);
1053 /* FIXME: suboptimal. */
1055 grub_font_blit_glyph (struct grub_font_glyph
*target
,
1056 struct grub_font_glyph
*src
, unsigned dx
, unsigned dy
)
1058 unsigned src_bit
, tgt_bit
, src_byte
, tgt_byte
;
1060 for (i
= 0; i
< src
->height
; i
++)
1062 src_bit
= (src
->width
* i
) % 8;
1063 src_byte
= (src
->width
* i
) / 8;
1064 tgt_bit
= (target
->width
* (dy
+ i
) + dx
) % 8;
1065 tgt_byte
= (target
->width
* (dy
+ i
) + dx
) / 8;
1066 for (j
= 0; j
< src
->width
; j
++)
1068 target
->bitmap
[tgt_byte
] |= ((src
->bitmap
[src_byte
] << src_bit
)
1087 grub_font_blit_glyph_mirror (struct grub_font_glyph
*target
,
1088 struct grub_font_glyph
*src
,
1089 unsigned dx
, unsigned dy
)
1091 unsigned tgt_bit
, src_byte
, tgt_byte
;
1094 for (i
= 0; i
< src
->height
; i
++)
1096 src_bit
= (src
->width
* i
+ src
->width
- 1) % 8;
1097 src_byte
= (src
->width
* i
+ src
->width
- 1) / 8;
1098 tgt_bit
= (target
->width
* (dy
+ i
) + dx
) % 8;
1099 tgt_byte
= (target
->width
* (dy
+ i
) + dx
) / 8;
1100 for (j
= 0; j
< src
->width
; j
++)
1102 target
->bitmap
[tgt_byte
] |= ((src
->bitmap
[src_byte
] << src_bit
)
1120 /* Context for blit_comb. */
1121 struct blit_comb_ctx
1123 struct grub_font_glyph
*glyph
;
1125 struct grub_video_signed_rect bounds
;
1128 /* Helper for blit_comb. */
1130 do_blit (struct grub_font_glyph
*src
, signed dx
, signed dy
,
1131 struct blit_comb_ctx
*ctx
)
1134 grub_font_blit_glyph (ctx
->glyph
, src
, dx
- ctx
->glyph
->offset_x
,
1135 (ctx
->glyph
->height
+ ctx
->glyph
->offset_y
) + dy
);
1136 if (dx
< ctx
->bounds
.x
)
1138 ctx
->bounds
.width
+= ctx
->bounds
.x
- dx
;
1141 if (ctx
->bounds
.y
> -src
->height
- dy
)
1143 ctx
->bounds
.height
+= ctx
->bounds
.y
- (-src
->height
- dy
);
1144 ctx
->bounds
.y
= (-src
->height
- dy
);
1146 if (dx
+ src
->width
- ctx
->bounds
.x
>= (signed) ctx
->bounds
.width
)
1147 ctx
->bounds
.width
= dx
+ src
->width
- ctx
->bounds
.x
+ 1;
1148 if ((signed) ctx
->bounds
.height
< src
->height
+ (-src
->height
- dy
)
1150 ctx
->bounds
.height
= src
->height
+ (-src
->height
- dy
) - ctx
->bounds
.y
;
1153 /* Helper for blit_comb. */
1155 add_device_width (int val
, struct blit_comb_ctx
*ctx
)
1158 ctx
->glyph
->device_width
+= val
;
1159 if (ctx
->device_width
)
1160 *ctx
->device_width
+= val
;
1164 blit_comb (const struct grub_unicode_glyph
*glyph_id
,
1165 struct grub_font_glyph
*glyph
,
1166 struct grub_video_signed_rect
*bounds_out
,
1167 struct grub_font_glyph
*main_glyph
,
1168 struct grub_font_glyph
**combining_glyphs
, int *device_width
)
1170 struct blit_comb_ctx ctx
= {
1172 .device_width
= device_width
1175 signed above_rightx
, above_righty
;
1176 signed above_leftx
, above_lefty
;
1177 signed below_rightx
, below_righty
;
1178 signed min_devwidth
= 0;
1179 const struct grub_unicode_combining
*comb
;
1182 glyph
->device_width
= main_glyph
->device_width
;
1184 *device_width
= main_glyph
->device_width
;
1186 ctx
.bounds
.x
= main_glyph
->offset_x
;
1187 ctx
.bounds
.y
= main_glyph
->offset_y
;
1188 ctx
.bounds
.width
= main_glyph
->width
;
1189 ctx
.bounds
.height
= main_glyph
->height
;
1191 above_rightx
= main_glyph
->offset_x
+ main_glyph
->width
;
1192 above_righty
= ctx
.bounds
.y
+ ctx
.bounds
.height
;
1194 above_leftx
= main_glyph
->offset_x
;
1195 above_lefty
= ctx
.bounds
.y
+ ctx
.bounds
.height
;
1197 below_rightx
= ctx
.bounds
.x
+ ctx
.bounds
.width
;
1198 below_righty
= ctx
.bounds
.y
;
1200 comb
= grub_unicode_get_comb (glyph_id
);
1202 for (i
= 0; i
< glyph_id
->ncomb
; i
++)
1204 grub_int16_t space
= 0;
1205 /* Center by default. */
1206 grub_int16_t targetx
;
1208 if (!combining_glyphs
[i
])
1210 targetx
= (ctx
.bounds
.width
- combining_glyphs
[i
]->width
) / 2 + ctx
.bounds
.x
;
1211 /* CGJ is to avoid diacritics reordering. */
1213 == GRUB_UNICODE_COMBINING_GRAPHEME_JOINER
)
1215 switch (comb
[i
].type
)
1217 case GRUB_UNICODE_COMB_OVERLAY
:
1218 do_blit (combining_glyphs
[i
],
1220 (ctx
.bounds
.height
- combining_glyphs
[i
]->height
) / 2
1221 - (ctx
.bounds
.height
+ ctx
.bounds
.y
), &ctx
);
1222 if (min_devwidth
< combining_glyphs
[i
]->width
)
1223 min_devwidth
= combining_glyphs
[i
]->width
;
1226 case GRUB_UNICODE_COMB_ATTACHED_ABOVE_RIGHT
:
1227 do_blit (combining_glyphs
[i
], above_rightx
, -above_righty
, &ctx
);
1228 above_rightx
+= combining_glyphs
[i
]->width
;
1231 case GRUB_UNICODE_COMB_ABOVE_RIGHT
:
1232 do_blit (combining_glyphs
[i
], above_rightx
,
1233 -(above_righty
+ combining_glyphs
[i
]->height
), &ctx
);
1234 above_rightx
+= combining_glyphs
[i
]->width
;
1237 case GRUB_UNICODE_COMB_ABOVE_LEFT
:
1238 above_leftx
-= combining_glyphs
[i
]->width
;
1239 do_blit (combining_glyphs
[i
], above_leftx
,
1240 -(above_lefty
+ combining_glyphs
[i
]->height
), &ctx
);
1243 case GRUB_UNICODE_COMB_BELOW_RIGHT
:
1244 do_blit (combining_glyphs
[i
], below_rightx
, below_righty
, &ctx
);
1245 below_rightx
+= combining_glyphs
[i
]->width
;
1248 case GRUB_UNICODE_COMB_HEBREW_HOLAM
:
1249 if (glyph_id
->base
!= GRUB_UNICODE_HEBREW_WAW
)
1251 main_glyph
->offset_x
- combining_glyphs
[i
]->width
-
1252 (combining_glyphs
[i
]->width
+ 3) / 4;
1255 case GRUB_UNICODE_COMB_HEBREW_SIN_DOT
:
1256 targetx
= main_glyph
->offset_x
+ combining_glyphs
[i
]->width
/ 4;
1259 case GRUB_UNICODE_COMB_HEBREW_SHIN_DOT
:
1261 main_glyph
->width
+ main_glyph
->offset_x
-
1262 combining_glyphs
[i
]->width
;
1264 space
= combining_glyphs
[i
]->offset_y
1265 - grub_font_get_xheight (combining_glyphs
[i
]->font
) - 1;
1267 space
= 1 + (grub_font_get_xheight (main_glyph
->font
)) / 8;
1268 do_blit (combining_glyphs
[i
], targetx
,
1269 -(main_glyph
->height
+ main_glyph
->offset_y
+ space
1270 + combining_glyphs
[i
]->height
), &ctx
);
1271 if (min_devwidth
< combining_glyphs
[i
]->width
)
1272 min_devwidth
= combining_glyphs
[i
]->width
;
1275 /* TODO: Put dammah, fathah and alif nearer to shadda. */
1276 case GRUB_UNICODE_COMB_SYRIAC_SUPERSCRIPT_ALAPH
:
1277 case GRUB_UNICODE_COMB_ARABIC_DAMMAH
:
1278 case GRUB_UNICODE_COMB_ARABIC_DAMMATAN
:
1279 case GRUB_UNICODE_COMB_ARABIC_FATHATAN
:
1280 case GRUB_UNICODE_COMB_ARABIC_FATHAH
:
1281 case GRUB_UNICODE_COMB_ARABIC_SUPERSCRIPT_ALIF
:
1282 case GRUB_UNICODE_COMB_ARABIC_SUKUN
:
1283 case GRUB_UNICODE_COMB_ARABIC_SHADDA
:
1284 case GRUB_UNICODE_COMB_HEBREW_RAFE
:
1285 case GRUB_UNICODE_STACK_ABOVE
:
1287 space
= combining_glyphs
[i
]->offset_y
1288 - grub_font_get_xheight (combining_glyphs
[i
]->font
) - 1;
1290 space
= 1 + (grub_font_get_xheight (main_glyph
->font
)) / 8;
1292 case GRUB_UNICODE_STACK_ATTACHED_ABOVE
:
1293 do_blit (combining_glyphs
[i
], targetx
,
1294 -(ctx
.bounds
.height
+ ctx
.bounds
.y
+ space
1295 + combining_glyphs
[i
]->height
), &ctx
);
1296 if (min_devwidth
< combining_glyphs
[i
]->width
)
1297 min_devwidth
= combining_glyphs
[i
]->width
;
1300 case GRUB_UNICODE_COMB_HEBREW_DAGESH
:
1301 do_blit (combining_glyphs
[i
], targetx
,
1302 -(ctx
.bounds
.height
/ 2 + ctx
.bounds
.y
1303 + combining_glyphs
[i
]->height
/ 2), &ctx
);
1304 if (min_devwidth
< combining_glyphs
[i
]->width
)
1305 min_devwidth
= combining_glyphs
[i
]->width
;
1308 case GRUB_UNICODE_COMB_HEBREW_SHEVA
:
1309 case GRUB_UNICODE_COMB_HEBREW_HIRIQ
:
1310 case GRUB_UNICODE_COMB_HEBREW_QAMATS
:
1311 case GRUB_UNICODE_COMB_HEBREW_TSERE
:
1312 case GRUB_UNICODE_COMB_HEBREW_SEGOL
:
1313 /* TODO: placement in final kaf and under reish. */
1315 case GRUB_UNICODE_COMB_HEBREW_HATAF_SEGOL
:
1316 case GRUB_UNICODE_COMB_HEBREW_HATAF_PATAH
:
1317 case GRUB_UNICODE_COMB_HEBREW_HATAF_QAMATS
:
1318 case GRUB_UNICODE_COMB_HEBREW_PATAH
:
1319 case GRUB_UNICODE_COMB_HEBREW_QUBUTS
:
1320 case GRUB_UNICODE_COMB_HEBREW_METEG
:
1321 /* TODO: Put kasra and kasratan under shadda. */
1322 case GRUB_UNICODE_COMB_ARABIC_KASRA
:
1323 case GRUB_UNICODE_COMB_ARABIC_KASRATAN
:
1324 /* I don't know how ypogegrammeni differs from subscript. */
1325 case GRUB_UNICODE_COMB_YPOGEGRAMMENI
:
1326 case GRUB_UNICODE_STACK_BELOW
:
1328 space
= -(combining_glyphs
[i
]->offset_y
1329 + combining_glyphs
[i
]->height
);
1331 space
= 1 + (grub_font_get_xheight (main_glyph
->font
)) / 8;
1334 case GRUB_UNICODE_STACK_ATTACHED_BELOW
:
1335 do_blit (combining_glyphs
[i
], targetx
, -(ctx
.bounds
.y
- space
),
1337 if (min_devwidth
< combining_glyphs
[i
]->width
)
1338 min_devwidth
= combining_glyphs
[i
]->width
;
1341 case GRUB_UNICODE_COMB_MN
:
1342 switch (comb
[i
].code
)
1344 case GRUB_UNICODE_THAANA_ABAFILI
:
1345 case GRUB_UNICODE_THAANA_AABAAFILI
:
1346 case GRUB_UNICODE_THAANA_UBUFILI
:
1347 case GRUB_UNICODE_THAANA_OOBOOFILI
:
1348 case GRUB_UNICODE_THAANA_EBEFILI
:
1349 case GRUB_UNICODE_THAANA_EYBEYFILI
:
1350 case GRUB_UNICODE_THAANA_OBOFILI
:
1351 case GRUB_UNICODE_THAANA_OABOAFILI
:
1352 case GRUB_UNICODE_THAANA_SUKUN
:
1354 case GRUB_UNICODE_THAANA_IBIFILI
:
1355 case GRUB_UNICODE_THAANA_EEBEEFILI
:
1361 /* Default handling. Just draw combining character on top
1363 FIXME: support more unicode types correctly.
1365 do_blit (combining_glyphs
[i
],
1366 main_glyph
->device_width
1367 + combining_glyphs
[i
]->offset_x
,
1368 -(combining_glyphs
[i
]->height
1369 + combining_glyphs
[i
]->offset_y
), &ctx
);
1370 add_device_width (combining_glyphs
[i
]->device_width
, &ctx
);
1374 add_device_width ((above_rightx
>
1375 below_rightx
? above_rightx
: below_rightx
) -
1376 (main_glyph
->offset_x
+ main_glyph
->width
), &ctx
);
1377 add_device_width (above_leftx
- main_glyph
->offset_x
, &ctx
);
1378 if (glyph
&& glyph
->device_width
< min_devwidth
)
1379 glyph
->device_width
= min_devwidth
;
1380 if (device_width
&& *device_width
< min_devwidth
)
1381 *device_width
= min_devwidth
;
1384 *bounds_out
= ctx
.bounds
;
1387 static struct grub_font_glyph
*
1388 grub_font_construct_dry_run (grub_font_t hinted_font
,
1389 const struct grub_unicode_glyph
*glyph_id
,
1390 struct grub_video_signed_rect
*bounds
,
1391 struct grub_font_glyph
**combining_glyphs
,
1394 struct grub_font_glyph
*main_glyph
= NULL
;
1395 grub_uint32_t desired_attributes
= 0;
1397 grub_uint32_t base
= glyph_id
->base
;
1398 const struct grub_unicode_combining
*comb
;
1400 if (glyph_id
->attributes
& GRUB_UNICODE_GLYPH_ATTRIBUTE_RIGHT_JOINED
)
1401 desired_attributes
|= GRUB_FONT_CODE_RIGHT_JOINED
;
1403 if (glyph_id
->attributes
& GRUB_UNICODE_GLYPH_ATTRIBUTE_LEFT_JOINED
)
1404 desired_attributes
|= GRUB_FONT_CODE_LEFT_JOINED
;
1406 comb
= grub_unicode_get_comb (glyph_id
);
1408 if (base
== 'i' || base
== 'j')
1410 for (i
= 0; i
< glyph_id
->ncomb
; i
++)
1411 if (comb
[i
].type
== GRUB_UNICODE_STACK_ABOVE
)
1413 if (i
< glyph_id
->ncomb
&& base
== 'i')
1414 base
= GRUB_UNICODE_DOTLESS_LOWERCASE_I
;
1415 if (i
< glyph_id
->ncomb
&& base
== 'j')
1416 base
= GRUB_UNICODE_DOTLESS_LOWERCASE_J
;
1419 main_glyph
= grub_font_get_glyph_with_fallback (hinted_font
, base
1420 | desired_attributes
);
1423 main_glyph
= grub_font_get_glyph_with_fallback (hinted_font
,
1426 /* Glyph not available in any font. Use ASCII fallback. */
1428 main_glyph
= ascii_glyph_lookup (base
);
1430 /* Glyph not available in any font. Return unknown glyph. */
1435 *device_width
= main_glyph
->device_width
;
1437 if (!glyph_id
->ncomb
&& !glyph_id
->attributes
)
1440 if (glyph_id
->ncomb
&& !combining_glyphs
)
1442 grub_errno
= GRUB_ERR_NONE
;
1446 for (i
= 0; i
< glyph_id
->ncomb
; i
++)
1448 = grub_font_get_glyph_with_fallback (main_glyph
->font
,
1451 blit_comb (glyph_id
, NULL
, bounds
, main_glyph
, combining_glyphs
,
1457 static struct grub_font_glyph
**render_combining_glyphs
= 0;
1458 static grub_size_t render_max_comb_glyphs
= 0;
1461 ensure_comb_space (const struct grub_unicode_glyph
*glyph_id
)
1463 if (glyph_id
->ncomb
<= render_max_comb_glyphs
)
1466 render_max_comb_glyphs
= 2 * glyph_id
->ncomb
;
1467 if (render_max_comb_glyphs
< 8)
1468 render_max_comb_glyphs
= 8;
1469 grub_free (render_combining_glyphs
);
1470 render_combining_glyphs
= grub_malloc (render_max_comb_glyphs
1471 * sizeof (render_combining_glyphs
[0]));
1472 if (!render_combining_glyphs
)
1477 grub_font_get_constructed_device_width (grub_font_t hinted_font
,
1478 const struct grub_unicode_glyph
1482 struct grub_font_glyph
*main_glyph
;
1484 ensure_comb_space (glyph_id
);
1486 main_glyph
= grub_font_construct_dry_run (hinted_font
, glyph_id
, NULL
,
1487 render_combining_glyphs
, &ret
);
1489 return unknown_glyph
->device_width
;
1493 struct grub_font_glyph
*
1494 grub_font_construct_glyph (grub_font_t hinted_font
,
1495 const struct grub_unicode_glyph
*glyph_id
)
1497 struct grub_font_glyph
*main_glyph
;
1498 struct grub_video_signed_rect bounds
;
1499 static struct grub_font_glyph
*glyph
= 0;
1500 static grub_size_t max_glyph_size
= 0;
1502 ensure_comb_space (glyph_id
);
1504 main_glyph
= grub_font_construct_dry_run (hinted_font
, glyph_id
,
1505 &bounds
, render_combining_glyphs
,
1509 return unknown_glyph
;
1511 if (!render_combining_glyphs
&& glyph_id
->ncomb
)
1514 if (!glyph_id
->ncomb
&& !glyph_id
->attributes
)
1517 if (max_glyph_size
< sizeof (*glyph
) + (bounds
.width
* bounds
.height
+ GRUB_CHAR_BIT
- 1) / GRUB_CHAR_BIT
)
1520 max_glyph_size
= (sizeof (*glyph
) + (bounds
.width
* bounds
.height
+ GRUB_CHAR_BIT
- 1) / GRUB_CHAR_BIT
) * 2;
1521 if (max_glyph_size
< 8)
1523 glyph
= grub_malloc (max_glyph_size
);
1527 grub_errno
= GRUB_ERR_NONE
;
1531 grub_memset (glyph
, 0, sizeof (*glyph
)
1532 + (bounds
.width
* bounds
.height
1533 + GRUB_CHAR_BIT
- 1) / GRUB_CHAR_BIT
);
1535 glyph
->font
= main_glyph
->font
;
1536 glyph
->width
= bounds
.width
;
1537 glyph
->height
= bounds
.height
;
1538 glyph
->offset_x
= bounds
.x
;
1539 glyph
->offset_y
= bounds
.y
;
1541 if (glyph_id
->attributes
& GRUB_UNICODE_GLYPH_ATTRIBUTE_MIRROR
)
1542 grub_font_blit_glyph_mirror (glyph
, main_glyph
,
1543 main_glyph
->offset_x
- glyph
->offset_x
,
1544 (glyph
->height
+ glyph
->offset_y
)
1545 - (main_glyph
->height
+
1546 main_glyph
->offset_y
));
1548 grub_font_blit_glyph (glyph
, main_glyph
,
1549 main_glyph
->offset_x
- glyph
->offset_x
,
1550 (glyph
->height
+ glyph
->offset_y
)
1551 - (main_glyph
->height
+ main_glyph
->offset_y
));
1553 blit_comb (glyph_id
, glyph
, NULL
, main_glyph
, render_combining_glyphs
, NULL
);
1558 /* Draw the specified glyph at (x, y). The y coordinate designates the
1559 baseline of the character, while the x coordinate designates the left
1560 side location of the character. */
1562 grub_font_draw_glyph (struct grub_font_glyph
* glyph
,
1563 grub_video_color_t color
, int left_x
, int baseline_y
)
1565 struct grub_video_bitmap glyph_bitmap
;
1567 /* Don't try to draw empty glyphs (U+0020, etc.). */
1568 if (glyph
->width
== 0 || glyph
->height
== 0)
1569 return GRUB_ERR_NONE
;
1571 glyph_bitmap
.mode_info
.width
= glyph
->width
;
1572 glyph_bitmap
.mode_info
.height
= glyph
->height
;
1573 glyph_bitmap
.mode_info
.mode_type
1574 = (1 << GRUB_VIDEO_MODE_TYPE_DEPTH_POS
) | GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP
;
1575 glyph_bitmap
.mode_info
.blit_format
= GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED
;
1576 glyph_bitmap
.mode_info
.bpp
= 1;
1578 /* Really 1 bit per pixel. */
1579 glyph_bitmap
.mode_info
.bytes_per_pixel
= 0;
1581 /* Packed densely as bits. */
1582 glyph_bitmap
.mode_info
.pitch
= glyph
->width
;
1584 glyph_bitmap
.mode_info
.number_of_colors
= 2;
1585 glyph_bitmap
.mode_info
.bg_red
= 0;
1586 glyph_bitmap
.mode_info
.bg_green
= 0;
1587 glyph_bitmap
.mode_info
.bg_blue
= 0;
1588 glyph_bitmap
.mode_info
.bg_alpha
= 0;
1589 grub_video_unmap_color (color
,
1590 &glyph_bitmap
.mode_info
.fg_red
,
1591 &glyph_bitmap
.mode_info
.fg_green
,
1592 &glyph_bitmap
.mode_info
.fg_blue
,
1593 &glyph_bitmap
.mode_info
.fg_alpha
);
1594 glyph_bitmap
.data
= glyph
->bitmap
;
1596 int bitmap_left
= left_x
+ glyph
->offset_x
;
1597 int bitmap_bottom
= baseline_y
- glyph
->offset_y
;
1598 int bitmap_top
= bitmap_bottom
- glyph
->height
;
1600 return grub_video_blit_bitmap (&glyph_bitmap
, GRUB_VIDEO_BLIT_BLEND
,
1601 bitmap_left
, bitmap_top
,
1602 0, 0, glyph
->width
, glyph
->height
);