]> git.proxmox.com Git - grub2.git/blame - grub-core/font/font.c
Merge powerpc grub-mkrescue flavour with common. Use xorriso HFS+
[grub2.git] / grub-core / font / font.c
CommitLineData
1e901a75 1/* font.c - Font API and font file loader. */
2/*
3 * GRUB -- GRand Unified Bootloader
d1fb0f65 4 * Copyright (C) 2003,2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc.
1e901a75 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>
f0cfb703 29#include <grub/charset.h>
72f12cdc 30#include <grub/unicode.h>
de0b7a4e 31#include <grub/fontformat.h>
274416e8 32#include <grub/env.h>
1e901a75 33
e745cf0c
VS
34GRUB_MOD_LICENSE ("GPLv3+");
35
d0230c21 36#ifdef USE_ASCII_FAILBACK
26ba5c22 37#include "ascii.h"
38#endif
1e901a75 39
40#ifndef FONT_DEBUG
41#define FONT_DEBUG 0
42#endif
43
44struct char_index_entry
45{
46 grub_uint32_t code;
47 grub_uint8_t storage_flags;
48 grub_uint32_t offset;
b39f9d20 49
1e901a75 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
26ba5c22 56#define ASCII_BITMAP_SIZE 16
1e901a75 57
58struct grub_font
59{
60 char *name;
61 grub_file_t file;
62 char *family;
63 short point_size;
64 short weight;
65 short max_char_width;
66 short max_char_height;
67 short ascent;
68 short descent;
69 short leading;
70 grub_uint32_t num_chars;
71 struct char_index_entry *char_index;
f9e4ed6a 72 grub_uint16_t *bmp_idx;
1e901a75 73};
74
75/* Definition of font registry. */
76struct grub_font_node *grub_font_list;
77
78static int register_font (grub_font_t font);
79static void font_init (grub_font_t font);
80static void free_font (grub_font_t font);
81static void remove_font (grub_font_t font);
82
83struct font_file_section
84{
85 /* The file this section is in. */
86 grub_file_t file;
b39f9d20 87
1e901a75 88 /* FOURCC name of the section. */
89 char name[4];
b39f9d20 90
1e901a75 91 /* Length of the section contents. */
92 grub_uint32_t length;
b39f9d20 93
1e901a75 94 /* Set by open_section() on EOF. */
95 int eof;
96};
97
1e901a75 98/* Replace unknown glyphs with a rounded question mark. */
1118c32e 99static grub_uint8_t unknown_glyph_bitmap[] = {
1e901a75 100 /* 76543210 */
1118c32e
VS
101 0x7C, /* ooooo */
102 0x82, /* o o */
103 0xBA, /* o ooo o */
104 0xAA, /* o o o o */
105 0xAA, /* o o o o */
106 0x8A, /* o o o */
107 0x9A, /* o oo o */
108 0x92, /* o o o */
109 0x92, /* o o o */
110 0x92, /* o o o */
111 0x92, /* o o o */
112 0x82, /* o o */
113 0x92, /* o o o */
114 0x82, /* o o */
115 0x7C, /* ooooo */
116 0x00 /* */
1e901a75 117};
118
119/* The "unknown glyph" glyph, used as a last resort. */
120static struct grub_font_glyph *unknown_glyph;
121
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. */
125static struct grub_font null_font;
126
127/* Flag to ensure module is initialized only once. */
128static grub_uint8_t font_loader_initialized;
129
d0230c21 130#ifdef USE_ASCII_FAILBACK
26ba5c22 131static struct grub_font_glyph *ascii_font_glyph[0x80];
132#endif
133
134static struct grub_font_glyph *
135ascii_glyph_lookup (grub_uint32_t code)
136{
1118c32e 137#ifdef USE_ASCII_FAILBACK
26ba5c22 138 static int ascii_failback_initialized = 0;
139
140 if (code >= 0x80)
53f312c1 141 return NULL;
26ba5c22 142
143 if (ascii_failback_initialized == 0)
144 {
145 int current;
146 for (current = 0; current < 0x80; current++)
1118c32e
VS
147 {
148 ascii_font_glyph[current] =
149 grub_malloc (sizeof (struct grub_font_glyph) + ASCII_BITMAP_SIZE);
150
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;
26ba5c22 155 ascii_font_glyph[current]->device_width = 8;
e6d428c1 156 ascii_font_glyph[current]->font = NULL;
26ba5c22 157
158 grub_memcpy (ascii_font_glyph[current]->bitmap,
c0420b76 159 &ascii_bitmaps[current * ASCII_BITMAP_SIZE],
26ba5c22 160 ASCII_BITMAP_SIZE);
161 }
162
163 ascii_failback_initialized = 1;
164 }
165
166 return ascii_font_glyph[code];
167#else
168 (void) code;
53f312c1 169 return NULL;
26ba5c22 170#endif
171}
172
1e901a75 173void
174grub_font_loader_init (void)
175{
176 /* Only initialize font loader once. */
177 if (font_loader_initialized)
178 return;
179
180 /* Make glyph for unknown glyph. */
1118c32e
VS
181 unknown_glyph = grub_malloc (sizeof (struct grub_font_glyph)
182 + sizeof (unknown_glyph_bitmap));
183 if (!unknown_glyph)
1e901a75 184 return;
185
186 unknown_glyph->width = 8;
187 unknown_glyph->height = 16;
188 unknown_glyph->offset_x = 0;
bee5fe5d 189 unknown_glyph->offset_y = -3;
1e901a75 190 unknown_glyph->device_width = 8;
1118c32e
VS
191 grub_memcpy (unknown_glyph->bitmap,
192 unknown_glyph_bitmap, sizeof (unknown_glyph_bitmap));
1e901a75 193
194 /* Initialize the null font. */
195 font_init (&null_font);
ebcecdf1
VS
196 /* FIXME: Fix this slightly improper cast. */
197 null_font.name = (char *) "<No Font>";
1118c32e 198 null_font.ascent = unknown_glyph->height - 3;
bee5fe5d 199 null_font.descent = 3;
1e901a75 200 null_font.max_char_width = unknown_glyph->width;
201 null_font.max_char_height = unknown_glyph->height;
202
203 font_loader_initialized = 1;
204}
205
206/* Initialize the font object with initial default values. */
207static void
208font_init (grub_font_t font)
209{
210 font->name = 0;
211 font->file = 0;
212 font->family = 0;
213 font->point_size = 0;
214 font->weight = 0;
b39f9d20 215
1e901a75 216 /* Default leading value, not in font file yet. */
217 font->leading = 1;
b39f9d20 218
1e901a75 219 font->max_char_width = 0;
220 font->max_char_height = 0;
221 font->ascent = 0;
222 font->descent = 0;
223 font->num_chars = 0;
224 font->char_index = 0;
f9e4ed6a 225 font->bmp_idx = 0;
1e901a75 226}
227
228/* Open the next section in the file.
229
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
4241d2b1 232 grub_errno is set appropriately with an error message.
1e901a75 233
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. */
236static int
237open_section (grub_file_t file, struct font_file_section *section)
238{
239 grub_ssize_t retval;
240 grub_uint32_t raw_length;
241
242 section->file = file;
243 section->eof = 0;
244
245 /* Read the FOURCC section name. */
246 retval = grub_file_read (file, section->name, 4);
247 if (retval >= 0 && retval < 4)
248 {
249 /* EOF encountered. */
250 section->eof = 1;
251 return 1;
252 }
253 else if (retval < 0)
254 {
7a45a539 255 /* Read error. */
1e901a75 256 return 1;
257 }
258
259 /* Read the big-endian 32-bit section length. */
5c5215d5 260 retval = grub_file_read (file, &raw_length, 4);
1e901a75 261 if (retval >= 0 && retval < 4)
262 {
263 /* EOF encountered. */
264 section->eof = 1;
265 return 1;
266 }
267 else if (retval < 0)
268 {
7a45a539 269 /* Read error. */
1e901a75 270 return 1;
271 }
272
273 /* Convert byte-order and store in *length. */
274 section->length = grub_be_to_cpu32 (raw_length);
275
276 return 0;
277}
278
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)
282
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). */
288static int
289load_font_index (grub_file_t file, grub_uint32_t sect_length, struct
1118c32e 290 grub_font *font)
1e901a75 291{
292 unsigned i;
c8048e32 293 grub_uint32_t last_code;
1e901a75 294
295#if FONT_DEBUG >= 2
1118c32e 296 grub_printf ("load_font_index(sect_length=%d)\n", sect_length);
1e901a75 297#endif
298
299 /* Sanity check: ensure section length is divisible by the entry size. */
300 if ((sect_length % FONT_CHAR_INDEX_ENTRY_SIZE) != 0)
301 {
302 grub_error (GRUB_ERR_BAD_FONT,
1118c32e
VS
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);
1e901a75 306 return 1;
307 }
308
309 /* Calculate the number of characters. */
310 font->num_chars = sect_length / FONT_CHAR_INDEX_ENTRY_SIZE;
311
312 /* Allocate the character index array. */
313 font->char_index = grub_malloc (font->num_chars
1118c32e
VS
314 * sizeof (struct char_index_entry));
315 if (!font->char_index)
1e901a75 316 return 1;
f9e4ed6a 317 font->bmp_idx = grub_malloc (0x10000 * sizeof (grub_uint16_t));
1118c32e 318 if (!font->bmp_idx)
25a45338 319 return 1;
f9e4ed6a
VS
320 grub_memset (font->bmp_idx, 0xff, 0x10000 * sizeof (grub_uint16_t));
321
1e901a75 322
323#if FONT_DEBUG >= 2
1118c32e 324 grub_printf ("num_chars=%d)\n", font->num_chars);
1e901a75 325#endif
326
c8048e32 327 last_code = 0;
328
1e901a75 329 /* Load the character index data from the file. */
330 for (i = 0; i < font->num_chars; i++)
331 {
332 struct char_index_entry *entry = &font->char_index[i];
333
334 /* Read code point value; convert to native byte order. */
5c5215d5 335 if (grub_file_read (file, &entry->code, 4) != 4)
1118c32e 336 return 1;
1e901a75 337 entry->code = grub_be_to_cpu32 (entry->code);
338
c8048e32 339 /* Verify that characters are in ascending order. */
340 if (i != 0 && entry->code <= last_code)
1118c32e
VS
341 {
342 grub_error (GRUB_ERR_BAD_FONT,
343 "font characters not in ascending order: %u <= %u",
344 entry->code, last_code);
345 return 1;
346 }
c8048e32 347
f9e4ed6a
VS
348 if (entry->code < 0x10000)
349 font->bmp_idx[entry->code] = i;
350
c8048e32 351 last_code = entry->code;
352
1e901a75 353 /* Read storage flags byte. */
5c5215d5 354 if (grub_file_read (file, &entry->storage_flags, 1) != 1)
1118c32e 355 return 1;
1e901a75 356
357 /* Read glyph data offset; convert to native byte order. */
5c5215d5 358 if (grub_file_read (file, &entry->offset, 4) != 4)
1118c32e 359 return 1;
1e901a75 360 entry->offset = grub_be_to_cpu32 (entry->offset);
361
362 /* No glyph loaded. Will be loaded on demand and cached thereafter. */
363 entry->glyph = 0;
364
365#if FONT_DEBUG >= 5
366 /* Print the 1st 10 characters. */
367 if (i < 10)
1118c32e 368 grub_printf ("c=%d o=%d\n", entry->code, entry->offset);
1e901a75 369#endif
370 }
371
372 return 0;
373}
374
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. */
377static char *
378read_section_as_string (struct font_file_section *section)
379{
380 char *str;
381 grub_ssize_t ret;
382
383 str = grub_malloc (section->length + 1);
1118c32e 384 if (!str)
1e901a75 385 return 0;
386
387 ret = grub_file_read (section->file, str, section->length);
388 if (ret < 0 || ret != (grub_ssize_t) section->length)
389 {
390 grub_free (str);
391 return 0;
392 }
393
394 str[section->length] = '\0';
395 return str;
396}
397
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. */
401static int
1118c32e
VS
402read_section_as_short (struct font_file_section *section,
403 grub_int16_t * value)
1e901a75 404{
405 grub_uint16_t raw_value;
406
407 if (section->length != 2)
408 {
409 grub_error (GRUB_ERR_BAD_FONT,
1118c32e
VS
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);
1e901a75 414 return 1;
415 }
5c5215d5 416 if (grub_file_read (section->file, &raw_value, 2) != 2)
1e901a75 417 return 1;
418
419 *value = grub_be_to_cpu16 (raw_value);
420 return 0;
421}
422
423/* Load a font and add it to the beginning of the global font list.
424 Returns 0 upon success, nonzero upon failure. */
a79b8a15 425grub_font_t
1e901a75 426grub_font_load (const char *filename)
427{
428 grub_file_t file = 0;
429 struct font_file_section section;
430 char magic[4];
431 grub_font_t font = 0;
432
433#if FONT_DEBUG >= 1
1118c32e 434 grub_printf ("add_font(%s)\n", filename);
1e901a75 435#endif
436
274416e8
VS
437 if (filename[0] == '(' || filename[0] == '/' || filename[0] == '+')
438 file = grub_buffile_open (filename, 1024);
439 else
440 {
441 const char *prefix = grub_env_get ("prefix");
442 char *fullname, *ptr;
443 if (!prefix)
444 {
445 grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't set"),
446 "prefix");
447 goto fail;
448 }
449 fullname = grub_malloc (grub_strlen (prefix) + grub_strlen (filename) + 1
450 + sizeof ("/fonts/") + sizeof (".pf2"));
451 if (!fullname)
452 goto fail;
453 ptr = grub_stpcpy (fullname, prefix);
454 ptr = grub_stpcpy (ptr, "/fonts/");
455 ptr = grub_stpcpy (ptr, filename);
456 ptr = grub_stpcpy (ptr, ".pf2");
457 *ptr = 0;
458 file = grub_buffile_open (fullname, 1024);
459 grub_free (fullname);
460 }
1e901a75 461 if (!file)
462 goto fail;
463
464#if FONT_DEBUG >= 3
1118c32e 465 grub_printf ("file opened\n");
1e901a75 466#endif
467
468 /* Read the FILE section. It indicates the file format. */
469 if (open_section (file, &section) != 0)
470 goto fail;
471
472#if FONT_DEBUG >= 3
1118c32e 473 grub_printf ("opened FILE section\n");
1e901a75 474#endif
de0b7a4e 475 if (grub_memcmp (section.name, FONT_FORMAT_SECTION_NAMES_FILE,
1118c32e 476 sizeof (FONT_FORMAT_SECTION_NAMES_FILE) - 1) != 0)
1e901a75 477 {
478 grub_error (GRUB_ERR_BAD_FONT,
1118c32e 479 "font file format error: 1st section must be FILE");
1e901a75 480 goto fail;
481 }
482
483#if FONT_DEBUG >= 3
1118c32e 484 grub_printf ("section name ok\n");
1e901a75 485#endif
486 if (section.length != 4)
487 {
488 grub_error (GRUB_ERR_BAD_FONT,
1118c32e
VS
489 "font file format error (file type ID length is %d "
490 "but should be 4)", section.length);
1e901a75 491 goto fail;
492 }
493
494#if FONT_DEBUG >= 3
1118c32e 495 grub_printf ("section length ok\n");
1e901a75 496#endif
497 /* Check the file format type code. */
498 if (grub_file_read (file, magic, 4) != 4)
499 goto fail;
500
501#if FONT_DEBUG >= 3
1118c32e 502 grub_printf ("read magic ok\n");
1e901a75 503#endif
504
de0b7a4e 505 if (grub_memcmp (magic, FONT_FORMAT_PFF2_MAGIC, 4) != 0)
1e901a75 506 {
7fd0baee 507 grub_error (GRUB_ERR_BAD_FONT, "invalid font magic %x %x %x %x",
1118c32e 508 magic[0], magic[1], magic[2], magic[3]);
1e901a75 509 goto fail;
510 }
511
512#if FONT_DEBUG >= 3
1118c32e 513 grub_printf ("compare magic ok\n");
1e901a75 514#endif
515
516 /* Allocate the font object. */
25a45338 517 font = (grub_font_t) grub_zalloc (sizeof (struct grub_font));
1118c32e 518 if (!font)
1e901a75 519 goto fail;
520
521 font_init (font);
522 font->file = file;
523
524#if FONT_DEBUG >= 3
1118c32e 525 grub_printf ("allocate font ok; loading font info\n");
1e901a75 526#endif
527
528 /* Load the font information. */
529 while (1)
530 {
531 if (open_section (file, &section) != 0)
1118c32e
VS
532 {
533 if (section.eof)
534 break; /* Done reading the font file. */
535 else
536 goto fail;
537 }
1e901a75 538
539#if FONT_DEBUG >= 2
1118c32e
VS
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]);
1e901a75 543#endif
544
de0b7a4e 545 if (grub_memcmp (section.name, FONT_FORMAT_SECTION_NAMES_FONT_NAME,
1118c32e
VS
546 sizeof (FONT_FORMAT_SECTION_NAMES_FONT_NAME) - 1) == 0)
547 {
548 font->name = read_section_as_string (&section);
549 if (!font->name)
550 goto fail;
551 }
de0b7a4e 552 else if (grub_memcmp (section.name,
1118c32e
VS
553 FONT_FORMAT_SECTION_NAMES_POINT_SIZE,
554 sizeof (FONT_FORMAT_SECTION_NAMES_POINT_SIZE) -
555 1) == 0)
556 {
557 if (read_section_as_short (&section, &font->point_size) != 0)
558 goto fail;
559 }
de0b7a4e 560 else if (grub_memcmp (section.name, FONT_FORMAT_SECTION_NAMES_WEIGHT,
1118c32e
VS
561 sizeof (FONT_FORMAT_SECTION_NAMES_WEIGHT) - 1)
562 == 0)
563 {
564 char *wt;
565 wt = read_section_as_string (&section);
566 if (!wt)
567 continue;
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;
573 grub_free (wt);
574 }
de0b7a4e 575 else if (grub_memcmp (section.name,
1118c32e
VS
576 FONT_FORMAT_SECTION_NAMES_MAX_CHAR_WIDTH,
577 sizeof (FONT_FORMAT_SECTION_NAMES_MAX_CHAR_WIDTH)
578 - 1) == 0)
579 {
580 if (read_section_as_short (&section, &font->max_char_width) != 0)
581 goto fail;
582 }
de0b7a4e 583 else if (grub_memcmp (section.name,
1118c32e
VS
584 FONT_FORMAT_SECTION_NAMES_MAX_CHAR_HEIGHT,
585 sizeof (FONT_FORMAT_SECTION_NAMES_MAX_CHAR_HEIGHT)
586 - 1) == 0)
587 {
588 if (read_section_as_short (&section, &font->max_char_height) != 0)
589 goto fail;
590 }
de0b7a4e 591 else if (grub_memcmp (section.name,
1118c32e
VS
592 FONT_FORMAT_SECTION_NAMES_ASCENT,
593 sizeof (FONT_FORMAT_SECTION_NAMES_ASCENT) - 1)
594 == 0)
595 {
596 if (read_section_as_short (&section, &font->ascent) != 0)
597 goto fail;
598 }
de0b7a4e 599 else if (grub_memcmp (section.name, FONT_FORMAT_SECTION_NAMES_DESCENT,
1118c32e
VS
600 sizeof (FONT_FORMAT_SECTION_NAMES_DESCENT) - 1)
601 == 0)
602 {
603 if (read_section_as_short (&section, &font->descent) != 0)
604 goto fail;
605 }
de0b7a4e 606 else if (grub_memcmp (section.name,
1118c32e
VS
607 FONT_FORMAT_SECTION_NAMES_CHAR_INDEX,
608 sizeof (FONT_FORMAT_SECTION_NAMES_CHAR_INDEX) -
609 1) == 0)
610 {
611 if (load_font_index (file, section.length, font) != 0)
612 goto fail;
613 }
de0b7a4e 614 else if (grub_memcmp (section.name, FONT_FORMAT_SECTION_NAMES_DATA,
1118c32e
VS
615 sizeof (FONT_FORMAT_SECTION_NAMES_DATA) - 1) == 0)
616 {
617 /* When the DATA section marker is reached, we stop reading. */
618 break;
619 }
1e901a75 620 else
1118c32e
VS
621 {
622 /* Unhandled section type, simply skip past it. */
1e901a75 623#if FONT_DEBUG >= 3
1118c32e 624 grub_printf ("Unhandled section type, skipping.\n");
1e901a75 625#endif
1118c32e
VS
626 grub_off_t section_end = grub_file_tell (file) + section.length;
627 if ((int) grub_file_seek (file, section_end) == -1)
628 goto fail;
629 }
1e901a75 630 }
631
1118c32e 632 if (!font->name)
1e901a75 633 {
df067ad1 634 grub_dprintf ("font", "Font has no name.\n");
1e901a75 635 font->name = grub_strdup ("Unknown");
636 }
637
638#if FONT_DEBUG >= 1
639 grub_printf ("Loaded font `%s'.\n"
1118c32e
VS
640 "Ascent=%d Descent=%d MaxW=%d MaxH=%d Number of characters=%d.\n",
641 font->name,
642 font->ascent, font->descent,
643 font->max_char_width, font->max_char_height, font->num_chars);
1e901a75 644#endif
645
646 if (font->max_char_width == 0
647 || font->max_char_height == 0
648 || font->num_chars == 0
1118c32e 649 || font->char_index == 0 || font->ascent == 0 || font->descent == 0)
1e901a75 650 {
651 grub_error (GRUB_ERR_BAD_FONT,
1118c32e 652 "invalid font file: missing some required data");
1e901a75 653 goto fail;
654 }
655
656 /* Add the font to the global font registry. */
657 if (register_font (font) != 0)
658 goto fail;
659
a79b8a15 660 return font;
1e901a75 661
662fail:
25a45338
VS
663 if (file)
664 grub_file_close (file);
665 if (font)
666 font->file = 0;
667
1e901a75 668 free_font (font);
a79b8a15 669 return 0;
1e901a75 670}
671
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. */
675static int
676read_be_uint16 (grub_file_t file, grub_uint16_t * value)
677{
5c5215d5 678 if (grub_file_read (file, value, 2) != 2)
1e901a75 679 return 1;
680 *value = grub_be_to_cpu16 (*value);
681 return 0;
682}
683
684static int
685read_be_int16 (grub_file_t file, grub_int16_t * value)
686{
687 /* For the signed integer version, use the same code as for unsigned. */
688 return read_be_uint16 (file, (grub_uint16_t *) value);
689}
690
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. */
f9e4ed6a 693static inline struct char_index_entry *
1e901a75 694find_glyph (const grub_font_t font, grub_uint32_t code)
695{
c8048e32 696 struct char_index_entry *table;
697 grub_size_t lo;
698 grub_size_t hi;
699 grub_size_t mid;
700
c8048e32 701 table = font->char_index;
f9e4ed6a
VS
702
703 /* Use BMP index if possible. */
61e89d9d 704 if (code < 0x10000 && font->bmp_idx)
f9e4ed6a
VS
705 {
706 if (font->bmp_idx[code] == 0xffff)
707 return 0;
708 return &table[font->bmp_idx[code]];
709 }
710
711 /* Do a binary search in `char_index', which is ordered by code point. */
c8048e32 712 lo = 0;
713 hi = font->num_chars - 1;
1e901a75 714
1118c32e 715 if (!table)
57a55913 716 return 0;
717
c8048e32 718 while (lo <= hi)
1e901a75 719 {
c8048e32 720 mid = lo + (hi - lo) / 2;
721 if (code < table[mid].code)
1118c32e 722 hi = mid - 1;
c8048e32 723 else if (code > table[mid].code)
1118c32e 724 lo = mid + 1;
c8048e32 725 else
1118c32e 726 return &table[mid];
1e901a75 727 }
728
729 return 0;
730}
731
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. */
735static struct grub_font_glyph *
736grub_font_get_glyph_internal (grub_font_t font, grub_uint32_t code)
737{
738 struct char_index_entry *index_entry;
739
740 index_entry = find_glyph (font, code);
741 if (index_entry)
742 {
743 struct grub_font_glyph *glyph = 0;
744 grub_uint16_t width;
745 grub_uint16_t height;
746 grub_int16_t xoff;
747 grub_int16_t yoff;
748 grub_int16_t dwidth;
749 int len;
750
751 if (index_entry->glyph)
1118c32e
VS
752 /* Return cached glyph. */
753 return index_entry->glyph;
1e901a75 754
1118c32e
VS
755 if (!font->file)
756 /* No open file, can't load any glyphs. */
757 return 0;
1e901a75 758
759 /* Make sure we can find glyphs for error messages. Push active
760 error message to error stack and reset error message. */
761 grub_error_push ();
762
763 grub_file_seek (font->file, index_entry->offset);
764
765 /* Read the glyph width, height, and baseline. */
1118c32e
VS
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)
771 {
772 remove_font (font);
773 return 0;
774 }
1e901a75 775
776 len = (width * height + 7) / 8;
777 glyph = grub_malloc (sizeof (struct grub_font_glyph) + len);
1118c32e
VS
778 if (!glyph)
779 {
780 remove_font (font);
781 return 0;
782 }
1e901a75 783
784 glyph->font = font;
785 glyph->width = width;
786 glyph->height = height;
787 glyph->offset_x = xoff;
788 glyph->offset_y = yoff;
789 glyph->device_width = dwidth;
790
791 /* Don't try to read empty bitmaps (e.g., space characters). */
792 if (len != 0)
1118c32e
VS
793 {
794 if (grub_file_read (font->file, glyph->bitmap, len) != len)
795 {
796 remove_font (font);
797 return 0;
798 }
799 }
1e901a75 800
801 /* Restore old error message. */
802 grub_error_pop ();
803
804 /* Cache the glyph. */
805 index_entry->glyph = glyph;
806
807 return glyph;
808 }
809
810 return 0;
811}
812
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. */
817static void
818free_font (grub_font_t font)
819{
820 if (font)
821 {
822 if (font->file)
1118c32e 823 grub_file_close (font->file);
1e901a75 824 grub_free (font->name);
825 grub_free (font->family);
826 grub_free (font->char_index);
25a45338 827 grub_free (font->bmp_idx);
1e901a75 828 grub_free (font);
829 }
830}
831
832/* Add FONT to the global font registry.
833 Returns 0 upon success, nonzero on failure
834 (the font was not registered). */
835static int
836register_font (grub_font_t font)
837{
838 struct grub_font_node *node = 0;
839
840 node = grub_malloc (sizeof (struct grub_font_node));
1118c32e 841 if (!node)
1e901a75 842 return 1;
843
844 node->value = font;
845 node->next = grub_font_list;
846 grub_font_list = node;
847
848 return 0;
849}
850
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. */
853static void
854remove_font (grub_font_t font)
855{
856 struct grub_font_node **nextp, *cur;
857
858 for (nextp = &grub_font_list, cur = *nextp;
1118c32e 859 cur; nextp = &cur->next, cur = cur->next)
1e901a75 860 {
861 if (cur->value == font)
1118c32e
VS
862 {
863 *nextp = cur->next;
1e901a75 864
1118c32e
VS
865 /* Free the node, but not the font itself. */
866 grub_free (cur);
1e901a75 867
1118c32e
VS
868 return;
869 }
1e901a75 870 }
871}
872
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. */
877grub_font_t
878grub_font_get (const char *font_name)
879{
880 struct grub_font_node *node;
881
882 for (node = grub_font_list; node; node = node->next)
883 {
884 grub_font_t font = node->value;
885 if (grub_strcmp (font->name, font_name) == 0)
1118c32e 886 return font;
1e901a75 887 }
888
889 /* If no font by that name is found, return the first font in the list
890 as a fallback. */
891 if (grub_font_list && grub_font_list->value)
892 return grub_font_list->value;
893 else
894 /* The null_font is a last resort. */
895 return &null_font;
896}
897
d9f31a41 898/* Get the full name of the font. */
1e901a75 899const char *
900grub_font_get_name (grub_font_t font)
901{
902 return font->name;
903}
904
905/* Get the maximum width of any character in the font in pixels. */
906int
907grub_font_get_max_char_width (grub_font_t font)
908{
909 return font->max_char_width;
910}
911
912/* Get the maximum height of any character in the font in pixels. */
913int
914grub_font_get_max_char_height (grub_font_t font)
915{
916 return font->max_char_height;
917}
918
919/* Get the distance in pixels from the top of characters to the baseline. */
920int
921grub_font_get_ascent (grub_font_t font)
922{
923 return font->ascent;
924}
925
926/* Get the distance in pixels from the baseline to the lowest descenders
927 (for instance, in a lowercase 'y', 'g', etc.). */
928int
929grub_font_get_descent (grub_font_t font)
930{
931 return font->descent;
932}
933
97ea65d4
VS
934/* FIXME: not correct for all fonts. */
935int
936grub_font_get_xheight (grub_font_t font)
937{
938 return font->ascent / 2;
939}
940
1e901a75 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. */
945int
946grub_font_get_leading (grub_font_t font)
947{
948 return font->leading;
949}
950
951/* Get the distance in pixels between baselines of adjacent lines of text. */
952int
953grub_font_get_height (grub_font_t font)
954{
955 return font->ascent + font->descent + font->leading;
956}
957
1e901a75 958/* Get the glyph for FONT corresponding to the Unicode code point CODE.
26ba5c22 959 Returns the ASCII glyph for the code if no other fonts are available.
960 The glyphs are cached once loaded. */
1e901a75 961struct grub_font_glyph *
962grub_font_get_glyph (grub_font_t font, grub_uint32_t code)
963{
61e89d9d
VS
964 struct grub_font_glyph *glyph = 0;
965 if (font)
966 glyph = grub_font_get_glyph_internal (font, code);
1e901a75 967 if (glyph == 0)
26ba5c22 968 {
969 glyph = ascii_glyph_lookup (code);
970 }
1e901a75 971 return glyph;
972}
973
974
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. */
982static int
1118c32e 983get_font_diversity (grub_font_t a, grub_font_t b)
1e901a75 984{
985 int d;
986
987 d = 0;
988
989 if (a->ascent && b->ascent)
990 d += grub_abs (a->ascent - b->ascent) * 8;
991 else
992 /* Penalty for missing attributes. */
993 d += 50;
994
995 if (a->max_char_height && b->max_char_height)
996 d += grub_abs (a->max_char_height - b->max_char_height) * 8;
997 else
998 /* Penalty for missing attributes. */
999 d += 50;
b39f9d20 1000
1e901a75 1001 /* Weight is a minor factor. */
1002 d += (a->weight != b->weight) ? 5 : 0;
1003
1004 return d;
1005}
1006
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. */
1015struct grub_font_glyph *
1016grub_font_get_glyph_with_fallback (grub_font_t font, grub_uint32_t code)
1017{
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. */
1025 int best_diversity;
1026 struct grub_font_glyph *best_glyph;
1027
1028 if (font)
1029 {
1030 /* First try to get the glyph from the specified font. */
1031 glyph = grub_font_get_glyph_internal (font, code);
1032 if (glyph)
1118c32e 1033 return glyph;
1e901a75 1034 }
1035
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;
1039 best_glyph = 0;
1040
1041 for (node = grub_font_list; node; node = next)
1042 {
1043 grub_font_t curfont;
1044
1045 curfont = node->value;
1046 next = node->next;
1047
1048 glyph = grub_font_get_glyph_internal (curfont, code);
942a10c7
VS
1049 if (glyph && !font)
1050 return glyph;
1e901a75 1051 if (glyph)
1118c32e
VS
1052 {
1053 int d;
1054
1055 d = get_font_diversity (curfont, font);
1056 if (d < best_diversity)
1057 {
1058 best_diversity = d;
1059 best_glyph = glyph;
1060 }
1061 }
1e901a75 1062 }
1063
72f12cdc 1064 return best_glyph;
1e901a75 1065}
1066
72f12cdc
VS
1067static struct grub_font_glyph *
1068grub_font_dup_glyph (struct grub_font_glyph *glyph)
1069{
1070 static struct grub_font_glyph *ret;
1071 ret = grub_malloc (sizeof (*ret) + (glyph->width * glyph->height + 7) / 8);
1072 if (!ret)
1073 return NULL;
1074 grub_memcpy (ret, glyph, sizeof (*ret)
1075 + (glyph->width * glyph->height + 7) / 8);
1076 return ret;
1077}
1078
1079/* FIXME: suboptimal. */
1080static void
1081grub_font_blit_glyph (struct grub_font_glyph *target,
a87add6d 1082 struct grub_font_glyph *src, unsigned dx, unsigned dy)
72f12cdc
VS
1083{
1084 unsigned src_bit, tgt_bit, src_byte, tgt_byte;
1085 unsigned i, j;
1086 for (i = 0; i < src->height; i++)
1087 {
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++)
1093 {
1094 target->bitmap[tgt_byte] |= ((src->bitmap[src_byte] << src_bit)
1095 & 0x80) >> tgt_bit;
1096 src_bit++;
1097 tgt_bit++;
1098 if (src_bit == 8)
1099 {
1100 src_byte++;
1101 src_bit = 0;
1102 }
1103 if (tgt_bit == 8)
1104 {
1105 tgt_byte++;
1106 tgt_bit = 0;
1107 }
1108 }
1109 }
1110}
1111
97ea65d4
VS
1112static void
1113grub_font_blit_glyph_mirror (struct grub_font_glyph *target,
1114 struct grub_font_glyph *src,
1115 unsigned dx, unsigned dy)
1116{
1117 unsigned tgt_bit, src_byte, tgt_byte;
1118 signed src_bit;
1119 unsigned i, j;
1120 for (i = 0; i < src->height; i++)
1121 {
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++)
1127 {
1128 target->bitmap[tgt_byte] |= ((src->bitmap[src_byte] << src_bit)
1129 & 0x80) >> tgt_bit;
1130 src_bit--;
1131 tgt_bit++;
1132 if (src_bit == -1)
1133 {
1134 src_byte--;
1135 src_bit = 7;
1136 }
1137 if (tgt_bit == 8)
1138 {
1139 tgt_byte++;
1140 tgt_bit = 0;
1141 }
1142 }
1143 }
1144}
1145
d44892fe
CW
1146/* Context for blit_comb. */
1147struct blit_comb_ctx
1148{
1149 struct grub_font_glyph *glyph;
1150 int *device_width;
1151 struct grub_video_signed_rect bounds;
1152};
1153
1154/* Helper for blit_comb. */
1155static void
1156do_blit (struct grub_font_glyph *src, signed dx, signed dy,
1157 struct blit_comb_ctx *ctx)
1158{
1159 if (ctx->glyph)
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)
1163 {
1164 ctx->bounds.width += ctx->bounds.x - dx;
1165 ctx->bounds.x = dx;
1166 }
1167 if (ctx->bounds.y > -src->height - dy)
1168 {
1169 ctx->bounds.height += ctx->bounds.y - (-src->height - dy);
1170 ctx->bounds.y = (-src->height - dy);
1171 }
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)
1175 - ctx->bounds.y)
1176 ctx->bounds.height = src->height + (-src->height - dy) - ctx->bounds.y;
1177}
1178
1179/* Helper for blit_comb. */
1180static inline void
1181add_device_width (int val, struct blit_comb_ctx *ctx)
1182{
1183 if (ctx->glyph)
1184 ctx->glyph->device_width += val;
1185 if (ctx->device_width)
1186 *ctx->device_width += val;
1187}
1188
4361ca1c
VS
1189static void
1190blit_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,
a87add6d 1194 struct grub_font_glyph **combining_glyphs, int *device_width)
4361ca1c 1195{
d44892fe
CW
1196 struct blit_comb_ctx ctx = {
1197 .glyph = glyph,
1198 .device_width = device_width
1199 };
4361ca1c 1200 unsigned i;
09d708c6 1201 signed above_rightx, above_righty;
7624beba 1202 signed above_leftx, above_lefty;
45511ee9 1203 signed below_rightx, below_righty;
7624beba 1204 signed min_devwidth = 0;
4361ca1c 1205
0a239a82
VS
1206 if (glyph)
1207 glyph->device_width = main_glyph->device_width;
1208 if (device_width)
1209 *device_width = main_glyph->device_width;
1210
d44892fe
CW
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;
4361ca1c 1215
7624beba 1216 above_rightx = main_glyph->offset_x + main_glyph->width;
d44892fe 1217 above_righty = ctx.bounds.y + ctx.bounds.height;
09d708c6 1218
7624beba 1219 above_leftx = main_glyph->offset_x;
d44892fe 1220 above_lefty = ctx.bounds.y + ctx.bounds.height;
7624beba 1221
d44892fe
CW
1222 below_rightx = ctx.bounds.x + ctx.bounds.width;
1223 below_righty = ctx.bounds.y;
45511ee9 1224
4361ca1c
VS
1225 for (i = 0; i < glyph_id->ncomb; i++)
1226 {
4361ca1c 1227 grub_int16_t space = 0;
832d1370 1228 /* Center by default. */
e6d428c1 1229 grub_int16_t targetx;
a87add6d 1230
4361ca1c
VS
1231 if (!combining_glyphs[i])
1232 continue;
d44892fe 1233 targetx = (ctx.bounds.width - combining_glyphs[i]->width) / 2 + ctx.bounds.x;
1876cfdb 1234 /* CGJ is to avoid diacritics reordering. */
a87add6d
VS
1235 if (glyph_id->combining[i].code
1236 == GRUB_UNICODE_COMBINING_GRAPHEME_JOINER)
1876cfdb 1237 continue;
53c648d2 1238 switch (glyph_id->combining[i].type)
4361ca1c
VS
1239 {
1240 case GRUB_UNICODE_COMB_OVERLAY:
1241 do_blit (combining_glyphs[i],
832d1370 1242 targetx,
d44892fe
CW
1243 (ctx.bounds.height - combining_glyphs[i]->height) / 2
1244 - (ctx.bounds.height + ctx.bounds.y), &ctx);
7624beba
VS
1245 if (min_devwidth < combining_glyphs[i]->width)
1246 min_devwidth = combining_glyphs[i]->width;
4361ca1c
VS
1247 break;
1248
0a71a097 1249 case GRUB_UNICODE_COMB_ATTACHED_ABOVE_RIGHT:
d44892fe 1250 do_blit (combining_glyphs[i], above_rightx, -above_righty, &ctx);
0a71a097 1251 above_rightx += combining_glyphs[i]->width;
0a71a097
VS
1252 break;
1253
09d708c6
VS
1254 case GRUB_UNICODE_COMB_ABOVE_RIGHT:
1255 do_blit (combining_glyphs[i], above_rightx,
d44892fe 1256 -(above_righty + combining_glyphs[i]->height), &ctx);
09d708c6 1257 above_rightx += combining_glyphs[i]->width;
7624beba
VS
1258 break;
1259
1260 case GRUB_UNICODE_COMB_ABOVE_LEFT:
1261 above_leftx -= combining_glyphs[i]->width;
1262 do_blit (combining_glyphs[i], above_leftx,
d44892fe 1263 -(above_lefty + combining_glyphs[i]->height), &ctx);
09d708c6
VS
1264 break;
1265
45511ee9 1266 case GRUB_UNICODE_COMB_BELOW_RIGHT:
d44892fe 1267 do_blit (combining_glyphs[i], below_rightx, below_righty, &ctx);
45511ee9 1268 below_rightx += combining_glyphs[i]->width;
45511ee9
VS
1269 break;
1270
832d1370
VS
1271 case GRUB_UNICODE_COMB_HEBREW_HOLAM:
1272 if (glyph_id->base != GRUB_UNICODE_HEBREW_WAW)
a87add6d
VS
1273 targetx =
1274 main_glyph->offset_x - combining_glyphs[i]->width -
1275 (combining_glyphs[i]->width + 3) / 4;
832d1370
VS
1276 goto above_on_main;
1277
1278 case GRUB_UNICODE_COMB_HEBREW_SIN_DOT:
1279 targetx = main_glyph->offset_x + combining_glyphs[i]->width / 4;
1280 goto above_on_main;
1281
1282 case GRUB_UNICODE_COMB_HEBREW_SHIN_DOT:
a87add6d
VS
1283 targetx =
1284 main_glyph->width + main_glyph->offset_x -
1285 combining_glyphs[i]->width;
832d1370
VS
1286 above_on_main:
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 do_blit (combining_glyphs[i], targetx,
1292 -(main_glyph->height + main_glyph->offset_y + space
d44892fe 1293 + combining_glyphs[i]->height), &ctx);
832d1370
VS
1294 if (min_devwidth < combining_glyphs[i]->width)
1295 min_devwidth = combining_glyphs[i]->width;
1296 break;
1297
e2588ccb
VS
1298 /* TODO: Put dammah, fathah and alif nearer to shadda. */
1299 case GRUB_UNICODE_COMB_SYRIAC_SUPERSCRIPT_ALAPH:
ad109fe0
VS
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:
e2588ccb 1304 case GRUB_UNICODE_COMB_ARABIC_SUPERSCRIPT_ALIF:
ad109fe0
VS
1305 case GRUB_UNICODE_COMB_ARABIC_SUKUN:
1306 case GRUB_UNICODE_COMB_ARABIC_SHADDA:
832d1370 1307 case GRUB_UNICODE_COMB_HEBREW_RAFE:
4361ca1c 1308 case GRUB_UNICODE_STACK_ABOVE:
0546172e 1309 stacked_above:
4361ca1c 1310 space = combining_glyphs[i]->offset_y
832d1370
VS
1311 - grub_font_get_xheight (combining_glyphs[i]->font) - 1;
1312 if (space <= 0)
4361ca1c 1313 space = 1 + (grub_font_get_xheight (main_glyph->font)) / 8;
a87add6d
VS
1314
1315 case GRUB_UNICODE_STACK_ATTACHED_ABOVE:
832d1370 1316 do_blit (combining_glyphs[i], targetx,
d44892fe
CW
1317 -(ctx.bounds.height + ctx.bounds.y + space
1318 + combining_glyphs[i]->height), &ctx);
7624beba
VS
1319 if (min_devwidth < combining_glyphs[i]->width)
1320 min_devwidth = combining_glyphs[i]->width;
4361ca1c
VS
1321 break;
1322
0a2d5a82
VS
1323 case GRUB_UNICODE_COMB_HEBREW_DAGESH:
1324 do_blit (combining_glyphs[i], targetx,
d44892fe
CW
1325 -(ctx.bounds.height / 2 + ctx.bounds.y
1326 + combining_glyphs[i]->height / 2), &ctx);
0a2d5a82
VS
1327 if (min_devwidth < combining_glyphs[i]->width)
1328 min_devwidth = combining_glyphs[i]->width;
1329 break;
1330
832d1370
VS
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:
832d1370
VS
1336 /* TODO: placement in final kaf and under reish. */
1337
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:
ad109fe0
VS
1344 /* TODO: Put kasra and kasratan under shadda. */
1345 case GRUB_UNICODE_COMB_ARABIC_KASRA:
1346 case GRUB_UNICODE_COMB_ARABIC_KASRATAN:
9d9dca02
VS
1347 /* I don't know how ypogegrammeni differs from subscript. */
1348 case GRUB_UNICODE_COMB_YPOGEGRAMMENI:
4361ca1c 1349 case GRUB_UNICODE_STACK_BELOW:
0546172e 1350 stacked_below:
a87add6d 1351 space = -(combining_glyphs[i]->offset_y
4361ca1c 1352 + combining_glyphs[i]->height);
ad109fe0 1353 if (space <= 0)
4361ca1c 1354 space = 1 + (grub_font_get_xheight (main_glyph->font)) / 8;
a87add6d 1355
4361ca1c 1356 case GRUB_UNICODE_STACK_ATTACHED_BELOW:
d44892fe
CW
1357 do_blit (combining_glyphs[i], targetx, -(ctx.bounds.y - space),
1358 &ctx);
7624beba
VS
1359 if (min_devwidth < combining_glyphs[i]->width)
1360 min_devwidth = combining_glyphs[i]->width;
4361ca1c
VS
1361 break;
1362
0546172e
VS
1363 case GRUB_UNICODE_COMB_MN:
1364 switch (glyph_id->combining[i].code)
1365 {
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:
1375 goto stacked_above;
1376 case GRUB_UNICODE_THAANA_IBIFILI:
1377 case GRUB_UNICODE_THAANA_EEBEEFILI:
a87add6d 1378 goto stacked_below;
0546172e
VS
1379 }
1380 /* Fall through. */
4361ca1c
VS
1381 default:
1382 {
1383 /* Default handling. Just draw combining character on top
1384 of base character.
1385 FIXME: support more unicode types correctly.
a87add6d 1386 */
4361ca1c
VS
1387 do_blit (combining_glyphs[i],
1388 main_glyph->device_width
a87add6d
VS
1389 + combining_glyphs[i]->offset_x,
1390 -(combining_glyphs[i]->height
d44892fe
CW
1391 + combining_glyphs[i]->offset_y), &ctx);
1392 add_device_width (combining_glyphs[i]->device_width, &ctx);
4361ca1c
VS
1393 }
1394 }
1395 }
a87add6d
VS
1396 add_device_width ((above_rightx >
1397 below_rightx ? above_rightx : below_rightx) -
d44892fe
CW
1398 (main_glyph->offset_x + main_glyph->width), &ctx);
1399 add_device_width (above_leftx - main_glyph->offset_x, &ctx);
7624beba 1400 if (glyph && glyph->device_width < min_devwidth)
a87add6d 1401 glyph->device_width = min_devwidth;
7624beba
VS
1402 if (device_width && *device_width < min_devwidth)
1403 *device_width = min_devwidth;
1404
4361ca1c 1405 if (bounds_out)
d44892fe 1406 *bounds_out = ctx.bounds;
4361ca1c
VS
1407}
1408
72f12cdc 1409static struct grub_font_glyph *
0a239a82
VS
1410grub_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,
1414 int *device_width)
72f12cdc 1415{
e6d428c1 1416 struct grub_font_glyph *main_glyph = NULL;
72f12cdc 1417 struct grub_font_glyph **combining_glyphs;
e6d428c1 1418 grub_uint32_t desired_attributes = 0;
4e5a96b0
VS
1419 unsigned i;
1420 grub_uint32_t base = glyph_id->base;
0a239a82
VS
1421
1422 if (combining_glyphs_out)
1423 *combining_glyphs_out = NULL;
72f12cdc 1424
e6d428c1
VS
1425 if (glyph_id->attributes & GRUB_UNICODE_GLYPH_ATTRIBUTE_RIGHT_JOINED)
1426 desired_attributes |= GRUB_FONT_CODE_RIGHT_JOINED;
1427
1428 if (glyph_id->attributes & GRUB_UNICODE_GLYPH_ATTRIBUTE_LEFT_JOINED)
1429 desired_attributes |= GRUB_FONT_CODE_LEFT_JOINED;
1430
4e5a96b0
VS
1431
1432 if (base == 'i' || base == 'j')
1433 {
1434 for (i = 0; i < glyph_id->ncomb; i++)
1435 if (glyph_id->combining[i].type == GRUB_UNICODE_STACK_ABOVE)
1436 break;
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;
1441 }
1442
1443 main_glyph = grub_font_get_glyph_with_fallback (hinted_font, base
e6d428c1
VS
1444 | desired_attributes);
1445
1446 if (!main_glyph)
1447 main_glyph = grub_font_get_glyph_with_fallback (hinted_font,
4e5a96b0 1448 base);
72f12cdc 1449
53f312c1 1450 /* Glyph not available in any font. Use ASCII fallback. */
72f12cdc 1451 if (!main_glyph)
4e5a96b0 1452 main_glyph = ascii_glyph_lookup (base);
53f312c1
VS
1453
1454 /* Glyph not available in any font. Return unknown glyph. */
1455 if (!main_glyph)
0a239a82
VS
1456 return NULL;
1457
1458 if (device_width)
1459 *device_width = main_glyph->device_width;
72f12cdc 1460
97ea65d4 1461 if (!glyph_id->ncomb && !glyph_id->attributes)
0a239a82 1462 return main_glyph;
72f12cdc
VS
1463
1464 combining_glyphs = grub_malloc (sizeof (combining_glyphs[0])
1465 * glyph_id->ncomb);
97ea65d4 1466 if (glyph_id->ncomb && !combining_glyphs)
72f12cdc
VS
1467 {
1468 grub_errno = GRUB_ERR_NONE;
0a239a82 1469 return main_glyph;
72f12cdc 1470 }
a87add6d 1471
4e5a96b0
VS
1472 for (i = 0; i < glyph_id->ncomb; i++)
1473 combining_glyphs[i]
1474 = grub_font_get_glyph_with_fallback (main_glyph->font,
1475 glyph_id->combining[i].code);
82c5d8dc 1476
a87add6d
VS
1477 blit_comb (glyph_id, NULL, bounds, main_glyph, combining_glyphs,
1478 device_width);
0a239a82
VS
1479 if (combining_glyphs_out)
1480 *combining_glyphs_out = combining_glyphs;
1481 else
1482 grub_free (combining_glyphs);
1483
1484 return main_glyph;
1485}
1486
1487int
1488grub_font_get_constructed_device_width (grub_font_t hinted_font,
a87add6d
VS
1489 const struct grub_unicode_glyph
1490 *glyph_id)
0a239a82
VS
1491{
1492 int ret;
1493 struct grub_font_glyph *main_glyph;
1494 main_glyph = grub_font_construct_dry_run (hinted_font, glyph_id, NULL,
1495 NULL, &ret);
1496 if (!main_glyph)
1497 return unknown_glyph->device_width;
1498 return ret;
1499}
1500
1501struct grub_font_glyph *
1502grub_font_construct_glyph (grub_font_t hinted_font,
1503 const struct grub_unicode_glyph *glyph_id)
1504{
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;
1509
1510 main_glyph = grub_font_construct_dry_run (hinted_font, glyph_id,
1511 &bounds, &combining_glyphs, NULL);
1512
1513 if (!main_glyph)
1514 return grub_font_dup_glyph (unknown_glyph);
97ea65d4 1515
0a239a82
VS
1516 if (!combining_glyphs)
1517 return grub_font_dup_glyph (main_glyph);
a87add6d
VS
1518
1519 glyph =
1520 grub_zalloc (sizeof (*glyph) + (bounds.width * bounds.height + 7) / 8);
72f12cdc
VS
1521 if (!glyph)
1522 {
1523 grub_errno = GRUB_ERR_NONE;
1524 return grub_font_dup_glyph (main_glyph);
1525 }
1526
0a239a82 1527 glyph->font = main_glyph->font;
4361ca1c
VS
1528 glyph->width = bounds.width;
1529 glyph->height = bounds.height;
1530 glyph->offset_x = bounds.x;
1531 glyph->offset_y = bounds.y;
72f12cdc 1532
97ea65d4
VS
1533 if (glyph_id->attributes & GRUB_UNICODE_GLYPH_ATTRIBUTE_MIRROR)
1534 grub_font_blit_glyph_mirror (glyph, main_glyph,
4361ca1c
VS
1535 main_glyph->offset_x - glyph->offset_x,
1536 (glyph->height + glyph->offset_y)
a87add6d
VS
1537 - (main_glyph->height +
1538 main_glyph->offset_y));
97ea65d4 1539 else
4361ca1c
VS
1540 grub_font_blit_glyph (glyph, main_glyph,
1541 main_glyph->offset_x - glyph->offset_x,
1542 (glyph->height + glyph->offset_y)
97ea65d4
VS
1543 - (main_glyph->height + main_glyph->offset_y));
1544
0a239a82 1545 blit_comb (glyph_id, glyph, NULL, main_glyph, combining_glyphs, NULL);
72f12cdc
VS
1546
1547 return glyph;
1548}
1e901a75 1549
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. */
1553grub_err_t
1118c32e
VS
1554grub_font_draw_glyph (struct grub_font_glyph * glyph,
1555 grub_video_color_t color, int left_x, int baseline_y)
1e901a75 1556{
1557 struct grub_video_bitmap glyph_bitmap;
1558
1559 /* Don't try to draw empty glyphs (U+0020, etc.). */
1560 if (glyph->width == 0 || glyph->height == 0)
1561 return GRUB_ERR_NONE;
1562
1563 glyph_bitmap.mode_info.width = glyph->width;
1564 glyph_bitmap.mode_info.height = glyph->height;
a87add6d
VS
1565 glyph_bitmap.mode_info.mode_type
1566 = (1 << GRUB_VIDEO_MODE_TYPE_DEPTH_POS) | GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP;
1e901a75 1567 glyph_bitmap.mode_info.blit_format = GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED;
1568 glyph_bitmap.mode_info.bpp = 1;
b39f9d20 1569
1e901a75 1570 /* Really 1 bit per pixel. */
1571 glyph_bitmap.mode_info.bytes_per_pixel = 0;
b39f9d20 1572
1e901a75 1573 /* Packed densely as bits. */
1574 glyph_bitmap.mode_info.pitch = glyph->width;
b39f9d20 1575
1e901a75 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;
1118c32e
VS
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);
1e901a75 1586 glyph_bitmap.data = glyph->bitmap;
1587
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;
1591
1592 return grub_video_blit_bitmap (&glyph_bitmap, GRUB_VIDEO_BLIT_BLEND,
1118c32e
VS
1593 bitmap_left, bitmap_top,
1594 0, 0, glyph->width, glyph->height);
1e901a75 1595}