Russian Qt Forum
Ноябрь 24, 2024, 08:24 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: Шрифты. Стили одного семейства.  (Прочитано 5414 раз)
GDV1981
Гость
« : Сентябрь 13, 2011, 19:59 »

Всем привет. Нужна помощь.

В ресурсы разрабатываемого приложения внедряю шрифты (OTF). Не устанавливаю в операционке, - это важно. Шрифты одного семейства (font family). Отличаются тем, что содержат начертания символов в различном стиле/весе (font weight) - обычное начертание (Normal / regular / roman / plain) и "облегченный" вариант (Light). Хочу использовать эти два стиля.

Добавляю шрифты в базу данных.
Код:
QFontDatabase::addApplicationFont(":/res/font/HelveticaNeueCyr-Light.otf");
QFontDatabase::addApplicationFont(":/res/font/HelveticaNeueCyr-Roman.otf");
Они нормально устанавливаются. Каждому из них присваивается свой идентификатор.

Если добавлять только один шрифт с "облегченным" начертанием и посмотреть его стили
Код:
QFontDatabase fdb;
QStringList list = fdb.styles("HelveticaNeueCyr");
то все хорошо. Присутствует стиль Light.

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


Ходил в отладчике по исходникам Qt (под windows). С точки зрения API - все вызывается по науке (msdn).
Сначала
Код:
AddFontMemResourceEx
Потом
Код:
GetTextMetrics
И вот в
Код:
*PTEXTMETRIC p;
p->tmWeight
лежит значение, характеризующее только обычный стиль начертания.


Интуитивно чувствую, что проблема, наверное, в том, что используется какая-нибудь карта/хэш/мультисэт, в которой ключами являются имена семейств шрифтов. При добавлении в карту проходит все хорошо, а вот при получении значений (списка поддерживаемых стилей) - находится первое (?) и возвращается. Но это предположение, - не верю я в такую криворукость.


update 1
Набросал демку (см. внизу testFont.zip).
Два списка: шрифты и их стили. Выбираем шрифт, отображаются доступные ему стили.
Два чекбокса. Первый добавляет шрифт "Light", второй - обычный.

Сценарий теста
Жмем Refresh. Загружается список шрифтов.
Убеждаемся, что HelveticaNeueCyr отсутствует.
Жмем первый чек (Light). В списке появляется  HelveticaNeueCyr.
Клик по HelveticaNeueCyr в первом списке. Во втором отображаются доступные ему стили.
Жмем второй чек (Roman). .... По идее список стилей должен расшириться.... Но нет. Происходит замена "Light" на "Normal".
....куча вопросов...

update 2
Если установить эти два шрифта, то получается все совсем интересно.
В списке шрифтов естественно уже будет наш шрифт. При получении списка его стилей вижу абсолютно верный набор. Но... Использовать все варианты не удается. Начертание кириллических символов не изменяется.

update 3
Проверил на openSuse...... все работает. Причем так как надо. ....
..все понимаю...  но нужна она  ...нужна Винда.


Написал много. Надеюсь, что мой пост дочитан вами до конца. И еще очень надеюсь на вашу помощь.
« Последнее редактирование: Сентябрь 15, 2011, 13:46 от GDV1981 » Записан
GDV1981
Гость
« Ответ #1 : Сентябрь 15, 2011, 19:30 »

Что-то совсем тут тихо....

Ну ладно, отпишусь о достигнутых результатах.

1. Анализ работы подсистемы шрифтов Qt (Windows).
Для меня вообще удивительно, почему QFontDatabase не построен как синглтон. Этот объект создается при каждом "чихе" (отрисовка любого текстового элемента). После его создания, происходит переинициализация (!) всех установленных в системе шрифтов. И так каждый раз. Вдумайтесь только. Например, есть форма, в ней табличка 5 строк 10 столбцов = 50 элементов (без заголовков). Чтобы отрисовать текст в каждой ячейке, создается объект QFontDatabase. При создании вызывается метод  populate_database(), в котором идет обращение к API-функции EnumFontFamiliesEx(). Вот что говорит MSDN про этот метод:
Цитировать
function enumerates all uniquely-named fonts in the system
То есть, если в системе установлено около 200 шрифтов с уникальным именем, то переданная в функцию EnumFontFamiliesEx аргументом функция-callback вызовется 200 раз. Теперь перемножаем 50 на 200, получаем 10 000 (!) вызовов для того, чтоб несколько раз переинициализировать свою шрифтовую базу. Эт неправильно я раньше в криворукость не верил....

2. Ответ на вопрос "почему".
Почему так происходит, что при добавлении двух шрифтов одного семейства, Qt показывает стиль/вес только одного из них? Как раз по обозначенной выше причине. При добавлении нового шрифта, в очередной раз переинициализируется шрифтовая база. API-функция, которой пользуются разработчики Qt - GetTextMetrics . Она позволяет получить свойства шрифта, который наиболее подходит по запрошенным параметрам. Теперь вопрос - а что будет, если никаких параметров кроме имени семейства не запрашивать? ... Правильно. Вернется шрифт с параметрами "по умолчанию".
Вот и выходит, что после добавления в приложение двух шрифтов (Light и Roman), и после запроса их стилей/веса с параметрами по умолчанию - Windows честно возвращает шрифт по умолчанию. Который, как наверное все уже догадались, является шрифтом со стилем/весом/насыщенностью равной 400. А это Roman (normal/plain/regular). И разработчики Qt смело, ни о чем не думая.........

3. Ответ на вопрос "что делать".
Тут вижу три выхода.
Первый. Не, ну надо исправляться....
Второй. Делать нужную отрисовку средствами API. Проверил, все работает так как надо. Кому будет нужно объясню как и отдам свою демку.
Третий. Искать такой же шрифт, но чтоб имя его семейства отличалось. И использовать два разных шрифта. Проверено, работает. Ну или найти/написать программку, которая сменит familyName в самом файле шрифта. Правда побить за это могут, - всеж лицензия...

Я пошел по третьему "правильному" пути и нашел такую же Helvetica Neue, только уже не кириллическую (мне не принципиально, цифры выводить). Попутно думаю о том, чтоб написать баг-репорт и подарить тролям свою демку, пусть посмотрят как надо...
Записан
BRE
Гость
« Ответ #2 : Сентябрь 15, 2011, 19:46 »

А не спешишь ли ты с выводами? Подмигивающий
Посмотри внимательно исходники еще раз. Посмотри что такое privateDb().
Записан
GDV1981
Гость
« Ответ #3 : Сентябрь 15, 2011, 22:19 »

Что именно посмотреть, privateDb()?
То что, инициализируется статический внутренний приватный объект QFontDatabasePrivate, как впрочем и во всех (?) остальных классах Qt, совсем ничего не означает. Это как бы раз.
Еще раз посмотрел на QFontDatabasePrivate. Он и содержит как раз в себе QtFontFamily ** и средство очистки free(), которое вызывается каждый раз при добавлении шрифта в приложение. Это два. Ну и что, что это срабатывает только при добавлении шрифта.
Если по-твоему все тут нормально, то как объяснить такое количество вызовов populate_database().
Записан
BRE
Гость
« Ответ #4 : Сентябрь 16, 2011, 07:05 »

То что, инициализируется статический внутренний приватный объект QFontDatabasePrivate, как впрочем и во всех (?) остальных классах Qt, совсем ничего не означает. Это как бы раз.
А в каких всех остальных классах ты видел создание статического приватного объекта?
А здесь это как раз тот самый синглетон, о котором ты удивлялся. Это раз.

Еще раз посмотрел на QFontDatabasePrivate. Он и содержит как раз в себе QtFontFamily ** и средство очистки free(), которое вызывается каждый раз при добавлении шрифта в приложение. Это два. Ну и что, что это срабатывает только при добавлении шрифта.
Если по-твоему все тут нормально, то как объяснить такое количество вызовов populate_database().
А что тебя смущает в вызове populate_database()?
Давай посмотрим на код для венды:
Код
C++ (Qt)
static
void populate_database(const QString& fam)
{
   Получили instance объекта кеширующего загруженные шрифты
   QFontDatabasePrivate *d = privateDb();
   if (!d)
       return;
 
   // Поискали его в кеше и если нашли и он уже загружен, то просто вышли. Все!
   QtFontFamily *family = 0;
   if(!fam.isEmpty()) {
       family = d->family(fam);
       if(family && family->loaded)
           return;
   } else if (d->count) {
       return;
   }
 
   // А вот если его в кеше нет или он не згружен, то работаем.
   HDC dummy = GetDC(0);
 
Это два. Улыбающийся
Записан
GDV1981
Гость
« Ответ #5 : Сентябрь 16, 2011, 11:39 »

Поставь точку в отладчике на
Код:
HDC dummy = GetDC(0);
и посмотри сам сколько раз туда и откуда приходит вызов  Подмигивающий

PS. А вообще, мне глубоко фиолетово, как оно там сделано. То что я хотел сделать, у меня получилось. Проблематика этой темы не то, что и сколько раз вызывается, а то, - почему нельзя использовать одновременно шрифты одного семейства разной насыщенности при добавлении методом addApplicationFont. На этот вопрос, судя по всему, ни у кого не нашлось достойного ответа.
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.107 секунд. Запросов: 21.