Автор: Maks150988
Дата сообщения: 28.03.2012 13:27
Всем привет у меня вот такая задача. Необходимо отрисовать массив слов так как это делает функция DrawText - если ширина строки превышает ширину прямоугольника, в котором происходит отрисовка, то часть слов переносится на следующую строку. Ну вобщем смысл понятен я надеюсь. Задача усложняется тем, что это не просто массив слов, а массив записей, в каждой из них сам текст и свойства шрифта, с которыми будет отрисовано слово - жирность, подчеркнутость и наклон. Привожу свои куски кода.
Сама функция для отрисовки слова. Если прямоугольник нулевой - высчитывает ширину и высоту слова. Если нет - выводит текст.
type
tagDrawItem = record
pszText : AnsiString;
bWeight : Boolean;
bItalic : Boolean;
bUnderline: Boolean;
end;
PDrawItem = ^TDrawItem;
TDrawItem = tagDrawItem;
function Measure(hWnd: HWND; hdcIn: HDC; rcPaint: PRect; Data: TDrawItem): TSize;
const
dwHeight: Array [Boolean] of DWORD = (FW_NORMAL, FW_BOLD);
var
lf : TLogFont;
dwRet : DWORD;
phfnt : HFONT;
hfnt : HFONT;
oldfnt : HFONT;
txtSize: TSize;
rcTop : Integer;
begin
ZeroMemory(@Result, SizeOf(TSize));
phfnt := SendMessage(hWnd, WM_GETFONT, 0, 0);
if (phfnt <> 0) then
begin
ZeroMemory(@lf, SizeOf(TLogFont));
dwRet := GetObject(phfnt, SizeOf(TLogFont), @lf);
if (dwRet <> 0) then
begin
lf.lfWeight := dwHeight[Data.bWeight];
lf.lfItalic := Integer(Data.bItalic);
lf.lfUnderline := Integer(Data.bUnderline);
hfnt := CreateFontIndirect(lf);
end;
end;
if (hfnt <> 0) then
oldfnt := SelectObject(hdcIn, hfnt)
else
oldfnt := 0;
GetTextExtentPoint32(
hdcIn,
LPCSTR(Data.pszText),
lstrlen(LPCSTR(Data.pszText)),
txtSize
);
if (rcPaint <> nil) then
begin
rcTop := rcPaint.Top + (((rcPaint.Bottom - rcPaint.Top) - txtSize.cy) div 2);
TextOut(
hdcIn,
rcPaint.Left,
rcTop,
LPCSTR(Data.pszText),
lstrlen(LPCSTR(Data.pszText))
);
end
else
begin
Result.cx := txtSize.cx;
Result.cy := txtSize.cy;
end;
if (hfnt <> 0) then
DeleteObject(hfnt);
if (oldfnt <> 0) then
SelectObject(hdcIn, oldfnt);
end;
И самое главная процедура для отрисовки слов на контексте. Сделано как шаблон для отладки. Понять хотя бы принцип.
procedure Draw2(h: HWND; dc: HDC; data: Array of TDrawItem; rc: PRect);
var
rect: TRect;
tm: TTextMetric;
i: Integer;
size: TSize;
l: Integer;
_f: Integer;
_l: Integer;
lead: Integer;
id: Integer;
begin
CopyRect(rect, rc^);
GetTextMetrics(dc, tm);
FillRect(dc, rect, GetStockObject(WHITE_BRUSH));
// сброс значений параметров.
l := 0;
_f := Low(data);
_l := High(data);
lead := tm.tmInternalLeading + tm.tmExternalLeading;
// перебор всех слов в строке.
for i := Low(data) to High(data) do
begin
// извлекаем ширину и высоту слова в пикселях. добавляем к ширине пробел.
size := Measure(h, dc, nil, data[i]);
Inc(l, size.cx);
Inc(l, tm.tmAveCharWidth);
// проверяем, выходит ли за пределы ширины окна суммарная ширина слов,
// которые отрисуем. ширину слов учитываем без последнего пробела.
if ((l - tm.tmAveCharWidth) > (rc^.Right - rc^.Left)) then
begin
// перед отрисовкой слов устанавливаем индекс последнего слова, которое
// будет завершающим в рисуемой строке.
_l := i - 1;
// удаляем ширину лишнего слова и ширину пробела после него.
Dec(l, size.cx);
Dec(l, tm.tmAveCharWidth);
// устанавливаем границы для отрисовки каждого слова в строке. для начала
// сдвигаем левую координату на определенное количество пикселей, чтобы
// уместить текст строки по центру от краев окна.
rect.Left := ((rc^.Right - rc^.Left) - l) div 2;
rect.Bottom := rect.Top + size.cy + lead;
// проходимся по индексам слов, которые отрисуем в строке. у каждого слова
// узнаем ширину и высоту. устанавливаем относительно этих показаний
// координаты правой границы, в которых будем отрисовывать слово. после
// отрисовки сдвигаем координату левой границы прямоугольника для отрисовки
// следующего слова.
for id := _f to _l do
begin
size := Measure(h, dc, nil, data[id]);
rect.Right := rect.Left + size.cx;
Measure(h, dc, @rect, data[id]);
//FrameRect(dc, rect, GetStockObject(LTGRAY_BRUSH));
Inc(rect.Left, size.cx);
Inc(rect.Left, tm.tmAveCharWidth);
end;
// так как ширина строки превысила ширину окна, то необходимо отрисовать
// оставшуюся часть строки в новых координатах. для этого сдвигаем
// верх и низ прямоугольника.
Inc(rect.Top, size.cy);
Inc(rect.Top, lead);
Inc(rect.Bottom, size.cy);
Inc(rect.Bottom, lead);
// сбрасываем параметр ширины отрисовываемой части строки. устанавливаем
// индексы для итерации массива оставшихся слов для их отрисовки.
l := 0;
_f := i;
_l := High(data);
end;
// if ((l - tm.tmAveCharWidth) < (rc^.Right - rc^.Left)) and (i = High(data)) then
if (i = High(data)) then
begin
Dec(l, tm.tmAveCharWidth);
rect.Left := ((rc^.Right - rc^.Left) - l) div 2;
rect.Bottom := rect.Top + size.cy + lead;
for id := _f to _l do
begin
size := Measure(h, dc, nil, data[id]);
rect.Right := rect.Left + size.cx;
Measure(h, dc, @rect, data[id]);
//FrameRect(dc, rect, GetStockObject(LTGRAY_BRUSH));
Inc(rect.Left, size.cx);
Inc(rect.Left, tm.tmAveCharWidth);
end;
end;
end;
end;
Меняю размеры окна - некоторые строчки по центру, а некоторые со сдвигом. Кто знает как сделать все корректно - подскажите что подправить в if else условии. Не додумаюсь как нормлаьно сделать.