font.c 43.8 KB
Newer Older
1 2 3
/* font.c - Font API and font file loader.  */
/*
 *  GRUB  --  GRand Unified Bootloader
4
 *  Copyright (C) 2003,2005,2006,2007,2008,2009,2010  Free Software Foundation, Inc.
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
 *
 *  GRUB is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  GRUB is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <grub/bufio.h>
#include <grub/dl.h>
#include <grub/file.h>
#include <grub/font.h>
#include <grub/misc.h>
#include <grub/mm.h>
#include <grub/types.h>
#include <grub/video.h>
#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 35
GRUB_MOD_LICENSE ("GPLv3+");

36
#if HAVE_FONT_SOURCE
37 38
#include "ascii.h"
#endif
39 40 41 42 43 44 45 46 47 48

#ifndef FONT_DEBUG
#define FONT_DEBUG 0
#endif

struct char_index_entry
{
  grub_uint32_t code;
  grub_uint8_t storage_flags;
  grub_uint32_t offset;
49

50 51 52 53 54 55
  /* Glyph if loaded, or NULL otherwise.  */
  struct grub_font_glyph *glyph;
};

#define FONT_WEIGHT_NORMAL 100
#define FONT_WEIGHT_BOLD 200
56
#define ASCII_BITMAP_SIZE 16
57 58 59 60 61 62 63 64 65 66 67 68 69

/* Definition of font registry.  */
struct grub_font_node *grub_font_list;

static int register_font (grub_font_t font);
static void font_init (grub_font_t font);
static void free_font (grub_font_t font);
static void remove_font (grub_font_t font);

struct font_file_section
{
  /* The file this section is in.  */
  grub_file_t file;
70

71 72
  /* FOURCC name of the section.  */
  char name[4];
73

74 75
  /* Length of the section contents.  */
  grub_uint32_t length;
76

77 78 79 80 81
  /* Set by open_section() on EOF.  */
  int eof;
};

/* Replace unknown glyphs with a rounded question mark.  */
82
static grub_uint8_t unknown_glyph_bitmap[] = {
83
  /*       76543210 */
84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99
  0x7C,				/*  ooooo   */
  0x82,				/* o     o  */
  0xBA,				/* o ooo o  */
  0xAA,				/* o o o o  */
  0xAA,				/* o o o o  */
  0x8A,				/* o   o o  */
  0x9A,				/* o  oo o  */
  0x92,				/* o  o  o  */
  0x92,				/* o  o  o  */
  0x92,				/* o  o  o  */
  0x92,				/* o  o  o  */
  0x82,				/* o     o  */
  0x92,				/* o  o  o  */
  0x82,				/* o     o  */
  0x7C,				/*  ooooo   */
  0x00				/*          */
100 101 102 103 104 105 106 107 108 109 110 111 112
};

/* The "unknown glyph" glyph, used as a last resort.  */
static struct grub_font_glyph *unknown_glyph;

/* The font structure used when no other font is loaded.  This functions
   as a "Null Object" pattern, so that code everywhere does not have to
   check for a NULL grub_font_t to avoid dereferencing a null pointer.  */
static struct grub_font null_font;

/* Flag to ensure module is initialized only once.  */
static grub_uint8_t font_loader_initialized;

113
#if HAVE_FONT_SOURCE
114 115 116 117 118 119
static struct grub_font_glyph *ascii_font_glyph[0x80];
#endif

static struct grub_font_glyph *
ascii_glyph_lookup (grub_uint32_t code)
{
120
#if HAVE_FONT_SOURCE
121 122 123
  static int ascii_failback_initialized = 0;

  if (code >= 0x80)
124
    return NULL;
125 126 127 128 129

  if (ascii_failback_initialized == 0)
    {
      int current;
      for (current = 0; current < 0x80; current++)
130 131 132 133 134 135 136 137
	{
	  ascii_font_glyph[current] =
	    grub_malloc (sizeof (struct grub_font_glyph) + ASCII_BITMAP_SIZE);

	  ascii_font_glyph[current]->width = 8;
	  ascii_font_glyph[current]->height = 16;
	  ascii_font_glyph[current]->offset_x = 0;
	  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 144 145 146 147 148 149 150 151
		       ASCII_BITMAP_SIZE);
	}

      ascii_failback_initialized = 1;
    }

  return ascii_font_glyph[code];
#else
  (void) code;
152
  return NULL;
153 154 155
#endif
}

156 157 158 159 160 161 162 163
void
grub_font_loader_init (void)
{
  /* Only initialize font loader once.  */
  if (font_loader_initialized)
    return;

  /* Make glyph for unknown glyph.  */
164 165 166
  unknown_glyph = grub_malloc (sizeof (struct grub_font_glyph)
			       + sizeof (unknown_glyph_bitmap));
  if (!unknown_glyph)
167 168 169 170 171
    return;

  unknown_glyph->width = 8;
  unknown_glyph->height = 16;
  unknown_glyph->offset_x = 0;
172
  unknown_glyph->offset_y = -3;
173
  unknown_glyph->device_width = 8;
174 175
  grub_memcpy (unknown_glyph->bitmap,
	       unknown_glyph_bitmap, sizeof (unknown_glyph_bitmap));
176 177 178

  /* Initialize the null font.  */
  font_init (&null_font);
179 180
  /* FIXME: Fix this slightly improper cast.  */
  null_font.name = (char *) "<No Font>";
181
  null_font.ascent = unknown_glyph->height - 3;
182
  null_font.descent = 3;
183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
  null_font.max_char_width = unknown_glyph->width;
  null_font.max_char_height = unknown_glyph->height;

  font_loader_initialized = 1;
}

/* Initialize the font object with initial default values.  */
static void
font_init (grub_font_t font)
{
  font->name = 0;
  font->file = 0;
  font->family = 0;
  font->point_size = 0;
  font->weight = 0;
198

199 200
  /* Default leading value, not in font file yet.  */
  font->leading = 1;
201

202 203 204 205 206 207
  font->max_char_width = 0;
  font->max_char_height = 0;
  font->ascent = 0;
  font->descent = 0;
  font->num_chars = 0;
  font->char_index = 0;
208
  font->bmp_idx = 0;
209 210 211 212 213 214
}

/* Open the next section in the file.

   On success, the section name is stored in section->name and the length in
   section->length, and 0 is returned.  On failure, 1 is returned and
215
   grub_errno is set appropriately with an error message.
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237

   If 1 is returned due to being at the end of the file, then section->eof is
   set to 1; otherwise, section->eof is set to 0.  */
static int
open_section (grub_file_t file, struct font_file_section *section)
{
  grub_ssize_t retval;
  grub_uint32_t raw_length;

  section->file = file;
  section->eof = 0;

  /* Read the FOURCC section name.  */
  retval = grub_file_read (file, section->name, 4);
  if (retval >= 0 && retval < 4)
    {
      /* EOF encountered.  */
      section->eof = 1;
      return 1;
    }
  else if (retval < 0)
    {
238
      /* Read error.  */
239 240 241 242
      return 1;
    }

  /* Read the big-endian 32-bit section length.  */
243
  retval = grub_file_read (file, &raw_length, 4);
244 245 246 247 248 249 250 251
  if (retval >= 0 && retval < 4)
    {
      /* EOF encountered.  */
      section->eof = 1;
      return 1;
    }
  else if (retval < 0)
    {
252
      /* Read error.  */
253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272
      return 1;
    }

  /* Convert byte-order and store in *length.  */
  section->length = grub_be_to_cpu32 (raw_length);

  return 0;
}

/* Size in bytes of each character index (CHIX section)
   entry in the font file.  */
#define FONT_CHAR_INDEX_ENTRY_SIZE (4 + 1 + 4)

/* Load the character index (CHIX) section contents from the font file.  This
   presumes that the position of FILE is positioned immediately after the
   section length for the CHIX section (i.e., at the start of the section
   contents).  Returns 0 upon success, nonzero for failure (in which case
   grub_errno is set appropriately).  */
static int
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 281 282 283 284 285
#endif

  /* Sanity check: ensure section length is divisible by the entry size.  */
  if ((sect_length % FONT_CHAR_INDEX_ENTRY_SIZE) != 0)
    {
      grub_error (GRUB_ERR_BAD_FONT,
286 287 288
		  "font file format error: character index length %d "
		  "is not a multiple of the entry size %d",
		  sect_length, FONT_CHAR_INDEX_ENTRY_SIZE);
289 290 291 292 293 294 295 296
      return 1;
    }

  /* Calculate the number of characters.  */
  font->num_chars = sect_length / FONT_CHAR_INDEX_ENTRY_SIZE;

  /* Allocate the character index array.  */
  font->char_index = grub_malloc (font->num_chars
297 298
				  * sizeof (struct char_index_entry));
  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 304
  grub_memset (font->bmp_idx, 0xff, 0x10000 * sizeof (grub_uint16_t));

305 306

#if FONT_DEBUG >= 2
307
  grub_dprintf ("font", "num_chars=%d)\n", font->num_chars);
308 309
#endif

310 311
  last_code = 0;

312 313 314 315 316 317
  /* Load the character index data from the file.  */
  for (i = 0; i < font->num_chars; i++)
    {
      struct char_index_entry *entry = &font->char_index[i];

      /* Read code point value; convert to native byte order.  */
318
      if (grub_file_read (file, &entry->code, 4) != 4)
319
	return 1;
320 321
      entry->code = grub_be_to_cpu32 (entry->code);

322 323
      /* Verify that characters are in ascending order.  */
      if (i != 0 && entry->code <= last_code)
324 325 326 327 328 329
	{
	  grub_error (GRUB_ERR_BAD_FONT,
		      "font characters not in ascending order: %u <= %u",
		      entry->code, last_code);
	  return 1;
	}
330

331 332 333
      if (entry->code < 0x10000)
	font->bmp_idx[entry->code] = i;

334 335
      last_code = entry->code;

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 344 345 346 347 348 349 350
      entry->offset = grub_be_to_cpu32 (entry->offset);

      /* No glyph loaded.  Will be loaded on demand and cached thereafter.  */
      entry->glyph = 0;

#if FONT_DEBUG >= 5
      /* Print the 1st 10 characters.  */
      if (i < 10)
351
	grub_dprintf ("font", "c=%d o=%d\n", entry->code, entry->offset);
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366
#endif
    }

  return 0;
}

/* Read the contents of the specified section as a string, which is
   allocated on the heap.  Returns 0 if there is an error.  */
static char *
read_section_as_string (struct font_file_section *section)
{
  char *str;
  grub_ssize_t ret;

  str = grub_malloc (section->length + 1);
367
  if (!str)
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
    return 0;

  ret = grub_file_read (section->file, str, section->length);
  if (ret < 0 || ret != (grub_ssize_t) section->length)
    {
      grub_free (str);
      return 0;
    }

  str[section->length] = '\0';
  return str;
}

/* Read the contents of the current section as a 16-bit integer value,
   which is stored into *VALUE.
   Returns 0 upon success, nonzero upon failure.  */
static int
385 386
read_section_as_short (struct font_file_section *section,
		       grub_int16_t * value)
387 388 389 390 391 392
{
  grub_uint16_t raw_value;

  if (section->length != 2)
    {
      grub_error (GRUB_ERR_BAD_FONT,
393 394 395 396
		  "font file format error: section %c%c%c%c length "
		  "is %d but should be 2",
		  section->name[0], section->name[1],
		  section->name[2], section->name[3], section->length);
397 398
      return 1;
    }
399
  if (grub_file_read (section->file, &raw_value, 2) != 2)
400 401 402 403 404 405 406 407
    return 1;

  *value = grub_be_to_cpu16 (raw_value);
  return 0;
}

/* Load a font and add it to the beginning of the global font list.
   Returns 0 upon success, nonzero upon failure.  */
408
grub_font_t
409 410 411 412 413 414 415 416
grub_font_load (const char *filename)
{
  grub_file_t file = 0;
  struct font_file_section section;
  char magic[4];
  grub_font_t font = 0;

#if FONT_DEBUG >= 1
417
  grub_dprintf ("font", "add_font(%s)\n", filename);
418 419
#endif

420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443
  if (filename[0] == '(' || filename[0] == '/' || filename[0] == '+')
    file = grub_buffile_open (filename, 1024);
  else
    {
      const char *prefix = grub_env_get ("prefix");
      char *fullname, *ptr;
      if (!prefix)
	{
	  grub_error (GRUB_ERR_FILE_NOT_FOUND, N_("variable `%s' isn't set"),
		      "prefix");
	  goto fail;
	}
      fullname = grub_malloc (grub_strlen (prefix) + grub_strlen (filename) + 1
			      + sizeof ("/fonts/") + sizeof (".pf2"));
      if (!fullname)
	goto fail;
      ptr = grub_stpcpy (fullname, prefix);
      ptr = grub_stpcpy (ptr, "/fonts/");
      ptr = grub_stpcpy (ptr, filename);
      ptr = grub_stpcpy (ptr, ".pf2");
      *ptr = 0;
      file = grub_buffile_open (fullname, 1024);
      grub_free (fullname);
    }
444 445 446 447
  if (!file)
    goto fail;

#if FONT_DEBUG >= 3
448
  grub_dprintf ("font", "file opened\n");
449 450 451 452 453 454 455
#endif

  /* Read the FILE section.  It indicates the file format.  */
  if (open_section (file, &section) != 0)
    goto fail;

#if FONT_DEBUG >= 3
456
  grub_dprintf ("font", "opened FILE section\n");
457
#endif
458
  if (grub_memcmp (section.name, FONT_FORMAT_SECTION_NAMES_FILE,
459
		   sizeof (FONT_FORMAT_SECTION_NAMES_FILE) - 1) != 0)
460 461
    {
      grub_error (GRUB_ERR_BAD_FONT,
462
		  "font file format error: 1st section must be FILE");
463 464 465 466
      goto fail;
    }

#if FONT_DEBUG >= 3
467
  grub_dprintf ("font", "section name ok\n");
468 469 470 471
#endif
  if (section.length != 4)
    {
      grub_error (GRUB_ERR_BAD_FONT,
472 473
		  "font file format error (file type ID length is %d "
		  "but should be 4)", section.length);
474 475 476 477
      goto fail;
    }

#if FONT_DEBUG >= 3
478
  grub_dprintf ("font", "section length ok\n");
479 480 481 482 483 484
#endif
  /* Check the file format type code.  */
  if (grub_file_read (file, magic, 4) != 4)
    goto fail;

#if FONT_DEBUG >= 3
485
  grub_dprintf ("font", "read magic ok\n");
486 487
#endif

488
  if (grub_memcmp (magic, FONT_FORMAT_PFF2_MAGIC, 4) != 0)
489
    {
490
      grub_error (GRUB_ERR_BAD_FONT, "invalid font magic %x %x %x %x",
491
		  magic[0], magic[1], magic[2], magic[3]);
492 493 494 495
      goto fail;
    }

#if FONT_DEBUG >= 3
496
  grub_dprintf ("font", "compare magic ok\n");
497 498 499
#endif

  /* Allocate the font object.  */
500
  font = (grub_font_t) grub_zalloc (sizeof (struct grub_font));
501
  if (!font)
502 503 504 505 506 507
    goto fail;

  font_init (font);
  font->file = file;

#if FONT_DEBUG >= 3
508
  grub_dprintf ("font", "allocate font ok; loading font info\n");
509 510 511 512 513 514
#endif

  /* Load the font information.  */
  while (1)
    {
      if (open_section (file, &section) != 0)
515 516 517 518 519 520
	{
	  if (section.eof)
	    break;		/* Done reading the font file.  */
	  else
	    goto fail;
	}
521 522

#if FONT_DEBUG >= 2
523
      grub_dprintf ("font", "opened section %c%c%c%c ok\n",
524 525
		   section.name[0], section.name[1],
		   section.name[2], section.name[3]);
526 527
#endif

528
      if (grub_memcmp (section.name, FONT_FORMAT_SECTION_NAMES_FONT_NAME,
529 530 531 532 533 534
		       sizeof (FONT_FORMAT_SECTION_NAMES_FONT_NAME) - 1) == 0)
	{
	  font->name = read_section_as_string (&section);
	  if (!font->name)
	    goto fail;
	}
535
      else if (grub_memcmp (section.name,
536 537 538 539 540 541 542
			    FONT_FORMAT_SECTION_NAMES_POINT_SIZE,
			    sizeof (FONT_FORMAT_SECTION_NAMES_POINT_SIZE) -
			    1) == 0)
	{
	  if (read_section_as_short (&section, &font->point_size) != 0)
	    goto fail;
	}
543
      else if (grub_memcmp (section.name, FONT_FORMAT_SECTION_NAMES_WEIGHT,
544 545 546 547 548 549 550 551 552 553 554 555 556 557
			    sizeof (FONT_FORMAT_SECTION_NAMES_WEIGHT) - 1)
	       == 0)
	{
	  char *wt;
	  wt = read_section_as_string (&section);
	  if (!wt)
	    continue;
	  /* Convert the weight string 'normal' or 'bold' into a number.  */
	  if (grub_strcmp (wt, "normal") == 0)
	    font->weight = FONT_WEIGHT_NORMAL;
	  else if (grub_strcmp (wt, "bold") == 0)
	    font->weight = FONT_WEIGHT_BOLD;
	  grub_free (wt);
	}
558
      else if (grub_memcmp (section.name,
559 560 561 562 563 564 565
			    FONT_FORMAT_SECTION_NAMES_MAX_CHAR_WIDTH,
			    sizeof (FONT_FORMAT_SECTION_NAMES_MAX_CHAR_WIDTH)
			    - 1) == 0)
	{
	  if (read_section_as_short (&section, &font->max_char_width) != 0)
	    goto fail;
	}
566
      else if (grub_memcmp (section.name,
567 568 569 570 571 572 573
			    FONT_FORMAT_SECTION_NAMES_MAX_CHAR_HEIGHT,
			    sizeof (FONT_FORMAT_SECTION_NAMES_MAX_CHAR_HEIGHT)
			    - 1) == 0)
	{
	  if (read_section_as_short (&section, &font->max_char_height) != 0)
	    goto fail;
	}
574
      else if (grub_memcmp (section.name,
575 576 577 578 579 580 581
			    FONT_FORMAT_SECTION_NAMES_ASCENT,
			    sizeof (FONT_FORMAT_SECTION_NAMES_ASCENT) - 1)
	       == 0)
	{
	  if (read_section_as_short (&section, &font->ascent) != 0)
	    goto fail;
	}
582
      else if (grub_memcmp (section.name, FONT_FORMAT_SECTION_NAMES_DESCENT,
583 584 585 586 587 588
			    sizeof (FONT_FORMAT_SECTION_NAMES_DESCENT) - 1)
	       == 0)
	{
	  if (read_section_as_short (&section, &font->descent) != 0)
	    goto fail;
	}
589
      else if (grub_memcmp (section.name,
590 591 592 593 594 595 596
			    FONT_FORMAT_SECTION_NAMES_CHAR_INDEX,
			    sizeof (FONT_FORMAT_SECTION_NAMES_CHAR_INDEX) -
			    1) == 0)
	{
	  if (load_font_index (file, section.length, font) != 0)
	    goto fail;
	}
597
      else if (grub_memcmp (section.name, FONT_FORMAT_SECTION_NAMES_DATA,
598 599 600 601 602
			    sizeof (FONT_FORMAT_SECTION_NAMES_DATA) - 1) == 0)
	{
	  /* When the DATA section marker is reached, we stop reading.  */
	  break;
	}
603
      else
604 605
	{
	  /* Unhandled section type, simply skip past it.  */
606
#if FONT_DEBUG >= 3
607
	  grub_dprintf ("font", "Unhandled section type, skipping.\n");
608
#endif
609 610 611 612
	  grub_off_t section_end = grub_file_tell (file) + section.length;
	  if ((int) grub_file_seek (file, section_end) == -1)
	    goto fail;
	}
613 614
    }

615
  if (!font->name)
616
    {
617
      grub_dprintf ("font", "Font has no name.\n");
618 619 620 621
      font->name = grub_strdup ("Unknown");
    }

#if FONT_DEBUG >= 1
622
  grub_dprintf ("font", "Loaded font `%s'.\n"
623 624 625 626
	       "Ascent=%d Descent=%d MaxW=%d MaxH=%d Number of characters=%d.\n",
	       font->name,
	       font->ascent, font->descent,
	       font->max_char_width, font->max_char_height, font->num_chars);
627 628 629 630 631
#endif

  if (font->max_char_width == 0
      || font->max_char_height == 0
      || font->num_chars == 0
632
      || font->char_index == 0 || font->ascent == 0 || font->descent == 0)
633 634
    {
      grub_error (GRUB_ERR_BAD_FONT,
635
		  "invalid font file: missing some required data");
636 637 638 639 640 641 642
      goto fail;
    }

  /* Add the font to the global font registry.  */
  if (register_font (font) != 0)
    goto fail;

643
  return font;
644 645

fail:
646 647 648 649 650
  if (file)
    grub_file_close (file);
  if (font)
    font->file = 0;

651
  free_font (font);
652
  return 0;
653 654 655 656 657 658 659 660
}

/* Read a 16-bit big-endian integer from FILE, convert it to native byte
   order, and store it in *VALUE.
   Returns 0 on success, 1 on failure.  */
static int
read_be_uint16 (grub_file_t file, grub_uint16_t * value)
{
661
  if (grub_file_read (file, value, 2) != 2)
662 663 664 665 666 667 668 669 670 671 672 673 674 675
    return 1;
  *value = grub_be_to_cpu16 (*value);
  return 0;
}

static int
read_be_int16 (grub_file_t file, grub_int16_t * value)
{
  /* For the signed integer version, use the same code as for unsigned.  */
  return read_be_uint16 (file, (grub_uint16_t *) value);
}

/* Return a pointer to the character index entry for the glyph corresponding to
   the codepoint CODE in the font FONT.  If not found, return zero.  */
676
static inline struct char_index_entry *
677 678
find_glyph (const grub_font_t font, grub_uint32_t code)
{
679 680 681 682 683 684
  struct char_index_entry *table;
  grub_size_t lo;
  grub_size_t hi;
  grub_size_t mid;

  table = font->char_index;
685 686

  /* Use BMP index if possible.  */
687
  if (code < 0x10000 && font->bmp_idx)
688 689 690 691 692 693 694
    {
      if (font->bmp_idx[code] == 0xffff)
	return 0;
      return &table[font->bmp_idx[code]];
    }

  /* Do a binary search in `char_index', which is ordered by code point.  */
695 696
  lo = 0;
  hi = font->num_chars - 1;
697

698
  if (!table)
699 700
    return 0;

701
  while (lo <= hi)
702
    {
703 704
      mid = lo + (hi - lo) / 2;
      if (code < table[mid].code)
705
	hi = mid - 1;
706
      else if (code > table[mid].code)
707
	lo = mid + 1;
708
      else
709
	return &table[mid];
710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734
    }

  return 0;
}

/* Get a glyph for the Unicode character CODE in FONT.  The glyph is loaded
   from the font file if has not been loaded yet.
   Returns a pointer to the glyph if found, or 0 if it is not found.  */
static struct grub_font_glyph *
grub_font_get_glyph_internal (grub_font_t font, grub_uint32_t code)
{
  struct char_index_entry *index_entry;

  index_entry = find_glyph (font, code);
  if (index_entry)
    {
      struct grub_font_glyph *glyph = 0;
      grub_uint16_t width;
      grub_uint16_t height;
      grub_int16_t xoff;
      grub_int16_t yoff;
      grub_int16_t dwidth;
      int len;

      if (index_entry->glyph)
735 736
	/* Return cached glyph.  */
	return index_entry->glyph;
737

738 739 740
      if (!font->file)
	/* No open file, can't load any glyphs.  */
	return 0;
741 742 743 744 745 746 747 748

      /* Make sure we can find glyphs for error messages.  Push active
         error message to error stack and reset error message.  */
      grub_error_push ();

      grub_file_seek (font->file, index_entry->offset);

      /* Read the glyph width, height, and baseline.  */
749 750 751 752 753 754 755 756 757
      if (read_be_uint16 (font->file, &width) != 0
	  || read_be_uint16 (font->file, &height) != 0
	  || read_be_int16 (font->file, &xoff) != 0
	  || read_be_int16 (font->file, &yoff) != 0
	  || read_be_int16 (font->file, &dwidth) != 0)
	{
	  remove_font (font);
	  return 0;
	}
758 759 760

      len = (width * height + 7) / 8;
      glyph = grub_malloc (sizeof (struct grub_font_glyph) + len);
761 762 763 764 765
      if (!glyph)
	{
	  remove_font (font);
	  return 0;
	}
766 767 768 769 770 771 772 773 774 775

      glyph->font = font;
      glyph->width = width;
      glyph->height = height;
      glyph->offset_x = xoff;
      glyph->offset_y = yoff;
      glyph->device_width = dwidth;

      /* Don't try to read empty bitmaps (e.g., space characters).  */
      if (len != 0)
776 777 778 779
	{
	  if (grub_file_read (font->file, glyph->bitmap, len) != len)
	    {
	      remove_font (font);
780
	      grub_free (glyph);
781 782 783
	      return 0;
	    }
	}
784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806

      /* Restore old error message.  */
      grub_error_pop ();

      /* Cache the glyph.  */
      index_entry->glyph = glyph;

      return glyph;
    }

  return 0;
}

/* Free the memory used by FONT.
   This should not be called if the font has been made available to
   users (once it is added to the global font list), since there would
   be the possibility of a dangling pointer.  */
static void
free_font (grub_font_t font)
{
  if (font)
    {
      if (font->file)
807
	grub_file_close (font->file);
808 809 810
      grub_free (font->name);
      grub_free (font->family);
      grub_free (font->char_index);
811
      grub_free (font->bmp_idx);
812 813 814 815 816 817 818 819 820 821 822 823 824
      grub_free (font);
    }
}

/* Add FONT to the global font registry.
   Returns 0 upon success, nonzero on failure
   (the font was not registered).  */
static int
register_font (grub_font_t font)
{
  struct grub_font_node *node = 0;

  node = grub_malloc (sizeof (struct grub_font_node));
825
  if (!node)
826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842
    return 1;

  node->value = font;
  node->next = grub_font_list;
  grub_font_list = node;

  return 0;
}

/* Remove the font from the global font list.  We don't actually free the
   font's memory since users could be holding references to the font.  */
static void
remove_font (grub_font_t font)
{
  struct grub_font_node **nextp, *cur;

  for (nextp = &grub_font_list, cur = *nextp;
843
       cur; nextp = &cur->next, cur = cur->next)
844 845
    {
      if (cur->value == font)
846 847
	{
	  *nextp = cur->next;
848

849 850
	  /* Free the node, but not the font itself.  */
	  grub_free (cur);
851

852 853
	  return;
	}
854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869
    }
}

/* Get a font from the list of loaded fonts.  This function will return
   another font if the requested font is not available.  If no fonts are
   loaded, then a special 'null font' is returned, which contains no glyphs,
   but is not a null pointer so the caller may omit checks for NULL.  */
grub_font_t
grub_font_get (const char *font_name)
{
  struct grub_font_node *node;

  for (node = grub_font_list; node; node = node->next)
    {
      grub_font_t font = node->value;
      if (grub_strcmp (font->name, font_name) == 0)
870
	return font;
871 872 873 874 875 876 877 878 879 880 881
    }

  /* If no font by that name is found, return the first font in the list
     as a fallback.  */
  if (grub_font_list && grub_font_list->value)
    return grub_font_list->value;
  else
    /* The null_font is a last resort.  */
    return &null_font;
}

882
/* Get the full name of the font.  */
883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903
const char *
grub_font_get_name (grub_font_t font)
{
  return font->name;
}

/* Get the maximum width of any character in the font in pixels.  */
int
grub_font_get_max_char_width (grub_font_t font)
{
  return font->max_char_width;
}

/* Get the distance in pixels from the baseline to the lowest descenders
   (for instance, in a lowercase 'y', 'g', etc.).  */
int
grub_font_get_descent (grub_font_t font)
{
  return font->descent;
}

904 905 906 907 908 909 910
/* FIXME: not correct for all fonts.  */
int
grub_font_get_xheight (grub_font_t font)
{
  return font->ascent / 2;
}

911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928
/* Get the *standard leading* of the font in pixel, which is the spacing
   between two lines of text.  Specifically, it is the space between the
   descent of one line and the ascent of the next line.  This is included
   in the *height* metric.  */
int
grub_font_get_leading (grub_font_t font)
{
  return font->leading;
}

/* Get the distance in pixels between baselines of adjacent lines of text.  */
int
grub_font_get_height (grub_font_t font)
{
  return font->ascent + font->descent + font->leading;
}

/* Get the glyph for FONT corresponding to the Unicode code point CODE.
929 930
   Returns the ASCII glyph for the code if no other fonts are available. 
   The glyphs are cached once loaded.  */
931 932 933
struct grub_font_glyph *
grub_font_get_glyph (grub_font_t font, grub_uint32_t code)
{
934 935 936
  struct grub_font_glyph *glyph = 0;
  if (font)
    glyph = grub_font_get_glyph_internal (font, code);
937
  if (glyph == 0)
938 939 940
    {
      glyph = ascii_glyph_lookup (code);
    }
941 942 943 944 945 946 947 948 949 950 951 952
  return glyph;
}


/* Calculate a subject value representing "how similar" two fonts are.
   This is used to prioritize the order that fonts are scanned for missing
   glyphs.  The object is to select glyphs from the most similar font
   possible, for the best appearance.
   The heuristic is crude, but it helps greatly when fonts of similar
   sizes are used so that tiny 8 point glyphs are not mixed into a string
   of 24 point text unless there is no other choice.  */
static int
953
get_font_diversity (grub_font_t a, grub_font_t b)
954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969
{
  int d;

  d = 0;

  if (a->ascent && b->ascent)
    d += grub_abs (a->ascent - b->ascent) * 8;
  else
    /* Penalty for missing attributes.  */
    d += 50;

  if (a->max_char_height && b->max_char_height)
    d += grub_abs (a->max_char_height - b->max_char_height) * 8;
  else
    /* Penalty for missing attributes.  */
    d += 50;
970

971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002
  /* Weight is a minor factor. */
  d += (a->weight != b->weight) ? 5 : 0;

  return d;
}

/* Get a glyph corresponding to the codepoint CODE.  If FONT contains the
   specified glyph, then it is returned.  Otherwise, all other loaded fonts
   are searched until one is found that contains a glyph for CODE.
   If no glyph is available for CODE in the loaded fonts, then a glyph
   representing an unknown character is returned.
   This function never returns NULL.
   The returned glyph is owned by the font manager and should not be freed
   by the caller.  The glyphs are cached.  */
struct grub_font_glyph *
grub_font_get_glyph_with_fallback (grub_font_t font, grub_uint32_t code)
{
  struct grub_font_glyph *glyph;
  struct grub_font_node *node;
  /* Keep track of next node, in case there's an I/O error in
     grub_font_get_glyph_internal() and the font is removed from the list.  */
  struct grub_font_node *next;
  /* Information on the best glyph found so far, to help find the glyph in
     the best matching to the requested one.  */
  int best_diversity;
  struct grub_font_glyph *best_glyph;

  if (font)
    {
      /* First try to get the glyph from the specified font.  */
      glyph = grub_font_get_glyph_internal (font, code);
      if (glyph)
1003
	return glyph;
1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018
    }

  /* Otherwise, search all loaded fonts for the glyph and use the one from
     the font that best matches the requested font.  */
  best_diversity = 10000;
  best_glyph = 0;

  for (node = grub_font_list; node; node = next)
    {
      grub_font_t curfont;

      curfont = node->value;
      next = node->next;

      glyph = grub_font_get_glyph_internal (curfont, code);
1019 1020
      if (glyph && !font)
	return glyph;
1021
      if (glyph)
1022 1023 1024 1025 1026 1027 1028 1029 1030 1031
	{
	  int d;

	  d = get_font_diversity (curfont, font);
	  if (d < best_diversity)
	    {
	      best_diversity = d;
	      best_glyph = glyph;
	    }
	}
1032 1033
    }

1034
  return best_glyph;
1035 1036
}

1037
#if 0
1038 1039 1040 1041 1042 1043 1044 1045 1046 1047 1048
static struct grub_font_glyph *
grub_font_dup_glyph (struct grub_font_glyph *glyph)
{
  static struct grub_font_glyph *ret;
  ret = grub_malloc (sizeof (*ret) + (glyph->width * glyph->height + 7) / 8);
  if (!ret)
    return NULL;
  grub_memcpy (ret, glyph, sizeof (*ret)
	       + (glyph->width * glyph->height + 7) / 8);
  return ret;
}
1049
#endif
1050 1051 1052 1053

/* FIXME: suboptimal.  */
static void
grub_font_blit_glyph (struct grub_font_glyph *target,
1054
		      struct grub_font_glyph *src, unsigned dx, unsigned dy)
1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082 1083
{
  unsigned src_bit, tgt_bit, src_byte, tgt_byte;
  unsigned i, j;
  for (i = 0; i < src->height; i++)
    {
      src_bit = (src->width * i) % 8;
      src_byte = (src->width * i) / 8;
      tgt_bit = (target->width * (dy + i) + dx) % 8;
      tgt_byte = (target->width * (dy + i) + dx) / 8;
      for (j = 0; j < src->width; j++)
	{
	  target->bitmap[tgt_byte] |= ((src->bitmap[src_byte] << src_bit)
				       & 0x80) >> tgt_bit;
	  src_bit++;
	  tgt_bit++;
	  if (src_bit == 8)
	    {
	      src_byte++;
	      src_bit = 0;
	    }
	  if (tgt_bit == 8)
	    {
	      tgt_byte++;
	      tgt_bit = 0;
	    }
	}
    }
}

1084 1085 1086 1087 1088 1089 1090 1091 1092 1093 1094 1095 1096 1097 1098 1099 1100 1101 1102 1103 1104 1105 1106 1107 1108 1109 1110 1111 1112 1113 1114 1115 1116 1117
static void
grub_font_blit_glyph_mirror (struct grub_font_glyph *target,
			     struct grub_font_glyph *src,
			     unsigned dx, unsigned dy)
{
  unsigned tgt_bit, src_byte, tgt_byte;
  signed src_bit;
  unsigned i, j;
  for (i = 0; i < src->height; i++)
    {
      src_bit = (src->width * i + src->width - 1) % 8;
      src_byte = (src->width * i + src->width - 1) / 8;
      tgt_bit = (target->width * (dy + i) + dx) % 8;
      tgt_byte = (target->width * (dy + i) + dx) / 8;
      for (j = 0; j < src->width; j++)
	{
	  target->bitmap[tgt_byte] |= ((src->bitmap[src_byte] << src_bit)
				       & 0x80) >> tgt_bit;
	  src_bit--;
	  tgt_bit++;
	  if (src_bit == -1)
	    {
	      src_byte--;
	      src_bit = 7;
	    }
	  if (tgt_bit == 8)
	    {
	      tgt_byte++;
	      tgt_bit = 0;
	    }
	}
    }
}

1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159 1160
/* Context for blit_comb.  */
struct blit_comb_ctx
{
  struct grub_font_glyph *glyph;
  int *device_width;
  struct grub_video_signed_rect bounds;
};

/* Helper for blit_comb.  */
static void
do_blit (struct grub_font_glyph *src, signed dx, signed dy,
	 struct blit_comb_ctx *ctx)
{
  if (ctx->glyph)
    grub_font_blit_glyph (ctx->glyph, src, dx - ctx->glyph->offset_x,
			  (ctx->glyph->height + ctx->glyph->offset_y) + dy);
  if (dx < ctx->bounds.x)
    {
      ctx->bounds.width += ctx->bounds.x - dx;
      ctx->bounds.x = dx;
    }
  if (ctx->bounds.y > -src->height - dy)
    {
      ctx->bounds.height += ctx->bounds.y - (-src->height - dy);
      ctx->bounds.y = (-src->height - dy);
    }
  if (dx + src->width - ctx->bounds.x >= (signed) ctx->bounds.width)
    ctx->bounds.width = dx + src->width - ctx->bounds.x + 1;
  if ((signed) ctx->bounds.height < src->height + (-src->height - dy)
      - ctx->bounds.y)
    ctx->bounds.height = src->height + (-src->height - dy) - ctx->bounds.y;
}

/* Helper for blit_comb.  */
static inline void
add_device_width (int val, struct blit_comb_ctx *ctx)
{
  if (ctx->glyph)
    ctx->glyph->device_width += val;
  if (ctx->device_width)
    *ctx->device_width += val;
}

1161 1162 1163 1164 1165
static void
blit_comb (const struct grub_unicode_glyph *glyph_id,
	   struct grub_font_glyph *glyph,
	   struct grub_video_signed_rect *bounds_out,
	   struct grub_font_glyph *main_glyph,
1166
	   struct grub_font_glyph **combining_glyphs, int *device_width)
1167
{
1168 1169 1170 1171
  struct blit_comb_ctx ctx = {
    .glyph = glyph,
    .device_width = device_width
  };
1172
  unsigned i;
1173
  signed above_rightx, above_righty;
1174
  signed above_leftx, above_lefty;
1175
  signed below_rightx, below_righty;
1176
  signed min_devwidth = 0;
1177
  const struct grub_unicode_combining *comb;
1178

1179 1180 1181 1182 1183
  if (glyph)
    glyph->device_width = main_glyph->device_width;
  if (device_width)
    *device_width = main_glyph->device_width;

1184 1185 1186 1187
  ctx.bounds.x = main_glyph->offset_x;
  ctx.bounds.y = main_glyph->offset_y;
  ctx.bounds.width = main_glyph->width;
  ctx.bounds.height = main_glyph->height;
1188

1189
  above_rightx = main_glyph->offset_x + main_glyph->width;
1190
  above_righty = ctx.bounds.y + ctx.bounds.height;
1191

1192
  above_leftx = main_glyph->offset_x;
1193
  above_lefty = ctx.bounds.y + ctx.bounds.height;
1194

1195 1196
  below_rightx = ctx.bounds.x + ctx.bounds.width;
  below_righty = ctx.bounds.y;
1197

1198 1199
  comb = grub_unicode_get_comb (glyph_id);

1200 1201 1202
  for (i = 0; i < glyph_id->ncomb; i++)
    {
      grub_int16_t space = 0;
1203
      /* Center by default.  */
1204
      grub_int16_t targetx;
1205

1206 1207
      if (!combining_glyphs[i])
	continue;
1208
      targetx = (ctx.bounds.width - combining_glyphs[i]->width) / 2 + ctx.bounds.x;
Vladimir 'phcoder' Serbinenko's avatar
Vladimir 'phcoder' Serbinenko committed
1209
      /* CGJ is to avoid diacritics reordering. */
1210
      if (comb[i].code
1211
	  == GRUB_UNICODE_COMBINING_GRAPHEME_JOINER)
Vladimir 'phcoder' Serbinenko's avatar
Vladimir 'phcoder' Serbinenko committed
1212
	continue;
1213
      switch (comb[i].type)
1214 1215 1216
	{
	case GRUB_UNICODE_COMB_OVERLAY:
	  do_blit (combining_glyphs[i],
1217
		   targetx,
1218 1219
		   (ctx.bounds.height - combining_glyphs[i]->height) / 2
		   - (ctx.bounds.height + ctx.bounds.y), &ctx);
1220 1221
	  if (min_devwidth < combining_glyphs[i]->width)
	    min_devwidth = combining_glyphs[i]->width;
1222 1223
	  break;

Vladimir 'phcoder' Serbinenko's avatar
Vladimir 'phcoder' Serbinenko committed
1224
	case GRUB_UNICODE_COMB_ATTACHED_ABOVE_RIGHT:
1225
	  do_blit (combining_glyphs[i], above_rightx, -above_righty, &ctx);
Vladimir 'phcoder' Serbinenko's avatar
Vladimir 'phcoder' Serbinenko committed
1226 1227 1228
	  above_rightx += combining_glyphs[i]->width;
	  break;

1229 1230
	case GRUB_UNICODE_COMB_ABOVE_RIGHT:
	  do_blit (combining_glyphs[i], above_rightx,
1231
		   -(above_righty + combining_glyphs[i]->height), &ctx);
1232
	  above_rightx += combining_glyphs[i]->width;
1233 1234 1235 1236 1237
	  break;

	case GRUB_UNICODE_COMB_ABOVE_LEFT:
	  above_leftx -= combining_glyphs[i]->width;
	  do_blit (combining_glyphs[i], above_leftx,
1238
		   -(above_lefty + combining_glyphs[i]->height), &ctx);
1239 1240
	  break;

1241
	case GRUB_UNICODE_COMB_BELOW_RIGHT:
1242
	  do_blit (combining_glyphs[i], below_rightx, below_righty, &ctx);
1243 1244 1245
	  below_rightx += combining_glyphs[i]->width;
	  break;

1246 1247
	case GRUB_UNICODE_COMB_HEBREW_HOLAM:
	  if (glyph_id->base != GRUB_UNICODE_HEBREW_WAW)
1248 1249 1250
	    targetx =
	      main_glyph->offset_x - combining_glyphs[i]->width -
	      (combining_glyphs[i]->width + 3) / 4;
1251 1252 1253 1254 1255 1256 1257
	  goto above_on_main;

	case GRUB_UNICODE_COMB_HEBREW_SIN_DOT:
	  targetx = main_glyph->offset_x + combining_glyphs[i]->width / 4;
	  goto above_on_main;

	case GRUB_UNICODE_COMB_HEBREW_SHIN_DOT:
1258 1259 1260
	  targetx =
	    main_glyph->width + main_glyph->offset_x -
	    combining_glyphs[i]->width;
1261 1262 1263 1264 1265 1266 1267
	above_on_main:
	  space = combining_glyphs[i]->offset_y
	    - grub_font_get_xheight (combining_glyphs[i]->font) - 1;
	  if (space <= 0)
	    space = 1 + (grub_font_get_xheight (main_glyph->font)) / 8;
	  do_blit (combining_glyphs[i], targetx,
		   -(main_glyph->height + main_glyph->offset_y + space
1268
		     + combining_glyphs[i]->height), &ctx);
1269 1270 1271 1272
	  if (min_devwidth < combining_glyphs[i]->width)
	    min_devwidth = combining_glyphs[i]->width;
	  break;

1273 1274
	  /* TODO: Put dammah, fathah and alif nearer to shadda.  */
	case GRUB_UNICODE_COMB_SYRIAC_SUPERSCRIPT_ALAPH:
1275 1276 1277 1278
	case GRUB_UNICODE_COMB_ARABIC_DAMMAH:
	case GRUB_UNICODE_COMB_ARABIC_DAMMATAN:
	case GRUB_UNICODE_COMB_ARABIC_FATHATAN:
	case GRUB_UNICODE_COMB_ARABIC_FATHAH:
1279
	case GRUB_UNICODE_COMB_ARABIC_SUPERSCRIPT_ALIF:
1280 1281
	case GRUB_UNICODE_COMB_ARABIC_SUKUN:
	case GRUB_UNICODE_COMB_ARABIC_SHADDA:
1282
	case GRUB_UNICODE_COMB_HEBREW_RAFE:
1283
	case GRUB_UNICODE_STACK_ABOVE:
1284
	stacked_above:
1285
	  space = combining_glyphs[i]->offset_y
1286 1287
	    - grub_font_get_xheight (combining_glyphs[i]->font) - 1;
	  if (space <= 0)
1288
	    space = 1 + (grub_font_get_xheight (main_glyph->font)) / 8;
1289
	  /* Fallthrough.  */
1290
	case GRUB_UNICODE_STACK_ATTACHED_ABOVE:
1291
	  do_blit (combining_glyphs[i], targetx,
1292 1293
		   -(ctx.bounds.height + ctx.bounds.y + space
		     + combining_glyphs[i]->height), &ctx);
1294 1295
	  if (min_devwidth < combining_glyphs[i]->width)
	    min_devwidth = combining_glyphs[i]->width;
1296 1297
	  break;

1298 1299
	case GRUB_UNICODE_COMB_HEBREW_DAGESH:
	  do_blit (combining_glyphs[i], targetx,
1300 1301
		   -(ctx.bounds.height / 2 + ctx.bounds.y
		     + combining_glyphs[i]->height / 2), &ctx);
1302 1303 1304 1305
	  if (min_devwidth < combining_glyphs[i]->width)
	    min_devwidth = combining_glyphs[i]->width;
	  break;

1306 1307 1308 1309 1310 1311 1312 1313 1314 1315 1316 1317 1318
	case GRUB_UNICODE_COMB_HEBREW_SHEVA:
	case GRUB_UNICODE_COMB_HEBREW_HIRIQ:
	case GRUB_UNICODE_COMB_HEBREW_QAMATS:
	case GRUB_UNICODE_COMB_HEBREW_TSERE:
	case GRUB_UNICODE_COMB_HEBREW_SEGOL:
	  /* TODO: placement in final kaf and under reish.  */

	case GRUB_UNICODE_COMB_HEBREW_HATAF_SEGOL:
	case GRUB_UNICODE_COMB_HEBREW_HATAF_PATAH:
	case GRUB_UNICODE_COMB_HEBREW_HATAF_QAMATS:
	case GRUB_UNICODE_COMB_HEBREW_PATAH:
	case GRUB_UNICODE_COMB_HEBREW_QUBUTS:
	case GRUB_UNICODE_COMB_HEBREW_METEG:
1319 1320 1321
	  /* TODO: Put kasra and kasratan under shadda.  */
	case GRUB_UNICODE_COMB_ARABIC_KASRA:
	case GRUB_UNICODE_COMB_ARABIC_KASRATAN:
Vladimir 'phcoder' Serbinenko's avatar
Vladimir 'phcoder' Serbinenko committed
1322 1323
	  /* I don't know how ypogegrammeni differs from subscript. */
	case GRUB_UNICODE_COMB_YPOGEGRAMMENI:
1324
	case GRUB_UNICODE_STACK_BELOW:
1325
	stacked_below:
1326
	  space = -(combining_glyphs[i]->offset_y
1327
		    + combining_glyphs[i]->height);
1328
	  if (space <= 0)
1329
	    space = 1 + (grub_font_get_xheight (main_glyph->font)) / 8;
1330
	  /* Fallthrough.  */
1331

1332
	case GRUB_UNICODE_STACK_ATTACHED_BELOW:
1333 1334
	  do_blit (combining_glyphs[i], targetx, -(ctx.bounds.y - space),
		   &ctx);
1335 1336
	  if (min_devwidth < combining_glyphs[i]->width)
	    min_devwidth = combining_glyphs[i]->width;
1337 1338
	  break;

1339
	case GRUB_UNICODE_COMB_MN:
1340
	  switch (comb[i].code)
1341 1342 1343 1344 1345 1346 1347 1348 1349 1350 1351 1352 1353
	    {
	    case GRUB_UNICODE_THAANA_ABAFILI:
	    case GRUB_UNICODE_THAANA_AABAAFILI:
	    case GRUB_UNICODE_THAANA_UBUFILI:
	    case GRUB_UNICODE_THAANA_OOBOOFILI:
	    case GRUB_UNICODE_THAANA_EBEFILI:
	    case GRUB_UNICODE_THAANA_EYBEYFILI:
	    case GRUB_UNICODE_THAANA_OBOFILI:
	    case GRUB_UNICODE_THAANA_OABOAFILI:
	    case GRUB_UNICODE_THAANA_SUKUN:
	      goto stacked_above;
	    case GRUB_UNICODE_THAANA_IBIFILI:
	    case GRUB_UNICODE_THAANA_EEBEEFILI:
1354
	      goto stacked_below;
1355 1356
	    }
	  /* Fall through.  */
1357 1358 1359 1360 1361
	default:
	  {
	    /* Default handling. Just draw combining character on top
	       of base character.
	       FIXME: support more unicode types correctly.
1362
	     */
1363 1364
	    do_blit (combining_glyphs[i],
		     main_glyph->device_width
1365 1366
		     + combining_glyphs[i]->offset_x,
		     -(combining_glyphs[i]->height
1367 1368
		       + combining_glyphs[i]->offset_y), &ctx);
	    add_device_width (combining_glyphs[i]->device_width, &ctx);
1369 1370 1371
	  }
	}
    }
1372 1373
  add_device_width ((above_rightx >
		     below_rightx ? above_rightx : below_rightx) -
1374 1375
		    (main_glyph->offset_x + main_glyph->width), &ctx);
  add_device_width (above_leftx - main_glyph->offset_x, &ctx);
1376
  if (glyph && glyph->device_width < min_devwidth)
1377
    glyph->device_width = min_devwidth;
1378 1379 1380
  if (device_width && *device_width < min_devwidth)
    *device_width = min_devwidth;

1381
  if (bounds_out)
1382
    *bounds_out = ctx.bounds;
1383 1384
}

1385
static struct grub_font_glyph *
1386 1387 1388
grub_font_construct_dry_run (grub_font_t hinted_font,
			     const struct grub_unicode_glyph *glyph_id,
			     struct grub_video_signed_rect *bounds,
1389
			     struct grub_font_glyph **combining_glyphs,
1390
			     int *device_width)
1391
{
1392 1393
  struct grub_font_glyph *main_glyph = NULL;
  grub_uint32_t desired_attributes = 0;
1394 1395
  unsigned i;
  grub_uint32_t base = glyph_id->base;
1396
  const struct grub_unicode_combining *comb;
1397

1398 1399 1400 1401 1402 1403
  if (glyph_id->attributes & GRUB_UNICODE_GLYPH_ATTRIBUTE_RIGHT_JOINED)
    desired_attributes |= GRUB_FONT_CODE_RIGHT_JOINED;

  if (glyph_id->attributes & GRUB_UNICODE_GLYPH_ATTRIBUTE_LEFT_JOINED)
    desired_attributes |= GRUB_FONT_CODE_LEFT_JOINED;

1404
  comb = grub_unicode_get_comb (glyph_id);
1405 1406 1407 1408

  if (base == 'i' || base == 'j')
    {
      for (i = 0; i < glyph_id->ncomb; i++)
1409
	if (comb[i].type == GRUB_UNICODE_STACK_ABOVE)
1410 1411 1412 1413 1414 1415 1416 1417
	  break;
      if (i < glyph_id->ncomb && base == 'i')
	base = GRUB_UNICODE_DOTLESS_LOWERCASE_I;
      if (i < glyph_id->ncomb && base == 'j')
	base = GRUB_UNICODE_DOTLESS_LOWERCASE_J;
    }

  main_glyph = grub_font_get_glyph_with_fallback (hinted_font, base
1418 1419 1420 1421
						  | desired_attributes);

  if (!main_glyph)
    main_glyph = grub_font_get_glyph_with_fallback (hinted_font,
1422
						    base);
1423

1424
  /* Glyph not available in any font.  Use ASCII fallback.  */
1425
  if (!main_glyph)
1426
    main_glyph = ascii_glyph_lookup (base);
1427 1428 1429

  /* Glyph not available in any font.  Return unknown glyph.  */
  if (!main_glyph)
1430 1431 1432 1433
    return NULL;

  if (device_width)
    *device_width = main_glyph->device_width;
1434

1435
  if (!glyph_id->ncomb && !glyph_id->attributes)
1436
    return main_glyph;
1437

1438
  if (glyph_id->ncomb && !combining_glyphs)
1439 1440
    {
      grub_errno = GRUB_ERR_NONE;
1441
      return main_glyph;
1442
    }
1443

1444 1445 1446
  for (i = 0; i < glyph_id->ncomb; i++)
    combining_glyphs[i]
      = grub_font_get_glyph_with_fallback (main_glyph->font,
1447
					   comb[i].code);
1448

1449 1450
  blit_comb (glyph_id, NULL, bounds, main_glyph, combining_glyphs,
	     device_width);
1451 1452 1453 1454

  return main_glyph;
}

1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473
static struct grub_font_glyph **render_combining_glyphs = 0;
static grub_size_t render_max_comb_glyphs = 0;

static void
ensure_comb_space (const struct grub_unicode_glyph *glyph_id)
{
  if (glyph_id->ncomb <= render_max_comb_glyphs)
    return;

  render_max_comb_glyphs = 2 * glyph_id->ncomb;
  if (render_max_comb_glyphs < 8)
    render_max_comb_glyphs = 8;
  grub_free (render_combining_glyphs);
  render_combining_glyphs = grub_malloc (render_max_comb_glyphs
					 * sizeof (render_combining_glyphs[0]));
  if (!render_combining_glyphs)
    grub_errno = 0;
}

1474 1475
int
grub_font_get_constructed_device_width (grub_font_t hinted_font,
1476 1477
					const struct grub_unicode_glyph
					*glyph_id)
1478 1479 1480
{
  int ret;
  struct grub_font_glyph *main_glyph;
1481 1482 1483

  ensure_comb_space (glyph_id);

1484
  main_glyph = grub_font_construct_dry_run (hinted_font, glyph_id, NULL,
1485
					    render_combining_glyphs, &ret);
1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496
  if (!main_glyph)
    return unknown_glyph->device_width;
  return ret;
}

struct grub_font_glyph *
grub_font_construct_glyph (grub_font_t hinted_font,
			   const struct grub_unicode_glyph *glyph_id)
{
  struct grub_font_glyph *main_glyph;
  struct grub_video_signed_rect bounds;
1497 1498 1499 1500
  static struct grub_font_glyph *glyph = 0;
  static grub_size_t max_glyph_size = 0;

  ensure_comb_space (glyph_id);
1501 1502

  main_glyph = grub_font_construct_dry_run (hinted_font, glyph_id,
1503 1504
					    &bounds, render_combining_glyphs,
					    NULL);
1505 1506

  if (!main_glyph)
1507
    return unknown_glyph;
1508

1509 1510 1511 1512 1513
  if (!render_combining_glyphs && glyph_id->ncomb)
    return main_glyph;

  if (!glyph_id->ncomb && !glyph_id->attributes)
    return main_glyph;
1514

1515 1516 1517 1518 1519 1520 1521 1522
  if (max_glyph_size < sizeof (*glyph) + (bounds.width * bounds.height + GRUB_CHAR_BIT - 1) / GRUB_CHAR_BIT)
    {
      grub_free (glyph);
      max_glyph_size = (sizeof (*glyph) + (bounds.width * bounds.height + GRUB_CHAR_BIT - 1) / GRUB_CHAR_BIT) * 2;
      if (max_glyph_size < 8)
	max_glyph_size = 8;
      glyph = grub_malloc (max_glyph_size);
    }
1523 1524 1525
  if (!glyph)
    {
      grub_errno = GRUB_ERR_NONE;
1526
      return main_glyph;
1527 1528
    }

1529 1530 1531 1532
  grub_memset (glyph, 0, sizeof (*glyph)
	       + (bounds.width * bounds.height
		  + GRUB_CHAR_BIT - 1) / GRUB_CHAR_BIT);

1533
  glyph->font = main_glyph->font;
1534 1535 1536 1537
  glyph->width = bounds.width;
  glyph->height = bounds.height;
  glyph->offset_x = bounds.x;
  glyph->offset_y = bounds.y;
1538

1539 1540
  if (glyph_id->attributes & GRUB_UNICODE_GLYPH_ATTRIBUTE_MIRROR)
    grub_font_blit_glyph_mirror (glyph, main_glyph,
1541 1542
				 main_glyph->offset_x - glyph->offset_x,
				 (glyph->height + glyph->offset_y)
1543 1544
				 - (main_glyph->height +
				    main_glyph->offset_y));
1545
  else
1546 1547 1548
    grub_font_blit_glyph (glyph, main_glyph,
			  main_glyph->offset_x - glyph->offset_x,
			  (glyph->height + glyph->offset_y)
1549 1550
			  - (main_glyph->height + main_glyph->offset_y));

1551
  blit_comb (glyph_id, glyph, NULL, main_glyph, render_combining_glyphs, NULL);
1552

1553 1554
  return glyph;
}
1555 1556 1557 1558 1559

/* Draw the specified glyph at (x, y).  The y coordinate designates the
   baseline of the character, while the x coordinate designates the left
   side location of the character.  */
grub_err_t
1560 1561
grub_font_draw_glyph (struct grub_font_glyph * glyph,
		      grub_video_color_t color, int left_x, int baseline_y)
1562 1563 1564 1565 1566 1567 1568 1569 1570
{
  struct grub_video_bitmap glyph_bitmap;

  /* Don't try to draw empty glyphs (U+0020, etc.).  */
  if (glyph->width == 0 || glyph->height == 0)
    return GRUB_ERR_NONE;

  glyph_bitmap.mode_info.width = glyph->width;
  glyph_bitmap.mode_info.height = glyph->height;
1571 1572
  glyph_bitmap.mode_info.mode_type
    = (1 << GRUB_VIDEO_MODE_TYPE_DEPTH_POS) | GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP;
1573 1574
  glyph_bitmap.mode_info.blit_format = GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED;
  glyph_bitmap.mode_info.bpp = 1;
1575

1576 1577
  /* Really 1 bit per pixel.  */
  glyph_bitmap.mode_info.bytes_per_pixel = 0;
1578

1579 1580
  /* Packed densely as bits.  */
  glyph_bitmap.mode_info.pitch = glyph->width;
1581

1582 1583 1584 1585 1586
  glyph_bitmap.mode_info.number_of_colors = 2;
  glyph_bitmap.mode_info.bg_red = 0;
  glyph_bitmap.mode_info.bg_green = 0;
  glyph_bitmap.mode_info.bg_blue = 0;
  glyph_bitmap.mode_info.bg_alpha = 0;
1587 1588 1589 1590 1591
  grub_video_unmap_color (color,
			  &glyph_bitmap.mode_info.fg_red,
			  &glyph_bitmap.mode_info.fg_green,
			  &glyph_bitmap.mode_info.fg_blue,
			  &glyph_bitmap.mode_info.fg_alpha);
1592 1593 1594 1595 1596 1597 1598
  glyph_bitmap.data = glyph->bitmap;

  int bitmap_left = left_x + glyph->offset_x;
  int bitmap_bottom = baseline_y - glyph->offset_y;
  int bitmap_top = bitmap_bottom - glyph->height;

  return grub_video_blit_bitmap (&glyph_bitmap, GRUB_VIDEO_BLIT_BLEND,
1599 1600
				 bitmap_left, bitmap_top,
				 0, 0, glyph->width, glyph->height);
1601
}