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+");
36 #ifdef USE_ASCII_FAILBACK
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
66 short max_char_height
;
70 grub_uint32_t num_chars
;
71 struct char_index_entry
*char_index
;
72 grub_uint16_t
*bmp_idx
;
75 /* Definition of font registry. */
76 struct grub_font_node
*grub_font_list
;
78 static int register_font (grub_font_t font
);
79 static void font_init (grub_font_t font
);
80 static void free_font (grub_font_t font
);
81 static void remove_font (grub_font_t font
);
83 struct font_file_section
85 /* The file this section is in. */
88 /* FOURCC name of the section. */
91 /* Length of the section contents. */
94 /* Set by open_section() on EOF. */
98 /* Replace unknown glyphs with a rounded question mark. */
99 static grub_uint8_t unknown_glyph_bitmap
[] = {
119 /* The "unknown glyph" glyph, used as a last resort. */
120 static struct grub_font_glyph
*unknown_glyph
;
122 /* The font structure used when no other font is loaded. This functions
123 as a "Null Object" pattern, so that code everywhere does not have to
124 check for a NULL grub_font_t to avoid dereferencing a null pointer. */
125 static struct grub_font null_font
;
127 /* Flag to ensure module is initialized only once. */
128 static grub_uint8_t font_loader_initialized
;
130 #ifdef USE_ASCII_FAILBACK
131 static struct grub_font_glyph
*ascii_font_glyph
[0x80];
134 static struct grub_font_glyph
*
135 ascii_glyph_lookup (grub_uint32_t code
)
137 #ifdef USE_ASCII_FAILBACK
138 static int ascii_failback_initialized
= 0;
143 if (ascii_failback_initialized
== 0)
146 for (current
= 0; current
< 0x80; current
++)
148 ascii_font_glyph
[current
] =
149 grub_malloc (sizeof (struct grub_font_glyph
) + ASCII_BITMAP_SIZE
);
151 ascii_font_glyph
[current
]->width
= 8;
152 ascii_font_glyph
[current
]->height
= 16;
153 ascii_font_glyph
[current
]->offset_x
= 0;
154 ascii_font_glyph
[current
]->offset_y
= -2;
155 ascii_font_glyph
[current
]->device_width
= 8;
156 ascii_font_glyph
[current
]->font
= NULL
;
158 grub_memcpy (ascii_font_glyph
[current
]->bitmap
,
159 &ascii_bitmaps
[current
* ASCII_BITMAP_SIZE
],
163 ascii_failback_initialized
= 1;
166 return ascii_font_glyph
[code
];
174 grub_font_loader_init (void)
176 /* Only initialize font loader once. */
177 if (font_loader_initialized
)
180 /* Make glyph for unknown glyph. */
181 unknown_glyph
= grub_malloc (sizeof (struct grub_font_glyph
)
182 + sizeof (unknown_glyph_bitmap
));
186 unknown_glyph
->width
= 8;
187 unknown_glyph
->height
= 16;
188 unknown_glyph
->offset_x
= 0;
189 unknown_glyph
->offset_y
= -3;
190 unknown_glyph
->device_width
= 8;
191 grub_memcpy (unknown_glyph
->bitmap
,
192 unknown_glyph_bitmap
, sizeof (unknown_glyph_bitmap
));
194 /* Initialize the null font. */
195 font_init (&null_font
);
196 /* FIXME: Fix this slightly improper cast. */
197 null_font
.name
= (char *) "<No Font>";
198 null_font
.ascent
= unknown_glyph
->height
- 3;
199 null_font
.descent
= 3;
200 null_font
.max_char_width
= unknown_glyph
->width
;
201 null_font
.max_char_height
= unknown_glyph
->height
;
203 font_loader_initialized
= 1;
206 /* Initialize the font object with initial default values. */
208 font_init (grub_font_t font
)
213 font
->point_size
= 0;
216 /* Default leading value, not in font file yet. */
219 font
->max_char_width
= 0;
220 font
->max_char_height
= 0;
224 font
->char_index
= 0;
228 /* Open the next section in the file.
230 On success, the section name is stored in section->name and the length in
231 section->length, and 0 is returned. On failure, 1 is returned and
232 grub_errno is set appropriately with an error message.
234 If 1 is returned due to being at the end of the file, then section->eof is
235 set to 1; otherwise, section->eof is set to 0. */
237 open_section (grub_file_t file
, struct font_file_section
*section
)
240 grub_uint32_t raw_length
;
242 section
->file
= file
;
245 /* Read the FOURCC section name. */
246 retval
= grub_file_read (file
, section
->name
, 4);
247 if (retval
>= 0 && retval
< 4)
249 /* EOF encountered. */
259 /* Read the big-endian 32-bit section length. */
260 retval
= grub_file_read (file
, &raw_length
, 4);
261 if (retval
>= 0 && retval
< 4)
263 /* EOF encountered. */
273 /* Convert byte-order and store in *length. */
274 section
->length
= grub_be_to_cpu32 (raw_length
);
279 /* Size in bytes of each character index (CHIX section)
280 entry in the font file. */
281 #define FONT_CHAR_INDEX_ENTRY_SIZE (4 + 1 + 4)
283 /* Load the character index (CHIX) section contents from the font file. This
284 presumes that the position of FILE is positioned immediately after the
285 section length for the CHIX section (i.e., at the start of the section
286 contents). Returns 0 upon success, nonzero for failure (in which case
287 grub_errno is set appropriately). */
289 load_font_index (grub_file_t file
, grub_uint32_t sect_length
, struct
293 grub_uint32_t last_code
;
296 grub_printf ("load_font_index(sect_length=%d)\n", sect_length
);
299 /* Sanity check: ensure section length is divisible by the entry size. */
300 if ((sect_length
% FONT_CHAR_INDEX_ENTRY_SIZE
) != 0)
302 grub_error (GRUB_ERR_BAD_FONT
,
303 "font file format error: character index length %d "
304 "is not a multiple of the entry size %d",
305 sect_length
, FONT_CHAR_INDEX_ENTRY_SIZE
);
309 /* Calculate the number of characters. */
310 font
->num_chars
= sect_length
/ FONT_CHAR_INDEX_ENTRY_SIZE
;
312 /* Allocate the character index array. */
313 font
->char_index
= grub_malloc (font
->num_chars
314 * sizeof (struct char_index_entry
));
315 if (!font
->char_index
)
317 font
->bmp_idx
= grub_malloc (0x10000 * sizeof (grub_uint16_t
));
320 grub_memset (font
->bmp_idx
, 0xff, 0x10000 * sizeof (grub_uint16_t
));
324 grub_printf ("num_chars=%d)\n", font
->num_chars
);
329 /* Load the character index data from the file. */
330 for (i
= 0; i
< font
->num_chars
; i
++)
332 struct char_index_entry
*entry
= &font
->char_index
[i
];
334 /* Read code point value; convert to native byte order. */
335 if (grub_file_read (file
, &entry
->code
, 4) != 4)
337 entry
->code
= grub_be_to_cpu32 (entry
->code
);
339 /* Verify that characters are in ascending order. */
340 if (i
!= 0 && entry
->code
<= last_code
)
342 grub_error (GRUB_ERR_BAD_FONT
,
343 "font characters not in ascending order: %u <= %u",
344 entry
->code
, last_code
);
348 if (entry
->code
< 0x10000)
349 font
->bmp_idx
[entry
->code
] = i
;
351 last_code
= entry
->code
;
353 /* Read storage flags byte. */
354 if (grub_file_read (file
, &entry
->storage_flags
, 1) != 1)
357 /* Read glyph data offset; convert to native byte order. */
358 if (grub_file_read (file
, &entry
->offset
, 4) != 4)
360 entry
->offset
= grub_be_to_cpu32 (entry
->offset
);
362 /* No glyph loaded. Will be loaded on demand and cached thereafter. */
366 /* Print the 1st 10 characters. */
368 grub_printf ("c=%d o=%d\n", entry
->code
, entry
->offset
);
375 /* Read the contents of the specified section as a string, which is
376 allocated on the heap. Returns 0 if there is an error. */
378 read_section_as_string (struct font_file_section
*section
)
383 str
= grub_malloc (section
->length
+ 1);
387 ret
= grub_file_read (section
->file
, str
, section
->length
);
388 if (ret
< 0 || ret
!= (grub_ssize_t
) section
->length
)
394 str
[section
->length
] = '\0';
398 /* Read the contents of the current section as a 16-bit integer value,
399 which is stored into *VALUE.
400 Returns 0 upon success, nonzero upon failure. */
402 read_section_as_short (struct font_file_section
*section
,
403 grub_int16_t
* value
)
405 grub_uint16_t raw_value
;
407 if (section
->length
!= 2)
409 grub_error (GRUB_ERR_BAD_FONT
,
410 "font file format error: section %c%c%c%c length "
411 "is %d but should be 2",
412 section
->name
[0], section
->name
[1],
413 section
->name
[2], section
->name
[3], section
->length
);
416 if (grub_file_read (section
->file
, &raw_value
, 2) != 2)
419 *value
= grub_be_to_cpu16 (raw_value
);
423 /* Load a font and add it to the beginning of the global font list.
424 Returns 0 upon success, nonzero upon failure. */
426 grub_font_load (const char *filename
)
428 grub_file_t file
= 0;
429 struct font_file_section section
;
431 grub_font_t font
= 0;
434 grub_printf ("add_font(%s)\n", filename
);
437 if (filename
[0] == '(' || filename
[0] == '/' || filename
[0] == '+')
438 file
= grub_buffile_open (filename
, 1024);
441 const char *prefix
= grub_env_get ("prefix");
442 char *fullname
, *ptr
;
445 grub_error (GRUB_ERR_FILE_NOT_FOUND
, N_("variable `%s' isn't set"),
449 fullname
= grub_malloc (grub_strlen (prefix
) + grub_strlen (filename
) + 1
450 + sizeof ("/fonts/") + sizeof (".pf2"));
453 ptr
= grub_stpcpy (fullname
, prefix
);
454 ptr
= grub_stpcpy (ptr
, "/fonts/");
455 ptr
= grub_stpcpy (ptr
, filename
);
456 ptr
= grub_stpcpy (ptr
, ".pf2");
458 file
= grub_buffile_open (fullname
, 1024);
459 grub_free (fullname
);
465 grub_printf ("file opened\n");
468 /* Read the FILE section. It indicates the file format. */
469 if (open_section (file
, §ion
) != 0)
473 grub_printf ("opened FILE section\n");
475 if (grub_memcmp (section
.name
, FONT_FORMAT_SECTION_NAMES_FILE
,
476 sizeof (FONT_FORMAT_SECTION_NAMES_FILE
) - 1) != 0)
478 grub_error (GRUB_ERR_BAD_FONT
,
479 "font file format error: 1st section must be FILE");
484 grub_printf ("section name ok\n");
486 if (section
.length
!= 4)
488 grub_error (GRUB_ERR_BAD_FONT
,
489 "font file format error (file type ID length is %d "
490 "but should be 4)", section
.length
);
495 grub_printf ("section length ok\n");
497 /* Check the file format type code. */
498 if (grub_file_read (file
, magic
, 4) != 4)
502 grub_printf ("read magic ok\n");
505 if (grub_memcmp (magic
, FONT_FORMAT_PFF2_MAGIC
, 4) != 0)
507 grub_error (GRUB_ERR_BAD_FONT
, "invalid font magic %x %x %x %x",
508 magic
[0], magic
[1], magic
[2], magic
[3]);
513 grub_printf ("compare magic ok\n");
516 /* Allocate the font object. */
517 font
= (grub_font_t
) grub_zalloc (sizeof (struct grub_font
));
525 grub_printf ("allocate font ok; loading font info\n");
528 /* Load the font information. */
531 if (open_section (file
, §ion
) != 0)
534 break; /* Done reading the font file. */
540 grub_printf ("opened section %c%c%c%c ok\n",
541 section
.name
[0], section
.name
[1],
542 section
.name
[2], section
.name
[3]);
545 if (grub_memcmp (section
.name
, FONT_FORMAT_SECTION_NAMES_FONT_NAME
,
546 sizeof (FONT_FORMAT_SECTION_NAMES_FONT_NAME
) - 1) == 0)
548 font
->name
= read_section_as_string (§ion
);
552 else if (grub_memcmp (section
.name
,
553 FONT_FORMAT_SECTION_NAMES_POINT_SIZE
,
554 sizeof (FONT_FORMAT_SECTION_NAMES_POINT_SIZE
) -
557 if (read_section_as_short (§ion
, &font
->point_size
) != 0)
560 else if (grub_memcmp (section
.name
, FONT_FORMAT_SECTION_NAMES_WEIGHT
,
561 sizeof (FONT_FORMAT_SECTION_NAMES_WEIGHT
) - 1)
565 wt
= read_section_as_string (§ion
);
568 /* Convert the weight string 'normal' or 'bold' into a number. */
569 if (grub_strcmp (wt
, "normal") == 0)
570 font
->weight
= FONT_WEIGHT_NORMAL
;
571 else if (grub_strcmp (wt
, "bold") == 0)
572 font
->weight
= FONT_WEIGHT_BOLD
;
575 else if (grub_memcmp (section
.name
,
576 FONT_FORMAT_SECTION_NAMES_MAX_CHAR_WIDTH
,
577 sizeof (FONT_FORMAT_SECTION_NAMES_MAX_CHAR_WIDTH
)
580 if (read_section_as_short (§ion
, &font
->max_char_width
) != 0)
583 else if (grub_memcmp (section
.name
,
584 FONT_FORMAT_SECTION_NAMES_MAX_CHAR_HEIGHT
,
585 sizeof (FONT_FORMAT_SECTION_NAMES_MAX_CHAR_HEIGHT
)
588 if (read_section_as_short (§ion
, &font
->max_char_height
) != 0)
591 else if (grub_memcmp (section
.name
,
592 FONT_FORMAT_SECTION_NAMES_ASCENT
,
593 sizeof (FONT_FORMAT_SECTION_NAMES_ASCENT
) - 1)
596 if (read_section_as_short (§ion
, &font
->ascent
) != 0)
599 else if (grub_memcmp (section
.name
, FONT_FORMAT_SECTION_NAMES_DESCENT
,
600 sizeof (FONT_FORMAT_SECTION_NAMES_DESCENT
) - 1)
603 if (read_section_as_short (§ion
, &font
->descent
) != 0)
606 else if (grub_memcmp (section
.name
,
607 FONT_FORMAT_SECTION_NAMES_CHAR_INDEX
,
608 sizeof (FONT_FORMAT_SECTION_NAMES_CHAR_INDEX
) -
611 if (load_font_index (file
, section
.length
, font
) != 0)
614 else if (grub_memcmp (section
.name
, FONT_FORMAT_SECTION_NAMES_DATA
,
615 sizeof (FONT_FORMAT_SECTION_NAMES_DATA
) - 1) == 0)
617 /* When the DATA section marker is reached, we stop reading. */
622 /* Unhandled section type, simply skip past it. */
624 grub_printf ("Unhandled section type, skipping.\n");
626 grub_off_t section_end
= grub_file_tell (file
) + section
.length
;
627 if ((int) grub_file_seek (file
, section_end
) == -1)
634 grub_dprintf ("font", "Font has no name.\n");
635 font
->name
= grub_strdup ("Unknown");
639 grub_printf ("Loaded font `%s'.\n"
640 "Ascent=%d Descent=%d MaxW=%d MaxH=%d Number of characters=%d.\n",
642 font
->ascent
, font
->descent
,
643 font
->max_char_width
, font
->max_char_height
, font
->num_chars
);
646 if (font
->max_char_width
== 0
647 || font
->max_char_height
== 0
648 || font
->num_chars
== 0
649 || font
->char_index
== 0 || font
->ascent
== 0 || font
->descent
== 0)
651 grub_error (GRUB_ERR_BAD_FONT
,
652 "invalid font file: missing some required data");
656 /* Add the font to the global font registry. */
657 if (register_font (font
) != 0)
664 grub_file_close (file
);
672 /* Read a 16-bit big-endian integer from FILE, convert it to native byte
673 order, and store it in *VALUE.
674 Returns 0 on success, 1 on failure. */
676 read_be_uint16 (grub_file_t file
, grub_uint16_t
* value
)
678 if (grub_file_read (file
, value
, 2) != 2)
680 *value
= grub_be_to_cpu16 (*value
);
685 read_be_int16 (grub_file_t file
, grub_int16_t
* value
)
687 /* For the signed integer version, use the same code as for unsigned. */
688 return read_be_uint16 (file
, (grub_uint16_t
*) value
);
691 /* Return a pointer to the character index entry for the glyph corresponding to
692 the codepoint CODE in the font FONT. If not found, return zero. */
693 static inline struct char_index_entry
*
694 find_glyph (const grub_font_t font
, grub_uint32_t code
)
696 struct char_index_entry
*table
;
701 table
= font
->char_index
;
703 /* Use BMP index if possible. */
704 if (code
< 0x10000 && font
->bmp_idx
)
706 if (font
->bmp_idx
[code
] == 0xffff)
708 return &table
[font
->bmp_idx
[code
]];
711 /* Do a binary search in `char_index', which is ordered by code point. */
713 hi
= font
->num_chars
- 1;
720 mid
= lo
+ (hi
- lo
) / 2;
721 if (code
< table
[mid
].code
)
723 else if (code
> table
[mid
].code
)
732 /* Get a glyph for the Unicode character CODE in FONT. The glyph is loaded
733 from the font file if has not been loaded yet.
734 Returns a pointer to the glyph if found, or 0 if it is not found. */
735 static struct grub_font_glyph
*
736 grub_font_get_glyph_internal (grub_font_t font
, grub_uint32_t code
)
738 struct char_index_entry
*index_entry
;
740 index_entry
= find_glyph (font
, code
);
743 struct grub_font_glyph
*glyph
= 0;
745 grub_uint16_t height
;
751 if (index_entry
->glyph
)
752 /* Return cached glyph. */
753 return index_entry
->glyph
;
756 /* No open file, can't load any glyphs. */
759 /* Make sure we can find glyphs for error messages. Push active
760 error message to error stack and reset error message. */
763 grub_file_seek (font
->file
, index_entry
->offset
);
765 /* Read the glyph width, height, and baseline. */
766 if (read_be_uint16 (font
->file
, &width
) != 0
767 || read_be_uint16 (font
->file
, &height
) != 0
768 || read_be_int16 (font
->file
, &xoff
) != 0
769 || read_be_int16 (font
->file
, &yoff
) != 0
770 || read_be_int16 (font
->file
, &dwidth
) != 0)
776 len
= (width
* height
+ 7) / 8;
777 glyph
= grub_malloc (sizeof (struct grub_font_glyph
) + len
);
785 glyph
->width
= width
;
786 glyph
->height
= height
;
787 glyph
->offset_x
= xoff
;
788 glyph
->offset_y
= yoff
;
789 glyph
->device_width
= dwidth
;
791 /* Don't try to read empty bitmaps (e.g., space characters). */
794 if (grub_file_read (font
->file
, glyph
->bitmap
, len
) != len
)
801 /* Restore old error message. */
804 /* Cache the glyph. */
805 index_entry
->glyph
= glyph
;
813 /* Free the memory used by FONT.
814 This should not be called if the font has been made available to
815 users (once it is added to the global font list), since there would
816 be the possibility of a dangling pointer. */
818 free_font (grub_font_t font
)
823 grub_file_close (font
->file
);
824 grub_free (font
->name
);
825 grub_free (font
->family
);
826 grub_free (font
->char_index
);
827 grub_free (font
->bmp_idx
);
832 /* Add FONT to the global font registry.
833 Returns 0 upon success, nonzero on failure
834 (the font was not registered). */
836 register_font (grub_font_t font
)
838 struct grub_font_node
*node
= 0;
840 node
= grub_malloc (sizeof (struct grub_font_node
));
845 node
->next
= grub_font_list
;
846 grub_font_list
= node
;
851 /* Remove the font from the global font list. We don't actually free the
852 font's memory since users could be holding references to the font. */
854 remove_font (grub_font_t font
)
856 struct grub_font_node
**nextp
, *cur
;
858 for (nextp
= &grub_font_list
, cur
= *nextp
;
859 cur
; nextp
= &cur
->next
, cur
= cur
->next
)
861 if (cur
->value
== font
)
865 /* Free the node, but not the font itself. */
873 /* Get a font from the list of loaded fonts. This function will return
874 another font if the requested font is not available. If no fonts are
875 loaded, then a special 'null font' is returned, which contains no glyphs,
876 but is not a null pointer so the caller may omit checks for NULL. */
878 grub_font_get (const char *font_name
)
880 struct grub_font_node
*node
;
882 for (node
= grub_font_list
; node
; node
= node
->next
)
884 grub_font_t font
= node
->value
;
885 if (grub_strcmp (font
->name
, font_name
) == 0)
889 /* If no font by that name is found, return the first font in the list
891 if (grub_font_list
&& grub_font_list
->value
)
892 return grub_font_list
->value
;
894 /* The null_font is a last resort. */
898 /* Get the full name of the font. */
900 grub_font_get_name (grub_font_t font
)
905 /* Get the maximum width of any character in the font in pixels. */
907 grub_font_get_max_char_width (grub_font_t font
)
909 return font
->max_char_width
;
912 /* Get the maximum height of any character in the font in pixels. */
914 grub_font_get_max_char_height (grub_font_t font
)
916 return font
->max_char_height
;
919 /* Get the distance in pixels from the top of characters to the baseline. */
921 grub_font_get_ascent (grub_font_t font
)
926 /* Get the distance in pixels from the baseline to the lowest descenders
927 (for instance, in a lowercase 'y', 'g', etc.). */
929 grub_font_get_descent (grub_font_t font
)
931 return font
->descent
;
934 /* FIXME: not correct for all fonts. */
936 grub_font_get_xheight (grub_font_t font
)
938 return font
->ascent
/ 2;
941 /* Get the *standard leading* of the font in pixel, which is the spacing
942 between two lines of text. Specifically, it is the space between the
943 descent of one line and the ascent of the next line. This is included
944 in the *height* metric. */
946 grub_font_get_leading (grub_font_t font
)
948 return font
->leading
;
951 /* Get the distance in pixels between baselines of adjacent lines of text. */
953 grub_font_get_height (grub_font_t font
)
955 return font
->ascent
+ font
->descent
+ font
->leading
;
958 /* Get the glyph for FONT corresponding to the Unicode code point CODE.
959 Returns the ASCII glyph for the code if no other fonts are available.
960 The glyphs are cached once loaded. */
961 struct grub_font_glyph
*
962 grub_font_get_glyph (grub_font_t font
, grub_uint32_t code
)
964 struct grub_font_glyph
*glyph
= 0;
966 glyph
= grub_font_get_glyph_internal (font
, code
);
969 glyph
= ascii_glyph_lookup (code
);
975 /* Calculate a subject value representing "how similar" two fonts are.
976 This is used to prioritize the order that fonts are scanned for missing
977 glyphs. The object is to select glyphs from the most similar font
978 possible, for the best appearance.
979 The heuristic is crude, but it helps greatly when fonts of similar
980 sizes are used so that tiny 8 point glyphs are not mixed into a string
981 of 24 point text unless there is no other choice. */
983 get_font_diversity (grub_font_t a
, grub_font_t b
)
989 if (a
->ascent
&& b
->ascent
)
990 d
+= grub_abs (a
->ascent
- b
->ascent
) * 8;
992 /* Penalty for missing attributes. */
995 if (a
->max_char_height
&& b
->max_char_height
)
996 d
+= grub_abs (a
->max_char_height
- b
->max_char_height
) * 8;
998 /* Penalty for missing attributes. */
1001 /* Weight is a minor factor. */
1002 d
+= (a
->weight
!= b
->weight
) ? 5 : 0;
1007 /* Get a glyph corresponding to the codepoint CODE. If FONT contains the
1008 specified glyph, then it is returned. Otherwise, all other loaded fonts
1009 are searched until one is found that contains a glyph for CODE.
1010 If no glyph is available for CODE in the loaded fonts, then a glyph
1011 representing an unknown character is returned.
1012 This function never returns NULL.
1013 The returned glyph is owned by the font manager and should not be freed
1014 by the caller. The glyphs are cached. */
1015 struct grub_font_glyph
*
1016 grub_font_get_glyph_with_fallback (grub_font_t font
, grub_uint32_t code
)
1018 struct grub_font_glyph
*glyph
;
1019 struct grub_font_node
*node
;
1020 /* Keep track of next node, in case there's an I/O error in
1021 grub_font_get_glyph_internal() and the font is removed from the list. */
1022 struct grub_font_node
*next
;
1023 /* Information on the best glyph found so far, to help find the glyph in
1024 the best matching to the requested one. */
1026 struct grub_font_glyph
*best_glyph
;
1030 /* First try to get the glyph from the specified font. */
1031 glyph
= grub_font_get_glyph_internal (font
, code
);
1036 /* Otherwise, search all loaded fonts for the glyph and use the one from
1037 the font that best matches the requested font. */
1038 best_diversity
= 10000;
1041 for (node
= grub_font_list
; node
; node
= next
)
1043 grub_font_t curfont
;
1045 curfont
= node
->value
;
1048 glyph
= grub_font_get_glyph_internal (curfont
, code
);
1055 d
= get_font_diversity (curfont
, font
);
1056 if (d
< best_diversity
)
1067 static struct grub_font_glyph
*
1068 grub_font_dup_glyph (struct grub_font_glyph
*glyph
)
1070 static struct grub_font_glyph
*ret
;
1071 ret
= grub_malloc (sizeof (*ret
) + (glyph
->width
* glyph
->height
+ 7) / 8);
1074 grub_memcpy (ret
, glyph
, sizeof (*ret
)
1075 + (glyph
->width
* glyph
->height
+ 7) / 8);
1079 /* FIXME: suboptimal. */
1081 grub_font_blit_glyph (struct grub_font_glyph
*target
,
1082 struct grub_font_glyph
*src
, unsigned dx
, unsigned dy
)
1084 unsigned src_bit
, tgt_bit
, src_byte
, tgt_byte
;
1086 for (i
= 0; i
< src
->height
; i
++)
1088 src_bit
= (src
->width
* i
) % 8;
1089 src_byte
= (src
->width
* i
) / 8;
1090 tgt_bit
= (target
->width
* (dy
+ i
) + dx
) % 8;
1091 tgt_byte
= (target
->width
* (dy
+ i
) + dx
) / 8;
1092 for (j
= 0; j
< src
->width
; j
++)
1094 target
->bitmap
[tgt_byte
] |= ((src
->bitmap
[src_byte
] << src_bit
)
1113 grub_font_blit_glyph_mirror (struct grub_font_glyph
*target
,
1114 struct grub_font_glyph
*src
,
1115 unsigned dx
, unsigned dy
)
1117 unsigned tgt_bit
, src_byte
, tgt_byte
;
1120 for (i
= 0; i
< src
->height
; i
++)
1122 src_bit
= (src
->width
* i
+ src
->width
- 1) % 8;
1123 src_byte
= (src
->width
* i
+ src
->width
- 1) / 8;
1124 tgt_bit
= (target
->width
* (dy
+ i
) + dx
) % 8;
1125 tgt_byte
= (target
->width
* (dy
+ i
) + dx
) / 8;
1126 for (j
= 0; j
< src
->width
; j
++)
1128 target
->bitmap
[tgt_byte
] |= ((src
->bitmap
[src_byte
] << src_bit
)
1146 /* Context for blit_comb. */
1147 struct blit_comb_ctx
1149 struct grub_font_glyph
*glyph
;
1151 struct grub_video_signed_rect bounds
;
1154 /* Helper for blit_comb. */
1156 do_blit (struct grub_font_glyph
*src
, signed dx
, signed dy
,
1157 struct blit_comb_ctx
*ctx
)
1160 grub_font_blit_glyph (ctx
->glyph
, src
, dx
- ctx
->glyph
->offset_x
,
1161 (ctx
->glyph
->height
+ ctx
->glyph
->offset_y
) + dy
);
1162 if (dx
< ctx
->bounds
.x
)
1164 ctx
->bounds
.width
+= ctx
->bounds
.x
- dx
;
1167 if (ctx
->bounds
.y
> -src
->height
- dy
)
1169 ctx
->bounds
.height
+= ctx
->bounds
.y
- (-src
->height
- dy
);
1170 ctx
->bounds
.y
= (-src
->height
- dy
);
1172 if (dx
+ src
->width
- ctx
->bounds
.x
>= (signed) ctx
->bounds
.width
)
1173 ctx
->bounds
.width
= dx
+ src
->width
- ctx
->bounds
.x
+ 1;
1174 if ((signed) ctx
->bounds
.height
< src
->height
+ (-src
->height
- dy
)
1176 ctx
->bounds
.height
= src
->height
+ (-src
->height
- dy
) - ctx
->bounds
.y
;
1179 /* Helper for blit_comb. */
1181 add_device_width (int val
, struct blit_comb_ctx
*ctx
)
1184 ctx
->glyph
->device_width
+= val
;
1185 if (ctx
->device_width
)
1186 *ctx
->device_width
+= val
;
1190 blit_comb (const struct grub_unicode_glyph
*glyph_id
,
1191 struct grub_font_glyph
*glyph
,
1192 struct grub_video_signed_rect
*bounds_out
,
1193 struct grub_font_glyph
*main_glyph
,
1194 struct grub_font_glyph
**combining_glyphs
, int *device_width
)
1196 struct blit_comb_ctx ctx
= {
1198 .device_width
= device_width
1201 signed above_rightx
, above_righty
;
1202 signed above_leftx
, above_lefty
;
1203 signed below_rightx
, below_righty
;
1204 signed min_devwidth
= 0;
1207 glyph
->device_width
= main_glyph
->device_width
;
1209 *device_width
= main_glyph
->device_width
;
1211 ctx
.bounds
.x
= main_glyph
->offset_x
;
1212 ctx
.bounds
.y
= main_glyph
->offset_y
;
1213 ctx
.bounds
.width
= main_glyph
->width
;
1214 ctx
.bounds
.height
= main_glyph
->height
;
1216 above_rightx
= main_glyph
->offset_x
+ main_glyph
->width
;
1217 above_righty
= ctx
.bounds
.y
+ ctx
.bounds
.height
;
1219 above_leftx
= main_glyph
->offset_x
;
1220 above_lefty
= ctx
.bounds
.y
+ ctx
.bounds
.height
;
1222 below_rightx
= ctx
.bounds
.x
+ ctx
.bounds
.width
;
1223 below_righty
= ctx
.bounds
.y
;
1225 for (i
= 0; i
< glyph_id
->ncomb
; i
++)
1227 grub_int16_t space
= 0;
1228 /* Center by default. */
1229 grub_int16_t targetx
;
1231 if (!combining_glyphs
[i
])
1233 targetx
= (ctx
.bounds
.width
- combining_glyphs
[i
]->width
) / 2 + ctx
.bounds
.x
;
1234 /* CGJ is to avoid diacritics reordering. */
1235 if (glyph_id
->combining
[i
].code
1236 == GRUB_UNICODE_COMBINING_GRAPHEME_JOINER
)
1238 switch (glyph_id
->combining
[i
].type
)
1240 case GRUB_UNICODE_COMB_OVERLAY
:
1241 do_blit (combining_glyphs
[i
],
1243 (ctx
.bounds
.height
- combining_glyphs
[i
]->height
) / 2
1244 - (ctx
.bounds
.height
+ ctx
.bounds
.y
), &ctx
);
1245 if (min_devwidth
< combining_glyphs
[i
]->width
)
1246 min_devwidth
= combining_glyphs
[i
]->width
;
1249 case GRUB_UNICODE_COMB_ATTACHED_ABOVE_RIGHT
:
1250 do_blit (combining_glyphs
[i
], above_rightx
, -above_righty
, &ctx
);
1251 above_rightx
+= combining_glyphs
[i
]->width
;
1254 case GRUB_UNICODE_COMB_ABOVE_RIGHT
:
1255 do_blit (combining_glyphs
[i
], above_rightx
,
1256 -(above_righty
+ combining_glyphs
[i
]->height
), &ctx
);
1257 above_rightx
+= combining_glyphs
[i
]->width
;
1260 case GRUB_UNICODE_COMB_ABOVE_LEFT
:
1261 above_leftx
-= combining_glyphs
[i
]->width
;
1262 do_blit (combining_glyphs
[i
], above_leftx
,
1263 -(above_lefty
+ combining_glyphs
[i
]->height
), &ctx
);
1266 case GRUB_UNICODE_COMB_BELOW_RIGHT
:
1267 do_blit (combining_glyphs
[i
], below_rightx
, below_righty
, &ctx
);
1268 below_rightx
+= combining_glyphs
[i
]->width
;
1271 case GRUB_UNICODE_COMB_HEBREW_HOLAM
:
1272 if (glyph_id
->base
!= GRUB_UNICODE_HEBREW_WAW
)
1274 main_glyph
->offset_x
- combining_glyphs
[i
]->width
-
1275 (combining_glyphs
[i
]->width
+ 3) / 4;
1278 case GRUB_UNICODE_COMB_HEBREW_SIN_DOT
:
1279 targetx
= main_glyph
->offset_x
+ combining_glyphs
[i
]->width
/ 4;
1282 case GRUB_UNICODE_COMB_HEBREW_SHIN_DOT
:
1284 main_glyph
->width
+ main_glyph
->offset_x
-
1285 combining_glyphs
[i
]->width
;
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;
1291 do_blit (combining_glyphs
[i
], targetx
,
1292 -(main_glyph
->height
+ main_glyph
->offset_y
+ space
1293 + combining_glyphs
[i
]->height
), &ctx
);
1294 if (min_devwidth
< combining_glyphs
[i
]->width
)
1295 min_devwidth
= combining_glyphs
[i
]->width
;
1298 /* TODO: Put dammah, fathah and alif nearer to shadda. */
1299 case GRUB_UNICODE_COMB_SYRIAC_SUPERSCRIPT_ALAPH
:
1300 case GRUB_UNICODE_COMB_ARABIC_DAMMAH
:
1301 case GRUB_UNICODE_COMB_ARABIC_DAMMATAN
:
1302 case GRUB_UNICODE_COMB_ARABIC_FATHATAN
:
1303 case GRUB_UNICODE_COMB_ARABIC_FATHAH
:
1304 case GRUB_UNICODE_COMB_ARABIC_SUPERSCRIPT_ALIF
:
1305 case GRUB_UNICODE_COMB_ARABIC_SUKUN
:
1306 case GRUB_UNICODE_COMB_ARABIC_SHADDA
:
1307 case GRUB_UNICODE_COMB_HEBREW_RAFE
:
1308 case GRUB_UNICODE_STACK_ABOVE
:
1310 space
= combining_glyphs
[i
]->offset_y
1311 - grub_font_get_xheight (combining_glyphs
[i
]->font
) - 1;
1313 space
= 1 + (grub_font_get_xheight (main_glyph
->font
)) / 8;
1315 case GRUB_UNICODE_STACK_ATTACHED_ABOVE
:
1316 do_blit (combining_glyphs
[i
], targetx
,
1317 -(ctx
.bounds
.height
+ ctx
.bounds
.y
+ space
1318 + combining_glyphs
[i
]->height
), &ctx
);
1319 if (min_devwidth
< combining_glyphs
[i
]->width
)
1320 min_devwidth
= combining_glyphs
[i
]->width
;
1323 case GRUB_UNICODE_COMB_HEBREW_DAGESH
:
1324 do_blit (combining_glyphs
[i
], targetx
,
1325 -(ctx
.bounds
.height
/ 2 + ctx
.bounds
.y
1326 + combining_glyphs
[i
]->height
/ 2), &ctx
);
1327 if (min_devwidth
< combining_glyphs
[i
]->width
)
1328 min_devwidth
= combining_glyphs
[i
]->width
;
1331 case GRUB_UNICODE_COMB_HEBREW_SHEVA
:
1332 case GRUB_UNICODE_COMB_HEBREW_HIRIQ
:
1333 case GRUB_UNICODE_COMB_HEBREW_QAMATS
:
1334 case GRUB_UNICODE_COMB_HEBREW_TSERE
:
1335 case GRUB_UNICODE_COMB_HEBREW_SEGOL
:
1336 /* TODO: placement in final kaf and under reish. */
1338 case GRUB_UNICODE_COMB_HEBREW_HATAF_SEGOL
:
1339 case GRUB_UNICODE_COMB_HEBREW_HATAF_PATAH
:
1340 case GRUB_UNICODE_COMB_HEBREW_HATAF_QAMATS
:
1341 case GRUB_UNICODE_COMB_HEBREW_PATAH
:
1342 case GRUB_UNICODE_COMB_HEBREW_QUBUTS
:
1343 case GRUB_UNICODE_COMB_HEBREW_METEG
:
1344 /* TODO: Put kasra and kasratan under shadda. */
1345 case GRUB_UNICODE_COMB_ARABIC_KASRA
:
1346 case GRUB_UNICODE_COMB_ARABIC_KASRATAN
:
1347 /* I don't know how ypogegrammeni differs from subscript. */
1348 case GRUB_UNICODE_COMB_YPOGEGRAMMENI
:
1349 case GRUB_UNICODE_STACK_BELOW
:
1351 space
= -(combining_glyphs
[i
]->offset_y
1352 + combining_glyphs
[i
]->height
);
1354 space
= 1 + (grub_font_get_xheight (main_glyph
->font
)) / 8;
1356 case GRUB_UNICODE_STACK_ATTACHED_BELOW
:
1357 do_blit (combining_glyphs
[i
], targetx
, -(ctx
.bounds
.y
- space
),
1359 if (min_devwidth
< combining_glyphs
[i
]->width
)
1360 min_devwidth
= combining_glyphs
[i
]->width
;
1363 case GRUB_UNICODE_COMB_MN
:
1364 switch (glyph_id
->combining
[i
].code
)
1366 case GRUB_UNICODE_THAANA_ABAFILI
:
1367 case GRUB_UNICODE_THAANA_AABAAFILI
:
1368 case GRUB_UNICODE_THAANA_UBUFILI
:
1369 case GRUB_UNICODE_THAANA_OOBOOFILI
:
1370 case GRUB_UNICODE_THAANA_EBEFILI
:
1371 case GRUB_UNICODE_THAANA_EYBEYFILI
:
1372 case GRUB_UNICODE_THAANA_OBOFILI
:
1373 case GRUB_UNICODE_THAANA_OABOAFILI
:
1374 case GRUB_UNICODE_THAANA_SUKUN
:
1376 case GRUB_UNICODE_THAANA_IBIFILI
:
1377 case GRUB_UNICODE_THAANA_EEBEEFILI
:
1383 /* Default handling. Just draw combining character on top
1385 FIXME: support more unicode types correctly.
1387 do_blit (combining_glyphs
[i
],
1388 main_glyph
->device_width
1389 + combining_glyphs
[i
]->offset_x
,
1390 -(combining_glyphs
[i
]->height
1391 + combining_glyphs
[i
]->offset_y
), &ctx
);
1392 add_device_width (combining_glyphs
[i
]->device_width
, &ctx
);
1396 add_device_width ((above_rightx
>
1397 below_rightx
? above_rightx
: below_rightx
) -
1398 (main_glyph
->offset_x
+ main_glyph
->width
), &ctx
);
1399 add_device_width (above_leftx
- main_glyph
->offset_x
, &ctx
);
1400 if (glyph
&& glyph
->device_width
< min_devwidth
)
1401 glyph
->device_width
= min_devwidth
;
1402 if (device_width
&& *device_width
< min_devwidth
)
1403 *device_width
= min_devwidth
;
1406 *bounds_out
= ctx
.bounds
;
1409 static struct grub_font_glyph
*
1410 grub_font_construct_dry_run (grub_font_t hinted_font
,
1411 const struct grub_unicode_glyph
*glyph_id
,
1412 struct grub_video_signed_rect
*bounds
,
1413 struct grub_font_glyph
***combining_glyphs_out
,
1416 struct grub_font_glyph
*main_glyph
= NULL
;
1417 struct grub_font_glyph
**combining_glyphs
;
1418 grub_uint32_t desired_attributes
= 0;
1420 grub_uint32_t base
= glyph_id
->base
;
1422 if (combining_glyphs_out
)
1423 *combining_glyphs_out
= NULL
;
1425 if (glyph_id
->attributes
& GRUB_UNICODE_GLYPH_ATTRIBUTE_RIGHT_JOINED
)
1426 desired_attributes
|= GRUB_FONT_CODE_RIGHT_JOINED
;
1428 if (glyph_id
->attributes
& GRUB_UNICODE_GLYPH_ATTRIBUTE_LEFT_JOINED
)
1429 desired_attributes
|= GRUB_FONT_CODE_LEFT_JOINED
;
1432 if (base
== 'i' || base
== 'j')
1434 for (i
= 0; i
< glyph_id
->ncomb
; i
++)
1435 if (glyph_id
->combining
[i
].type
== GRUB_UNICODE_STACK_ABOVE
)
1437 if (i
< glyph_id
->ncomb
&& base
== 'i')
1438 base
= GRUB_UNICODE_DOTLESS_LOWERCASE_I
;
1439 if (i
< glyph_id
->ncomb
&& base
== 'j')
1440 base
= GRUB_UNICODE_DOTLESS_LOWERCASE_J
;
1443 main_glyph
= grub_font_get_glyph_with_fallback (hinted_font
, base
1444 | desired_attributes
);
1447 main_glyph
= grub_font_get_glyph_with_fallback (hinted_font
,
1450 /* Glyph not available in any font. Use ASCII fallback. */
1452 main_glyph
= ascii_glyph_lookup (base
);
1454 /* Glyph not available in any font. Return unknown glyph. */
1459 *device_width
= main_glyph
->device_width
;
1461 if (!glyph_id
->ncomb
&& !glyph_id
->attributes
)
1464 combining_glyphs
= grub_malloc (sizeof (combining_glyphs
[0])
1466 if (glyph_id
->ncomb
&& !combining_glyphs
)
1468 grub_errno
= GRUB_ERR_NONE
;
1472 for (i
= 0; i
< glyph_id
->ncomb
; i
++)
1474 = grub_font_get_glyph_with_fallback (main_glyph
->font
,
1475 glyph_id
->combining
[i
].code
);
1477 blit_comb (glyph_id
, NULL
, bounds
, main_glyph
, combining_glyphs
,
1479 if (combining_glyphs_out
)
1480 *combining_glyphs_out
= combining_glyphs
;
1482 grub_free (combining_glyphs
);
1488 grub_font_get_constructed_device_width (grub_font_t hinted_font
,
1489 const struct grub_unicode_glyph
1493 struct grub_font_glyph
*main_glyph
;
1494 main_glyph
= grub_font_construct_dry_run (hinted_font
, glyph_id
, NULL
,
1497 return unknown_glyph
->device_width
;
1501 struct grub_font_glyph
*
1502 grub_font_construct_glyph (grub_font_t hinted_font
,
1503 const struct grub_unicode_glyph
*glyph_id
)
1505 struct grub_font_glyph
*main_glyph
;
1506 struct grub_video_signed_rect bounds
;
1507 struct grub_font_glyph
*glyph
;
1508 struct grub_font_glyph
**combining_glyphs
;
1510 main_glyph
= grub_font_construct_dry_run (hinted_font
, glyph_id
,
1511 &bounds
, &combining_glyphs
, NULL
);
1514 return grub_font_dup_glyph (unknown_glyph
);
1516 if (!combining_glyphs
)
1517 return grub_font_dup_glyph (main_glyph
);
1520 grub_zalloc (sizeof (*glyph
) + (bounds
.width
* bounds
.height
+ 7) / 8);
1523 grub_errno
= GRUB_ERR_NONE
;
1524 return grub_font_dup_glyph (main_glyph
);
1527 glyph
->font
= main_glyph
->font
;
1528 glyph
->width
= bounds
.width
;
1529 glyph
->height
= bounds
.height
;
1530 glyph
->offset_x
= bounds
.x
;
1531 glyph
->offset_y
= bounds
.y
;
1533 if (glyph_id
->attributes
& GRUB_UNICODE_GLYPH_ATTRIBUTE_MIRROR
)
1534 grub_font_blit_glyph_mirror (glyph
, main_glyph
,
1535 main_glyph
->offset_x
- glyph
->offset_x
,
1536 (glyph
->height
+ glyph
->offset_y
)
1537 - (main_glyph
->height
+
1538 main_glyph
->offset_y
));
1540 grub_font_blit_glyph (glyph
, main_glyph
,
1541 main_glyph
->offset_x
- glyph
->offset_x
,
1542 (glyph
->height
+ glyph
->offset_y
)
1543 - (main_glyph
->height
+ main_glyph
->offset_y
));
1545 blit_comb (glyph_id
, glyph
, NULL
, main_glyph
, combining_glyphs
, NULL
);
1550 /* Draw the specified glyph at (x, y). The y coordinate designates the
1551 baseline of the character, while the x coordinate designates the left
1552 side location of the character. */
1554 grub_font_draw_glyph (struct grub_font_glyph
* glyph
,
1555 grub_video_color_t color
, int left_x
, int baseline_y
)
1557 struct grub_video_bitmap glyph_bitmap
;
1559 /* Don't try to draw empty glyphs (U+0020, etc.). */
1560 if (glyph
->width
== 0 || glyph
->height
== 0)
1561 return GRUB_ERR_NONE
;
1563 glyph_bitmap
.mode_info
.width
= glyph
->width
;
1564 glyph_bitmap
.mode_info
.height
= glyph
->height
;
1565 glyph_bitmap
.mode_info
.mode_type
1566 = (1 << GRUB_VIDEO_MODE_TYPE_DEPTH_POS
) | GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP
;
1567 glyph_bitmap
.mode_info
.blit_format
= GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED
;
1568 glyph_bitmap
.mode_info
.bpp
= 1;
1570 /* Really 1 bit per pixel. */
1571 glyph_bitmap
.mode_info
.bytes_per_pixel
= 0;
1573 /* Packed densely as bits. */
1574 glyph_bitmap
.mode_info
.pitch
= glyph
->width
;
1576 glyph_bitmap
.mode_info
.number_of_colors
= 2;
1577 glyph_bitmap
.mode_info
.bg_red
= 0;
1578 glyph_bitmap
.mode_info
.bg_green
= 0;
1579 glyph_bitmap
.mode_info
.bg_blue
= 0;
1580 glyph_bitmap
.mode_info
.bg_alpha
= 0;
1581 grub_video_unmap_color (color
,
1582 &glyph_bitmap
.mode_info
.fg_red
,
1583 &glyph_bitmap
.mode_info
.fg_green
,
1584 &glyph_bitmap
.mode_info
.fg_blue
,
1585 &glyph_bitmap
.mode_info
.fg_alpha
);
1586 glyph_bitmap
.data
= glyph
->bitmap
;
1588 int bitmap_left
= left_x
+ glyph
->offset_x
;
1589 int bitmap_bottom
= baseline_y
- glyph
->offset_y
;
1590 int bitmap_top
= bitmap_bottom
- glyph
->height
;
1592 return grub_video_blit_bitmap (&glyph_bitmap
, GRUB_VIDEO_BLIT_BLEND
,
1593 bitmap_left
, bitmap_top
,
1594 0, 0, glyph
->width
, glyph
->height
);