diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a007fea --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +build/* diff --git a/CMakeLists.txt b/CMakeLists.txt index e69de29..2afa986 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.15) +project(ttf2bmp) + +# 1. Скачиваем STB в libs/stb +include(FetchContent) + +FetchContent_Declare( + stb + GIT_REPOSITORY https://github.com/nothings/stb.git + GIT_TAG master + SOURCE_DIR "${CMAKE_SOURCE_DIR}/libs/stb" +) + +FetchContent_MakeAvailable(stb) + +# 2. Добавляем libs/stb в include-пути +include_directories(libs/stb) + +# 3. Компилируем основную программу (STB реализован прямо в ttf2bmp.c) +add_executable(ttf2bmp src/ttf2bmp.c) + +# Подключаем математическую библиотеку (libm) +target_link_libraries(ttf2bmp PRIVATE m) + +# Устанавливаем стандарт C для всего проекта (опционально) +set_property(TARGET ttf2bmp PROPERTY C_STANDARD 11) +set_property(TARGET ttf2bmp PROPERTY CXX_STANDARD 11) # Если вдруг есть C++ код \ No newline at end of file diff --git a/src/ttf2bmp.c b/src/ttf2bmp.c index e69de29..13c9e7b 100644 --- a/src/ttf2bmp.c +++ b/src/ttf2bmp.c @@ -0,0 +1,144 @@ +#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; +} \ No newline at end of file