Files
fltk/util/code_snapshot.cxx
Albrecht Schlosser a057e13fb4
Some checks failed
Build and Test / build-linux (push) Has been cancelled
Build and Test / build-wayland (push) Has been cancelled
Build and Test / build-macos (push) Has been cancelled
Build and Test / build-windows (push) Has been cancelled
Improve code snapshot generation for PDF docs (#1353)
CMake: execute `code_snapshot` separately for each file we want to
  scan so we can create timestamps and generate PNG images only if
  the source file (e.g. unicode.dox) was modified.

documentation/src/unicode.dox: remove output folder `generated`, use
  only the filename. CMake sets the current working directory as needed.

util/code_snapshot.cxx: format and improve comments, reset code buffer
  so we can generate multiple images per input file.
2025-12-29 20:02:06 +01:00

181 lines
5.7 KiB
C++

//
// 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 <FL/Fl_Window.H>
#include <FL/Fl_Group.H>
#include <FL/filename.H>
#include <FL/fl_draw.H>
#include <FL/Fl_Image_Surface.H>
#include <FL/Fl_PNG_Image.H>
#include "../fluid/widgets/Code_Editor.h"
#include "../fluid/widgets/Code_Viewer.h"
#include "../fluid/widgets/Style_Parser.h"
#include <stdio.h>
#include <algorithm>
Fl_Window* window = nullptr;
Fl_Group* group = nullptr;
fld::widget::Code_Viewer* code_viewer = nullptr;
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();
line_height = fl_height(code_viewer->textfont(), code_viewer->textsize());
line_height = std::max(line_height, fl_height(FL_COURIER, code_viewer->textsize()));
line_height = std::max(line_height, fl_height(FL_COURIER_BOLD, code_viewer->textsize()));
line_height = std::max(line_height, fl_height(FL_COURIER_ITALIC, code_viewer->textsize()));
line_height = std::max(line_height, fl_height(FL_COURIER_BOLD_ITALIC, code_viewer->textsize()));
}
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]);
std::string code;
std::string filename;
bool in_code_block = false;
for (;;) {
fgets(line, 1023, f);
if (feof(f)) break;
if (in_code_block) {
if (strstr(line, "\\endcode_international")) {
if (!code.empty()) {
code.resize(code.size() - 1);
save_snapshot(code.c_str(), filename.c_str());
}
in_code_block = false;
code = "";
} else {
code += line;
}
} 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)) {
filename = std::string(fn_start+2, fn_end-fn_start-2);
in_code_block = true;
}
}
}
}
fclose(f);
}
delete window;
return ret;
}