#include #include #include #define STB_TRUETYPE_IMPLEMENTATION #include "stb_truetype.h" #define STB_IMAGE_WRITE_IMPLEMENTATION #include "stb_image_write.h" // Функция для декодирования UTF-8 в кодовую точку Unicode int utf8_to_unicode(const char *str, int *bytes_read) { unsigned char c = str[0]; if (c < 0x80) { *bytes_read = 1; return c; } else if ((c & 0xE0) == 0xC0) { *bytes_read = 2; return ((str[0] & 0x1F) << 6) | (str[1] & 0x3F); } else if ((c & 0xF0) == 0xE0) { *bytes_read = 3; return ((str[0] & 0x0F) << 12) | ((str[1] & 0x3F) << 6) | (str[2] & 0x3F); } *bytes_read = 1; return 0; // Некорректный символ } int main(int argc, char *argv[]) { if (argc != 3) { fprintf(stderr, "Usage: %s \n", argv[0]); return 1; } // Чтение файла шрифта FILE *font_file = fopen(argv[1], "rb"); if (!font_file) { fprintf(stderr, "Failed to open font file: %s\n", argv[1]); return 1; } fseek(font_file, 0, SEEK_END); long font_size = ftell(font_file); fseek(font_file, 0, SEEK_SET); unsigned char *font_data = malloc(font_size); fread(font_data, 1, font_size, font_file); fclose(font_file); // Инициализация шрифта stbtt_fontinfo font; if (!stbtt_InitFont(&font, font_data, 0)) { fprintf(stderr, "Failed to initialize font\n"); free(font_data); return 1; } // Парсинг высоты шрифта int font_height = atoi(argv[2]); if (font_height <= 0) { fprintf(stderr, "Invalid font height: %s\n", argv[2]); free(font_data); return 1; } // Набор символов (ASCII + кириллица) const char *chars = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" "АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдеёжзийклмнопрстуфхцчшщъыьэюя"; // Подсчет кодовых точек int char_count = 0; for (int i = 0; chars[i]; ) { int bytes_read; utf8_to_unicode(&chars[i], &bytes_read); i += bytes_read; char_count++; } // Массив для хранения кодовых точек int *codepoints = malloc(char_count * sizeof(int)); int idx = 0; for (int i = 0; chars[i]; ) { int bytes_read; codepoints[idx++] = utf8_to_unicode(&chars[i], &bytes_read); i += bytes_read; } // Расчет масштаба и метрик float scale = stbtt_ScaleForPixelHeight(&font, font_height); int ascent, descent, line_gap; stbtt_GetFontVMetrics(&font, &ascent, &descent, &line_gap); ascent = (int)(ascent * scale); descent = (int)(descent * scale); // Расчет общей ширины с дополнительным отступом (20% от ширины символа) const float spacing_factor = 1.3f; // 20% дополнительного пространства int total_width = 0; for (int i = 0; i < char_count; i++) { int advance, lsb; stbtt_GetCodepointHMetrics(&font, codepoints[i], &advance, &lsb); total_width += (int)(advance * scale * spacing_factor); } // Создание битмапа unsigned char *bitmap = calloc(total_width * font_height, 1); if (!bitmap) { fprintf(stderr, "Failed to allocate bitmap\n"); free(codepoints); free(font_data); return 1; } // Рендеринг каждого символа int x_offset = 0; for (int i = 0; i < char_count; i++) { int w, h, xoff, yoff; unsigned char *glyph = stbtt_GetCodepointBitmap(&font, 0, scale, codepoints[i], &w, &h, &xoff, &yoff); // Копирование глифа в битмап for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { int bitmap_x = x_offset + x + xoff; int bitmap_y = ascent + y + yoff; if (bitmap_x >= 0 && bitmap_x < total_width && bitmap_y >= 0 && bitmap_y < font_height) { bitmap[bitmap_y * total_width + bitmap_x] = glyph[y * w + x]; } } } // Обновление x_offset с учетом отступа int advance, lsb; stbtt_GetCodepointHMetrics(&font, codepoints[i], &advance, &lsb); x_offset += (int)(advance * scale * spacing_factor); stbtt_FreeBitmap(glyph, NULL); } // Запись BMP if (!stbi_write_bmp("output.bmp", total_width, font_height, 1, bitmap)) { fprintf(stderr, "Failed to write BMP file\n"); } // Очистка free(bitmap); free(codepoints); free(font_data); return 0; }