]> glassweightruler.freedombox.rocks Git - Ventoy.git/blob - GRUB2/MOD_SRC/grub-2.04/grub-core/font/font.c
Update Hindi Translation (#1941)
[Ventoy.git] / GRUB2 / MOD_SRC / grub-2.04 / grub-core / font / font.c
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.
5 *
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.
10 *
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.
15 *
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/>.
18 */
19
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>
33
34 GRUB_MOD_LICENSE ("GPLv3+");
35
36 #if HAVE_FONT_SOURCE
37 #include "ascii.h"
38 #endif
39
40 #ifndef FONT_DEBUG
41 #define FONT_DEBUG 0
42 #endif
43
44 struct char_index_entry
45 {
46 grub_uint32_t code;
47 grub_uint8_t storage_flags;
48 grub_uint32_t offset;
49
50 /* Glyph if loaded, or NULL otherwise. */
51 struct grub_font_glyph *glyph;
52 };
53
54 #define FONT_WEIGHT_NORMAL 100
55 #define FONT_WEIGHT_BOLD 200
56 #define ASCII_BITMAP_SIZE 16
57
58 /* Definition of font registry. */
59 struct grub_font_node *grub_font_list;
60
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);
65
66 struct font_file_section
67 {
68 /* The file this section is in. */
69 grub_file_t file;
70
71 /* FOURCC name of the section. */
72 char name[4];
73
74 /* Length of the section contents. */
75 grub_uint32_t length;
76
77 /* Set by open_section() on EOF. */
78 int eof;
79 };
80
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 /* */
100 };
101
102 /* The "unknown glyph" glyph, used as a last resort. */
103 static struct grub_font_glyph *unknown_glyph;
104
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;
109
110 /* Flag to ensure module is initialized only once. */
111 static grub_uint8_t font_loader_initialized;
112
113 #if HAVE_FONT_SOURCE
114 static struct grub_font_glyph *ascii_font_glyph[0x80];
115 #endif
116
117 static struct grub_font_glyph *
118 ascii_glyph_lookup (grub_uint32_t code)
119 {
120 #if HAVE_FONT_SOURCE
121 static int ascii_failback_initialized = 0;
122
123 if (code >= 0x80)
124 return NULL;
125
126 if (ascii_failback_initialized == 0)
127 {
128 int current;
129 for (current = 0; current < 0x80; current++)
130 {
131 ascii_font_glyph[current] =
132 grub_malloc (sizeof (struct grub_font_glyph) + ASCII_BITMAP_SIZE);
133
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;
140
141 grub_memcpy (ascii_font_glyph[current]->bitmap,
142 &ascii_bitmaps[current * ASCII_BITMAP_SIZE],
143 ASCII_BITMAP_SIZE);
144 }
145
146 ascii_failback_initialized = 1;
147 }
148
149 return ascii_font_glyph[code];
150 #else
151 (void) code;
152 return NULL;
153 #endif
154 }
155
156 void
157 grub_font_loader_init (void)
158 {
159 /* Only initialize font loader once. */
160 if (font_loader_initialized)
161 return;
162
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;
168
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));
176
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;
185
186 font_loader_initialized = 1;
187 }
188
189 /* Initialize the font object with initial default values. */
190 static void
191 font_init (grub_font_t font)
192 {
193 font->name = 0;
194 font->file = 0;
195 font->family = 0;
196 font->point_size = 0;
197 font->weight = 0;
198
199 /* Default leading value, not in font file yet. */
200 font->leading = 1;
201
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;
209 }
210
211 /* Open the next section in the file.
212
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.
216
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)
221 {
222 grub_ssize_t retval;
223 grub_uint32_t raw_length;
224
225 section->file = file;
226 section->eof = 0;
227
228 /* Read the FOURCC section name. */
229 retval = grub_file_read (file, section->name, 4);
230 if (retval >= 0 && retval < 4)
231 {
232 /* EOF encountered. */
233 section->eof = 1;
234 return 1;
235 }
236 else if (retval < 0)
237 {
238 /* Read error. */
239 return 1;
240 }
241
242 /* Read the big-endian 32-bit section length. */
243 retval = grub_file_read (file, &raw_length, 4);
244 if (retval >= 0 && retval < 4)
245 {
246 /* EOF encountered. */
247 section->eof = 1;
248 return 1;
249 }
250 else if (retval < 0)
251 {
252 /* Read error. */
253 return 1;
254 }
255
256 /* Convert byte-order and store in *length. */
257 section->length = grub_be_to_cpu32 (raw_length);
258
259 return 0;
260 }
261
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)
265
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)
274 {
275 unsigned i;
276 grub_uint32_t last_code;
277
278 #if FONT_DEBUG >= 2
279 grub_dprintf ("font", "load_font_index(sect_length=%d)\n", sect_length);
280 #endif
281
282 /* Sanity check: ensure section length is divisible by the entry size. */
283 if ((sect_length % FONT_CHAR_INDEX_ENTRY_SIZE) != 0)
284 {
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;
290 }
291
292 /* Calculate the number of characters. */
293 font->num_chars = sect_length / FONT_CHAR_INDEX_ENTRY_SIZE;
294
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));
304
305
306 #if FONT_DEBUG >= 2
307 grub_dprintf ("font", "num_chars=%d)\n", font->num_chars);
308 #endif
309
310 last_code = 0;
311
312 /* Load the character index data from the file. */
313 for (i = 0; i < font->num_chars; i++)
314 {
315 struct char_index_entry *entry = &font->char_index[i];
316
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);
321
322 /* Verify that characters are in ascending order. */
323 if (i != 0 && entry->code <= last_code)
324 {
325 grub_error (GRUB_ERR_BAD_FONT,
326 "font characters not in ascending order: %u <= %u",
327 entry->code, last_code);
328 return 1;
329 }
330
331 if (entry->code < 0x10000)
332 font->bmp_idx[entry->code] = i;
333
334 last_code = entry->code;
335
336 /* Read storage flags byte. */
337 if (grub_file_read (file, &entry->storage_flags, 1) != 1)
338 return 1;
339
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);
344
345 /* No glyph loaded. Will be loaded on demand and cached thereafter. */
346 entry->glyph = 0;
347
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
353 }
354
355 return 0;
356 }
357
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)
362 {
363 char *str;
364 grub_ssize_t ret;
365
366 str = grub_malloc (section->length + 1);
367 if (!str)
368 return 0;
369
370 ret = grub_file_read (section->file, str, section->length);
371 if (ret < 0 || ret != (grub_ssize_t) section->length)
372 {
373 grub_free (str);
374 return 0;
375 }
376
377 str[section->length] = '\0';
378 return str;
379 }
380
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)
387 {
388 grub_uint16_t raw_value;
389
390 if (section->length != 2)
391 {
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;
398 }
399 if (grub_file_read (section->file, &raw_value, 2) != 2)
400 return 1;
401
402 *value = grub_be_to_cpu16 (raw_value);
403 return 0;
404 }
405
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)
410 {
411 grub_file_t file = 0;
412 struct font_file_section section;
413 char magic[4];
414 grub_font_t font = 0;
415
416 #if FONT_DEBUG >= 1
417 grub_dprintf ("font", "add_font(%s)\n", filename);
418 #endif
419
420 if (filename[0] == '(' || filename[0] == '/' || filename[0] == '+')
421 file = grub_buffile_open (filename, GRUB_FILE_TYPE_FONT, 1024);
422 else if (grub_strncmp(filename, "mem:", 4) == 0)
423 file = grub_buffile_open (filename, GRUB_FILE_TYPE_FONT, 1024);
424 else
425 {
426 const char *prefix = grub_env_get ("prefix");
427 char *fullname, *ptr;
428 if (!prefix)
429 {
430 grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't set"),
431 "prefix");
432 goto fail;
433 }
434 fullname = grub_malloc (grub_strlen (prefix) + grub_strlen (filename) + 1
435 + sizeof ("/fonts/") + sizeof (".pf2"));
436 if (!fullname)
437 goto fail;
438 ptr = grub_stpcpy (fullname, prefix);
439 ptr = grub_stpcpy (ptr, "/fonts/");
440 ptr = grub_stpcpy (ptr, filename);
441 ptr = grub_stpcpy (ptr, ".pf2");
442 *ptr = 0;
443 file = grub_buffile_open (fullname, GRUB_FILE_TYPE_FONT, 1024);
444 grub_free (fullname);
445 }
446 if (!file)
447 goto fail;
448
449 #if FONT_DEBUG >= 3
450 grub_dprintf ("font", "file opened\n");
451 #endif
452
453 /* Read the FILE section. It indicates the file format. */
454 if (open_section (file, &section) != 0)
455 goto fail;
456
457 #if FONT_DEBUG >= 3
458 grub_dprintf ("font", "opened FILE section\n");
459 #endif
460 if (grub_memcmp (section.name, FONT_FORMAT_SECTION_NAMES_FILE,
461 sizeof (FONT_FORMAT_SECTION_NAMES_FILE) - 1) != 0)
462 {
463 grub_error (GRUB_ERR_BAD_FONT,
464 "font file format error: 1st section must be FILE");
465 goto fail;
466 }
467
468 #if FONT_DEBUG >= 3
469 grub_dprintf ("font", "section name ok\n");
470 #endif
471 if (section.length != 4)
472 {
473 grub_error (GRUB_ERR_BAD_FONT,
474 "font file format error (file type ID length is %d "
475 "but should be 4)", section.length);
476 goto fail;
477 }
478
479 #if FONT_DEBUG >= 3
480 grub_dprintf ("font", "section length ok\n");
481 #endif
482 /* Check the file format type code. */
483 if (grub_file_read (file, magic, 4) != 4)
484 goto fail;
485
486 #if FONT_DEBUG >= 3
487 grub_dprintf ("font", "read magic ok\n");
488 #endif
489
490 if (grub_memcmp (magic, FONT_FORMAT_PFF2_MAGIC, 4) != 0)
491 {
492 grub_error (GRUB_ERR_BAD_FONT, "invalid font magic %x %x %x %x",
493 magic[0], magic[1], magic[2], magic[3]);
494 goto fail;
495 }
496
497 #if FONT_DEBUG >= 3
498 grub_dprintf ("font", "compare magic ok\n");
499 #endif
500
501 /* Allocate the font object. */
502 font = (grub_font_t) grub_zalloc (sizeof (struct grub_font));
503 if (!font)
504 goto fail;
505
506 font_init (font);
507 font->file = file;
508
509 #if FONT_DEBUG >= 3
510 grub_dprintf ("font", "allocate font ok; loading font info\n");
511 #endif
512
513 /* Load the font information. */
514 while (1)
515 {
516 if (open_section (file, &section) != 0)
517 {
518 if (section.eof)
519 break; /* Done reading the font file. */
520 else
521 goto fail;
522 }
523
524 #if FONT_DEBUG >= 2
525 grub_dprintf ("font", "opened section %c%c%c%c ok\n",
526 section.name[0], section.name[1],
527 section.name[2], section.name[3]);
528 #endif
529
530 if (grub_memcmp (section.name, FONT_FORMAT_SECTION_NAMES_FONT_NAME,
531 sizeof (FONT_FORMAT_SECTION_NAMES_FONT_NAME) - 1) == 0)
532 {
533 font->name = read_section_as_string (&section);
534 if (!font->name)
535 goto fail;
536 }
537 else if (grub_memcmp (section.name,
538 FONT_FORMAT_SECTION_NAMES_POINT_SIZE,
539 sizeof (FONT_FORMAT_SECTION_NAMES_POINT_SIZE) -
540 1) == 0)
541 {
542 if (read_section_as_short (&section, &font->point_size) != 0)
543 goto fail;
544 }
545 else if (grub_memcmp (section.name, FONT_FORMAT_SECTION_NAMES_WEIGHT,
546 sizeof (FONT_FORMAT_SECTION_NAMES_WEIGHT) - 1)
547 == 0)
548 {
549 char *wt;
550 wt = read_section_as_string (&section);
551 if (!wt)
552 continue;
553 /* Convert the weight string 'normal' or 'bold' into a number. */
554 if (grub_strcmp (wt, "normal") == 0)
555 font->weight = FONT_WEIGHT_NORMAL;
556 else if (grub_strcmp (wt, "bold") == 0)
557 font->weight = FONT_WEIGHT_BOLD;
558 grub_free (wt);
559 }
560 else if (grub_memcmp (section.name,
561 FONT_FORMAT_SECTION_NAMES_MAX_CHAR_WIDTH,
562 sizeof (FONT_FORMAT_SECTION_NAMES_MAX_CHAR_WIDTH)
563 - 1) == 0)
564 {
565 if (read_section_as_short (&section, &font->max_char_width) != 0)
566 goto fail;
567 }
568 else if (grub_memcmp (section.name,
569 FONT_FORMAT_SECTION_NAMES_MAX_CHAR_HEIGHT,
570 sizeof (FONT_FORMAT_SECTION_NAMES_MAX_CHAR_HEIGHT)
571 - 1) == 0)
572 {
573 if (read_section_as_short (&section, &font->max_char_height) != 0)
574 goto fail;
575 }
576 else if (grub_memcmp (section.name,
577 FONT_FORMAT_SECTION_NAMES_ASCENT,
578 sizeof (FONT_FORMAT_SECTION_NAMES_ASCENT) - 1)
579 == 0)
580 {
581 if (read_section_as_short (&section, &font->ascent) != 0)
582 goto fail;
583 }
584 else if (grub_memcmp (section.name, FONT_FORMAT_SECTION_NAMES_DESCENT,
585 sizeof (FONT_FORMAT_SECTION_NAMES_DESCENT) - 1)
586 == 0)
587 {
588 if (read_section_as_short (&section, &font->descent) != 0)
589 goto fail;
590 }
591 else if (grub_memcmp (section.name,
592 FONT_FORMAT_SECTION_NAMES_CHAR_INDEX,
593 sizeof (FONT_FORMAT_SECTION_NAMES_CHAR_INDEX) -
594 1) == 0)
595 {
596 if (load_font_index (file, section.length, font) != 0)
597 goto fail;
598 }
599 else if (grub_memcmp (section.name, FONT_FORMAT_SECTION_NAMES_DATA,
600 sizeof (FONT_FORMAT_SECTION_NAMES_DATA) - 1) == 0)
601 {
602 /* When the DATA section marker is reached, we stop reading. */
603 break;
604 }
605 else
606 {
607 /* Unhandled section type, simply skip past it. */
608 #if FONT_DEBUG >= 3
609 grub_dprintf ("font", "Unhandled section type, skipping.\n");
610 #endif
611 grub_off_t section_end = grub_file_tell (file) + section.length;
612 if ((int) grub_file_seek (file, section_end) == -1)
613 goto fail;
614 }
615 }
616
617 if (!font->name)
618 {
619 grub_dprintf ("font", "Font has no name.\n");
620 font->name = grub_strdup ("Unknown");
621 }
622
623 #if FONT_DEBUG >= 1
624 grub_dprintf ("font", "Loaded font `%s'.\n"
625 "Ascent=%d Descent=%d MaxW=%d MaxH=%d Number of characters=%d.\n",
626 font->name,
627 font->ascent, font->descent,
628 font->max_char_width, font->max_char_height, font->num_chars);
629 #endif
630
631 if (font->max_char_width == 0
632 || font->max_char_height == 0
633 || font->num_chars == 0
634 || font->char_index == 0 || font->ascent == 0 || font->descent == 0)
635 {
636 grub_error (GRUB_ERR_BAD_FONT,
637 "invalid font file: missing some required data");
638 goto fail;
639 }
640
641 /* Add the font to the global font registry. */
642 if (register_font (font) != 0)
643 goto fail;
644
645 return font;
646
647 fail:
648 if (file)
649 grub_file_close (file);
650 if (font)
651 font->file = 0;
652
653 free_font (font);
654 return 0;
655 }
656
657 /* Read a 16-bit big-endian integer from FILE, convert it to native byte
658 order, and store it in *VALUE.
659 Returns 0 on success, 1 on failure. */
660 static int
661 read_be_uint16 (grub_file_t file, grub_uint16_t * value)
662 {
663 if (grub_file_read (file, value, 2) != 2)
664 return 1;
665 *value = grub_be_to_cpu16 (*value);
666 return 0;
667 }
668
669 static int
670 read_be_int16 (grub_file_t file, grub_int16_t * value)
671 {
672 /* For the signed integer version, use the same code as for unsigned. */
673 return read_be_uint16 (file, (grub_uint16_t *) value);
674 }
675
676 /* Return a pointer to the character index entry for the glyph corresponding to
677 the codepoint CODE in the font FONT. If not found, return zero. */
678 static inline struct char_index_entry *
679 find_glyph (const grub_font_t font, grub_uint32_t code)
680 {
681 struct char_index_entry *table;
682 grub_size_t lo;
683 grub_size_t hi;
684 grub_size_t mid;
685
686 table = font->char_index;
687
688 /* Use BMP index if possible. */
689 if (code < 0x10000 && font->bmp_idx)
690 {
691 if (font->bmp_idx[code] == 0xffff)
692 return 0;
693 return &table[font->bmp_idx[code]];
694 }
695
696 /* Do a binary search in `char_index', which is ordered by code point. */
697 lo = 0;
698 hi = font->num_chars - 1;
699
700 if (!table)
701 return 0;
702
703 while (lo <= hi)
704 {
705 mid = lo + (hi - lo) / 2;
706 if (code < table[mid].code)
707 hi = mid - 1;
708 else if (code > table[mid].code)
709 lo = mid + 1;
710 else
711 return &table[mid];
712 }
713
714 return 0;
715 }
716
717 /* Get a glyph for the Unicode character CODE in FONT. The glyph is loaded
718 from the font file if has not been loaded yet.
719 Returns a pointer to the glyph if found, or 0 if it is not found. */
720 static struct grub_font_glyph *
721 grub_font_get_glyph_internal (grub_font_t font, grub_uint32_t code)
722 {
723 struct char_index_entry *index_entry;
724
725 index_entry = find_glyph (font, code);
726 if (index_entry)
727 {
728 struct grub_font_glyph *glyph = 0;
729 grub_uint16_t width;
730 grub_uint16_t height;
731 grub_int16_t xoff;
732 grub_int16_t yoff;
733 grub_int16_t dwidth;
734 int len;
735
736 if (index_entry->glyph)
737 /* Return cached glyph. */
738 return index_entry->glyph;
739
740 if (!font->file)
741 /* No open file, can't load any glyphs. */
742 return 0;
743
744 /* Make sure we can find glyphs for error messages. Push active
745 error message to error stack and reset error message. */
746 grub_error_push ();
747
748 grub_file_seek (font->file, index_entry->offset);
749
750 /* Read the glyph width, height, and baseline. */
751 if (read_be_uint16 (font->file, &width) != 0
752 || read_be_uint16 (font->file, &height) != 0
753 || read_be_int16 (font->file, &xoff) != 0
754 || read_be_int16 (font->file, &yoff) != 0
755 || read_be_int16 (font->file, &dwidth) != 0)
756 {
757 remove_font (font);
758 return 0;
759 }
760
761 len = (width * height + 7) / 8;
762 glyph = grub_malloc (sizeof (struct grub_font_glyph) + len);
763 if (!glyph)
764 {
765 remove_font (font);
766 return 0;
767 }
768
769 glyph->font = font;
770 glyph->width = width;
771 glyph->height = height;
772 glyph->offset_x = xoff;
773 glyph->offset_y = yoff;
774 glyph->device_width = dwidth;
775
776 /* Don't try to read empty bitmaps (e.g., space characters). */
777 if (len != 0)
778 {
779 if (grub_file_read (font->file, glyph->bitmap, len) != len)
780 {
781 remove_font (font);
782 grub_free (glyph);
783 return 0;
784 }
785 }
786
787 /* Restore old error message. */
788 grub_error_pop ();
789
790 /* Cache the glyph. */
791 index_entry->glyph = glyph;
792
793 return glyph;
794 }
795
796 return 0;
797 }
798
799 /* Free the memory used by FONT.
800 This should not be called if the font has been made available to
801 users (once it is added to the global font list), since there would
802 be the possibility of a dangling pointer. */
803 static void
804 free_font (grub_font_t font)
805 {
806 if (font)
807 {
808 if (font->file)
809 grub_file_close (font->file);
810 grub_free (font->name);
811 grub_free (font->family);
812 grub_free (font->char_index);
813 grub_free (font->bmp_idx);
814 grub_free (font);
815 }
816 }
817
818 /* Add FONT to the global font registry.
819 Returns 0 upon success, nonzero on failure
820 (the font was not registered). */
821 static int
822 register_font (grub_font_t font)
823 {
824 struct grub_font_node *node = 0;
825
826 node = grub_malloc (sizeof (struct grub_font_node));
827 if (!node)
828 return 1;
829
830 node->value = font;
831 node->next = grub_font_list;
832 grub_font_list = node;
833
834 return 0;
835 }
836
837 /* Remove the font from the global font list. We don't actually free the
838 font's memory since users could be holding references to the font. */
839 static void
840 remove_font (grub_font_t font)
841 {
842 struct grub_font_node **nextp, *cur;
843
844 for (nextp = &grub_font_list, cur = *nextp;
845 cur; nextp = &cur->next, cur = cur->next)
846 {
847 if (cur->value == font)
848 {
849 *nextp = cur->next;
850
851 /* Free the node, but not the font itself. */
852 grub_free (cur);
853
854 return;
855 }
856 }
857 }
858
859 /* Get a font from the list of loaded fonts. This function will return
860 another font if the requested font is not available. If no fonts are
861 loaded, then a special 'null font' is returned, which contains no glyphs,
862 but is not a null pointer so the caller may omit checks for NULL. */
863 grub_font_t
864 grub_font_get (const char *font_name)
865 {
866 struct grub_font_node *node;
867
868 for (node = grub_font_list; node; node = node->next)
869 {
870 grub_font_t font = node->value;
871 if (grub_strcmp (font->name, font_name) == 0)
872 return font;
873 }
874
875 /* If no font by that name is found, return the first font in the list
876 as a fallback. */
877 if (grub_font_list && grub_font_list->value)
878 return grub_font_list->value;
879 else
880 /* The null_font is a last resort. */
881 return &null_font;
882 }
883
884 /* Get the full name of the font. */
885 const char *
886 grub_font_get_name (grub_font_t font)
887 {
888 return font->name;
889 }
890
891 /* Get the maximum width of any character in the font in pixels. */
892 int
893 grub_font_get_max_char_width (grub_font_t font)
894 {
895 return font->max_char_width;
896 }
897
898 /* Get the distance in pixels from the baseline to the lowest descenders
899 (for instance, in a lowercase 'y', 'g', etc.). */
900 int
901 grub_font_get_descent (grub_font_t font)
902 {
903 return font->descent;
904 }
905
906 /* FIXME: not correct for all fonts. */
907 int
908 grub_font_get_xheight (grub_font_t font)
909 {
910 return font->ascent / 2;
911 }
912
913 /* Get the *standard leading* of the font in pixel, which is the spacing
914 between two lines of text. Specifically, it is the space between the
915 descent of one line and the ascent of the next line. This is included
916 in the *height* metric. */
917 int
918 grub_font_get_leading (grub_font_t font)
919 {
920 return font->leading;
921 }
922
923 /* Get the distance in pixels between baselines of adjacent lines of text. */
924 int
925 grub_font_get_height (grub_font_t font)
926 {
927 return font->ascent + font->descent + font->leading;
928 }
929
930 /* Get the glyph for FONT corresponding to the Unicode code point CODE.
931 Returns the ASCII glyph for the code if no other fonts are available.
932 The glyphs are cached once loaded. */
933 struct grub_font_glyph *
934 grub_font_get_glyph (grub_font_t font, grub_uint32_t code)
935 {
936 struct grub_font_glyph *glyph = 0;
937 if (font)
938 glyph = grub_font_get_glyph_internal (font, code);
939 if (glyph == 0)
940 {
941 glyph = ascii_glyph_lookup (code);
942 }
943 return glyph;
944 }
945
946
947 /* Calculate a subject value representing "how similar" two fonts are.
948 This is used to prioritize the order that fonts are scanned for missing
949 glyphs. The object is to select glyphs from the most similar font
950 possible, for the best appearance.
951 The heuristic is crude, but it helps greatly when fonts of similar
952 sizes are used so that tiny 8 point glyphs are not mixed into a string
953 of 24 point text unless there is no other choice. */
954 static int
955 get_font_diversity (grub_font_t a, grub_font_t b)
956 {
957 int d;
958
959 d = 0;
960
961 if (a->ascent && b->ascent)
962 d += grub_abs (a->ascent - b->ascent) * 8;
963 else
964 /* Penalty for missing attributes. */
965 d += 50;
966
967 if (a->max_char_height && b->max_char_height)
968 d += grub_abs (a->max_char_height - b->max_char_height) * 8;
969 else
970 /* Penalty for missing attributes. */
971 d += 50;
972
973 /* Weight is a minor factor. */
974 d += (a->weight != b->weight) ? 5 : 0;
975
976 return d;
977 }
978
979 /* Get a glyph corresponding to the codepoint CODE. If FONT contains the
980 specified glyph, then it is returned. Otherwise, all other loaded fonts
981 are searched until one is found that contains a glyph for CODE.
982 If no glyph is available for CODE in the loaded fonts, then a glyph
983 representing an unknown character is returned.
984 This function never returns NULL.
985 The returned glyph is owned by the font manager and should not be freed
986 by the caller. The glyphs are cached. */
987 struct grub_font_glyph *
988 grub_font_get_glyph_with_fallback (grub_font_t font, grub_uint32_t code)
989 {
990 struct grub_font_glyph *glyph;
991 struct grub_font_node *node;
992 /* Keep track of next node, in case there's an I/O error in
993 grub_font_get_glyph_internal() and the font is removed from the list. */
994 struct grub_font_node *next;
995 /* Information on the best glyph found so far, to help find the glyph in
996 the best matching to the requested one. */
997 int best_diversity;
998 struct grub_font_glyph *best_glyph;
999
1000 if (font)
1001 {
1002 /* First try to get the glyph from the specified font. */
1003 glyph = grub_font_get_glyph_internal (font, code);
1004 if (glyph)
1005 return glyph;
1006 }
1007
1008 /* Otherwise, search all loaded fonts for the glyph and use the one from
1009 the font that best matches the requested font. */
1010 best_diversity = 10000;
1011 best_glyph = 0;
1012
1013 for (node = grub_font_list; node; node = next)
1014 {
1015 grub_font_t curfont;
1016
1017 curfont = node->value;
1018 next = node->next;
1019
1020 glyph = grub_font_get_glyph_internal (curfont, code);
1021 if (glyph && !font)
1022 return glyph;
1023 if (glyph)
1024 {
1025 int d;
1026
1027 d = get_font_diversity (curfont, font);
1028 if (d < best_diversity)
1029 {
1030 best_diversity = d;
1031 best_glyph = glyph;
1032 }
1033 }
1034 }
1035
1036 return best_glyph;
1037 }
1038
1039 #if 0
1040 static struct grub_font_glyph *
1041 grub_font_dup_glyph (struct grub_font_glyph *glyph)
1042 {
1043 static struct grub_font_glyph *ret;
1044 ret = grub_malloc (sizeof (*ret) + (glyph->width * glyph->height + 7) / 8);
1045 if (!ret)
1046 return NULL;
1047 grub_memcpy (ret, glyph, sizeof (*ret)
1048 + (glyph->width * glyph->height + 7) / 8);
1049 return ret;
1050 }
1051 #endif
1052
1053 /* FIXME: suboptimal. */
1054 static void
1055 grub_font_blit_glyph (struct grub_font_glyph *target,
1056 struct grub_font_glyph *src, unsigned dx, unsigned dy)
1057 {
1058 unsigned src_bit, tgt_bit, src_byte, tgt_byte;
1059 unsigned i, j;
1060 for (i = 0; i < src->height; i++)
1061 {
1062 src_bit = (src->width * i) % 8;
1063 src_byte = (src->width * i) / 8;
1064 tgt_bit = (target->width * (dy + i) + dx) % 8;
1065 tgt_byte = (target->width * (dy + i) + dx) / 8;
1066 for (j = 0; j < src->width; j++)
1067 {
1068 target->bitmap[tgt_byte] |= ((src->bitmap[src_byte] << src_bit)
1069 & 0x80) >> tgt_bit;
1070 src_bit++;
1071 tgt_bit++;
1072 if (src_bit == 8)
1073 {
1074 src_byte++;
1075 src_bit = 0;
1076 }
1077 if (tgt_bit == 8)
1078 {
1079 tgt_byte++;
1080 tgt_bit = 0;
1081 }
1082 }
1083 }
1084 }
1085
1086 static void
1087 grub_font_blit_glyph_mirror (struct grub_font_glyph *target,
1088 struct grub_font_glyph *src,
1089 unsigned dx, unsigned dy)
1090 {
1091 unsigned tgt_bit, src_byte, tgt_byte;
1092 signed src_bit;
1093 unsigned i, j;
1094 for (i = 0; i < src->height; i++)
1095 {
1096 src_bit = (src->width * i + src->width - 1) % 8;
1097 src_byte = (src->width * i + src->width - 1) / 8;
1098 tgt_bit = (target->width * (dy + i) + dx) % 8;
1099 tgt_byte = (target->width * (dy + i) + dx) / 8;
1100 for (j = 0; j < src->width; j++)
1101 {
1102 target->bitmap[tgt_byte] |= ((src->bitmap[src_byte] << src_bit)
1103 & 0x80) >> tgt_bit;
1104 src_bit--;
1105 tgt_bit++;
1106 if (src_bit == -1)
1107 {
1108 src_byte--;
1109 src_bit = 7;
1110 }
1111 if (tgt_bit == 8)
1112 {
1113 tgt_byte++;
1114 tgt_bit = 0;
1115 }
1116 }
1117 }
1118 }
1119
1120 /* Context for blit_comb. */
1121 struct blit_comb_ctx
1122 {
1123 struct grub_font_glyph *glyph;
1124 int *device_width;
1125 struct grub_video_signed_rect bounds;
1126 };
1127
1128 /* Helper for blit_comb. */
1129 static void
1130 do_blit (struct grub_font_glyph *src, signed dx, signed dy,
1131 struct blit_comb_ctx *ctx)
1132 {
1133 if (ctx->glyph)
1134 grub_font_blit_glyph (ctx->glyph, src, dx - ctx->glyph->offset_x,
1135 (ctx->glyph->height + ctx->glyph->offset_y) + dy);
1136 if (dx < ctx->bounds.x)
1137 {
1138 ctx->bounds.width += ctx->bounds.x - dx;
1139 ctx->bounds.x = dx;
1140 }
1141 if (ctx->bounds.y > -src->height - dy)
1142 {
1143 ctx->bounds.height += ctx->bounds.y - (-src->height - dy);
1144 ctx->bounds.y = (-src->height - dy);
1145 }
1146 if (dx + src->width - ctx->bounds.x >= (signed) ctx->bounds.width)
1147 ctx->bounds.width = dx + src->width - ctx->bounds.x + 1;
1148 if ((signed) ctx->bounds.height < src->height + (-src->height - dy)
1149 - ctx->bounds.y)
1150 ctx->bounds.height = src->height + (-src->height - dy) - ctx->bounds.y;
1151 }
1152
1153 /* Helper for blit_comb. */
1154 static inline void
1155 add_device_width (int val, struct blit_comb_ctx *ctx)
1156 {
1157 if (ctx->glyph)
1158 ctx->glyph->device_width += val;
1159 if (ctx->device_width)
1160 *ctx->device_width += val;
1161 }
1162
1163 static void
1164 blit_comb (const struct grub_unicode_glyph *glyph_id,
1165 struct grub_font_glyph *glyph,
1166 struct grub_video_signed_rect *bounds_out,
1167 struct grub_font_glyph *main_glyph,
1168 struct grub_font_glyph **combining_glyphs, int *device_width)
1169 {
1170 struct blit_comb_ctx ctx = {
1171 .glyph = glyph,
1172 .device_width = device_width
1173 };
1174 unsigned i;
1175 signed above_rightx, above_righty;
1176 signed above_leftx, above_lefty;
1177 signed below_rightx, below_righty;
1178 signed min_devwidth = 0;
1179 const struct grub_unicode_combining *comb;
1180
1181 if (glyph)
1182 glyph->device_width = main_glyph->device_width;
1183 if (device_width)
1184 *device_width = main_glyph->device_width;
1185
1186 ctx.bounds.x = main_glyph->offset_x;
1187 ctx.bounds.y = main_glyph->offset_y;
1188 ctx.bounds.width = main_glyph->width;
1189 ctx.bounds.height = main_glyph->height;
1190
1191 above_rightx = main_glyph->offset_x + main_glyph->width;
1192 above_righty = ctx.bounds.y + ctx.bounds.height;
1193
1194 above_leftx = main_glyph->offset_x;
1195 above_lefty = ctx.bounds.y + ctx.bounds.height;
1196
1197 below_rightx = ctx.bounds.x + ctx.bounds.width;
1198 below_righty = ctx.bounds.y;
1199
1200 comb = grub_unicode_get_comb (glyph_id);
1201
1202 for (i = 0; i < glyph_id->ncomb; i++)
1203 {
1204 grub_int16_t space = 0;
1205 /* Center by default. */
1206 grub_int16_t targetx;
1207
1208 if (!combining_glyphs[i])
1209 continue;
1210 targetx = (ctx.bounds.width - combining_glyphs[i]->width) / 2 + ctx.bounds.x;
1211 /* CGJ is to avoid diacritics reordering. */
1212 if (comb[i].code
1213 == GRUB_UNICODE_COMBINING_GRAPHEME_JOINER)
1214 continue;
1215 switch (comb[i].type)
1216 {
1217 case GRUB_UNICODE_COMB_OVERLAY:
1218 do_blit (combining_glyphs[i],
1219 targetx,
1220 (ctx.bounds.height - combining_glyphs[i]->height) / 2
1221 - (ctx.bounds.height + ctx.bounds.y), &ctx);
1222 if (min_devwidth < combining_glyphs[i]->width)
1223 min_devwidth = combining_glyphs[i]->width;
1224 break;
1225
1226 case GRUB_UNICODE_COMB_ATTACHED_ABOVE_RIGHT:
1227 do_blit (combining_glyphs[i], above_rightx, -above_righty, &ctx);
1228 above_rightx += combining_glyphs[i]->width;
1229 break;
1230
1231 case GRUB_UNICODE_COMB_ABOVE_RIGHT:
1232 do_blit (combining_glyphs[i], above_rightx,
1233 -(above_righty + combining_glyphs[i]->height), &ctx);
1234 above_rightx += combining_glyphs[i]->width;
1235 break;
1236
1237 case GRUB_UNICODE_COMB_ABOVE_LEFT:
1238 above_leftx -= combining_glyphs[i]->width;
1239 do_blit (combining_glyphs[i], above_leftx,
1240 -(above_lefty + combining_glyphs[i]->height), &ctx);
1241 break;
1242
1243 case GRUB_UNICODE_COMB_BELOW_RIGHT:
1244 do_blit (combining_glyphs[i], below_rightx, below_righty, &ctx);
1245 below_rightx += combining_glyphs[i]->width;
1246 break;
1247
1248 case GRUB_UNICODE_COMB_HEBREW_HOLAM:
1249 if (glyph_id->base != GRUB_UNICODE_HEBREW_WAW)
1250 targetx =
1251 main_glyph->offset_x - combining_glyphs[i]->width -
1252 (combining_glyphs[i]->width + 3) / 4;
1253 goto above_on_main;
1254
1255 case GRUB_UNICODE_COMB_HEBREW_SIN_DOT:
1256 targetx = main_glyph->offset_x + combining_glyphs[i]->width / 4;
1257 goto above_on_main;
1258
1259 case GRUB_UNICODE_COMB_HEBREW_SHIN_DOT:
1260 targetx =
1261 main_glyph->width + main_glyph->offset_x -
1262 combining_glyphs[i]->width;
1263 above_on_main:
1264 space = combining_glyphs[i]->offset_y
1265 - grub_font_get_xheight (combining_glyphs[i]->font) - 1;
1266 if (space <= 0)
1267 space = 1 + (grub_font_get_xheight (main_glyph->font)) / 8;
1268 do_blit (combining_glyphs[i], targetx,
1269 -(main_glyph->height + main_glyph->offset_y + space
1270 + combining_glyphs[i]->height), &ctx);
1271 if (min_devwidth < combining_glyphs[i]->width)
1272 min_devwidth = combining_glyphs[i]->width;
1273 break;
1274
1275 /* TODO: Put dammah, fathah and alif nearer to shadda. */
1276 case GRUB_UNICODE_COMB_SYRIAC_SUPERSCRIPT_ALAPH:
1277 case GRUB_UNICODE_COMB_ARABIC_DAMMAH:
1278 case GRUB_UNICODE_COMB_ARABIC_DAMMATAN:
1279 case GRUB_UNICODE_COMB_ARABIC_FATHATAN:
1280 case GRUB_UNICODE_COMB_ARABIC_FATHAH:
1281 case GRUB_UNICODE_COMB_ARABIC_SUPERSCRIPT_ALIF:
1282 case GRUB_UNICODE_COMB_ARABIC_SUKUN:
1283 case GRUB_UNICODE_COMB_ARABIC_SHADDA:
1284 case GRUB_UNICODE_COMB_HEBREW_RAFE:
1285 case GRUB_UNICODE_STACK_ABOVE:
1286 stacked_above:
1287 space = combining_glyphs[i]->offset_y
1288 - grub_font_get_xheight (combining_glyphs[i]->font) - 1;
1289 if (space <= 0)
1290 space = 1 + (grub_font_get_xheight (main_glyph->font)) / 8;
1291 /* Fallthrough. */
1292 case GRUB_UNICODE_STACK_ATTACHED_ABOVE:
1293 do_blit (combining_glyphs[i], targetx,
1294 -(ctx.bounds.height + ctx.bounds.y + space
1295 + combining_glyphs[i]->height), &ctx);
1296 if (min_devwidth < combining_glyphs[i]->width)
1297 min_devwidth = combining_glyphs[i]->width;
1298 break;
1299
1300 case GRUB_UNICODE_COMB_HEBREW_DAGESH:
1301 do_blit (combining_glyphs[i], targetx,
1302 -(ctx.bounds.height / 2 + ctx.bounds.y
1303 + combining_glyphs[i]->height / 2), &ctx);
1304 if (min_devwidth < combining_glyphs[i]->width)
1305 min_devwidth = combining_glyphs[i]->width;
1306 break;
1307
1308 case GRUB_UNICODE_COMB_HEBREW_SHEVA:
1309 case GRUB_UNICODE_COMB_HEBREW_HIRIQ:
1310 case GRUB_UNICODE_COMB_HEBREW_QAMATS:
1311 case GRUB_UNICODE_COMB_HEBREW_TSERE:
1312 case GRUB_UNICODE_COMB_HEBREW_SEGOL:
1313 /* TODO: placement in final kaf and under reish. */
1314
1315 case GRUB_UNICODE_COMB_HEBREW_HATAF_SEGOL:
1316 case GRUB_UNICODE_COMB_HEBREW_HATAF_PATAH:
1317 case GRUB_UNICODE_COMB_HEBREW_HATAF_QAMATS:
1318 case GRUB_UNICODE_COMB_HEBREW_PATAH:
1319 case GRUB_UNICODE_COMB_HEBREW_QUBUTS:
1320 case GRUB_UNICODE_COMB_HEBREW_METEG:
1321 /* TODO: Put kasra and kasratan under shadda. */
1322 case GRUB_UNICODE_COMB_ARABIC_KASRA:
1323 case GRUB_UNICODE_COMB_ARABIC_KASRATAN:
1324 /* I don't know how ypogegrammeni differs from subscript. */
1325 case GRUB_UNICODE_COMB_YPOGEGRAMMENI:
1326 case GRUB_UNICODE_STACK_BELOW:
1327 stacked_below:
1328 space = -(combining_glyphs[i]->offset_y
1329 + combining_glyphs[i]->height);
1330 if (space <= 0)
1331 space = 1 + (grub_font_get_xheight (main_glyph->font)) / 8;
1332 /* Fallthrough. */
1333
1334 case GRUB_UNICODE_STACK_ATTACHED_BELOW:
1335 do_blit (combining_glyphs[i], targetx, -(ctx.bounds.y - space),
1336 &ctx);
1337 if (min_devwidth < combining_glyphs[i]->width)
1338 min_devwidth = combining_glyphs[i]->width;
1339 break;
1340
1341 case GRUB_UNICODE_COMB_MN:
1342 switch (comb[i].code)
1343 {
1344 case GRUB_UNICODE_THAANA_ABAFILI:
1345 case GRUB_UNICODE_THAANA_AABAAFILI:
1346 case GRUB_UNICODE_THAANA_UBUFILI:
1347 case GRUB_UNICODE_THAANA_OOBOOFILI:
1348 case GRUB_UNICODE_THAANA_EBEFILI:
1349 case GRUB_UNICODE_THAANA_EYBEYFILI:
1350 case GRUB_UNICODE_THAANA_OBOFILI:
1351 case GRUB_UNICODE_THAANA_OABOAFILI:
1352 case GRUB_UNICODE_THAANA_SUKUN:
1353 goto stacked_above;
1354 case GRUB_UNICODE_THAANA_IBIFILI:
1355 case GRUB_UNICODE_THAANA_EEBEEFILI:
1356 goto stacked_below;
1357 }
1358 /* Fall through. */
1359 default:
1360 {
1361 /* Default handling. Just draw combining character on top
1362 of base character.
1363 FIXME: support more unicode types correctly.
1364 */
1365 do_blit (combining_glyphs[i],
1366 main_glyph->device_width
1367 + combining_glyphs[i]->offset_x,
1368 -(combining_glyphs[i]->height
1369 + combining_glyphs[i]->offset_y), &ctx);
1370 add_device_width (combining_glyphs[i]->device_width, &ctx);
1371 }
1372 }
1373 }
1374 add_device_width ((above_rightx >
1375 below_rightx ? above_rightx : below_rightx) -
1376 (main_glyph->offset_x + main_glyph->width), &ctx);
1377 add_device_width (above_leftx - main_glyph->offset_x, &ctx);
1378 if (glyph && glyph->device_width < min_devwidth)
1379 glyph->device_width = min_devwidth;
1380 if (device_width && *device_width < min_devwidth)
1381 *device_width = min_devwidth;
1382
1383 if (bounds_out)
1384 *bounds_out = ctx.bounds;
1385 }
1386
1387 static struct grub_font_glyph *
1388 grub_font_construct_dry_run (grub_font_t hinted_font,
1389 const struct grub_unicode_glyph *glyph_id,
1390 struct grub_video_signed_rect *bounds,
1391 struct grub_font_glyph **combining_glyphs,
1392 int *device_width)
1393 {
1394 struct grub_font_glyph *main_glyph = NULL;
1395 grub_uint32_t desired_attributes = 0;
1396 unsigned i;
1397 grub_uint32_t base = glyph_id->base;
1398 const struct grub_unicode_combining *comb;
1399
1400 if (glyph_id->attributes & GRUB_UNICODE_GLYPH_ATTRIBUTE_RIGHT_JOINED)
1401 desired_attributes |= GRUB_FONT_CODE_RIGHT_JOINED;
1402
1403 if (glyph_id->attributes & GRUB_UNICODE_GLYPH_ATTRIBUTE_LEFT_JOINED)
1404 desired_attributes |= GRUB_FONT_CODE_LEFT_JOINED;
1405
1406 comb = grub_unicode_get_comb (glyph_id);
1407
1408 if (base == 'i' || base == 'j')
1409 {
1410 for (i = 0; i < glyph_id->ncomb; i++)
1411 if (comb[i].type == GRUB_UNICODE_STACK_ABOVE)
1412 break;
1413 if (i < glyph_id->ncomb && base == 'i')
1414 base = GRUB_UNICODE_DOTLESS_LOWERCASE_I;
1415 if (i < glyph_id->ncomb && base == 'j')
1416 base = GRUB_UNICODE_DOTLESS_LOWERCASE_J;
1417 }
1418
1419 main_glyph = grub_font_get_glyph_with_fallback (hinted_font, base
1420 | desired_attributes);
1421
1422 if (!main_glyph)
1423 main_glyph = grub_font_get_glyph_with_fallback (hinted_font,
1424 base);
1425
1426 /* Glyph not available in any font. Use ASCII fallback. */
1427 if (!main_glyph)
1428 main_glyph = ascii_glyph_lookup (base);
1429
1430 /* Glyph not available in any font. Return unknown glyph. */
1431 if (!main_glyph)
1432 return NULL;
1433
1434 if (device_width)
1435 *device_width = main_glyph->device_width;
1436
1437 if (!glyph_id->ncomb && !glyph_id->attributes)
1438 return main_glyph;
1439
1440 if (glyph_id->ncomb && !combining_glyphs)
1441 {
1442 grub_errno = GRUB_ERR_NONE;
1443 return main_glyph;
1444 }
1445
1446 for (i = 0; i < glyph_id->ncomb; i++)
1447 combining_glyphs[i]
1448 = grub_font_get_glyph_with_fallback (main_glyph->font,
1449 comb[i].code);
1450
1451 blit_comb (glyph_id, NULL, bounds, main_glyph, combining_glyphs,
1452 device_width);
1453
1454 return main_glyph;
1455 }
1456
1457 static struct grub_font_glyph **render_combining_glyphs = 0;
1458 static grub_size_t render_max_comb_glyphs = 0;
1459
1460 static void
1461 ensure_comb_space (const struct grub_unicode_glyph *glyph_id)
1462 {
1463 if (glyph_id->ncomb <= render_max_comb_glyphs)
1464 return;
1465
1466 render_max_comb_glyphs = 2 * glyph_id->ncomb;
1467 if (render_max_comb_glyphs < 8)
1468 render_max_comb_glyphs = 8;
1469 grub_free (render_combining_glyphs);
1470 render_combining_glyphs = grub_malloc (render_max_comb_glyphs
1471 * sizeof (render_combining_glyphs[0]));
1472 if (!render_combining_glyphs)
1473 grub_errno = 0;
1474 }
1475
1476 int
1477 grub_font_get_constructed_device_width (grub_font_t hinted_font,
1478 const struct grub_unicode_glyph
1479 *glyph_id)
1480 {
1481 int ret;
1482 struct grub_font_glyph *main_glyph;
1483
1484 ensure_comb_space (glyph_id);
1485
1486 main_glyph = grub_font_construct_dry_run (hinted_font, glyph_id, NULL,
1487 render_combining_glyphs, &ret);
1488 if (!main_glyph)
1489 return unknown_glyph->device_width;
1490 return ret;
1491 }
1492
1493 struct grub_font_glyph *
1494 grub_font_construct_glyph (grub_font_t hinted_font,
1495 const struct grub_unicode_glyph *glyph_id)
1496 {
1497 struct grub_font_glyph *main_glyph;
1498 struct grub_video_signed_rect bounds;
1499 static struct grub_font_glyph *glyph = 0;
1500 static grub_size_t max_glyph_size = 0;
1501
1502 ensure_comb_space (glyph_id);
1503
1504 main_glyph = grub_font_construct_dry_run (hinted_font, glyph_id,
1505 &bounds, render_combining_glyphs,
1506 NULL);
1507
1508 if (!main_glyph)
1509 return unknown_glyph;
1510
1511 if (!render_combining_glyphs && glyph_id->ncomb)
1512 return main_glyph;
1513
1514 if (!glyph_id->ncomb && !glyph_id->attributes)
1515 return main_glyph;
1516
1517 if (max_glyph_size < sizeof (*glyph) + (bounds.width * bounds.height + GRUB_CHAR_BIT - 1) / GRUB_CHAR_BIT)
1518 {
1519 grub_free (glyph);
1520 max_glyph_size = (sizeof (*glyph) + (bounds.width * bounds.height + GRUB_CHAR_BIT - 1) / GRUB_CHAR_BIT) * 2;
1521 if (max_glyph_size < 8)
1522 max_glyph_size = 8;
1523 glyph = grub_malloc (max_glyph_size);
1524 }
1525 if (!glyph)
1526 {
1527 grub_errno = GRUB_ERR_NONE;
1528 return main_glyph;
1529 }
1530
1531 grub_memset (glyph, 0, sizeof (*glyph)
1532 + (bounds.width * bounds.height
1533 + GRUB_CHAR_BIT - 1) / GRUB_CHAR_BIT);
1534
1535 glyph->font = main_glyph->font;
1536 glyph->width = bounds.width;
1537 glyph->height = bounds.height;
1538 glyph->offset_x = bounds.x;
1539 glyph->offset_y = bounds.y;
1540
1541 if (glyph_id->attributes & GRUB_UNICODE_GLYPH_ATTRIBUTE_MIRROR)
1542 grub_font_blit_glyph_mirror (glyph, main_glyph,
1543 main_glyph->offset_x - glyph->offset_x,
1544 (glyph->height + glyph->offset_y)
1545 - (main_glyph->height +
1546 main_glyph->offset_y));
1547 else
1548 grub_font_blit_glyph (glyph, main_glyph,
1549 main_glyph->offset_x - glyph->offset_x,
1550 (glyph->height + glyph->offset_y)
1551 - (main_glyph->height + main_glyph->offset_y));
1552
1553 blit_comb (glyph_id, glyph, NULL, main_glyph, render_combining_glyphs, NULL);
1554
1555 return glyph;
1556 }
1557
1558 /* Draw the specified glyph at (x, y). The y coordinate designates the
1559 baseline of the character, while the x coordinate designates the left
1560 side location of the character. */
1561 grub_err_t
1562 grub_font_draw_glyph (struct grub_font_glyph * glyph,
1563 grub_video_color_t color, int left_x, int baseline_y)
1564 {
1565 struct grub_video_bitmap glyph_bitmap;
1566
1567 /* Don't try to draw empty glyphs (U+0020, etc.). */
1568 if (glyph->width == 0 || glyph->height == 0)
1569 return GRUB_ERR_NONE;
1570
1571 glyph_bitmap.mode_info.width = glyph->width;
1572 glyph_bitmap.mode_info.height = glyph->height;
1573 glyph_bitmap.mode_info.mode_type
1574 = (1 << GRUB_VIDEO_MODE_TYPE_DEPTH_POS) | GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP;
1575 glyph_bitmap.mode_info.blit_format = GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED;
1576 glyph_bitmap.mode_info.bpp = 1;
1577
1578 /* Really 1 bit per pixel. */
1579 glyph_bitmap.mode_info.bytes_per_pixel = 0;
1580
1581 /* Packed densely as bits. */
1582 glyph_bitmap.mode_info.pitch = glyph->width;
1583
1584 glyph_bitmap.mode_info.number_of_colors = 2;
1585 glyph_bitmap.mode_info.bg_red = 0;
1586 glyph_bitmap.mode_info.bg_green = 0;
1587 glyph_bitmap.mode_info.bg_blue = 0;
1588 glyph_bitmap.mode_info.bg_alpha = 0;
1589 grub_video_unmap_color (color,
1590 &glyph_bitmap.mode_info.fg_red,
1591 &glyph_bitmap.mode_info.fg_green,
1592 &glyph_bitmap.mode_info.fg_blue,
1593 &glyph_bitmap.mode_info.fg_alpha);
1594 glyph_bitmap.data = glyph->bitmap;
1595
1596 int bitmap_left = left_x + glyph->offset_x;
1597 int bitmap_bottom = baseline_y - glyph->offset_y;
1598 int bitmap_top = bitmap_bottom - glyph->height;
1599
1600 return grub_video_blit_bitmap (&glyph_bitmap, GRUB_VIDEO_BLIT_BLEND,
1601 bitmap_left, bitmap_top,
1602 0, 0, glyph->width, glyph->height);
1603 }