diff --git a/ChangeLog b/ChangeLog index 301346eb2..6fb3e626e 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,92 @@ +2009-01-02 Colin D Bennett + + New font engine. + + Additional changes by Vesa Jääskeläinen 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-28 Felix Zielcke * util/getroot.c (grub_util_get_grub_dev): Add support for diff --git a/DISTLIST b/DISTLIST index f86844285..22be10bce 100644 --- a/DISTLIST +++ b/DISTLIST @@ -103,7 +103,8 @@ docs/fdl.texi 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 diff --git a/Makefile.in b/Makefile.in index 5ce8be712..8cc0f1afc 100644 --- a/Makefile.in +++ b/Makefile.in @@ -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,2009 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 @@ AWK = @AWK@ LIBCURSES = @LIBCURSES@ LIBLZO = @LIBLZO@ YACC = @YACC@ -UNIFONT_HEX = @UNIFONT_HEX@ +UNIFONT_BDF = @UNIFONT_BDF@ # Options. enable_grub_emu = @enable_grub_emu@ @@ -148,19 +148,26 @@ fs.lst: $(FSFILES) 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 UNICODE_LINES=0x2501-0x251B -unicode.pff: $(UNIFONT_HEX) - ruby $(srcdir)/util/unifont2pff.rb $(UNIFONT_HEX) > $@ +# Note: fonttool should be replaced with C only implementation -ascii.pff: $(UNIFONT_HEX) - ruby $(srcdir)/util/unifont2pff.rb 0x0-0x7f $(UNICODE_ARROWS) $(UNICODE_LINES) $(UNIFONT_HEX) > $@ +$(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 . + +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 diff --git a/commands/videotest.c b/commands/videotest.c index 477727bbc..b0599df3b 100644 --- a/commands/videotest.c +++ b/commands/videotest.c @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2006,2007 Free Software Foundation, Inc. + * Copyright (C) 2006,2007,2009 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,17 +36,21 @@ grub_cmd_videotest (struct grub_arg_list *state __attribute__ ((unused)), 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; 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 @@ grub_cmd_videotest (struct grub_arg_list *state __attribute__ ((unused)), 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,18 +88,69 @@ grub_cmd_videotest (struct grub_arg_list *state __attribute__ ((unused)), 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); - grub_font_get_glyph ('*', &glyph); + 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); + + 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++) { 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); diff --git a/conf/common.mk b/conf/common.mk index 6c12bacdc..d54093198 100644 --- a/conf/common.mk +++ b/conf/common.mk @@ -2803,13 +2803,13 @@ help_mod_CFLAGS = $(COMMON_CFLAGS) help_mod_LDFLAGS = $(COMMON_LDFLAGS) # For font.mod. -font_mod_SOURCES = font/manager.c -CLEANFILES += font.mod mod-font.o mod-font.c pre-font.o font_mod-font_manager.o und-font.lst +font_mod_SOURCES = font/font_cmd.c font/font.c +CLEANFILES += font.mod mod-font.o mod-font.c pre-font.o font_mod-font_font_cmd.o font_mod-font_font.o und-font.lst ifneq ($(font_mod_EXPORTS),no) CLEANFILES += def-font.lst DEFSYMFILES += def-font.lst endif -MOSTLYCLEANFILES += font_mod-font_manager.d +MOSTLYCLEANFILES += font_mod-font_font_cmd.d font_mod-font_font.d UNDSYMFILES += und-font.lst font.mod: pre-font.o mod-font.o $(TARGET_OBJ2ELF) @@ -2818,9 +2818,9 @@ font.mod: pre-font.o mod-font.o $(TARGET_OBJ2ELF) if test ! -z $(TARGET_OBJ2ELF); then ./$(TARGET_OBJ2ELF) $@ || (rm -f $@; exit 1); fi $(STRIP) --strip-unneeded -K grub_mod_init -K grub_mod_fini -K _grub_mod_init -K _grub_mod_fini -R .note -R .comment $@ -pre-font.o: $(font_mod_DEPENDENCIES) font_mod-font_manager.o +pre-font.o: $(font_mod_DEPENDENCIES) font_mod-font_font_cmd.o font_mod-font_font.o -rm -f $@ - $(TARGET_CC) $(font_mod_LDFLAGS) $(TARGET_LDFLAGS) -Wl,-r,-d -o $@ font_mod-font_manager.o + $(TARGET_CC) $(font_mod_LDFLAGS) $(TARGET_LDFLAGS) -Wl,-r,-d -o $@ font_mod-font_font_cmd.o font_mod-font_font.o mod-font.o: mod-font.c $(TARGET_CC) $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(font_mod_CFLAGS) -c -o $@ $< @@ -2837,22 +2837,41 @@ und-font.lst: pre-font.o echo 'font' > $@ $(NM) -u -P -p $< | cut -f1 -d' ' >> $@ -font_mod-font_manager.o: font/manager.c $(font/manager.c_DEPENDENCIES) +font_mod-font_font_cmd.o: font/font_cmd.c $(font/font_cmd.c_DEPENDENCIES) $(TARGET_CC) -Ifont -I$(srcdir)/font $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(font_mod_CFLAGS) -MD -c -o $@ $< --include font_mod-font_manager.d +-include font_mod-font_font_cmd.d -CLEANFILES += cmd-font_mod-font_manager.lst fs-font_mod-font_manager.lst partmap-font_mod-font_manager.lst -COMMANDFILES += cmd-font_mod-font_manager.lst -FSFILES += fs-font_mod-font_manager.lst -PARTMAPFILES += partmap-font_mod-font_manager.lst +CLEANFILES += cmd-font_mod-font_font_cmd.lst fs-font_mod-font_font_cmd.lst partmap-font_mod-font_font_cmd.lst +COMMANDFILES += cmd-font_mod-font_font_cmd.lst +FSFILES += fs-font_mod-font_font_cmd.lst +PARTMAPFILES += partmap-font_mod-font_font_cmd.lst -cmd-font_mod-font_manager.lst: font/manager.c $(font/manager.c_DEPENDENCIES) gencmdlist.sh +cmd-font_mod-font_font_cmd.lst: font/font_cmd.c $(font/font_cmd.c_DEPENDENCIES) gencmdlist.sh set -e; $(TARGET_CC) -Ifont -I$(srcdir)/font $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(font_mod_CFLAGS) -E $< | sh $(srcdir)/gencmdlist.sh font > $@ || (rm -f $@; exit 1) -fs-font_mod-font_manager.lst: font/manager.c $(font/manager.c_DEPENDENCIES) genfslist.sh +fs-font_mod-font_font_cmd.lst: font/font_cmd.c $(font/font_cmd.c_DEPENDENCIES) genfslist.sh set -e; $(TARGET_CC) -Ifont -I$(srcdir)/font $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(font_mod_CFLAGS) -E $< | sh $(srcdir)/genfslist.sh font > $@ || (rm -f $@; exit 1) -partmap-font_mod-font_manager.lst: font/manager.c $(font/manager.c_DEPENDENCIES) genpartmaplist.sh +partmap-font_mod-font_font_cmd.lst: font/font_cmd.c $(font/font_cmd.c_DEPENDENCIES) genpartmaplist.sh + set -e; $(TARGET_CC) -Ifont -I$(srcdir)/font $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(font_mod_CFLAGS) -E $< | sh $(srcdir)/genpartmaplist.sh font > $@ || (rm -f $@; exit 1) + + +font_mod-font_font.o: font/font.c $(font/font.c_DEPENDENCIES) + $(TARGET_CC) -Ifont -I$(srcdir)/font $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(font_mod_CFLAGS) -MD -c -o $@ $< +-include font_mod-font_font.d + +CLEANFILES += cmd-font_mod-font_font.lst fs-font_mod-font_font.lst partmap-font_mod-font_font.lst +COMMANDFILES += cmd-font_mod-font_font.lst +FSFILES += fs-font_mod-font_font.lst +PARTMAPFILES += partmap-font_mod-font_font.lst + +cmd-font_mod-font_font.lst: font/font.c $(font/font.c_DEPENDENCIES) gencmdlist.sh + set -e; $(TARGET_CC) -Ifont -I$(srcdir)/font $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(font_mod_CFLAGS) -E $< | sh $(srcdir)/gencmdlist.sh font > $@ || (rm -f $@; exit 1) + +fs-font_mod-font_font.lst: font/font.c $(font/font.c_DEPENDENCIES) genfslist.sh + set -e; $(TARGET_CC) -Ifont -I$(srcdir)/font $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(font_mod_CFLAGS) -E $< | sh $(srcdir)/genfslist.sh font > $@ || (rm -f $@; exit 1) + +partmap-font_mod-font_font.lst: font/font.c $(font/font.c_DEPENDENCIES) genpartmaplist.sh set -e; $(TARGET_CC) -Ifont -I$(srcdir)/font $(TARGET_CPPFLAGS) $(TARGET_CFLAGS) $(font_mod_CFLAGS) -E $< | sh $(srcdir)/genpartmaplist.sh font > $@ || (rm -f $@; exit 1) diff --git a/conf/common.rmk b/conf/common.rmk index 251a21f8e..78a7dc55e 100644 --- a/conf/common.rmk +++ b/conf/common.rmk @@ -364,7 +364,7 @@ help_mod_CFLAGS = $(COMMON_CFLAGS) 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) diff --git a/configure b/configure index 6af424a27..f1639999c 100644 --- a/configure +++ b/configure @@ -667,7 +667,7 @@ target_os platform CMP YACC -UNIFONT_HEX +UNIFONT_BDF INSTALL_PROGRAM INSTALL_SCRIPT INSTALL_DATA @@ -2107,9 +2107,9 @@ echo "$as_me: error: bison is not found" >&2;} { (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 @@ target_os!$target_os$ac_delim 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 diff --git a/configure.ac b/configure.ac index 4aaf200ce..ad6429ff0 100644 --- a/configure.ac +++ b/configure.ac @@ -1,6 +1,6 @@ # Process this file with autoconf to produce a configure script. -# Copyright (C) 2002,2003,2004,2005,2006,2007,2008 Free Software Foundation, Inc. +# Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009 Free Software Foundation, Inc. # # This configure.ac is free software; the author # gives unlimited permission to copy and/or distribute it, @@ -127,9 +127,9 @@ if test "x$YACC" = x; then 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 diff --git a/font/font.c b/font/font.c new file mode 100644 index 000000000..a16267a82 --- /dev/null +++ b/font/font.c @@ -0,0 +1,1026 @@ +/* font.c - Font API and font file loader. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2003,2005,2006,2007,2008,2009 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 . + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 = ""; + 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, §ion) != 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, §ion) != 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 (§ion); + if (!font->name) + goto fail; + } + else if (grub_memcmp (section.name, section_names_point_size, 4) == 0) + { + if (read_section_as_short (§ion, &font->point_size) != 0) + goto fail; + } + else if (grub_memcmp (section.name, section_names_weight, 4) == 0) + { + char *wt; + wt = read_section_as_string (§ion); + 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 (§ion, &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 (§ion, &font->max_char_height) != 0) + goto fail; + } + else if (grub_memcmp (section.name, section_names_ascent, 4) == 0) + { + if (read_section_as_short (§ion, &font->ascent) != 0) + goto fail; + } + else if (grub_memcmp (section.name, section_names_descent, 4) == 0) + { + if (read_section_as_short (§ion, &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; +} + diff --git a/font/font_cmd.c b/font/font_cmd.c new file mode 100644 index 000000000..6cbae5b0a --- /dev/null +++ b/font/font_cmd.c @@ -0,0 +1,77 @@ +/* font_cmd.c - Font command definition. */ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2003,2005,2006,2007,2008,2009 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 . + */ + +#include +#include +#include +#include + +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"); +} + diff --git a/font/manager.c b/font/manager.c deleted file mode 100644 index 28f63ae75..000000000 --- a/font/manager.c +++ /dev/null @@ -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 . - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -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"); -} diff --git a/include/grub/font.h b/include/grub/font.h index 0841a151c..8a5f3ac7d 100644 --- a/include/grub/font.h +++ b/include/grub/font.h @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2003,2007 Free Software Foundation, Inc. + * Copyright (C) 2003,2007,2008,2009 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 +#include -#define GRUB_FONT_MAGIC "PPF\x7f" +/* 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; +}; + +/* 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); -int grub_font_get_glyph (grub_uint32_t code, - grub_font_glyph_t glyph); +/* 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); + +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 */ diff --git a/include/grub/misc.h b/include/grub/misc.h index 15c18f534..9c999487a 100644 --- a/include/grub/misc.h +++ b/include/grub/misc.h @@ -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,2009 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_uint8_t *EXPORT_FUNC(grub_utf16_to_utf8) (grub_uint8_t *dest, 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); diff --git a/include/grub/video.h b/include/grub/video.h index 13993267d..cb73dae21 100644 --- a/include/grub/video.h +++ b/include/grub/video.h @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2006,2007,2008 Free Software Foundation, Inc. + * Copyright (C) 2006,2007,2008,2009 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 @@ -31,17 +31,17 @@ typedef grub_uint32_t grub_video_color_t; 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 /* 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 @@ enum grub_video_blit_format 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 @@ struct grub_video_mode_info /* 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 @@ struct grub_video_adapter 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_unmap_color (grub_video_color_t color, 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, diff --git a/kern/misc.c b/kern/misc.c index 5b1e65e4a..6fbc6510a 100644 --- a/kern/misc.c +++ b/kern/misc.c @@ -1,7 +1,7 @@ /* misc.c - definitions of misc functions */ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008 Free Software Foundation, Inc. + * Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009 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 @@ -951,22 +951,29 @@ grub_utf16_to_utf8 (grub_uint8_t *dest, grub_uint16_t *src, 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; 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 @@ grub_utf8_to_ucs4 (grub_uint32_t *dest, const grub_uint8_t *src, } else { + if (c == 0) + break; + if ((c & 0x80) == 0x00) code = c; else if ((c & 0xe0) == 0xc0) @@ -1011,14 +1021,18 @@ grub_utf8_to_ucs4 (grub_uint32_t *dest, const grub_uint8_t *src, code = c & 0x01; } else - /* invalid */ return -1; } if (count == 0) - *p++ = code; + { + *p++ = code; + destsize--; + } } + if (srcend) + *srcend = src; return p - dest; } diff --git a/kern/term.c b/kern/term.c index 2acfdae72..8d5a23b41 100644 --- a/kern/term.c +++ b/kern/term.c @@ -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,2009 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_putchar (int c) 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) { diff --git a/normal/menu.c b/normal/menu.c index 103aa6f15..644446976 100644 --- a/normal/menu.c +++ b/normal/menu.c @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2003,2004,2005,2006,2007,2008 Free Software Foundation, Inc. + * Copyright (C) 2003,2004,2005,2006,2007,2008,2009 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 @@ -116,19 +116,21 @@ print_entry (int y, int highlight, grub_menu_entry_t entry) { int x; const char *title; + grub_size_t title_len; grub_ssize_t len; grub_uint32_t *unicode_title; grub_ssize_t i; 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; - 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. */ diff --git a/term/gfxterm.c b/term/gfxterm.c index c4377929c..3e7fcd826 100644 --- a/term/gfxterm.c +++ b/term/gfxterm.c @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2006,2007,2008 Free Software Foundation, Inc. + * Copyright (C) 2006,2007,2008,2009 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 @@ -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_colored_char 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 int dirty_region_is_empty (void); 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,25 +174,32 @@ grub_virtual_screen_free (void) 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 (); /* 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; 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; /* 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 @@ grub_virtual_screen_setup (unsigned int x, unsigned int y, 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 @@ grub_gfxterm_init (void) 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 @@ grub_gfxterm_init (void) /* 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,11 +676,12 @@ static void 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; unsigned int y; + int ascent; /* Find out active character. */ p = (virtual_screen.text_buffer @@ -671,27 +691,28 @@ write_char (void) 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; - 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. */ 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 @@ write_cursor (void) 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 @@ scroll_up (void) if (!bitmap) { /* Remove cursor. */ - write_char (); + draw_cursor (0); /* Redraw only changed regions. */ dirty_region_redraw (); @@ -755,7 +786,7 @@ scroll_up (void) /* 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 @@ scroll_up (void) 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 @@ grub_gfxterm_putchar (grub_uint32_t c) { /* Erase current cursor, if any. */ if (virtual_screen.cursor_state) - write_char (); + draw_cursor (0); switch (c) { @@ -814,18 +845,27 @@ grub_gfxterm_putchar (grub_uint32_t c) /* 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; - /* Get properties of the character. */ - grub_font_get_glyph (c, &glyph); + /* Erase current cursor, if any. */ + if (virtual_screen.cursor_state) + draw_cursor (0); + + /* 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 @@ grub_gfxterm_putchar (grub_uint32_t c) 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 @@ grub_gfxterm_putchar (grub_uint32_t c) 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 @@ grub_gfxterm_putchar (grub_uint32_t c) /* 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,14 +983,16 @@ grub_gfxterm_gotoxy (grub_uint8_t x, grub_uint8_t y) 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; + /* Draw cursor if visible. */ if (virtual_screen.cursor_state) - write_cursor (); + draw_cursor (1); } static void @@ -995,9 +1077,9 @@ grub_gfxterm_setcursor (int on) 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; } diff --git a/term/i386/pc/vesafb.c b/term/i386/pc/vesafb.c index c5a46731d..730595433 100644 --- a/term/i386/pc/vesafb.c +++ b/term/i386/pc/vesafb.c @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2005,2007 Free Software Foundation, Inc. + * Copyright (C) 2005,2007,2009 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 . */ +// TODO: Deprecated and broken. Scheduled for removal as there is VBE driver in Video subsystem. + #include #include #include @@ -250,10 +252,11 @@ grub_virtual_screen_get_glyph (grub_uint32_t code, 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, diff --git a/term/i386/pc/vga.c b/term/i386/pc/vga.c index 7143745cc..d32c86e6b 100644 --- a/term/i386/pc/vga.c +++ b/term/i386/pc/vga.c @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2000,2001,2002,2003,2004,2005,2007,2008 Free Software Foundation, Inc. + * Copyright (C) 2000,2001,2002,2003,2004,2005,2007,2008,2009 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 . */ +// TODO: Deprecated and broken. Needs to be converted to Video Driver! + #include #include #include @@ -65,6 +67,7 @@ static unsigned char fg_color, bg_color; 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 @@ grub_vga_mod_init (void) 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 @@ static void 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 @@ write_char (void) 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,19 +214,23 @@ write_char (void) 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)) mem[i] = (fg_mask | bg_mask); } +#endif /* 0 */ } } @@ -320,36 +330,37 @@ grub_vga_putchar (grub_uint32_t c) } 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; 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; } } write_char (); - xpos += glyph.char_width; + xpos += char_width; if (xpos >= TEXT_WIDTH) { xpos = 0; @@ -381,11 +392,16 @@ grub_vga_putchar (grub_uint32_t c) 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 diff --git a/util/fonttool/src/org/gnu/grub/fonttool/BDFLoader.java b/util/fonttool/src/org/gnu/grub/fonttool/BDFLoader.java new file mode 100644 index 000000000..1cb53a805 --- /dev/null +++ b/util/fonttool/src/org/gnu/grub/fonttool/BDFLoader.java @@ -0,0 +1,271 @@ +/** + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009 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 . + */ +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; + } +} diff --git a/util/fonttool/src/org/gnu/grub/fonttool/CharDefs.java b/util/fonttool/src/org/gnu/grub/fonttool/CharDefs.java new file mode 100644 index 000000000..fa2fd9b6e --- /dev/null +++ b/util/fonttool/src/org/gnu/grub/fonttool/CharDefs.java @@ -0,0 +1,181 @@ +/** + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009 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 . + */ +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 glyphs; + private ByteArrayOutputStream charDefsData; + private int maxCharWidth; + private int maxCharHeight; + private HashMap 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 rangeList) { + charIndex = new HashMap(); + HashMap charDefIndex = new HashMap(); + 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 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 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(); + } +} diff --git a/util/fonttool/src/org/gnu/grub/fonttool/CharStorageInfo.java b/util/fonttool/src/org/gnu/grub/fonttool/CharStorageInfo.java new file mode 100644 index 000000000..3d2ba554e --- /dev/null +++ b/util/fonttool/src/org/gnu/grub/fonttool/CharStorageInfo.java @@ -0,0 +1,36 @@ +/** + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009 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 . + */ +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; + } +} diff --git a/util/fonttool/src/org/gnu/grub/fonttool/CharacterRange.java b/util/fonttool/src/org/gnu/grub/fonttool/CharacterRange.java new file mode 100644 index 000000000..378fdedea --- /dev/null +++ b/util/fonttool/src/org/gnu/grub/fonttool/CharacterRange.java @@ -0,0 +1,75 @@ +/** + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009 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 . + */ +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; + } +} diff --git a/util/fonttool/src/org/gnu/grub/fonttool/Converter.java b/util/fonttool/src/org/gnu/grub/fonttool/Converter.java new file mode 100644 index 000000000..c84fc7104 --- /dev/null +++ b/util/fonttool/src/org/gnu/grub/fonttool/Converter.java @@ -0,0 +1,168 @@ +/** + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009 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 . + */ +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 rangeList = new ArrayList(); + + 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 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); + } +} diff --git a/util/fonttool/src/org/gnu/grub/fonttool/Font.java b/util/fonttool/src/org/gnu/grub/fonttool/Font.java new file mode 100644 index 000000000..77df4b25c --- /dev/null +++ b/util/fonttool/src/org/gnu/grub/fonttool/Font.java @@ -0,0 +1,123 @@ +/** + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009 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 . + */ +package org.gnu.grub.fonttool; + +import java.util.TreeMap; + +public class Font { + public static final int UNKNOWN_POINT_SIZE = -1; + + private TreeMap 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(); + } + + 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 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(); + } +} diff --git a/util/fonttool/src/org/gnu/grub/fonttool/Glyph.java b/util/fonttool/src/org/gnu/grub/fonttool/Glyph.java new file mode 100644 index 000000000..c1bc5e576 --- /dev/null +++ b/util/fonttool/src/org/gnu/grub/fonttool/Glyph.java @@ -0,0 +1,100 @@ +/** + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009 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 . + */ +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; + } +} diff --git a/util/fonttool/src/org/gnu/grub/fonttool/PFF2Sections.java b/util/fonttool/src/org/gnu/grub/fonttool/PFF2Sections.java new file mode 100644 index 000000000..61e8d8892 --- /dev/null +++ b/util/fonttool/src/org/gnu/grub/fonttool/PFF2Sections.java @@ -0,0 +1,36 @@ +/** + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009 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 . + */ +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"; +} diff --git a/util/fonttool/src/org/gnu/grub/fonttool/PFF2Writer.java b/util/fonttool/src/org/gnu/grub/fonttool/PFF2Writer.java new file mode 100644 index 000000000..d38ed2724 --- /dev/null +++ b/util/fonttool/src/org/gnu/grub/fonttool/PFF2Writer.java @@ -0,0 +1,154 @@ +/** + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009 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 . + */ +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 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); + } +} diff --git a/util/grub-mkconfig_lib.in b/util/grub-mkconfig_lib.in index fb842f2d4..0c5c45621 100644 --- a/util/grub-mkconfig_lib.in +++ b/util/grub-mkconfig_lib.in @@ -1,5 +1,5 @@ # Helper library for grub-mkconfig -# Copyright (C) 2007,2008 Free Software Foundation, Inc. +# Copyright (C) 2007,2008,2009 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 @@ -154,7 +154,7 @@ font_path () # 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 diff --git a/util/grub.d/00_header.in b/util/grub.d/00_header.in index 3d434d765..93b90675b 100644 --- a/util/grub.d/00_header.in +++ b/util/grub.d/00_header.in @@ -1,7 +1,7 @@ #! /bin/sh -e # update-grub helper script. -# Copyright (C) 2006,2007,2008 Free Software Foundation, Inc. +# Copyright (C) 2006,2007,2008,2009 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 @@ -87,7 +87,7 @@ case x${GRUB_TERMINAL_OUTPUT} in 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} diff --git a/video/i386/pc/vbe.c b/video/i386/pc/vbe.c index 41660ac06..7f1cb9d90 100644 --- a/video/i386/pc/vbe.c +++ b/video/i386/pc/vbe.c @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2005,2006,2007,2008 Free Software Foundation, Inc. + * Copyright (C) 2005,2006,2007,2008,2009 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 @@ -26,7 +26,6 @@ #include #include #include -#include #include #include #include @@ -710,6 +709,16 @@ grub_video_vbe_map_rgb (grub_uint8_t red, grub_uint8_t green, 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 @@ grub_video_vbe_map_rgba (grub_uint8_t red, grub_uint8_t green, /* 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 @@ grub_video_vbe_unmap_color_int (struct grub_video_i386_vbeblit_info * source, *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 @@ grub_video_vbe_fill_rect (grub_video_color_t color, int x, int y, 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 @@ static struct grub_video_adapter grub_video_vbe_adapter = .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, diff --git a/video/i386/pc/vbeutil.c b/video/i386/pc/vbeutil.c index 4556928ef..1040dc934 100644 --- a/video/i386/pc/vbeutil.c +++ b/video/i386/pc/vbeutil.c @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2006,2007 Free Software Foundation, Inc. + * Copyright (C) 2006,2007,2009 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 @@ get_data_ptr (struct grub_video_i386_vbeblit_info *source, + 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 @@ get_pixel (struct grub_video_i386_vbeblit_info *source, 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 @@ set_pixel (struct grub_video_i386_vbeblit_info *source, } 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; } diff --git a/video/video.c b/video/video.c index 1caf8861b..49e6cb95e 100644 --- a/video/video.c +++ b/video/video.c @@ -1,6 +1,6 @@ /* * GRUB -- GRand Unified Bootloader - * Copyright (C) 2006,2007,2008 Free Software Foundation, Inc. + * Copyright (C) 2006,2007,2008,2009 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 @@ -336,17 +336,6 @@ grub_video_fill_rect (grub_video_color_t color, int x, int y, 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,