// // PDF documentation tool to generate a png image from a Doxygen `@code` // segment with international characters for the Fast Light Tool Kit (FLTK). // // Copyright 2025 by Bill Spitzak and others. // // This library is free software. Distribution and use rights are outlined in // the file "COPYING" which should have been included with this file. If this // file is missing or damaged, see the license at: // // https://www.fltk.org/COPYING.php // // Please see the following page on how to report bugs and issues: // // https://www.fltk.org/bugs.php // // // Our documentation for the FLTK unicode contains international characters // to illustrate use of non ASCII characters in the GUI. To generate PDF // output, Doxygen uses LaTeX which can not easily handle UTF-8 characters // beyond Western encoding. This tool generates PNG images from code segments // containing international characters so that they can be included in the // PDF documentation instead of the code segments with UTF-8 characters. // // Notes: // - This program is work in progress... // - The PDF generation process (CMake) calls it once for each source file // that needs image generation. The loop over all commandline arguments is // currently not necessary but kept for potential extensions. // - The program exits silently if no commandline is given. // - If the terminating "\endcode_international" line is missing or misspelled, // the program reads the "code" until the end of the file is reached and // terminates w/o error message. This could be improved... #include #include #include #include #include #include #include "../fluid/widgets/Code_Editor.h" #include "../fluid/widgets/Code_Viewer.h" #include "../fluid/widgets/Style_Parser.h" #include #include #include Fl_Window* window = 0; Fl_Group* group = 0; fld::widget::Code_Viewer* code_viewer = 0; int line_height = 10; void create_window() { window = new Fl_Window(1024, 100); group = new Fl_Group(0, 0, 1024, 100); group->color(0xf7f7ff00); group->box(FL_FLAT_BOX); code_viewer = new fld::widget::Code_Viewer(5, 5, 1014, 94); code_viewer->box(FL_FLAT_BOX); code_viewer->color(0xf7f7ff00); code_viewer->textsize(30); // code_viewer->cursor_style(CARET_CURSOR); window->resizable(group); group->resizable(code_viewer); // Make sure the display is opened. Fl_Display_Device::display_device(); int h; line_height = fl_height(code_viewer->textfont(), code_viewer->textsize()); h = fl_height(FL_COURIER, code_viewer->textsize()); if (h > line_height) line_height = h; h = fl_height(FL_COURIER_BOLD, code_viewer->textsize()); if (h > line_height) line_height = h; h = fl_height(FL_COURIER_ITALIC, code_viewer->textsize()); if (h > line_height) line_height = h; h = fl_height(FL_COURIER_BOLD_ITALIC, code_viewer->textsize()); if (h > line_height) line_height = h; } void save_snapshot(const char* code, const char* filename) { // fprintf(stderr, "\\code\n%s\n\\endcode\n", code); code_viewer->buffer()->text(code); int n_lines = 1; for (const char* s=code; *s; ++s) if (*s == '\n') n_lines++; // 300 dpi for 7 inches = 2100 pixels window->size(2100, (line_height * n_lines) + 18 ); // Generate the Image Surface Fl_Image_Surface *srfc = new Fl_Image_Surface(window->w(), window->h()); // Draw the window and its content Fl_Image_Surface::push_current(srfc); srfc->draw(group, 0, 0); fl_rect(0, 0, window->w(), window->h(), 0xccccff00); Fl_Image_Surface::pop_current(); // fprintf(stderr, " Saving to \"%s\".\n", filename); // Write the generated image Fl_RGB_Image *img = srfc->image(); fl_write_png(filename, img); // Clean up delete img; delete srfc; } /** Main entry point for the PDF documentation helper tool. The app scans the input file for the `\code_international{"filename"}` directive, reads the following code segment until `\endcode_international`, and generates a PNG image file with the given filename containing the code segment rendered with FLTK's code rendering capabilities. \param[in] argc Argument count \param[in] argv a list of input files with documentation in Doxygen format \return Exit code (0 for success, non-zero for failure) */ int main(int argc, char *argv[]) { int ret = 0; char line[1024]; char cwd[FL_PATH_MAX]; // fl_getcwd(cwd, FL_PATH_MAX-1); // fprintf(stderr, "code_snapshot:\n"); // fprintf(stderr, "Working directory is \"%s\".\n", cwd); create_window(); for (int i = 1; i < argc; i++) { FILE* f = fl_fopen(argv[i], "rb"); if (!f) { fl_getcwd(cwd, FL_PATH_MAX-1); fprintf(stderr, "code_snapshot:\nCan't open file \"%s\".\n", argv[i]); fprintf(stderr, "Working directory is \"%s\".\n", cwd); ret = -1; break; } // fprintf(stderr, "Reading \"%s\".\n", argv[i]); char *code = 0; size_t code_len = 0; size_t code_alloc = 0; char filename[FL_PATH_MAX]; bool in_code_block = false; int j; for (;;) { fgets(line, 1023, f); if (feof(f)) break; if (in_code_block) { if (strstr(line, "\\endcode_international")) { if (code_len > 0) { code[code_len - 1] = '\0'; // remove trailing newline save_snapshot(code, filename); } in_code_block = false; code_len = 0; } else { size_t linelen = strlen(line); if (code_len + linelen >= code_alloc) { code_alloc = (code_alloc == 0) ? 4096 : code_alloc * 2; code = (char *)realloc(code, code_alloc); } memcpy(code + code_len, line, linelen); code_len += linelen; code[code_len] = '\0'; } } else { if (strstr(line, "\\code_international")) { const char* fn_start = strstr(line, "{\""); const char* fn_end = strstr(line, "\"}"); if (fn_start && fn_end && (fn_end > fn_start)) { int len = (int)(fn_end - fn_start - 2); if (len >= FL_PATH_MAX) len = FL_PATH_MAX - 1; for (j = 0; j < len; j++) filename[j] = fn_start[2 + j]; filename[len] = '\0'; in_code_block = true; } } } } if (code) free(code); fclose(f); } delete window; return ret; }