2006. május 18., csütörtök

How to calculate the bounding box for every character in a rotated text


Problem/Question/Abstract:

I need to calculate the bounding box (the rotated bounding box) for every character in a rotated text, for later allowing selection by picking inside this bounding box. What is the best method for doing that? Using GetCharABCWidths together with GetTextMetrics and later rotate myself every single character bounding box? I cannot use GetGlyphOutline to extract vector information because it will be too slow for my application that needs to draw thousands of text.

Answer:

I work with WinGDI32 routines to handle rotated text via transforming the device context. You can automatically get the extention coordinates in DC units by the affine transformation data. To illustrate this I give you the code from my extended TDrawer Object. This object handles some BGI like routintes like text justify and so on.

procedure TDrawer.fOutStringXYW(w: Double; x, y: Integer; S: string);
var
  nx, ny, xw, xh: Integer;
  O, T: TXForm;
begin
  nx := 0;
  ny := 0;
  xw := TheDraw.Textwidth(S); {the common routine}
  xh := TheDraw.TextHeight('Ag' + S); {not fine but fast}
  {handle the justification to get the exact root point for text out}
  case TextJust of
    tjCenterTop:
      begin
        nx := -xw div 2;
        ny := 0;
      end;
    tjCenterBottom:
      begin
        nx := -xw div 2;
        ny := -xh;
      end;
    tjCenterCenter:
      begin
        nx := -xw div 2;
        ny := -xh div 2;
      end;
    tjLeftTop:
      begin
        nx := 0;
        ny := 0;
      end;
    tjLeftCenter:
      begin
        nx := 0;
        ny := -xh div 2;
      end;
    tjLeftBottom:
      begin
        nx := 0;
        ny := -xh;
      end;
    tjRightCenter:
      begin
        nx := -xw;
        ny := -xh div 2;
      end;
    tjRightTop:
      begin
        nx := -xw;
        ny := 0;
      end;
    tjRightBottom:
      begin
        nx := -xw;
        ny := -xh;
      end;
  end;
  {WinGDI Routines start here}
  SetGraphicsMode(TheDraw.Handle, GM_Advanced);
  {Angle in degree}
  T.eM11 := 1 * Cos(w / 360 * Pi * 2);
  T.eM22 := 1 * Cos(w / 360 * Pi * 2);
  T.eM12 := 1 * Sin(w / 360 * Pi * 2);
  T.eM21 := 1 * -Sin(w / 360 * Pi * 2);
  T.eDX := X;
  T.eDY := Y;
  GetWorldTransform(TheDraw.Handle, O);
  ModifyWorldTransform(TheDraw.Handle, T, MWT_LEFTMULTIPLY);
  // TheDraw.Pen.Style := psClear;
  // TheDraw.Rectangle(nx - 1, ny - 1, nx + xw + 3, ny + xh + 2);
  {here should comes your code to get out the extention of your text.
        You see the variables above nx, ny, xw, xh. Outextention would be ...}
  // out_nx1 := round(nx * T.eM11 + ny * T.aM12 + TeDX);
  // out_ny1 := round(nx * T.eM21 + ny * T.aM22 + TeDY);
  // out_nx2 := round((nx + xw) * T.eM11 + (ny + xh) * T.aM12 + TeDX);
  // out_ny2 := round((nx + xw) * T.eM21 + (ny + xy) * T.aM22 + TeDY);
  {now you have to sort your data that out_nx1< out_nx2  and out_ny1<_out_ny2 and
        you will have exact your  boundingbox coordinates}
  TheDraw.TextOut(nx, ny, S);
  T.eM11 := 1;
  T.eM22 := 1;
  T.eM12 := 0;
  T.eM21 := 0;
  T.eDX := 0;
  T.eDY := 0;
  SetWorldTransform(TheDraw.Handle, O);
end;

Nincsenek megjegyzések:

Megjegyzés küldése