Maks150988
Advanced Member | Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору Всем привет у меня вот такая задача. Необходимо отрисовать массив слов так как это делает функция 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 условии. Не додумаюсь как нормлаьно сделать. |