diff options
Diffstat (limited to 'util/grub-mkfont.c')
-rw-r--r-- | util/grub-mkfont.c | 1287 |
1 files changed, 1287 insertions, 0 deletions
diff --git a/util/grub-mkfont.c b/util/grub-mkfont.c new file mode 100644 index 0000000..0fe45a6 --- /dev/null +++ b/util/grub-mkfont.c @@ -0,0 +1,1287 @@ +/* + * GRUB -- GRand Unified Bootloader + * Copyright (C) 2009,2010 Free Software Foundation, Inc. + * + * GRUB is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * GRUB is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with GRUB. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <grub/types.h> +#include <grub/misc.h> +#include <grub/emu/misc.h> +#include <grub/util/misc.h> +#include <grub/misc.h> +#include <grub/i18n.h> +#include <grub/fontformat.h> +#include <grub/font.h> +#include <grub/unicode.h> + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifndef GRUB_BUILD +#define _GNU_SOURCE 1 +#pragma GCC diagnostic ignored "-Wmissing-prototypes" +#pragma GCC diagnostic ignored "-Wmissing-declarations" +#include <argp.h> +#pragma GCC diagnostic error "-Wmissing-prototypes" +#pragma GCC diagnostic error "-Wmissing-declarations" +#endif +#include <assert.h> + +#include <errno.h> + +#include <ft2build.h> +#include FT_FREETYPE_H +#include FT_TRUETYPE_TAGS_H +#include FT_TRUETYPE_TABLES_H +#include FT_SYNTHESIS_H + +#undef __FTERRORS_H__ +#define FT_ERROR_START_LIST const char *ft_errmsgs[] = { +#define FT_ERRORDEF(e, v, s) [e] = s, +#define FT_ERROR_END_LIST }; +#include FT_ERRORS_H + +#ifndef GRUB_BUILD +#include "progname.h" +#endif + +#ifdef GRUB_BUILD +#define grub_util_fopen fopen +#endif + +#define GRUB_FONT_DEFAULT_SIZE 16 + +#define GRUB_FONT_RANGE_BLOCK 1024 + +struct grub_glyph_info +{ + struct grub_glyph_info *next; + grub_uint32_t char_code; + int width; + int height; + int x_ofs; + int y_ofs; + int device_width; + int bitmap_size; + grub_uint8_t *bitmap; +}; + +enum file_formats +{ + PF2 +}; + +enum + { + GRUB_FONT_FLAG_BOLD = 1, + GRUB_FONT_FLAG_NOBITMAP = 2, + GRUB_FONT_FLAG_NOHINTING = 4, + GRUB_FONT_FLAG_FORCEHINT = 8 + }; + +struct grub_font_info +{ + const char *name; + int style; + int desc; + int asce; + int size; + int max_width; + int max_height; + int min_y; + int max_y; + int flags; + int num_range; + grub_uint32_t *ranges; + struct grub_glyph_info *glyphs_unsorted; + struct grub_glyph_info *glyphs_sorted; + int num_glyphs; +}; + +static int font_verbosity; + +static void +add_pixel (grub_uint8_t **data, int *mask, int not_blank) +{ + if (*mask == 0) + { + (*data)++; + **data = 0; + *mask = 128; + } + + if (not_blank) + **data |= *mask; + + *mask >>= 1; +} + +static void +add_glyph (struct grub_font_info *font_info, FT_UInt glyph_idx, FT_Face face, + grub_uint32_t char_code, int nocut) +{ + struct grub_glyph_info *glyph_info; + int width, height; + int cuttop, cutbottom, cutleft, cutright; + grub_uint8_t *data; + int mask, i, j, bitmap_size; + FT_GlyphSlot glyph; + int flag = FT_LOAD_RENDER | FT_LOAD_MONOCHROME; + FT_Error err; + + if (font_info->flags & GRUB_FONT_FLAG_NOBITMAP) + flag |= FT_LOAD_NO_BITMAP; + + if (font_info->flags & GRUB_FONT_FLAG_NOHINTING) + flag |= FT_LOAD_NO_HINTING; + else if (font_info->flags & GRUB_FONT_FLAG_FORCEHINT) + flag |= FT_LOAD_FORCE_AUTOHINT; + + err = FT_Load_Glyph (face, glyph_idx, flag); + if (err) + { + printf (_("Freetype Error %d loading glyph 0x%x for U+0x%x%s"), + err, glyph_idx, char_code & GRUB_FONT_CODE_CHAR_MASK, + char_code & GRUB_FONT_CODE_RIGHT_JOINED + /* TRANSLATORS: These qualifiers are used for cursive typography, + mainly Arabic. Note that the terms refer to the visual position + and not logical order and if used in left-to-right script then + leftmost is initial but with right-to-left script like Arabic + rightmost is the initial. */ + ? ((char_code & GRUB_FONT_CODE_LEFT_JOINED) ? _(" (medial)"): + _(" (leftmost)")) + : ((char_code & GRUB_FONT_CODE_LEFT_JOINED) ? _(" (rightmost)"): + "")); + + if (err > 0 && err < (signed) ARRAY_SIZE (ft_errmsgs)) + printf (": %s\n", ft_errmsgs[err]); + else + printf ("\n"); + return; + } + + glyph = face->glyph; + + if (font_info->flags & GRUB_FONT_FLAG_BOLD) + FT_GlyphSlot_Embolden (glyph); + + if (nocut) + cuttop = cutbottom = cutleft = cutright = 0; + else + { + for (cuttop = 0; cuttop < glyph->bitmap.rows; cuttop++) + { + for (j = 0; j < glyph->bitmap.width; j++) + if (glyph->bitmap.buffer[j / 8 + cuttop * glyph->bitmap.pitch] + & (1 << (7 - (j & 7)))) + break; + if (j != glyph->bitmap.width) + break; + } + + for (cutbottom = glyph->bitmap.rows - 1; cutbottom >= 0; cutbottom--) + { + for (j = 0; j < glyph->bitmap.width; j++) + if (glyph->bitmap.buffer[j / 8 + cutbottom * glyph->bitmap.pitch] + & (1 << (7 - (j & 7)))) + break; + if (j != glyph->bitmap.width) + break; + } + cutbottom = glyph->bitmap.rows - 1 - cutbottom; + if (cutbottom + cuttop >= glyph->bitmap.rows) + cutbottom = 0; + + for (cutleft = 0; cutleft < glyph->bitmap.width; cutleft++) + { + for (j = 0; j < glyph->bitmap.rows; j++) + if (glyph->bitmap.buffer[cutleft / 8 + j * glyph->bitmap.pitch] + & (1 << (7 - (cutleft & 7)))) + break; + if (j != glyph->bitmap.rows) + break; + } + for (cutright = glyph->bitmap.width - 1; cutright >= 0; cutright--) + { + for (j = 0; j < glyph->bitmap.rows; j++) + if (glyph->bitmap.buffer[cutright / 8 + j * glyph->bitmap.pitch] + & (1 << (7 - (cutright & 7)))) + break; + if (j != glyph->bitmap.rows) + break; + } + cutright = glyph->bitmap.width - 1 - cutright; + if (cutright + cutleft >= glyph->bitmap.width) + cutright = 0; + } + + width = glyph->bitmap.width - cutleft - cutright; + height = glyph->bitmap.rows - cutbottom - cuttop; + + bitmap_size = ((width * height + 7) / 8); + glyph_info = xmalloc (sizeof (struct grub_glyph_info)); + glyph_info->bitmap = xmalloc (bitmap_size); + glyph_info->bitmap_size = bitmap_size; + + glyph_info->next = font_info->glyphs_unsorted; + font_info->glyphs_unsorted = glyph_info; + font_info->num_glyphs++; + + glyph_info->char_code = char_code; + glyph_info->width = width; + glyph_info->height = height; + glyph_info->x_ofs = glyph->bitmap_left + cutleft; + glyph_info->y_ofs = glyph->bitmap_top - height - cuttop; + glyph_info->device_width = glyph->metrics.horiAdvance / 64; + + if (width > font_info->max_width) + font_info->max_width = width; + + if (height > font_info->max_height) + font_info->max_height = height; + + if (glyph_info->y_ofs < font_info->min_y && glyph_info->y_ofs > -font_info->size) + font_info->min_y = glyph_info->y_ofs; + + if (glyph_info->y_ofs + height > font_info->max_y) + font_info->max_y = glyph_info->y_ofs + height; + + mask = 0; + data = &glyph_info->bitmap[0] - 1; + for (j = cuttop; j < height + cuttop; j++) + for (i = cutleft; i < width + cutleft; i++) + add_pixel (&data, &mask, + glyph->bitmap.buffer[i / 8 + j * glyph->bitmap.pitch] & + (1 << (7 - (i & 7)))); +} + +struct glyph_replace *subst_rightjoin, *subst_leftjoin, *subst_medijoin; + +struct glyph_replace +{ + struct glyph_replace *next; + grub_uint32_t from, to; +}; + +/* TODO: sort glyph_replace and use binary search if necessary. */ +static void +add_char (struct grub_font_info *font_info, FT_Face face, + grub_uint32_t char_code, int nocut) +{ + FT_UInt glyph_idx; + struct glyph_replace *cur; + + glyph_idx = FT_Get_Char_Index (face, char_code); + if (!glyph_idx) + return; + add_glyph (font_info, glyph_idx, face, char_code, nocut); + for (cur = subst_rightjoin; cur; cur = cur->next) + if (cur->from == glyph_idx) + { + add_glyph (font_info, cur->to, face, + char_code | GRUB_FONT_CODE_RIGHT_JOINED, nocut); + break; + } + if (!cur && char_code >= GRUB_UNICODE_ARABIC_START + && char_code < GRUB_UNICODE_ARABIC_END) + { + int i; + for (i = 0; grub_unicode_arabic_shapes[i].code; i++) + if (grub_unicode_arabic_shapes[i].code == char_code + && grub_unicode_arabic_shapes[i].right_linked) + { + FT_UInt idx2; + idx2 = FT_Get_Char_Index (face, grub_unicode_arabic_shapes[i] + .right_linked); + if (idx2) + add_glyph (font_info, idx2, face, + char_code | GRUB_FONT_CODE_RIGHT_JOINED, nocut); + break; + } + + } + + for (cur = subst_leftjoin; cur; cur = cur->next) + if (cur->from == glyph_idx) + { + add_glyph (font_info, cur->to, face, + char_code | GRUB_FONT_CODE_LEFT_JOINED, nocut); + break; + } + if (!cur && char_code >= GRUB_UNICODE_ARABIC_START + && char_code < GRUB_UNICODE_ARABIC_END) + { + int i; + for (i = 0; grub_unicode_arabic_shapes[i].code; i++) + if (grub_unicode_arabic_shapes[i].code == char_code + && grub_unicode_arabic_shapes[i].left_linked) + { + FT_UInt idx2; + idx2 = FT_Get_Char_Index (face, grub_unicode_arabic_shapes[i] + .left_linked); + if (idx2) + add_glyph (font_info, idx2, face, + char_code | GRUB_FONT_CODE_LEFT_JOINED, nocut); + break; + } + + } + for (cur = subst_medijoin; cur; cur = cur->next) + if (cur->from == glyph_idx) + { + add_glyph (font_info, cur->to, face, + char_code | GRUB_FONT_CODE_LEFT_JOINED + | GRUB_FONT_CODE_RIGHT_JOINED, nocut); + break; + } + if (!cur && char_code >= GRUB_UNICODE_ARABIC_START + && char_code < GRUB_UNICODE_ARABIC_END) + { + int i; + for (i = 0; grub_unicode_arabic_shapes[i].code; i++) + if (grub_unicode_arabic_shapes[i].code == char_code + && grub_unicode_arabic_shapes[i].both_linked) + { + FT_UInt idx2; + idx2 = FT_Get_Char_Index (face, grub_unicode_arabic_shapes[i] + .both_linked); + if (idx2) + add_glyph (font_info, idx2, face, + char_code | GRUB_FONT_CODE_LEFT_JOINED + | GRUB_FONT_CODE_RIGHT_JOINED, nocut); + break; + } + + } +} + +struct gsub_header +{ + grub_uint32_t version; + grub_uint16_t scripts_off; + grub_uint16_t features_off; + grub_uint16_t lookups_off; +} GRUB_PACKED; + +struct gsub_features +{ + grub_uint16_t count; + struct + { +#define FEATURE_FINA 0x66696e61 +#define FEATURE_INIT 0x696e6974 +#define FEATURE_MEDI 0x6d656469 +#define FEATURE_AALT 0x61616c74 +#define FEATURE_LIGA 0x6c696761 +#define FEATURE_RLIG 0x726c6967 + grub_uint32_t feature_tag; + grub_uint16_t offset; + } GRUB_PACKED features[0]; +} GRUB_PACKED; + +struct gsub_feature +{ + grub_uint16_t params; + grub_uint16_t lookupcount; + grub_uint16_t lookupindices[0]; +} GRUB_PACKED; + +struct gsub_lookup_list +{ + grub_uint16_t count; + grub_uint16_t offsets[0]; +} GRUB_PACKED; + +struct gsub_lookup +{ + grub_uint16_t type; + grub_uint16_t flag; + grub_uint16_t subtablecount; + grub_uint16_t subtables[0]; +} GRUB_PACKED; + +struct gsub_substitution +{ + grub_uint16_t type; + grub_uint16_t coverage_off; + union + { + grub_int16_t delta; + struct + { + grub_int16_t count; + grub_uint16_t repl[0]; + }; + }; +} GRUB_PACKED; + +struct gsub_coverage_list +{ + grub_uint16_t type; + grub_uint16_t count; + grub_uint16_t glyphs[0]; +} GRUB_PACKED; + +struct gsub_coverage_ranges +{ + grub_uint16_t type; + grub_uint16_t count; + struct + { + grub_uint16_t start; + grub_uint16_t end; + grub_uint16_t start_index; + } GRUB_PACKED ranges[0]; +} GRUB_PACKED; + +#define GSUB_SINGLE_SUBSTITUTION 1 + +#define GSUB_SUBSTITUTION_DELTA 1 +#define GSUB_SUBSTITUTION_MAP 2 + +#define GSUB_COVERAGE_LIST 1 +#define GSUB_COVERAGE_RANGE 2 + +#define GSUB_RTL_CHAR 1 + +static void +add_subst (grub_uint32_t from, grub_uint32_t to, struct glyph_replace **target) +{ + struct glyph_replace *new = xmalloc (sizeof (*new)); + new->next = *target; + new->from = from; + new->to = to; + *target = new; +} + +static void +subst (const struct gsub_substitution *sub, grub_uint32_t glyph, + struct glyph_replace **target, int *i) +{ + grub_uint16_t substtype; + substtype = grub_be_to_cpu16 (sub->type); + + if (substtype == GSUB_SUBSTITUTION_DELTA) + add_subst (glyph, glyph + grub_be_to_cpu16 (sub->delta), target); + else if (*i >= grub_be_to_cpu16 (sub->count)) + printf (_("Out of range substitution (%d, %d)\n"), *i, + grub_be_to_cpu16 (sub->count)); + else + add_subst (glyph, grub_be_to_cpu16 (sub->repl[(*i)++]), target); +} + +static void +process_cursive (struct gsub_feature *feature, + struct gsub_lookup_list *lookups, + grub_uint32_t feattag) +{ + int j, k; + int i; + struct glyph_replace **target = NULL; + struct gsub_substitution *sub; + + for (j = 0; j < grub_be_to_cpu16 (feature->lookupcount); j++) + { + int lookup_index = grub_be_to_cpu16 (feature->lookupindices[j]); + struct gsub_lookup *lookup; + if (lookup_index >= grub_be_to_cpu16 (lookups->count)) + { + /* TRANSLATORS: "lookup" is taken directly from font specifications + which are formulated as "Under condition X replace LOOKUP with + SUBSTITUITION". "*/ + printf (_("Out of range lookup: %d\n"), lookup_index); + continue; + } + lookup = (struct gsub_lookup *) + ((grub_uint8_t *) lookups + + grub_be_to_cpu16 (lookups->offsets[lookup_index])); + if (grub_be_to_cpu16 (lookup->type) != GSUB_SINGLE_SUBSTITUTION) + { + printf (_("Unsupported substitution type: %d\n"), + grub_be_to_cpu16 (lookup->type)); + continue; + } + if (grub_be_to_cpu16 (lookup->flag) & ~GSUB_RTL_CHAR) + { + grub_util_info ("unsupported substitution flag: 0x%x", + grub_be_to_cpu16 (lookup->flag)); + } + switch (feattag) + { + case FEATURE_INIT: + if (grub_be_to_cpu16 (lookup->flag) & GSUB_RTL_CHAR) + target = &subst_leftjoin; + else + target = &subst_rightjoin; + break; + case FEATURE_FINA: + if (grub_be_to_cpu16 (lookup->flag) & GSUB_RTL_CHAR) + target = &subst_rightjoin; + else + target = &subst_leftjoin; + break; + case FEATURE_MEDI: + target = &subst_medijoin; + break; + } + for (k = 0; k < grub_be_to_cpu16 (lookup->subtablecount); k++) + { + sub = (struct gsub_substitution *) + ((grub_uint8_t *) lookup + grub_be_to_cpu16 (lookup->subtables[k])); + grub_uint16_t substtype; + substtype = grub_be_to_cpu16 (sub->type); + if (substtype != GSUB_SUBSTITUTION_MAP + && substtype != GSUB_SUBSTITUTION_DELTA) + { + printf (_("Unsupported substitution specification: %d\n"), + substtype); + continue; + } + void *coverage = (grub_uint8_t *) sub + + grub_be_to_cpu16 (sub->coverage_off); + grub_uint32_t covertype; + covertype = grub_be_to_cpu16 (grub_get_unaligned16 (coverage)); + i = 0; + if (covertype == GSUB_COVERAGE_LIST) + { + struct gsub_coverage_list *cover = coverage; + int l; + for (l = 0; l < grub_be_to_cpu16 (cover->count); l++) + subst (sub, grub_be_to_cpu16 (cover->glyphs[l]), target, &i); + } + else if (covertype == GSUB_COVERAGE_RANGE) + { + struct gsub_coverage_ranges *cover = coverage; + int l, m; + for (l = 0; l < grub_be_to_cpu16 (cover->count); l++) + for (m = grub_be_to_cpu16 (cover->ranges[l].start); + m <= grub_be_to_cpu16 (cover->ranges[l].end); m++) + subst (sub, m, target, &i); + } + else + /* TRANSLATORS: most font transformations apply only to + some glyphs. Those glyphs are described as "coverage". + There are 2 coverage specifications: list and range. + This warning is thrown when another coverage specification + is detected. */ + fprintf (stderr, + _("Unsupported coverage specification: %d\n"), covertype); + } + } +} + +static void +add_font (struct grub_font_info *font_info, FT_Face face, int nocut) +{ + struct gsub_header *gsub = NULL; + FT_ULong gsub_len = 0; + + if (!FT_Load_Sfnt_Table (face, TTAG_GSUB, 0, NULL, &gsub_len)) + { + gsub = xmalloc (gsub_len); + if (FT_Load_Sfnt_Table (face, TTAG_GSUB, 0, (void *) gsub, &gsub_len)) + { + free (gsub); + gsub = NULL; + gsub_len = 0; + } + } + if (gsub) + { + struct gsub_features *features + = (struct gsub_features *) (((grub_uint8_t *) gsub) + + grub_be_to_cpu16 (gsub->features_off)); + struct gsub_lookup_list *lookups + = (struct gsub_lookup_list *) (((grub_uint8_t *) gsub) + + grub_be_to_cpu16 (gsub->lookups_off)); + int i; + int nfeatures = grub_be_to_cpu16 (features->count); + for (i = 0; i < nfeatures; i++) + { + struct gsub_feature *feature = (struct gsub_feature *) + ((grub_uint8_t *) features + + grub_be_to_cpu16 (features->features[i].offset)); + grub_uint32_t feattag + = grub_be_to_cpu32 (features->features[i].feature_tag); + if (feature->params) + fprintf (stderr, + _("WARNING: unsupported font feature parameters: %x\n"), + grub_be_to_cpu16 (feature->params)); + switch (feattag) + { + /* Used for retrieving all possible variants. Useless in grub. */ + case FEATURE_AALT: + break; + + /* FIXME: Add ligature support. */ + case FEATURE_LIGA: + case FEATURE_RLIG: + break; + + /* Cursive form variants. */ + case FEATURE_FINA: + case FEATURE_INIT: + case FEATURE_MEDI: + process_cursive (feature, lookups, feattag); + break; + + default: + { + char str[5]; + int j; + memcpy (str, &features->features[i].feature_tag, + sizeof (features->features[i].feature_tag)); + str[4] = 0; + for (j = 0; j < 4; j++) + if (!grub_isgraph (str[j])) + str[j] = '?'; + /* TRANSLATORS: It's gsub feature, not gsub font. */ + grub_util_info ("Unknown gsub font feature 0x%x (%s)", + feattag, str); + } + } + } + } + + if (font_info->num_range) + { + int i; + grub_uint32_t j; + + for (i = 0; i < font_info->num_range; i++) + for (j = font_info->ranges[i * 2]; j <= font_info->ranges[i * 2 + 1]; + j++) + add_char (font_info, face, j, nocut); + } + else + { + grub_uint32_t char_code, glyph_index; + + for (char_code = FT_Get_First_Char (face, &glyph_index); + glyph_index; + char_code = FT_Get_Next_Char (face, char_code, &glyph_index)) + add_char (font_info, face, char_code, nocut); + } +} + +static void +write_string_section (const char *name, const char *str, + int *offset, FILE *file, + const char *filename) +{ + grub_uint32_t leng, leng_be32; + + leng = strlen (str) + 1; + leng_be32 = grub_cpu_to_be32 (leng); + + grub_util_write_image (name, 4, file, filename); + grub_util_write_image ((char *) &leng_be32, 4, file, filename); + grub_util_write_image (str, leng, file, filename); + + *offset += 8 + leng; +} + +static void +write_be16_section (const char *name, grub_uint16_t data, int* offset, + FILE *file, const char *filename) +{ + grub_uint32_t leng; + + leng = grub_cpu_to_be32_compile_time (2); + data = grub_cpu_to_be16 (data); + grub_util_write_image (name, 4, file, filename); + grub_util_write_image ((char *) &leng, 4, file, filename); + grub_util_write_image ((char *) &data, 2, file, filename); + + *offset += 10; +} + +static void +print_glyphs (struct grub_font_info *font_info) +{ + int num; + struct grub_glyph_info *glyph; + char line[512]; + + for (glyph = font_info->glyphs_sorted, num = 0; num < font_info->num_glyphs; + glyph++, num++) + { + int x, y, xmax, xmin, ymax, ymin; + grub_uint8_t *bitmap, mask; + + printf ("\nGlyph #%d, U+%04x\n", num, glyph->char_code); + printf ("Width %d, Height %d, X offset %d, Y offset %d, Device width %d\n", + glyph->width, glyph->height, glyph->x_ofs, glyph->y_ofs, + glyph->device_width); + + xmax = glyph->x_ofs + glyph->width; + if (xmax < glyph->device_width) + xmax = glyph->device_width; + + xmin = glyph->x_ofs; + if (xmin > 0) + xmin = 0; + + ymax = glyph->y_ofs + glyph->height; + if (ymax < font_info->asce) + ymax = font_info->asce; + + ymin = glyph->y_ofs; + if (ymin > - font_info->desc) + ymin = - font_info->desc; + + bitmap = glyph->bitmap; + mask = 0x80; + for (y = ymax - 1; y > ymin - 1; y--) + { + int line_pos; + + line_pos = 0; + for (x = xmin; x < xmax; x++) + { + if ((x >= glyph->x_ofs) && + (x < glyph->x_ofs + glyph->width) && + (y >= glyph->y_ofs) && + (y < glyph->y_ofs + glyph->height)) + { + line[line_pos++] = (*bitmap & mask) ? '#' : '_'; + mask >>= 1; + if (mask == 0) + { + mask = 0x80; + bitmap++; + } + } + else if ((x >= 0) && + (x < glyph->device_width) && + (y >= - font_info->desc) && + (y < font_info->asce)) + { + line[line_pos++] = ((x == 0) || (y == 0)) ? '+' : '.'; + } + else + line[line_pos++] = '*'; + } + line[line_pos] = 0; + printf ("%s\n", line); + } + } +} + +static void +write_font_pf2 (struct grub_font_info *font_info, char *output_file) +{ + FILE *file; + grub_uint32_t leng; + char style_name[20], *font_name, *ptr; + int offset; + struct grub_glyph_info *cur; + + file = grub_util_fopen (output_file, "wb"); + if (! file) + grub_util_error (_("cannot write to `%s': %s"), output_file, + strerror (errno)); + + offset = 0; + + leng = grub_cpu_to_be32_compile_time (4); + grub_util_write_image (FONT_FORMAT_SECTION_NAMES_FILE, + sizeof(FONT_FORMAT_SECTION_NAMES_FILE) - 1, file, + output_file); + grub_util_write_image ((char *) &leng, 4, file, output_file); + grub_util_write_image (FONT_FORMAT_PFF2_MAGIC, 4, file, output_file); + offset += 12; + + if (! font_info->name) + font_info->name = "Unknown"; + + if (font_info->flags & GRUB_FONT_FLAG_BOLD) + font_info->style |= FT_STYLE_FLAG_BOLD; + + style_name[0] = 0; + if (font_info->style & FT_STYLE_FLAG_BOLD) + strcpy (style_name, " Bold"); + + if (font_info->style & FT_STYLE_FLAG_ITALIC) + strcat (style_name, " Italic"); + + if (! style_name[0]) + strcpy (style_name, " Regular"); + + font_name = xmalloc (strlen (font_info->name) + strlen (&style_name[1]) + + 3 + 20); + ptr = grub_stpcpy (font_name, font_info->name); + *ptr++ = ' '; + ptr = grub_stpcpy (ptr, &style_name[1]); + *ptr++ = ' '; + snprintf (ptr, 20, "%d", font_info->size); + + write_string_section (FONT_FORMAT_SECTION_NAMES_FONT_NAME, + font_name, &offset, file, output_file); + write_string_section (FONT_FORMAT_SECTION_NAMES_FAMILY, + font_info->name, &offset, file, output_file); + write_string_section (FONT_FORMAT_SECTION_NAMES_WEIGHT, + (font_info->style & FT_STYLE_FLAG_BOLD) ? + "bold" : "normal", + &offset, file, output_file); + write_string_section (FONT_FORMAT_SECTION_NAMES_SLAN, + (font_info->style & FT_STYLE_FLAG_ITALIC) ? + "italic" : "normal", + &offset, file, output_file); + + write_be16_section (FONT_FORMAT_SECTION_NAMES_POINT_SIZE, + font_info->size, &offset, file, output_file); + write_be16_section (FONT_FORMAT_SECTION_NAMES_MAX_CHAR_WIDTH, + font_info->max_width, &offset, file, output_file); + write_be16_section (FONT_FORMAT_SECTION_NAMES_MAX_CHAR_HEIGHT, + font_info->max_height, &offset, file, output_file); + + if (! font_info->desc) + { + if (font_info->min_y >= 0) + font_info->desc = 1; + else + font_info->desc = - font_info->min_y; + } + + if (! font_info->asce) + { + if (font_info->max_y <= 0) + font_info->asce = 1; + else + font_info->asce = font_info->max_y; + } + + write_be16_section (FONT_FORMAT_SECTION_NAMES_ASCENT, + font_info->asce, &offset, file, output_file); + write_be16_section (FONT_FORMAT_SECTION_NAMES_DESCENT, + font_info->desc, &offset, file, output_file); + + if (font_verbosity > 0) + { + printf ("Font name: %s\n", font_name); + printf ("Max width: %d\n", font_info->max_width); + printf ("Max height: %d\n", font_info->max_height); + printf ("Font ascent: %d\n", font_info->asce); + printf ("Font descent: %d\n", font_info->desc); + } + + if (font_verbosity > 0) + printf ("Number of glyph: %d\n", font_info->num_glyphs); + + leng = grub_cpu_to_be32 (font_info->num_glyphs * 9); + grub_util_write_image (FONT_FORMAT_SECTION_NAMES_CHAR_INDEX, + sizeof(FONT_FORMAT_SECTION_NAMES_CHAR_INDEX) - 1, + file, output_file); + grub_util_write_image ((char *) &leng, 4, file, output_file); + offset += 8 + font_info->num_glyphs * 9 + 8; + + for (cur = font_info->glyphs_sorted; + cur < font_info->glyphs_sorted + font_info->num_glyphs; cur++) + { + grub_uint32_t data32; + grub_uint8_t data8; + data32 = grub_cpu_to_be32 (cur->char_code); + grub_util_write_image ((char *) &data32, 4, file, output_file); + data8 = 0; + grub_util_write_image ((char *) &data8, 1, file, output_file); + data32 = grub_cpu_to_be32 (offset); + grub_util_write_image ((char *) &data32, 4, file, output_file); + offset += 10 + cur->bitmap_size; + } + + leng = 0xffffffff; + grub_util_write_image (FONT_FORMAT_SECTION_NAMES_DATA, + sizeof(FONT_FORMAT_SECTION_NAMES_DATA) - 1, + file, output_file); + grub_util_write_image ((char *) &leng, 4, file, output_file); + + for (cur = font_info->glyphs_sorted; + cur < font_info->glyphs_sorted + font_info->num_glyphs; cur++) + { + grub_uint16_t data; + data = grub_cpu_to_be16 (cur->width); + grub_util_write_image ((char *) &data, 2, file, output_file); + data = grub_cpu_to_be16 (cur->height); + grub_util_write_image ((char *) &data, 2, file, output_file); + data = grub_cpu_to_be16 (cur->x_ofs); + grub_util_write_image ((char *) &data, 2, file, output_file); + data = grub_cpu_to_be16 (cur->y_ofs); + grub_util_write_image ((char *) &data, 2, file, output_file); + data = grub_cpu_to_be16 (cur->device_width); + grub_util_write_image ((char *) &data, 2, file, output_file); + grub_util_write_image ((char *) &cur->bitmap[0], cur->bitmap_size, + file, output_file); + } + + fclose (file); +} + +#ifndef GRUB_BUILD +static struct argp_option options[] = { + {"output", 'o', N_("FILE"), 0, N_("save output in FILE [required]"), 0}, + /* TRANSLATORS: bitmaps are images like e.g. in JPEG. */ + {"index", 'i', N_("NUM"), 0, + /* TRANSLATORS: some font files may have multiple faces (fonts). + This option is used to chose among them, the first face being '0'. + Rarely used. */ + N_("select face index"), 0}, + {"range", 'r', N_("FROM-TO[,FROM-TO]"), 0, + /* TRANSLATORS: It refers to the range of characters in font. */ + N_("set font range"), 0}, + {"name", 'n', N_("NAME"), 0, + /* TRANSLATORS: "family name" for font is just a generic name without suffix + like "Bold". */ + N_("set font family name"), 0}, + {"size", 's', N_("SIZE"), 0, N_("set font size"), 0}, + {"desc", 'd', N_("NUM"), 0, N_("set font descent"), 0}, + {"asce", 'c', N_("NUM"), 0, N_("set font ascent"), 0}, + {"bold", 'b', 0, 0, N_("convert to bold font"), 0}, + {"force-autohint", 'a', 0, 0, N_("force autohint"), 0}, + {"no-hinting", 0x101, 0, 0, N_("disable hinting"), 0}, + {"no-bitmap", 0x100, 0, 0, + /* TRANSLATORS: some fonts contain bitmap rendering for + some sizes. This option forces rerendering even if + pre-rendered bitmap is available. + */ + N_("ignore bitmap strikes when loading"), 0}, + {"verbose", 'v', 0, 0, N_("print verbose messages."), 0}, + { 0, 0, 0, 0, 0, 0 } +}; +#define my_argp_parse argp_parse +#define MY_ARGP_KEY_ARG ARGP_KEY_ARG +#define my_error_t error_t +#define MY_ARGP_ERR_UNKNOWN ARGP_ERR_UNKNOWN +#define my_argp_state argp_state + +#else + +#define my_error_t int +#define MY_ARGP_ERR_UNKNOWN -1 +#define MY_ARGP_KEY_ARG -1 +#define my_argp_parse(a, argc, argv, b, c, st) my_argp_parse_real(argc, argv, st) +struct my_argp_state +{ + void *input; +}; + +#endif + +struct arguments +{ + struct grub_font_info font_info; + size_t nfiles; + size_t files_max; + char **files; + char *output_file; + int font_index; + int font_size; + enum file_formats file_format; +}; + +#ifdef GRUB_BUILD + +static int +has_argument (int v) +{ + return v =='o' || v == 'i' || v == 'r' || v == 'n' || v == 's' + || v == 'd' || v == 'c'; +} + +#endif + +static my_error_t +argp_parser (int key, char *arg, struct my_argp_state *state) +{ + /* Get the input argument from argp_parse, which we + know is a pointer to our arguments structure. */ + struct arguments *arguments = state->input; + + switch (key) + { + case 'b': + arguments->font_info.flags |= GRUB_FONT_FLAG_BOLD; + break; + + case 0x100: + arguments->font_info.flags |= GRUB_FONT_FLAG_NOBITMAP; + break; + + case 0x101: + arguments->font_info.flags |= GRUB_FONT_FLAG_NOHINTING; + break; + + case 'a': + arguments->font_info.flags |= GRUB_FONT_FLAG_FORCEHINT; + break; + + case 'o': + arguments->output_file = xstrdup (arg); + break; + + case 'n': + arguments->font_info.name = xstrdup (arg); + break; + + case 'i': + arguments->font_index = strtoul (arg, NULL, 0); + break; + + case 's': + arguments->font_size = strtoul (arg, NULL, 0); + break; + + case 'r': + { + char *p = arg; + + while (1) + { + grub_uint32_t a, b; + + a = strtoul (p, &p, 0); + if (*p != '-') + /* TRANSLATORS: It refers to the range of characters in font. */ + grub_util_error ("%s", _("invalid font range")); + b = strtoul (p + 1, &p, 0); + if ((arguments->font_info.num_range + & (GRUB_FONT_RANGE_BLOCK - 1)) == 0) + arguments->font_info.ranges = xrealloc (arguments->font_info.ranges, + (arguments->font_info.num_range + + GRUB_FONT_RANGE_BLOCK) * + sizeof (grub_uint32_t) * 2); + + arguments->font_info.ranges[arguments->font_info.num_range * 2] = a; + arguments->font_info.ranges[arguments->font_info.num_range * 2 + 1] = b; + arguments->font_info.num_range++; + + if (*p) + { + if (*p != ',') + grub_util_error ("%s", _("invalid font range")); + p++; + } + else + break; + } + break; + } + + case 'd': + arguments->font_info.desc = strtoul (arg, NULL, 0); + break; + + case 'c': + arguments->font_info.asce = strtoul (arg, NULL, 0); + break; + + case 'v': + font_verbosity++; + break; + + case MY_ARGP_KEY_ARG: + assert (arguments->nfiles < arguments->files_max); + arguments->files[arguments->nfiles++] = xstrdup(arg); + break; + + default: + return MY_ARGP_ERR_UNKNOWN; + } + return 0; +} + +#ifdef GRUB_BUILD + +/* We don't require host platform to have argp. In the same time configuring + gnulib for build would result in even worse mess. So we have our + minimalistic argp replacement just enough for build system. Most + argp features are omitted. */ + +static int +my_argp_parse_real (int argc, char **argv, void *st) +{ + int curar; + struct my_argp_state p; + + p.input = st; + + for (curar = 1; curar < argc; ) + { + if (argv[curar][0] == '-') + { + if (has_argument (argv[curar][1]) + && curar + 1 >= argc) + return 1; + if (has_argument (argv[curar][1])) + { + if (argp_parser (argv[curar][1], argv[curar + 1], &p)) + return 1; + curar += 2; + continue; + } + if (argp_parser (argv[curar][1], NULL, &p)) + return 1; + curar++; + continue; + } + if (argp_parser (MY_ARGP_KEY_ARG, argv[curar], &p)) + return 1; + curar++; + } + return 0; +} +#endif + +#ifndef GRUB_BUILD +static struct argp argp = { + options, argp_parser, N_("[OPTIONS] FONT_FILES"), + N_("Convert common font file formats into PF2"), + NULL, NULL, NULL +}; +#endif + +int +main (int argc, char *argv[]) +{ + FT_Library ft_lib; + struct arguments arguments; + +#ifndef GRUB_BUILD + grub_util_host_init (&argc, &argv); +#endif + + memset (&arguments, 0, sizeof (struct arguments)); + arguments.file_format = PF2; + arguments.files_max = argc + 1; + arguments.files = xmalloc ((arguments.files_max + 1) + * sizeof (arguments.files[0])); + memset (arguments.files, 0, (arguments.files_max + 1) + * sizeof (arguments.files[0])); + + if (my_argp_parse (&argp, argc, argv, 0, 0, &arguments) != 0) + { + fprintf (stderr, "%s", _("Error in parsing command line arguments\n")); + exit(1); + } + + if (! arguments.output_file) + grub_util_error ("%s", _("output file must be specified")); + + if (FT_Init_FreeType (&ft_lib)) + grub_util_error ("%s", _("FT_Init_FreeType fails")); + + { + size_t i; + for (i = 0; i < arguments.nfiles; i++) + { + FT_Face ft_face; + int size; + FT_Error err; + + err = FT_New_Face (ft_lib, arguments.files[i], + arguments.font_index, &ft_face); + if (err) + { + printf (_("can't open file %s, index %d: error %d"), + arguments.files[i], + arguments.font_index, err); + if (err > 0 && err < (signed) ARRAY_SIZE (ft_errmsgs)) + printf (": %s\n", ft_errmsgs[err]); + else + printf ("\n"); + + continue; + } + + if ((! arguments.font_info.name) && (ft_face->family_name)) + arguments.font_info.name = xstrdup (ft_face->family_name); + + size = arguments.font_size; + if (! size) + { + if ((ft_face->face_flags & FT_FACE_FLAG_SCALABLE) || + (! ft_face->num_fixed_sizes)) + size = GRUB_FONT_DEFAULT_SIZE; + else + size = ft_face->available_sizes[0].height; + } + + arguments.font_info.style = ft_face->style_flags; + arguments.font_info.size = size; + + err = FT_Set_Pixel_Sizes (ft_face, size, size); + + if (err) + grub_util_error (_("can't set %dx%d font size: Freetype error %d: %s"), + size, size, err, + (err > 0 && err < (signed) ARRAY_SIZE (ft_errmsgs)) + ? ft_errmsgs[err] : ""); + add_font (&arguments.font_info, ft_face, arguments.file_format != PF2); + FT_Done_Face (ft_face); + } + } + + FT_Done_FreeType (ft_lib); + + { + int counter[65537]; + struct grub_glyph_info *tmp, *cur; + int i; + + memset (counter, 0, sizeof (counter)); + + for (cur = arguments.font_info.glyphs_unsorted; cur; cur = cur->next) + counter[(cur->char_code & 0xffff) + 1]++; + for (i = 0; i < 0x10000; i++) + counter[i+1] += counter[i]; + tmp = xmalloc (arguments.font_info.num_glyphs + * sizeof (tmp[0])); + for (cur = arguments.font_info.glyphs_unsorted; cur; cur = cur->next) + tmp[counter[(cur->char_code & 0xffff)]++] = *cur; + + memset (counter, 0, sizeof (counter)); + + for (cur = tmp; cur < tmp + arguments.font_info.num_glyphs; cur++) + counter[((cur->char_code & 0xffff0000) >> 16) + 1]++; + for (i = 0; i < 0x10000; i++) + counter[i+1] += counter[i]; + arguments.font_info.glyphs_sorted = xmalloc (arguments.font_info.num_glyphs + * sizeof (arguments.font_info.glyphs_sorted[0])); + for (cur = tmp; cur < tmp + arguments.font_info.num_glyphs; cur++) + arguments.font_info.glyphs_sorted[counter[(cur->char_code & 0xffff0000) + >> 16]++] = *cur; + free (tmp); + } + + switch (arguments.file_format) + { + case PF2: + write_font_pf2 (&arguments.font_info, arguments.output_file); + break; + } + + if (font_verbosity > 1) + print_glyphs (&arguments.font_info); + + { + size_t i; + for (i = 0; i < arguments.nfiles; i++) + free (arguments.files[i]); + } + + return 0; +} |