• 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

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

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


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

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

  • 0
2 часа назад, wamaco сказал:

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

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

Кстати, я еще не потерял надежды услышать Ваш комментарий к вопросу о сохранении стиля в разделе "Default". Очень обяжете, если прольете свет.

P.S. Отбой! С сохранением стиля помог dnekrasov. Ему мое спасибо. Но и вам тоже.

Изменено пользователем Вадим Смоленский

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


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

В своем проекте (это японско-русский словарь) мне приходится выводить на TImage.Bitmap.Canvas хранящиеся в юникоде иероглифы

Чё-то я изначально затормозил... Если Вам просто надо отобразить иероглиф - то можно ведь сделать всё намного проще - через TPathLabel (она должна сама обрезать все отступы)

var
  F: TFont;
  FG: TFontGlyph;
  bmp: TBitmap;
begin
  F := TFont.Create;
  try
    F.Family := 'Kozuka Gothic Pr6N B';
    F.Size := 60;

    FG := TFontGlyphManager.Current.GetGlyph(Char.ConvertToUtf32('字', 0), F, 1, [TFontGlyphSetting.Path]);
    try
      PathLabel.Width := FG.Path.GetBounds.Width;
      PathLabel.Height := F.Size;
      PathLabel.Data := FG.Path;
      // ну и если всё-таки bitmap-ка нужна
      bmp := PathLabel.MakeScreenshot;
      try
        // что-то делаем с битмапкой
      finally
        bmp.Free;
      end;
    finally
      FG.Free;
    end;
  finally
    F.Free;
  end;
end;

 

Изменено пользователем dnekrasov
В коде замена "PathLabel.Width := F.Size;" на "PathLabel.Width := FG.Path.GetBounds.Width;"

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


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

В общем случае мне нужно выводить не просто одиночный иероглиф, а несколько японских символов, соседствующих на TCanvas с кириллицей и латиницей. Там это едва ли подойдет. Но на главную панель словарной статьи выводится именно одиночный иероглиф, крупным шрифтом (как показано на моих картинках). Именно там наиболее возможен выбор пользователем нестандартного шрифта. Так что вариант с TPathLabel я непременно опробую, спасибо!

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


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

Там это едва ли подойдет

Ну почему же:

var
  F: TFont;
  PD: TPathData;
begin
  F := TFont.Create;
  try
    F.Family := 'Kozuka Gothic Pr6N B';
    F.Size := 60;
    with TBitmap.Create do
    try
      Canvas.Font.Assign(F);
      PD := TPathData.Create;
      try
        if Canvas.TextToPath(PD, RectF(0, 0, 10000, F.Size), '字字字 AsD АыЩ', False, TTextAlign.Leading, TTextAlign.Leading) then
        begin
          PathLabel.Width := PD.GetBounds.Width;
          PathLabel.Data := PD;
        end;
      finally
        PD.Free;
      end;
    finally
      Free;
    end
  finally
    F.Free;
  end;
end;

 

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

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


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

Спустя три недели все-таки решил опробовать ваш вариант с TPathData, а то тестировщики продолжают жаловаться. Сделал, не задействуя TPathLabel, вот так:

with ThisImage.Bitmap.Canvas do
begin
 PD:=TPathData.Create;
 BeginScene;
 try
  if TextToPath(PD,RectF(X,Y,Width-1,Height-1),TheText,False,TTextAlign.Leading,TTextAlign.Leading) then
  FillPath(PD,1);
 finally
  PD.Free;
 end;
 EndScene;
end;

Увы, результат ровно тот же, с нежелательными вертикальными смещениями.

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


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

Увы, результат ровно тот же, с нежелательными вертикальными смещениями.

Перед FillPath попробуйте вставить

PD.Translate(-PD.GetBounds.Left, -PD.GetBounds.Top);

 

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


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

А еще можно попробовать FillText со смещением -PD.GetBounds.Left, -PD.GetBounds.Top

Я и рад бы это опробовать но не понял, куда именно нужно вставлять такое смещение в случае FillText.

А вставка PD.Translate помогла, теперь иероглиф надежно прилепляется к левому и верхнему краям. Хорошо бы еще научиться выравнивать его, чтобы он не лепился к этим краям, а вставал бы в середину указанного прямоугольника. Не приходит пока в голову, как это сделать.

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


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

Я и рад бы это опробовать но не понял, куда именно нужно вставлять такое смещение в случае FillText

Ну как же? В первом параметре идет прямоугольник в который выводится текст. Ему и сделать Offset(-PD.GetBounds.Left, -PD.GetBounds.Top)

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

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

Всё просто:

PD.Translate(-PD.GetBounds.Left + DestRect.Left + (DestRect.Width - PD.GetBounds.Width) / 2, -PD.GetBounds.Top + DestRect.Top + (DestRect.Height - PD.GetBounds.Height) / 2)

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


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

FillText с Offset, к сожалению, не срабатывает, иероглиф смещается по вертикали. Стало быть, приму вариант с FillPath. Спасибо!

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


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

Впрочем, не всё так гладко. Вот, например, как теперь выводится иероглиф "Единица", состоящий из одного-единственного горизонтального штриха:

ichi.png.813e8a2e44d8237cba6c89fbb658cff5.png

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

Хотя, может, есть способ вытащить откуда-то из TPathData информацию о реальных размерах символа и, исходя из нее, смещать его туда-сюда? Было бы замечательно.

 

 

 

Изменено пользователем Вадим Смоленский

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


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

Отвечаю сам себе: да, есть такой способ! У TPathData есть метод GetBounds:

if TextToPath(PD,DestRect,TheKanjiString,False,TTextAlign.Leading,TTextAlign.Leading) then
begin
 W:=(Width-PD.GetBounds.Width)/2;
 H:=(Height-PD.GetBounds.Height)/2;
 PD.Translate(X+W-PD.GetBounds.Left,Y+H-PD.GetBounds.Top);
 FillPath(PD,1);
end;

Теперь всё красиво:

ichi2.png.0aa5f411640e1aa8c349761623332e31.png

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


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

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

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

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

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

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

Войти

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

Войти


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

    • От Вадим Смоленский
      Наткнулся на странное поведение FMX-приложений под Windows. Если развернуть окно приложения на весь экран, потом воспользоваться системной кнопкой "Свернуть все окна" (в правом нижнем углу экрана) и после этого опять вызвать окно приложения, то оно выводится уже не развернутым, а обычным. Появились недовольные таким поведением пользователи. Можно ли их чем-нибудь утешить?
    • От Вадим Смоленский
      Когда мое приложение для Windows компилировалось в Delphi 6, линуксоиды успешно запускали его в Wine. Рапортовали, что неидеально, но работать можно. Теперь, после перевода на FireMonkey, один тестировщик попробовал запустить его в Wine 3.0 и сообщает:
      "Результат неутешителен - программа в нём практически не работает. Интерфейс сломан, кнопки интерфейса не нажимаются (в большинстве; а если и нажимаются, то возникают всякие ошибки)."
      Не сталкивался ли кто-нибудь здесь с этой проблемой? Я тихо надеюсь, что существует некая волшебная кнопка, которая поможет всё наладить.
      Приложение использует FireDAC и SQLite. Сторонних компонентов никаких.
    • От Вадим Смоленский
      Один тестер моего проекта (один изо всей команды!) поставил меня в полный тупик. У него Windows 7, и он запускает приложение на разных компьютерах, выбирая разные темы - то с Aero, то без. Почему-то приложение выглядит у него прилично лишь при запуске с Aero; при запуске же с базовой темой наблюдаются искажения отрисовки и функциональные нарушения. А на одном компьютере с Aero-темой приложение может не запуститься вовсе: например, если установлено системное масштабирование шрифтов 135%. Тогда при запуске появляется сообщение об ошибке в модуле ntdll.dll.
      Не мог бы кто-нибудь разъяснить, как установка темы связана с параметрами Windows API? Можно ли как-то нивелировать эту разницу? На что вообще в таких случаях нужно обратить внимание?
    • От Вадим Смоленский
      Разрабатывая свою софтину, о сенсорных экранах я совсем не думал (отстал от прогресса). Но один тестер сейчас погонял текущую сборку на планшете. Оказалось, что почти всё работает хорошо, но в некоторых местах по нажатию пальцем отрабатывается не OnClick и не OnMouseUp, а почему-то OnMouseMove. Прежде чем изобретать велосипед, хочу спросить у знающих людей: а как вообще лучше организовывать работу с мышью/пальцем, чтобы она одинаково хорошо шла в обоих вариантах? Есть ли здесь четкие рекомендации? Вот, например, я вижу событие OnTap: стоит ли задействовать его? Или это только для мобильных платформ?
    • От Вадим Смоленский
      Некоторые юзеры (видимо, из близоруких) прибегают к масштабированию экрана и требуют, чтобы ваше приложение тоже масштабировалось. Когда мой проект был в 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. Чем бы его заменить?
  • Последние посетители   0 пользователей онлайн

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