grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [PATCH] GSoC #10 new font engine (UTF-8 support+bugfix)


From: Vesa Jääskeläinen
Subject: Re: [PATCH] GSoC #10 new font engine (UTF-8 support+bugfix)
Date: Sun, 28 Dec 2008 02:35:40 +0200
User-agent: Thunderbird 2.0.0.18 (Windows/20081105)

And the actual patch...
Index: ChangeLog
===================================================================
--- ChangeLog   (revision 1934)
+++ ChangeLog   (working copy)
@@ -1,3 +1,92 @@
+2008-12-28  Colin D Bennett  <address@hidden>
+
+       New font engine.
+       
+       Additional changes by Vesa Jääskeläinen <address@hidden> to adapt to
+       build system and fixed gfxterm.c to work with different sized fonts.
+
+       * configure.ac: Changed UNIFONT_HEX to UNIFONT_BDF.
+       
+       * configure: Re-generated.
+       
+       * DISTLIST: Removed font/manager.c.
+       Added font/font.c.
+       Added font/font_cmd.c.
+       
+       * Makefile.in: Changed UNIFONT_HEX to UNIFONT_BDF.  Added Font tool
+       compilation.
+       
+       * include/grub/misc.h (grub_utf8_to_ucs4): Changed prototype.  Changed 
users.
+       
+       * kern/misc.c (grub_utf8_to_ucs4): Changed prototype. 
+
+       * kern/term.c: Changed users of grub_utf8_to_ucs4.
+       
+       * normal/menu.c: Likewise.
+               
+       * conf/common.rmk (font_mod_SOURCES): Removed font/manager.c.
+       (font_mod_SOURCES): Added font/font_cmd.c, font/font.c.
+       
+       * include/grub/font.h: Replaced with new file.
+       
+       * include/grub/video.h (GRUB_VIDEO_MODE_TYPE_ALPHA): Changed value.
+       (GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED): Likewise.
+       (GRUB_VIDEO_MODE_TYPE_COLOR_MASK): Likewise.
+       (GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP): Added.
+       (grub_video_blit_format): Added GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED.
+       (grub_video_mode_info): Added bg_red, bg_green, bg_blue, bg_alpha, 
+       fg_red, fg_green, fg_blue, fg_alpha.
+       (grub_video_adapter): Removed blit_glyph.
+       (grub_video_blit_glyph): Removed.       
+       
+       * font/manager.c: Removed file.
+       
+       * font/font.c: New file. 
+       
+       * font/font_cmd.c: Likewise.
+       
+       * video/video.c (grub_video_blit_glyph): Removed.
+       
+       * video/i386/pc/vbe.c (grub_video_vbe_map_rgb): Added 1-bit support.
+       (grub_video_vbe_map_rgba): Likewise.
+       (grub_video_vbe_unmap_color_int): Likewise.
+       (grub_video_vbe_blit_glyph): Removed.
+       (grub_video_vbe_adapter): Removed blit_glyph.
+       
+       * video/i386/pc/vbeutil.c (get_data_ptr): Added 1-bit support.
+       (get_pixel): Likewise.
+       (set_pixel): Likewise. 
+       
+       * commands/videotest.c (grub_cmd_videotest): Added more tests for fonts.
+       
+       * term/gfxterm.c: Adapted to new font engine.
+       
+       * term/i386/pc/vesafb.c: Marked as deprecated.  Made it compile.
+       
+       * term/i386/pc/vga.c: Likewise.
+       
+       * util/fonttool/src/org/gnu/grub/fonttool/BDFLoader.java: New file.
+       
+       * util/fonttool/src/org/gnu/grub/fonttool/CharDefs.java: Likewise.
+       
+       * util/fonttool/src/org/gnu/grub/fonttool/CharacterRange.java: Likewise.
+       
+       * util/fonttool/src/org/gnu/grub/fonttool/CharacterRange.java: Likewise.
+       
+       * util/fonttool/src/org/gnu/grub/fonttool/Converter.java: Likewise.
+       
+       * util/fonttool/src/org/gnu/grub/fonttool/Font.java: Likewise.
+       
+       * util/fonttool/src/org/gnu/grub/fonttool/Glyph.java: Likewise.
+       
+       * util/fonttool/src/org/gnu/grub/fonttool/PFF2Sections.java: Likewise.
+       
+       * util/fonttool/src/org/gnu/grub/fonttool/PFF2Writer.java: Likewise.
+
+       * util/grub.d/00_header.in: Changed to use new loadfont command.
+       
+       * util/grub-mkconfig_lib.in: Changed font extension.
+       
 2008-12-12  Alex Smith  <address@hidden>
 
        * fs/i386/pc/pxe.c (grub_pxefs_open): Handle the one open connection
Index: kern/term.c
===================================================================
--- kern/term.c (revision 1934)
+++ kern/term.c (working copy)
@@ -1,6 +1,6 @@
 /*
  *  GRUB  --  GRand Unified Bootloader
- *  Copyright (C) 2002,2003,2005,2007  Free Software Foundation, Inc.
+ *  Copyright (C) 2002,2003,2005,2007,2008  Free Software Foundation, Inc.
  *
  *  GRUB is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -208,7 +208,7 @@
   grub_ssize_t ret;
 
   buf[size++] = c;
-  ret = grub_utf8_to_ucs4 (&code, buf, size);
+  ret = grub_utf8_to_ucs4 (&code, 1, buf, size, 0);
   
   if (ret > 0)
     {
Index: kern/misc.c
===================================================================
--- kern/misc.c (revision 1934)
+++ kern/misc.c (working copy)
@@ -951,13 +951,16 @@
   return dest;
 }
 
-/* Convert an UTF-8 string to an UCS-4 string. Return the number of
-   characters converted. DEST must be able to hold at least SIZE
-   characters (when the input is unknown). If an invalid sequence is found,
-   return -1.  */
+/* Convert a (possibly null-terminated) UTF-8 string of at most SRCSIZE
+   bytes (if SRCSIZE is -1, it is ignored) in length to a UCS-4 string.
+   Return the number of characters converted. DEST must be able to hold
+   at least DESTSIZE characters. If an invalid sequence is found, return -1.
+   If SRCEND is not NULL, then *SRCEND is set to the next byte after the
+   last byte used in SRC.  */
 grub_ssize_t
-grub_utf8_to_ucs4 (grub_uint32_t *dest, const grub_uint8_t *src,
-                  grub_size_t size)
+grub_utf8_to_ucs4 (grub_uint32_t *dest, grub_size_t destsize,
+                  const grub_uint8_t *src, grub_size_t srcsize,
+                  const grub_uint8_t **srcend)
 {
   grub_uint32_t *p = dest;
   int count = 0;
@@ -963,10 +966,14 @@
   int count = 0;
   grub_uint32_t code = 0;
   
-  while (size--)
+  if (srcend)
+    *srcend = src;
+
+  while (srcsize && destsize)
     {
       grub_uint32_t c = *src++;
-      
+      if (srcsize != (grub_size_t)-1)
+       srcsize--;
       if (count)
        {
          if ((c & 0xc0) != 0x80)
@@ -983,6 +990,9 @@
        }
       else
        {
+         if (c == 0)
+           break;
+         
          if ((c & 0x80) == 0x00)
            code = c;
          else if ((c & 0xe0) == 0xc0)
@@ -1011,7 +1021,6 @@
              code = c & 0x01;
            }
          else
-           /* invalid */
            return -1;
        }
 
@@ -1016,9 +1025,14 @@
        }
 
       if (count == 0)
-       *p++ = code;
+       {
+         *p++ = code;
+         destsize--;
+       }
     }
 
+  if (srcend)
+    *srcend = src;
   return p - dest;
 }
 
Index: term/gfxterm.c
===================================================================
--- term/gfxterm.c      (revision 1934)
+++ term/gfxterm.c      (working copy)
@@ -34,9 +34,6 @@
 #define DEFAULT_VIDEO_HEIGHT   480
 #define DEFAULT_VIDEO_FLAGS    0
 
-#define DEFAULT_CHAR_WIDTH     8
-#define DEFAULT_CHAR_HEIGHT    16
-
 #define DEFAULT_BORDER_WIDTH   10
 
 #define DEFAULT_STANDARD_COLOR  0x07
@@ -69,27 +66,32 @@
 
 struct grub_virtual_screen
 {
-  /* Dimensions of the virtual screen.  */
+  /* Dimensions of the virtual screen in pixels.  */
   unsigned int width;
   unsigned int height;
 
-  /* Offset in the display.  */
+  /* Offset in the display in pixels.  */
   unsigned int offset_x;
   unsigned int offset_y;
 
-  /* TTY Character sizes.  */
-  unsigned int char_width;
-  unsigned int char_height;
+  /* TTY Character sizes in pixes.  */
+  unsigned int normal_char_width;
+  unsigned int normal_char_height;
 
-  /* Virtual screen TTY size.  */
+  /* Virtual screen TTY size in characters.  */
   unsigned int columns;
   unsigned int rows;
 
-  /* Current cursor details.  */
+  /* Current cursor location in characters.  */
   unsigned int cursor_x;
   unsigned int cursor_y;
+
+  /* Current cursor state. */
   int cursor_state;
 
+  /* Font settings. */
+  grub_font_t font;
+
   /* Terminal color settings.  */
   grub_uint8_t standard_color_setting;
   grub_uint8_t normal_color_setting;
@@ -125,6 +127,10 @@
 static void dirty_region_add (int x, int y, 
                               unsigned int width, unsigned int height);
 
+static unsigned int calculate_normal_character_width (grub_font_t font);
+
+static unsigned char calculate_character_width (struct grub_font_glyph *glyph);
+
 static void
 set_term_color (grub_uint8_t term_color)
 {
@@ -168,7 +174,8 @@
 
 static grub_err_t
 grub_virtual_screen_setup (unsigned int x, unsigned int y,
-                           unsigned int width, unsigned int height)
+                           unsigned int width, unsigned int height,
+                           const char *font_name)
 {
   /* Free old virtual screen.  */
   grub_virtual_screen_free ();
@@ -174,6 +181,10 @@
   grub_virtual_screen_free ();
 
   /* Initialize with default data.  */
+  virtual_screen.font = grub_font_get (font_name);
+  if (!virtual_screen.font)
+    return grub_error (GRUB_ERR_BAD_FONT,
+                       "No font loaded.");
   virtual_screen.width = width;
   virtual_screen.height = height;
   virtual_screen.offset_x = x;
@@ -178,8 +189,10 @@
   virtual_screen.height = height;
   virtual_screen.offset_x = x;
   virtual_screen.offset_y = y;
-  virtual_screen.char_width = DEFAULT_CHAR_WIDTH;
-  virtual_screen.char_height = DEFAULT_CHAR_HEIGHT;
+  virtual_screen.normal_char_width =
+    calculate_normal_character_width (virtual_screen.font);
+  virtual_screen.normal_char_height =
+    grub_font_get_max_char_height (virtual_screen.font);
   virtual_screen.cursor_x = 0;
   virtual_screen.cursor_y = 0;
   virtual_screen.cursor_state = 1;
@@ -185,8 +198,8 @@
   virtual_screen.cursor_state = 1;
 
   /* Calculate size of text buffer.  */
-  virtual_screen.columns = virtual_screen.width / virtual_screen.char_width;
-  virtual_screen.rows = virtual_screen.height / virtual_screen.char_height;
+  virtual_screen.columns = virtual_screen.width / 
virtual_screen.normal_char_width;
+  virtual_screen.rows = virtual_screen.height / 
virtual_screen.normal_char_height;
 
   /* Allocate memory for text buffer.  */
   virtual_screen.text_buffer =
@@ -225,6 +238,7 @@
 static grub_err_t
 grub_gfxterm_init (void)
 {
+  char *font_name;
   char *modevar;
   int width = DEFAULT_VIDEO_WIDTH;
   int height = DEFAULT_VIDEO_HEIGHT;
@@ -232,6 +246,11 @@
   int flags = DEFAULT_VIDEO_FLAGS;
   grub_video_color_t color;
 
+  /* Select the font to use. */
+  font_name = grub_env_get ("gfxterm_font");
+  if (! font_name)
+    font_name = "";   /* Allow fallback to any font. */
+
   /* Parse gfxmode environment variable if set.  */
   modevar = grub_env_get ("gfxmode");
   if (modevar)
@@ -471,7 +490,7 @@
 
   /* Create virtual screen.  */
   if (grub_virtual_screen_setup (DEFAULT_BORDER_WIDTH, DEFAULT_BORDER_WIDTH,
-                                 width, height) != GRUB_ERR_NONE)
+                                 width, height, font_name) != GRUB_ERR_NONE)
     {
       grub_video_restore ();
       return grub_errno;
@@ -657,7 +676,7 @@
 write_char (void)
 {
   struct grub_colored_char *p;
-  struct grub_font_glyph glyph;
+  struct grub_font_glyph *glyph;
   grub_video_color_t color;
   grub_video_color_t bgcolor;
   unsigned int x;
@@ -662,6 +681,7 @@
   grub_video_color_t bgcolor;
   unsigned int x;
   unsigned int y;
+  int ascent;
 
   /* Find out active character.  */
   p = (virtual_screen.text_buffer
@@ -671,7 +691,8 @@
   p -= p->index;
 
   /* Get glyph for character.  */
-  grub_font_get_glyph (p->code, &glyph);
+  glyph = grub_font_get_glyph (virtual_screen.font, p->code);
+  ascent = grub_font_get_ascent (virtual_screen.font);
 
   color = p->fg_color;
   bgcolor = p->bg_color;
@@ -676,13 +697,13 @@
   color = p->fg_color;
   bgcolor = p->bg_color;
 
-  x = virtual_screen.cursor_x * virtual_screen.char_width;
-  y = virtual_screen.cursor_y * virtual_screen.char_height;
+  x = virtual_screen.cursor_x * virtual_screen.normal_char_width;
+  y = virtual_screen.cursor_y * virtual_screen.normal_char_height;
 
   /* Render glyph to text layer.  */
   grub_video_set_active_render_target (text_layer);
-  grub_video_fill_rect (bgcolor, x, y, glyph.width, glyph.height);
-  grub_video_blit_glyph (&glyph, color, x, y);
+  grub_video_fill_rect (bgcolor, x, y, glyph->width, glyph->height);
+  grub_font_draw_glyph (glyph, color, x, y + ascent);
   grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
 
   /* Mark character to be drawn.  */
@@ -687,11 +708,11 @@
 
   /* Mark character to be drawn.  */
   dirty_region_add (virtual_screen.offset_x + x, virtual_screen.offset_y + y,
-                    glyph.width, glyph.height);
+                    glyph->width, glyph->height);
 }
 
 static void
-write_cursor (void)
+draw_cursor (int show)
 {
   unsigned int x;
   unsigned int y;
@@ -700,12 +721,22 @@
   grub_video_color_t color;
 
   /* Determine cursor properties and position on text layer. */
-  x = virtual_screen.cursor_x * virtual_screen.char_width;
-  y = ((virtual_screen.cursor_y + 1) * virtual_screen.char_height) - 3;  
-  width = virtual_screen.char_width;
+  x = virtual_screen.cursor_x * virtual_screen.normal_char_width;
+  y = (virtual_screen.cursor_y * virtual_screen.normal_char_height
+       + grub_font_get_ascent (virtual_screen.font));
+  width = virtual_screen.normal_char_width;
   height = 2;
 
-  color = virtual_screen.fg_color;
+  if (show)
+    {
+      color = virtual_screen.fg_color;
+    }
+  else
+    {
+      color = virtual_screen.bg_color;
+      y = virtual_screen.cursor_y * virtual_screen.normal_char_height;
+      height = virtual_screen.normal_char_height;
+    }
 
   /* Render cursor to text layer.  */
   grub_video_set_active_render_target (text_layer);
@@ -727,7 +758,7 @@
   if (!bitmap)
     {
       /* Remove cursor.  */
-      write_char ();
+      draw_cursor (0);
 
       /* Redraw only changed regions.  */
       dirty_region_redraw ();
@@ -755,7 +786,7 @@
   /* Scroll physical screen.  */
   grub_video_set_active_render_target (text_layer);
   color = virtual_screen.bg_color;
-  grub_video_scroll (color, 0, -virtual_screen.char_height);
+  grub_video_scroll (color, 0, -virtual_screen.normal_char_height);
   grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
   
   /* If we have bitmap, re-draw screen, otherwise scroll physical screen too.  
*/
@@ -767,16 +798,16 @@
   else
     {      
       /* Clear new border area.  */
-      grub_video_fill_rect (color, 
-                            virtual_screen.offset_x, virtual_screen.offset_y, 
-                            virtual_screen.width, virtual_screen.char_height);
-      
+      grub_video_fill_rect (color,
+                            virtual_screen.offset_x, virtual_screen.offset_y,
+                            virtual_screen.width, 
virtual_screen.normal_char_height);
+
       /* Scroll physical screen.  */
-      grub_video_scroll (color, 0, -virtual_screen.char_height);      
+      grub_video_scroll (color, 0, -virtual_screen.normal_char_height);
 
       /* Draw cursor if visible.  */
       if (virtual_screen.cursor_state)
-        write_cursor ();
+       draw_cursor (1);
     }
 }
 
@@ -791,7 +822,7 @@
     {
       /* Erase current cursor, if any.  */
       if (virtual_screen.cursor_state)
-        write_char ();
+       draw_cursor (0);
 
       switch (c)
         {
@@ -814,18 +845,27 @@
 
       /* Redraw cursor if visible.  */
       if (virtual_screen.cursor_state)
-        write_cursor ();
+       draw_cursor (1);
     }
   else
     {
-      struct grub_font_glyph glyph;
+      struct grub_font_glyph *glyph;
       struct grub_colored_char *p;
+      unsigned char char_width;
+
+      /* Erase current cursor, if any.  */
+      if (virtual_screen.cursor_state)
+       draw_cursor (0);
 
-      /* Get properties of the character.  */    
-      grub_font_get_glyph (c, &glyph);
+      /* Get properties of the character.  */
+      glyph = grub_font_get_glyph (virtual_screen.font, c);
+
+      /* Calculate actual character width for glyph. This is number of
+         times of normal_font_width.  */
+      char_width = calculate_character_width(glyph);
 
       /* If we are about to exceed line length, wrap to next line.  */
-      if (virtual_screen.cursor_x + glyph.char_width > virtual_screen.columns)
+      if (virtual_screen.cursor_x + char_width > virtual_screen.columns)
         grub_putchar ('\n');
 
       /* Find position on virtual screen, and fill information.  */
@@ -835,18 +875,18 @@
       p->code = c;
       p->fg_color = virtual_screen.fg_color;
       p->bg_color = virtual_screen.bg_color;
-      p->width = glyph.char_width - 1;
+      p->width = char_width - 1;
       p->index = 0;
 
       /* If we have large glyph, add fixup info.  */
-      if (glyph.char_width > 1)
+      if (char_width > 1)
         {
           unsigned i;
 
-          for (i = 1; i < glyph.char_width; i++)
+          for (i = 1; i < char_width; i++)
             {
               p[i].code = ' ';
-              p[i].width = glyph.char_width - 1;
+              p[i].width = char_width - 1;
               p[i].index = i;
             }
         }
@@ -855,7 +895,7 @@
       write_char ();
 
       /* Make sure we scroll screen when needed and wrap line correctly.  */
-      virtual_screen.cursor_x += glyph.char_width;
+      virtual_screen.cursor_x += char_width;
       if (virtual_screen.cursor_x >= virtual_screen.columns)
         {
           virtual_screen.cursor_x = 0;
@@ -868,18 +908,58 @@
 
       /* Draw cursor if visible.  */
       if (virtual_screen.cursor_state)
-        write_cursor ();
+       draw_cursor (1);
     }
 }
 
+/* Use ASCII characters to determine normal character width.  */
+static unsigned int
+calculate_normal_character_width (grub_font_t font)
+{
+  struct grub_font_glyph *glyph;
+  unsigned int width = 0;
+  unsigned int i;
+
+  /* Get properties of every printable ASCII character.  */
+  for (i = 32; i < 127; i++)
+    {
+      glyph = grub_font_get_glyph (font, i);
+
+      /* Skip unknown characters.  Should never happen on normal conditions.  
*/
+      if (! glyph)
+       continue;
+
+      if (glyph->device_width > width)
+       width = glyph->device_width;
+    }
+
+  return width;
+}
+
+static unsigned char
+calculate_character_width (struct grub_font_glyph *glyph)
+{
+  if (! glyph || glyph->device_width == 0)
+    return 1;
+
+  return (glyph->device_width
+          + (virtual_screen.normal_char_width - 1))
+         / virtual_screen.normal_char_width;
+}
+
 static grub_ssize_t
 grub_gfxterm_getcharwidth (grub_uint32_t c)
 {
-  struct grub_font_glyph glyph;
+  struct grub_font_glyph *glyph;
+  unsigned char char_width;
 
-  grub_font_get_glyph (c, &glyph);
+  /* Get properties of the character.  */
+  glyph = grub_font_get_glyph (virtual_screen.font, c);
 
-  return glyph.char_width;
+  /* Calculate actual character width for glyph.  */
+  char_width = calculate_character_width (glyph);
+
+  return char_width;
 }
 
 static grub_uint16_t
@@ -903,8 +983,9 @@
   if (y >= virtual_screen.rows)
     y = virtual_screen.rows - 1;
 
+  /* Erase current cursor, if any.  */
   if (virtual_screen.cursor_state)
-    write_char ();
+    draw_cursor (0);
 
   virtual_screen.cursor_x = x;
   virtual_screen.cursor_y = y;
@@ -909,8 +990,9 @@
   virtual_screen.cursor_x = x;
   virtual_screen.cursor_y = y;
 
+  /* Draw cursor if visible.  */
   if (virtual_screen.cursor_state)
-    write_cursor ();
+    draw_cursor (1);
 }
 
 static void
@@ -995,9 +1077,9 @@
   if (virtual_screen.cursor_state != on)
     {
       if (virtual_screen.cursor_state)
-        write_char ();
+       draw_cursor (0);
       else
-        write_cursor ();
+       draw_cursor (1);
 
       virtual_screen.cursor_state = on;
     }
Index: term/i386/pc/vesafb.c
===================================================================
--- term/i386/pc/vesafb.c       (revision 1934)
+++ term/i386/pc/vesafb.c       (working copy)
@@ -1,6 +1,6 @@
 /*
  *  GRUB  --  GRand Unified Bootloader
- *  Copyright (C) 2005,2007  Free Software Foundation, Inc.
+ *  Copyright (C) 2005,2007,2008  Free Software Foundation, Inc.
  *
  *  GRUB is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -16,6 +16,8 @@
  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+// TODO: Deprecated and broken. Scheduled for removal as there is VBE driver 
in Video subsystem.
+
 #include <grub/machine/memory.h>
 #include <grub/machine/vga.h>
 #include <grub/machine/vbe.h>
@@ -250,10 +252,11 @@
          break;
 
        default:
-         return grub_font_get_glyph (code, bitmap, width);
+         return grub_font_get_glyph_any (code, bitmap, width);
        }
     }
 
+  /* TODO This is wrong for the new font module.  Should it be fixed?  */
   if (bitmap)
     grub_memcpy (bitmap,
                 vga_font + code * virtual_screen.char_height,
Index: term/i386/pc/vga.c
===================================================================
--- term/i386/pc/vga.c  (revision 1934)
+++ term/i386/pc/vga.c  (working copy)
@@ -16,6 +16,8 @@
  *  along with GRUB.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+// TODO: Deprecated and broken. Needs to be converted to Video Driver!
+
 #include <grub/machine/vga.h>
 #include <grub/machine/console.h>
 #include <grub/cpu/io.h>
@@ -65,6 +67,7 @@
 static struct colored_char text_buf[TEXT_WIDTH * TEXT_HEIGHT];
 static unsigned char saved_map_mask;
 static int page = 0;
+static grub_font_t font = 0;
 
 #define SEQUENCER_ADDR_PORT    0x3C4
 #define SEQUENCER_DATA_PORT    0x3C5
@@ -161,6 +164,9 @@
   saved_map_mask = get_map_mask ();
   set_map_mask (0x0f);
   set_start_address (PAGE_OFFSET (page));
+  font = grub_font_get ("");  /* Choose any font, for now. */
+  if (!font)
+    return grub_error (GRUB_ERR_BAD_FONT, "No font loaded.");
   
   return GRUB_ERR_NONE;
 }
@@ -185,7 +191,7 @@
 write_char (void)
 {
   struct colored_char *p = text_buf + xpos + ypos * TEXT_WIDTH;
-  struct grub_font_glyph glyph;
+  struct grub_font_glyph *glyph;
   unsigned char *mem_base;
   unsigned plane;
 
@@ -194,7 +200,7 @@
   p -= p->index;
 
   /* Get glyph for character.  */
-  grub_font_get_glyph (p->code, &glyph);
+  glyph = grub_font_get_glyph (font, p->code);
   
   for (plane = 0x01; plane <= 0x08; plane <<= 1)
     {
@@ -208,14 +214,17 @@
           y < CHAR_HEIGHT;
           y++, mem += TEXT_WIDTH)
        {
+          /* TODO Re-implement glyph drawing for vga module.  */
+#if 0
          unsigned i;
 
-         for (i = 0; i < glyph.char_width && offset < 32; i++)
+          unsigned char_width = 1; /* TODO Figure out wide characters.  */
+         for (i = 0; i < char_width && offset < 32; i++)
            {
              unsigned char fg_mask, bg_mask;
              
-             fg_mask = (p->fg_color & plane) ? glyph.bitmap[offset] : 0;
-             bg_mask = (p->bg_color & plane) ? ~(glyph.bitmap[offset]) : 0;
+             fg_mask = (p->fg_color & plane) ? glyph->bitmap[offset] : 0;
+             bg_mask = (p->bg_color & plane) ? ~(glyph->bitmap[offset]) : 0;
              offset++;
 
              if (check_vga_mem (mem + i))
@@ -221,6 +230,7 @@
              if (check_vga_mem (mem + i))
                mem[i] = (fg_mask | bg_mask);
            }
+#endif /* 0 */ 
        }
     }
 
@@ -320,12 +330,13 @@
     }
   else
     {
-      struct grub_font_glyph glyph;
+      struct grub_font_glyph *glyph;
       struct colored_char *p;
+      unsigned char_width = 1;
       
-      grub_font_get_glyph(c, &glyph);
+      glyph = grub_font_get_glyph(font, c);
 
-      if (xpos + glyph.char_width > TEXT_WIDTH)
+      if (xpos + char_width > TEXT_WIDTH)
        grub_putchar ('\n');
 
       p = text_buf + xpos + ypos * TEXT_WIDTH;
@@ -332,17 +343,17 @@
       p->code = c;
       p->fg_color = fg_color;
       p->bg_color = bg_color;
-      p->width = glyph.char_width - 1;
+      p->width = char_width - 1;
       p->index = 0;
 
-      if (glyph.char_width > 1)
+      if (char_width > 1)
        {
          unsigned i;
 
-         for (i = 1; i < glyph.char_width; i++)
+         for (i = 1; i < char_width; i++)
            {
              p[i].code = ' ';
-             p[i].width = glyph.char_width - 1;
+             p[i].width = char_width - 1;
              p[i].index = i;
            }
        }
@@ -349,7 +360,7 @@
          
       write_char ();
   
-      xpos += glyph.char_width;
+      xpos += char_width;
       if (xpos >= TEXT_WIDTH)
        {
          xpos = 0;
@@ -381,11 +392,16 @@
 static grub_ssize_t
 grub_vga_getcharwidth (grub_uint32_t c)
 {
+#if 0
   struct grub_font_glyph glyph;
   
-  grub_font_get_glyph (c, &glyph);
+  glyph = grub_font_get_glyph (c);
   
   return glyph.char_width;
+#else
+  (void) c;   /* Prevent warning.  */
+  return 1;   /* TODO Fix wide characters?  */
+#endif
 }
 
 static grub_uint16_t
Index: conf/common.rmk
===================================================================
--- conf/common.rmk     (revision 1934)
+++ conf/common.rmk     (working copy)
@@ -364,7 +364,7 @@
 help_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
 # For font.mod.
-font_mod_SOURCES = font/manager.c
+font_mod_SOURCES = font/font_cmd.c font/font.c
 font_mod_CFLAGS = $(COMMON_CFLAGS)
 font_mod_LDFLAGS = $(COMMON_LDFLAGS)
 
Index: normal/menu.c
===================================================================
--- normal/menu.c       (revision 1934)
+++ normal/menu.c       (working copy)
@@ -116,6 +116,7 @@
 {
   int x;
   const char *title;
+  grub_size_t title_len;
   grub_ssize_t len;
   grub_uint32_t *unicode_title;
   grub_ssize_t i;
@@ -122,7 +123,8 @@
   grub_uint8_t old_color_normal, old_color_highlight;
 
   title = entry ? entry->title : "";
-  unicode_title = grub_malloc (grub_strlen (title) * sizeof (*unicode_title));
+  title_len = grub_strlen (title);
+  unicode_title = grub_malloc (title_len * sizeof (*unicode_title));
   if (! unicode_title)
     /* XXX How to show this error?  */
     return;
@@ -127,8 +129,8 @@
     /* XXX How to show this error?  */
     return;
   
-  len = grub_utf8_to_ucs4 (unicode_title, (grub_uint8_t *) title,
-                          grub_strlen (title));
+  len = grub_utf8_to_ucs4 (unicode_title, title_len,
+                           (grub_uint8_t *) title, -1, 0);
   if (len < 0)
     {
       /* It is an invalid sequence.  */
Index: include/grub/misc.h
===================================================================
--- include/grub/misc.h (revision 1934)
+++ include/grub/misc.h (working copy)
@@ -1,7 +1,7 @@
 /* misc.h - prototypes for misc functions */
 /*
  *  GRUB  --  GRand Unified Bootloader
- *  Copyright (C) 2002,2003,2005,2006,2007  Free Software Foundation, Inc.
+ *  Copyright (C) 2002,2003,2005,2006,2007,2008  Free Software Foundation, Inc.
  *
  *  GRUB is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -77,8 +77,10 @@
                                               grub_uint16_t *src,
                                               grub_size_t size);
 grub_ssize_t EXPORT_FUNC(grub_utf8_to_ucs4) (grub_uint32_t *dest,
+                                            grub_size_t destsize,
                                             const grub_uint8_t *src,
-                                            grub_size_t size);
+                                            grub_size_t srcsize,
+                                            const grub_uint8_t **srcend);
 grub_uint64_t EXPORT_FUNC(grub_divmod64) (grub_uint64_t n,
                                          grub_uint32_t d, grub_uint32_t *r);
 
Index: include/grub/font.h
===================================================================
--- include/grub/font.h (revision 1934)
+++ include/grub/font.h (working copy)
@@ -1,6 +1,6 @@
 /*
  *  GRUB  --  GRand Unified Bootloader
- *  Copyright (C) 2003,2007  Free Software Foundation, Inc.
+ *  Copyright (C) 2003,2007,2008  Free Software Foundation, Inc.
  *
  *  GRUB is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -20,34 +20,96 @@
 #define GRUB_FONT_HEADER       1
 
 #include <grub/types.h>
+#include <grub/video.h>
+
+/* Forward declaration of opaque structure grub_font.
+   Users only pass struct grub_font pointers to the font module functions,
+   and do not have knowledge of the structure contents.  */
+struct grub_font;
+
+/* Font type used to access font functions.  */
+typedef struct grub_font *grub_font_t;
+
+struct grub_font_node
+{
+  struct grub_font_node *next;
+  grub_font_t value;
+};
 
-#define GRUB_FONT_MAGIC        "PPF\x7f"
+/* Global font registry.  */
+extern struct grub_font_node *grub_font_list;
 
 struct grub_font_glyph
 {
-  /* Glyph width in pixels.  */
-  grub_uint8_t width;
-  
-  /* Glyph height in pixels.  */
-  grub_uint8_t height;
-  
-  /* Glyph width in characters.  */
-  grub_uint8_t char_width;
-  
-  /* Glyph baseline position in pixels (from up).  */
-  grub_uint8_t baseline;
-  
-  /* Glyph bitmap data array of bytes in ((width + 7) / 8) * height.
-     Bitmap is formulated by height scanlines, each scanline having
-     width number of pixels. Pixels are coded as bits, value 1 meaning
-     of opaque pixel and 0 is transparent. If width does not fit byte
-     boundary, it will be padded with 0 to make it fit.  */
-  grub_uint8_t bitmap[32];
+  /* Reference to the font this glyph belongs to.  */
+  grub_font_t font;
+
+  /* Glyph bitmap width in pixels.  */
+  grub_uint16_t width;
+
+  /* Glyph bitmap height in pixels.  */
+  grub_uint16_t height;
+
+  /* Glyph bitmap x offset in pixels.  Add to screen coordinate.  */
+  grub_int16_t offset_x;
+
+  /* Glyph bitmap y offset in pixels.  Subtract from screen coordinate.  */
+  grub_int16_t offset_y;
+
+  /* Number of pixels to advance to start the next character.  */
+  grub_uint16_t device_width;
+
+  /* Row-major order, packed bits (no padding; rows can break within a byte).
+     The length of the array is (width * height + 7) / 8.  Within a
+     byte, the most significant bit is the first (leftmost/uppermost) pixel.
+     Pixels are coded as bits, value 1 meaning of opaque pixel and 0 is
+     transparent.  If the length of the array does not fit byte boundary, it
+     will be padded with 0 bits to make it fit.  */
+  grub_uint8_t bitmap[0];
 };
 
-typedef struct grub_font_glyph *grub_font_glyph_t;
+/* Initialize the font loader.
+   Must be called before any fonts are loaded or used.  */
+void grub_font_loader_init (void);
+
+/* Load a font and add it to the beginning of the global font list.
+   Returns: 0 upon success; nonzero upon failure.  */
+int grub_font_load (const char *filename);
+
+/* Get the font that has the specified name.  Font names are in the form
+   "Family Name Bold Italic 14", where Bold and Italic are optional.
+   If no font matches the name specified, the most recently loaded font
+   is returned as a fallback.  */
+grub_font_t grub_font_get (const char *font_name);
+
+const char *grub_font_get_name (grub_font_t font);
+
+int grub_font_get_max_char_width (grub_font_t font);
+
+int grub_font_get_max_char_height (grub_font_t font);
+
+int grub_font_get_ascent (grub_font_t font);
+
+int grub_font_get_descent (grub_font_t font);
+
+int grub_font_get_leading (grub_font_t font);
+
+int grub_font_get_height (grub_font_t font);
+
+int grub_font_get_string_width (grub_font_t font, const char *str);
+
+struct grub_font_glyph *grub_font_get_glyph (grub_font_t font,
+                                             grub_uint32_t code);
 
-int grub_font_get_glyph (grub_uint32_t code,
-                        grub_font_glyph_t glyph);
+struct grub_font_glyph *grub_font_get_glyph_with_fallback (grub_font_t font,
+                                                           grub_uint32_t code);
+
+grub_err_t grub_font_draw_glyph (struct grub_font_glyph *glyph,
+                                        grub_video_color_t color,
+                                        int left_x, int baseline_y);
+
+grub_err_t grub_font_draw_string (const char *str, grub_font_t font,
+                                  grub_video_color_t color,
+                                  int left_x, int baseline_y);
 
 #endif /* ! GRUB_FONT_HEADER */
Index: include/grub/video.h
===================================================================
--- include/grub/video.h        (revision 1934)
+++ include/grub/video.h        (working copy)
@@ -31,12 +31,12 @@
 struct grub_video_render_target;
 
 /* Forward declarations for used data structures.  */
-struct grub_font_glyph;
 struct grub_video_bitmap;
 
 /* Defines used to describe video mode or rendering target.  */
-#define GRUB_VIDEO_MODE_TYPE_ALPHA             0x00000008
-#define GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED   0x00000004
+#define GRUB_VIDEO_MODE_TYPE_ALPHA             0x00000020
+#define GRUB_VIDEO_MODE_TYPE_DOUBLE_BUFFERED   0x00000010
+#define GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP       0x00000004
 #define GRUB_VIDEO_MODE_TYPE_INDEX_COLOR       0x00000002
 #define GRUB_VIDEO_MODE_TYPE_RGB               0x00000001
 
@@ -41,7 +41,7 @@
 #define GRUB_VIDEO_MODE_TYPE_RGB               0x00000001
 
 /* Defines used to mask flags.  */
-#define GRUB_VIDEO_MODE_TYPE_COLOR_MASK                0x00000003
+#define GRUB_VIDEO_MODE_TYPE_COLOR_MASK                0x0000000F
 
 /* Defines used to specify requested bit depth.  */
 #define GRUB_VIDEO_MODE_TYPE_DEPTH_MASK                0x0000ff00
@@ -72,7 +72,10 @@
     GRUB_VIDEO_BLIT_FORMAT_BGR_565,
 
     /* When needed, decode color or just use value as is.  */
-    GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR
+    GRUB_VIDEO_BLIT_FORMAT_INDEXCOLOR,
+    
+    /* Two color bitmap; bits packed: rows are not padded to byte boundary.  */
+    GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED
   };
 
 /* Define blitting operators.  */
@@ -135,6 +138,18 @@
   /* What is location of reserved color bits.  In Index Color mode,
      this is 0.  */
   unsigned int reserved_field_pos;
+
+  /* For 1-bit bitmaps, the background color.  Used for bits = 0.  */
+  grub_uint8_t bg_red;
+  grub_uint8_t bg_green;
+  grub_uint8_t bg_blue;
+  grub_uint8_t bg_alpha;
+
+  /* For 1-bit bitmaps, the foreground color.  Used for bits = 1.  */
+  grub_uint8_t fg_red;
+  grub_uint8_t fg_green;
+  grub_uint8_t fg_blue;
+  grub_uint8_t fg_alpha;
 };
 
 struct grub_video_palette_data
@@ -188,9 +203,6 @@
   grub_err_t (*fill_rect) (grub_video_color_t color, int x, int y,
                            unsigned int width, unsigned int height);
 
-  grub_err_t (*blit_glyph) (struct grub_font_glyph *glyph,
-                            grub_video_color_t color, int x, int y);
-
   grub_err_t (*blit_bitmap) (struct grub_video_bitmap *bitmap,
                              enum grub_video_blit_operators oper,
                              int x, int y, int offset_x, int offset_y,
@@ -260,9 +272,6 @@
 grub_err_t grub_video_fill_rect (grub_video_color_t color, int x, int y,
                                  unsigned int width, unsigned int height);
 
-grub_err_t grub_video_blit_glyph (struct grub_font_glyph *glyph,
-                                  grub_video_color_t color, int x, int y);
-
 grub_err_t grub_video_blit_bitmap (struct grub_video_bitmap *bitmap,
                                    enum grub_video_blit_operators oper,
                                    int x, int y, int offset_x, int offset_y,
Index: Makefile.in
===================================================================
--- Makefile.in (revision 1934)
+++ Makefile.in (working copy)
@@ -1,6 +1,6 @@
 # -*- makefile -*-
 #
-# Copyright (C) 
1994,1995,1996,1997,1998,1999,2000,2001,2002,2004,2005,2006,2007 Free Software 
Foundation, Inc.
+# Copyright (C) 
1994,1995,1996,1997,1998,1999,2000,2001,2002,2004,2005,2006,2007,2008 Free 
Software Foundation, Inc.
 #
 # This Makefile.in is free software; the author
 # gives unlimited permission to copy and/or distribute it,
@@ -88,7 +88,7 @@
 LIBCURSES = @LIBCURSES@
 LIBLZO = @LIBLZO@
 YACC = @YACC@
-UNIFONT_HEX = @UNIFONT_HEX@
+UNIFONT_BDF = @UNIFONT_BDF@
 
 # Options.
 enable_grub_emu = @enable_grub_emu@
@@ -148,9 +148,9 @@
 partmap.lst: $(PARTMAPFILES)
        cat $^ /dev/null | sort > $@
 
-ifeq (, $(UNIFONT_HEX))
+ifeq (, $(UNIFONT_BDF))
 else
-pkgdata_DATA += unicode.pff ascii.pff
+pkgdata_DATA += unicode.pf2 ascii.pf2
 
 # Arrows and lines are needed to draw the menu, so we always include them
 UNICODE_ARROWS=0x2190-0x2193
@@ -156,11 +156,18 @@
 UNICODE_ARROWS=0x2190-0x2193
 UNICODE_LINES=0x2501-0x251B
 
-unicode.pff: $(UNIFONT_HEX)
-       ruby $(srcdir)/util/unifont2pff.rb $(UNIFONT_HEX) > $@
+# Note: fonttool should be replaced with C only implementation
+
+$(builddir)/fonttool/fonttool.jar: 
+       mkdir -p "$(builddir)/fonttool/src"
+       javac -source 1.5 -target 1.5 -g -deprecation -encoding UTF-8 -d 
"$(builddir)/fonttool/src" `find "$(srcdir)/util/fonttool/src/" -name '*.java'`
+       jar cf $(builddir)/fonttool/fonttool.jar -C $(builddir)/fonttool/src .
 
-ascii.pff: $(UNIFONT_HEX)
-       ruby $(srcdir)/util/unifont2pff.rb 0x0-0x7f $(UNICODE_ARROWS) 
$(UNICODE_LINES) $(UNIFONT_HEX) > $@
+unicode.pf2: $(UNIFONT_BDF) $(builddir)/fonttool/fonttool.jar
+       java -cp $(builddir)/fonttool/fonttool.jar 
org.gnu.grub.fonttool.Converter --in=$(UNIFONT_BDF) --out=$@
+
+ascii.pf2: $(UNIFONT_BDF) $(builddir)/fonttool/fonttool.jar
+       java -cp $(builddir)/fonttool/fonttool.jar 
org.gnu.grub.fonttool.Converter --in=$(UNIFONT_BDF) --out=$@ 0x0-0x7f 
$(UNICODE_ARROWS) $(UNICODE_LINES)
 endif
 
 # Used for building modules externally
Index: DISTLIST
===================================================================
--- DISTLIST    (revision 1934)
+++ DISTLIST    (working copy)
@@ -103,7 +103,8 @@
 docs/grub.cfg
 docs/grub.texi
 docs/texinfo.tex
-font/manager.c
+font/font.c
+font/font_cmd.c
 fs/affs.c
 fs/afs.c
 fs/cpio.c
Index: configure
===================================================================
--- configure   (revision 1934)
+++ configure   (working copy)
@@ -667,7 +667,7 @@
 platform
 CMP
 YACC
-UNIFONT_HEX
+UNIFONT_BDF
 INSTALL_PROGRAM
 INSTALL_SCRIPT
 INSTALL_DATA
@@ -2107,9 +2107,9 @@
    { (exit 1); exit 1; }; }
 fi
 
-for file in /usr/share/unifont/unifont.hex ; do
+for file in /usr/src/unifont.bdf ; do
   if test -e $file ; then
-    UNIFONT_HEX=$file
+    UNIFONT_BDF=$file
 
     break
   fi
@@ -9110,7 +9110,7 @@
 platform!$platform$ac_delim
 CMP!$CMP$ac_delim
 YACC!$YACC$ac_delim
-UNIFONT_HEX!$UNIFONT_HEX$ac_delim
+UNIFONT_BDF!$UNIFONT_BDF$ac_delim
 INSTALL_PROGRAM!$INSTALL_PROGRAM$ac_delim
 INSTALL_SCRIPT!$INSTALL_SCRIPT$ac_delim
 INSTALL_DATA!$INSTALL_DATA$ac_delim
Index: video/video.c
===================================================================
--- video/video.c       (revision 1934)
+++ video/video.c       (working copy)
@@ -336,17 +336,6 @@
   return grub_video_adapter_active->fill_rect (color, x, y, width, height);
 }
 
-/* Blit glyph to screen using specified color.  */
-grub_err_t
-grub_video_blit_glyph (struct grub_font_glyph *glyph,
-                       grub_video_color_t color, int x, int y)
-{
-  if (! grub_video_adapter_active)
-    return grub_error (GRUB_ERR_BAD_DEVICE, "No video mode activated");
-
-  return grub_video_adapter_active->blit_glyph (glyph, color, x, y);
-}
-
 /* Blit bitmap to screen.  */
 grub_err_t
 grub_video_blit_bitmap (struct grub_video_bitmap *bitmap,
Index: video/i386/pc/vbe.c
===================================================================
--- video/i386/pc/vbe.c (revision 1934)
+++ video/i386/pc/vbe.c (working copy)
@@ -26,7 +26,6 @@
 #include <grub/types.h>
 #include <grub/dl.h>
 #include <grub/misc.h>
-#include <grub/font.h>
 #include <grub/mm.h>
 #include <grub/video.h>
 #include <grub/bitmap.h>
@@ -710,6 +709,16 @@
 
       return minindex;
     }
+  else if ((render_target->mode_info.mode_type 
+            & GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP) != 0)
+    {
+       if (red == render_target->mode_info.fg_red
+           && green == render_target->mode_info.fg_green
+           && blue == render_target->mode_info.fg_blue)
+         return 1;
+       else
+         return 0;
+    }
   else
     {
       grub_uint32_t value;
@@ -740,6 +749,17 @@
     /* No alpha available in index color modes, just use
        same value as in only RGB modes.  */
     return grub_video_vbe_map_rgb (red, green, blue);
+  else if ((render_target->mode_info.mode_type 
+            & GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP) != 0)
+    {
+      if (red == render_target->mode_info.fg_red
+          && green == render_target->mode_info.fg_green
+          && blue == render_target->mode_info.fg_blue
+          && alpha == render_target->mode_info.fg_alpha)
+        return 1;
+      else
+        return 0;
+    }
   else
     {
       grub_uint32_t value;
@@ -802,6 +822,24 @@
       *alpha = framebuffer.palette[color].a;
       return;
     }
+  else if ((mode_info->mode_type
+            & GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP) != 0)
+    {
+      if (color & 1)
+        {
+          *red = mode_info->fg_red;
+          *green = mode_info->fg_green;
+          *blue = mode_info->fg_blue;
+          *alpha = mode_info->fg_alpha;
+        }
+      else
+        {
+          *red = mode_info->bg_red;
+          *green = mode_info->bg_green;
+          *blue = mode_info->bg_blue;
+          *alpha = mode_info->bg_alpha;
+        }
+    }
   else
     {
       grub_uint32_t tmp;
@@ -925,76 +963,6 @@
   return GRUB_ERR_NONE;
 }
 
-// TODO: Remove this method and replace with bitmap based glyphs
-static grub_err_t
-grub_video_vbe_blit_glyph (struct grub_font_glyph * glyph,
-                           grub_video_color_t color, int x, int y)
-{
-  struct grub_video_i386_vbeblit_info target;
-  unsigned int width;
-  unsigned int charwidth;
-  unsigned int height;
-  unsigned int i;
-  unsigned int j;
-  unsigned int x_offset = 0;
-  unsigned int y_offset = 0;
-
-  /* Make sure there is something to do.  */
-  if (x >= (int)render_target->viewport.width)
-    return GRUB_ERR_NONE;
-
-  if (y >= (int)render_target->viewport.height)
-    return GRUB_ERR_NONE;
-
-  /* Calculate glyph dimensions.  */
-  width = ((glyph->width + 7) / 8) * 8;
-  charwidth = width;
-  height = glyph->height;
-
-  if (x + (int)width < 0)
-    return GRUB_ERR_NONE;
-
-  if (y + (int)height < 0)
-    return GRUB_ERR_NONE;
-
-  /* Do not allow drawing out of viewport.  */
-  if (x < 0)
-    {
-      width += x;
-      x_offset = (unsigned int)-x;
-      x = 0;
-    }
-  if (y < 0)
-    {
-      height += y;
-      y_offset = (unsigned int)-y;
-      y = 0;
-    }
-
-  if ((x + width) > render_target->viewport.width)
-    width = render_target->viewport.width - x;
-  if ((y + height) > render_target->viewport.height)
-    height = render_target->viewport.height - y;
-
-  /* Add viewport offset.  */
-  x += render_target->viewport.x;
-  y += render_target->viewport.y;
-
-  /* Use vbeblit_info to encapsulate rendering.  */
-  target.mode_info = &render_target->mode_info;
-  target.data = render_target->data;
-
-  /* Draw glyph.  */
-  for (j = 0; j < height; j++)
-    for (i = 0; i < width; i++)
-      if ((glyph->bitmap[((i + x_offset) / 8)
-                         + (j + y_offset) * (charwidth / 8)]
-           & (1 << ((charwidth - (i + x_offset) - 1) % 8))))
-        set_pixel (&target, x+i, y+j, color);
-
-  return GRUB_ERR_NONE;
-}
-
 /* NOTE: This function assumes that given coordinates are within bounds of
    handled data.  */
 static void
@@ -1619,7 +1587,6 @@
     .map_rgba = grub_video_vbe_map_rgba,
     .unmap_color = grub_video_vbe_unmap_color,
     .fill_rect = grub_video_vbe_fill_rect,
-    .blit_glyph = grub_video_vbe_blit_glyph,
     .blit_bitmap = grub_video_vbe_blit_bitmap,
     .blit_render_target = grub_video_vbe_blit_render_target,
     .scroll = grub_video_vbe_scroll,
Index: video/i386/pc/vbeutil.c
===================================================================
--- video/i386/pc/vbeutil.c     (revision 1934)
+++ video/i386/pc/vbeutil.c     (working copy)
@@ -1,6 +1,6 @@
 /*
  *  GRUB  --  GRand Unified Bootloader
- *  Copyright (C) 2006,2007  Free Software Foundation, Inc.
+ *  Copyright (C) 2006,2007,2008  Free Software Foundation, Inc.
  *
  *  GRUB is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -52,6 +52,12 @@
             + y * source->mode_info->pitch
             + x;
       break;
+
+    case 1:
+      /* For 1-bit bitmaps, addressing needs to be done at the bit level
+         and it doesn't make sense, in general, to ask for a pointer
+         to a particular pixel's data.  */
+      break;
     }
 
   return ptr;
@@ -86,6 +92,17 @@
       color = *(grub_uint8_t *)get_data_ptr (source, x, y);
       break;
 
+    case 1:
+      if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED)
+        {
+          int bit_index = y * source->mode_info->width + x;
+          grub_uint8_t *ptr = (grub_uint8_t *)source->data
+                              + bit_index / 8;
+          int bit_pos = 7 - bit_index % 8;
+          color = (*ptr >> bit_pos) & 0x01;
+        }
+      break;
+
     default:
       break;
     }
@@ -143,6 +160,17 @@
       }
       break;
 
+    case 1:
+      if (source->mode_info->blit_format == GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED)
+        {
+          int bit_index = y * source->mode_info->width + x;
+          grub_uint8_t *ptr = (grub_uint8_t *)source->data
+                              + bit_index / 8;
+          int bit_pos = 7 - bit_index % 8;
+          *ptr = (*ptr & ~(1 << bit_pos)) | ((color & 0x01) << bit_pos);
+        }
+      break;
+
     default:
       break;
     }
Index: configure.ac
===================================================================
--- configure.ac        (revision 1934)
+++ configure.ac        (working copy)
@@ -127,9 +127,9 @@
   AC_MSG_ERROR([bison is not found])
 fi
 
-for file in /usr/share/unifont/unifont.hex ; do
+for file in /usr/src/unifont.bdf ; do
   if test -e $file ; then
-    AC_SUBST([UNIFONT_HEX], [$file])
+    AC_SUBST([UNIFONT_BDF], [$file])
     break
   fi
 done
Index: font/font_cmd.c
===================================================================
--- font/font_cmd.c     (revision 0)
+++ font/font_cmd.c     (revision 0)
@@ -0,0 +1,77 @@
+/* font_cmd.c - Font command definition. */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2003,2005,2006,2007,2008  Free Software Foundation, Inc.
+ *
+ *  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/font.h>
+#include <grub/dl.h>
+#include <grub/normal.h>
+#include <grub/misc.h>
+
+static grub_err_t
+loadfont_command (struct grub_arg_list *state __attribute__ ((unused)),
+             int argc,
+             char **args)
+{
+  if (argc == 0)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, "no font specified");
+
+  while (argc--)
+    if (grub_font_load (*args++) != 0)
+      return GRUB_ERR_BAD_FONT;
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+lsfonts_command (struct grub_arg_list *state __attribute__ ((unused)),
+                 int argc __attribute__ ((unused)),
+                 char **args __attribute__ ((unused)))
+{
+  struct grub_font_node *node;
+
+  grub_printf ("Loaded fonts:\n");
+  for (node = grub_font_list; node; node = node->next)
+    {
+      grub_font_t font = node->value;
+      grub_printf ("%s\n", grub_font_get_name (font));
+    }
+
+  return GRUB_ERR_NONE;
+}
+
+GRUB_MOD_INIT(font_manager)
+{
+  grub_font_loader_init ();
+
+  grub_register_command ("loadfont", loadfont_command, GRUB_COMMAND_FLAG_BOTH,
+                        "loadfont FILE...",
+                        "Specify one or more font files to load.", 0);
+
+  grub_register_command ("lsfonts", lsfonts_command, GRUB_COMMAND_FLAG_BOTH,
+                        "lsfonts",
+                        "List the loaded fonts.", 0);
+}
+
+GRUB_MOD_FINI(font_manager)
+{
+  /* TODO: Determine way to free allocated resources.  
+     Warning: possible pointer references could be in use.  */
+
+  grub_unregister_command ("loadfont");
+}
+
Index: font/font.c
===================================================================
--- font/font.c (revision 0)
+++ font/font.c (revision 0)
@@ -0,0 +1,1026 @@
+/* font.c - Font API and font file loader.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2003,2005,2006,2007,2008  Free Software Foundation, Inc.
+ *
+ *  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>
+
+#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;
+  
+  /* Glyph if loaded, or NULL otherwise.  */
+  struct grub_font_glyph *glyph;
+};
+
+#define FONT_WEIGHT_NORMAL 100
+#define FONT_WEIGHT_BOLD 200
+
+struct grub_font
+{
+  char *name;
+  grub_file_t file;
+  char *family;
+  short point_size;
+  short weight;
+  short max_char_width;
+  short max_char_height;
+  short ascent;
+  short descent;
+  short leading;
+  grub_uint32_t num_chars;
+  struct char_index_entry *char_index;
+};
+
+/* 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;
+  
+  /* FOURCC name of the section.  */
+  char name[4];
+  
+  /* Length of the section contents.  */
+  grub_uint32_t length;
+  
+  /* Set by open_section() on EOF.  */
+  int eof;
+};
+
+/* Font file format constants.  */
+static const char pff2_magic[4] = { 'P', 'F', 'F', '2' };
+static const char section_names_file[4] = { 'F', 'I', 'L', 'E' };
+static const char section_names_font_name[4] = { 'N', 'A', 'M', 'E' };
+static const char section_names_point_size[4] = { 'P', 'T', 'S', 'Z' };
+static const char section_names_weight[4] = { 'W', 'E', 'I', 'G' };
+static const char section_names_max_char_width[4] = { 'M', 'A', 'X', 'W' };
+static const char section_names_max_char_height[4] = { 'M', 'A', 'X', 'H' };
+static const char section_names_ascent[4] = { 'A', 'S', 'C', 'E' };
+static const char section_names_descent[4] = { 'D', 'E', 'S', 'C' };
+static const char section_names_char_index[4] = { 'C', 'H', 'I', 'X' };
+static const char section_names_data[4] = { 'D', 'A', 'T', 'A' };
+
+/* Replace unknown glyphs with a rounded question mark.  */
+static grub_uint8_t unknown_glyph_bitmap[] =
+{
+  /*       76543210 */
+  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  /*          */
+};
+
+/* 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;
+
+void
+grub_font_loader_init (void)
+{
+  /* Only initialize font loader once.  */
+  if (font_loader_initialized)
+    return;
+
+  /* Make glyph for unknown glyph.  */
+  unknown_glyph = grub_malloc(sizeof(struct grub_font_glyph)
+                              + sizeof(unknown_glyph_bitmap));
+  if (! unknown_glyph)
+    return;
+
+  unknown_glyph->width = 8;
+  unknown_glyph->height = 16;
+  unknown_glyph->offset_x = 0;
+  unknown_glyph->offset_y = 0;
+  unknown_glyph->device_width = 8;
+  grub_memcpy(unknown_glyph->bitmap,
+              unknown_glyph_bitmap, sizeof(unknown_glyph_bitmap));
+
+  /* Initialize the null font.  */
+  font_init (&null_font);
+  null_font.name = "<No Font>";
+  null_font.ascent = unknown_glyph->height;
+  null_font.descent = 1;
+  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;
+  
+  /* Default leading value, not in font file yet.  */
+  font->leading = 1;
+  
+  font->max_char_width = 0;
+  font->max_char_height = 0;
+  font->ascent = 0;
+  font->descent = 0;
+  font->num_chars = 0;
+  font->char_index = 0;
+}
+
+/* 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
+   grub_errno is set approriately with an error message.
+
+   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)
+    {
+      grub_error (GRUB_ERR_BAD_FONT,
+                  "Font format error: can't read section name");
+      return 1;
+    }
+
+  /* Read the big-endian 32-bit section length.  */
+  retval = grub_file_read (file, (char *) &raw_length, 4);
+  if (retval >= 0 && retval < 4)
+    {
+      /* EOF encountered.  */
+      section->eof = 1;
+      return 1;
+    }
+  else if (retval < 0)
+    {
+      grub_error (GRUB_ERR_BAD_FONT,
+                  "Font format error: can't read section length");
+      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
+                 grub_font *font)
+{
+  unsigned i;
+
+#if FONT_DEBUG >= 2
+  grub_printf("load_font_index(sect_length=%d)\n", sect_length);
+#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,
+                  "Font file format error: character index length %d "
+                  "is not a multiple of the entry size %d",
+                  sect_length, FONT_CHAR_INDEX_ENTRY_SIZE);
+      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
+                                  * sizeof (struct char_index_entry));
+  if (! font->char_index)
+    return 1;
+
+#if FONT_DEBUG >= 2
+  grub_printf("num_chars=%d)\n", font->num_chars);
+#endif
+
+  /* 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.  */
+      if (grub_file_read (file, (char *) &entry->code, 4) != 4)
+        return 1;
+      entry->code = grub_be_to_cpu32 (entry->code);
+
+      /* Read storage flags byte.  */
+      if (grub_file_read (file, (char *) &entry->storage_flags, 1) != 1)
+        return 1;
+
+      /* Read glyph data offset; convert to native byte order.  */
+      if (grub_file_read (file, (char *) &entry->offset, 4) != 4)
+        return 1;
+      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)
+        grub_printf("c=%d o=%d\n", entry->code, entry->offset);
+#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);
+  if (! str)
+    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
+read_section_as_short (struct font_file_section *section, grub_int16_t *value)
+{
+  grub_uint16_t raw_value;
+
+  if (section->length != 2)
+    {
+      grub_error (GRUB_ERR_BAD_FONT,
+                  "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);
+      return 1;
+    }
+  if (grub_file_read (section->file, (char *) &raw_value, 2) != 2)
+    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.  */
+int
+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
+  grub_printf("add_font(%s)\n", filename);
+#endif
+
+  file = grub_buffile_open (filename, 1024);
+  if (!file)
+    goto fail;
+
+#if FONT_DEBUG >= 3
+  grub_printf("file opened\n");
+#endif
+
+  /* Read the FILE section.  It indicates the file format.  */
+  if (open_section (file, &section) != 0)
+    goto fail;
+
+#if FONT_DEBUG >= 3
+  grub_printf("opened FILE section\n");
+#endif
+  if (grub_memcmp (section.name, section_names_file, 4) != 0)
+    {
+      grub_error (GRUB_ERR_BAD_FONT,
+                  "Font file format error: 1st section must be FILE");
+      goto fail;
+    }
+
+#if FONT_DEBUG >= 3
+  grub_printf("section name ok\n");
+#endif
+  if (section.length != 4)
+    {
+      grub_error (GRUB_ERR_BAD_FONT,
+                  "Font file format error (file type ID length is %d "
+                  "but should be 4)", section.length);
+      goto fail;
+    }
+
+#if FONT_DEBUG >= 3
+  grub_printf("section length ok\n");
+#endif
+  /* Check the file format type code.  */
+  if (grub_file_read (file, magic, 4) != 4)
+    goto fail;
+
+#if FONT_DEBUG >= 3
+  grub_printf("read magic ok\n");
+#endif
+
+  if (grub_memcmp (magic, pff2_magic, 4) != 0)
+    {
+      grub_error (GRUB_ERR_BAD_FONT, "Invalid font magic %x %x %x %x",
+                  magic[0], magic[1], magic[2], magic[3]);
+      goto fail;
+    }
+
+#if FONT_DEBUG >= 3
+  grub_printf("compare magic ok\n");
+#endif
+
+  /* Allocate the font object.  */
+  font = (grub_font_t) grub_malloc (sizeof (struct grub_font));
+  if (! font)
+    goto fail;
+
+  font_init (font);
+  font->file = file;
+
+#if FONT_DEBUG >= 3
+  grub_printf("allocate font ok; loading font info\n");
+#endif
+
+  /* Load the font information.  */
+  while (1)
+    {
+      if (open_section (file, &section) != 0)
+        {
+          if (section.eof)
+            break;              /* Done reading the font file.  */
+          else
+            goto fail;
+        }
+
+#if FONT_DEBUG >= 2
+      grub_printf("opened section %c%c%c%c ok\n",
+                  section.name[0], section.name[1],
+                  section.name[2], section.name[3]);
+#endif
+
+      if (grub_memcmp (section.name, section_names_font_name, 4) == 0)
+        {
+          font->name = read_section_as_string (&section);
+          if (!font->name)
+            goto fail;
+        }
+      else if (grub_memcmp (section.name, section_names_point_size, 4) == 0)
+        {
+          if (read_section_as_short (&section, &font->point_size) != 0)
+            goto fail;
+        }
+      else if (grub_memcmp (section.name, section_names_weight, 4) == 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);
+        }
+      else if (grub_memcmp (section.name, section_names_max_char_width, 4) == 
0)
+        {
+          if (read_section_as_short (&section, &font->max_char_width) != 0)
+            goto fail;
+        }
+      else if (grub_memcmp (section.name, section_names_max_char_height, 4) == 
0)
+        {
+          if (read_section_as_short (&section, &font->max_char_height) != 0)
+            goto fail;
+        }
+      else if (grub_memcmp (section.name, section_names_ascent, 4) == 0)
+        {
+          if (read_section_as_short (&section, &font->ascent) != 0)
+            goto fail;
+        }
+      else if (grub_memcmp (section.name, section_names_descent, 4) == 0)
+        {
+          if (read_section_as_short (&section, &font->descent) != 0)
+            goto fail;
+        }
+      else if (grub_memcmp (section.name, section_names_char_index, 4) == 0)
+        {
+          if (load_font_index (file, section.length, font) != 0)
+            goto fail;
+        }
+      else if (grub_memcmp (section.name, section_names_data, 4) == 0)
+        {
+          /* When the DATA section marker is reached, we stop reading.  */
+          break;
+        }
+      else
+        {
+          /* Unhandled section type, simply skip past it.  */
+#if FONT_DEBUG >= 3
+          grub_printf("Unhandled section type, skipping.\n");
+#endif
+          grub_off_t section_end = grub_file_tell (file) + section.length;
+          if ((int) grub_file_seek (file, section_end) == -1)
+            goto fail;
+        }
+    }
+
+  if (! font->name)
+    {
+      grub_printf ("Note: Font has no name.\n");
+      font->name = grub_strdup ("Unknown");
+    }
+
+#if FONT_DEBUG >= 1
+  grub_printf ("Loaded font `%s'.\n"
+               "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);
+#endif
+
+  if (font->max_char_width == 0
+      || font->max_char_height == 0
+      || font->num_chars == 0
+      || font->char_index == 0
+      || font->ascent == 0
+      || font->descent == 0)
+    {
+      grub_error (GRUB_ERR_BAD_FONT,
+                  "Invalid font file: missing some required data.");
+      goto fail;
+    }
+
+  /* Add the font to the global font registry.  */
+  if (register_font (font) != 0)
+    goto fail;
+
+  return 0;
+
+fail:
+  free_font (font);
+  return 1;
+}
+
+/* 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)
+{
+  if (grub_file_read (file, (char *) value, 2) != 2)
+    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.  */
+static struct char_index_entry *
+find_glyph (const grub_font_t font, grub_uint32_t code)
+{
+  grub_uint32_t i;
+  grub_uint32_t len = font->num_chars;
+  struct char_index_entry *table = font->char_index;
+
+  /* Do a linear search.  */
+  for (i = 0; i < len; i++)
+    {
+      if (table[i].code == code)
+        return &table[i];
+    }
+
+  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)
+        /* Return cached glyph.  */
+        return index_entry->glyph;
+
+      if (! font->file)
+        /* No open file, can't load any glyphs.  */
+        return 0;     
+
+      /* 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.  */
+      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;
+        }
+
+      len = (width * height + 7) / 8;
+      glyph = grub_malloc (sizeof (struct grub_font_glyph) + len);
+      if (! glyph)
+        {
+          remove_font (font);
+          return 0;
+        }
+
+      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)
+        {
+          if (grub_file_read (font->file, (char *) glyph->bitmap, len) != len)
+            {
+              remove_font (font);
+              return 0;
+            }
+        }
+
+      /* 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)
+        grub_file_close (font->file);
+      grub_free (font->name);
+      grub_free (font->family);
+      grub_free (font->char_index);
+      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));
+  if (! node)
+    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;
+       cur;
+       nextp = &cur->next, cur = cur->next)
+    {
+      if (cur->value == font)
+        {
+          *nextp = cur->next;
+
+          /* Free the node, but not the font itself.  */
+          grub_free (cur);
+
+          return;
+        }
+    }
+}
+
+/* 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)
+        return font;
+    }
+
+  /* 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;
+}
+
+/* Get the full name of the font.  For instance, "Helvetica Bold 12".  */
+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 maximum height of any character in the font in pixels.  */
+int
+grub_font_get_max_char_height (grub_font_t font)
+{
+  return font->max_char_height;
+}
+
+/* Get the distance in pixels from the top of characters to the baseline.  */
+int
+grub_font_get_ascent (grub_font_t font)
+{
+  return font->ascent;
+}
+
+/* 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;
+}
+
+/* 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 width in pixels of the specified UTF-8 string, when rendered in
+   in the specified font (but falling back on other fonts for glyphs that
+   are missing).  */
+int
+grub_font_get_string_width (grub_font_t font, const char *str)
+{
+  int width;
+  struct grub_font_glyph *glyph;
+  grub_uint32_t code;
+  const grub_uint8_t *ptr;
+
+  for (ptr = (const grub_uint8_t *) str, width = 0;
+       grub_utf8_to_ucs4 (&code, 1, ptr, -1, &ptr) > 0; )
+    {
+      glyph = grub_font_get_glyph_with_fallback (font, code);
+      width += glyph->device_width;
+    }
+
+  return width;
+}
+
+/* Get the glyph for FONT corresponding to the Unicode code point CODE.
+   Returns a pointer to an glyph indicating there is no glyph available
+   if CODE does not exist in the font.  The glyphs are cached once loaded.  */
+struct grub_font_glyph *
+grub_font_get_glyph (grub_font_t font, grub_uint32_t code)
+{
+  struct grub_font_glyph *glyph;
+  glyph = grub_font_get_glyph_internal (font, code);
+  if (glyph == 0)
+    glyph = unknown_glyph;
+  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
+get_font_diversity(grub_font_t a, grub_font_t b)
+{
+  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;
+  
+  /* 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)
+        return glyph;
+    }
+
+  /* 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);
+      if (glyph)
+        {
+          int d;
+
+          d = get_font_diversity (curfont, font);
+          if (d < best_diversity)
+            {
+              best_diversity = d;
+              best_glyph = glyph;
+            }
+        }
+    }
+
+  if (best_glyph)
+    return best_glyph;
+  else
+    /* Glyph not available in any font.  Return unknown glyph.  */
+    return unknown_glyph;
+}
+
+
+/* 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
+grub_font_draw_glyph (struct grub_font_glyph *glyph,
+                      grub_video_color_t color,
+                      int left_x, int baseline_y)
+{
+  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;
+  glyph_bitmap.mode_info.mode_type =
+    (1 << GRUB_VIDEO_MODE_TYPE_DEPTH_POS)
+    | GRUB_VIDEO_MODE_TYPE_1BIT_BITMAP;
+  glyph_bitmap.mode_info.blit_format = GRUB_VIDEO_BLIT_FORMAT_1BIT_PACKED;
+  glyph_bitmap.mode_info.bpp = 1;
+  
+  /* Really 1 bit per pixel.  */
+  glyph_bitmap.mode_info.bytes_per_pixel = 0;
+  
+  /* Packed densely as bits.  */
+  glyph_bitmap.mode_info.pitch = glyph->width;
+  
+  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;
+  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);
+  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,
+                                 bitmap_left, bitmap_top,
+                                 0, 0,
+                                 glyph->width, glyph->height);
+}
+
+/* Draw a UTF-8 string of text on the current video render target.
+   The x coordinate specifies the starting x position for the first character,
+   while the y coordinate specifies the baseline position.
+   If the string contains a character that FONT does not contain, then
+   a glyph from another loaded font may be used instead.  */
+grub_err_t
+grub_font_draw_string (const char *str, grub_font_t font,
+                       grub_video_color_t color,
+                       int left_x, int baseline_y)
+{
+  int x;
+  struct grub_font_glyph *glyph;
+  grub_uint32_t code;
+  const grub_uint8_t *ptr;
+
+  for (ptr = (const grub_uint8_t *) str, x = left_x;
+       grub_utf8_to_ucs4 (&code, 1, ptr, -1, &ptr) > 0; )
+    {
+      glyph = grub_font_get_glyph_with_fallback (font, code);
+      if (grub_font_draw_glyph (glyph, color, x, baseline_y)
+          != GRUB_ERR_NONE)
+        return grub_errno;
+      x += glyph->device_width;
+    }
+
+  return GRUB_ERR_NONE;
+}
+
Index: font/manager.c
===================================================================
--- font/manager.c      (revision 1934)
+++ font/manager.c      (working copy)
@@ -1,283 +0,0 @@
-/*
- *  GRUB  --  GRand Unified Bootloader
- *  Copyright (C) 2003,2005,2006,2007  Free Software Foundation, Inc.
- *
- *  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/file.h>
-#include <grub/misc.h>
-#include <grub/dl.h>
-#include <grub/normal.h>
-#include <grub/types.h>
-#include <grub/mm.h>
-#include <grub/font.h>
-#include <grub/bufio.h>
-
-struct entry
-{
-  grub_uint32_t code;
-  grub_uint32_t offset;
-};
-
-struct font
-{
-  struct font *next;
-  grub_file_t file;
-  grub_uint32_t num;
-  struct entry table[0];
-};
-
-static struct font *font_list;
-
-/* Fill unknown glyph's with rounded question mark.  */
-static grub_uint8_t unknown_glyph[16] =
-{       /* 76543210 */
-  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  /*          */
-};
-
-static int
-add_font (const char *filename)
-{
-  grub_file_t file = 0;
-  char magic[4];
-  grub_uint32_t num, i;
-  struct font *font = 0;
-
-  file = grub_buffile_open (filename, 0);
-  if (! file)
-    goto fail;
-
-  if (grub_file_read (file, magic, 4) != 4)
-    goto fail;
-
-  if (grub_memcmp (magic, GRUB_FONT_MAGIC, 4) != 0)
-    {
-      grub_error (GRUB_ERR_BAD_FONT, "invalid font magic");
-      goto fail;
-    }
-
-  if (grub_file_read (file, (char *) &num, 4) != 4)
-    goto fail;
-
-  num = grub_le_to_cpu32 (num);
-  font = (struct font *) grub_malloc (sizeof (struct font)
-                                     + sizeof (struct entry) * num);
-  if (! font)
-    goto fail;
-
-  font->file = file;
-  font->num = num;
-
-  for (i = 0; i < num; i++)
-    {
-      grub_uint32_t code, offset;
-      
-      if (grub_file_read (file, (char *) &code, 4) != 4)
-       goto fail;
-
-      if (grub_file_read (file, (char *) &offset, 4) != 4)
-       goto fail;
-
-      font->table[i].code = grub_le_to_cpu32 (code);
-      font->table[i].offset = grub_le_to_cpu32 (offset);
-    }
-
-  font->next = font_list;
-  font_list = font;
-
-  return 1;
-
- fail:
-  if (font)
-    grub_free (font);
-
-  if (file)
-    grub_file_close (file);
-
-  return 0;
-}
-
-static void
-remove_font (struct font *font)
-{
-  struct font **p, *q;
-
-  for (p = &font_list, q = *p; q; p = &(q->next), q = q->next)
-    if (q == font)
-      {
-        *p = q->next;
-       
-       grub_file_close (font->file);
-       grub_free (font);
-       
-        break;
-      }
-}
-
-/* Return the offset of the glyph corresponding to the codepoint CODE
-   in the font FONT.  If no found, return zero.  */
-static grub_uint32_t
-find_glyph (const struct font *font, grub_uint32_t code)
-{
-  grub_uint32_t start = 0;
-  grub_uint32_t end = font->num - 1;
-  const struct entry *table = font->table;
-  
-  /* This shouldn't happen.  */
-  if (font->num == 0)
-    return 0;
-
-  /* Do a binary search.  */
-  while (start <= end)
-    {
-      grub_uint32_t i = (start + end) / 2;
-
-      if (table[i].code < code)
-       start = i + 1;
-      else if (table[i].code > code)
-       end = i - 1;
-      else
-       return table[i].offset;
-    }
-
-  return 0;
-}
-
-/* Set the glyph to something stupid.  */
-static void
-fill_with_default_glyph (grub_font_glyph_t glyph)
-{
-  unsigned i;
-
-  /* Use pre-defined pattern to fill unknown glyphs.  */
-  for (i = 0; i < 16; i++)
-    glyph->bitmap[i] = unknown_glyph[i];
-
-  glyph->char_width = 1;
-  glyph->width = glyph->char_width * 8;
-  glyph->height = 16;
-  glyph->baseline = (16 * 3) / 4;
-}
-
-/* Get a glyph corresponding to the codepoint CODE.  Always fill glyph
-   information with something, even if no glyph is found.  */
-int
-grub_font_get_glyph (grub_uint32_t code,
-                    grub_font_glyph_t glyph)
-{
-  struct font *font;
-  grub_uint8_t bitmap[32];
-
-  /* FIXME: It is necessary to cache glyphs!  */
-  
- restart:
-  for (font = font_list; font; font = font->next)
-    {
-      grub_uint32_t offset;
-
-      offset = find_glyph (font, code);
-      if (offset)
-       {
-         grub_uint32_t w;
-         int len;
-
-          /* 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, offset);
-         if ((len = grub_file_read (font->file, (char *) &w, sizeof (w)))
-             != sizeof (w))
-           {
-              remove_font (font);
-              goto restart;
-           }
-
-         w = grub_le_to_cpu32 (w);
-         if (w != 1 && w != 2)
-           {
-             /* grub_error (GRUB_ERR_BAD_FONT, "invalid width"); */
-             remove_font (font);
-             goto restart;
-           }
-
-         if (grub_file_read (font->file, (char *) bitmap, w * 16)
-             != (grub_ssize_t) w * 16)
-           {
-             remove_font (font);
-             goto restart;
-           }
-
-          /* Fill glyph with information.  */      
-          grub_memcpy (glyph->bitmap, bitmap, w * 16);
-          
-         glyph->char_width = w;
-         glyph->width = glyph->char_width * 8;
-         glyph->height = 16;
-         glyph->baseline = (16 * 3) / 4;
-         
-         /* Restore old error message.  */
-          grub_error_pop ();
-          
-         return 1;
-       }
-    }
-
-  /* Uggh...  No font was found.  */
-  fill_with_default_glyph (glyph);
-  return 0;
-}
-
-static grub_err_t
-font_command (struct grub_arg_list *state __attribute__ ((unused)),
-             int argc  __attribute__ ((unused)),
-             char **args __attribute__ ((unused)))
-{
-  if (argc == 0)
-    return grub_error (GRUB_ERR_BAD_ARGUMENT, "no font specified");
-
-  while (argc--)
-    if (! add_font (*args++))
-      return 1;
-
-  return 0;
-}
-
-GRUB_MOD_INIT(font_manager)
-{
-  grub_register_command ("font", font_command, GRUB_COMMAND_FLAG_BOTH,
-                        "font FILE...",
-                        "Specify one or more font files to display.", 0);
-}
-
-GRUB_MOD_FINI(font_manager)
-{
-  grub_unregister_command ("font");
-}
Index: commands/videotest.c
===================================================================
--- commands/videotest.c        (revision 1934)
+++ commands/videotest.c        (working copy)
@@ -1,6 +1,6 @@
 /*
  *  GRUB  --  GRand Unified Bootloader
- *  Copyright (C) 2006,2007  Free Software Foundation, Inc.
+ *  Copyright (C) 2006,2007,2008  Free Software Foundation, Inc.
  *
  *  GRUB is free software: you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -36,8 +36,6 @@
                         GRUB_VIDEO_MODE_TYPE_INDEX_COLOR) != GRUB_ERR_NONE)
     return grub_errno;
 
-  grub_getkey ();
-
   grub_video_color_t color;
   unsigned int x;
   unsigned int y;
@@ -44,9 +42,15 @@
   unsigned int width;
   unsigned int height;
   int i;
-  struct grub_font_glyph glyph;
+  grub_font_t sansbig;
+  grub_font_t sans;
+  grub_font_t sanssmall;
+  grub_font_t fixed;
+  struct grub_font_glyph *glyph;
   struct grub_video_render_target *text_layer;
   grub_video_color_t palette[16];
+  const char *str;
+  int texty;
 
   grub_video_get_viewport (&x, &y, &width, &height);
 
@@ -65,8 +69,15 @@
   color = grub_video_map_rgb (0, 255, 255);
   grub_video_fill_rect (color, 100, 100, 100, 100);
 
-  grub_font_get_glyph ('*', &glyph);  
-  grub_video_blit_glyph (&glyph, color, 200 ,0);
+  sansbig = grub_font_get ("Helvetica Bold 24");
+  sans = grub_font_get ("Helvetica Bold 14");
+  sanssmall = grub_font_get ("Helvetica 8");
+  fixed = grub_font_get ("Fixed 20");
+  if (! sansbig || ! sans || ! sanssmall || ! fixed)
+    return grub_error (GRUB_ERR_BAD_FONT, "No font loaded.");
+
+  glyph = grub_font_get_glyph (fixed, '*');
+  grub_font_draw_glyph (glyph, color, 200 ,0);
 
   grub_video_set_viewport (x + 150, y + 150,
                            width - 150 * 2, height - 150 * 2);
@@ -77,12 +88,63 @@
 
   color = grub_video_map_rgb (255, 255, 255);
 
-  grub_font_get_glyph ('A', &glyph);
-  grub_video_blit_glyph (&glyph, color, 16, 16);
-  grub_font_get_glyph ('B', &glyph);
-  grub_video_blit_glyph (&glyph, color, 16 * 2, 16);
+  texty = 32;
+  grub_font_draw_string ("The quick brown fox jumped over the lazy dog.",
+                         sans, color, 16, texty);
+  texty += grub_font_get_descent (sans) + grub_font_get_leading (sans);
+
+  texty += grub_font_get_ascent (fixed);
+  grub_font_draw_string ("The quick brown fox jumped over the lazy dog.",
+                         fixed, color, 16, texty);
+  texty += grub_font_get_descent (fixed) + grub_font_get_leading (fixed);
+
+  /* To convert Unicode characters into UTF-8 for this test, the following
+     command is useful:
+       echo -ne '\x00\x00\x26\x3A' | iconv -f UTF-32BE -t UTF-8 | od -t x1
+     This converts the Unicode character U+263A to UTF-8.  */
+
+  /* Characters used:
+     Code point  Description                    UTF-8 encoding
+     ----------- ------------------------------ --------------
+     U+263A      unfilled smiley face           E2 98 BA
+     U+00A1      inverted exclamation point     C2 A1
+     U+00A3      British pound currency symbol  C2 A3
+     U+03C4      Greek tau                      CF 84
+     U+00E4      lowercase letter a with umlaut C3 A4
+     U+2124      set 'Z' symbol (integers)      E2 84 A4
+     U+2287      subset symbol                  E2 8A 87
+     U+211D      set 'R' symbol (real numbers)  E2 84 9D  */
+
+  str =
+    "Unicode test: happy\xE2\x98\xBA \xC2\xA3 5.00"
+    " \xC2\xA1\xCF\x84\xC3\xA4u! "
+    " \xE2\x84\xA4\xE2\x8A\x87\xE2\x84\x9D";
+  color = grub_video_map_rgb (128, 128, 255);
+
+  /* All characters in the string exist in the 'Fixed 20' (10x20) font.  */
+  texty += grub_font_get_ascent(fixed);
+  grub_font_draw_string (str, fixed, color, 16, texty);
+  texty += grub_font_get_descent (fixed) + grub_font_get_leading (fixed);
+
+  /* Some character don't exist in the Helvetica font, so the font engine
+     will fall back to using glyphs from another font that does contain them.
+     TODO The font engine should be smart about selecting a replacement font
+     and prioritize fonts with similar sizes.  */
+
+  texty += grub_font_get_ascent(sansbig);
+  grub_font_draw_string (str, sansbig, color, 16, texty);
+  texty += grub_font_get_descent (sansbig) + grub_font_get_leading (sansbig);
 
-  grub_font_get_glyph ('*', &glyph);
+  texty += grub_font_get_ascent(sans);
+  grub_font_draw_string (str, sans, color, 16, texty);
+  texty += grub_font_get_descent (sans) + grub_font_get_leading (sans);
+
+  texty += grub_font_get_ascent(sanssmall);
+  grub_font_draw_string (str, sanssmall, color, 16, texty);
+  texty += (grub_font_get_descent (sanssmall)
+            + grub_font_get_leading (sanssmall));
+
+  glyph = grub_font_get_glyph (fixed, '*');
 
   for (i = 0; i < 16; i++)
     {
@@ -88,7 +150,7 @@
     {
       color = grub_video_map_color (i);
       palette[i] = color;
-      grub_video_blit_glyph (&glyph, color, 16 + i * 16, 32);
+      grub_font_draw_glyph (glyph, color, 16 + i * 16, 220);
     }
 
   grub_video_set_active_render_target (GRUB_VIDEO_RENDER_TARGET_DISPLAY);
Index: util/fonttool/src/org/gnu/grub/fonttool/BDFLoader.java
===================================================================
--- util/fonttool/src/org/gnu/grub/fonttool/BDFLoader.java      (revision 0)
+++ util/fonttool/src/org/gnu/grub/fonttool/BDFLoader.java      (revision 0)
@@ -0,0 +1,271 @@
+/**
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  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/>.
+ */
+package org.gnu.grub.fonttool;
+
+import java.io.*;
+import java.util.StringTokenizer;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public class BDFLoader {
+    private final BufferedReader in;
+    private final Font font;
+    private int maxCharWidth;
+    private int maxCharHeight;
+
+    BDFLoader(BufferedReader in) {
+        this.in = in;
+        this.font = new Font();
+        this.maxCharWidth = 0;
+        this.maxCharHeight = 0;
+    }
+
+    public static boolean isBDFFile(String filename) {
+        DataInputStream in = null;
+        try {
+            in = new DataInputStream(new FileInputStream(filename));
+            final String signature = "STARTFONT ";
+            byte[] b = new byte[signature.length()];
+            in.readFully(b);
+            in.close();
+
+            String s = new String(b, "US-ASCII");
+            return signature.equals(s);
+        } catch (IOException e) {
+            if (in != null) {
+                try {
+                    in.close();
+                } catch (IOException e1) {
+                    // Ignore.
+                }
+            }
+            return false;
+        }
+    }
+
+    public static Font loadFontResource(String resourceName) throws 
IOException {
+        InputStream in = 
BDFLoader.class.getClassLoader().getResourceAsStream(resourceName);
+        if (in == null)
+            throw new FileNotFoundException("Font resource " + resourceName + 
" not found");
+        return loadFontFromStream(in);
+    }
+
+    public static Font loadFontFile(String filename) throws IOException {
+        InputStream in = new FileInputStream(filename);
+        return loadFontFromStream(in);
+    }
+
+    private static Font loadFontFromStream(InputStream inStream) throws 
IOException {
+        BufferedReader in;
+        try {
+            in = new BufferedReader(new InputStreamReader(inStream, 
"ISO-8859-1"));
+        } catch (UnsupportedEncodingException e) {
+            throw new RuntimeException("Encoding not supported: " + 
e.getMessage(), e);
+        }
+        return new BDFLoader(in).loadFont();
+    }
+
+    private Font loadFont() throws IOException {
+        loadFontInfo();
+        while (loadChar()) {
+            /* Loop. */
+        }
+
+        font.setMaxCharWidth(maxCharWidth);
+        font.setMaxCharHeight(maxCharHeight);
+        return font;
+    }
+
+    private void loadFontInfo() throws IOException {
+        final Pattern stringSettingPattern = 
Pattern.compile("^(\\w+)\\s+\"([^\"]+)\"$");
+        String line;
+        // Load the global font information that appears before CHARS.
+        final int UNSET = Integer.MIN_VALUE;
+        font.setAscent(UNSET);
+        font.setDescent(UNSET);
+        font.setFamily("Unknown");
+        font.setBold(false);
+        font.setItalic(false);
+        font.setPointSize(Font.UNKNOWN_POINT_SIZE);
+        do {
+            line = in.readLine();
+            if (line == null)
+                throw new IOException("BDF format error: end of file while " +
+                                      "reading global font information");
+
+            StringTokenizer st = new StringTokenizer(line);
+            if (st.hasMoreTokens()) {
+                String name = st.nextToken();
+                if (name.equals("FONT_ASCENT")) {
+                    if (!st.hasMoreTokens())
+                        throw new IOException("BDF format error: " +
+                                              "no tokens after " + name);
+                    font.setAscent(Integer.parseInt(st.nextToken()));
+                } else if (name.equals("FONT_DESCENT")) {
+                    if (!st.hasMoreTokens())
+                        throw new IOException("BDF format error: " +
+                                              "no tokens after " + name);
+                    font.setDescent(Integer.parseInt(st.nextToken()));
+                } else if (name.equals("POINT_SIZE")) {
+                    if (!st.hasMoreTokens())
+                        throw new IOException("BDF format error: " +
+                                              "no tokens after " + name);
+                    // Divide by 10, since it is stored X10.
+                    font.setPointSize(Integer.parseInt(st.nextToken()) / 10);
+                } else if (name.equals("FAMILY_NAME")) {
+                    Matcher matcher = stringSettingPattern.matcher(line);
+                    if (!matcher.matches())
+                        throw new IOException("BDF format error: " +
+                                              "line doesn't match string " +
+                                              "setting pattern: " + line);
+                    font.setFamily(matcher.group(2));
+                } else if (name.equals("WEIGHT_NAME")) {
+                    Matcher matcher = stringSettingPattern.matcher(line);
+                    if (!matcher.matches())
+                        throw new IOException("BDF format error: " +
+                                              "line doesn't match string " +
+                                              "setting pattern: " + line);
+                    String weightName = matcher.group(2);
+                    font.setBold("bold".equalsIgnoreCase(weightName));
+                } else if (name.equals("SLANT")) {
+                    Matcher matcher = stringSettingPattern.matcher(line);
+                    if (!matcher.matches())
+                        throw new IOException("BDF format error: " +
+                                              "line doesn't match string " +
+                                              "setting pattern: " + line);
+                    String slantType = matcher.group(2);
+                    font.setItalic(!"R".equalsIgnoreCase(slantType));
+                } else if (name.equals("CHARS")) {
+                    // This is the end of the global font information and
+                    // the beginning of the character definitions.
+                    break;
+                } else {
+                    // Skip other fields.
+                }
+            }
+        } while (true);
+
+        if (font.getAscent() == UNSET)
+            throw new IOException("BDF format error: no FONT_ASCENT property");
+        if (font.getDescent() == UNSET)
+            throw new IOException("BDF format error: no FONT_DESCENT 
property");
+    }
+
+    private boolean loadChar() throws IOException {
+        String line;
+        // Find start of character
+        do {
+            line = in.readLine();
+            if (line == null)
+                return false;
+            StringTokenizer st = new StringTokenizer(line);
+            if (st.hasMoreTokens() && st.nextToken().equals("STARTCHAR")) {
+                if (!st.hasMoreTokens())
+                    throw new IOException("BDF format error: no character name 
after STARTCHAR");
+                break;
+            }
+        } while (true);
+
+        // Find properties
+        final int UNSET = Integer.MIN_VALUE;
+        int codePoint = UNSET;
+        int bbx = UNSET;
+        int bby = UNSET;
+        int bbox = UNSET;
+        int bboy = UNSET;
+        int dwidth = UNSET;
+        do {
+            line = in.readLine();
+            if (line == null)
+                return false;
+            StringTokenizer st = new StringTokenizer(line);
+            if (st.hasMoreTokens()) {
+                String field = st.nextToken();
+                if (field.equals("ENCODING")) {
+                    if (!st.hasMoreTokens())
+                        throw new IOException("BDF format error: no encoding # 
after ENCODING");
+                    String codePointStr = st.nextToken();
+                    codePoint = Integer.parseInt(codePointStr);
+                } else if (field.equals("BBX")) {
+                    if (!st.hasMoreTokens())
+                        throw new IOException("BDF format error: no tokens 
after BBX");
+                    bbx = Integer.parseInt(st.nextToken());
+                    bby = Integer.parseInt(st.nextToken());
+                    bbox = Integer.parseInt(st.nextToken());
+                    bboy = Integer.parseInt(st.nextToken());
+                } else if (field.equals("DWIDTH")) {
+                    if (!st.hasMoreTokens())
+                        throw new IOException("BDF format error: no tokens 
after DWIDTH");
+                    dwidth = Integer.parseInt(st.nextToken());
+                    int dwidthY = Integer.parseInt(st.nextToken());
+                    // The DWIDTH Y value should be zero for any normal font.
+                    if (dwidthY != 0) {
+                        throw new IOException("BDF format error: dwidth Y 
value" +
+                                              "is nonzero (" + dwidthY + ") " +
+                                              "for char " + codePoint + ".");
+                    }
+                } else if (field.equals("BITMAP")) {
+                    break; // now read the bitmap
+                }
+            }
+        } while (true);
+
+        if (codePoint == UNSET)
+            throw new IOException("BDF format error: " +
+                                  "no code point set");
+        if (bbx == UNSET || bby == UNSET)
+            throw new IOException("BDF format error: " +
+                                  "bbx/bby missing: " + bbx + ", " + bby +
+                                  " for char " + codePoint);
+
+        if (bbox == UNSET || bboy == UNSET)
+            throw new IOException("BDF format error: " +
+                                  "bbox/bboy missing: " + bbox + ", " + bboy +
+                                  " for char " + codePoint);
+
+        if (dwidth == UNSET)
+            throw new IOException("BDF format error: " +
+                                  "dwidth missing for char " + codePoint);
+
+        final int glyphWidth = bbx;
+        final int glyphHeight = bby;
+        if (glyphWidth > maxCharWidth)
+            maxCharWidth = glyphWidth;
+        if (glyphHeight > maxCharHeight)
+            maxCharHeight = glyphHeight;
+
+        // Read the bitmap
+        Glyph glyph = new Glyph(codePoint, glyphWidth, glyphHeight, bbox, 
bboy, dwidth);
+        for (int y = 0; y < glyphHeight; y++) {
+            line = in.readLine();
+            if (line == null)
+                return false;
+            for (int b = 0; b < line.length(); b++) {
+                int v = Integer.parseInt(Character.toString(line.charAt(b)), 
16);
+                for (int x = b * 4, i = 0; i < 4 && x < glyphWidth; x++, i++) {
+                    boolean set = (v & 0x8) != 0;
+                    v <<= 1;
+                    glyph.setPixel(x, y, set);
+                }
+            }
+        }
+
+        font.putGlyph(codePoint, glyph);
+        return true;
+    }
+}
Index: util/fonttool/src/org/gnu/grub/fonttool/CharDefs.java
===================================================================
--- util/fonttool/src/org/gnu/grub/fonttool/CharDefs.java       (revision 0)
+++ util/fonttool/src/org/gnu/grub/fonttool/CharDefs.java       (revision 0)
@@ -0,0 +1,181 @@
+/**
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  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/>.
+ */
+package org.gnu.grub.fonttool;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.TreeMap;
+
+class CharDefs {
+    private boolean debug = "1".equals(System.getProperty("fonttool.debug"));
+    private TreeMap<Integer, Glyph> glyphs;
+    private ByteArrayOutputStream charDefsData;
+    private int maxCharWidth;
+    private int maxCharHeight;
+    private HashMap<Integer, CharStorageInfo> charIndex;
+
+    public CharDefs(Font font) {
+        this.glyphs = font.getGlyphs();
+        this.charIndex = null;
+        this.charDefsData = null;
+
+        calculateMaxSizes();
+    }
+
+    private void calculateMaxSizes() {
+        maxCharWidth = 0;
+        maxCharHeight = 0;
+        for (Glyph glyph : glyphs.values()) {
+            final int w = glyph.getWidth();
+            final int h = glyph.getHeight();
+            if (w > maxCharWidth)
+                maxCharWidth = w;
+            if (h > maxCharHeight)
+                maxCharHeight = h;
+        }
+    }
+
+    void buildDefinitions(List<CharacterRange> rangeList) {
+        charIndex = new HashMap<Integer, CharStorageInfo>();
+        HashMap<CharDef, Long> charDefIndex = new HashMap<CharDef, Long>();
+        charDefsData = new ByteArrayOutputStream();
+        DataOutputStream charDefs = new DataOutputStream(charDefsData);
+        try {
+            // Loop through all the glyphs, writing the glyph data to the
+            // in-memory byte stream, collapsing duplicate glyphs, and
+            // constructing index information.
+            for (Glyph glyph : glyphs.values()) {
+               // Determine if glyph should be included in written file
+                               if (rangeList.size() > 0) {
+                       boolean skip = true;
+                       
+                                       for (Iterator<CharacterRange> iter = 
rangeList.iterator(); iter
+                                                       .hasNext();) {
+                                               CharacterRange item = 
iter.next();
+
+                                               if 
(item.isWithinRange(glyph.getCodePoint())) {
+                                                       skip = false;
+                                                       break;
+                                               }
+                                       }
+
+                                       if (skip) {
+                                               continue;
+                                       }
+                               }
+               
+                CharDef charDef = new CharDef(glyph.getWidth(),
+                                              glyph.getHeight(),
+                                              glyph.getBitmap());
+
+                if (charDefIndex.containsKey(charDef)) {
+                    // Use already-written glyph.
+                    if (debug)
+                        System.out.printf("Duplicate glyph for character 
U+%04X%n",
+                                          glyph.getCodePoint());
+                    final int charOffset = 
charDefIndex.get(charDef).intValue();
+                    final CharStorageInfo info =
+                            new CharStorageInfo(glyph.getCodePoint(), 
charOffset);
+                    charIndex.put(glyph.getCodePoint(), info);
+                } else {
+                    // Write glyph data.
+                    final int charOffset = charDefs.size();
+                    final CharStorageInfo info =
+                            new CharStorageInfo(glyph.getCodePoint(), 
charOffset);
+                    charIndex.put(glyph.getCodePoint(), info);
+
+                    charDefIndex.put(charDef, (long) charOffset);
+
+                    charDefs.writeShort(glyph.getWidth());
+                    charDefs.writeShort(glyph.getHeight());
+                    charDefs.writeShort(glyph.getBbox());
+                    charDefs.writeShort(glyph.getBboy());
+                    charDefs.writeShort(glyph.getDeviceWidth());
+                    charDefs.write(glyph.getBitmap());
+                }
+            }
+        } catch (IOException e) {
+            throw new RuntimeException("Error writing to in-memory byte 
stream", e);
+        }
+    }
+
+    public int getMaxCharWidth() {
+        return maxCharWidth;
+    }
+
+    public int getMaxCharHeight() {
+        return maxCharHeight;
+    }
+
+    public HashMap<Integer, CharStorageInfo> getCharIndex() {
+        if (charIndex == null) throw new IllegalStateException();
+        return charIndex;
+    }
+
+    public byte[] getDefinitionData() {
+        if (charDefsData == null) throw new IllegalStateException();
+        return charDefsData.toByteArray();
+    }
+
+
+    private static class CharDef {
+        private final int width;
+        private final int height;
+        private final byte[] data;
+
+        public CharDef(int width, int height, byte[] data) {
+            this.width = width;
+            this.height = height;
+            this.data = data;
+        }
+
+        public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+
+            CharDef charDef = (CharDef) o;
+
+            if (height != charDef.height) return false;
+            if (width != charDef.width) return false;
+            if (!Arrays.equals(data, charDef.data)) return false;
+
+            return true;
+        }
+
+        public int hashCode() {
+            int result;
+            result = width;
+            result = 31 * result + height;
+            result = 31 * result + Arrays.hashCode(data);
+            return result;
+        }
+    }
+
+
+       public int getIndexSize() {
+               if (charIndex == null)
+                       return 0;
+               
+               return charIndex.size();
+       }
+}
Index: util/fonttool/src/org/gnu/grub/fonttool/CharStorageInfo.java
===================================================================
--- util/fonttool/src/org/gnu/grub/fonttool/CharStorageInfo.java        
(revision 0)
+++ util/fonttool/src/org/gnu/grub/fonttool/CharStorageInfo.java        
(revision 0)
@@ -0,0 +1,36 @@
+/**
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  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/>.
+ */
+package org.gnu.grub.fonttool;
+
+class CharStorageInfo {
+    private final int codePoint;
+    private final int fileOffset;
+
+    public CharStorageInfo(int codePoint, int fileOffset) {
+        this.codePoint = codePoint;
+        this.fileOffset = fileOffset;
+    }
+
+    public int getCodePoint() {
+        return codePoint;
+    }
+
+    public int getFileOffset() {
+        return fileOffset;
+    }
+}
Index: util/fonttool/src/org/gnu/grub/fonttool/CharacterRange.java
===================================================================
--- util/fonttool/src/org/gnu/grub/fonttool/CharacterRange.java (revision 0)
+++ util/fonttool/src/org/gnu/grub/fonttool/CharacterRange.java (revision 0)
@@ -0,0 +1,75 @@
+/**
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  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/>.
+ */
+package org.gnu.grub.fonttool;
+
+/**
+ * @author chaac
+ *
+ */
+public class CharacterRange {
+       private int start;
+       private int end;
+       
+       public CharacterRange(int start, int end) {
+               this.start = start;
+               this.end = end;
+       }
+
+       public int getStart() {
+               return start;
+       }
+
+       public int getEnd() {
+               return end;
+       }
+       
+       protected boolean isCombinable(CharacterRange range) {
+               if (getStart() <= range.getStart() && range.getStart() <= 
getEnd())
+                       return true;
+               
+               if (range.getStart() <= getStart() && getStart() <= 
range.getEnd())
+                       return true;
+
+               return false;
+       }
+       
+       public boolean isWithinRange(int value) {
+               if (value >= start && value <= end)
+                       return true;
+               
+               return false;
+       }
+       
+       public boolean combine(CharacterRange range) {
+               int start;
+               int end;
+               
+               if (! isCombinable(range))
+                       return false;
+               
+               start = getStart();
+               if (range.getStart() < start)
+                       start = range.getStart();
+               
+               end = getEnd();
+               if (range.getEnd() > end)
+                       end = range.getEnd();
+               
+               return true;
+       }
+}
Index: util/fonttool/src/org/gnu/grub/fonttool/Converter.java
===================================================================
--- util/fonttool/src/org/gnu/grub/fonttool/Converter.java      (revision 0)
+++ util/fonttool/src/org/gnu/grub/fonttool/Converter.java      (revision 0)
@@ -0,0 +1,168 @@
+/**
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  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/>.
+ */
+package org.gnu.grub.fonttool;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+/**
+ * Program to convert BDF fonts into PFF2 fonts for use with GRUB.
+ */
+public class Converter {
+    public static void main(String[] args) {
+        if (args.length < 1) {
+            printUsageAndExit();
+        }
+
+        String in = null;
+        String out = null;
+        List <CharacterRange> rangeList = new ArrayList<CharacterRange>();
+
+        try {
+            for (String arg : args) {
+                               if (arg.startsWith("--")) {
+                                       String option;
+                                       String value;
+                                       int equalsPos = arg.indexOf('=');
+                                       if (equalsPos < 0) {
+                                               option = arg.substring(2);
+                                               value = null;
+                                       } else {
+                                               option = arg.substring(2, 
equalsPos);
+                                               value = arg.substring(equalsPos 
+ 1);
+                                       }
+
+                                       if ("in".equals(option)) {
+                                               if (value == null)
+                                                       throw new 
CommandLineException(option
+                                                                       + " 
option requires a value.");
+                                               in = value;
+                                       } else if ("out".equals(option)) {
+                                               if (value == null)
+                                                       throw new 
CommandLineException(option
+                                                                       + " 
option requires a value.");
+                                               out = value;
+                                       }
+                               } else if (arg.startsWith("0x")) {
+                                       // Range specifier
+                                       String strRange[] = arg.split("-");
+
+                                       if (strRange.length > 0) {
+                                               boolean validRange = true;
+                                               int start;
+                                               int end;
+
+                                               if (strRange.length > 2) {
+                                                       validRange = false;
+                                               } else if (strRange.length == 2
+                                                               && 
!strRange[1].startsWith("0x")) {
+                                                       validRange = false;
+                                               } else
+                                               {
+                                                       try {
+                                                               start = 
Integer.parseInt(strRange[0]
+                                                                               
.substring(2), 16);
+                                                               end = start;
+
+                                                               if 
(strRange.length == 2) {
+                                                                       end = 
Integer.parseInt(strRange[1]
+                                                                               
        .substring(2), 16);
+                                                               }
+                                                               
+                                                               CharacterRange 
range = new CharacterRange(
+                                                                               
start, end);
+                                                               boolean add = 
true;
+                                                               
+                                                               // First, try 
to combine range to existing ranges
+                                                               for 
(Iterator<CharacterRange> iter = rangeList.iterator(); iter.hasNext(); )
+                                                               {
+                                                                       
CharacterRange item = iter.next();
+                                                                       
+                                                                       if 
(range.equals(item))
+                                                                       {
+                                                                               
add = false;
+                                                                               
continue;
+                                                                       }
+                                                                       
+                                                                       if 
(item.combine(range))
+                                                                       {
+                                                                               
// Start from beginning of list using combined range
+                                                                               
range = item;
+                                                                               
iter = rangeList.iterator();
+                                                                               
add = false;
+                                                                       }
+                                                               }
+                                                               
+                                                               // If range 
could not be combined or no matching range, add it to the list
+                                                               if (add)
+                                                               {
+                                                                       
rangeList.add(range);
+                                                               }
+
+                                                       } catch 
(NumberFormatException e) {
+                                                               validRange = 
false;
+                                                       }
+
+                                               }
+
+                                               if (!validRange) {
+                                                       throw new 
CommandLineException("Invalid range `"
+                                                                       + arg + 
"'.");
+                                               }
+                                       }
+                               } else {
+                    throw new CommandLineException("Non-option argument `" + 
arg + "'.");
+                }
+            }
+            if (in == null || out == null) {
+                throw new CommandLineException("Both --in=X and --out=Y must 
be specified.");
+            }
+        } catch (CommandLineException e) {
+            System.err.println("Error: " + e.getMessage());
+            System.exit(1);
+        }
+
+        try {
+            // Read BDF.
+            Font font = BDFLoader.loadFontFile(in);
+
+            // Write PFF2.
+            new PFF2Writer(out).writeFont(font, rangeList);
+        } catch (IOException e) {
+            System.err.println("I/O error converting font: " + e);
+            e.printStackTrace();
+            System.exit(1);
+        }
+    }
+
+    private static class CommandLineException extends Exception {
+        public CommandLineException(String message) {
+            super(message);
+        }
+    }
+
+    private static void printUsageAndExit() {
+        System.err.println("GNU GRUB Font Conversion Tool");
+        System.err.println();
+        System.err.println("Usage: Converter --in=IN.bdf --out=OUT.pf2");
+        System.err.println();
+        System.exit(1);
+    }
+}
Index: util/fonttool/src/org/gnu/grub/fonttool/Font.java
===================================================================
--- util/fonttool/src/org/gnu/grub/fonttool/Font.java   (revision 0)
+++ util/fonttool/src/org/gnu/grub/fonttool/Font.java   (revision 0)
@@ -0,0 +1,123 @@
+/**
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  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/>.
+ */
+package org.gnu.grub.fonttool;
+
+import java.util.TreeMap;
+
+public class Font {
+    public static final int UNKNOWN_POINT_SIZE = -1;
+
+    private TreeMap<Integer, Glyph> glyphs;
+    private String family;
+    private boolean bold;
+    private boolean italic;
+    private int pointSize;
+    private int maxCharWidth;
+    private int maxCharHeight;
+    private int ascent;
+    private int descent;
+
+    public Font() {
+        glyphs = new TreeMap<Integer, Glyph>();
+    }
+
+    public String getFamily() {
+        return family;
+    }
+
+    public void setFamily(String family) {
+        this.family = family;
+    }
+
+    public boolean isBold() {
+        return bold;
+    }
+
+    public void setBold(boolean bold) {
+        this.bold = bold;
+    }
+
+    public boolean isItalic() {
+        return italic;
+    }
+
+    public void setItalic(boolean italic) {
+        this.italic = italic;
+    }
+
+    public int getPointSize() {
+        return pointSize;
+    }
+
+    public void setPointSize(int pointSize) {
+        this.pointSize = pointSize;
+    }
+
+    public int getMaxCharWidth() {
+        return maxCharWidth;
+    }
+
+    public void setMaxCharWidth(int maxCharWidth) {
+        this.maxCharWidth = maxCharWidth;
+    }
+
+    public int getMaxCharHeight() {
+        return maxCharHeight;
+    }
+
+    public void setMaxCharHeight(int maxCharHeight) {
+        this.maxCharHeight = maxCharHeight;
+    }
+
+    public int getAscent() {
+        return ascent;
+    }
+
+    public void setAscent(int ascent) {
+        this.ascent = ascent;
+    }
+
+    public int getDescent() {
+        return descent;
+    }
+
+    public void setDescent(int descent) {
+        this.descent = descent;
+    }
+
+    public void putGlyph(int codePoint, Glyph glyph) {
+        glyphs.put(codePoint, glyph);
+    }
+
+    public TreeMap<Integer, Glyph> getGlyphs() {
+        return glyphs;
+    }
+
+    public Glyph getGlyph(int codePoint) {
+        return glyphs.get(codePoint);
+    }
+
+    public String getStandardName() {
+        StringBuilder name = new StringBuilder(getFamily());
+        if (isBold()) name.append(" Bold");
+        if (isItalic()) name.append(" Italic");
+        name.append(' ');
+        name.append(getPointSize());
+        return name.toString();
+    }
+}
Index: util/fonttool/src/org/gnu/grub/fonttool/Glyph.java
===================================================================
--- util/fonttool/src/org/gnu/grub/fonttool/Glyph.java  (revision 0)
+++ util/fonttool/src/org/gnu/grub/fonttool/Glyph.java  (revision 0)
@@ -0,0 +1,100 @@
+/**
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  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/>.
+ */
+package org.gnu.grub.fonttool;
+
+public class Glyph {
+    private final int codePoint;
+    private final int width;
+    private final int height;
+
+    // These define the amounts to shift the character bitmap by
+    // before drawing it.  See
+    //    http://www.adobe.com/devnet/font/pdfs/5005.BDF_Spec.pdf
+    // and
+    //    http://www.linuxbabble.com/documentation/x/bdf/
+    // for explanatory figures.
+    private final int bbox;
+    private final int bboy;
+
+    // Number of pixels to advance horizontally from this character's origin
+    // to the origin of the next character.
+    private final int deviceWidth;
+
+    // Row-major order, no padding.  Rows can break within a byte.
+    // MSb is first (leftmost/uppermost) pixel.
+    private final byte[] bitmap;
+
+    public Glyph(int codePoint, int width, int height, int bbox, int bboy, int 
deviceWidth) {
+        this(codePoint, width, height, bbox, bboy, deviceWidth,
+             new byte[(width * height + 7) / 8]);
+    }
+
+    public Glyph(int codePoint, int width, int height,
+                 int bbox, int bboy, int deviceWidth,
+                 byte[] bitmap) {
+        this.codePoint = codePoint;
+        this.width = width;
+        this.height = height;
+        this.bboy = bboy;
+        this.bbox = bbox;
+        this.deviceWidth = deviceWidth;
+        this.bitmap = bitmap;
+    }
+
+    public void setPixel(int x, int y, boolean value) {
+        if (x < 0 || y < 0 || x >= width || y >= height)
+            throw new IllegalArgumentException(
+                    "Invalid pixel location (" + x + ", " + y + ") for "
+                    + width + "x" + height + " glyph");
+
+        int bitIndex = y * width + x;
+        int byteIndex = bitIndex / 8;
+        int bitPos = bitIndex % 8;
+        int v = value ? 0x80 >>> bitPos : 0;
+        int mask = ~(0x80 >>> bitPos);
+        bitmap[byteIndex] = (byte) ((bitmap[byteIndex] & mask) | v);
+    }
+
+    public int getCodePoint() {
+        return codePoint;
+    }
+
+    public int getWidth() {
+        return width;
+    }
+
+    public int getHeight() {
+        return height;
+    }
+
+    public int getBbox() {
+        return bbox;
+    }
+
+    public int getBboy() {
+        return bboy;
+    }
+
+    public int getDeviceWidth() {
+        return deviceWidth;
+    }
+
+    public byte[] getBitmap() {
+        return bitmap;
+    }
+}
Index: util/fonttool/src/org/gnu/grub/fonttool/PFF2Sections.java
===================================================================
--- util/fonttool/src/org/gnu/grub/fonttool/PFF2Sections.java   (revision 0)
+++ util/fonttool/src/org/gnu/grub/fonttool/PFF2Sections.java   (revision 0)
@@ -0,0 +1,36 @@
+/**
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  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/>.
+ */
+package org.gnu.grub.fonttool;
+
+/**
+ * Section name constants for the PFF2 file format.
+ */
+public class PFF2Sections {
+    static final String FILE = "FILE";
+    static final String FONT_NAME = "NAME";
+    static final String FONT_FAMILY = "FAMI";
+    static final String FONT_WEIGHT = "WEIG";
+    static final String FONT_SLANT = "SLAN";
+    static final String FONT_POINT_SIZE = "PTSZ";
+    static final String MAX_CHAR_WIDTH = "MAXW";
+    static final String MAX_CHAR_HEIGHT = "MAXH";
+    static final String FONT_ASCENT = "ASCE";
+    static final String FONT_DESCENT = "DESC";
+    static final String CHAR_INDEX = "CHIX";
+    static final String REMAINDER_IS_DATA = "DATA";
+}
Index: util/fonttool/src/org/gnu/grub/fonttool/PFF2Writer.java
===================================================================
--- util/fonttool/src/org/gnu/grub/fonttool/PFF2Writer.java     (revision 0)
+++ util/fonttool/src/org/gnu/grub/fonttool/PFF2Writer.java     (revision 0)
@@ -0,0 +1,154 @@
+/**
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008  Free Software Foundation, Inc.
+ *
+ *  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/>.
+ */
+package org.gnu.grub.fonttool;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import java.util.List;
+
+// TODO Add DEFLATE compressed blocks of characters.
+public class PFF2Writer {
+    private RandomAccessFile f;
+    private String currentSection;
+    private long currentSectionStart;
+
+    public PFF2Writer(String filename) throws FileNotFoundException {
+        this.f = new RandomAccessFile(filename, "rw");
+        this.currentSection = null;
+        this.currentSectionStart = -1;
+    }
+
+    public void writeFont(Font font, List<CharacterRange> rangeList) throws 
IOException {
+        // Clear existing file.
+       f.setLength(0);
+       
+        // Write file type ID header.
+        writeSection(PFF2Sections.FILE, "PFF2");
+
+        writeSection(PFF2Sections.FONT_NAME, font.getStandardName());
+        writeSection(PFF2Sections.FONT_FAMILY, font.getFamily());
+        writeSection(PFF2Sections.FONT_WEIGHT, font.isBold() ? "bold" : 
"normal");
+        writeSection(PFF2Sections.FONT_SLANT, font.isItalic() ? "italic" : 
"normal");
+        if (font.getPointSize() != Font.UNKNOWN_POINT_SIZE)
+            writeShortSection(PFF2Sections.FONT_POINT_SIZE, 
font.getPointSize());
+
+        // Construct character definitions.
+        CharDefs charDefs = new CharDefs(font);
+        charDefs.buildDefinitions(rangeList);
+
+        // Write max character width and height metrics.
+        writeShortSection(PFF2Sections.MAX_CHAR_WIDTH, 
charDefs.getMaxCharWidth());
+        writeShortSection(PFF2Sections.MAX_CHAR_HEIGHT, 
charDefs.getMaxCharHeight());
+        writeShortSection(PFF2Sections.FONT_ASCENT, font.getAscent());
+        writeShortSection(PFF2Sections.FONT_DESCENT, font.getDescent());
+
+        // Write character index with pointers to the character definitions.
+        beginSection(PFF2Sections.CHAR_INDEX);
+
+        // Determine the size of the index, so we can properly refer to the
+        // character definition offset in the index.  The actual number of
+        // bytes written is compared to the calculated value to ensure we
+        // are correct.
+        final int indexStart = (int) f.getFilePointer();
+        final int calculatedIndexLength =
+                charDefs.getIndexSize() * (4 + 1 + 4);
+        final int charDefStart = indexStart + calculatedIndexLength + 8;
+
+        for (CharStorageInfo storageInfo : charDefs.getCharIndex().values()) {
+            f.writeInt(storageInfo.getCodePoint());
+            f.writeByte(0);   // Storage flags: bits 1..0 = 00b : uncompressed.
+            f.writeInt(charDefStart + storageInfo.getFileOffset());
+        }
+
+        final int indexEnd = (int) f.getFilePointer();
+        if (indexEnd - indexStart != calculatedIndexLength) {
+            throw new RuntimeException("Incorrect index length calculated, 
calc="
+                                       + calculatedIndexLength
+                                       + " actual=" + (indexEnd - indexStart));
+        }
+        endSection(PFF2Sections.CHAR_INDEX);
+
+        f.writeBytes(PFF2Sections.REMAINDER_IS_DATA);
+        f.writeInt(-1);     // Data takes up the rest of the file.
+        f.write(charDefs.getDefinitionData());
+
+        f.close();
+    }
+
+    private void beginSection(String sectionName) throws IOException {
+        verifyOkToBeginSection(sectionName);
+
+        f.writeBytes(sectionName);
+        f.writeInt(-1);     // Placeholder for the section length.
+        currentSection = sectionName;
+        currentSectionStart = f.getFilePointer();
+    }
+
+    private void endSection(String sectionName) throws IOException {
+        verifyOkToEndSection(sectionName);
+
+        long sectionEnd = f.getFilePointer();
+        long sectionLength = sectionEnd - currentSectionStart;
+        f.seek(currentSectionStart - 4);
+        f.writeInt((int) sectionLength);
+        f.seek(sectionEnd);
+        currentSection = null;
+        currentSectionStart = -1;
+    }
+
+    private void verifyOkToBeginSection(String sectionName) {
+        if (sectionName.length() != 4)
+            throw new IllegalArgumentException(
+                    "Section names must be 4 characters: `" + sectionName + 
"'.");
+        if (currentSection != null)
+            throw new IllegalStateException(
+                    "Attempt to start `" + sectionName
+                    + "' section before ending the previous section `"
+                    + currentSection + "'.");
+    }
+
+    private void verifyOkToEndSection(String sectionName) {
+        if (sectionName.length() != 4)
+            throw new IllegalStateException("Invalid section name '" + 
sectionName
+                                            + "'; must be 4 characters.");
+        if (currentSection == null)
+            throw new IllegalStateException(
+                    "Attempt to end section `" + sectionName
+                    + "' when no section active.");
+        if (!sectionName.equals(currentSection))
+            throw new IllegalStateException(
+                    "Attempt to end `" + sectionName
+                    + "' section during active section `"
+                    + currentSection + "'.");
+    }
+
+    private void writeSection(String sectionName, String contents) throws 
IOException {
+        verifyOkToBeginSection(sectionName);
+        f.writeBytes(sectionName);
+        f.writeInt(contents.length());
+        f.writeBytes(contents);
+    }
+
+    private void writeShortSection(String sectionName, int value) throws 
IOException {
+        verifyOkToBeginSection(sectionName);
+        f.writeBytes(sectionName);
+        f.writeInt(2);
+        f.writeShort(value);
+    }
+}
Index: util/grub.d/00_header.in
===================================================================
--- util/grub.d/00_header.in    (revision 1934)
+++ util/grub.d/00_header.in    (working copy)
@@ -87,7 +87,7 @@
     fi
 
     cat << EOF
-if font `make_system_path_relative_to_its_root ${GRUB_FONT_PATH}` ; then
+if loadfont `make_system_path_relative_to_its_root ${GRUB_FONT_PATH}` ; then
   set gfxmode=${GRUB_GFXMODE}
   insmod gfxterm
   insmod ${video_backend}
Index: util/grub-mkconfig_lib.in
===================================================================
--- util/grub-mkconfig_lib.in   (revision 1934)
+++ util/grub-mkconfig_lib.in   (working copy)
@@ -154,7 +154,7 @@
     # FIXME: We prefer ascii because loading complete fonts is too slow (and
     # we don't yet provide the gettext magic that would make unicode useful).
     for basename in ascii unicode unifont ; do
-      path="${dir}/${basename}.pff"
+      path="${dir}/${basename}.pf2"
       if is_path_readable_by_grub ${path} > /dev/null ; then
         echo "${path}"
         return 0

reply via email to

[Prev in Thread] Current Thread [Next in Thread]