/*
 * Copyright (c) 1997-2006  Motoyuki Kasahara
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the project nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE PROJECT AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE PROJECT OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <limits.h>
#include <unistd.h>
#include <fcntl.h>

#ifdef ENABLE_NLS
#ifdef HAVE_LOCALE_H
#include <locale.h>
#endif
#include <libintl.h>
#endif

#include "eb/eb.h"
#include "eb/error.h"
#include "eb/font.h"

#include "getopt.h"
#include "getumask.h"
#include "makedir.h"
#include "ebutils.h"

#ifndef HAVE_STRCASECMP
int strcasecmp(const char *, const char *);
int strncasecmp(const char *, const char *, size_t);
#endif

#ifndef O_BINARY
#define O_BINARY 0
#endif

/*
 * The maximum length of path name.
 */
#ifndef PATH_MAX
#ifdef MAXPATHLEN
#define PATH_MAX        MAXPATHLEN
#else /* not MAXPATHLEN */
#define PATH_MAX        1024
#endif /* not MAXPATHLEN */
#endif /* not PATH_MAX */

/*
 * Tricks for gettext.
 */
#ifdef ENABLE_NLS
#define _(string) gettext(string)
#ifdef gettext_noop
#define N_(string) gettext_noop(string)
#else
#define N_(string) (string)
#endif
#else
#define _(string) (string)
#define N_(string) (string)
#endif

/*
 * Trick for difference of path notation between UNIX and DOS.
 */
#ifndef DOS_FILE_PATH
#define F_(path1, path2) (path1)
#else
#define F_(path1, path2) (path2)
#endif

/*
 * Command line options.
 */
static const char *short_options = "df:hi:o:S:v";
static struct option long_options[] = {
  {"debug",             no_argument,       NULL, 'd'},
  {"verbose",           no_argument,       NULL, 'd'},
  {"font-height",       required_argument, NULL, 'f'},
  {"help",              no_argument,       NULL, 'h'},
  {"image-format",      required_argument, NULL, 'i'},
  {"subbook",           required_argument, NULL, 'S'},
  {"output-directory",  required_argument, NULL, 'o'},
  {"version",           no_argument,       NULL, 'v'},
  {NULL, 0, NULL, 0}
};

/*
 * Supported image formats.
 */
typedef int Image_Format_Code;

typedef struct {
    const char *name;
    const char *suffix;
    EB_Error_Code (*function)(const char *bitmap_data, int width, int height,
	char *image_data, size_t *image_size);
} Image_Format;

static Image_Format image_formats[] = {
    {"xbm", "xbm", eb_bitmap_to_xbm},
    {"xpm", "xpm", eb_bitmap_to_xpm},
    {"gif", "gif", eb_bitmap_to_gif},
    {"bmp", "bmp", eb_bitmap_to_bmp},
    {"png", "png", eb_bitmap_to_png},
    {NULL, NULL, NULL}
};

#define MAX_IMAGE_FORMATS	5
#define MAX_LENGTH_IMAGE_NAME	3
#define MAX_LENGTH_IMAGE_SUFFIX	3

/*
 * Program name and version.
 */
static const char *program_name = "ebfont";
static const char *program_version = VERSION;
static const char *invoked_name;

/*
 * Debug flag.
 */
static int debug_flag;

/*
 * List of target subbook names.
 */
char subbook_name_list[EB_MAX_SUBBOOKS][EB_MAX_DIRECTORY_NAME_LENGTH + 1];
int subbook_name_count = 0;

/*
 * List of target subbook codes.
 */
static EB_Subbook_Code subbook_list[EB_MAX_SUBBOOKS];
static int subbook_count = 0;

/*
 * List of target font heights.
 */
static EB_Font_Code font_list[EB_MAX_FONTS];
static int font_count = 0;

/*
 * Target Image formats.
 */
static Image_Format_Code image_list[MAX_IMAGE_FORMATS];
static int image_count = 0;

/*
 * Defaults and limitations.
 */
#define DEFAULT_FONT_HEIGHT		"16"
#define DEFAULT_IMAGE_FORMAT		"xbm"
#define DEFAULT_BOOK_DIRECTORY		"."
#define DEFAULT_OUTPUT_DIRECTORY	"."

#define MAX_LENGTH_FONT_NAME		2
#define MAX_LENGTH_STRING		255

/*
 * Unexported functions.
 */
static int parse_font_argument(const char *argument, EB_Font_Code *font_list,
    int *font_count);
static int parse_image_argument(const char *argument,
    Image_Format_Code *image_list, int *image_count);
static void output_help(void);
static int make_book_fonts(EB_Book *book, const char *out_path,
    EB_Subbook_Code *subbook_list, int subbook_count, EB_Font_Code *font_list,
    int font_count, Image_Format_Code *image_list, int image_count);
static int make_subbook_fonts(EB_Book *book, const char *subbook_path,
    EB_Font_Code *font_list, int font_count, Image_Format_Code *image_list,
    int image_count);
static int make_subbook_size_fonts(EB_Book *book, const char *font_path,
    Image_Format_Code *image_list, int image_count);
static int make_subbook_size_image_fonts(EB_Book *book, const char *image_path,
    Image_Format_Code image);
static int save_image_file(const char *file_name, const char *image_data,
    size_t image_size);


int
main(int argc, char *argv[])
{
    const char *book_path;
    char out_path[PATH_MAX + 1];
    EB_Error_Code error_code;
    EB_Book book;
    int ch;

    invoked_name = argv[0];
    debug_flag = 0;
    strcpy(out_path, DEFAULT_OUTPUT_DIRECTORY);

    /*
     * Initialize locale data.
     */
#ifdef ENABLE_NLS
#ifdef HAVE_SETLOCALE
       setlocale(LC_ALL, "");
#endif
       bindtextdomain(TEXT_DOMAIN_NAME, LOCALEDIR);
       textdomain(TEXT_DOMAIN_NAME);
#endif

    /*
     * Initialize `book'.
     */
    error_code = eb_initialize_library();
    if (error_code != EB_SUCCESS) {
	fprintf(stderr, "%s: %s\n", invoked_name,
	    eb_error_message(error_code));
	goto die;
    }
    eb_initialize_book(&book);

    /*
     * Parse command line options.
     */
    for (;;) {
	ch = getopt_long(argc, argv, short_options, long_options, NULL);
	if (ch == -1)
	    break;
	switch (ch) {
	case 'd':
	    /*
	     * Option `-d'.  Debug mode.
	     */
	    debug_flag = 1;
	    break;

	case 'f':
	    /*
	     * Option `-f'.  Generate fonts with HEIGHT.
	     */
	    if (parse_font_argument(optarg, font_list, &font_count) < 0) {
		output_try_help(invoked_name);
		goto die;
	    }
	    break;

	case 'h':
	    /*
	     * Option `-h'.  Display help message, then exit.
	     */
	    output_help();
	    exit(0);

	case 'i':
	    /*
	     * Option `-i'.  Generate fonts as FORMAT.
	     */
	    if (parse_image_argument(optarg, image_list, &image_count) < 0) {
		output_try_help(invoked_name);
		goto die;
	    }
	    break;

	case 'o':
	    /*
	     * Option `-o'.  Output fonts under DIRECTORY.
	     * The file names of fonts are:
	     *     "<path>/<subbook>/fonts/<height>/{narrow,wide}/
	     *         <charno>.<suffix>"
	     *
	     * <path>:    is `optarg'.
	     * <subbook>: require EB_MAX_DIRECTORY_NAME_LENGTH characters.
	     * fonts:     require 5 characters.
	     * <height>:  require 2 characters ("16", "24", "32", or "48").
	     * {narrow,wide}:
	     *            requre 6 characters (= strlen("narrow")).
	     * <charno>:  requre 4 characters. ("0000" ... "ffff")
	     * <suffix>:  requre MAX_LENGTH_IMAGE_SUFFIX characters.
	     */
	    if (PATH_MAX < strlen(optarg)) {
		fprintf(stderr, _("%s: too long output directory path\n"),
		    invoked_name);
		goto die;
	    }
	    strcpy(out_path, optarg);
	    canonicalize_path(out_path);
	    if (PATH_MAX < strlen(out_path) + 1 + EB_MAX_DIRECTORY_NAME_LENGTH
		+ 1 + 5 + 1 + 2 + 1 + 6 + 1 + 4 + 1
		+ MAX_LENGTH_IMAGE_SUFFIX) {
		fprintf(stderr, _("%s: too long output directory path\n"),
		    invoked_name);
		goto die;
	    }
	    break;

        case 'S':
            /*
             * Option `-S'.  Specify target subbooks.
             */
            if (parse_subbook_name_argument(invoked_name, optarg,
		subbook_name_list, &subbook_name_count) < 0) {
		output_try_help(invoked_name);
		goto die;
	    }
            break;

	case 'v':
	    /*
	     * Option `-v'.  Display version number, then exit.
	     */
	    output_version(program_name, program_version);
	    exit(0);

	default:
	    output_try_help(invoked_name);
	    goto die;
	}
    }

    /*
     * Check the number of rest arguments.
     */
    if (1 < argc - optind) {
	fprintf(stderr, _("%s: too many arguments\n"), invoked_name);
	output_try_help(invoked_name);
	goto die;
    }

    if (image_count == 0)
	parse_image_argument(DEFAULT_IMAGE_FORMAT, image_list, &image_count);

    if (font_count == 0)
	parse_font_argument(DEFAULT_FONT_HEIGHT, font_list, &font_count);

    /*
     * Set a book path.
     */
    if (argc == optind)
	book_path = DEFAULT_BOOK_DIRECTORY;
    else
	book_path = argv[optind];

    /*
     * Bind a book.
     */
    error_code = eb_bind(&book, book_path);
    if (error_code != EB_SUCCESS) {
	fprintf(stderr, "%s: %s\n", invoked_name,
	    eb_error_message(error_code));
	goto die;
    }

    /*
     * For each targe subbook, convert a subbook-names to a subbook-codes.
     * If no subbook is specified by `--subbook'(`-S'), set all subbooks
     * as the target.
     */
    if (subbook_name_count == 0) {
	error_code = eb_subbook_list(&book, subbook_list, &subbook_count);
	if (error_code != EB_SUCCESS) {
	    fprintf(stderr, "%s: %s\n", invoked_name,
		eb_error_message(error_code));
	    goto die;
	}
    } else {
	int i;
	EB_Subbook_Code sub;

	for (i = 0; i < subbook_name_count; i++) {
	    error_code = find_subbook(&book, subbook_name_list[i], &sub);
	    if (error_code != EB_SUCCESS) {
		fprintf(stderr, _("%s: unknown subbook name `%s'\n"),
		    invoked_name, subbook_name_list[i]);
	    }
	    subbook_list[subbook_count++] = sub;
	}
    }

    /*
     * Make image files for fonts in the book.
     */
    if (make_book_fonts(&book, out_path, subbook_list, subbook_count,
	font_list, font_count, image_list, image_count) < 0)
	goto die;

    /*
     * Dispose the book.
     */
    eb_finalize_book(&book);
    eb_finalize_library();

    return 0;

    /*
     * A critical error occurs...
     */
  die:
    fflush(stderr);
    eb_finalize_book(&book);
    eb_finalize_library();
    exit(1);
}


/*
 * Parse an argument to option `--font-height (-f)'
 */
static int
parse_font_argument(const char *argument, EB_Font_Code *font_list,
    int *font_count)
{
    const char *argument_p = argument;
    char font_name[MAX_LENGTH_FONT_NAME + 1];
    char *font_name_p;
    EB_Font_Code font_code;
    int i;

    while (*argument_p != '\0') {
	/*
	 * Take a next element in the argument.
	 */
	i = 0;
	font_name_p = font_name;
	while (*argument_p != ',' && *argument_p != '\0'
	    && i < MAX_LENGTH_FONT_NAME) {
	    *font_name_p = *argument_p;
	    i++;
	    font_name_p++;
	    argument_p++;
	}
	*font_name_p = '\0';
	if (*argument_p == ',')
	    argument_p++;
	else if (*argument_p != '\0') {
	    fprintf(stderr, _("%s: unknown font height `%s...'\n"),
		invoked_name, font_name);
	    goto failed;
	}

	/*
	 * Check for the font name.
	 */
	if (strcmp(font_name, "16") == 0)
	    font_code = EB_FONT_16;
	else if (strcmp(font_name, "24") == 0)
	    font_code = EB_FONT_24;
	else if (strcmp(font_name, "30") == 0)
	    font_code = EB_FONT_30;
	else if (strcmp(font_name, "48") == 0)
	    font_code = EB_FONT_48;
	else {
	    fprintf(stderr, _("%s: unknown font height `%s'\n"),
		invoked_name, font_name);
	    fflush(stderr);
	    goto failed;
	}

	/*
	 * If the font name is not found in `font_list', it is added to
	 * the list.
	 */
	for (i = 0; i < *font_count; i++) {
	    if (*(font_list + i) == font_code)
		break;
	}
	if (*font_count <= i) {
	    *(font_list + i) = font_code;
	    (*font_count)++;
	}
    }

    return 0;

    /*
     * An error occurs...
     */
  failed:
    fflush(stderr);
    return -1;
}


/*
 * Parse an argument to option `--font-image-format (-i)'
 */
static int
parse_image_argument(const char *argument, Image_Format_Code *image_list,
    int *image_count)
{
    const char *argument_p = argument;
    char image_name[MAX_LENGTH_IMAGE_NAME + 1];
    char *image_name_p;
    int image_code;
    int i;

    while (*argument_p != '\0') {
	/*
	 * Take a next element in the argument.
	 */
	i = 0;
	image_name_p = image_name;
	while (*argument_p != ',' && *argument_p != '\0'
	    && i < MAX_LENGTH_IMAGE_NAME) {
	    *image_name_p = *argument_p;
	    i++;
	    image_name_p++;
	    argument_p++;
	}
	*image_name_p = '\0';
	if (*argument_p == ',')
	    argument_p++;
	else if (*argument_p != '\0') {
	    fprintf(stderr, _("%s: unknown image format name `%s...'\n"),
		invoked_name, image_name);
	    goto failed;
	}

	/*
	 * Check for the image-format name.
	 */
	for (i = 0; i < MAX_IMAGE_FORMATS; i++) {
	    if (strcasecmp((image_formats + i)->name, image_name) == 0)
		break;
	}
	if (MAX_IMAGE_FORMATS <= i) {
	    fprintf(stderr, _("%s: unknown image format name `%s'\n"),
		invoked_name, image_name);
	    goto failed;
	}
	image_code = i;

	/*
	 * If the image-format name is not found in `image_list', it
	 * is added to the list.
	 */
	for (i = 0; i < *image_count; i++) {
	    if (*(image_list + i) == image_code)
		break;
	}
	if (*image_count <= i) {
	    *(image_list + i) = image_code;
	    (*image_count)++;
	}
    }

    return 0;

    /*
     * An error occurs...
     */
  failed:
    fflush(stderr);
    return -1;
}


/*
 * Output help message to standard out, then exit.
 */
static void
output_help(void)
{
    printf(_("Usage: %s [option...] [book-directory]\n"), program_name);
    printf(_("Options:\n"));
    printf(_("  -d  --debug  --verbose     degug mode\n"));
    printf(_("  -f HEIGHT[,HEIGHT...]  --font-height HEIGHT[,HEIGHT...]\n"));
    printf(_("                             generate fonts with HEIGHT; 16, 24, 30 or 48\n"));
    printf(_("                             (default: %s)\n"),
	DEFAULT_FONT_HEIGHT);
    printf(_("  -h  --help                 display this help, then exit\n"));
    printf(_("  -i FORMAT[,FORMAT...]  --image-format FORMAT[,FORMAT...]\n"));
    printf(_("                             generate fonts as FORMAT;\n"));
    printf(_("                             xbm, xpm, gif, bmp or png\n"));
    printf(_("                             (default: %s)\n"),
	DEFAULT_IMAGE_FORMAT);
    printf(_("  -o DIRECTORY  --output-directory DIRECTORY\n"));
    printf(_("                             output fonts under DIRECTORY\n"));
    printf(_("                             (default: %s)\n"),
	DEFAULT_OUTPUT_DIRECTORY);
    printf(_("  -S SUBBOOK[,SUBBOOK...]  --subbook SUBBOOK[,SUBBOOK...]\n"));
    printf(_("                             target subbook\n"));
    printf(_("                             (default: all subbooks)\n"));
    printf(_("  -v  --version              display version number, then exit\n"));
    printf(_("\nArgument:\n"));
    printf(_("  book-directory             top directory of a CD-ROM book\n"));
    printf(_("                             (default: %s)\n"),
	DEFAULT_BOOK_DIRECTORY);
    printf(_("\nReport bugs to %s.\n"), MAILING_ADDRESS);
    fflush(stdout);
}


/*
 * Make font-files in the `book_path'.
 */
static int
make_book_fonts(EB_Book *book, const char *out_path,
    EB_Subbook_Code *subbook_list, int subbook_count, EB_Font_Code *font_list,
    int font_count, Image_Format_Code *image_list, int image_count)
{
    EB_Error_Code error_code;
    char subbook_path[PATH_MAX + 1];
    char subbook_directory[EB_MAX_DIRECTORY_NAME_LENGTH + 1];
    int i;

    /*
     * If `book_path' represents "/", replace it to an empty string,
     * or `file_name' starts with double slashes.
     */
    if (strcmp(out_path, "/") == 0)
	out_path++;

    for (i = 0; i < subbook_count; i++) {
	/*
	 * Set the current subbook to `subbook_list[i]'.
	 */
	error_code = eb_set_subbook(book, subbook_list[i]);
	if (error_code != EB_SUCCESS) {
	    fprintf(stderr, "%s: %s: subbook=%d\n", invoked_name,
		eb_error_message(error_code), subbook_list[i]);
	    goto failed;
	}

	/*
	 * Get directory name of the subbook.
	 */
	error_code = eb_subbook_directory(book, subbook_directory);
	if (error_code != EB_SUCCESS) {
	    fprintf(stderr, "%s: %s: subbook=%d\n", invoked_name,
		eb_error_message(error_code), subbook_list[i]);
	    goto failed;
	}

	/*
	 * Output debug information.
	 */
	if (debug_flag) {
	    fprintf(stderr, "%s: debug: subbook %s\n", invoked_name,
		subbook_directory);
	}

	/*
	 * Make a directory for the subbook.
	 */
	sprintf(subbook_path, F_("%s/%s", "%s\\%s"),
	    out_path, subbook_directory);
	if (make_missing_directory(subbook_path, 0777 ^ get_umask()) < 0)
	    goto failed;

	/*
	 * Make fonts in the subbook.
	 */
	if (make_subbook_fonts(book, subbook_path, font_list, font_count,
	    image_list, image_count) < 0)
	    goto failed;
    }

    return 0;

    /*
     * An error occurs...
     */
  failed:
    fflush(stderr);
    return -1;
}


/*
 * Make font-files in the current subbook.
 */
static int
make_subbook_fonts(EB_Book *book, const char *subbook_path,
    EB_Font_Code *font_list, int font_count, Image_Format_Code *image_list,
    int image_count)
{
    EB_Error_Code error_code;
    char subbook_directory[EB_MAX_DIRECTORY_NAME_LENGTH + 1];
    char font_path[PATH_MAX + 1];
    int font_height;
    int i;

    /*
     * Get the current subbook name.
     */
    error_code = eb_subbook_directory(book, subbook_directory);
    if (error_code != EB_SUCCESS) {
	fprintf(stderr, "%s: %s\n", invoked_name,
	    eb_error_message(error_code));
	goto failed;
    }

    if (debug_flag) {
	fprintf(stderr, "%s: debug: subbook %s:\n", invoked_name,
	    subbook_directory);
    }

    for (i = 0; i < font_count; i++) {
	/*
	 * Set the current font to `font_list[i]'.
	 */
	if (!eb_have_font(book, font_list[i]))
	    continue;
	error_code = eb_set_font(book, font_list[i]);
	if (error_code != EB_SUCCESS) {
	    fprintf(stderr, "%s: %s: subbook=%s, font=%d\n", invoked_name,
		eb_error_message(error_code), subbook_directory, i);
	    goto failed;
	}

	/*
	 * Output debug information.
	 */
	if (debug_flag) {
	    fprintf(stderr, "%s: debug: font %d\n", invoked_name,
		font_list[i]);
	}

	/*
	 * Make a directory for the font.
	 */
	eb_font_height2(font_list[i], &font_height);
	sprintf(font_path, F_("%s/%d", "%s\\%d"), subbook_path, font_height);
	if (make_missing_directory(font_path, 0777 ^ get_umask()) < 0)
	    goto failed;

	/*
	 * Make font-files with the size.
	 */
	if (make_subbook_size_fonts(book, font_path, image_list, image_count)
	    < 0)
	    goto failed;
    }

    return 0;

    /*
     * An error occurs...
     */
  failed:
    fflush(stderr);
    return -1;
}


/*
 * Make font-files of the current font.
 */
static int
make_subbook_size_fonts(EB_Book *book, const char *font_path,
    Image_Format_Code *image_list, int image_count)
{
    EB_Error_Code error_code;
    char subbook_directory[EB_MAX_DIRECTORY_NAME_LENGTH + 1];
    int i;

    /*
     * Get the current subbook name.
     */
    error_code = eb_subbook_directory(book, subbook_directory);
    if (subbook_directory == NULL) {
	fprintf(stderr, "%s: %s\n", invoked_name,
	    eb_error_message(error_code));
	goto failed;
    }

    for (i = 0; i < image_count; i++) {
	/*
	 * Output debug information.
	 */
	if (debug_flag) {
	    fprintf(stderr, "%s: debug: image %s\n", invoked_name,
		image_formats[image_list[i]].name);
	}

	/*
	 * Make font-files as the image format.
	 */
	if (make_subbook_size_image_fonts(book, font_path, image_list[i]) < 0)
	    goto failed;
    }

    return 0;

    /*
     * An error occurs...
     */
  failed:
    fflush(stderr);
    return -1;
}


/*
 * Make font-files of the current font as the image format.
 */
static int
make_subbook_size_image_fonts(EB_Book *book, const char *image_path,
    Image_Format_Code image)
{
    EB_Error_Code error_code;
    char subbook_directory[EB_MAX_DIRECTORY_NAME_LENGTH + 1];
    char type_path[PATH_MAX + 1];
    char file_name[PATH_MAX + 1];
    char bitmap_data[EB_SIZE_WIDE_FONT_48];
    char image_data[EB_SIZE_FONT_IMAGE];
    size_t image_size;
    int image_width;
    int image_height;
    int character_number;

    /*
     * Get the current subbook name.
     */
    error_code = eb_subbook_directory(book, subbook_directory);
    if (error_code != EB_SUCCESS) {
	fprintf(stderr, "%s: %s\n", invoked_name,
	    eb_error_message(error_code));
	goto failed;
    }

    /*
     * Get the current font size.
     */
    error_code = eb_font_height(book, &image_height);
    if (error_code != EB_SUCCESS) {
	fprintf(stderr, "%s: %s: subbook=%s\n", invoked_name,
	    eb_error_message(error_code), subbook_directory);
	goto failed;
    }

    /*
     * Make the bitmap files of narrow fonts.
     */
    if (eb_have_narrow_font(book)) {
	/*
	 * Get narrow font information.
	 */
	error_code = eb_narrow_font_width(book, &image_width);
	if (error_code != EB_SUCCESS) {
	    fprintf(stderr, "%s: %s: subbook=%s, font=%d, type=narrow\n",
		invoked_name, eb_error_message(error_code), subbook_directory,
		image_height);
	    goto failed;
	}
	error_code = eb_narrow_font_start(book, &character_number);
	if (error_code != EB_SUCCESS) {
	    fprintf(stderr, "%s: %s: subbook=%s, font=%d, type=narrow\n",
		invoked_name, eb_error_message(error_code), subbook_directory,
		image_height);
	    goto failed;
	}

	/*
	 * Make a directory for the narrow font.
	 */
	sprintf(type_path, F_("%s/narrow", "%s\\narrow"), image_path);
	if (make_missing_directory(type_path, 0777 ^ get_umask()) < 0)
	    goto failed;

	while (0 <= character_number) {
	    /*
	     * Output debug information.
	     */
	    if (debug_flag) {
		fprintf(stderr, "%s: debug: character %04x\n", invoked_name,
		    character_number);
	    }

	    /*
	     * Generate a bitmap file for the character `character_number'.
	     */
	    sprintf(file_name, F_("%s/%04x.%s", "%s\\%04x.%s"),
		type_path, character_number, image_formats[image].suffix);
	    error_code = eb_narrow_font_character_bitmap(book,
		character_number, bitmap_data);
	    if (error_code != EB_SUCCESS) {
		fprintf(stderr, "%s: %s: subbook=%s, font=%d, type=narrow, \
character=0x%04x\n",
		    invoked_name, eb_error_message(error_code),
		    subbook_directory, image_height, character_number);
		goto failed;
	    }

	    error_code = (image_formats[image].function)(bitmap_data,
		image_width, image_height, image_data, &image_size);
	    if (error_code != EB_SUCCESS) {
		fprintf(stderr, "%s: %s: subbook=%s, font=%d, type=narrow, \
character=0x%04x\n",
		    invoked_name, eb_error_message(error_code),
		    subbook_directory, image_height, character_number);
		goto failed;
	    }
	    if (save_image_file(file_name, image_data, image_size) < 0)
		goto failed;

	    /*
	     * Toward next charahacter.
	     */
	    eb_forward_narrow_font_character(book, 1, &character_number);
	}
    }

    /*
     * Make the bitmap files of wide fonts.
     */
    if (eb_have_wide_font(book)) {
	/*
	 * Get wide font information.
	 */
	error_code = eb_wide_font_width(book, &image_width);
	if (error_code != EB_SUCCESS) {
	    fprintf(stderr, "%s: %s: subbook=%s, font=%d, type=wide\n",
		invoked_name, eb_error_message(error_code), subbook_directory,
		image_height);
	    goto failed;
	}
	error_code = eb_wide_font_start(book, &character_number);
	if (error_code != EB_SUCCESS) {
	    fprintf(stderr, "%s: %s: subbook=%s, font=%d, type=wide\n",
		invoked_name, eb_error_message(error_code), subbook_directory,
		image_height);
	    goto failed;
	}

	/*
	 * Make a directory for the wide font.
	 */
	sprintf(type_path, F_("%s/wide", "%s\\wide"), image_path);
	if (make_missing_directory(type_path, 0777 ^ get_umask()) < 0)
	    goto failed;

	while (0 <= character_number) {
	    /*
	     * Output debug information.
	     */
	    if (debug_flag) {
		fprintf(stderr, "%s: debug: character %04x\n", invoked_name,
		    character_number);
	    }

	    /*
	     * Generate a bitmap file for the character `character_number'.
	     */
	    sprintf(file_name, F_("%s/%04x.%s", "%s\\%04x.%s"),
		type_path, character_number, image_formats[image].suffix);
	    error_code = eb_wide_font_character_bitmap(book, character_number,
		bitmap_data);
	    if (error_code != EB_SUCCESS) {
		fprintf(stderr, "%s: %s: subbook=%s, font=%d, type=wide, \
character=0x%04x\n",
		    invoked_name, eb_error_message(error_code),
		    subbook_directory, image_height, character_number);
		goto failed;
	    }

	    error_code = (image_formats[image].function)(bitmap_data,
		image_width, image_height, image_data, &image_size);
	    if (error_code != EB_SUCCESS) {
		fprintf(stderr, "%s: %s: subbook=%s, font=%d, type=narrow, \
character=0x%04x\n",
		    invoked_name, eb_error_message(error_code),
		    subbook_directory, image_height, character_number);
		goto failed;
	    }
	    if (save_image_file(file_name, image_data, image_size) < 0)
		goto failed;

	    /*
	     * Toward next charahacter.
	     */
	    eb_forward_wide_font_character(book, 1, &character_number);
	}
    }

    return 0;

    /*
     * An error occurs...
     */
  failed:
    fflush(stderr);
    return -1;
}


/*
 * Save an image file.
 */
static int
save_image_file(const char *file_name, const char *image_data,
    size_t image_size)
{
    int file = -1;

#ifdef O_CREAT
    file = open(file_name, O_CREAT | O_TRUNC | O_WRONLY | O_BINARY,
	0666 ^ get_umask());
#else
    file = creat(file_name, 0666 ^ get_umask());
#endif

    if (file < 0) {
	fprintf(stderr, _("%s: failed to open the file, %s: %s\n"),
	    invoked_name, strerror(errno), file_name);
	goto failed;
    }
    if (write(file, image_data, image_size) != image_size) {
	fprintf(stderr, _("%s: failed to write to the file, %s: %s\n"),
	    invoked_name, strerror(errno), file_name);
	goto failed;
    }
    if (close(file) < 0) {
	fprintf(stderr, _("%s: failed to write to the file, %s: %s\n"),
	    invoked_name, strerror(errno), file_name);
	file = -1;
	goto failed;
    }

    return 0;

    /*
     * An error occurs...
     */
  failed:
    if (0 <= file && close(file) < 0) {
	fprintf(stderr, _("%s: failed to close the file, %s: %s\n"),
	    invoked_name, strerror(errno), file_name);
    }

    return -1;
}

