Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   Delphi Bounding rectangle eines fromatierten RTF-Textes ermitteln. (https://www.delphipraxis.net/52708-bounding-rectangle-eines-fromatierten-rtf-textes-ermitteln.html)

alzaimar 2. Sep 2005 07:05


Bounding rectangle eines fromatierten RTF-Textes ermitteln.
 
Seit gestern versuche ich das.
Gegeben sei ein RTF-Text sowie ein imaginäres TRichEdit mit einer definierten Breite. Dahinein plaziere ich den RTF-formatierten Text. Ich benötige die Höhe des formatierten Textes. Das geht bei einem nicht imaginären TRichEdit über den Aufruf
Delphi-Quellcode:
Var
  P : TPoint;
Begin
  reContainer.Perform (EM_POSFROMCHAR,LongInt (@P), 32767)
 ...
In P.Y steht dann die Höhe des Texts (wenn dieser mit einer Leerzeile abschliesst).
Super, das will ich jetzt OHNE ein TRichEdit realisieren. Wenn ich mir einfach eine Instanz erstelle (Parent = Nil), wird sofort die Notbremse (Exception) beim Setzen der Lines-Eigenschaft gezogen.

Weiss jemand wie ich das sonst hinbekomme, oder mir ein TRichEdit mit einem Parent bastle, ohne Bezug zu einem Formular nehmen zu müssen?

Ich möchte sozusagen eine Funktion implementieren:
Delphi-Quellcode:
Function RTF_GetFormatedTextRect (Const aText : String; aCanvasRect : TRect) : TRect;
Sie liefert dann den Umriss des formatierten Texts in Pixeln.

Danke für die Mühe

[edit]EM_GETRECT in EM_POSFROMCHAR geändert[/edit]

BrunoT 2. Sep 2005 07:14

Re: Bounding rectangle eines fromatierten RTF-Textes ermitte
 
Hi erstelle doch ein Richedit mit

visible;=false; :stupid:

Das kannst Du dann zum Berechnen der Texthöhe nehmen.

Oder Schau Dir mal die Funktion Drawtext mit DT_CALCRECT als Parameter im Windows SDK an. Diese Funktion gibt ein Rechteck zurück, welches die Eckkoordinaten des Textes enthält, wenn der Text geschrieben werden würde. :zwinker:

mfg

BrunoT

alzaimar 2. Sep 2005 07:48

Re: Bounding rectangle eines fromatierten RTF-Textes ermitte
 
Hi BrunoT.

Nee, mit Visible=False geht das nicht, weil sich dann das blöde Ding weigert, die korrekten Werte zu berechnen. Es denkt sich wohl: Wer mich nicht sieht, merkt auch nicht, das ich nix tue... Ausserdem will ich gerade KEIN TRechEdit auf einer Form haben.

Der zweite Tipp bezieht sich aber nicht auf RTF-formatierte Texte, oder doch?
Wie hoch ist dieser Text bei einer Boundingrect, die 150 pixel breit ist?
Code:
{\rtf1\ansi\ansicpg1252\deff0\deflang1031{\fonttbl{\f0\fswiss
\fcharset0 Arial;}{\f1\fswiss\fprq2\fcharset0 Microsoft Sans Serif;}}
{\colortbl ;\red0\green0\blue128;}{\*\generator CSIBERLIN;}
\viewkind4\uc1\pard\cf1\b\i\f0\fs20 Dies ist eine Überschrift\cf0\b0\i0\par
\f1 Und das hier nicht, denn es ist der Text\f0\par}

marabu 2. Sep 2005 08:23

Re: Bounding rectangle eines fromatierten RTF-Textes ermitte
 
Hi alzaimar,

Zitat:

Zitat von alzaimar
Ich benötige die Höhe des formatierten Textes. Das geht bei einem nicht imaginären TRichEdit über den Aufruf
Delphi-Quellcode:
Var
  P : TPoint;
Begin
  reContainer.Perform (EM_GETRECT,Integer (@P), 32767)
 ...

Müsste der Aufruf, damit er keine Speicherverletzung produziert, nicht so aussehen?

Delphi-Quellcode:
var
  r: TRect;
begin
  re.Perform(EM_GETRECT, 0, Integer(@r));
end;
Geliefert wird da aber das formatting rectangle - und das ist laut PSDK identisch zur client area vom richedit, wenn du es nicht änderst.

Zitat:

Zitat von alzaimar
das will ich jetzt OHNE ein TRichEdit realisieren. Wenn ich mir einfach eine Instanz erstelle (Parent = Nil), wird sofort die Notbremse (Exception) beim Setzen der Lines-Eigenschaft gezogen.

Du musst dein control als message-only-window erzeugen:

Delphi-Quellcode:
var
  re: TRichEdit;
begin
  re := TRichEdit.CreateParented(HWND(-3));
end;
Bis zu deiner Funktion RTF_GetFormattedTextRect() bist du dann aber immernoch ein Stück unterwegs...

Grüße vom marabu

Flocke 2. Sep 2005 09:01

Re: Bounding rectangle eines fromatierten RTF-Textes ermitte
 
Wie sieht es aus mit der Meldung MSDN-Library durchsuchenEM_FORMATRANGE - die ist zwar eigentlich nur für den Druck gedacht, aber das heißt ja vielleicht nichts.

Ich meine mich zu erinnern, dass wenn man dem ein "unendlich" hohes Rechteck in FORMATRANGE.rc vorwirft, dass dann der gesamte Text formatiert wird und FORMATRANGE.rc.right und FORMATRANEG.rc.bottom entsprechend angepasst werden. Du musst halt nur ein bisschen zwischen Pixeln und Twips hin und her rechnen.

Flocke 2. Sep 2005 09:24

Re: Bounding rectangle eines fromatierten RTF-Textes ermitte
 
So scheint's zu funktionieren:
Delphi-Quellcode:
function GetRichEditTextExtent(hRichEdit: HWND; nWidth: integer): TPoint;
var
  dc: HDC;
  fr: TFormatRange;
  res: TPoint;
  rv: integer;
begin
  dc := GetDC(hRichEdit);
  try
    res.x := GetDeviceCaps(dc, LOGPIXELSX);
    res.y := GetDeviceCaps(dc, LOGPIXELSY);

    fr.hdc := dc;
    fr.hdcTarget := dc;
    fr.rc := Rect(0, 0, MulDiv(nWidth, 1440, res.x), $1000000);
    fr.rcPage := fr.rc;
    fr.chrg.cpMin := 0;
    fr.chrg.cpMax := SendMessage(hRichEdit, WM_GETTEXTLENGTH, 0, 0);

    rv := SendMessage(hRichEdit, EM_FORMATRANGE, 0, LPARAM(@fr));
    SendMessage(hRichEdit, EM_FORMATRANGE, 0, 0);

    Assert(rv >= fr.chrg.cpMax);

    Result.x := MulDiv(fr.rc.Right, res.x, 1440);
    Result.y := MulDiv(fr.rc.Bottom, res.y, 1440);
  finally
    ReleaseDC(hRichEdit, dc);
  end;
end;
//EDIT: Berechnet aber offensichtlich nur die Höhe ...

[Nachtrag]

So berechnet er auch die Breite, aber perfekt ist's immer noch nicht wegen der Zeilenumbrüche. Diese Funktion ermittelt die "minimale Breite" die nötig ist, um die berechnet Höhe zu halten. Das kann anders aussehen als bei dir auf dem Bildschirm.

Delphi-Quellcode:
function GetRichEditTextExtent(hRichEdit: HWND; nWidth: integer): TPoint;
var
  dc: HDC;
  fr: TFormatRange;
  res: TPoint;
  txl: integer;
  rv: integer;
  wmin, wmid, wmax: integer;

  function CalcHeight(w: integer): integer;
  begin
    fr.hdc := dc;
    fr.hdcTarget := dc;
    fr.rc := Rect(0, 0, MulDiv(w, 1440, res.x), $1000000);
    fr.rcPage := fr.rc;
    fr.chrg.cpMin := 0;
    fr.chrg.cpMax := txl;

    rv := SendMessage(hRichEdit, EM_FORMATRANGE, 0, LPARAM(@fr));
    Assert(rv >= fr.chrg.cpMax);

    Result := MulDiv(fr.rc.Bottom, res.y, 1440);
  end;

begin
  dc := GetDC(0);
  try
    res.x := GetDeviceCaps(dc, LOGPIXELSX);
    res.y := GetDeviceCaps(dc, LOGPIXELSY);
    txl := SendMessage(hRichEdit, WM_GETTEXTLENGTH, 0, 0);

    Result.X := nWidth;
    Result.Y := CalcHeight(Result.X);

    wmin := 1;
    wmax := nWidth;

    while wmin <= wmax do
    begin
      wmid := (wmin + wmax) div 2;
      if CalcHeight(wmid) <= Result.Y then
      begin
        Result.X := wmid;
        wmax := wmid - 1;
      end
      else
        wmin := wmid + 1;
    end;
  finally
    SendMessage(hRichEdit, EM_FORMATRANGE, 0, 0);
    ReleaseDC(0, dc);
  end;
end;
Eine exakte Lösung wäre z.B., mit bekannter Breite und Höhe eine Bitmap zu erzeugen und das RichEdit dort hinein zeichnen zu lassen. Danach müsste man dann den rechten Rand dieser Bitmap überprüfen (ziemlich aufwändig).

alzaimar 2. Sep 2005 09:47

Re: Bounding rectangle eines fromatierten RTF-Textes ermitte
 
Goil! Keiner arbeitet, Alle sind im Forum und helfen.... :thumb:

@marabu: Ich hatte mich vertippt, die message lautet EM_GETPOSFROMCHAR, denn mit EM_GETRECT geht das nicht, frag mich nicht wieso. Ich benötige nur die Höhe des RTF-Textes, und wenn ich die Cursorposition des letzten Zeichens abfrage (vorausgesetzt, ich habe eine leere Zeile am ende des Textes), dann entspricht P.Y genau der Texthöhe...
Nach der 'ControlParented' Lösung habe ich (zwar nicht so richtig :oops:) gesucht, aber ich wusste, das mir hier einer helfen kann. Danke!

@Flocke: Deine Funktion in Verbindung mit dem 'unsichtbaren' TRichEdit vom marabu ist DIE Lösung. Die exakte Breite ist derzeit nicht nötig. Die Problemstellung ist: Ich habe ein Panel mit einer definierten Breite, da soll das RTF rein, nur wie hoch muss das Panel sein, damit alles reinpasst... Aber auch ein fettes Danke! Ich denke, die 'exakte Breite' ist der maximale X Wert, den mir die EM_GETPOSFROMCHAR-Geschichte liefert. Das ist zwar billig und lahm, aber wir haben ja mittlerweile GHZe im Überangebot.

Nochmal! Danke Euch beiden! (So, reicht :zwinker: )

Schönen Tag (Hier in Berlin ist es --- Heeeeiiiiiisssss!)

wünscht

der "als Eimer"


Alle Zeitangaben in WEZ +1. Es ist jetzt 12:38 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz