Текст и шрифты

Загрузка шрифта

Перед рисованием текста, вам нужно загрузить шрифт. Для работы со шрифтами используется специальный класс sf::Font. Он поддерживает три главных операции: загрузка шрифта, получение из него глифов (визуальных символов) и чтение его атрибутов.

Начнём с загрузки. Самый простой способ загрузить шрифт, это загрузить его из файла на диске, сделать это можно при помощи функции loadFromFile().

sf::Font font;
if (!font.loadFromFile("arial.ttf"))
{
 // ошибка...
}

Обратите внимание на то, что SFML не станет автоматически загружать ваши системные шрифты, т.о. конструкция font.loadFromFile(«Courier New») работать не будет. Во-первых, потому, что SFML требует имена файлов, а не названия шрифтов, а во-вторых, потому, что SFML не имеет волшебного доступа к системной папке содержащей шрифты. И так, если вы хотите загрузить шрифт, то вам нужно иметь файл шрифта для вашей программы, точно так же, как и прочие ресурсы (картинки, музыку..).

Иногда функция loadFromFile() терпит неудачу по не очевидной причине. Сначала проверьте сообщение об ошибке выводимое в стандартный вывод (проверьте консоль). Если сообщение гласит unable to open file (не удалось открыть файл), убедитесь что рабочий каталог (это каталог относительно которого и будут интерпретироваться все пути к файлам) именно там, где вы предполагаете: когда вы запускаете приложение из проводника, рабочим каталогом является папка с приложением, но когда вы запускаете вашу программу через IDE (Visual Studio, Code :: Blocks, …), то рабочий каталог иногда устанавливается в папку с проектом, как правило это легко изменить в настройках проекта.

Вы также можете загрузить файл шрифта из памяти (loadFromMemory()), или из потока пользовательского ввода (LoadFromStream()).

SFML поддерживает наиболее распространенные форматы шрифтов. Полный список доступен в документации API.

Вот и все. Как только ваш шрифт загружен, вы можете начать рисовать текст.

Рисование текста

Для рисования вам нужно использовать класс sf::Text. Он очень прост в использовании:

f::Text text;

// выбираем шрифт
text.setFont(font); // font это sf::Font

// выбираем отображаемую строку
text.setString("Hello world");

// выбираем размер символов
text.setCharacterSize(24); // в пикселях, а не точках!

// выбираем цвет
text.setColor(sf::Color::Red);

// выбираем стиль текста
text.setStyle(sf::Text::Bold | sf::Text::Underlined);

...

// внутри главного цикла, между window.clear() и window.display()
window.draw(text);

graphics-text-draw

Тексты могут быть трансформированы: они имеют позицию, ориентацию и масштаб. Функции применяемые при этом, точно такие же, как и для спрайтов sf::Sprite и для других объектов SFML, подробнее об этом рассказывается в уроке трансформация объектов.

Как избежать проблем с не ASCII символами?

Правильная обработка не ASCII символов (таких как русские, китайский, арабские..) может оказаться непростой задачей. Это требует от вас хороших знаний о различных видах кодировок участвующих в интерпретации и рисовании текста. Можно конечно помучится с этими кодировками, а можно поступить и проще: использовать расширенные строковые литералы.

text.setString(L"Привет, о дивный новый мир!");

 Да-да, этот простой префикс «L» перед строкой сильно упрощает нам жизнь. Он, говорит компилятору о том, что используются расширенные строковые литералы. Довольно странно, но в стандарте C++ ничего несказанно об их размере (16 бит, 32 бита?) так же не сказано и о кодировке которую они используют (UTF-16 в UTF-32?). Однако мы знаем, что на большинстве платформ, если не на всех, они являются строками Unicode, и SFML умеет корректно их обрабатывать.

Обратите внимание, что стандарт C++11 поддерживает новые типы символов и префиксы для создания UTF-8, UTF-16 и UTF-32 строковых литералов, но SFML пока их не поддерживает.

Это может показаться очевидным, но вы также должны убедиться, что шрифт, который вы используете, содержит символы, которые вы хотите применить. Действительно, шрифты не определяют все возможные символы (их более 100000 в стандарте Unicode!), И арабский шрифт не будет в состоянии отображать текст на японском языке.

Собственный класс для работы с текстом

Если класс sf::Text вам кажется ограниченным в своих возможностях или вы хотите сделать что-нибудь с глифами перед их рисованием на экране, то класс sf::Font обеспечит вас всем необходимым.

Во-первых, вы можете получить текстуру, которая содержит все глифы определенного размера:

const sf::Texture& texture = font.getTexture(characterSize);

Заметьте, глифы добавляются в текстуру лишь по требованию. Шрифт может содержать так много символов (более 100000), что все они не могут быть сразу получены на этапе загрузки. Вместо этого они визуализируются на лету при вызове функции getGlyph() (см. ниже).

Что бы сделать что-то полезное с текстурой глифа, вы теперь должны получить координаты глифов которые в ней находятся:

sf::Glyph glyph = font.getGlyph(character, characterSize, bold);

Символ (character) — это UTF-32 код глифа который вы хотите получить. Кроме того, вы должны указать размер символа и спецификацию глифа (жирный(bold), обычный(regular)).

Структура sf::Glyph содержит три члена:

  • textureRect — это текстурные координаты глифа внутри текстуры
  • bounds — это ограничивающий прямоугольник глифа, который помогает позиционировать его относительно базовой линии текста 
  • advance — это горизонтальное смещение, применяемое для получения начальной позиции следующего глифа в тексте

Наконец, вы можете получить некоторые другие метрики шрифта, такие как интервал между строками или кернинг (всегда для определенного размера символа):

int lineSpacing = font.getLineSpacing(characterSize);

int kerning = font.getKerning(character1, character2, characterSize);

 

9 Комментарии

  1. Павел

    А как вывести на экран не строчку, а какую нибудь переменную?

    1. samuel_unknown (Автор записи)

      Для этого необходимо сначала преобразовать переменную в строку, можешь погуглить в этом направлении.. :)

      1. Павел

        Пытался поискать, но ни чего не выходит, сможете подсказать, как это можно сделать?

        1. samuel_unknown (Автор записи)
          #include 
          std::string Convert (float number){
              std::ostringstream buff;
              buff < < number;
              return buff.str();   
          }

          1. Павел

            Попробовал сделать вот так:
            text.setString(Convert(b));
            Но появилась ошибка: Convert: Идентификатор не найден

          2. Павел

            о!, Всё заработало, большое вам спасибо. Я просто поместил эту функцию перед main()

          3. samuel_unknown (Автор записи)

            Рад что помог =) я как раз хотел написать что ты, должно быть, забыл прототип функции описать перед main()

  2. Юра

    подскажите, как создать блок в котором текст будет сам переносится на новую строку достигну конца блока по Х координате?

    1. samuel_unknown (Автор записи)

      А фиг его знает %) Готового решения вроде как нет..

Добавить комментарий для samuel_unknown Отменить ответ

Ваш e-mail не будет опубликован. Обязательные поля помечены *

Капча * Лимит времени истёк. Пожалуйста, перезагрузите CAPTCHA.

Этот сайт использует Akismet для борьбы со спамом. Узнайте как обрабатываются ваши данные комментариев.