Indentation fix, cleanup.
[AROS.git] / arch / all-pc / boot / grub2-aros / grub-core / font / font.c
blob53d76a64d042f2dab0d8584723cfc90e91d8245c
1 /* font.c - Font API and font file loader. */
2 /*
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>
21 #include <grub/dl.h>
22 #include <grub/file.h>
23 #include <grub/font.h>
24 #include <grub/misc.h>
25 #include <grub/mm.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>
32 #include <grub/env.h>
34 GRUB_MOD_LICENSE ("GPLv3+");
36 #if HAVE_FONT_SOURCE
37 #include "ascii.h"
38 #endif
40 #ifndef FONT_DEBUG
41 #define FONT_DEBUG 0
42 #endif
44 struct char_index_entry
46 grub_uint32_t code;
47 grub_uint8_t storage_flags;
48 grub_uint32_t offset;
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. */
69 grub_file_t file;
71 /* FOURCC name of the section. */
72 char name[4];
74 /* Length of the section contents. */
75 grub_uint32_t length;
77 /* Set by open_section() on EOF. */
78 int eof;
81 /* Replace unknown glyphs with a rounded question mark. */
82 static grub_uint8_t unknown_glyph_bitmap[] = {
83 /* 76543210 */
84 0x7C, /* ooooo */
85 0x82, /* o o */
86 0xBA, /* o ooo o */
87 0xAA, /* o o o o */
88 0xAA, /* o o o o */
89 0x8A, /* o o o */
90 0x9A, /* o oo o */
91 0x92, /* o o o */
92 0x92, /* o o o */
93 0x92, /* o o o */
94 0x92, /* o o o */
95 0x82, /* o o */
96 0x92, /* o o o */
97 0x82, /* o o */
98 0x7C, /* ooooo */
99 0x00 /* */
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;
113 #if HAVE_FONT_SOURCE
114 static struct grub_font_glyph *ascii_font_glyph[0x80];
115 #endif
117 static struct grub_font_glyph *
118 ascii_glyph_lookup (grub_uint32_t code)
120 #if HAVE_FONT_SOURCE
121 static int ascii_failback_initialized = 0;
123 if (code >= 0x80)
124 return NULL;
126 if (ascii_failback_initialized == 0)
128 int current;
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],
143 ASCII_BITMAP_SIZE);
146 ascii_failback_initialized = 1;
149 return ascii_font_glyph[code];
150 #else
151 (void) code;
152 return NULL;
153 #endif
156 void
157 grub_font_loader_init (void)
159 /* Only initialize font loader once. */
160 if (font_loader_initialized)
161 return;
163 /* Make glyph for unknown glyph. */
164 unknown_glyph = grub_malloc (sizeof (struct grub_font_glyph)
165 + sizeof (unknown_glyph_bitmap));
166 if (!unknown_glyph)
167 return;
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. */
190 static void
191 font_init (grub_font_t font)
193 font->name = 0;
194 font->file = 0;
195 font->family = 0;
196 font->point_size = 0;
197 font->weight = 0;
199 /* Default leading value, not in font file yet. */
200 font->leading = 1;
202 font->max_char_width = 0;
203 font->max_char_height = 0;
204 font->ascent = 0;
205 font->descent = 0;
206 font->num_chars = 0;
207 font->char_index = 0;
208 font->bmp_idx = 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. */
219 static int
220 open_section (grub_file_t file, struct font_file_section *section)
222 grub_ssize_t retval;
223 grub_uint32_t raw_length;
225 section->file = file;
226 section->eof = 0;
228 /* Read the FOURCC section name. */
229 retval = grub_file_read (file, section->name, 4);
230 if (retval >= 0 && retval < 4)
232 /* EOF encountered. */
233 section->eof = 1;
234 return 1;
236 else if (retval < 0)
238 /* Read error. */
239 return 1;
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. */
247 section->eof = 1;
248 return 1;
250 else if (retval < 0)
252 /* Read error. */
253 return 1;
256 /* Convert byte-order and store in *length. */
257 section->length = grub_be_to_cpu32 (raw_length);
259 return 0;
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). */
271 static int
272 load_font_index (grub_file_t file, grub_uint32_t sect_length, struct
273 grub_font *font)
275 unsigned i;
276 grub_uint32_t last_code;
278 #if FONT_DEBUG >= 2
279 grub_dprintf ("font", "load_font_index(sect_length=%d)\n", sect_length);
280 #endif
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);
289 return 1;
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)
299 return 1;
300 font->bmp_idx = grub_malloc (0x10000 * sizeof (grub_uint16_t));
301 if (!font->bmp_idx)
302 return 1;
303 grub_memset (font->bmp_idx, 0xff, 0x10000 * sizeof (grub_uint16_t));
306 #if FONT_DEBUG >= 2
307 grub_dprintf ("font", "num_chars=%d)\n", font->num_chars);
308 #endif
310 last_code = 0;
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)
319 return 1;
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);
328 return 1;
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)
338 return 1;
340 /* Read glyph data offset; convert to native byte order. */
341 if (grub_file_read (file, &entry->offset, 4) != 4)
342 return 1;
343 entry->offset = grub_be_to_cpu32 (entry->offset);
345 /* No glyph loaded. Will be loaded on demand and cached thereafter. */
346 entry->glyph = 0;
348 #if FONT_DEBUG >= 5
349 /* Print the 1st 10 characters. */
350 if (i < 10)
351 grub_dprintf ("font", "c=%d o=%d\n", entry->code, entry->offset);
352 #endif
355 return 0;
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. */
360 static char *
361 read_section_as_string (struct font_file_section *section)
363 char *str;
364 grub_ssize_t ret;
366 str = grub_malloc (section->length + 1);
367 if (!str)
368 return 0;
370 ret = grub_file_read (section->file, str, section->length);
371 if (ret < 0 || ret != (grub_ssize_t) section->length)
373 grub_free (str);
374 return 0;
377 str[section->length] = '\0';
378 return str;
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. */
384 static int
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);
397 return 1;
399 if (grub_file_read (section->file, &raw_value, 2) != 2)
400 return 1;
402 *value = grub_be_to_cpu16 (raw_value);
403 return 0;
406 /* Load a font and add it to the beginning of the global font list.
407 Returns 0 upon success, nonzero upon failure. */
408 grub_font_t
409 grub_font_load (const char *filename)
411 grub_file_t file = 0;
412 struct font_file_section section;
413 char magic[4];
414 grub_font_t font = 0;
416 #if FONT_DEBUG >= 1
417 grub_dprintf ("font", "add_font(%s)\n", filename);
418 #endif
420 if (filename[0] == '(' || filename[0] == '/' || filename[0] == '+')
421 file = grub_buffile_open (filename, 1024);
422 else
424 const char *prefix = grub_env_get ("prefix");
425 char *fullname, *ptr;
426 if (!prefix)
428 grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't set"),
429 "prefix");
430 goto fail;
432 fullname = grub_malloc (grub_strlen (prefix) + grub_strlen (filename) + 1
433 + sizeof ("/fonts/") + sizeof (".pf2"));
434 if (!fullname)
435 goto fail;
436 ptr = grub_stpcpy (fullname, prefix);
437 ptr = grub_stpcpy (ptr, "/fonts/");
438 ptr = grub_stpcpy (ptr, filename);
439 ptr = grub_stpcpy (ptr, ".pf2");
440 *ptr = 0;
441 file = grub_buffile_open (fullname, 1024);
442 grub_free (fullname);
444 if (!file)
445 goto fail;
447 #if FONT_DEBUG >= 3
448 grub_dprintf ("font", "file opened\n");
449 #endif
451 /* Read the FILE section. It indicates the file format. */
452 if (open_section (file, &section) != 0)
453 goto fail;
455 #if FONT_DEBUG >= 3
456 grub_dprintf ("font", "opened FILE section\n");
457 #endif
458 if (grub_memcmp (section.name, FONT_FORMAT_SECTION_NAMES_FILE,
459 sizeof (FONT_FORMAT_SECTION_NAMES_FILE) - 1) != 0)
461 grub_error (GRUB_ERR_BAD_FONT,
462 "font file format error: 1st section must be FILE");
463 goto fail;
466 #if FONT_DEBUG >= 3
467 grub_dprintf ("font", "section name ok\n");
468 #endif
469 if (section.length != 4)
471 grub_error (GRUB_ERR_BAD_FONT,
472 "font file format error (file type ID length is %d "
473 "but should be 4)", section.length);
474 goto fail;
477 #if FONT_DEBUG >= 3
478 grub_dprintf ("font", "section length ok\n");
479 #endif
480 /* Check the file format type code. */
481 if (grub_file_read (file, magic, 4) != 4)
482 goto fail;
484 #if FONT_DEBUG >= 3
485 grub_dprintf ("font", "read magic ok\n");
486 #endif
488 if (grub_memcmp (magic, FONT_FORMAT_PFF2_MAGIC, 4) != 0)
490 grub_error (GRUB_ERR_BAD_FONT, "invalid font magic %x %x %x %x",
491 magic[0], magic[1], magic[2], magic[3]);
492 goto fail;
495 #if FONT_DEBUG >= 3
496 grub_dprintf ("font", "compare magic ok\n");
497 #endif
499 /* Allocate the font object. */
500 font = (grub_font_t) grub_zalloc (sizeof (struct grub_font));
501 if (!font)
502 goto fail;
504 font_init (font);
505 font->file = file;
507 #if FONT_DEBUG >= 3
508 grub_dprintf ("font", "allocate font ok; loading font info\n");
509 #endif
511 /* Load the font information. */
512 while (1)
514 if (open_section (file, &section) != 0)
516 if (section.eof)
517 break; /* Done reading the font file. */
518 else
519 goto fail;
522 #if FONT_DEBUG >= 2
523 grub_dprintf ("font", "opened section %c%c%c%c ok\n",
524 section.name[0], section.name[1],
525 section.name[2], section.name[3]);
526 #endif
528 if (grub_memcmp (section.name, FONT_FORMAT_SECTION_NAMES_FONT_NAME,
529 sizeof (FONT_FORMAT_SECTION_NAMES_FONT_NAME) - 1) == 0)
531 font->name = read_section_as_string (&section);
532 if (!font->name)
533 goto fail;
535 else if (grub_memcmp (section.name,
536 FONT_FORMAT_SECTION_NAMES_POINT_SIZE,
537 sizeof (FONT_FORMAT_SECTION_NAMES_POINT_SIZE) -
538 1) == 0)
540 if (read_section_as_short (&section, &font->point_size) != 0)
541 goto fail;
543 else if (grub_memcmp (section.name, FONT_FORMAT_SECTION_NAMES_WEIGHT,
544 sizeof (FONT_FORMAT_SECTION_NAMES_WEIGHT) - 1)
545 == 0)
547 char *wt;
548 wt = read_section_as_string (&section);
549 if (!wt)
550 continue;
551 /* Convert the weight string 'normal' or 'bold' into a number. */
552 if (grub_strcmp (wt, "normal") == 0)
553 font->weight = FONT_WEIGHT_NORMAL;
554 else if (grub_strcmp (wt, "bold") == 0)
555 font->weight = FONT_WEIGHT_BOLD;
556 grub_free (wt);
558 else if (grub_memcmp (section.name,
559 FONT_FORMAT_SECTION_NAMES_MAX_CHAR_WIDTH,
560 sizeof (FONT_FORMAT_SECTION_NAMES_MAX_CHAR_WIDTH)
561 - 1) == 0)
563 if (read_section_as_short (&section, &font->max_char_width) != 0)
564 goto fail;
566 else if (grub_memcmp (section.name,
567 FONT_FORMAT_SECTION_NAMES_MAX_CHAR_HEIGHT,
568 sizeof (FONT_FORMAT_SECTION_NAMES_MAX_CHAR_HEIGHT)
569 - 1) == 0)
571 if (read_section_as_short (&section, &font->max_char_height) != 0)
572 goto fail;
574 else if (grub_memcmp (section.name,
575 FONT_FORMAT_SECTION_NAMES_ASCENT,
576 sizeof (FONT_FORMAT_SECTION_NAMES_ASCENT) - 1)
577 == 0)
579 if (read_section_as_short (&section, &font->ascent) != 0)
580 goto fail;
582 else if (grub_memcmp (section.name, FONT_FORMAT_SECTION_NAMES_DESCENT,
583 sizeof (FONT_FORMAT_SECTION_NAMES_DESCENT) - 1)
584 == 0)
586 if (read_section_as_short (&section, &font->descent) != 0)
587 goto fail;
589 else if (grub_memcmp (section.name,
590 FONT_FORMAT_SECTION_NAMES_CHAR_INDEX,
591 sizeof (FONT_FORMAT_SECTION_NAMES_CHAR_INDEX) -
592 1) == 0)
594 if (load_font_index (file, section.length, font) != 0)
595 goto fail;
597 else if (grub_memcmp (section.name, FONT_FORMAT_SECTION_NAMES_DATA,
598 sizeof (FONT_FORMAT_SECTION_NAMES_DATA) - 1) == 0)
600 /* When the DATA section marker is reached, we stop reading. */
601 break;
603 else
605 /* Unhandled section type, simply skip past it. */
606 #if FONT_DEBUG >= 3
607 grub_dprintf ("font", "Unhandled section type, skipping.\n");
608 #endif
609 grub_off_t section_end = grub_file_tell (file) + section.length;
610 if ((int) grub_file_seek (file, section_end) == -1)
611 goto fail;
615 if (!font->name)
617 grub_dprintf ("font", "Font has no name.\n");
618 font->name = grub_strdup ("Unknown");
621 #if FONT_DEBUG >= 1
622 grub_dprintf ("font", "Loaded font `%s'.\n"
623 "Ascent=%d Descent=%d MaxW=%d MaxH=%d Number of characters=%d.\n",
624 font->name,
625 font->ascent, font->descent,
626 font->max_char_width, font->max_char_height, font->num_chars);
627 #endif
629 if (font->max_char_width == 0
630 || font->max_char_height == 0
631 || font->num_chars == 0
632 || font->char_index == 0 || font->ascent == 0 || font->descent == 0)
634 grub_error (GRUB_ERR_BAD_FONT,
635 "invalid font file: missing some required data");
636 goto fail;
639 /* Add the font to the global font registry. */
640 if (register_font (font) != 0)
641 goto fail;
643 return font;
645 fail:
646 if (file)
647 grub_file_close (file);
648 if (font)
649 font->file = 0;
651 free_font (font);
652 return 0;
655 /* Read a 16-bit big-endian integer from FILE, convert it to native byte
656 order, and store it in *VALUE.
657 Returns 0 on success, 1 on failure. */
658 static int
659 read_be_uint16 (grub_file_t file, grub_uint16_t * value)
661 if (grub_file_read (file, value, 2) != 2)
662 return 1;
663 *value = grub_be_to_cpu16 (*value);
664 return 0;
667 static int
668 read_be_int16 (grub_file_t file, grub_int16_t * value)
670 /* For the signed integer version, use the same code as for unsigned. */
671 return read_be_uint16 (file, (grub_uint16_t *) value);
674 /* Return a pointer to the character index entry for the glyph corresponding to
675 the codepoint CODE in the font FONT. If not found, return zero. */
676 static inline struct char_index_entry *
677 find_glyph (const grub_font_t font, grub_uint32_t code)
679 struct char_index_entry *table;
680 grub_size_t lo;
681 grub_size_t hi;
682 grub_size_t mid;
684 table = font->char_index;
686 /* Use BMP index if possible. */
687 if (code < 0x10000 && font->bmp_idx)
689 if (font->bmp_idx[code] == 0xffff)
690 return 0;
691 return &table[font->bmp_idx[code]];
694 /* Do a binary search in `char_index', which is ordered by code point. */
695 lo = 0;
696 hi = font->num_chars - 1;
698 if (!table)
699 return 0;
701 while (lo <= hi)
703 mid = lo + (hi - lo) / 2;
704 if (code < table[mid].code)
705 hi = mid - 1;
706 else if (code > table[mid].code)
707 lo = mid + 1;
708 else
709 return &table[mid];
712 return 0;
715 /* Get a glyph for the Unicode character CODE in FONT. The glyph is loaded
716 from the font file if has not been loaded yet.
717 Returns a pointer to the glyph if found, or 0 if it is not found. */
718 static struct grub_font_glyph *
719 grub_font_get_glyph_internal (grub_font_t font, grub_uint32_t code)
721 struct char_index_entry *index_entry;
723 index_entry = find_glyph (font, code);
724 if (index_entry)
726 struct grub_font_glyph *glyph = 0;
727 grub_uint16_t width;
728 grub_uint16_t height;
729 grub_int16_t xoff;
730 grub_int16_t yoff;
731 grub_int16_t dwidth;
732 int len;
734 if (index_entry->glyph)
735 /* Return cached glyph. */
736 return index_entry->glyph;
738 if (!font->file)
739 /* No open file, can't load any glyphs. */
740 return 0;
742 /* Make sure we can find glyphs for error messages. Push active
743 error message to error stack and reset error message. */
744 grub_error_push ();
746 grub_file_seek (font->file, index_entry->offset);
748 /* Read the glyph width, height, and baseline. */
749 if (read_be_uint16 (font->file, &width) != 0
750 || read_be_uint16 (font->file, &height) != 0
751 || read_be_int16 (font->file, &xoff) != 0
752 || read_be_int16 (font->file, &yoff) != 0
753 || read_be_int16 (font->file, &dwidth) != 0)
755 remove_font (font);
756 return 0;
759 len = (width * height + 7) / 8;
760 glyph = grub_malloc (sizeof (struct grub_font_glyph) + len);
761 if (!glyph)
763 remove_font (font);
764 return 0;
767 glyph->font = font;
768 glyph->width = width;
769 glyph->height = height;
770 glyph->offset_x = xoff;
771 glyph->offset_y = yoff;
772 glyph->device_width = dwidth;
774 /* Don't try to read empty bitmaps (e.g., space characters). */
775 if (len != 0)
777 if (grub_file_read (font->file, glyph->bitmap, len) != len)
779 remove_font (font);
780 grub_free (glyph);
781 return 0;
785 /* Restore old error message. */
786 grub_error_pop ();
788 /* Cache the glyph. */
789 index_entry->glyph = glyph;
791 return glyph;
794 return 0;
797 /* Free the memory used by FONT.
798 This should not be called if the font has been made available to
799 users (once it is added to the global font list), since there would
800 be the possibility of a dangling pointer. */
801 static void
802 free_font (grub_font_t font)
804 if (font)
806 if (font->file)
807 grub_file_close (font->file);
808 grub_free (font->name);
809 grub_free (font->family);
810 grub_free (font->char_index);
811 grub_free (font->bmp_idx);
812 grub_free (font);
816 /* Add FONT to the global font registry.
817 Returns 0 upon success, nonzero on failure
818 (the font was not registered). */
819 static int
820 register_font (grub_font_t font)
822 struct grub_font_node *node = 0;
824 node = grub_malloc (sizeof (struct grub_font_node));
825 if (!node)
826 return 1;
828 node->value = font;
829 node->next = grub_font_list;
830 grub_font_list = node;
832 return 0;
835 /* Remove the font from the global font list. We don't actually free the
836 font's memory since users could be holding references to the font. */
837 static void
838 remove_font (grub_font_t font)
840 struct grub_font_node **nextp, *cur;
842 for (nextp = &grub_font_list, cur = *nextp;
843 cur; nextp = &cur->next, cur = cur->next)
845 if (cur->value == font)
847 *nextp = cur->next;
849 /* Free the node, but not the font itself. */
850 grub_free (cur);
852 return;
857 /* Get a font from the list of loaded fonts. This function will return
858 another font if the requested font is not available. If no fonts are
859 loaded, then a special 'null font' is returned, which contains no glyphs,
860 but is not a null pointer so the caller may omit checks for NULL. */
861 grub_font_t
862 grub_font_get (const char *font_name)
864 struct grub_font_node *node;
866 for (node = grub_font_list; node; node = node->next)
868 grub_font_t font = node->value;
869 if (grub_strcmp (font->name, font_name) == 0)
870 return font;
873 /* If no font by that name is found, return the first font in the list
874 as a fallback. */
875 if (grub_font_list && grub_font_list->value)
876 return grub_font_list->value;
877 else
878 /* The null_font is a last resort. */
879 return &null_font;
882 /* Get the full name of the font. */
883 const char *
884 grub_font_get_name (grub_font_t font)
886 return font->name;
889 /* Get the maximum width of any character in the font in pixels. */
891 grub_font_get_max_char_width (grub_font_t font)
893 return font->max_char_width;
896 /* Get the distance in pixels from the baseline to the lowest descenders
897 (for instance, in a lowercase 'y', 'g', etc.). */
899 grub_font_get_descent (grub_font_t font)
901 return font->descent;
904 /* FIXME: not correct for all fonts. */
906 grub_font_get_xheight (grub_font_t font)
908 return font->ascent / 2;
911 /* Get the *standard leading* of the font in pixel, which is the spacing
912 between two lines of text. Specifically, it is the space between the
913 descent of one line and the ascent of the next line. This is included
914 in the *height* metric. */
916 grub_font_get_leading (grub_font_t font)
918 return font->leading;
921 /* Get the distance in pixels between baselines of adjacent lines of text. */
923 grub_font_get_height (grub_font_t font)
925 return font->ascent + font->descent + font->leading;
928 /* Get the glyph for FONT corresponding to the Unicode code point CODE.
929 Returns the ASCII glyph for the code if no other fonts are available.
930 The glyphs are cached once loaded. */
931 struct grub_font_glyph *
932 grub_font_get_glyph (grub_font_t font, grub_uint32_t code)
934 struct grub_font_glyph *glyph = 0;
935 if (font)
936 glyph = grub_font_get_glyph_internal (font, code);
937 if (glyph == 0)
939 glyph = ascii_glyph_lookup (code);
941 return glyph;
945 /* Calculate a subject value representing "how similar" two fonts are.
946 This is used to prioritize the order that fonts are scanned for missing
947 glyphs. The object is to select glyphs from the most similar font
948 possible, for the best appearance.
949 The heuristic is crude, but it helps greatly when fonts of similar
950 sizes are used so that tiny 8 point glyphs are not mixed into a string
951 of 24 point text unless there is no other choice. */
952 static int
953 get_font_diversity (grub_font_t a, grub_font_t b)
955 int d;
957 d = 0;
959 if (a->ascent && b->ascent)
960 d += grub_abs (a->ascent - b->ascent) * 8;
961 else
962 /* Penalty for missing attributes. */
963 d += 50;
965 if (a->max_char_height && b->max_char_height)
966 d += grub_abs (a->max_char_height - b->max_char_height) * 8;
967 else
968 /* Penalty for missing attributes. */
969 d += 50;
971 /* Weight is a minor factor. */
972 d += (a->weight != b->weight) ? 5 : 0;
974 return d;
977 /* Get a glyph corresponding to the codepoint CODE. If FONT contains the
978 specified glyph, then it is returned. Otherwise, all other loaded fonts
979 are searched until one is found that contains a glyph for CODE.
980 If no glyph is available for CODE in the loaded fonts, then a glyph
981 representing an unknown character is returned.
982 This function never returns NULL.
983 The returned glyph is owned by the font manager and should not be freed
984 by the caller. The glyphs are cached. */
985 struct grub_font_glyph *
986 grub_font_get_glyph_with_fallback (grub_font_t font, grub_uint32_t code)
988 struct grub_font_glyph *glyph;
989 struct grub_font_node *node;
990 /* Keep track of next node, in case there's an I/O error in
991 grub_font_get_glyph_internal() and the font is removed from the list. */
992 struct grub_font_node *next;
993 /* Information on the best glyph found so far, to help find the glyph in
994 the best matching to the requested one. */
995 int best_diversity;
996 struct grub_font_glyph *best_glyph;
998 if (font)
1000 /* First try to get the glyph from the specified font. */
1001 glyph = grub_font_get_glyph_internal (font, code);
1002 if (glyph)
1003 return glyph;
1006 /* Otherwise, search all loaded fonts for the glyph and use the one from
1007 the font that best matches the requested font. */
1008 best_diversity = 10000;
1009 best_glyph = 0;
1011 for (node = grub_font_list; node; node = next)
1013 grub_font_t curfont;
1015 curfont = node->value;
1016 next = node->next;
1018 glyph = grub_font_get_glyph_internal (curfont, code);
1019 if (glyph && !font)
1020 return glyph;
1021 if (glyph)
1023 int d;
1025 d = get_font_diversity (curfont, font);
1026 if (d < best_diversity)
1028 best_diversity = d;
1029 best_glyph = glyph;
1034 return best_glyph;
1037 #if 0
1038 static struct grub_font_glyph *
1039 grub_font_dup_glyph (struct grub_font_glyph *glyph)
1041 static struct grub_font_glyph *ret;
1042 ret = grub_malloc (sizeof (*ret) + (glyph->width * glyph->height + 7) / 8);
1043 if (!ret)
1044 return NULL;
1045 grub_memcpy (ret, glyph, sizeof (*ret)
1046 + (glyph->width * glyph->height + 7) / 8);
1047 return ret;
1049 #endif
1051 /* FIXME: suboptimal. */
1052 static void
1053 grub_font_blit_glyph (struct grub_font_glyph *target,
1054 struct grub_font_glyph *src, unsigned dx, unsigned dy)
1056 unsigned src_bit, tgt_bit, src_byte, tgt_byte;
1057 unsigned i, j;
1058 for (i = 0; i < src->height; i++)
1060 src_bit = (src->width * i) % 8;
1061 src_byte = (src->width * i) / 8;
1062 tgt_bit = (target->width * (dy + i) + dx) % 8;
1063 tgt_byte = (target->width * (dy + i) + dx) / 8;
1064 for (j = 0; j < src->width; j++)
1066 target->bitmap[tgt_byte] |= ((src->bitmap[src_byte] << src_bit)
1067 & 0x80) >> tgt_bit;
1068 src_bit++;
1069 tgt_bit++;
1070 if (src_bit == 8)
1072 src_byte++;
1073 src_bit = 0;
1075 if (tgt_bit == 8)
1077 tgt_byte++;
1078 tgt_bit = 0;
1084 static void
1085 grub_font_blit_glyph_mirror (struct grub_font_glyph *target,
1086 struct grub_font_glyph *src,
1087 unsigned dx, unsigned dy)
1089 unsigned tgt_bit, src_byte, tgt_byte;
1090 signed src_bit;
1091 unsigned i, j;
1092 for (i = 0; i < src->height; i++)
1094 src_bit = (src->width * i + src->width - 1) % 8;
1095 src_byte = (src->width * i + src->width - 1) / 8;
1096 tgt_bit = (target->width * (dy + i) + dx) % 8;
1097 tgt_byte = (target->width * (dy + i) + dx) / 8;
1098 for (j = 0; j < src->width; j++)
1100 target->bitmap[tgt_byte] |= ((src->bitmap[src_byte] << src_bit)
1101 & 0x80) >> tgt_bit;
1102 src_bit--;
1103 tgt_bit++;
1104 if (src_bit == -1)
1106 src_byte--;
1107 src_bit = 7;
1109 if (tgt_bit == 8)
1111 tgt_byte++;
1112 tgt_bit = 0;
1118 /* Context for blit_comb. */
1119 struct blit_comb_ctx
1121 struct grub_font_glyph *glyph;
1122 int *device_width;
1123 struct grub_video_signed_rect bounds;
1126 /* Helper for blit_comb. */
1127 static void
1128 do_blit (struct grub_font_glyph *src, signed dx, signed dy,
1129 struct blit_comb_ctx *ctx)
1131 if (ctx->glyph)
1132 grub_font_blit_glyph (ctx->glyph, src, dx - ctx->glyph->offset_x,
1133 (ctx->glyph->height + ctx->glyph->offset_y) + dy);
1134 if (dx < ctx->bounds.x)
1136 ctx->bounds.width += ctx->bounds.x - dx;
1137 ctx->bounds.x = dx;
1139 if (ctx->bounds.y > -src->height - dy)
1141 ctx->bounds.height += ctx->bounds.y - (-src->height - dy);
1142 ctx->bounds.y = (-src->height - dy);
1144 if (dx + src->width - ctx->bounds.x >= (signed) ctx->bounds.width)
1145 ctx->bounds.width = dx + src->width - ctx->bounds.x + 1;
1146 if ((signed) ctx->bounds.height < src->height + (-src->height - dy)
1147 - ctx->bounds.y)
1148 ctx->bounds.height = src->height + (-src->height - dy) - ctx->bounds.y;
1151 /* Helper for blit_comb. */
1152 static inline void
1153 add_device_width (int val, struct blit_comb_ctx *ctx)
1155 if (ctx->glyph)
1156 ctx->glyph->device_width += val;
1157 if (ctx->device_width)
1158 *ctx->device_width += val;
1161 static void
1162 blit_comb (const struct grub_unicode_glyph *glyph_id,
1163 struct grub_font_glyph *glyph,
1164 struct grub_video_signed_rect *bounds_out,
1165 struct grub_font_glyph *main_glyph,
1166 struct grub_font_glyph **combining_glyphs, int *device_width)
1168 struct blit_comb_ctx ctx = {
1169 .glyph = glyph,
1170 .device_width = device_width
1172 unsigned i;
1173 signed above_rightx, above_righty;
1174 signed above_leftx, above_lefty;
1175 signed below_rightx, below_righty;
1176 signed min_devwidth = 0;
1177 const struct grub_unicode_combining *comb;
1179 if (glyph)
1180 glyph->device_width = main_glyph->device_width;
1181 if (device_width)
1182 *device_width = main_glyph->device_width;
1184 ctx.bounds.x = main_glyph->offset_x;
1185 ctx.bounds.y = main_glyph->offset_y;
1186 ctx.bounds.width = main_glyph->width;
1187 ctx.bounds.height = main_glyph->height;
1189 above_rightx = main_glyph->offset_x + main_glyph->width;
1190 above_righty = ctx.bounds.y + ctx.bounds.height;
1192 above_leftx = main_glyph->offset_x;
1193 above_lefty = ctx.bounds.y + ctx.bounds.height;
1195 below_rightx = ctx.bounds.x + ctx.bounds.width;
1196 below_righty = ctx.bounds.y;
1198 comb = grub_unicode_get_comb (glyph_id);
1200 for (i = 0; i < glyph_id->ncomb; i++)
1202 grub_int16_t space = 0;
1203 /* Center by default. */
1204 grub_int16_t targetx;
1206 if (!combining_glyphs[i])
1207 continue;
1208 targetx = (ctx.bounds.width - combining_glyphs[i]->width) / 2 + ctx.bounds.x;
1209 /* CGJ is to avoid diacritics reordering. */
1210 if (comb[i].code
1211 == GRUB_UNICODE_COMBINING_GRAPHEME_JOINER)
1212 continue;
1213 switch (comb[i].type)
1215 case GRUB_UNICODE_COMB_OVERLAY:
1216 do_blit (combining_glyphs[i],
1217 targetx,
1218 (ctx.bounds.height - combining_glyphs[i]->height) / 2
1219 - (ctx.bounds.height + ctx.bounds.y), &ctx);
1220 if (min_devwidth < combining_glyphs[i]->width)
1221 min_devwidth = combining_glyphs[i]->width;
1222 break;
1224 case GRUB_UNICODE_COMB_ATTACHED_ABOVE_RIGHT:
1225 do_blit (combining_glyphs[i], above_rightx, -above_righty, &ctx);
1226 above_rightx += combining_glyphs[i]->width;
1227 break;
1229 case GRUB_UNICODE_COMB_ABOVE_RIGHT:
1230 do_blit (combining_glyphs[i], above_rightx,
1231 -(above_righty + combining_glyphs[i]->height), &ctx);
1232 above_rightx += combining_glyphs[i]->width;
1233 break;
1235 case GRUB_UNICODE_COMB_ABOVE_LEFT:
1236 above_leftx -= combining_glyphs[i]->width;
1237 do_blit (combining_glyphs[i], above_leftx,
1238 -(above_lefty + combining_glyphs[i]->height), &ctx);
1239 break;
1241 case GRUB_UNICODE_COMB_BELOW_RIGHT:
1242 do_blit (combining_glyphs[i], below_rightx, below_righty, &ctx);
1243 below_rightx += combining_glyphs[i]->width;
1244 break;
1246 case GRUB_UNICODE_COMB_HEBREW_HOLAM:
1247 if (glyph_id->base != GRUB_UNICODE_HEBREW_WAW)
1248 targetx =
1249 main_glyph->offset_x - combining_glyphs[i]->width -
1250 (combining_glyphs[i]->width + 3) / 4;
1251 goto above_on_main;
1253 case GRUB_UNICODE_COMB_HEBREW_SIN_DOT:
1254 targetx = main_glyph->offset_x + combining_glyphs[i]->width / 4;
1255 goto above_on_main;
1257 case GRUB_UNICODE_COMB_HEBREW_SHIN_DOT:
1258 targetx =
1259 main_glyph->width + main_glyph->offset_x -
1260 combining_glyphs[i]->width;
1261 above_on_main:
1262 space = combining_glyphs[i]->offset_y
1263 - grub_font_get_xheight (combining_glyphs[i]->font) - 1;
1264 if (space <= 0)
1265 space = 1 + (grub_font_get_xheight (main_glyph->font)) / 8;
1266 do_blit (combining_glyphs[i], targetx,
1267 -(main_glyph->height + main_glyph->offset_y + space
1268 + combining_glyphs[i]->height), &ctx);
1269 if (min_devwidth < combining_glyphs[i]->width)
1270 min_devwidth = combining_glyphs[i]->width;
1271 break;
1273 /* TODO: Put dammah, fathah and alif nearer to shadda. */
1274 case GRUB_UNICODE_COMB_SYRIAC_SUPERSCRIPT_ALAPH:
1275 case GRUB_UNICODE_COMB_ARABIC_DAMMAH:
1276 case GRUB_UNICODE_COMB_ARABIC_DAMMATAN:
1277 case GRUB_UNICODE_COMB_ARABIC_FATHATAN:
1278 case GRUB_UNICODE_COMB_ARABIC_FATHAH:
1279 case GRUB_UNICODE_COMB_ARABIC_SUPERSCRIPT_ALIF:
1280 case GRUB_UNICODE_COMB_ARABIC_SUKUN:
1281 case GRUB_UNICODE_COMB_ARABIC_SHADDA:
1282 case GRUB_UNICODE_COMB_HEBREW_RAFE:
1283 case GRUB_UNICODE_STACK_ABOVE:
1284 stacked_above:
1285 space = combining_glyphs[i]->offset_y
1286 - grub_font_get_xheight (combining_glyphs[i]->font) - 1;
1287 if (space <= 0)
1288 space = 1 + (grub_font_get_xheight (main_glyph->font)) / 8;
1289 /* Fallthrough. */
1290 case GRUB_UNICODE_STACK_ATTACHED_ABOVE:
1291 do_blit (combining_glyphs[i], targetx,
1292 -(ctx.bounds.height + ctx.bounds.y + space
1293 + combining_glyphs[i]->height), &ctx);
1294 if (min_devwidth < combining_glyphs[i]->width)
1295 min_devwidth = combining_glyphs[i]->width;
1296 break;
1298 case GRUB_UNICODE_COMB_HEBREW_DAGESH:
1299 do_blit (combining_glyphs[i], targetx,
1300 -(ctx.bounds.height / 2 + ctx.bounds.y
1301 + combining_glyphs[i]->height / 2), &ctx);
1302 if (min_devwidth < combining_glyphs[i]->width)
1303 min_devwidth = combining_glyphs[i]->width;
1304 break;
1306 case GRUB_UNICODE_COMB_HEBREW_SHEVA:
1307 case GRUB_UNICODE_COMB_HEBREW_HIRIQ:
1308 case GRUB_UNICODE_COMB_HEBREW_QAMATS:
1309 case GRUB_UNICODE_COMB_HEBREW_TSERE:
1310 case GRUB_UNICODE_COMB_HEBREW_SEGOL:
1311 /* TODO: placement in final kaf and under reish. */
1313 case GRUB_UNICODE_COMB_HEBREW_HATAF_SEGOL:
1314 case GRUB_UNICODE_COMB_HEBREW_HATAF_PATAH:
1315 case GRUB_UNICODE_COMB_HEBREW_HATAF_QAMATS:
1316 case GRUB_UNICODE_COMB_HEBREW_PATAH:
1317 case GRUB_UNICODE_COMB_HEBREW_QUBUTS:
1318 case GRUB_UNICODE_COMB_HEBREW_METEG:
1319 /* TODO: Put kasra and kasratan under shadda. */
1320 case GRUB_UNICODE_COMB_ARABIC_KASRA:
1321 case GRUB_UNICODE_COMB_ARABIC_KASRATAN:
1322 /* I don't know how ypogegrammeni differs from subscript. */
1323 case GRUB_UNICODE_COMB_YPOGEGRAMMENI:
1324 case GRUB_UNICODE_STACK_BELOW:
1325 stacked_below:
1326 space = -(combining_glyphs[i]->offset_y
1327 + combining_glyphs[i]->height);
1328 if (space <= 0)
1329 space = 1 + (grub_font_get_xheight (main_glyph->font)) / 8;
1330 /* Fallthrough. */
1332 case GRUB_UNICODE_STACK_ATTACHED_BELOW:
1333 do_blit (combining_glyphs[i], targetx, -(ctx.bounds.y - space),
1334 &ctx);
1335 if (min_devwidth < combining_glyphs[i]->width)
1336 min_devwidth = combining_glyphs[i]->width;
1337 break;
1339 case GRUB_UNICODE_COMB_MN:
1340 switch (comb[i].code)
1342 case GRUB_UNICODE_THAANA_ABAFILI:
1343 case GRUB_UNICODE_THAANA_AABAAFILI:
1344 case GRUB_UNICODE_THAANA_UBUFILI:
1345 case GRUB_UNICODE_THAANA_OOBOOFILI:
1346 case GRUB_UNICODE_THAANA_EBEFILI:
1347 case GRUB_UNICODE_THAANA_EYBEYFILI:
1348 case GRUB_UNICODE_THAANA_OBOFILI:
1349 case GRUB_UNICODE_THAANA_OABOAFILI:
1350 case GRUB_UNICODE_THAANA_SUKUN:
1351 goto stacked_above;
1352 case GRUB_UNICODE_THAANA_IBIFILI:
1353 case GRUB_UNICODE_THAANA_EEBEEFILI:
1354 goto stacked_below;
1356 /* Fall through. */
1357 default:
1359 /* Default handling. Just draw combining character on top
1360 of base character.
1361 FIXME: support more unicode types correctly.
1363 do_blit (combining_glyphs[i],
1364 main_glyph->device_width
1365 + combining_glyphs[i]->offset_x,
1366 -(combining_glyphs[i]->height
1367 + combining_glyphs[i]->offset_y), &ctx);
1368 add_device_width (combining_glyphs[i]->device_width, &ctx);
1372 add_device_width ((above_rightx >
1373 below_rightx ? above_rightx : below_rightx) -
1374 (main_glyph->offset_x + main_glyph->width), &ctx);
1375 add_device_width (above_leftx - main_glyph->offset_x, &ctx);
1376 if (glyph && glyph->device_width < min_devwidth)
1377 glyph->device_width = min_devwidth;
1378 if (device_width && *device_width < min_devwidth)
1379 *device_width = min_devwidth;
1381 if (bounds_out)
1382 *bounds_out = ctx.bounds;
1385 static struct grub_font_glyph *
1386 grub_font_construct_dry_run (grub_font_t hinted_font,
1387 const struct grub_unicode_glyph *glyph_id,
1388 struct grub_video_signed_rect *bounds,
1389 struct grub_font_glyph **combining_glyphs,
1390 int *device_width)
1392 struct grub_font_glyph *main_glyph = NULL;
1393 grub_uint32_t desired_attributes = 0;
1394 unsigned i;
1395 grub_uint32_t base = glyph_id->base;
1396 const struct grub_unicode_combining *comb;
1398 if (glyph_id->attributes & GRUB_UNICODE_GLYPH_ATTRIBUTE_RIGHT_JOINED)
1399 desired_attributes |= GRUB_FONT_CODE_RIGHT_JOINED;
1401 if (glyph_id->attributes & GRUB_UNICODE_GLYPH_ATTRIBUTE_LEFT_JOINED)
1402 desired_attributes |= GRUB_FONT_CODE_LEFT_JOINED;
1404 comb = grub_unicode_get_comb (glyph_id);
1406 if (base == 'i' || base == 'j')
1408 for (i = 0; i < glyph_id->ncomb; i++)
1409 if (comb[i].type == GRUB_UNICODE_STACK_ABOVE)
1410 break;
1411 if (i < glyph_id->ncomb && base == 'i')
1412 base = GRUB_UNICODE_DOTLESS_LOWERCASE_I;
1413 if (i < glyph_id->ncomb && base == 'j')
1414 base = GRUB_UNICODE_DOTLESS_LOWERCASE_J;
1417 main_glyph = grub_font_get_glyph_with_fallback (hinted_font, base
1418 | desired_attributes);
1420 if (!main_glyph)
1421 main_glyph = grub_font_get_glyph_with_fallback (hinted_font,
1422 base);
1424 /* Glyph not available in any font. Use ASCII fallback. */
1425 if (!main_glyph)
1426 main_glyph = ascii_glyph_lookup (base);
1428 /* Glyph not available in any font. Return unknown glyph. */
1429 if (!main_glyph)
1430 return NULL;
1432 if (device_width)
1433 *device_width = main_glyph->device_width;
1435 if (!glyph_id->ncomb && !glyph_id->attributes)
1436 return main_glyph;
1438 if (glyph_id->ncomb && !combining_glyphs)
1440 grub_errno = GRUB_ERR_NONE;
1441 return main_glyph;
1444 for (i = 0; i < glyph_id->ncomb; i++)
1445 combining_glyphs[i]
1446 = grub_font_get_glyph_with_fallback (main_glyph->font,
1447 comb[i].code);
1449 blit_comb (glyph_id, NULL, bounds, main_glyph, combining_glyphs,
1450 device_width);
1452 return main_glyph;
1455 static struct grub_font_glyph **render_combining_glyphs = 0;
1456 static grub_size_t render_max_comb_glyphs = 0;
1458 static void
1459 ensure_comb_space (const struct grub_unicode_glyph *glyph_id)
1461 if (glyph_id->ncomb <= render_max_comb_glyphs)
1462 return;
1464 render_max_comb_glyphs = 2 * glyph_id->ncomb;
1465 if (render_max_comb_glyphs < 8)
1466 render_max_comb_glyphs = 8;
1467 grub_free (render_combining_glyphs);
1468 render_combining_glyphs = grub_malloc (render_max_comb_glyphs
1469 * sizeof (render_combining_glyphs[0]));
1470 if (!render_combining_glyphs)
1471 grub_errno = 0;
1475 grub_font_get_constructed_device_width (grub_font_t hinted_font,
1476 const struct grub_unicode_glyph
1477 *glyph_id)
1479 int ret;
1480 struct grub_font_glyph *main_glyph;
1482 ensure_comb_space (glyph_id);
1484 main_glyph = grub_font_construct_dry_run (hinted_font, glyph_id, NULL,
1485 render_combining_glyphs, &ret);
1486 if (!main_glyph)
1487 return unknown_glyph->device_width;
1488 return ret;
1491 struct grub_font_glyph *
1492 grub_font_construct_glyph (grub_font_t hinted_font,
1493 const struct grub_unicode_glyph *glyph_id)
1495 struct grub_font_glyph *main_glyph;
1496 struct grub_video_signed_rect bounds;
1497 static struct grub_font_glyph *glyph = 0;
1498 static grub_size_t max_glyph_size = 0;
1500 ensure_comb_space (glyph_id);
1502 main_glyph = grub_font_construct_dry_run (hinted_font, glyph_id,
1503 &bounds, render_combining_glyphs,
1504 NULL);
1506 if (!main_glyph)
1507 return unknown_glyph;
1509 if (!render_combining_glyphs && glyph_id->ncomb)
1510 return main_glyph;
1512 if (!glyph_id->ncomb && !glyph_id->attributes)
1513 return main_glyph;
1515 if (max_glyph_size < sizeof (*glyph) + (bounds.width * bounds.height + GRUB_CHAR_BIT - 1) / GRUB_CHAR_BIT)
1517 grub_free (glyph);
1518 max_glyph_size = (sizeof (*glyph) + (bounds.width * bounds.height + GRUB_CHAR_BIT - 1) / GRUB_CHAR_BIT) * 2;
1519 if (max_glyph_size < 8)
1520 max_glyph_size = 8;
1521 glyph = grub_malloc (max_glyph_size);
1523 if (!glyph)
1525 grub_errno = GRUB_ERR_NONE;
1526 return main_glyph;
1529 grub_memset (glyph, 0, sizeof (*glyph)
1530 + (bounds.width * bounds.height
1531 + GRUB_CHAR_BIT - 1) / GRUB_CHAR_BIT);
1533 glyph->font = main_glyph->font;
1534 glyph->width = bounds.width;
1535 glyph->height = bounds.height;
1536 glyph->offset_x = bounds.x;
1537 glyph->offset_y = bounds.y;
1539 if (glyph_id->attributes & GRUB_UNICODE_GLYPH_ATTRIBUTE_MIRROR)
1540 grub_font_blit_glyph_mirror (glyph, main_glyph,
1541 main_glyph->offset_x - glyph->offset_x,
1542 (glyph->height + glyph->offset_y)
1543 - (main_glyph->height +
1544 main_glyph->offset_y));
1545 else
1546 grub_font_blit_glyph (glyph, main_glyph,
1547 main_glyph->offset_x - glyph->offset_x,
1548 (glyph->height + glyph->offset_y)
1549 - (main_glyph->height + main_glyph->offset_y));
1551 blit_comb (glyph_id, glyph, NULL, main_glyph, render_combining_glyphs, NULL);
1553 return glyph;
1556 /* Draw the specified glyph at (x, y). The y coordinate designates the
1557 baseline of the character, while the x coordinate designates the left
1558 side location of the character. */
1559 grub_err_t
1560 grub_font_draw_glyph (struct grub_font_glyph * glyph,
1561 grub_video_color_t color, int left_x, int baseline_y)
1563 struct grub_video_bitmap glyph_bitmap;
1565 /* Don't try to draw empty glyphs (U+0020, etc.). */
1566 if (glyph->width == 0 || glyph->height == 0)
1567 return GRUB_ERR_NONE;
1569 glyph_bitmap.mode_info.width = glyph->width;
1570 glyph_bitmap.mode_info.height = glyph->height;
1571 glyph_bitmap.mode_info.mode_type
1572 = (1 << GRUB_VIDEO_MODE_TYPE_DEPTH_POS) | GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP;
1573 glyph_bitmap.mode_info.blit_format = GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED;
1574 glyph_bitmap.mode_info.bpp = 1;
1576 /* Really 1 bit per pixel. */
1577 glyph_bitmap.mode_info.bytes_per_pixel = 0;
1579 /* Packed densely as bits. */
1580 glyph_bitmap.mode_info.pitch = glyph->width;
1582 glyph_bitmap.mode_info.number_of_colors = 2;
1583 glyph_bitmap.mode_info.bg_red = 0;
1584 glyph_bitmap.mode_info.bg_green = 0;
1585 glyph_bitmap.mode_info.bg_blue = 0;
1586 glyph_bitmap.mode_info.bg_alpha = 0;
1587 grub_video_unmap_color (color,
1588 &glyph_bitmap.mode_info.fg_red,
1589 &glyph_bitmap.mode_info.fg_green,
1590 &glyph_bitmap.mode_info.fg_blue,
1591 &glyph_bitmap.mode_info.fg_alpha);
1592 glyph_bitmap.data = glyph->bitmap;
1594 int bitmap_left = left_x + glyph->offset_x;
1595 int bitmap_bottom = baseline_y - glyph->offset_y;
1596 int bitmap_top = bitmap_bottom - glyph->height;
1598 return grub_video_blit_bitmap (&glyph_bitmap, GRUB_VIDEO_BLIT_BLEND,
1599 bitmap_left, bitmap_top,
1600 0, 0, glyph->width, glyph->height);