Delphi: Как нарисовать текст требуемой ширины и количества строк с многоточием в конце?

Мне нужно нарисовать текст в ячейке таблицы с фиксированной шириной (в пикселях) и фиксированным количеством текстовых строк. Если текст обрезается прямоугольником ячейки, он должен заканчиваться многоточием. Проблема в том, что я не могу правильно рассчитать текстовый прямоугольник (или процедура TextRect/DrawText работает неправильно, я не уверен).

Я попытался использовать этот метод вычисления текстового прямоугольника:

var
  TextRect: TRect;
  tm: TEXTMETRIC;
...
GetTextMetrics(Canvas.Handle, tm);
TextLineHeight := tm.tmHeight + tm.tmExternalLeading;

TextRect.Bottom := TextRect.Top + TextLineHeight * NumberOfLines;
Canvas.TextRect(TextRect, 'some long long long text',
  [tfTop, tfLeft, tfEndEllipsis, tfWordBreak]);

Прямоугольник отсечения рассчитан правильно, но многоточие не появляется.

Многоточие появляется, когда я уменьшаю высоту прямоугольника отсечения на 1 пиксель:

TextRect.Bottom := TextRect.Top + TextLineHeight * NumberOfLines - 1;

Но тогда некоторые пиксели нижней строки моего текста обрезаются.

Как это сделать правильно?


person Andrew    schedule 04.04.2011    source источник
comment
Вы используете как TargetCanvas, так и Canvas. Это намеренно?   -  person Uli Gerhardt    schedule 04.04.2011
comment
упс, это тот же Canvas, просто опечатка при написании вопроса.   -  person Andrew    schedule 04.04.2011
comment
Ставить многоточие только тогда, когда последняя строка не подходит, действительно странно. Одним из уродливых обходных путей может быть сначала нарисовать его так, но также указав tfModifyString, затем увеличив «TextRect.Bottom» и отрисовав измененный (с многоточием в конце) текст как есть.   -  person Sertac Akyuz    schedule 04.04.2011
comment
@Sertac Akyuz: можете ли вы переместить этот комментарий в ответы? Я проверю, как принято. Ваше решение работает отлично! :)   -  person Andrew    schedule 04.04.2011


Ответы (2)


Поскольку API помещает конечный многоточие только тогда, когда последняя строка не помещается в указанный прямоугольник, одним из обходных путей может быть указание параметров форматирования tfModifyStringin при первом вызове «TextRect» с прямоугольником с уменьшенной высотой, а затем вызов «TextRect» снова с прямоугольником правильного размера и измененным текстом:

var
  Text: string;
...

  Text := 'some long long long text';
  SetLength(Text, Length(Text) + 4); // as per DrawTextEx documentation

  Dec(TextRect.Bottom);
  Canvas.TextRect(TextRect, Text,
      [tfTop, tfLeft, tfEndEllipsis, tfWordBreak, tfModifyString]);

  Inc(TextRect.Bottom);
  Canvas.TextRect(TextRect, Text, [tfTop, tfLeft, tfWordBreak]);


Однако я буду следить, если будущая версия ОС решит полностью обрезать последнюю строку, если она не полностью помещается в прямоугольник.. :)

person Sertac Akyuz    schedule 04.04.2011
comment
У меня есть небольшое замечание: текст с tfModifyString лучше рисовать где-то за видимой областью на Canvas, потому что написание одного и того же текста дважды на одном и том же месте влияет на сглаживание шрифта. - person Andrew; 06.04.2011

Я бы попробовал вычислить нужный прямоугольник через Canvas.TextRect(..., [tfCalcRect, ...]).

person Uli Gerhardt    schedule 04.04.2011
comment
Но я не знаю, какую часть текста мне следует обрезать, чтобы нарисовать ее в пределах моего ограниченного количества строк. - person Andrew; 04.04.2011
comment
@Andrew: Извините, идей больше нет. Но попробуйте поискать в Google DT_END_ELLIPSIS DT_WORDBREAK (эквиваленты API для tfEndEllipsis и tfWordBreak). Вы найдете много проблем и, возможно, некоторые решения. :-) - person Uli Gerhardt; 04.04.2011