Refactoring the iff decoder
[deark.git] / modules / gemfont.c
blobd7985190d65fa483d882edade1b8f3a474c5674e
1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
5 // GEM bitmap font
7 #include <deark-config.h>
8 #include <deark-private.h>
9 DE_DECLARE_MODULE(de_module_gemfont);
11 typedef struct localctx_struct {
12 struct de_bitmap_font *font;
13 i64 face_size;
14 i64 first_index, last_index;
15 i64 max_char_cell_width;
16 i64 char_offset_table_pos;
17 i64 font_data_pos;
18 i64 form_width_bytes;
19 i64 form_height_pixels;
20 u8 byte_swap_flag;
21 de_finfo *fi;
22 } lctx;
24 static int do_characters(deark *c, lctx *d)
26 i64 i;
27 i64 row;
28 i64 n;
29 struct de_bitmap_font_char *ch;
30 i64 char_startpos;
31 u8 *font_data = NULL;
32 i64 form_nbytes;
33 int retval = 0;
35 de_dbg(c, "reading characters");
36 de_dbg_indent(c, 1);
38 form_nbytes = d->form_width_bytes * d->form_height_pixels;
39 if(d->font_data_pos + form_nbytes > c->infile->len) {
40 de_err(c, "Font data goes beyond end of file");
41 goto done;
43 font_data = de_malloc(c, form_nbytes);
44 de_read(font_data, d->font_data_pos, form_nbytes);
46 for(i=0; i<d->font->num_chars; i++) {
47 ch = &d->font->char_array[i];
48 char_startpos = de_getu16le(d->char_offset_table_pos + 2*i);
49 n = de_getu16le(d->char_offset_table_pos + 2*(i+1));
50 ch->width = (int)(n - char_startpos);
51 ch->height = d->font->nominal_height;
52 ch->codepoint_nonunicode = (i32)(d->first_index+i);
53 de_dbg2(c, "char[%d] #%d offset=%d width=%d", (int)i, (int)ch->codepoint_nonunicode,
54 (int)char_startpos, ch->width);
55 if(ch->width<1 || ch->width>d->max_char_cell_width || ch->width>512) continue;
57 ch->rowspan = (ch->width+7)/8;
58 ch->bitmap = de_malloc(c, ch->height * ch->rowspan);
60 for(row=0; row<ch->height; row++) {
61 de_copy_bits(font_data + row*d->form_width_bytes, char_startpos,
62 ch->bitmap + row*ch->rowspan, 0, (i64)ch->width);
65 if(ch->width > d->font->nominal_width) {
66 // Track the maximum character width.
67 d->font->nominal_width = ch->width;
70 retval = 1;
72 done:
73 de_dbg_indent(c, -1);
74 de_free(c, font_data);
75 return retval;
78 static void do_face_name(deark *c, lctx *d)
80 de_ucstring *name = NULL;
82 name = ucstring_create(c);
83 dbuf_read_to_ucstring(c->infile, 4, 32, name, DE_CONVFLAG_STOP_AT_NUL,
84 DE_ENCODING_ASCII);
85 de_dbg(c, "face name: \"%s\"", ucstring_getpsz_d(name));
86 if(!c->filenames_from_file) goto done;
88 ucstring_strip_trailing_spaces(name);
89 ucstring_printf(name, DE_ENCODING_LATIN1, "-%d", (int)d->face_size);
90 d->fi = de_finfo_create(c);
91 de_finfo_set_name_from_ucstring(c, d->fi, name, 0);
93 done:
94 ucstring_destroy(name);
97 static int do_header(deark *c, lctx *d)
99 unsigned int font_flags;
100 i64 max_char_width;
101 i64 n;
103 de_dbg(c, "header at %d", 0);
104 de_dbg_indent(c, 1);
106 n = de_getu16le(0);
107 de_dbg(c, "face ID: %d", (int)n);
109 d->face_size = de_getu16le(2);
110 de_dbg(c, "point size: %d", (int)d->face_size);
112 do_face_name(c, d); // Offset 4-35
114 d->first_index = de_getu16le(36);
115 d->last_index = de_getu16le(38);
116 de_dbg(c, "first char: %d, last char: %d", (int)d->first_index, (int)d->last_index);
117 d->font->num_chars = d->last_index - d->first_index + 1;
119 max_char_width = de_getu16le(50);
120 d->max_char_cell_width = de_getu16le(52);
121 de_dbg(c, "max char width: %d, max char cell width: %d", (int)max_char_width,
122 (int)d->max_char_cell_width);
124 n = de_getu16le(54);
125 de_dbg(c, "left offset: %d", (int)n);
126 n = de_getu16le(56);
127 de_dbg(c, "right offset: %d", (int)n);
129 n = de_getu16le(62);
130 de_dbg(c, "lightening mask: 0x%04x", (unsigned int)n);
132 font_flags = (unsigned int)de_getu16le(66);
133 d->byte_swap_flag = (font_flags & 0x04) ? 1 : 0;
135 de_dbg(c, "byte swap flag: %d", (int)d->byte_swap_flag);
136 if(d->byte_swap_flag) {
137 de_warn(c, "This font uses an unsupported byte-swap option, and might not be "
138 "decoded correctly.");
141 n = de_getu32le(68);
142 de_dbg(c, "horiz. offset table offset: %u", (unsigned int)n);
144 d->char_offset_table_pos = de_getu32le(72);
145 d->font_data_pos = de_getu32le(76);
146 de_dbg(c, "char. offset table offset: %d", (int)d->char_offset_table_pos);
147 de_dbg(c, "font data offset: %d", (int)d->font_data_pos);
149 d->form_width_bytes = de_getu16le(80);
150 d->form_height_pixels = de_getu16le(82);
151 de_dbg(c, "form width: %d bytes", (int)d->form_width_bytes);
152 de_dbg(c, "form height: %d pixels", (int)d->form_height_pixels);
154 de_dbg_indent(c, -1);
155 return 1;
158 static void de_run_gemfont(deark *c, de_module_params *mparams)
160 lctx *d = NULL;
161 i64 i;
162 int saved_indent_level;
164 de_dbg_indent_save(c, &saved_indent_level);
165 d = de_malloc(c, sizeof(lctx));
166 d->font = de_create_bitmap_font(c);
167 d->font->has_nonunicode_codepoints = 1;
169 if(!do_header(c, d)) goto done;
171 d->font->nominal_width = 1; // This will be calculated later
172 d->font->nominal_height = (int)d->form_height_pixels;
173 if(d->font->nominal_height<1 || d->font->nominal_height>512) goto done;
175 if(d->font->num_chars<1) goto done;
176 d->font->char_array = de_mallocarray(c, d->font->num_chars, sizeof(struct de_bitmap_font_char));
178 if(!do_characters(c, d)) goto done;
180 de_font_bitmap_font_to_image(c, d->font, d->fi, 0);
182 done:
183 de_dbg_indent_restore(c, saved_indent_level);
184 if(d->font) {
185 if(d->font->char_array) {
186 for(i=0; i<d->font->num_chars; i++) {
187 de_free(c, d->font->char_array[i].bitmap);
189 de_free(c, d->font->char_array);
191 de_destroy_bitmap_font(c, d->font);
193 de_finfo_destroy(c, d->fi);
194 de_free(c, d);
197 // This is a difficult format to reliably identify.
198 static int de_identify_gemfont(deark *c)
200 int has_usual_lm;
201 i64 fdoffs, fwidth, fheight, eofd;
203 if(!de_input_file_has_ext(c, "fnt") &&
204 !de_input_file_has_ext(c, "gft"))
206 return 0;
209 has_usual_lm = !dbuf_memcmp(c->infile, 62, "UUUU", 4);
210 fdoffs = de_getu32le(76);
211 if(fdoffs<88) return 0;
212 fwidth = de_getu16le(80);
213 fheight = de_getu16le(82);
214 if(fwidth<1 || fheight<1) return 0;
215 eofd = fdoffs + fwidth*fheight; // end of font data
216 if(eofd > c->infile->len) return 0;
217 if(eofd==c->infile->len && has_usual_lm) return 100;
218 if(eofd==c->infile->len) return 70;
219 if(has_usual_lm) return 25;
220 return 0;
223 void de_module_gemfont(deark *c, struct deark_module_info *mi)
225 mi->id = "gemfont";
226 mi->desc = "GEM bitmap font";
227 mi->run_fn = de_run_gemfont;
228 mi->identify_fn = de_identify_gemfont;