Написание open source проекта на примере рендера для ttf шрифтов. Начало

21dbbc9b0f45812eb735490d7a07b7b3

Введение

О проекте

В данном проекте планируется создать парсер, а затем и рендер для шрифтов с расширениями TTF, OTF, TTC, OTC. Я прекрасно понимаю, что подобные проекты существуют и это будет изобретение велосипеда, однако благо, что данные инициативы не запрещены, поэтому можно заниматься этим сколь угодно много.

Документацию по данным форматам можно найти на сайте Microsoft и на сайте Apple. Мы будем их использовать как главные источники гнозиса.

Проект планируется разбить на следующие подпроекты.

  1. Парсинг. Это чтение файла шрифта и всех входящих в него таблиц и составление на их основе описания шрифтов.

  2. Создание векторных изображений символов и лигатур. На этом этапе можно встроить композитинг текста.

  3. Растеризация текста. На основе полученных векторных текстур составляется растровое изображение текста. Желательно «подружить» растеризатор с наиболее распространенными API (Vulkan, OpenGL, DirectX) или библиотеками рисования.

  4. Интеграция с композитингом текста. Здесь планируется добиться возможности правильного размещения глифов относительно друг друга. Реализация элементов текста (абзац, строчные элементы, блочные элементы).

Этот проект планируется мной как часть более крупного проекта по созданию приложений с пользовательским интерфейсом. Репозиторий на GitHub.

Описание проекта будет меняться со временем. Принимаю ваши замечания и исправления. Цикл данных статей является своеобразным влогом, но не обучающим. Наоборот я прошу об обратном отклике в виде комментариев и иных видах обратной связи.

О цикле статей

Описание проекта будет меняться со временем. Принимаю ваши замечания и исправления. Цикл данных статей является своеобразным влогом, но не обучающим. Наоборот я прошу об обратном отклике в виде комментариев.

Данный влог — это опыт создания open source проектов и изучения того как работают шрифты и как создаётся на их основе структурированный текст.

Под каждой статьёй я буду выкладывать вводную часть, в которой будет описан план работы и исправления ошибок предыдущих этапов.

Надеюсь, что чтение данного цикла статей не будет для вас утомительным. Приятного чтива!

Немного о себе

Я не создавал крупных open-source проектов, поэтому это мой первый опыт. Я являюсь самоучкой в программировании. Изучаю C++. Программирование не является моей работой, однако оно меня очень увлекает. Это также моя первая статья на habr. Я думал о том, чтобы записать видео, но текстовый формат мне понравился больше. Вдруг, кому-нибудь станет интересно, но не обещаю.

Этап вводный. Подготовка среды для проекта

Прежде чем начать писать код, необходима подготовка проекта. Начать бы проект хотелось с подготовки среды, в которой будет писаться код. Необходимы следующие вещи: среда разработки, средства создания документации кода, фреймворки для тестирования, а также, самое главное система сборки и контроля версий. Полученные шрифты нужно как-то отображать и отображать отладочную информацию о них.

Система сборки CMake

Я буду по-возможности использовать стандарт c++20. Это, на мой взгляд, самый поздний из стабильных на всех компиляторах. Будут использоваться старые добрые файлы заголовков и исходников.

CMake делится на три этапа: сборка, тестирование и установка. Мы пока остановимся на первых двух.

Так выглядит начало файла CMakeLists.txt.

#Будем использовать более-менее современную версию CMake.
cmake_minimum_required(VERSION 3.20)

#Проект будет на C/C++.
project(ttf-raster C CXX)

#Будем использовать стандарт c++20.
set(CMAKE_CXX_STANDART 20)
#c17.
set(CMAKE_C_STANDART 17)

Система сборка cmake преобразовывает файлы конфигурации в входные файлы сборки для различных генераторов. Генераторы уже собирают готовый проект.

Директории и файлы проекта

Подкаталогами данного проекта будут.

  • source. Там хранятся файлы исходного кода.

  • include. Заголовочные файлы.

  • test. Файлы для кода тестирования.

  • build. Файлы сборки.

  • resource. Файлы ресурсов.

  • install. Каталог, где будет окончательная сборка проекта для пользователя.

  • docs. Файлы документации.

  • cmake. Файлы конфигурации для Cmake.

  • lib. Файлы локальных библиотек.

Также в корне проекта располагается.

  • CMakeLists.txt для конфигурации CMake.

  • Doxyfile для конфигурации doxygen-а.

  • README.md для краткого описания проекта.

  • LICENSE для описания лицензионного соглашения.

  • .gitignore для сообщения git о том, какие файлы и директории будут игнорироваться системой контроля версий.

  • .gitmodule для использования одних проектов внутри других.

Для более-менее живого проекта эти файлы и каталоги необходимы. Будем надеется, что они в дальнейшем будут наполняться качественно задокументированным кодом.

Интеграция GTest в CMake

Для юнит тестирования нашего кода будем использовать небезызвестный фреймворк gtest. Его необходимо интегрировать в нашу CMake сборку. Юнит-тестирования — необходимый инструмент для разработки. Он поможет избегать фиаско с внезапными, трудно отлавливаемыми ошибками.

Для начала необходимо загрузить googletest. Делается это с помощью команды git:
git submodule add https://github.com/google/googletest.git

Откроем файл .gitmodules. Там уже написаны следующие строчки

[submodule "googletest"]
	path = googletest
	url = https://github.com/google/googletest

Теперь googletest будет нашем подмодулем.

Затем в CMakeLists.txt добавляем директорию googletest в список поддиректорий проекта. Для этого допишем в наш основной файл.

#Включаем тестирование.
enable_testing()

#Добавляем gtest в наш проект.
add_subdirectory(googletest)

#Добавляем в наш проект файлы для тестирования
add_subdirectory(test)

Теперь напишем следующий скелет файла CMakeLists.txt в каталоге test, то бишь настроим сборку тестов.

#Будем использовать более-менее современную версию CMake.
cmake_minimum_required(VERSION 3.20)

#Проект будет на C/C++.
project(ttf-raster-test C CXX)

#Минимальная необходимая версия стандарта c++14 будем использовать её.
set(CMAKE_CXX_STANDART 14)
#c17.
set(CMAKE_C_STANDART 99)

#Задаём список файлов исходного кода для тестирования
set(SOURCE_TESTS
  #Пока тут пусто(
)

#Создаём исполняемый файл для запуска тестирования
#Их может быть несколько поэтому вместо PROJECT_NAME 
#стоит использовать другие имена
add_executable(${PROJECT_NAME} ${SOURCE_TESTS})

#Добавим библиотеки для тестирования
target_link_libraries(
  ${PROJECT_NAME} PUBLIC
  gtest_main
  gmock_main
)

#Добавим их в списки тестов
add_test(
	NAME ${PROJECT_NAME}
	COMMAND ${PROJECT_NAME}
)

Теперь, чтобы запустить все тесты, нужно запустить команду: ctest

Интеграция Doxygen в Cmake

Нужно документировать свой код, иначе можно забыть откуда что берётся и куда кладётся. Будет использоваться Doxygen. Его прелесть в том, что он преобразовывает комментарии-инструкции в коде в формат документа. Поэтому нужно при добавлении функции, класса, перечисления и всего другого, что необходимо задокументировать писать специальные комментарии, которые являются инструкциями для doxygen-а. Добавим в наш главный CMakeLists.txt строчки.

#Добавляем создание документации в наш проект
add_subdiretory(docs)

В папке docs создадим файл CMakeLists.txt и напишем там.

find_program(DOXYGEN_PATH doxygen REQUIRED)

if(DOXYGEN_PATH_NOTFOUND)
  message(FATAL_ERROR "Doxygen is needed to build the documentation. Please install it on your system")
  else()
     message(STATUS "Doxygen found.")
     add_custom_target(documentation COMMAND ${DOXYGEN_PATH} ${CMAKE_CURRENT_LIST_DIR}/Doxyfile WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}
                     BYPRODUCTS ${CMAKE_BINARY_DIR}/html/index.html)
     add_custom_command(
     TARGET documentation POST_BUILD COMMAND echo "Documentation successfully generated. You can preview at: ${CMAKE_BINARY_DIR}/html/index.html")
endif()

Среда отображения

Как говорилось выше, полученные глифы нужно где-го отображать. Первое время будем использовать GDI. Будем программировать на платформе win32. Но в дальнейшем планируется поддержка других платформ. Пока давайте остановимся на этом. GDI довольно низкоуровневый интерфейс. Это то, что нам нужно. Однако, в ближайшее время нам ничего не придётся рисовать.

Для открытия файлов будем использовать стандартную библиотеку. В следующей статьёй мы подготовим поверхность, куда будут рисоваться пиксели и выводить информацию об отладке, затем мы начнём парсить шрифты.

Заключение

Зачем все эти сложности в проекте? Они необходимы для того, чтобы в дальнейшем избежать больших сложностей. Сложность написания проекта в начале пути — это автоматизация, чтобы не возиться в дальнейшем, нужно потрудиться в начале.

Это мой первый опыт. Данная статья НЕ ЯВЛЯЕТСЯ ТУТОРИАЛОМ, я никого не хочу научить. Не судите строго.

© Habrahabr.ru