rosprite: Cleanup and modernization
[deark.git] / src / deark-font.c
blob435497a6bd590bc7d10f4512e066c17c76631f0c
1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
5 // deark-font.c
6 //
7 // Functions related to fonts.
9 #define DE_NOT_IN_MODULE
10 #include "deark-config.h"
11 #include "deark-private.h"
13 static int is_valid_char(struct de_bitmap_font_char *ch)
15 if(!ch) return 0;
16 if(!ch->bitmap) return 0;
17 if(ch->width<0 || ch->height<0) return 0;
18 if(ch->extraspace_l<0 || ch->extraspace_r<0) return 0;
19 if(ch->width==0 && (ch->extraspace_l>0 || ch->extraspace_r>0)) {
20 // Allow 0-width character if extraspace makes that sensible.
23 else if(ch->width<1 || ch->height<1) {
24 return 0;
26 return 1;
29 struct de_bitmap_font *de_create_bitmap_font(deark *c)
31 struct de_bitmap_font *font;
32 font = de_malloc(c, sizeof(struct de_bitmap_font));
33 font->index_of_replacement_char = -1;
34 return font;
37 void de_destroy_bitmap_font(deark *c, struct de_bitmap_font *font)
39 de_free(c, font);
42 static void paint_character_internal(deark *c, de_bitmap *img,
43 struct de_bitmap_font_char *ch,
44 i64 xpos, i64 ypos, u32 fgcol, unsigned int flags)
46 i64 i, j;
47 i64 num_x_pixels_to_paint;
48 int vga9col_flag = 0;
50 num_x_pixels_to_paint = (i64)ch->width;
51 if((flags&DE_PAINTFLAG_VGA9COL) && ch->width==8) {
52 vga9col_flag = 1;
53 num_x_pixels_to_paint = 9;
56 for(j=0; j<ch->height; j++) {
57 i64 j_src;
59 j_src = j;
60 if(flags&DE_PAINTFLAG_TOPHALF) {
61 j_src = j/2;
63 else if(flags&DE_PAINTFLAG_BOTTOMHALF) {
64 j_src = (ch->height+j)/2;
67 for(i=0; i<num_x_pixels_to_paint; i++) {
68 i64 i_src; // -1 = No source position
69 int is_fg = 0;
70 u8 x;
72 i_src = i;
73 if(flags&DE_PAINTFLAG_LEFTHALF) {
74 i_src = i/2;
76 else if(flags&DE_PAINTFLAG_RIGHTHALF) {
77 i_src = (num_x_pixels_to_paint+i)/2;
80 if(i_src==8 && vga9col_flag) {
81 // Manufacture a column 8.
82 if(ch->codepoint_nonunicode>=0xb0 && ch->codepoint_nonunicode<=0xdf) {
83 i_src = 7; // Make this pixel a duplicate of the one in col #7.
85 else {
86 i_src = -1; // Make this pixel a background pixel.
90 if(i_src>=0 && i_src<ch->width) {
91 x = ch->bitmap[j_src*ch->rowspan + i_src/8];
92 if(x & (1<<(7-i_src%8))) {
93 is_fg = 1;
97 if(is_fg) {
98 u32 clr = fgcol;
100 de_bitmap_setpixel_rgba(img, xpos+i, ypos+j, clr);
106 // Paint a character at the given index in the given font, to the given bitmap.
107 void de_font_paint_character_idx(deark *c, de_bitmap *img,
108 struct de_bitmap_font *font, i64 char_idx,
109 i64 xpos, i64 ypos, de_color fgcol, de_color bgcol,
110 unsigned int flags)
112 struct de_bitmap_font_char *ch;
114 if(char_idx<0 || char_idx>=font->num_chars) return;
115 ch = &font->char_array[char_idx];
116 if(!is_valid_char(ch)) return;
117 if(ch->width > font->nominal_width) return;
118 if(ch->height > font->nominal_height) return;
120 // Paint a "canvas" for the char, if needed.
122 // If the "extraspace" feature is used, paint an additional canvas of
123 // a different color.
124 if(!(flags&DE_PAINTFLAG_TRNSBKGD) && (ch->extraspace_l || ch->extraspace_r)) {
125 i64 canvas_x, canvas_y;
126 i64 canvas_w, canvas_h;
128 canvas_x = xpos;
129 canvas_y = ypos+ch->v_offset;
130 canvas_w = (i64)ch->extraspace_l + (i64)ch->width + (i64)ch->extraspace_r;
131 canvas_h = ch->height;
132 // (We don't need to support both the "extraspace" and the VGA9COL
133 // feature at the same time.)
135 if(ch->width==0 || ch->height==0) {
136 // Bit of a hack. If we have a zero-size (spacer-only) character,
137 // paint the extraspace as the full height the cell.
138 // (In principle, maybe it should be painted this way for *all*
139 // characters, but I'm not sure how to make that look good.)
140 canvas_y = ypos;
141 canvas_h = font->nominal_height;
144 if(canvas_y + canvas_h > ypos + font->nominal_height) {
145 goto after_extraspace;
148 de_bitmap_rect(img, canvas_x, canvas_y,
149 canvas_w, canvas_h, DE_MAKE_RGB(192,255,192), 0);
152 after_extraspace:
154 if(ch->width<=0 || ch->height<=0) goto after_foreground; // Nothing to do
156 // Paint the canvas for the main part of the character.
157 if(!(flags&DE_PAINTFLAG_TRNSBKGD)) {
158 i64 canvas_x, canvas_y;
159 i64 canvas_w, canvas_h;
161 canvas_x = xpos + ch->extraspace_l;
162 canvas_y = ypos+ch->v_offset;
163 canvas_w = ch->width;
164 if((flags&DE_PAINTFLAG_VGA9COL) && ch->width==8) {
165 canvas_w++;
167 canvas_h = ch->height;
169 if(canvas_y + canvas_h > ypos + font->nominal_height) {
170 goto after_foreground;
172 de_bitmap_rect(img, canvas_x, canvas_y,
173 canvas_w, canvas_h, bgcol, 0);
176 // Paint the character foreground.
177 paint_character_internal(c, img, ch,
178 ch->extraspace_l + xpos, ch->v_offset + ypos, fgcol, flags);
180 after_foreground:
184 // Given a codepoint, returns the character index in the font.
185 // 'codepoint' is expected to be a Unicode codepoint. If the font does not
186 // have Unicode codepoints, the non-Unicode codepoint will be used instead.
187 // Returns -1 if not found.
188 static i64 get_char_idx_by_cp(deark *c, struct de_bitmap_font *font, i32 codepoint)
190 i64 i;
192 // TODO: Sometimes, a font has multiple characters that map to the same
193 // codepoint. We should have a way to find the *best* such character,
194 // which might not be the first one.
196 for(i=0; i<font->num_chars; i++) {
197 if(font->has_unicode_codepoints) {
198 if(font->char_array[i].codepoint_unicode == codepoint)
199 return i;
201 else {
202 if(font->char_array[i].codepoint_nonunicode == codepoint)
203 return i;
206 return -1;
209 // 'codepoint' is expected to be a Unicode codepoint. If the font does not
210 // have Unicode codepoints, the non-Unicode codepoint will be used instead.
211 void de_font_paint_character_cp(deark *c, de_bitmap *img,
212 struct de_bitmap_font *font, i32 codepoint,
213 i64 xpos, i64 ypos, de_color fgcol, de_color bgcol, unsigned int flags)
215 i64 char_idx;
217 char_idx = get_char_idx_by_cp(c, font, codepoint);
218 if(char_idx<0) {
219 if(font->index_of_replacement_char>=0) {
220 char_idx = font->index_of_replacement_char;
223 if(char_idx<0) {
224 char_idx = get_char_idx_by_cp(c, font, '?');
226 if(char_idx<0 || char_idx>=font->num_chars) {
227 return;
229 de_font_paint_character_idx(c, img, font, char_idx, xpos, ypos, fgcol, bgcol, flags);
232 struct dfont_char_data {
233 i32 codepoint_unicode;
234 u8 bitmap[7];
237 static const struct dfont_char_data dfont_data[16] = {
238 {48, {0x30,0x48,0x48,0x48,0x48,0x48,0x30}}, // 0
239 {49, {0x10,0x30,0x10,0x10,0x10,0x10,0x38}}, // 1
240 {50, {0x70,0x08,0x08,0x30,0x40,0x40,0x78}}, // 2
241 {51, {0x70,0x08,0x08,0x30,0x08,0x08,0x70}}, // 3
242 {52, {0x48,0x48,0x48,0x78,0x08,0x08,0x08}}, // 4
243 {53, {0x78,0x40,0x40,0x70,0x08,0x08,0x70}}, // 5
244 {54, {0x38,0x40,0x40,0x70,0x48,0x48,0x30}}, // 6
245 {55, {0x78,0x08,0x08,0x10,0x10,0x10,0x10}}, // 7
246 {56, {0x30,0x48,0x48,0x30,0x48,0x48,0x30}}, // 8
247 {57, {0x30,0x48,0x48,0x38,0x08,0x08,0x70}}, // 9
248 {65, {0x30,0x48,0x48,0x78,0x48,0x48,0x48}}, // A
249 {66, {0x70,0x48,0x48,0x70,0x48,0x48,0x70}}, // B
250 {67, {0x30,0x48,0x40,0x40,0x40,0x48,0x30}}, // C
251 {68, {0x70,0x58,0x48,0x48,0x48,0x58,0x70}}, // D
252 {69, {0x78,0x40,0x40,0x70,0x40,0x40,0x78}}, // E
253 {70, {0x78,0x40,0x40,0x70,0x40,0x40,0x40}} // F
256 static struct de_bitmap_font *make_digit_font(deark *c)
258 struct de_bitmap_font *dfont = NULL;
259 i64 i;
261 dfont = de_create_bitmap_font(c);
262 dfont->num_chars = 16;
263 dfont->nominal_width = 6;
264 dfont->nominal_height = 7;
265 dfont->has_unicode_codepoints = 1;
266 dfont->char_array = de_mallocarray(c, dfont->num_chars, sizeof(struct de_bitmap_font_char));
268 for(i=0; i<dfont->num_chars; i++) {
269 dfont->char_array[i].codepoint_unicode = dfont_data[i].codepoint_unicode;
270 dfont->char_array[i].width = dfont->nominal_width;
271 dfont->char_array[i].height = dfont->nominal_height;
272 dfont->char_array[i].rowspan = 1;
273 dfont->char_array[i].bitmap = (u8*)dfont_data[i].bitmap;
276 return dfont;
279 struct font_render_ctx {
280 struct de_bitmap_font *font;
281 i32 min_codepoint; // currently unused
282 i32 max_codepoint;
283 i64 num_valid_chars;
284 int render_as_unicode;
286 // Array of the actual codepoints we will use when dumping the font
287 // to an image. Size is font->num_chars.
288 i32 *codepoint_tmp;
291 #define DNFLAG_HEX 0x1
292 #define DNFLAG_LEADING_ZEROES 0x2
293 #define DNFLAG_HCENTER 0x4
295 // (xpos,ypos) is the lower-right corner
296 // (or the bottom-center, if hcenter==1).
297 static void draw_number(deark *c, de_bitmap *img,
298 struct de_bitmap_font *dfont, i64 n, i64 xpos1, i64 ypos1,
299 unsigned int flags)
301 char buf[32];
302 i64 len;
303 i64 i;
304 i64 xpos_start;
305 i64 xpos, ypos;
307 if(flags & DNFLAG_HEX) {
308 if(flags & DNFLAG_LEADING_ZEROES)
309 de_snprintf(buf, sizeof(buf), "%04X", (unsigned int)n);
310 else
311 de_snprintf(buf, sizeof(buf), "%X", (unsigned int)n);
313 else {
314 de_snprintf(buf, sizeof(buf), "%u", (unsigned int)n);
316 len = (i64)de_strlen(buf);
318 if(flags & DNFLAG_HCENTER)
319 xpos_start = xpos1-(dfont->nominal_width*len)/2;
320 else
321 xpos_start = xpos1-dfont->nominal_width*len;
323 // Make sure number doesn't go beyond the image
324 if(xpos_start + dfont->nominal_width*len > img->width) {
325 xpos_start = img->width - dfont->nominal_width*len;
328 for(i=len-1; i>=0; i--) {
329 xpos = xpos_start + dfont->nominal_width*i;
330 ypos = ypos1-dfont->nominal_height;
331 de_font_paint_character_cp(c, img, dfont, buf[i], xpos, ypos,
332 DE_MAKE_GRAY(255), 0, DE_PAINTFLAG_TRNSBKGD);
336 static void get_min_max_codepoint(struct font_render_ctx *fctx)
338 i64 i;
340 fctx->min_codepoint = 0x10ffff;
341 fctx->max_codepoint = 0;
342 fctx->num_valid_chars = 0;
344 for(i=0; i<fctx->font->num_chars; i++) {
345 if(!is_valid_char(&fctx->font->char_array[i])) continue;
346 if(fctx->codepoint_tmp[i] == DE_CODEPOINT_INVALID) continue;
347 fctx->num_valid_chars++;
348 if(fctx->codepoint_tmp[i] < fctx->min_codepoint)
349 fctx->min_codepoint = fctx->codepoint_tmp[i];
350 if(fctx->codepoint_tmp[i] > fctx->max_codepoint)
351 fctx->max_codepoint = fctx->codepoint_tmp[i];
355 // Put the actual codepont to use in the font->char_array[].codepoint_tmp field.
356 static void fixup_codepoints(deark *c, struct font_render_ctx *fctx)
358 i64 i;
359 i32 c1;
360 i64 num_uncoded_chars = 0;
361 u8 *used_codepoint_map = NULL;
362 u8 codepoint_already_used;
364 if(!fctx->render_as_unicode) {
365 for(i=0; i<fctx->font->num_chars; i++) {
366 fctx->codepoint_tmp[i] = fctx->font->char_array[i].codepoint_nonunicode;
368 goto done;
371 // An array of bits to remember if we've seen a codepoint before (BMP only).
372 // A character with a duplicate codepoint will be moved to another
373 // location, so that it doesn't get painted over the previous one.
374 used_codepoint_map = de_malloc(c, 65536/8);
376 for(i=0; i<fctx->font->num_chars; i++) {
377 if(!is_valid_char(&fctx->font->char_array[i])) continue;
378 c1 = fctx->font->char_array[i].codepoint_unicode;
380 codepoint_already_used = 0;
381 if(c1>=0 && c1<65536) {
382 // Check if we've seen this codepoint before.
383 codepoint_already_used = used_codepoint_map[c1/8] & (1<<(c1%8));
385 // Remember that we've seen this codepoint.
386 used_codepoint_map[c1/8] |= 1<<(c1%8);
389 if(codepoint_already_used || c1==DE_CODEPOINT_INVALID) {
390 if(codepoint_already_used) {
391 de_dbg2(c, "moving duplicate codepoint U+%04x at index %d to private use area",
392 (unsigned int)c1, (int)i);
394 // Move uncoded characters to a Private Use area.
395 // (Supplementary Private Use Area-A = U+F0000 - U+FFFFD)
396 if(DE_CODEPOINT_MOVED + num_uncoded_chars <= DE_CODEPOINT_MOVED_MAX) {
397 fctx->codepoint_tmp[i] = (i32)(DE_CODEPOINT_MOVED + num_uncoded_chars);
398 num_uncoded_chars++;
401 else {
402 fctx->codepoint_tmp[i] = c1;
406 done:
407 de_free(c, used_codepoint_map);
410 struct row_info_struct {
411 u8 is_visible;
412 i64 display_pos;
415 struct col_info_struct {
416 i64 display_width;
417 i64 display_pos;
420 static void checkerboard_bkgd(de_bitmap *img, i64 xpos, i64 ypos, i64 w, i64 h)
422 i64 ii, jj;
424 for(jj=0; jj<h; jj++) {
425 for(ii=0; ii<w; ii++) {
426 de_bitmap_setpixel_gray(img, xpos+ii, ypos+jj, (ii/2+jj/2)%2 ? 176 : 192);
431 void de_font_bitmap_font_to_image(deark *c, struct de_bitmap_font *font1, de_finfo *fi,
432 unsigned int createflags)
434 struct font_render_ctx *fctx = NULL;
435 i64 i, j, k;
436 de_bitmap *img = NULL;
437 i64 xpos, ypos;
438 i64 img_leftmargin, img_topmargin;
439 i64 img_rightmargin, img_bottommargin;
440 i64 img_vpixelsperchar;
441 i64 img_width, img_height;
442 i64 num_table_rows_to_display;
443 i64 num_table_rows_total;
444 i64 last_valid_row;
445 struct de_bitmap_font *dfont = NULL;
446 i64 chars_per_row = 32;
447 const char *s;
448 struct row_info_struct *row_info = NULL;
449 struct col_info_struct *col_info = NULL;
450 int unicode_req = 0;
451 i64 label_stride;
452 i64 rownum, colnum;
453 i64 curpos;
454 unsigned int dnflags;
456 fctx = de_malloc(c, sizeof(struct font_render_ctx));
457 fctx->font = font1;
459 if(fctx->font->num_chars<1) goto done;
460 if(fctx->font->num_chars>17*65536) goto done;
461 if(fctx->font->nominal_width>512 || fctx->font->nominal_height>512) {
462 de_err(c, "Font size too big (%d"DE_CHAR_TIMES"%d). Not supported.",
463 (int)fctx->font->nominal_width, (int)fctx->font->nominal_height);
464 goto done;
467 // -1 = "no preference"
468 unicode_req = de_get_ext_option_bool(c, "font:tounicode", -1);
470 if(unicode_req==0 &&
471 (fctx->font->has_nonunicode_codepoints || !fctx->font->has_unicode_codepoints))
473 ; // Render as nonunicode.
475 else if(fctx->font->has_unicode_codepoints &&
476 (unicode_req>0 || fctx->font->prefer_unicode || !fctx->font->has_nonunicode_codepoints))
478 fctx->render_as_unicode = 1;
481 s = de_get_ext_option(c, "font:charsperrow");
482 if(s) {
483 chars_per_row = de_atoi64(s);
484 if(chars_per_row<1) chars_per_row=1;
487 dfont = make_digit_font(c);
489 fctx->codepoint_tmp = de_mallocarray(c, fctx->font->num_chars, sizeof(i32));
490 fixup_codepoints(c, fctx);
492 get_min_max_codepoint(fctx);
493 if(fctx->num_valid_chars<1) goto done;
494 num_table_rows_total = fctx->max_codepoint/chars_per_row+1;
496 // TODO: Clean up these margin calculations, and make it more general.
497 if(fctx->render_as_unicode) {
498 img_leftmargin = (i64)dfont->nominal_width * 5 + 6;
500 else {
501 if(fctx->max_codepoint >= 1000)
502 img_leftmargin = (i64)dfont->nominal_width * 5 + 6;
503 else
504 img_leftmargin = (i64)dfont->nominal_width * 3 + 6;
506 img_topmargin = (i64)dfont->nominal_height + 6;
507 img_rightmargin = 1;
508 img_bottommargin = 1;
510 // Scan the characters, and record relevant information.
511 row_info = de_mallocarray(c, num_table_rows_total, sizeof(struct row_info_struct));
512 col_info = de_mallocarray(c, chars_per_row, sizeof(struct col_info_struct));
513 for(i=0; i<chars_per_row; i++) {
514 #define MIN_CHAR_CELL_WIDTH 5
515 col_info[i].display_width = MIN_CHAR_CELL_WIDTH;
518 for(k=0; k<fctx->font->num_chars; k++) {
519 i64 char_display_width;
521 if(fctx->codepoint_tmp[k] == DE_CODEPOINT_INVALID) continue;
522 if(!is_valid_char(&fctx->font->char_array[k])) continue;
523 rownum = fctx->codepoint_tmp[k] / chars_per_row;
524 colnum = fctx->codepoint_tmp[k] % chars_per_row;
525 if(rownum<0 || rownum>=num_table_rows_total) {
526 de_internal_err_fatal(c, "bad rownum");
529 // Remember that there is at least one valid character in this character's row.
530 row_info[rownum].is_visible = 1;
532 // Track the maximum width of any character in this character's column.
533 char_display_width = (i64)fctx->font->char_array[k].width +
534 (i64)fctx->font->char_array[k].extraspace_l +
535 (i64)fctx->font->char_array[k].extraspace_r;
536 if(char_display_width > col_info[colnum].display_width) {
537 col_info[colnum].display_width = char_display_width;
541 img_vpixelsperchar = (i64)fctx->font->nominal_height + 1;
543 // Figure out how many rows are used, and where to draw them.
544 num_table_rows_to_display = 0;
545 last_valid_row = -1;
546 curpos = img_topmargin;
547 for(j=0; j<num_table_rows_total; j++) {
548 if(!row_info[j].is_visible) continue;
550 // If we skipped one or more rows, leave some extra vertical space.
551 if(num_table_rows_to_display>0 && !row_info[j-1].is_visible) curpos+=3;
553 last_valid_row = j;
554 row_info[j].display_pos = curpos;
555 curpos += img_vpixelsperchar;
556 num_table_rows_to_display++;
558 if(num_table_rows_to_display<1) goto done;
560 // Figure out the positions of the columns.
561 curpos = img_leftmargin;
562 for(i=0; i<chars_per_row; i++) {
563 col_info[i].display_pos = curpos;
564 curpos += col_info[i].display_width + 1;
567 img_width = col_info[chars_per_row-1].display_pos +
568 col_info[chars_per_row-1].display_width + img_rightmargin;
569 img_height = row_info[last_valid_row].display_pos +
570 img_vpixelsperchar -1 + img_bottommargin;
572 img = de_bitmap_create(c, img_width, img_height, 3);
574 // Clear the image
575 de_bitmap_rect(img, 0, 0, img->width, img->height, DE_MAKE_RGB(32,32,144), 0);
577 // Draw/clear the cell backgrounds
578 for(j=0; j<num_table_rows_total; j++) {
579 if(!row_info[j].is_visible) continue;
580 ypos = row_info[j].display_pos;
582 for(i=0; i<chars_per_row; i++) {
583 xpos = col_info[i].display_pos;
584 de_bitmap_rect(img, xpos, ypos,
585 col_info[i].display_width, img_vpixelsperchar-1,
586 DE_MAKE_RGB(112,112,160), 0);
590 // Draw the labels in the top margin.
592 // TODO: Better label spacing logic.
593 if(fctx->font->nominal_width <= 12)
594 label_stride = 2;
595 else
596 label_stride = 1;
598 for(i=0; i<chars_per_row; i++) {
599 if(i%label_stride != 0) continue;
600 xpos = col_info[i].display_pos + col_info[i].display_width/2;
601 ypos = img_topmargin - 3;
603 dnflags = DNFLAG_HCENTER;
604 if(fctx->render_as_unicode) dnflags |= DNFLAG_HEX;
606 draw_number(c, img, dfont, i, xpos, ypos, dnflags);
609 // Draw the labels in the left margin.
610 for(j=0; j<num_table_rows_total; j++) {
611 if(!row_info[j].is_visible) continue;
612 xpos = img_leftmargin - 3;
613 ypos = row_info[j].display_pos + (img_vpixelsperchar + dfont->nominal_height + 1)/2;
615 dnflags = 0;
616 if(fctx->render_as_unicode) dnflags |= DNFLAG_HEX | DNFLAG_LEADING_ZEROES;
618 draw_number(c, img, dfont, j*chars_per_row, xpos, ypos, dnflags);
621 // Render the glyphs.
622 for(k=0; k<fctx->font->num_chars; k++) {
623 if(fctx->codepoint_tmp[k] == DE_CODEPOINT_INVALID) continue;
624 if(!is_valid_char(&fctx->font->char_array[k])) continue;
626 rownum = fctx->codepoint_tmp[k] / chars_per_row;
627 colnum = fctx->codepoint_tmp[k] % chars_per_row;
628 if(rownum<0 || rownum>=num_table_rows_total) {
629 de_internal_err_fatal(c, "bad rownum");
632 xpos = col_info[colnum].display_pos;
633 ypos = row_info[rownum].display_pos;
635 checkerboard_bkgd(img, xpos, ypos,
636 col_info[colnum].display_width, img_vpixelsperchar-1);
638 de_font_paint_character_idx(c, img, fctx->font, k, xpos, ypos,
639 DE_STOCKCOLOR_BLACK, DE_STOCKCOLOR_WHITE, 0);
642 de_bitmap_write_to_file_finfo(img, fi, createflags);
644 done:
645 if(dfont) {
646 de_free(c, dfont->char_array);
647 de_destroy_bitmap_font(c, dfont);
649 de_bitmap_destroy(img);
650 de_free(c, row_info);
651 de_free(c, col_info);
652 if(fctx) {
653 de_free(c, fctx->codepoint_tmp);
654 de_free(c, fctx);
658 // Do we recognize the font as a standard VGA CP437 font?
659 // This function is quick and dirty. Ideally we would:
660 // * look at each character, instead or requiring the whole font to be identical
661 // * recognize fonts with other character sets
662 int de_font_is_standard_vga_font(deark *c, u32 crc)
664 switch(crc) {
665 case 0x2c3cf7d2U: // e.g.: ndh - Ada.xb
666 case 0x3c0aa3eeU: // https://commons.wikimedia.org/w/index.php?title=File:Codepage-437.png&oldid=153353189
667 case 0x71e15998U: // Used in many XBIN files.
668 case 0xb6133c6eU: // blocktronics_baud_dudes/k1-strax.xb (8x14)
669 case 0xb7cb6e5cU: // e.g.: T1-XBIN.XB
670 return 1;
672 return 0;