• 0
Вадим Смоленский

Вертикальное смещение юникодных шрифтов

Вопросы

В своем проекте (это японско-русский словарь) мне приходится выводить на TImage.Bitmap.Canvas хранящиеся в юникоде иероглифы, причем шрифт пользователь может выбрать сам. Столкнулся с неприятным явлением: разные шрифты располагают выводимый символ на разной высоте, в результате чего иероглиф часто не вписывается в предназначенную для него область. По умолчанию принят шрифт Tahoma, с ним всё хорошо:

JI_tahoma.png.756f1d23bc94c79ad622f20e25f44271.png

Но вот шрифт Yu Mincho:

JI_Yu_Mincho.png.f63e2b85d50bf8ae202b587c7b30f652.png

А вот, еще хлеще, Kozuka Gothic:

JI_Kozuka_Gothic.png.1856d8711b3cd526b42c278ddf699957.png

Вопрос: что за параметр регулирует вертикальное смещение, можно ли его вынуть и с ним работать?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

29 ответов на этот вопрос

  • 0
48 минут назад, dnekrasov сказал:

Попробуйте сместить вверх на TTextMetric.tmExternalLeading

Компилятору не нравится, говорит: method identifier expected.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
5 минут назад, Вадим Смоленский сказал:

Компилятору не нравится, говорит: method identifier expected

А как Вы его используете? TextMetric надо предварительно загрузить для выбранного шрифта API-шными функциями (посмотрите FMX.FontGlyphs.Win.TWinFontGlyphManager.LoadResource)

Изменено пользователем dnekrasov

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
4 минуты назад, dnekrasov сказал:

посмотрите FMX.FontGlyphs.Win.TWinFontGlyphManager.LoadResource

Простите мне мое невежество: это где нужно смотреть? В хелпах? На сайте Embarcadero? Где-нибудь еще?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
2 минуты назад, Вадим Смоленский сказал:

это где нужно смотреть

Это использование WinAPI. В частности функция GetTextMetrics. Смотреть лучше всего в MSDN

В модуле FMX.FontGlyphs.Win в методе TWinFontGlyphManager.LoadResource просто хорошо видно как с ней работать.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
2 часа назад, dnekrasov сказал:

В модуле FMX.FontGlyphs.Win в методе TWinFontGlyphManager.LoadResource просто хорошо видно как с ней работать.

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

function FontVertShift (F: TFont): Single;
var Metrics: TTextMetric;
    DC: HDC;
    HF: HFont;
begin
  HF:=SelectObject(DC, F.Handle);
  SelectObject(DC, HF);
  GetTextMetrics(DC, Metrics);
  result := Metrics.tmExternalLeading
end;

Затык в том, что FireMonkey не предоставляет Handle для шрифта. Как же передать сюда шрифт?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
1 час назад, Вадим Смоленский сказал:

Как же передать сюда шрифт?

Как-то так:

function FontVertShift (F: TFont): Single;
var
  Metrics: TTextMetric;
  DC: HDC;
  HF: HFont;
begin
  DC := CreateCompatibleDC(0);
  try
    HF := CreateFont(-Round(F.Size * GetDCScale(DC)), 0, 0, 0, FontWeightToWinapi(F.StyleExt.Weight),
      Cardinal(not F.StyleExt.Slant.IsRegular),
      0, 0, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
      CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
      DEFAULT_PITCH or FF_DONTCARE, PChar(F.Family));
    if HF = 0 then
      Exit(0);

    try
      SelectObject(DC, HF);
      GetTextMetrics(DC, Metrics);
    finally
      DeleteObject(HF);
    end;

    Result := Metrics.tmExternalLeading
  finally
    DeleteDC(DC);
  end;
end;

 

Изменено пользователем dnekrasov

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
28 минут назад, dnekrasov сказал:

Как-то так:

Запускается, но tmExternalLeading на любом шрифте возвращает 0.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

Вот, кстати, tmInternalLeading больше похоже на то, что я ищу. Он недостаточно смещает, но пропорционально. Попробую подобрать коэффициент...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

А ещё загляните сюда - здесь много того, что не помешает знать при работе со шрифтами.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

Код самый простой:

with MyImage.Bitmap.Canvas do
begin
 BeginScene;
 Font.Family := 'Tahoma';
 Font.Style := [];
 Font.Size := 60;
 FillText (RectF (0, 0-FontVertShift(Font), Width-1, Height-1), '字' , False , 1 , [] , TTextAlign.Leading, TTextAlign.Leading);
 EndScene;
end;

Код функции брал ваш, только tmExternalLeading заменил на  tmInternalLeading.

Шрифты пробовал такие (после названия - сначала значение tmInternalLeading, затем tmExternalLeading):

MS Mincho:  0, 0  (проблемы нет)
MS Gothic:  0, 0  (проблемы нет)
Tahoma:  12, 0  (в самый раз)
Yu Gothic:  17, 19  (недостаточно)
Kozuka Gothic Pr6N B:  50,0  (недостаточно)
Meiryo:  30, 0  (чересчур)

За ссылку спасибо, заглянул - но там, как я понял, материал для разработчиков шрифтов. Мои амбиции так далеко не простираются.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

Вообще, судя по спецификации, смещение надо вычислять так

Metrics.tmAscent + Metrics.tmDescent + Metrics.tmExternalLeading - F.Size

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

Почти для всех вышеперечисленных шрифтов по этой формуле получается как раз значение tmInternalLeading. Единственное исключение - Yu Gothic, для него получилось 36, против прежнего 17. Было недостаточно, стало чересчур. Подход по-прежнему работает только для шрифта Tahoma.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

@Вадим Смоленский, попробуйте для шрифта Yu Gothic рассчитывать смещение как Font.Size * 0.408576 а для Kozuka Gothic Pr6N B - Font.Size * 1.0033 (мне просто интересно - правильно ли я рассчитал коэффиценты)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

Да, с этими коэффициентами на этих шрифтах выходит отлично! Что же тут за калькуляция?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
7 часов назад, Вадим Смоленский сказал:

Да, с этими коэффициентами на этих шрифтах выходит отлично!

Во как! Значит GetTextMetrics неправильно рассчитывает tmExternalLeading  и tmInternalLeading.

7 часов назад, Вадим Смоленский сказал:

Что же тут за калькуляция?

Сам расчет описан здесь

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0

@Вадим Смоленский, а можно как-то увидеть Ваш проект - а то дочка японский учит - сказала, что хороший словарик всегда пригодится :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
1 час назад, dnekrasov сказал:

@Вадим Смоленский, а можно как-то увидеть Ваш проект - а то дочка японский учит - сказала, что хороший словарик всегда пригодится :)

Да, конечно. Это ЯРКСИ (японско-русский компьютерный словарь иероглифов). Если ваша дочка про него еще не слышала, то вот он где:

http://www.susi.ru/yarxi

Там есть версии для мобильных платформ тоже. Версия 7.7 для Windows, которая вывешена, разрабатывалась еще в Delphi 6, которой я продолжал пользоваться до 2016 года. Сейчас перешел наконец в современную программную среду и целый год потратил на переделку интерфейса. Идет бета-тестирование; надеюсь через неделю-другую вывесить версию 8.0, которая будет несравнимо лучше. Так что есть смысл со скачиванием немножко подождать.

Документацию изучаю, но пока не смог понять, как именно нужно действовать...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
18 минут назад, Вадим Смоленский сказал:

Если ваша дочка про него еще не слышала

Говорит, что слышала - однокурсники нахваливают, а преподаватели советуют.

 

20 минут назад, Вадим Смоленский сказал:

Документацию изучаю, но пока не смог понять, как именно нужно действовать

Посмотрите раздел "Baseline to Baseline Distances". Метрики шрифта я вытягивал с помощью редактора FontForge - как это сделать программно - не знаю, надо досконально разбираться в формате.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
35 минут назад, dnekrasov сказал:

Посмотрите раздел "Baseline to Baseline Distances".

Мало что можно понять из этого раздела. Вот, дают формулу для external leading:

MAX( 0, LineGap - ((usWinAscent + usWinDescent) - (Ascender - Descender)))

Что такое LineGap? Какая разница между usWinAscent и Ascender? Как они оба соотносятся с нашим Metrics.tmAscent?

Трудно поверить, что я один во всем интернете столкнулся с этой проблемой. Где-то все-таки должен быть готовый алгоритм.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
33 минуты назад, Вадим Смоленский сказал:

Что такое LineGap? Какая разница между usWinAscent и Ascender?

Про это можно почитать здесь

42 минуты назад, Вадим Смоленский сказал:

Как они оба соотносятся с нашим Metrics.tmAscent

Как я понял:

Metrics.tmAscent = unitsPerEm / Font.Size * uWinAscend

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
17 минут назад, dnekrasov сказал:

Про это можно почитать здесь

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

Да, если у вашей дочки есть телефон с андроидом, то буду рад выписать ей бесплатную лицензию на полную андроидную версию ЯРКСИ. Все-таки вы мне помогли, в какой-то мере прояснили вопрос. Пишите на vsСОБАКАsusi.ru

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 0
6 часов назад, Вадим Смоленский сказал:

Там есть версии для мобильных платформ тоже.

Для мобильных платформ тоже на Delphi сделано?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Для публикации сообщений создайте учётную запись или авторизуйтесь

Вы должны быть пользователем, чтобы оставить комментарий

Создать учетную запись

Зарегистрируйте новую учётную запись в нашем сообществе. Это очень просто!

Регистрация нового пользователя

Войти

Уже есть аккаунт? Войти в систему.

Войти


  • Похожий контент

    • От Вадим Смоленский
      Некоторые юзеры (видимо, из близоруких) прибегают к масштабированию экрана и требуют, чтобы ваше приложение тоже масштабировалось. Когда мой проект был в VCL, я смотрел на параметр Screen.PixelsPerInch и учитывал его значение в своем коде. В FireMonkey этого нет. Я пытался понять, как это делать теперь, и окончательно запутался. Microsoft на официальном сайте упоминает функцию SystemParametersInfo с параметром SPI_GETLOGICALDPIOVERRIDE, но делает оговорку, что такую конфигурацию лучше не использовать - дескать, в новых версиях Windows она не будет работать. Взамен рекомендуют функцию GetScaleFactorForDevice, но тут же заявляют, что под Windows 8 и она работать не будет, а будет работать только GetScaleFactorForMonitor. Голова кругом. Нет ли у кого, случаем, готового решения, которое работало бы на любых Windows - хотя бы от семерки до десятки?
      Еще один момент, который я не могу понять - в Windows 10 в настройках экрана, помимо масштаба в процентах, можно выбирать еще и разрешение в пикселях. По-моему, раньше такого разделения не было, хотя могу ошибаться. Связаны ли эти настройки друг с другом? И как они соотносятся со старым добрым DPI?
    • От Вадим Смоленский
      Работая в Berlin, подправил стиль для одного компонента посредством вызова "Edit Custom Style" во всплывающем меню. Потом перешел на Tokyo; потом поменял компьютер и установил на него Tokyo заново. Сейчас вижу, что компонент отображается в стиле по умолчанию. Никаких сообщений по поводу пропавшего стиля не припомню. Где мне найти и как опять подключить мой отредактированный стиль?
    • От Вадим Смоленский
      В своем проекте (это японско-русский словарь) я часто вывожу текст на TImage методом TCanvas.FillText, и результат выглядит так:

      Один из тестеров, у которого установлена Windows XP, прислал скриншот того, что выводится у него:

      Ровно такую же безобразную картину я видел у себя, когда, экспериментируя с быстродействием, вставлял в файл dpr операторы  FMX.Types.GlobalUseDX:=False или FMX.Types.GlobalUseDirect2D:=False
      То ли в XP эти установки приняты по умолчанию, то ли так сконфигурирована система у тестера. Первое, что приходит в голову - вставить в код присвоение обеим упомянутым переменным значения True. Но прежде, чем предпринять такую попытку, хотелось бы услышать мнение экспертов. В чем здесь корень зла?
    • От Вадим Смоленский
      Записываю русский текст в файл:
      procedure WriteToFile; var F: TextFile; S: string; begin S:='Русский текст'; AssignFile(F,MyFile.txt'); ReWrite(F); WriteLn(F,S); CloseFile(F); end; Тип данных string - по идее, то же, что WideString. То бишь, в строку S записан юникод. Тем не менее, открывая потом файл, вижу, что русский текст сохранен в ANSI. Как добиться его сохранения в юникоде?
    • От Вадим Смоленский
      Писал на Delphi 10.2 Tokyo под Windows. У тестеров приложение валится при первой же попытке обращения к БД с сообщением "unable to open database file". У меня на компе всё нормально. Файл sqlite3.dll версии 3.21.0.0 в дистрибутив включен, кладется рядом с исполнимым файлом. Что может быть не так?
    • От Вадим Смоленский
      В своем VCL-проекте я использовал оригинальный способ застраховаться от повторного запуска приложения. Обычно это делают через Mutex. Но умные люди на experts-exchange.com много лет назад посоветовали мне следующее:
      // в главном окне: protected procedure CreateParams(var Params: TCreateParams); override; procedure TMyApp.CreateParams(var Params: TCreateParams); begin inherited CreateParams(Params); Params.WinClassName:='MyAPP String ID'; end; // В файле .dpr: var AWnd, A1Wnd: HWND; AWnd:=FindWindow('MyApp String ID',nil); if IsWindow(AWnd) then begin A1Wnd := GetWindow(AWnd, GW_OWNER); if IsWindow(A1Wnd) then AWnd := A1Wnd; ShowWindow(AWnd, SW_RESTORE); SetForegroundWindow(AWnd); end else begin Application.Initialize; ....... end Способ хорош тем, что он не просто блокирует повторный запуск, а выводит уже запущенное приложение на передний план, тычет юзера в него носом. Но при переходе на FireMonkey возникла закавыка: компилятору не нравится TCreateParams. Говорит: undeclared identifier. Чем бы его заменить?
    • От Вадим Смоленский
      Перейдя с Берлина на Токио, столкнулся с неприятным феноменом. Раньше, выводя на экран форму с обилием TLabel, которые нужно было заполнить текстом и выровнять, я спокойно ставил везде AutoSize=True, присваивал нужные значения полям Text, после чего ширина каждого TLabel принимала новое значение, и мой код их красиво выравнивал. Теперь же ширина не меняется до полной отрисовки на экране всего и вся. Соответственно, на этапе выравнивания код оперирует со значениями ширины, которые были еще на этапе дизайна. Как следствие - всё наперекосяк! Что делать?
    • От Вадим Смоленский
      Windows, Berlin. Когда TWebBrowser на форме получает фокус, он начинает перехватывать все нажатия клавиш. До FormKeyDown управление уже не доходит. Отключение свойства браузера CanFocus не помогает, своего события OnKeyDown у него нет. Как быть? В идеале хотелось бы оставить браузеру навигационные клавиши (стрелки, PgDn, PgUp, Home, End), но все остальные отправлять на форму. Возможно ли это?
    • От Вадим Смоленский
      Работаю в Windows с FireDAC (SQLite). При каждом обращении к базе данных экранный курсор превращается в песочные часы с надписью SQL. Пользователи моего продукта далеки от программирования и ни про какой SQL не слыхивали, им это совершенно ни к чему. Как мне подавить этот феномен? В свойствах TFDConnection я ничего на эту тему не нахожу...
    • От Вадим Смоленский
      Мастерю из TRectangle всплывающую подсказку с тенью и анимацией (увеличиваю Opacity от 0 до 1). Без TFloatAnimation тень нормально отображается, а с анимацией пропадает. Действительно ли здесь есть какие-то ограничения, или проблему можно решить?
  • Последние посетители   0 пользователей онлайн

    Ни одного зарегистрированного пользователя не просматривает данную страницу