Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Основная";
Текущий архив: 2005.10.30;
Скачать: [xml.tar.bz2];

Вниз

Ширина выводимого текста на Canvas е   Найти похожие ветки 

 
Antonn ©   (2005-10-08 14:38) [0]

Здравствуйте! Помогите разобраться с такой проблемой - не могу определить ширину текста, выводимого на канвас TBitmap"а, если у текста стиль fsitalic. Уже весь день потерял на эту фигню:(
На форме лежит Комбобокс(имя CBF), у которого изменяю стиль его шрифта, и с этим стилем(а вообще там и название шрифта) нужно отрисовать на битмапе текст(хранимый в s:string). Но почему то не определяется правильно ширина текста, если текст "Итальянский". Если fsBold есть, то реагирует, но только на него. Уже и textwidth и GetTextExtentPoint пробую - не получается.

var s:string; _b:TBitmap; Size: TSize;
begin
_b:=TBitmap.Create;
 try
  _B.Canvas.Font.Name:="verdana";
  _B.Canvas.Font.Size:=10;
  _B.Canvas.Font.Style:=[];
 if fsBold in CBF.Font.style then
  _B.Canvas.Font.Style:=_B.Canvas.Font.Style+ [fsBold];
 if fsItalic in CBF.Font.style then
  _B.Canvas.Font.Style:=_B.Canvas.Font.Style+ [fsItalic];
  s:="8";

  GetTextExtentPoint(_B.canvas.Handle,pchar(s),length(s),Size);
  label29.Caption:=inttostr(Size.cx);
  label7.Caption:=inttostr(_B.Canvas.TextWidth(s));
 finally
  _b.Free;
 end;
end;


пример возвращает 9 при присутствии fsbold и 8 в остальных случаях, хотя при fsItalic ширина почти в 2 раза больше.


 
MBo ©   (2005-10-08 15:18) [1]

>при fsItalic ширина почти в 2 раза больше.
Не вижу такого эффекта для шрифта Verdana.


 
Antonn ©   (2005-10-08 16:29) [2]

MBo ©   (08.10.05 15:18) [1]
ну в 2 разf погорячился, взял шрифт типа "Пушкин", но факт, что размер ненормальный.
можно взять Tahoma.


 
Antonn ©   (2005-10-08 16:47) [3]

вообще мне нужно в рамку текст взять. вот иллистрацию реализовал:


_B.Canvas.Font.Size:=20;
..
_b.Width:=100;
_B.Height:=100;
_b.Canvas.TextOut(2,2,s);
_b.Canvas.Brush.Style:=bsclear;
_b.Canvas.Rectangle(2,2,Size.cx+2,Size.cy+2);
canvas.CopyRect(rect(0,0,100,100),_b.Canvas,rect(0,0,100,100));


Если шрифт будет Courier New или Comic Sans MS, то текст вылезает за рамку. Особенно если одновременно жирный и курсив. Я понимаю, что у rectangle правая и нижняя граница должна быть +1 пиксел, но все равно не помогает, текст сильно уж выступает.


 
MBo ©   (2005-10-08 16:49) [4]

На строке "Пушкин" со шрифтом Tahoma размером 20 выдается 95 для обычного и 97 для наклонного, так что разница все же есть.
Она меньше ожидаемой тобой, поскольку GetTextExtentPoint или использующая ее Canvas.TextWidth выдают приблизительный прямоугольник вывода. При выводе строки Windows использует сложные типографские правила, расстояние между буквами меняется для разных их пар (эти расстояния описаны в файле TTF-шрифтов). GetTextExtentPoint учитывает не все. Указанная строка, как легко увидеть, не особо отличается по длине в обычном и наклонном варианте, а при выделении конец последней наклонной буквы выходит за прямоугольник выделения. Это связано с тем, что GetTextExtentPoint, вероятно, не принимает во внимание величины A, B, C для символов. Эти величины можно получить с помощью функции GetCharABCWidths. Грубо говоря, A - промежуток от начала прямоугольника до реального начала символа, а C - расстояние от конца пр-ка до конца (самого правого черного пиксела) символа. Эти величины могут быть отрицательными, т.е. изображение символа выходит за прямоугольник.


 
MBo ©   (2005-10-08 16:50) [5]

>вообще мне нужно в рамку текст взять

GetCharABCWidths поможет.


 
Antonn ©   (2005-10-08 17:12) [6]

GetCharABCWidths поможет.
я ее сразу пытался использовать, но не пойму, как.
function GetCharABCWidths(DC: HDC; FirstChar, LastChar: UINT; const ABCStructs): BOOL;
а именно, что такое ABCStructs, какой тип?
Подсунул cardinal, получил в ответ 3(против 15 от GetTextExtentPoint), в чем она измеряет?
А FirstChar, LastChar - от 0 до length(s)? Тогда как она определит ширину моего текста, если она его не знает(текст)?


 
Antonn ©   (2005-10-08 17:13) [7]

Antonn ©   (08.10.05 17:12) [6]
от 0 до length(s)?

от 1 до length(s)?


 
Джо ©   (2005-10-08 17:19) [8]

[6] Antonn ©   (08.10.05 17:12)
> а именно, что такое ABCStructs, какой тип?

typedef struct _ABC {
 int     abcA;
 UINT    abcB;
 int     abcC;
} ABC, *PABC;
ты получаешь массив этих структур. Каждая структура для каждого символа в промежутке (range).


 
MBo ©   (2005-10-08 17:34) [9]

>Antonn ©   (08.10.05 17:12) [6]
Ну придется читать MSDN, разбираться, как работает функция.
Подробно описано это в книге Фень Юаня.

Есть еще один способ - но тоже придется поработать - использовать функции BeginPath/EndPath, шрифт - TTF должен быть. Нарисовать строку, получится траектория, преобразовать ее в регион PathToRegion, взять у региона окрйжающий прямоугольник GetRgnBox


 
Antonn ©   (2005-10-08 18:22) [10]

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

Вот, может кому пригодится(правда по правой и нижней координате нужно в результате +1 пиксель прибавлять):

procedure GetGrebaniyFontWidth(s:string; _F:Tfont; var _R:Trect);
const MaxPixelCount = MaxInt div SizeOf(TRGBTriple);
type TBack_Settings=(bs_Gradient_horizontal,bs_Gradient_vertical,bs_Picture);
    PRGBArray = ^TRGBArray;
    TRGBArray = array[0..MaxPixelCount-1] of TRGBTriple;
var _x,_y:integer; used:boolean;
   Row: PRGBArray;
   _B:TBitmap;
begin
_B:=TBitmap.Create;
 try
  _B.PixelFormat:=pf24bit;
  _B.Canvas.Font:=_F;
  _B.Width:=_B.Canvas.TextWidth(s)+trunc(_B.Canvas.TextWidth(s)*0.5);
  _B.Height:=_B.Canvas.TextHeight(s); _B.Canvas.FillRect(_B.Canvas.ClipRect);
  _R.Top:=_B.Height; _R.Bottom:=0; _R.Left:=_B.Width; _R.Right:=0;
  _B.Canvas.TextOut(0,0,s);

   for _y:=0 to _b.Height-1 do begin
      Row:=_b.ScanLine[_y];
      used:=true;
    for _x:=_B.Width-1 downto 0 do
     if used then begin
      if (Row[_x].rgbtRed=0) then begin
        used:=false;
        if _r.Right<_x then _R.Right:=_x;
      end;
    end;
      used:=true;
    for _x:=0 to _B.Width-1 do
     if used then begin
      if (Row[_x].rgbtRed=0) then begin
        used:=false;
        if _r.Left>_x then _R.Left:=_x;
      end;
    end;
       used:=true;
    for _x:=0 to _B.Width-1 do
     if used then begin
      if (Row[_x].rgbtRed=0)and(_R.Top>_y) then begin
        used:=false;
        _R.top:=_y;
      end;
    end;
      used:=true;
    for _x:=0 to _B.Width-1 do
     if used then begin
      if (Row[_x].rgbtRed=0)and(_R.Bottom<_y) then begin
        used:=false;
        _R.Bottom:=_y;
      end;
    end;
   end;
 finally
  _B.Free;
 end;
end;


Всем спасибо!


 
Джо ©   (2005-10-08 19:44) [11]


> [10] Antonn ©   (08.10.05 18:22)

Хм. Все же, вариант, предложенный [9] MBo © -- гораздо лучше. Вот, например, на его основе можно написать следующую функцию:

function GetStringRect (const AString: string; const X,Y: Integer; AFont: TFont): TRect;
var
 DC: HDC;
 RGN: HRGN;
begin
 DC := CreateCompatibleDC(0);
 Win32Check(DC<>0);
 try
   SelectObject(DC, AFont.Handle);
   Win32Check(BeginPath(DC));
   try
     TextOut(DC,X,Y,PChar(AString),Length(AString));
   finally
     EndPath(DC);
   end;

   RGN := PathToRegion(DC);
   Win32Check(RGN<>0);
   try
     GetRgnBox(RGN,Result);
   finally
     DeleteObject(RGN);
   end;

 finally
   DeleteDC(DC);
 end;
end;

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

procedure TForm10.Button1Click(Sender: TObject);
var
 Rct: TRect;
 Size: TSize;
begin
 Image1.Canvas.Font.Name := "Tahoma";
 Image1.Canvas.Font.Size := 14;
 Image1.Canvas.Font.Style := [fsItalic];

 Rct := GetStringRect(Caption,10,20,Image1.Canvas.Font);

 Image1.Canvas.Brush.Style := bsSolid;
 Image1.Canvas.Brush.Color := clRed;
 Image1.Canvas.FillRect(Rct);
 Image1.Canvas.Brush.Style := bsClear;
 Image1.Canvas.TextOut(10,20,Caption);
end;


 
Fay ©   (2005-10-09 04:42) [12]

DrawText + DT_CALCRECT
Ку?


 
Джо ©   (2005-10-09 04:56) [13]


>  [12] Fay ©   (09.10.05 04:42)
> Ку?


procedure RotateFont (AFont: TFont; Angle: Integer);
var
 LogFont : TLogFont;
begin
 GetObject(AFont.Handle, SizeOf(TLogFont), @LogFont);
 LogFont.lfEscapement := Angle*10;
 AFont.Handle := CreateFontIndirect(LogFont);
end;

procedure TForm10.Button1Click(Sender: TObject);
var
Rct: TRect;
Size: TSize;
begin
 with Image1.Canvas.Font do
 begin
   Name := "Tahoma";
   Size := 14;
   Style := [fsItalic];
 end;

 RotateFont(Image1.Canvas.Font,25);

 Rct.Left := 50;
 Rct.Top := 50;
 if DrawText(Image1.Canvas.Handle,PChar(Caption),Length(Caption),Rct,
   DT_SINGLELINE or DT_CALCRECT) = 0 then
      RaiseLastOSError;

 Image1.Canvas.Brush.Style := bsSolid;
 Image1.Canvas.Brush.Color := clRed;
 Image1.Canvas.FillRect(Rct);

 Image1.Canvas.Brush.Style := bsClear;
 Image1.Canvas.TextOut(50,50,Caption);
end;


Кю! ;-)


 
wicked ©   (2005-10-09 23:46) [14]

GetFontLanguageInfo + GetCharacterPlacement.... без извращений с рендерингом в битмап.... учитывает kerning pairs, лигатуры и прочие трутайповые фишечки....
выдаст всё, даже рОдную маму... :)

ЗЫ легких путей нету... есть еще более сложные.... подробности в msdn...


 
wicked ©   (2005-10-09 23:49) [15]

ЗЗЫ афаир, [14] не поддерживает вращение шрифтов....


 
Джо ©   (2005-10-10 00:32) [16]


>  [15] wicked ©   (09.10.05 23:49)
> ЗЗЫ афаир, [14] не поддерживает вращение шрифтов....

ЗЫ. Зато [11] - поддерживает ;-)



Страницы: 1 вся ветка

Форум: "Основная";
Текущий архив: 2005.10.30;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.5 MB
Время: 0.039 c
14-1128700447
ArtemESC
2005-10-07 19:54
2005.10.30
Алгоритм возведения числа в произвольную степень


3-1127203717
suharew
2005-09-20 12:08
2005.10.30
Индексы, поиск


11-1109765207
Boguslaw Brandys
2005-03-02 15:06
2005.10.30
Form.StatusCtl


2-1128851876
ArtemESC
2005-10-09 13:57
2005.10.30
Ascii => DWord


2-1128592502
root
2005-10-06 13:55
2005.10.30
Типы полей в Access





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский