1 // This file is part of Deark.
2 // Copyright (C) 2016 Jason Summers
3 // See the file COPYING for terms of use.
7 // Functions related to character graphics.
9 #define DE_NOT_IN_MODULE
10 #include "deark-config.h"
11 #include "deark-private.h"
21 u8 vga_9col_mode
; // Flag: Render an extra column, like VGA does
29 struct de_bitmap_font
*standard_font
;
30 struct de_bitmap_font
*font_to_use
;
32 i64 char_width_in_pixels
;
33 i64 char_height_in_pixels
;
35 struct screen_stats
*scrstats
; // pointer to array of struct screen_stats
38 struct de_char_context
*de_create_charctx(deark
*c
, unsigned int flags
)
40 struct de_char_context
*charctx
;
42 charctx
= de_malloc(c
, sizeof(struct de_char_context
));
46 // Free the ->screens data, assuming it has been allocated in a particular way.
47 void de_free_charctx_screens(deark
*c
, struct de_char_context
*charctx
)
52 if(!charctx
|| !charctx
->screens
) return;
54 for(pgnum
=0; pgnum
<charctx
->nscreens
; pgnum
++) {
55 if(charctx
->screens
[pgnum
]) {
56 if(charctx
->screens
[pgnum
]->cell_rows
) {
57 for(j
=0; j
<charctx
->screens
[pgnum
]->height
; j
++) {
58 de_free(c
, charctx
->screens
[pgnum
]->cell_rows
[j
]);
60 de_free(c
, charctx
->screens
[pgnum
]->cell_rows
);
62 de_free(c
, charctx
->screens
[pgnum
]);
65 de_free(c
, charctx
->screens
);
66 charctx
->screens
= NULL
;
69 // Frees a charctx struct.
70 // Does not free charctx->font.
71 // Does not free the ucstring fields.
72 void de_destroy_charctx(deark
*c
, struct de_char_context
*charctx
)
76 if(charctx
->screens
) {
77 de_internal_err_fatal(c
, "charctx");
82 // Deprecated in favor of de_free_charctx_screens() (if applicable),
83 // followed by de_destroy_charctx().
84 void de_free_charctx(deark
*c
, struct de_char_context
*charctx
)
87 de_free_charctx_screens(c
, charctx
);
92 static void do_prescan_screen(deark
*c
, struct de_char_context
*charctx
,
93 struct charextractx
*ectx
, i64 screen_idx
)
95 const struct de_char_cell
*cell
;
97 struct de_char_screen
*screen
;
98 u32 highest_fgcol_count
;
99 u32 highest_bgcol_count
;
101 screen
= charctx
->screens
[screen_idx
];
103 for(j
=0; j
<screen
->height
; j
++) {
104 for(i
=0; i
<screen
->width
; i
++) {
105 if(!screen
->cell_rows
|| !screen
->cell_rows
[j
]) continue;
106 cell
= &screen
->cell_rows
[j
][i
];
109 if(DE_IS_PAL_COLOR(cell
->fgcol
)) {
110 ectx
->used_fgcol
[cell
->fgcol
] = 1;
111 ectx
->scrstats
[screen_idx
].fgcol_count
[cell
->fgcol
]++;
114 ectx
->used_24bitcolor
= 1;
116 if(DE_IS_PAL_COLOR(cell
->bgcol
)) {
117 ectx
->used_bgcol
[cell
->bgcol
] = 1;
118 ectx
->scrstats
[screen_idx
].bgcol_count
[cell
->bgcol
]++;
121 ectx
->used_24bitcolor
= 1;
123 if(cell
->underline
) ectx
->used_underline
= 1;
124 if(cell
->strikethru
) ectx
->used_strikethru
= 1;
125 if(cell
->blink
) ectx
->used_blink
= 1;
129 // Find the most-used foreground and background (palette) colors
130 highest_fgcol_count
= ectx
->scrstats
[screen_idx
].fgcol_count
[0];
131 highest_bgcol_count
= ectx
->scrstats
[screen_idx
].bgcol_count
[0];
132 ectx
->scrstats
->most_used_fgcol
= 0;
133 ectx
->scrstats
->most_used_bgcol
= 0;
135 for(i
=1; i
<16; i
++) {
136 if(ectx
->scrstats
[screen_idx
].fgcol_count
[i
] > highest_fgcol_count
) {
137 highest_fgcol_count
= ectx
->scrstats
[screen_idx
].fgcol_count
[i
];
138 ectx
->scrstats
->most_used_fgcol
= (u32
)i
;
140 if(ectx
->scrstats
[screen_idx
].bgcol_count
[i
] > highest_bgcol_count
) {
141 highest_bgcol_count
= ectx
->scrstats
[screen_idx
].bgcol_count
[i
];
142 ectx
->scrstats
->most_used_bgcol
= (u32
)i
;
155 // This may modify sp->is_suppressed.
156 static void span_open(deark
*c
, dbuf
*ofile
, struct span_info
*sp
,
157 const struct screen_stats
*scrstats
)
159 int need_fgcol_attr
, need_bgcol_attr
;
160 int need_underline
, need_strikethru
, need_blink
;
163 int fgcol_is_24bit
, bgcol_is_24bit
;
166 fgcol_is_24bit
= !DE_IS_PAL_COLOR(sp
->fgcol
);
167 bgcol_is_24bit
= !DE_IS_PAL_COLOR(sp
->bgcol
);
169 need_fgcol_attr
= !scrstats
|| sp
->fgcol
!=scrstats
->most_used_fgcol
;
170 need_bgcol_attr
= !scrstats
|| sp
->bgcol
!=scrstats
->most_used_bgcol
;
171 if(fgcol_is_24bit
) { need_fgcol_attr
=0; need_style
=1; }
172 if(bgcol_is_24bit
) { need_bgcol_attr
=0; need_style
=1; }
173 need_underline
= (sp
->underline
!=0);
174 need_strikethru
= (sp
->strikethru
!=0);
175 need_blink
= (sp
->blink
!=0);
177 attrcount
= need_fgcol_attr
+ need_bgcol_attr
+ need_underline
+
178 need_strikethru
+ need_blink
;
179 if(attrcount
==0 && !need_style
) {
180 sp
->is_suppressed
= 1;
184 sp
->is_suppressed
= 0;
186 dbuf_puts(ofile
, "<span");
191 dbuf_puts(ofile
, " class=");
192 if(attrcount
>1) // Don't need quotes if there's only one attribute
193 dbuf_puts(ofile
, "\"");
195 // Classes for foreground and background colors
197 if(need_fgcol_attr
) {
198 dbuf_printf(ofile
, "f%c", de_get_hexchar(sp
->fgcol
));
202 if(need_bgcol_attr
) {
203 if(attrindex
) dbuf_puts(ofile
, " ");
204 dbuf_printf(ofile
, "b%c", de_get_hexchar(sp
->bgcol
));
211 if(attrindex
) dbuf_puts(ofile
, " ");
212 dbuf_puts(ofile
, "u");
216 if(attrindex
) dbuf_puts(ofile
, " ");
217 dbuf_puts(ofile
, "s");
221 if(attrindex
) dbuf_puts(ofile
, " ");
222 dbuf_puts(ofile
, "blink");
227 dbuf_puts(ofile
, "\"");
230 if(fgcol_is_24bit
|| bgcol_is_24bit
) {
233 dbuf_puts(ofile
, " style=\"");
235 de_color_to_css(sp
->fgcol
, tmpbuf
, sizeof(tmpbuf
));
236 dbuf_printf(ofile
, "color:%s", tmpbuf
);
241 dbuf_puts(ofile
, ";");
242 de_color_to_css(sp
->bgcol
, tmpbuf
, sizeof(tmpbuf
));
243 dbuf_printf(ofile
, "background-color:%s", tmpbuf
);
245 dbuf_puts(ofile
, "\"");
248 dbuf_puts(ofile
, ">");
252 static void span_close(deark
*c
, dbuf
*ofile
, struct span_info
*sp
)
254 if(sp
->is_suppressed
) return;
255 dbuf_puts(ofile
, "</span>");
258 static void do_output_html_screen(deark
*c
, struct de_char_context
*charctx
,
259 struct charextractx
*ectx
, i64 screen_idx
, dbuf
*ofile
)
261 const struct de_char_cell
*cell
;
262 struct de_char_cell blank_cell
;
263 struct de_char_screen
*screen
;
267 int need_newline
= 0;
268 u32 active_fgcol
= 0;
269 u32 active_bgcol
= 0;
270 u8 active_underline
= 0;
271 u8 active_strikethru
= 0;
274 struct span_info default_span
;
275 struct span_info cur_span
;
277 de_zeromem(&default_span
, sizeof(struct span_info
));
278 de_zeromem(&cur_span
, sizeof(struct span_info
));
280 screen
= charctx
->screens
[screen_idx
];
282 // In case a cell is missing, we'll use this one:
283 de_zeromem(&blank_cell
, sizeof(struct de_char_cell
));
284 blank_cell
.codepoint
= 32;
285 blank_cell
.codepoint_unicode
= 32;
287 dbuf_puts(ofile
, "<table class=mt><tr>\n<td>");
288 dbuf_puts(ofile
, "<pre>");
290 // Containing <span> with default colors.
291 default_span
.fgcol
= ectx
->scrstats
[screen_idx
].most_used_fgcol
;
292 default_span
.bgcol
= ectx
->scrstats
[screen_idx
].most_used_bgcol
;
293 span_open(c
, ofile
, &default_span
, NULL
);
295 for(j
=0; j
<screen
->height
; j
++) {
296 for(i
=0; i
<screen
->width
; i
++) {
297 if(!screen
->cell_rows
|| !screen
->cell_rows
[j
]) {
301 cell
= &screen
->cell_rows
[j
][i
];
302 if(!cell
) cell
= &blank_cell
;
305 n
= cell
->codepoint_unicode
;
307 if((cell
->size_flags
&DE_PAINTFLAG_RIGHTHALF
) ||
308 (cell
->size_flags
&DE_PAINTFLAG_BOTTOMHALF
))
310 // We don't support double-size characters with HTML output.
311 // Make the left / bottom parts of the cell blank so we don't
312 // duplicate the foreground character.
318 is_blank_char
= (n
==0x20 || n
==0xa0) &&
319 !cell
->underline
&& !cell
->strikethru
;
322 // If we're in an incompatible 'span' context, close it.
324 // Optimization: If this is a blank character, ignore a foreground color
325 // mismatch, because it won't be visible anyway. (Many other similar
326 // optimizations are also possible, but that could get very complex.)
327 if((cell
->fgcol
!=active_fgcol
&& !is_blank_char
) ||
328 cell
->bgcol
!=active_bgcol
||
329 cell
->underline
!=active_underline
||
330 cell
->strikethru
!=active_strikethru
||
331 cell
->blink
!=active_blink
)
333 span_close(c
, ofile
, &cur_span
);
340 dbuf_puts(ofile
, "\n");
344 cur_span
.fgcol
= cell
->fgcol
;
345 cur_span
.bgcol
= cell
->bgcol
;
346 cur_span
.underline
= cell
->underline
;
347 cur_span
.strikethru
= cell
->strikethru
;
348 cur_span
.blink
= cell
->blink
;
349 span_open(c
, ofile
, &cur_span
, &ectx
->scrstats
[screen_idx
]);
352 active_fgcol
= cell
->fgcol
;
353 active_bgcol
= cell
->bgcol
;
354 active_underline
= cell
->underline
;
355 active_strikethru
= cell
->strikethru
;
356 active_blink
= cell
->blink
;
360 dbuf_puts(ofile
, "\n");
364 de_write_codepoint_to_html(c
, ofile
, n
);
367 // Defer emitting a newline, so that we have more control over where
368 // to put it. We prefer to put it after "</span>".
373 span_close(c
, ofile
, &cur_span
);
376 // Close containing <span>
377 span_close(c
, ofile
, &default_span
);
379 dbuf_puts(ofile
, "</pre>");
380 dbuf_puts(ofile
, "</td>\n</tr></table>\n");
383 static void output_css_color_block(deark
*c
, dbuf
*ofile
, u32
*pal
,
384 const char *selectorprefix
, const char *prop
, const u8
*used_flags
)
389 for(i
=0; i
<16; i
++) {
390 if(!used_flags
[i
]) continue;
391 de_color_to_css(pal
[i
], tmpbuf
, sizeof(tmpbuf
));
392 dbuf_printf(ofile
, " %s%c { %s: %s }\n", selectorprefix
, de_get_hexchar(i
),
397 static void write_ucstring_to_html(deark
*c
, const de_ucstring
*s
, dbuf
*f
)
400 i64 len_at_last_linebreak
;
406 len_at_last_linebreak
= f
->len
;
408 for(i
=0; i
<s
->len
; i
++) {
411 // Don't let HTML collapse consecutive spaces
412 // (this is not ideal, but it's better than nothing).
424 dbuf_puts(f
, "<br>\n");
425 len_at_last_linebreak
= f
->len
;
427 else if(ch
==0x20 && (f
->len
- len_at_last_linebreak
> 70)) {
428 // Low-quality attempt at word wrapping
430 len_at_last_linebreak
= f
->len
;
433 de_write_codepoint_to_html(c
, f
, ch
);
438 static void print_header_item(deark
*c
, dbuf
*ofile
, const char *name_rawhtml
, const de_ucstring
*value
)
442 dbuf_puts(ofile
, "<td class=htc>");
443 if(ucstring_isnonempty(value
)) {
444 dbuf_printf(ofile
, "<span class=hn>%s: </span><span class=hv>", name_rawhtml
);
445 write_ucstring_to_html(c
, value
, ofile
);
446 dbuf_puts(ofile
, "</span>");
450 for(k
=0; k
<20; k
++) {
451 de_write_codepoint_to_html(c
, ofile
, 0x00a0); // nbsp
454 dbuf_puts(ofile
, "</td>\n");
457 static void print_comments(deark
*c
, struct de_char_context
*charctx
, dbuf
*ofile
)
459 if(ucstring_isempty(charctx
->comment
)) return;
460 dbuf_puts(ofile
, "<table class=htt><tr>\n");
461 dbuf_puts(ofile
, "<td class=hcth>");
462 dbuf_printf(ofile
, "<span class=hn>Comments:</span>");
463 dbuf_puts(ofile
, "</td>\n");
465 dbuf_puts(ofile
, "<td class=hctc>");
467 dbuf_puts(ofile
, "<span class=hv>");
468 // TODO: Some line breaks would be good.
469 write_ucstring_to_html(c
, charctx
->comment
, ofile
);
470 dbuf_puts(ofile
, "</span>");
472 dbuf_puts(ofile
, "</td>\n");
474 dbuf_puts(ofile
, "</tr></table>\n");
477 static void timestamp_to_ucstring(const struct de_timestamp
*ts
, de_ucstring
*s
)
479 char timestamp_buf
[64];
480 de_timestamp_to_string(ts
, timestamp_buf
, sizeof(timestamp_buf
), 0);
481 ucstring_append_sz(s
, timestamp_buf
, DE_ENCODING_UTF8
);
484 static void do_output_html_header(deark
*c
, struct de_char_context
*charctx
,
485 struct charextractx
*ectx
, dbuf
*ofile
)
487 int has_metadata
; // metadata other than comments
489 has_metadata
= charctx
->title
|| charctx
->artist
|| charctx
->organization
||
490 (charctx
->creation_date
.is_valid
);
491 if(c
->write_bom
&& !c
->ascii_html
) dbuf_write_uchar_as_utf8(ofile
, 0xfeff);
492 dbuf_puts(ofile
, "<!DOCTYPE html>\n");
493 dbuf_puts(ofile
, "<html>\n");
494 dbuf_puts(ofile
, "<head>\n");
495 dbuf_printf(ofile
, "<meta charset=\"%s\">\n", c
->ascii_html
?"US-ASCII":"UTF-8");
496 dbuf_puts(ofile
, "<title>");
497 write_ucstring_to_html(c
, charctx
->title
, ofile
);
498 dbuf_puts(ofile
, "</title>\n");
500 dbuf_puts(ofile
, "<style type=\"text/css\">\n");
502 dbuf_puts(ofile
, " body { background-color: #222; background-image: url(\"data:image/png;base64,"
503 "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAQMAAAAlPW0iAAAABlBMVEUgICAoKCidji3LAAAAMUlE"
504 "QVQI12NgaGBgPMDA/ICB/QMD/w8G+T8M9v8Y6v8z/P8PIoFsoAhQHCgLVMN4AACOoBFvDLHV4QAA"
505 "AABJRU5ErkJggg==\") }\n");
507 // The table for the main graphics
508 dbuf_puts(ofile
, " .mt { margin-left: auto; margin-right: auto }\n");
510 if(has_metadata
|| charctx
->comment
) {
511 // Styles for header name and value
512 // Header table <table> styles
513 dbuf_puts(ofile
, " .htt { width: 100%; border-collapse: collapse; background-color: #034 }\n");
514 // Header table cell <td> styles
515 dbuf_puts(ofile
, " .htc { border: 2px solid #056; text-align: center }\n");
516 if(charctx
->comment
) {
517 // Comment table cell <td> styles
518 dbuf_puts(ofile
, " .hcth { border: 2px solid #056; padding-left: 0.5em; "
519 "padding-right: 0.5em; width: 1px }\n");
520 dbuf_puts(ofile
, " .hctc { border: 2px solid #056; padding-left: 0.5em }\n");
522 // Header name styles
523 dbuf_puts(ofile
, " .hn { color: #aaa; font-style: italic }\n");
524 // Header value styles
525 dbuf_puts(ofile
, " .hv { color: #fff }\n");
528 output_css_color_block(c
, ofile
, charctx
->pal
, ".f", "color", &ectx
->used_fgcol
[0]);
529 output_css_color_block(c
, ofile
, charctx
->pal
, ".b", "background-color", &ectx
->used_bgcol
[0]);
531 if(ectx
->used_underline
) {
532 dbuf_puts(ofile
, " .u { text-decoration: underline }\n");
534 if(ectx
->used_strikethru
) {
535 dbuf_puts(ofile
, " .s { text-decoration: line-through }\n");
538 if(ectx
->used_blink
) {
539 dbuf_puts(ofile
, " .blink {\n"
540 " animation: blink 1s steps(1) infinite;\n"
541 " -webkit-animation: blink 1s steps(1) infinite }\n"
542 " @keyframes blink { 50% { color: transparent } }\n"
543 " @-webkit-keyframes blink { 50% { color: transparent } }\n");
545 dbuf_puts(ofile
, "</style>\n");
547 dbuf_puts(ofile
, "</head>\n");
548 dbuf_puts(ofile
, "<body>\n");
551 de_ucstring
*tmps
= NULL
;
552 dbuf_puts(ofile
, "<table class=htt><tr>\n");
553 print_header_item(c
, ofile
, "Title", charctx
->title
);
554 print_header_item(c
, ofile
, "Organization", charctx
->organization
);
555 print_header_item(c
, ofile
, "Artist", charctx
->artist
);
556 tmps
= ucstring_create(c
);
557 if(charctx
->creation_date
.is_valid
) {
558 timestamp_to_ucstring(&charctx
->creation_date
, tmps
);
560 print_header_item(c
, ofile
, "Date", tmps
);
561 ucstring_destroy(tmps
);
562 dbuf_puts(ofile
, "</tr></table>\n");
565 print_comments(c
, charctx
, ofile
);
568 static void do_output_html_footer(deark
*c
, struct de_char_context
*charctx
,
569 struct charextractx
*ectx
, dbuf
*ofile
)
571 dbuf_puts(ofile
, "</body>\n</html>\n");
574 static void de_char_output_to_html_file(deark
*c
, struct de_char_context
*charctx
,
575 struct charextractx
*ectx
)
580 if(charctx
->font
&& !charctx
->suppress_custom_font_warning
) {
581 de_warn(c
, "This file uses a custom font, which is not supported with "
585 if(ectx
->used_24bitcolor
) {
586 de_info(c
, "Note: This file uses 24-bit colors, which are supported but "
587 "not optimized. The HTML file may be very large.");
590 ofile
= dbuf_create_output_file(c
, "html", NULL
, 0);
592 do_output_html_header(c
, charctx
, ectx
, ofile
);
593 for(i
=0; i
<charctx
->nscreens
; i
++) {
594 do_output_html_screen(c
, charctx
, ectx
, i
, ofile
);
596 do_output_html_footer(c
, charctx
, ectx
, ofile
);
601 static void do_render_character(deark
*c
, struct de_char_context
*charctx
,
602 struct charextractx
*ectx
, de_bitmap
*img
,
604 i32 codepoint
, int codepoint_is_unicode
,
605 u32 fgcol
, u32 bgcol
,
606 unsigned int extra_flags
)
608 i64 xpos_in_pix
, ypos_in_pix
;
609 u32 fgcol_rgb
, bgcol_rgb
;
612 xpos_in_pix
= xpos
* ectx
->char_width_in_pixels
;
613 ypos_in_pix
= ypos
* ectx
->char_height_in_pixels
;
615 if(DE_IS_PAL_COLOR(fgcol
))
616 fgcol_rgb
= charctx
->pal
[fgcol
];
619 if(DE_IS_PAL_COLOR(bgcol
))
620 bgcol_rgb
= charctx
->pal
[bgcol
];
625 if(ectx
->vga_9col_mode
) flags
|= DE_PAINTFLAG_VGA9COL
;
627 if(codepoint_is_unicode
) {
628 de_font_paint_character_cp(c
, img
, ectx
->font_to_use
, codepoint
,
629 xpos_in_pix
, ypos_in_pix
, fgcol_rgb
, bgcol_rgb
, flags
);
632 de_font_paint_character_idx(c
, img
, ectx
->font_to_use
, (i64
)codepoint
,
633 xpos_in_pix
, ypos_in_pix
, fgcol_rgb
, bgcol_rgb
, flags
);
637 static void set_density(deark
*c
, struct de_char_context
*charctx
,
638 struct charextractx
*ectx
, de_finfo
*fi
)
640 // FIXME: This is quick and dirty. Need to put more thought into how to
641 // figure out the pixel density.
643 if(charctx
->no_density
) return;
645 if(ectx
->char_height_in_pixels
==16 && ectx
->char_width_in_pixels
==8) {
646 // Assume the intended display is 640x400.
647 fi
->density
.code
= DE_DENSITY_UNK_UNITS
;
648 fi
->density
.xdens
= 480.0;
649 fi
->density
.ydens
= 400.0;
651 else if(ectx
->char_height_in_pixels
==16 && ectx
->char_width_in_pixels
==9) {
652 // Assume the intended display is 720x400.
653 fi
->density
.code
= DE_DENSITY_UNK_UNITS
;
654 fi
->density
.xdens
= 540.0;
655 fi
->density
.ydens
= 400.0;
659 static void de_char_output_screen_to_image_file(deark
*c
, struct de_char_context
*charctx
,
660 struct charextractx
*ectx
, struct de_char_screen
*screen
)
662 i64 screen_width_in_pixels
, screen_height_in_pixels
;
663 de_bitmap
*img
= NULL
;
666 const struct de_char_cell
*cell
;
669 screen_width_in_pixels
= screen
->width
* ectx
->char_width_in_pixels
;
670 screen_height_in_pixels
= screen
->height
* ectx
->char_height_in_pixels
;
672 if(!de_good_image_dimensions(c
, screen_width_in_pixels
, screen_height_in_pixels
)) goto done
;
674 img
= de_bitmap_create(c
, screen_width_in_pixels
, screen_height_in_pixels
, 3);
676 fi
= de_finfo_create(c
);
677 set_density(c
, charctx
, ectx
, fi
);
679 if(charctx
->creation_date
.is_valid
) {
680 // The ->creation_date field is most likely from a SAUCE record, for which the
681 // only date field is documented as "The date the file was created".
682 // We intentionally treat it as a last-modified timestamp.
683 fi
->internal_mod_time
= charctx
->creation_date
;
686 for(j
=0; j
<screen
->height
; j
++) {
687 for(i
=0; i
<screen
->width
; i
++) {
688 if(!screen
->cell_rows
[j
]) continue;
689 cell
= &screen
->cell_rows
[j
][i
];
692 flags
= cell
->size_flags
;
694 do_render_character(c
, charctx
, ectx
, img
, i
, j
,
695 ectx
->uses_custom_font
? cell
->codepoint
: cell
->codepoint_unicode
,
696 ectx
->uses_custom_font
? 0 : 1,
697 cell
->fgcol
, cell
->bgcol
, flags
);
699 // TODO: It might be better to draw our own underline and/or
700 // strikethru marks, rather than relying on font glyphs that
701 // might be customized or otherwise sub-optimal.
702 if(cell
->underline
) {
703 do_render_character(c
, charctx
, ectx
, img
, i
, j
,
704 0x5f, 1, cell
->fgcol
, cell
->bgcol
, flags
|DE_PAINTFLAG_TRNSBKGD
);
706 if(cell
->strikethru
) {
707 do_render_character(c
, charctx
, ectx
, img
, i
, j
,
708 0x2d, 1, cell
->fgcol
, cell
->bgcol
, flags
|DE_PAINTFLAG_TRNSBKGD
);
713 de_bitmap_write_to_file_finfo(img
, fi
, 0);
715 de_bitmap_destroy(img
);
716 de_finfo_destroy(c
, fi
);
719 #define NUM_EXTRA_FONT_CHARS 13
720 static const u8 extra_font_data
[NUM_EXTRA_FONT_CHARS
*16] = {
721 0,0,0,126,66,66,66,66,66,66,66,126,0,0,0,0, // replacement char
722 0,0,0,0,16,56,124,254,124,56,16,0,0,0,0,0, // 25c6 diamond
723 0,0,216,216,248,216,216,0,30,12,12,12,12,0,0,0, // 2409 "HT"
724 0,0,248,192,240,192,192,0,62,48,60,48,48,0,0,0, // 240c "FF"
725 0,0,120,192,192,192,120,0,60,54,60,54,54,0,0,0, // 240d "CR"
726 0,0,192,192,192,192,240,0,62,48,60,48,48,0,0,0, // 240a "LF"
727 0,0,204,236,220,204,204,0,48,48,48,48,62,0,0,0, // 2424 "NL"
728 0,0,216,216,112,112,32,0,30,12,12,12,12,0,0,0, // 240b "VT"
729 0,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 23ba scan 1
730 0,0,0,0,255,0,0,0,0,0,0,0,0,0,0,0, // 23bb scan 3
731 0,0,0,0,0,0,0,0,0,0,255,0,0,0,0,0, // 23bc scan 7
732 0,0,0,0,0,0,0,0,0,0,0,0,0,255,0,0, // 23bd scan 9
733 0,0,0,2,4,126,8,16,126,32,64,0,0,0,0,0 // 2260 not equal
735 static const i32 extra_font_codepoints
[NUM_EXTRA_FONT_CHARS
] = {
736 0xfffd,0x25c6,0x2409,0x240c,0x240d,0x240a,0x2424,0x240b,
737 0x23ba,0x23bb,0x23bc,0x23bd,0x2260
740 static void do_create_standard_font(deark
*c
, struct charextractx
*ectx
)
743 struct de_bitmap_font
*font
;
744 const u8
*vga_cp437_font_data
;
745 struct de_bitmap_font_char
*ch
;
746 struct de_encconv_state es
;
748 font
= de_create_bitmap_font(c
);
749 ectx
->standard_font
= font
;
751 vga_cp437_font_data
= de_get_vga_cp437_font_ptr();
753 font
->num_chars
= 256 + NUM_EXTRA_FONT_CHARS
;
754 font
->nominal_width
= 8;
755 font
->nominal_height
= 16;
756 font
->has_nonunicode_codepoints
= 1;
757 font
->has_unicode_codepoints
= 1;
759 font
->char_array
= de_mallocarray(c
, font
->num_chars
, sizeof(struct de_bitmap_font_char
));
761 // Set defaults for each character
762 for(i
=0; i
<font
->num_chars
; i
++) {
763 ch
= &font
->char_array
[i
];
764 ch
->width
= font
->nominal_width
;
765 ch
->height
= font
->nominal_height
;
769 de_encconv_init(&es
, DE_ENCODING_CP437_G
);
770 for(i
=0; i
<font
->num_chars
; i
++) {
771 ch
= &font
->char_array
[i
];
772 ch
->codepoint_nonunicode
= (i32
)i
;
773 ch
->codepoint_unicode
= de_char_to_unicode_ex((i32
)i
, &es
);
774 ch
->bitmap
= (u8
*)&vga_cp437_font_data
[i
*16];
777 // Add vt100 characters that aren't in CP437
778 for(i
=0; i
<NUM_EXTRA_FONT_CHARS
; i
++) {
779 ch
= &font
->char_array
[256+i
];
780 ch
->codepoint_nonunicode
= DE_CODEPOINT_INVALID
;
781 ch
->codepoint_unicode
= extra_font_codepoints
[i
];
782 ch
->bitmap
= (u8
*)&extra_font_data
[i
*16];
784 font
->index_of_replacement_char
= 256;
787 static void de_char_output_to_image_files(deark
*c
, struct de_char_context
*charctx
,
788 struct charextractx
*ectx
)
792 if(ectx
->used_blink
) {
793 de_warn(c
, "This file uses blinking characters, which are not supported with "
798 ectx
->uses_custom_font
= 1;
799 ectx
->font_to_use
= charctx
->font
;
802 ectx
->uses_custom_font
= 0;
803 do_create_standard_font(c
, ectx
);
804 ectx
->font_to_use
= ectx
->standard_font
;
807 if(ectx
->vga_9col_mode
)
808 ectx
->char_width_in_pixels
= 9;
810 ectx
->char_width_in_pixels
= ectx
->font_to_use
->nominal_width
;
812 ectx
->char_height_in_pixels
= ectx
->font_to_use
->nominal_height
;
814 for(i
=0; i
<charctx
->nscreens
; i
++) {
815 de_char_output_screen_to_image_file(c
, charctx
, ectx
, charctx
->screens
[i
]);
818 if(ectx
->standard_font
) {
819 de_free(c
, ectx
->standard_font
->char_array
);
820 de_destroy_bitmap_font(c
, ectx
->standard_font
);
824 // Sets charctx->outfmt
825 void de_char_decide_output_format(deark
*c
, struct de_char_context
*charctx
)
829 if(charctx
->outfmt_known
) return;
830 charctx
->outfmt_known
= 1;
832 if(charctx
->prefer_image_output
)
835 s
= de_get_ext_option(c
, "char:output");
837 if(!de_strcmp(s
, "html")) {
840 else if(!de_strcmp(s
, "image")) {
846 void de_char_output_to_file(deark
*c
, struct de_char_context
*charctx
)
851 struct charextractx
*ectx
= NULL
;
853 ectx
= de_malloc(c
, sizeof(struct charextractx
));
855 de_char_decide_output_format(c
, charctx
);
857 if(charctx
->prefer_9col_mode
) {
858 ectx
->vga_9col_mode
= 1;
861 s
= de_get_ext_option(c
, "char:charwidth");
865 ectx
->vga_9col_mode
= 1;
868 ectx
->vga_9col_mode
= 0;
872 ectx
->scrstats
= de_mallocarray(c
, charctx
->nscreens
, sizeof(struct screen_stats
));
874 for(i
=0; i
<charctx
->nscreens
; i
++) {
875 do_prescan_screen(c
, charctx
, ectx
, i
);
878 switch(charctx
->outfmt
) {
880 de_char_output_to_image_files(c
, charctx
, ectx
);
883 de_char_output_to_html_file(c
, charctx
, ectx
);
887 de_free(c
, ectx
->scrstats
);