4

I need to draw some text in a table cell with fixed width (in pixels) and fixed number of text lines. If the text is clipped by cell rectangle, it must end with ellipsis. The problem is I can't calculate the text rectangle correctly (or the TextRect/DrawText procedure isn't working correctly, I'm not sure).

I tried to use this method of calculating text rectangle:

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]);

The clipping rectangle has been calculated correctly, but the ellipsis isn't appearing.

Ellipsis appearing when I decrease the height of clipping rectangle by 1 pixel:

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

But some pixels of the bottom line of my text are clipped then.

How to do it correctly?

Andrew
  • 3,696
  • 3
  • 40
  • 71
  • You use both `TargetCanvas` and `Canvas`. Is that intentional? – Uli Gerhardt Apr 04 '11 at 07:27
  • oops, it is the same Canvas, just mistyped when writing question. – Andrew Apr 04 '11 at 07:31
  • 1
    Putting ellipsis only when the last line does not fit is really strange.. One ugly workaround could be to first draw it like that but by also specifying `tfModifyString`, then incrementing 'TextRect.Bottom' and drawing the modified (with ellipsis at the end) text just as is. – Sertac Akyuz Apr 04 '11 at 09:48
  • @Sertac Akyuz: can you move this comment into answers? I'll check it as accepted. You solution works fine! :) – Andrew Apr 04 '11 at 10:10

2 Answers2

5

Since the api puts the end-ellipsis only when the last line does not fit in the specified rectangle, one workaround could be to specify tfModifyStringin formatting options in a first call to 'TextRect' with a rectangle with reduced height, then call 'TextRect' again with a proper sized rectangle and the modified text:

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]);


I'd be keeping an eye though, in case a future version of the OS decides to clip the last line entirely if it doesn't entirely fit in the rectangle.. :)

Sertac Akyuz
  • 54,131
  • 4
  • 102
  • 169
  • 1
    I have a small remark: it is better to draw the text with tfModifyString somewhere behind the visible area on Canvas, because writing the same text twice on the same place affects font antialiasing. – Andrew Apr 06 '11 at 05:02
0

I'd try calculating the needed rectangle via Canvas.TextRect(..., [tfCalcRect, ...]).

Uli Gerhardt
  • 13,748
  • 1
  • 45
  • 83
  • But I don't know which part of text should I trim to draw it within my limited NumberOfLines – Andrew Apr 04 '11 at 07:49
  • @Andrew: Sorry, no more ideas. But try googling for `DT_END_ELLIPSIS DT_WORDBREAK` (the API equivalents for tfEndEllipsis and tfWordBreak). You'll find lots of problems and maybe some solutions. :-) – Uli Gerhardt Apr 04 '11 at 08:50