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>
33 GRUB_MOD_LICENSE ("GPLv3+");
35 #ifdef USE_ASCII_FAILBACK
43 struct char_index_entry
46 grub_uint8_t storage_flags
;
49 /* Glyph if loaded, or NULL otherwise. */
50 struct grub_font_glyph
*glyph
;
53 #define FONT_WEIGHT_NORMAL 100
54 #define FONT_WEIGHT_BOLD 200
55 #define ASCII_BITMAP_SIZE 16
65 short max_char_height
;
69 grub_uint32_t num_chars
;
70 struct char_index_entry
*char_index
;
71 grub_uint16_t
*bmp_idx
;
74 /* Definition of font registry. */
75 struct grub_font_node
*grub_font_list
;
77 static int register_font (grub_font_t font
);
78 static void font_init (grub_font_t font
);
79 static void free_font (grub_font_t font
);
80 static void remove_font (grub_font_t font
);
82 struct font_file_section
84 /* The file this section is in. */
87 /* FOURCC name of the section. */
90 /* Length of the section contents. */
93 /* Set by open_section() on EOF. */
97 /* Replace unknown glyphs with a rounded question mark. */
98 static grub_uint8_t unknown_glyph_bitmap
[] = {
118 /* The "unknown glyph" glyph, used as a last resort. */
119 static struct grub_font_glyph
*unknown_glyph
;
121 /* The font structure used when no other font is loaded. This functions
122 as a "Null Object" pattern, so that code everywhere does not have to
123 check for a NULL grub_font_t to avoid dereferencing a null pointer. */
124 static struct grub_font null_font
;
126 /* Flag to ensure module is initialized only once. */
127 static grub_uint8_t font_loader_initialized
;
129 #ifdef USE_ASCII_FAILBACK
130 static struct grub_font_glyph
*ascii_font_glyph
[0x80];
133 static struct grub_font_glyph
*
134 ascii_glyph_lookup (grub_uint32_t code
)
136 #ifdef USE_ASCII_FAILBACK
137 static int ascii_failback_initialized
= 0;
142 if (ascii_failback_initialized
== 0)
145 for (current
= 0; current
< 0x80; current
++)
147 ascii_font_glyph
[current
] =
148 grub_malloc (sizeof (struct grub_font_glyph
) + ASCII_BITMAP_SIZE
);
150 ascii_font_glyph
[current
]->width
= 8;
151 ascii_font_glyph
[current
]->height
= 16;
152 ascii_font_glyph
[current
]->offset_x
= 0;
153 ascii_font_glyph
[current
]->offset_y
= -2;
154 ascii_font_glyph
[current
]->device_width
= 8;
155 ascii_font_glyph
[current
]->font
= NULL
;
157 grub_memcpy (ascii_font_glyph
[current
]->bitmap
,
158 &ascii_bitmaps
[current
* ASCII_BITMAP_SIZE
],
162 ascii_failback_initialized
= 1;
165 return ascii_font_glyph
[code
];
173 grub_font_loader_init (void)
175 /* Only initialize font loader once. */
176 if (font_loader_initialized
)
179 /* Make glyph for unknown glyph. */
180 unknown_glyph
= grub_malloc (sizeof (struct grub_font_glyph
)
181 + sizeof (unknown_glyph_bitmap
));
185 unknown_glyph
->width
= 8;
186 unknown_glyph
->height
= 16;
187 unknown_glyph
->offset_x
= 0;
188 unknown_glyph
->offset_y
= -3;
189 unknown_glyph
->device_width
= 8;
190 grub_memcpy (unknown_glyph
->bitmap
,
191 unknown_glyph_bitmap
, sizeof (unknown_glyph_bitmap
));
193 /* Initialize the null font. */
194 font_init (&null_font
);
195 /* FIXME: Fix this slightly improper cast. */
196 null_font
.name
= (char *) "<No Font>";
197 null_font
.ascent
= unknown_glyph
->height
- 3;
198 null_font
.descent
= 3;
199 null_font
.max_char_width
= unknown_glyph
->width
;
200 null_font
.max_char_height
= unknown_glyph
->height
;
202 font_loader_initialized
= 1;
205 /* Initialize the font object with initial default values. */
207 font_init (grub_font_t font
)
212 font
->point_size
= 0;
215 /* Default leading value, not in font file yet. */
218 font
->max_char_width
= 0;
219 font
->max_char_height
= 0;
223 font
->char_index
= 0;
227 /* Open the next section in the file.
229 On success, the section name is stored in section->name and the length in
230 section->length, and 0 is returned. On failure, 1 is returned and
231 grub_errno is set appropriately with an error message.
233 If 1 is returned due to being at the end of the file, then section->eof is
234 set to 1; otherwise, section->eof is set to 0. */
236 open_section (grub_file_t file
, struct font_file_section
*section
)
239 grub_uint32_t raw_length
;
241 section
->file
= file
;
244 /* Read the FOURCC section name. */
245 retval
= grub_file_read (file
, section
->name
, 4);
246 if (retval
>= 0 && retval
< 4)
248 /* EOF encountered. */
258 /* Read the big-endian 32-bit section length. */
259 retval
= grub_file_read (file
, &raw_length
, 4);
260 if (retval
>= 0 && retval
< 4)
262 /* EOF encountered. */
272 /* Convert byte-order and store in *length. */
273 section
->length
= grub_be_to_cpu32 (raw_length
);
278 /* Size in bytes of each character index (CHIX section)
279 entry in the font file. */
280 #define FONT_CHAR_INDEX_ENTRY_SIZE (4 + 1 + 4)
282 /* Load the character index (CHIX) section contents from the font file. This
283 presumes that the position of FILE is positioned immediately after the
284 section length for the CHIX section (i.e., at the start of the section
285 contents). Returns 0 upon success, nonzero for failure (in which case
286 grub_errno is set appropriately). */
288 load_font_index (grub_file_t file
, grub_uint32_t sect_length
, struct
292 grub_uint32_t last_code
;
295 grub_printf ("load_font_index(sect_length=%d)\n", sect_length
);
298 /* Sanity check: ensure section length is divisible by the entry size. */
299 if ((sect_length
% FONT_CHAR_INDEX_ENTRY_SIZE
) != 0)
301 grub_error (GRUB_ERR_BAD_FONT
,
302 "font file format error: character index length %d "
303 "is not a multiple of the entry size %d",
304 sect_length
, FONT_CHAR_INDEX_ENTRY_SIZE
);
308 /* Calculate the number of characters. */
309 font
->num_chars
= sect_length
/ FONT_CHAR_INDEX_ENTRY_SIZE
;
311 /* Allocate the character index array. */
312 font
->char_index
= grub_malloc (font
->num_chars
313 * sizeof (struct char_index_entry
));
314 if (!font
->char_index
)
316 font
->bmp_idx
= grub_malloc (0x10000 * sizeof (grub_uint16_t
));
319 grub_memset (font
->bmp_idx
, 0xff, 0x10000 * sizeof (grub_uint16_t
));
323 grub_printf ("num_chars=%d)\n", font
->num_chars
);
328 /* Load the character index data from the file. */
329 for (i
= 0; i
< font
->num_chars
; i
++)
331 struct char_index_entry
*entry
= &font
->char_index
[i
];
333 /* Read code point value; convert to native byte order. */
334 if (grub_file_read (file
, &entry
->code
, 4) != 4)
336 entry
->code
= grub_be_to_cpu32 (entry
->code
);
338 /* Verify that characters are in ascending order. */
339 if (i
!= 0 && entry
->code
<= last_code
)
341 grub_error (GRUB_ERR_BAD_FONT
,
342 "font characters not in ascending order: %u <= %u",
343 entry
->code
, last_code
);
347 if (entry
->code
< 0x10000)
348 font
->bmp_idx
[entry
->code
] = i
;
350 last_code
= entry
->code
;
352 /* Read storage flags byte. */
353 if (grub_file_read (file
, &entry
->storage_flags
, 1) != 1)
356 /* Read glyph data offset; convert to native byte order. */
357 if (grub_file_read (file
, &entry
->offset
, 4) != 4)
359 entry
->offset
= grub_be_to_cpu32 (entry
->offset
);
361 /* No glyph loaded. Will be loaded on demand and cached thereafter. */
365 /* Print the 1st 10 characters. */
367 grub_printf ("c=%d o=%d\n", entry
->code
, entry
->offset
);
374 /* Read the contents of the specified section as a string, which is
375 allocated on the heap. Returns 0 if there is an error. */
377 read_section_as_string (struct font_file_section
*section
)
382 str
= grub_malloc (section
->length
+ 1);
386 ret
= grub_file_read (section
->file
, str
, section
->length
);
387 if (ret
< 0 || ret
!= (grub_ssize_t
) section
->length
)
393 str
[section
->length
] = '\0';
397 /* Read the contents of the current section as a 16-bit integer value,
398 which is stored into *VALUE.
399 Returns 0 upon success, nonzero upon failure. */
401 read_section_as_short (struct font_file_section
*section
,
402 grub_int16_t
* value
)
404 grub_uint16_t raw_value
;
406 if (section
->length
!= 2)
408 grub_error (GRUB_ERR_BAD_FONT
,
409 "font file format error: section %c%c%c%c length "
410 "is %d but should be 2",
411 section
->name
[0], section
->name
[1],
412 section
->name
[2], section
->name
[3], section
->length
);
415 if (grub_file_read (section
->file
, &raw_value
, 2) != 2)
418 *value
= grub_be_to_cpu16 (raw_value
);
422 /* Load a font and add it to the beginning of the global font list.
423 Returns 0 upon success, nonzero upon failure. */
425 grub_font_load (const char *filename
)
427 grub_file_t file
= 0;
428 struct font_file_section section
;
430 grub_font_t font
= 0;
433 grub_printf ("add_font(%s)\n", filename
);
436 file
= grub_buffile_open (filename
, 1024);
441 grub_printf ("file opened\n");
444 /* Read the FILE section. It indicates the file format. */
445 if (open_section (file
, §ion
) != 0)
449 grub_printf ("opened FILE section\n");
451 if (grub_memcmp (section
.name
, FONT_FORMAT_SECTION_NAMES_FILE
,
452 sizeof (FONT_FORMAT_SECTION_NAMES_FILE
) - 1) != 0)
454 grub_error (GRUB_ERR_BAD_FONT
,
455 "font file format error: 1st section must be FILE");
460 grub_printf ("section name ok\n");
462 if (section
.length
!= 4)
464 grub_error (GRUB_ERR_BAD_FONT
,
465 "font file format error (file type ID length is %d "
466 "but should be 4)", section
.length
);
471 grub_printf ("section length ok\n");
473 /* Check the file format type code. */
474 if (grub_file_read (file
, magic
, 4) != 4)
478 grub_printf ("read magic ok\n");
481 if (grub_memcmp (magic
, FONT_FORMAT_PFF2_MAGIC
, 4) != 0)
483 grub_error (GRUB_ERR_BAD_FONT
, "invalid font magic %x %x %x %x",
484 magic
[0], magic
[1], magic
[2], magic
[3]);
489 grub_printf ("compare magic ok\n");
492 /* Allocate the font object. */
493 font
= (grub_font_t
) grub_zalloc (sizeof (struct grub_font
));
501 grub_printf ("allocate font ok; loading font info\n");
504 /* Load the font information. */
507 if (open_section (file
, §ion
) != 0)
510 break; /* Done reading the font file. */
516 grub_printf ("opened section %c%c%c%c ok\n",
517 section
.name
[0], section
.name
[1],
518 section
.name
[2], section
.name
[3]);
521 if (grub_memcmp (section
.name
, FONT_FORMAT_SECTION_NAMES_FONT_NAME
,
522 sizeof (FONT_FORMAT_SECTION_NAMES_FONT_NAME
) - 1) == 0)
524 font
->name
= read_section_as_string (§ion
);
528 else if (grub_memcmp (section
.name
,
529 FONT_FORMAT_SECTION_NAMES_POINT_SIZE
,
530 sizeof (FONT_FORMAT_SECTION_NAMES_POINT_SIZE
) -
533 if (read_section_as_short (§ion
, &font
->point_size
) != 0)
536 else if (grub_memcmp (section
.name
, FONT_FORMAT_SECTION_NAMES_WEIGHT
,
537 sizeof (FONT_FORMAT_SECTION_NAMES_WEIGHT
) - 1)
541 wt
= read_section_as_string (§ion
);
544 /* Convert the weight string 'normal' or 'bold' into a number. */
545 if (grub_strcmp (wt
, "normal") == 0)
546 font
->weight
= FONT_WEIGHT_NORMAL
;
547 else if (grub_strcmp (wt
, "bold") == 0)
548 font
->weight
= FONT_WEIGHT_BOLD
;
551 else if (grub_memcmp (section
.name
,
552 FONT_FORMAT_SECTION_NAMES_MAX_CHAR_WIDTH
,
553 sizeof (FONT_FORMAT_SECTION_NAMES_MAX_CHAR_WIDTH
)
556 if (read_section_as_short (§ion
, &font
->max_char_width
) != 0)
559 else if (grub_memcmp (section
.name
,
560 FONT_FORMAT_SECTION_NAMES_MAX_CHAR_HEIGHT
,
561 sizeof (FONT_FORMAT_SECTION_NAMES_MAX_CHAR_HEIGHT
)
564 if (read_section_as_short (§ion
, &font
->max_char_height
) != 0)
567 else if (grub_memcmp (section
.name
,
568 FONT_FORMAT_SECTION_NAMES_ASCENT
,
569 sizeof (FONT_FORMAT_SECTION_NAMES_ASCENT
) - 1)
572 if (read_section_as_short (§ion
, &font
->ascent
) != 0)
575 else if (grub_memcmp (section
.name
, FONT_FORMAT_SECTION_NAMES_DESCENT
,
576 sizeof (FONT_FORMAT_SECTION_NAMES_DESCENT
) - 1)
579 if (read_section_as_short (§ion
, &font
->descent
) != 0)
582 else if (grub_memcmp (section
.name
,
583 FONT_FORMAT_SECTION_NAMES_CHAR_INDEX
,
584 sizeof (FONT_FORMAT_SECTION_NAMES_CHAR_INDEX
) -
587 if (load_font_index (file
, section
.length
, font
) != 0)
590 else if (grub_memcmp (section
.name
, FONT_FORMAT_SECTION_NAMES_DATA
,
591 sizeof (FONT_FORMAT_SECTION_NAMES_DATA
) - 1) == 0)
593 /* When the DATA section marker is reached, we stop reading. */
598 /* Unhandled section type, simply skip past it. */
600 grub_printf ("Unhandled section type, skipping.\n");
602 grub_off_t section_end
= grub_file_tell (file
) + section
.length
;
603 if ((int) grub_file_seek (file
, section_end
) == -1)
610 grub_dprintf ("font", "Font has no name.\n");
611 font
->name
= grub_strdup ("Unknown");
615 grub_printf ("Loaded font `%s'.\n"
616 "Ascent=%d Descent=%d MaxW=%d MaxH=%d Number of characters=%d.\n",
618 font
->ascent
, font
->descent
,
619 font
->max_char_width
, font
->max_char_height
, font
->num_chars
);
622 if (font
->max_char_width
== 0
623 || font
->max_char_height
== 0
624 || font
->num_chars
== 0
625 || font
->char_index
== 0 || font
->ascent
== 0 || font
->descent
== 0)
627 grub_error (GRUB_ERR_BAD_FONT
,
628 "invalid font file: missing some required data");
632 /* Add the font to the global font registry. */
633 if (register_font (font
) != 0)
640 grub_file_close (file
);
648 /* Read a 16-bit big-endian integer from FILE, convert it to native byte
649 order, and store it in *VALUE.
650 Returns 0 on success, 1 on failure. */
652 read_be_uint16 (grub_file_t file
, grub_uint16_t
* value
)
654 if (grub_file_read (file
, value
, 2) != 2)
656 *value
= grub_be_to_cpu16 (*value
);
661 read_be_int16 (grub_file_t file
, grub_int16_t
* value
)
663 /* For the signed integer version, use the same code as for unsigned. */
664 return read_be_uint16 (file
, (grub_uint16_t
*) value
);
667 /* Return a pointer to the character index entry for the glyph corresponding to
668 the codepoint CODE in the font FONT. If not found, return zero. */
669 static inline struct char_index_entry
*
670 find_glyph (const grub_font_t font
, grub_uint32_t code
)
672 struct char_index_entry
*table
;
677 table
= font
->char_index
;
679 /* Use BMP index if possible. */
680 if (code
< 0x10000 && font
->bmp_idx
)
682 if (font
->bmp_idx
[code
] == 0xffff)
684 return &table
[font
->bmp_idx
[code
]];
687 /* Do a binary search in `char_index', which is ordered by code point. */
689 hi
= font
->num_chars
- 1;
696 mid
= lo
+ (hi
- lo
) / 2;
697 if (code
< table
[mid
].code
)
699 else if (code
> table
[mid
].code
)
708 /* Get a glyph for the Unicode character CODE in FONT. The glyph is loaded
709 from the font file if has not been loaded yet.
710 Returns a pointer to the glyph if found, or 0 if it is not found. */
711 static struct grub_font_glyph
*
712 grub_font_get_glyph_internal (grub_font_t font
, grub_uint32_t code
)
714 struct char_index_entry
*index_entry
;
716 index_entry
= find_glyph (font
, code
);
719 struct grub_font_glyph
*glyph
= 0;
721 grub_uint16_t height
;
727 if (index_entry
->glyph
)
728 /* Return cached glyph. */
729 return index_entry
->glyph
;
732 /* No open file, can't load any glyphs. */
735 /* Make sure we can find glyphs for error messages. Push active
736 error message to error stack and reset error message. */
739 grub_file_seek (font
->file
, index_entry
->offset
);
741 /* Read the glyph width, height, and baseline. */
742 if (read_be_uint16 (font
->file
, &width
) != 0
743 || read_be_uint16 (font
->file
, &height
) != 0
744 || read_be_int16 (font
->file
, &xoff
) != 0
745 || read_be_int16 (font
->file
, &yoff
) != 0
746 || read_be_int16 (font
->file
, &dwidth
) != 0)
752 len
= (width
* height
+ 7) / 8;
753 glyph
= grub_malloc (sizeof (struct grub_font_glyph
) + len
);
761 glyph
->width
= width
;
762 glyph
->height
= height
;
763 glyph
->offset_x
= xoff
;
764 glyph
->offset_y
= yoff
;
765 glyph
->device_width
= dwidth
;
767 /* Don't try to read empty bitmaps (e.g., space characters). */
770 if (grub_file_read (font
->file
, glyph
->bitmap
, len
) != len
)
777 /* Restore old error message. */
780 /* Cache the glyph. */
781 index_entry
->glyph
= glyph
;
789 /* Free the memory used by FONT.
790 This should not be called if the font has been made available to
791 users (once it is added to the global font list), since there would
792 be the possibility of a dangling pointer. */
794 free_font (grub_font_t font
)
799 grub_file_close (font
->file
);
800 grub_free (font
->name
);
801 grub_free (font
->family
);
802 grub_free (font
->char_index
);
803 grub_free (font
->bmp_idx
);
808 /* Add FONT to the global font registry.
809 Returns 0 upon success, nonzero on failure
810 (the font was not registered). */
812 register_font (grub_font_t font
)
814 struct grub_font_node
*node
= 0;
816 node
= grub_malloc (sizeof (struct grub_font_node
));
821 node
->next
= grub_font_list
;
822 grub_font_list
= node
;
827 /* Remove the font from the global font list. We don't actually free the
828 font's memory since users could be holding references to the font. */
830 remove_font (grub_font_t font
)
832 struct grub_font_node
**nextp
, *cur
;
834 for (nextp
= &grub_font_list
, cur
= *nextp
;
835 cur
; nextp
= &cur
->next
, cur
= cur
->next
)
837 if (cur
->value
== font
)
841 /* Free the node, but not the font itself. */
849 /* Get a font from the list of loaded fonts. This function will return
850 another font if the requested font is not available. If no fonts are
851 loaded, then a special 'null font' is returned, which contains no glyphs,
852 but is not a null pointer so the caller may omit checks for NULL. */
854 grub_font_get (const char *font_name
)
856 struct grub_font_node
*node
;
858 for (node
= grub_font_list
; node
; node
= node
->next
)
860 grub_font_t font
= node
->value
;
861 if (grub_strcmp (font
->name
, font_name
) == 0)
865 /* If no font by that name is found, return the first font in the list
867 if (grub_font_list
&& grub_font_list
->value
)
868 return grub_font_list
->value
;
870 /* The null_font is a last resort. */
874 /* Get the full name of the font. */
876 grub_font_get_name (grub_font_t font
)
881 /* Get the maximum width of any character in the font in pixels. */
883 grub_font_get_max_char_width (grub_font_t font
)
885 return font
->max_char_width
;
888 /* Get the maximum height of any character in the font in pixels. */
890 grub_font_get_max_char_height (grub_font_t font
)
892 return font
->max_char_height
;
895 /* Get the distance in pixels from the top of characters to the baseline. */
897 grub_font_get_ascent (grub_font_t font
)
902 /* Get the distance in pixels from the baseline to the lowest descenders
903 (for instance, in a lowercase 'y', 'g', etc.). */
905 grub_font_get_descent (grub_font_t font
)
907 return font
->descent
;
910 /* FIXME: not correct for all fonts. */
912 grub_font_get_xheight (grub_font_t font
)
914 return font
->ascent
/ 2;
917 /* Get the *standard leading* of the font in pixel, which is the spacing
918 between two lines of text. Specifically, it is the space between the
919 descent of one line and the ascent of the next line. This is included
920 in the *height* metric. */
922 grub_font_get_leading (grub_font_t font
)
924 return font
->leading
;
927 /* Get the distance in pixels between baselines of adjacent lines of text. */
929 grub_font_get_height (grub_font_t font
)
931 return font
->ascent
+ font
->descent
+ font
->leading
;
934 /* Get the glyph for FONT corresponding to the Unicode code point CODE.
935 Returns the ASCII glyph for the code if no other fonts are available.
936 The glyphs are cached once loaded. */
937 struct grub_font_glyph
*
938 grub_font_get_glyph (grub_font_t font
, grub_uint32_t code
)
940 struct grub_font_glyph
*glyph
= 0;
942 glyph
= grub_font_get_glyph_internal (font
, code
);
945 glyph
= ascii_glyph_lookup (code
);
951 /* Calculate a subject value representing "how similar" two fonts are.
952 This is used to prioritize the order that fonts are scanned for missing
953 glyphs. The object is to select glyphs from the most similar font
954 possible, for the best appearance.
955 The heuristic is crude, but it helps greatly when fonts of similar
956 sizes are used so that tiny 8 point glyphs are not mixed into a string
957 of 24 point text unless there is no other choice. */
959 get_font_diversity (grub_font_t a
, grub_font_t b
)
965 if (a
->ascent
&& b
->ascent
)
966 d
+= grub_abs (a
->ascent
- b
->ascent
) * 8;
968 /* Penalty for missing attributes. */
971 if (a
->max_char_height
&& b
->max_char_height
)
972 d
+= grub_abs (a
->max_char_height
- b
->max_char_height
) * 8;
974 /* Penalty for missing attributes. */
977 /* Weight is a minor factor. */
978 d
+= (a
->weight
!= b
->weight
) ? 5 : 0;
983 /* Get a glyph corresponding to the codepoint CODE. If FONT contains the
984 specified glyph, then it is returned. Otherwise, all other loaded fonts
985 are searched until one is found that contains a glyph for CODE.
986 If no glyph is available for CODE in the loaded fonts, then a glyph
987 representing an unknown character is returned.
988 This function never returns NULL.
989 The returned glyph is owned by the font manager and should not be freed
990 by the caller. The glyphs are cached. */
991 struct grub_font_glyph
*
992 grub_font_get_glyph_with_fallback (grub_font_t font
, grub_uint32_t code
)
994 struct grub_font_glyph
*glyph
;
995 struct grub_font_node
*node
;
996 /* Keep track of next node, in case there's an I/O error in
997 grub_font_get_glyph_internal() and the font is removed from the list. */
998 struct grub_font_node
*next
;
999 /* Information on the best glyph found so far, to help find the glyph in
1000 the best matching to the requested one. */
1002 struct grub_font_glyph
*best_glyph
;
1006 /* First try to get the glyph from the specified font. */
1007 glyph
= grub_font_get_glyph_internal (font
, code
);
1012 /* Otherwise, search all loaded fonts for the glyph and use the one from
1013 the font that best matches the requested font. */
1014 best_diversity
= 10000;
1017 for (node
= grub_font_list
; node
; node
= next
)
1019 grub_font_t curfont
;
1021 curfont
= node
->value
;
1024 glyph
= grub_font_get_glyph_internal (curfont
, code
);
1031 d
= get_font_diversity (curfont
, font
);
1032 if (d
< best_diversity
)
1043 static struct grub_font_glyph
*
1044 grub_font_dup_glyph (struct grub_font_glyph
*glyph
)
1046 static struct grub_font_glyph
*ret
;
1047 ret
= grub_malloc (sizeof (*ret
) + (glyph
->width
* glyph
->height
+ 7) / 8);
1050 grub_memcpy (ret
, glyph
, sizeof (*ret
)
1051 + (glyph
->width
* glyph
->height
+ 7) / 8);
1055 /* FIXME: suboptimal. */
1057 grub_font_blit_glyph (struct grub_font_glyph
*target
,
1058 struct grub_font_glyph
*src
, unsigned dx
, unsigned dy
)
1060 unsigned src_bit
, tgt_bit
, src_byte
, tgt_byte
;
1062 for (i
= 0; i
< src
->height
; i
++)
1064 src_bit
= (src
->width
* i
) % 8;
1065 src_byte
= (src
->width
* i
) / 8;
1066 tgt_bit
= (target
->width
* (dy
+ i
) + dx
) % 8;
1067 tgt_byte
= (target
->width
* (dy
+ i
) + dx
) / 8;
1068 for (j
= 0; j
< src
->width
; j
++)
1070 target
->bitmap
[tgt_byte
] |= ((src
->bitmap
[src_byte
] << src_bit
)
1089 grub_font_blit_glyph_mirror (struct grub_font_glyph
*target
,
1090 struct grub_font_glyph
*src
,
1091 unsigned dx
, unsigned dy
)
1093 unsigned tgt_bit
, src_byte
, tgt_byte
;
1096 for (i
= 0; i
< src
->height
; i
++)
1098 src_bit
= (src
->width
* i
+ src
->width
- 1) % 8;
1099 src_byte
= (src
->width
* i
+ src
->width
- 1) / 8;
1100 tgt_bit
= (target
->width
* (dy
+ i
) + dx
) % 8;
1101 tgt_byte
= (target
->width
* (dy
+ i
) + dx
) / 8;
1102 for (j
= 0; j
< src
->width
; j
++)
1104 target
->bitmap
[tgt_byte
] |= ((src
->bitmap
[src_byte
] << src_bit
)
1123 blit_comb (const struct grub_unicode_glyph
*glyph_id
,
1124 struct grub_font_glyph
*glyph
,
1125 struct grub_video_signed_rect
*bounds_out
,
1126 struct grub_font_glyph
*main_glyph
,
1127 struct grub_font_glyph
**combining_glyphs
, int *device_width
)
1129 struct grub_video_signed_rect bounds
;
1131 signed above_rightx
, above_righty
;
1132 signed above_leftx
, above_lefty
;
1133 signed below_rightx
, below_righty
;
1134 signed min_devwidth
= 0;
1135 auto void NESTED_FUNC_ATTR
do_blit (struct grub_font_glyph
*src
,
1136 signed dx
, signed dy
);
1137 void NESTED_FUNC_ATTR
do_blit (struct grub_font_glyph
*src
,
1138 signed dx
, signed dy
)
1141 grub_font_blit_glyph (glyph
, src
, dx
- glyph
->offset_x
,
1142 (glyph
->height
+ glyph
->offset_y
) + dy
);
1145 bounds
.width
+= bounds
.x
- dx
;
1148 if (bounds
.y
> -src
->height
- dy
)
1150 bounds
.height
+= bounds
.y
- (-src
->height
- dy
);
1151 bounds
.y
= (-src
->height
- dy
);
1153 if (dx
+ src
->width
- bounds
.x
>= (signed) bounds
.width
)
1154 bounds
.width
= dx
+ src
->width
- bounds
.x
+ 1;
1155 if ((signed) bounds
.height
< src
->height
+ (-src
->height
- dy
) - bounds
.y
)
1156 bounds
.height
= src
->height
+ (-src
->height
- dy
) - bounds
.y
;
1159 auto void add_device_width (int val
);
1160 void add_device_width (int val
)
1163 glyph
->device_width
+= val
;
1165 *device_width
+= val
;
1169 glyph
->device_width
= main_glyph
->device_width
;
1171 *device_width
= main_glyph
->device_width
;
1173 bounds
.x
= main_glyph
->offset_x
;
1174 bounds
.y
= main_glyph
->offset_y
;
1175 bounds
.width
= main_glyph
->width
;
1176 bounds
.height
= main_glyph
->height
;
1178 above_rightx
= main_glyph
->offset_x
+ main_glyph
->width
;
1179 above_righty
= bounds
.y
+ bounds
.height
;
1181 above_leftx
= main_glyph
->offset_x
;
1182 above_lefty
= bounds
.y
+ bounds
.height
;
1184 below_rightx
= bounds
.x
+ bounds
.width
;
1185 below_righty
= bounds
.y
;
1187 for (i
= 0; i
< glyph_id
->ncomb
; i
++)
1189 grub_int16_t space
= 0;
1190 /* Center by default. */
1191 grub_int16_t targetx
;
1193 if (!combining_glyphs
[i
])
1195 targetx
= (bounds
.width
- combining_glyphs
[i
]->width
) / 2 + bounds
.x
;
1196 /* CGJ is to avoid diacritics reordering. */
1197 if (glyph_id
->combining
[i
].code
1198 == GRUB_UNICODE_COMBINING_GRAPHEME_JOINER
)
1200 switch (glyph_id
->combining
[i
].type
)
1202 case GRUB_UNICODE_COMB_OVERLAY
:
1203 do_blit (combining_glyphs
[i
],
1205 (bounds
.height
- combining_glyphs
[i
]->height
) / 2
1206 - (bounds
.height
+ bounds
.y
));
1207 if (min_devwidth
< combining_glyphs
[i
]->width
)
1208 min_devwidth
= combining_glyphs
[i
]->width
;
1211 case GRUB_UNICODE_COMB_ATTACHED_ABOVE_RIGHT
:
1212 do_blit (combining_glyphs
[i
], above_rightx
, -above_righty
);
1213 above_rightx
+= combining_glyphs
[i
]->width
;
1216 case GRUB_UNICODE_COMB_ABOVE_RIGHT
:
1217 do_blit (combining_glyphs
[i
], above_rightx
,
1218 -(above_righty
+ combining_glyphs
[i
]->height
));
1219 above_rightx
+= combining_glyphs
[i
]->width
;
1222 case GRUB_UNICODE_COMB_ABOVE_LEFT
:
1223 above_leftx
-= combining_glyphs
[i
]->width
;
1224 do_blit (combining_glyphs
[i
], above_leftx
,
1225 -(above_lefty
+ combining_glyphs
[i
]->height
));
1228 case GRUB_UNICODE_COMB_BELOW_RIGHT
:
1229 do_blit (combining_glyphs
[i
], below_rightx
, below_righty
);
1230 below_rightx
+= combining_glyphs
[i
]->width
;
1233 case GRUB_UNICODE_COMB_HEBREW_HOLAM
:
1234 if (glyph_id
->base
!= GRUB_UNICODE_HEBREW_WAW
)
1236 main_glyph
->offset_x
- combining_glyphs
[i
]->width
-
1237 (combining_glyphs
[i
]->width
+ 3) / 4;
1240 case GRUB_UNICODE_COMB_HEBREW_SIN_DOT
:
1241 targetx
= main_glyph
->offset_x
+ combining_glyphs
[i
]->width
/ 4;
1244 case GRUB_UNICODE_COMB_HEBREW_SHIN_DOT
:
1246 main_glyph
->width
+ main_glyph
->offset_x
-
1247 combining_glyphs
[i
]->width
;
1249 space
= combining_glyphs
[i
]->offset_y
1250 - grub_font_get_xheight (combining_glyphs
[i
]->font
) - 1;
1252 space
= 1 + (grub_font_get_xheight (main_glyph
->font
)) / 8;
1253 do_blit (combining_glyphs
[i
], targetx
,
1254 -(main_glyph
->height
+ main_glyph
->offset_y
+ space
1255 + combining_glyphs
[i
]->height
));
1256 if (min_devwidth
< combining_glyphs
[i
]->width
)
1257 min_devwidth
= combining_glyphs
[i
]->width
;
1260 /* TODO: Put dammah, fathah and alif nearer to shadda. */
1261 case GRUB_UNICODE_COMB_SYRIAC_SUPERSCRIPT_ALAPH
:
1262 case GRUB_UNICODE_COMB_ARABIC_DAMMAH
:
1263 case GRUB_UNICODE_COMB_ARABIC_DAMMATAN
:
1264 case GRUB_UNICODE_COMB_ARABIC_FATHATAN
:
1265 case GRUB_UNICODE_COMB_ARABIC_FATHAH
:
1266 case GRUB_UNICODE_COMB_ARABIC_SUPERSCRIPT_ALIF
:
1267 case GRUB_UNICODE_COMB_ARABIC_SUKUN
:
1268 case GRUB_UNICODE_COMB_ARABIC_SHADDA
:
1269 case GRUB_UNICODE_COMB_HEBREW_RAFE
:
1270 case GRUB_UNICODE_STACK_ABOVE
:
1272 space
= combining_glyphs
[i
]->offset_y
1273 - grub_font_get_xheight (combining_glyphs
[i
]->font
) - 1;
1275 space
= 1 + (grub_font_get_xheight (main_glyph
->font
)) / 8;
1277 case GRUB_UNICODE_STACK_ATTACHED_ABOVE
:
1278 do_blit (combining_glyphs
[i
], targetx
,
1279 -(bounds
.height
+ bounds
.y
+ space
1280 + combining_glyphs
[i
]->height
));
1281 if (min_devwidth
< combining_glyphs
[i
]->width
)
1282 min_devwidth
= combining_glyphs
[i
]->width
;
1285 case GRUB_UNICODE_COMB_HEBREW_SHEVA
:
1286 case GRUB_UNICODE_COMB_HEBREW_HIRIQ
:
1287 case GRUB_UNICODE_COMB_HEBREW_QAMATS
:
1288 case GRUB_UNICODE_COMB_HEBREW_TSERE
:
1289 case GRUB_UNICODE_COMB_HEBREW_SEGOL
:
1290 /* TODO: placement in final kaf and under reish. */
1292 case GRUB_UNICODE_COMB_HEBREW_HATAF_SEGOL
:
1293 case GRUB_UNICODE_COMB_HEBREW_HATAF_PATAH
:
1294 case GRUB_UNICODE_COMB_HEBREW_HATAF_QAMATS
:
1295 case GRUB_UNICODE_COMB_HEBREW_PATAH
:
1296 case GRUB_UNICODE_COMB_HEBREW_QUBUTS
:
1297 case GRUB_UNICODE_COMB_HEBREW_METEG
:
1298 /* TODO: Put kasra and kasratan under shadda. */
1299 case GRUB_UNICODE_COMB_ARABIC_KASRA
:
1300 case GRUB_UNICODE_COMB_ARABIC_KASRATAN
:
1301 /* I don't know how ypogegrammeni differs from subscript. */
1302 case GRUB_UNICODE_COMB_YPOGEGRAMMENI
:
1303 case GRUB_UNICODE_STACK_BELOW
:
1305 space
= -(combining_glyphs
[i
]->offset_y
1306 + combining_glyphs
[i
]->height
);
1308 space
= 1 + (grub_font_get_xheight (main_glyph
->font
)) / 8;
1310 case GRUB_UNICODE_STACK_ATTACHED_BELOW
:
1311 do_blit (combining_glyphs
[i
], targetx
, -(bounds
.y
- space
));
1312 if (min_devwidth
< combining_glyphs
[i
]->width
)
1313 min_devwidth
= combining_glyphs
[i
]->width
;
1316 case GRUB_UNICODE_COMB_MN
:
1317 switch (glyph_id
->combining
[i
].code
)
1319 case GRUB_UNICODE_THAANA_ABAFILI
:
1320 case GRUB_UNICODE_THAANA_AABAAFILI
:
1321 case GRUB_UNICODE_THAANA_UBUFILI
:
1322 case GRUB_UNICODE_THAANA_OOBOOFILI
:
1323 case GRUB_UNICODE_THAANA_EBEFILI
:
1324 case GRUB_UNICODE_THAANA_EYBEYFILI
:
1325 case GRUB_UNICODE_THAANA_OBOFILI
:
1326 case GRUB_UNICODE_THAANA_OABOAFILI
:
1327 case GRUB_UNICODE_THAANA_SUKUN
:
1329 case GRUB_UNICODE_THAANA_IBIFILI
:
1330 case GRUB_UNICODE_THAANA_EEBEEFILI
:
1336 /* Default handling. Just draw combining character on top
1338 FIXME: support more unicode types correctly.
1340 do_blit (combining_glyphs
[i
],
1341 main_glyph
->device_width
1342 + combining_glyphs
[i
]->offset_x
,
1343 -(combining_glyphs
[i
]->height
1344 + combining_glyphs
[i
]->offset_y
));
1345 add_device_width (combining_glyphs
[i
]->device_width
);
1349 add_device_width ((above_rightx
>
1350 below_rightx
? above_rightx
: below_rightx
) -
1351 (main_glyph
->offset_x
+ main_glyph
->width
));
1352 add_device_width (above_leftx
- main_glyph
->offset_x
);
1353 if (glyph
&& glyph
->device_width
< min_devwidth
)
1354 glyph
->device_width
= min_devwidth
;
1355 if (device_width
&& *device_width
< min_devwidth
)
1356 *device_width
= min_devwidth
;
1359 *bounds_out
= bounds
;
1362 static struct grub_font_glyph
*
1363 grub_font_construct_dry_run (grub_font_t hinted_font
,
1364 const struct grub_unicode_glyph
*glyph_id
,
1365 struct grub_video_signed_rect
*bounds
,
1366 struct grub_font_glyph
***combining_glyphs_out
,
1369 struct grub_font_glyph
*main_glyph
= NULL
;
1370 struct grub_font_glyph
**combining_glyphs
;
1371 grub_uint32_t desired_attributes
= 0;
1373 if (combining_glyphs_out
)
1374 *combining_glyphs_out
= NULL
;
1376 if (glyph_id
->attributes
& GRUB_UNICODE_GLYPH_ATTRIBUTE_RIGHT_JOINED
)
1377 desired_attributes
|= GRUB_FONT_CODE_RIGHT_JOINED
;
1379 if (glyph_id
->attributes
& GRUB_UNICODE_GLYPH_ATTRIBUTE_LEFT_JOINED
)
1380 desired_attributes
|= GRUB_FONT_CODE_LEFT_JOINED
;
1382 main_glyph
= grub_font_get_glyph_with_fallback (hinted_font
, glyph_id
->base
1383 | desired_attributes
);
1386 main_glyph
= grub_font_get_glyph_with_fallback (hinted_font
,
1389 /* Glyph not available in any font. Use ASCII fallback. */
1391 main_glyph
= ascii_glyph_lookup (glyph_id
->base
);
1393 /* Glyph not available in any font. Return unknown glyph. */
1398 *device_width
= main_glyph
->device_width
;
1400 if (!glyph_id
->ncomb
&& !glyph_id
->attributes
)
1403 combining_glyphs
= grub_malloc (sizeof (combining_glyphs
[0])
1405 if (glyph_id
->ncomb
&& !combining_glyphs
)
1407 grub_errno
= GRUB_ERR_NONE
;
1413 for (i
= 0; i
< glyph_id
->ncomb
; i
++)
1415 = grub_font_get_glyph_with_fallback (main_glyph
->font
,
1416 glyph_id
->combining
[i
].code
);
1419 blit_comb (glyph_id
, NULL
, bounds
, main_glyph
, combining_glyphs
,
1421 if (combining_glyphs_out
)
1422 *combining_glyphs_out
= combining_glyphs
;
1424 grub_free (combining_glyphs
);
1430 grub_font_get_constructed_device_width (grub_font_t hinted_font
,
1431 const struct grub_unicode_glyph
1435 struct grub_font_glyph
*main_glyph
;
1436 main_glyph
= grub_font_construct_dry_run (hinted_font
, glyph_id
, NULL
,
1439 return unknown_glyph
->device_width
;
1443 struct grub_font_glyph
*
1444 grub_font_construct_glyph (grub_font_t hinted_font
,
1445 const struct grub_unicode_glyph
*glyph_id
)
1447 struct grub_font_glyph
*main_glyph
;
1448 struct grub_video_signed_rect bounds
;
1449 struct grub_font_glyph
*glyph
;
1450 struct grub_font_glyph
**combining_glyphs
;
1452 main_glyph
= grub_font_construct_dry_run (hinted_font
, glyph_id
,
1453 &bounds
, &combining_glyphs
, NULL
);
1456 return grub_font_dup_glyph (unknown_glyph
);
1458 if (!combining_glyphs
)
1459 return grub_font_dup_glyph (main_glyph
);
1462 grub_zalloc (sizeof (*glyph
) + (bounds
.width
* bounds
.height
+ 7) / 8);
1465 grub_errno
= GRUB_ERR_NONE
;
1466 return grub_font_dup_glyph (main_glyph
);
1469 glyph
->font
= main_glyph
->font
;
1470 glyph
->width
= bounds
.width
;
1471 glyph
->height
= bounds
.height
;
1472 glyph
->offset_x
= bounds
.x
;
1473 glyph
->offset_y
= bounds
.y
;
1475 if (glyph_id
->attributes
& GRUB_UNICODE_GLYPH_ATTRIBUTE_MIRROR
)
1476 grub_font_blit_glyph_mirror (glyph
, main_glyph
,
1477 main_glyph
->offset_x
- glyph
->offset_x
,
1478 (glyph
->height
+ glyph
->offset_y
)
1479 - (main_glyph
->height
+
1480 main_glyph
->offset_y
));
1482 grub_font_blit_glyph (glyph
, main_glyph
,
1483 main_glyph
->offset_x
- glyph
->offset_x
,
1484 (glyph
->height
+ glyph
->offset_y
)
1485 - (main_glyph
->height
+ main_glyph
->offset_y
));
1487 blit_comb (glyph_id
, glyph
, NULL
, main_glyph
, combining_glyphs
, NULL
);
1492 /* Draw the specified glyph at (x, y). The y coordinate designates the
1493 baseline of the character, while the x coordinate designates the left
1494 side location of the character. */
1496 grub_font_draw_glyph (struct grub_font_glyph
* glyph
,
1497 grub_video_color_t color
, int left_x
, int baseline_y
)
1499 struct grub_video_bitmap glyph_bitmap
;
1501 /* Don't try to draw empty glyphs (U+0020, etc.). */
1502 if (glyph
->width
== 0 || glyph
->height
== 0)
1503 return GRUB_ERR_NONE
;
1505 glyph_bitmap
.mode_info
.width
= glyph
->width
;
1506 glyph_bitmap
.mode_info
.height
= glyph
->height
;
1507 glyph_bitmap
.mode_info
.mode_type
1508 = (1 << GRUB_VIDEO_MODE_TYPE_DEPTH_POS
) | GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP
;
1509 glyph_bitmap
.mode_info
.blit_format
= GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED
;
1510 glyph_bitmap
.mode_info
.bpp
= 1;
1512 /* Really 1 bit per pixel. */
1513 glyph_bitmap
.mode_info
.bytes_per_pixel
= 0;
1515 /* Packed densely as bits. */
1516 glyph_bitmap
.mode_info
.pitch
= glyph
->width
;
1518 glyph_bitmap
.mode_info
.number_of_colors
= 2;
1519 glyph_bitmap
.mode_info
.bg_red
= 0;
1520 glyph_bitmap
.mode_info
.bg_green
= 0;
1521 glyph_bitmap
.mode_info
.bg_blue
= 0;
1522 glyph_bitmap
.mode_info
.bg_alpha
= 0;
1523 grub_video_unmap_color (color
,
1524 &glyph_bitmap
.mode_info
.fg_red
,
1525 &glyph_bitmap
.mode_info
.fg_green
,
1526 &glyph_bitmap
.mode_info
.fg_blue
,
1527 &glyph_bitmap
.mode_info
.fg_alpha
);
1528 glyph_bitmap
.data
= glyph
->bitmap
;
1530 int bitmap_left
= left_x
+ glyph
->offset_x
;
1531 int bitmap_bottom
= baseline_y
- glyph
->offset_y
;
1532 int bitmap_top
= bitmap_bottom
- glyph
->height
;
1534 return grub_video_blit_bitmap (&glyph_bitmap
, GRUB_VIDEO_BLIT_BLEND
,
1535 bitmap_left
, bitmap_top
,
1536 0, 0, glyph
->width
, glyph
->height
);