mirror of
https://github.com/stasenso/ttf2bmp.git
synced 2026-06-26 21:42:43 +03:00
Draft by Grok
This commit is contained in:
@@ -0,0 +1 @@
|
||||
build/*
|
||||
@@ -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++ код
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#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 <font_path> <font_height>\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;
|
||||
}
|
||||
Reference in New Issue
Block a user