Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi Textzeilen umbrechen (https://www.delphipraxis.net/50305-textzeilen-umbrechen.html)

xaromz 23. Jul 2005 10:29


Textzeilen umbrechen
 
Hallo,

ich schreibe gerade eine Edit-Komponente. Sie soll ähnlich wie RichEdit Formatierungen beherrschen. Klappt auch ganz gut. Ich möchte das Ganze aber beschleunigen (ist nämlich immer eine gute Idee :mrgreen: ). Dafür hab ich mir den Zeilenumbruch ausgesucht. Das ist eine ziemlich blöde Sache, ich messe und kürze solange meinen Text (TextExtend), bis der Text kleiner als der verfügbare Platz ist. Das können bei einem langen Text schon mal einige Aufrufe sein. Nun meine Frage:
Gibt es eine API-Funktion, die mir sowas ähnliches sagt:
Zitat:

Mit dem Font "Arial" in der Größe "10pt" passen die ersten 12 Zeichen deines Textes "Ceterum censeo" in eine Breite von 50 Pixel.
Ich stelle mir sowas vor:
Delphi-Quellcode:
function GetLinebreak(DC: HDC; S: PChar; Length: Integer; Width: Integer): Integer;
Wenn es sowas nicht gibt, hat jemand einen Algorithmus, um diesen Wert schnell zu finden?

Gruß
xaromz

Daniel G 23. Jul 2005 10:56

Re: Textzeilen umbrechen
 
Mal 'ne blöde Frage:

Als Basis nutzt du aber schon ein "EDIT" im Sinne der Windows API, oder?

Also im Prinzip sowas:

Zitat:

Handling Wordwrap and Line Breaks
An application can use Wordwrap functions with multiline edit controls to locate the word or word fragment that should be wrapped to the next line. Using the default Wordwrap function provided by the system, lines always end at the spaces between words. An application can specify its own Wordwrap function by supplying an EditWordBreakProc Wordwrap function and sending the edit control an EM_SETWORDBREAKPROC message. An application can retrieve the address of the current Wordwrap function by sending the control an EM_GETWORDBREAKPROC message.

An application may direct a multiline edit control to add or remove a soft line break character (two carriage returns and a line feed) automatically at the end of wrapped-text lines. An application can turn this feature on or off by sending the edit control an EM_FMTLINES message. This message applies only to multiline edit controls and does not affect a line that ends with a hard line break (one carriage return and a line feed entered by the user). Also in multiline edit controls, an application can specify the ES_WANTRETURN style to request that the system insert a carriage return when the user presses the ENTER key in the edit control.
//HDC? Hmm, also wohl doch kein Edit im Sinne der Win-api... Schade...

marabu 23. Jul 2005 10:57

Re: Textzeilen umbrechen
 
Hallo xaromz,

schau dir mal die Methode TextWidth() von TCanvas an.

Grüße vom marabu

xaromz 23. Jul 2005 11:27

Re: Textzeilen umbrechen
 
Hallo,

@Daniel G: Nein, ich benutze kein API-Edit. Ich leite von TCustomControl ab. Wie kann ich denn bei einem "EDIT" (im API-Sinne) einzelne Zeichen formatieren? Geht das überhaupt? :gruebel: Ich kenn mich da nicht so gut aus...

@marabu: Genau so mach ich das gerade: Textlänge messen -> zu lang -> Text kürzen, Textlänge messen usw. Das dauert aber lange und ist auch nicht sehr schön.

Gruß
xaromz

marabu 23. Jul 2005 13:23

Re: Textzeilen umbrechen
 
Hallo xaromz,

Zitat:

Zitat von xaromz
Genau so mach ich das gerade: Textlänge messen -> zu lang -> Text kürzen, Textlänge messen usw.

Abgesehen davon, dass ein multiline edit window den Umbruch fast ohne dein Zutun erledigt, könntest du einen entgegengesetzten Ansatz wählen. Nicht einen langen Text zeichenweise kürzen, sondern den Ausgabetext wortweise aufbauen. Du greifst einfach Wort für Wort - Trenner kann white space sein - aus deinem Textpuffer ab und bestimmst über die API Funktion GetCharWidth32() bzw. GetTextEntentPoint32() die benötigten Pixel. Das sollte deutlich schneller funktionieren als dein bisheriger Ansatz.

marabu

xaromz 23. Jul 2005 14:17

Re: Textzeilen umbrechen
 
Hallo,
Zitat:

Zitat von marabu
Abgesehen davon, dass ein multiline edit window den Umbruch fast ohne dein Zutun erledigt, könntest du einen entgegengesetzten Ansatz wählen. Nicht einen langen Text zeichenweise kürzen, sondern den Ausgabetext wortweise aufbauen. Du greifst einfach Wort für Wort - Trenner kann white space sein - aus deinem Textpuffer ab und bestimmst über die API Funktion GetCharWidth32() bzw. GetTextEntentPoint32() die benötigten Pixel. Das sollte deutlich schneller funktionieren als dein bisheriger Ansatz.

marabu

Ein Multiline-Edit kann aber keine Formatierungen darstellen, mein Edit schon.
Meine Vorgehensweise war natürlich vereinfacht ausgedrückt, ich springe beim Verkleinern immer zum nächsten Trennzeichen (Space, Interpunktionszeichen, Trennstrich...). Nur was mache ich, wenn mein Edit 30 Pixel breit ist und jemand "Donaudampfschifffahrtskapitän" eingibt? Da gibt es kein Trennzeichen. Also muss ich doch wieder nachmessen, bis es passt.
Ich hab mir schon überlegt ob ich einfach meinen Suchalgorithmus so optimiere:
1) Text messen
2) Text zu lang -> Textlänge halbieren -> Zurück zu 1)
3) Text zu kurz -> Textlänge um 50% vergrössern -> Zurück zu 1)

Da fehlt natürlich noch die Abbruchbedingung.
Das müsste die Textmessungen im Mittel drastisch verringern

Gruß
xaromz

Flocke 29. Jul 2005 18:53

Re: Textzeilen umbrechen
 
Da ist mir noch etwas eingefallen: MSDN-Library durchsuchenGetTextExtentExPoint
Zitat:

The GetTextExtentExPoint function retrieves the number of characters in a specified string that will fit within a specified space and fills an array with the text extent for each of those characters. (A text extent is the distance between the beginning of the space and a character that will fit in the space.) This information is useful for word-wrapping calculations.

Windows NT/2000/XP: Included in Windows NT 3.1 and later.
Windows 95/98/Me: Included in Windows 95 and later.

xaromz 29. Jul 2005 18:57

Re: Textzeilen umbrechen
 
Hallo,

danke für den Tipp. Werd ich mir mal ansehen.

Gruß
xaromz

Olli 29. Jul 2005 18:57

Re: Textzeilen umbrechen
 
Zitat:

Zitat von Flocke
Da ist mir noch etwas eingefallen: MSDN-Library durchsuchenGetTextExtentExPoint

Wäre MSDN-Library durchsuchenGetTextExtentPoint32() nicht praktikabler?

xaromz 29. Jul 2005 19:00

Re: Textzeilen umbrechen
 
Hallo,

Zitat:

Zitat von Olli
Wäre MSDN-Library durchsuchenGetTextExtentPoint32() nicht praktikabler?

Nein, denn ich will ja genau einen Zeilenumbruch machen und muss deshalb nicht wissen, wie lang der ganze String ist, sondern wie viele Zeichen davon in eine Zeile o. ä. passen.

Gruß
xaromz

Flocke 29. Jul 2005 19:06

Re: Textzeilen umbrechen
 
Der wichtige Unterschied ist das hier:
Zitat:

... and fills an array with the text extent for each of those characters.
Du bekommst also ein Array mit partiellen Stringbreiten bis zum jeweiligen Zeichen zurück, also quasi N Aufrufe von GetTextExtentPoint32 für einen String der Länge N.

Olli 29. Jul 2005 20:24

Re: Textzeilen umbrechen
 
Zitat:

Zitat von Flocke
Der wichtige Unterschied ist das hier: [...]

Ist bekannt. Aber wozu dienen denn nun die Zeilenlängen? Doch auch nur zum Zeichnen, oder? Denn ein echtes EDIT interessiert die Zeilenlänge ja auch nicht.
Aber vermutlich spart man sowieso keine Aufrufe, weil ja auch noch die Formatierung innerhalb einer Zeile verschieden sein kann.

Flocke 29. Jul 2005 20:56

Re: Textzeilen umbrechen
 
Hmmmm ... also gegeben hast du
a) einen String und
b) die restliche Ausgabebreite in der aktuellen Zeile,
und du willst wissen wo du in die nächste Zeile umbrechen musst.

-> mit GetTextExtentPoint32 erfährst du nur, ob du ihn umbrechen musst.
-> mit GetTextExtentExPoint erfährst du, ob und wenn ja wo du ihn umbrechen musst.

In diesem Sinne schlägt GetTextExtentExPoint also zwei Fliegen mit einer Klappe, xaromz braucht ja beide Informationen.

Olli 29. Jul 2005 21:02

Re: Textzeilen umbrechen
 
Zitat:

Zitat von Flocke
In diesem Sinne schlägt GetTextExtentExPoint also zwei Fliegen mit einer Klappe, xaromz braucht ja beide Informationen.

Ich gebe mich geschlagen :-D

Luckie 4. Jun 2006 00:22

Re: Textzeilen umbrechen
 
Kan mir mal jemand helfen, das anzuwenden?

Delphi-Quellcode:
var
  i                : Integer;
  s                : string;
  size             : TSize;
  cntChars         : PInteger;
  alpDx            : PInteger;
begin

  GetTextExtentExPoint(dc, PChar(TEXT), length(TEXT), -(PageW * 10) - 100, cntChars, alpDx, size);
  for i := 0 to length(alpDx) - 1 do
  begin
    s := copy(TEXT, alpDx[i], cntChars^);
    TextOut(dc, 100, -10 * (i + 16), PChar(s), length(s));
    end;
Ich dachte so geht es, aber in der for-Zeile meint er, inkompatible Typen. :gruebel:

Daniel G 4. Jun 2006 00:45

Re: Textzeilen umbrechen
 
Zitat:

Zitat von Luckie
Ich dachte so geht es, aber in der for-Zeile meint er, inkompatible Typen.

Na ja, ich tippe einfach mal, das liegt daran, dass du "Length" auf einen PInteger anwenden willst, oder?

//Edit:
Hilft dir das hier? Hab' ich bei Experts Exchange gefunden, müsstest natürlich noch etwas anpassen.

Delphi-Quellcode:
procedure TForm1.but_CustomTextClick(Sender: TObject);
var
TextStr: String;
Bmp1: TBitmap;
numChar, oNum, lines: Integer;
aSize: tagSize;
begin
TextStr := 'Thisisatextstringlongenoughtowrap a couple of times. And'+
           'Moretextherefollowedbevenmore text followed by eveen more text'+
           ' followed-by-even-more-text followed by even moore text';
Bmp1 := TBitmap.Create;
try
  Bmp1.Width := 188;// try to have a 3 pixel border, use 182 below
  Bmp1.Height := 230;
  Bmp1.Canvas.Font.Name := 'Arial';
  Bmp1.Canvas.Font.Size := 11;

  if Length(TextStr) < 1 then Exit;
  lines := 0;
  repeat
  GetTextExtentExPoint(Bmp1.Canvas.Handle, PChar(TextStr),Length(TextStr),182,
                     @numChar, nil, aSize);
  oNum := numChar;
  while (numChar > 1) and not
      (TextStr[numChar] in [' ','.',',','-','_','?','!','$','(',')']) do
       Dec(numChar);
  if numChar = 1 then
    numChar := oNum;
  TextOut(Bmp1.canvas.handle,3,(lines*(aSize.cy+8))+3,PChar(TextStr), numChar);
  Delete(TextStr,1,numChar);
  Inc(Lines);
  until Length(TextStr) < 1;

  Canvas.Draw(530,290, Bmp1);
  finally
  FreeAndNil(Bmp1);
  end;
end;
Quelle

Luckie 4. Jun 2006 00:55

Re: Textzeilen umbrechen
 
Habs rausgefunden:
Delphi-Quellcode:
    i := 0;
    s := TEXT;
    w := (PageW * 10) - 200;
    repeat
      GetTextExtentExPoint(dc, PChar(s), length(s), w, @cntChars, nil, size);
      TextOut(dc, 100, -100 + -i * (Size.cy + 8), PChar(s), cntChars);
      Delete(s, 1, cntChars);
      Inc(i);
    until length(s) < 1;
Hehe. Genau das gleiche habe ich auch gerade gefunden. ;)

Jetzt müsste man das nur noch so hinbekommen, dass er nicht mitten im Wort umbricht. :?

Und Zeilenumbruche im Text (#13#10) berücksichtig werden.

Leerzeichen hat sich erledigt:

Delphi-Quellcode:
    repeat
      GetTextExtentExPoint(dc, PChar(s), length(s), (PageW * 10) - BORDERLEFT - BORDERRIGHT, @cntChars, nil, size);
      while (s[cntChars] <> ' ') do
        Dec(cntChars);
      TextOut(dc, BORDERLEFT, -BORDERTOP + -i * (Size.cy + 8), PChar(s), cntChars);
      Delete(s, 1, cntChars);
      Inc(i);
    until cntChars < 1;
Nur wie mache ich das mit dem Zeilenumbruch?

Luckie 4. Jun 2006 05:24

Re: Textzeilen umbrechen
 
Zeilenumbrüche sind auch gelöst. Ich jage den String vorher durch ExplodeExplode. ;)

marabu 4. Jun 2006 07:16

Re: Textzeilen umbrechen
 
Guten Morgen Michael,

vielleicht doch besser so:

Delphi-Quellcode:
var
  n                : Integer;
  i                : Integer;
  s                : string;
  size             : PSize;
  cntChars         : Integer;
  alpDx            : PInteger;
begin
  // ...
  n := Min(Length(TEXT), cntChars);
  GetMem(alpDX, n * SizeOf(Integer));
  GetMem(size, n * SizeOf(TSIZE));

  GetTextExtentExPoint(dc, PChar(TEXT), length(TEXT), -(PageW * 10) - 100, @cntChars, alpDx, size);
  // ...
Grüße vom marabu

Luckie 4. Jun 2006 13:58

Re: Textzeilen umbrechen
 
Was wäre daran besser?

marabu 4. Jun 2006 19:07

Re: Textzeilen umbrechen
 
Ich wollte dich darauf hinweisen, dass du die API-Funktion aus der Schleife nehmen kannst. Rein theoretisch reicht ein einziger Aufruf von GetTextExtentExPoint() aus um deinen Text formatieren zu können.

Freundliche Grüße vom marabu

Luckie 6. Jun 2006 00:00

Re: Textzeilen umbrechen
 
Äh, stimmt, die Anzahl der Zeichen, die da reinpassen, ändert sich ja nicht. ;) Danke für den Hinweis.

DGL-luke 6. Jun 2006 11:31

Re: Textzeilen umbrechen
 
Hallo... ich verstehe die Funktion noch nicht ganz. Was genau wird mir in dem array of integer zurückgegeben?

Zitat:

Each element in the array gives the distance, in logical units, between the beginning of the string and one of the characters that fits in the space specified by the nMaxExtent parameter
Sind die "logical units" Anzahl Zeichen? Also so wie "in die erste zeile passen 10 Zeichen, in die zweite 12, in die dritte 9" und so weiter?


Alle Zeitangaben in WEZ +1. Es ist jetzt 13:58 Uhr.

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