Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Programmieren allgemein (https://www.delphipraxis.net/40-programmieren-allgemein/)
-   -   Textlänge abschneiden (https://www.delphipraxis.net/189448-textlaenge-abschneiden.html)

EWeiss 14. Jun 2016 05:54

Textlänge abschneiden
 
Ich ermittle die weite von Text anhand der Textlänge.

Delphi-Quellcode:
      fMaxStrLen := Strlen(SongName);
      if fMaxStrLen >= 59 then
        SongName := PAnsiChar(AnsiString(Ansimidstr(string(SongName),1, 54) + '..'));
Delphi-Quellcode:
procedure TBASSSoVis.HoldMatrixFontMode(x, y: GLFloat; Text: PAnsiChar;
  FontSet: tsFontID; TextAlign: integer);
var
  iMatrixMode : integer;
begin
  glGetIntegerv(GL_MATRIX_MODE, @iMatrixMode);
  glMatrixMode(GL_PROJECTION);
  glPushMatrix;
  try
    glPrintXY(x, y, text, FontSet, TextAlign)
  finally
    glMatrixMode(GL_PROJECTION);
    glPopMatrix;
    glMatrixMode(iMatrixMode);
  end;
 
end;
Delphi-Quellcode:
HoldMatrixFontMode(5, SON_Current_YPos, PAnsiChar(SongName), fLargeFontID, TS_ALIGN_LEFT);


Sollte eigentlich kein Problem sein.
Ist es aber da der Text in OpenGL gerendert wird.

Wenn der Text über 59 Zeichen geht, kurz vor der Zeitanzeige soll er abgeschnitten werde.
Beispiel:

xxxxx..

Warum ein Beispiel wenn man am Code schon sieht was ich machen will.
Ganz einfach.

Weil beim Rendern die breite einzelner Zeichen halt Unterschiedlich sind.
Nehme ich jetzt

IIIII..
Theoretisch könnte dann der Text eine länge von 69 Zeichen haben und wäre immer noch nicht im Bereich meiner Zeitanzeige.
Würde diese also nicht überzeichen.

Wie kann ich das jetzt lösen und woran soll ich mich halten?
Verwende Textsuite.

crosspost:
https://delphigl.com/forum/viewtopic...100406#p100406

gruss

Jasocul 14. Jun 2016 06:57

AW: Textlänge abschneiden
 
Wie du schon festgestellt hast, nützt die Anzahl der Zeichen nichts, wenn Proportionalschrift eingesetzt wird.
1. Lösung:
Keine Proportionalschrift verwenden. Ist wohl die einfachste Lösung, aber sieht oft irgendwie scheiße aus. :lol:
2. Lösung:
Die Länge des Textes in Pixeln über den Canvas der Ziel-Komponente bestimmen.
In der DP gibt es mehrere Beispiele dazu. Eins kannst du hier finden.

EWeiss 14. Jun 2016 07:08

AW: Textlänge abschneiden
 
Zitat:

Zitat von Jasocul (Beitrag 1340081)
Wie du schon festgestellt hast, nützt die Anzahl der Zeichen nichts, wenn Proportionalschrift eingesetzt wird.
1. Lösung:
Keine Proportionalschrift verwenden. Ist wohl die einfachste Lösung, aber sieht oft irgendwie scheiße aus. :lol:
2. Lösung:
Die Länge des Textes in Pixeln über den Canvas der Ziel-Komponente bestimmen.
In der DP gibt es mehrere Beispiele dazu. Eins kannst du hier finden.

Ich sagte ja schon es wird in OpenGL (TextSuite) gerendert da nutzt mir ein Canvas nichts.
Danke.

gruss

Jasocul 14. Jun 2016 07:26

AW: Textlänge abschneiden
 
Zitat:

Zitat von EWeiss (Beitrag 1340082)
Ich sagte ja schon es wird in OpenGL (TextSuite) gerendert da nutzt mir ein Canvas nichts.

Ups, sorry.
War wohl noch zu früh für meine Augen.

EWeiss 14. Jun 2016 07:36

AW: Textlänge abschneiden
 
Zitat:

Zitat von Jasocul (Beitrag 1340083)
Zitat:

Zitat von EWeiss (Beitrag 1340082)
Ich sagte ja schon es wird in OpenGL (TextSuite) gerendert da nutzt mir ein Canvas nichts.

Ups, sorry.
War wohl noch zu früh für meine Augen.

Na ja macht ja nichts ;)

Es gäbe eine Möglichkeit..
Delphi-Quellcode:
tsSetParameteri(TS_FONT_CHAR_SPACING, 1);


Damit sollte eigentlich zwischen den einzelnen Chars der Abstand immer gleich sein.
In dem Fall sollte es eigentlich funktionieren, will aber auch nicht so richtig.

Neutral General 14. Jun 2016 07:41

AW: Textlänge abschneiden
 
Du benutzt zwar OpenGL aber ich denke Canvas.TextWidth kann trotzdem ein guter Ansatz sein. Vielleicht sind die Ergebnisse nicht 1:1 auf OpenGL übertragbar aber das Verhältnis sollte ja auf jeden Fall gleich sein.

p80286 14. Jun 2016 08:14

AW: Textlänge abschneiden
 
Da jeder (TrueType-)Font seine eigenen Parameter für jeden einzelnen Buchstaben mitbringt, sollte TextWidth zumindest ein guter Anhaltspunkt sein. Allerdings nur wenn in beiden Fällen der gleiche Font in der gleichen Größe verwendet wird. Die Werte die man für Times New Roman erhält, für Garamond zu verwenden, um ein Beispiel zu nennen, ist nicht so sinnvoll.

Gruß
K-H

EWeiss 14. Jun 2016 08:15

AW: Textlänge abschneiden
 
Zitat:

Zitat von Neutral General (Beitrag 1340085)
Du benutzt zwar OpenGL aber ich denke Canvas.TextWidth kann trotzdem ein guter Ansatz sein. Vielleicht sind die Ergebnisse nicht 1:1 auf OpenGL übertragbar aber das Verhältnis sollte ja auf jeden Fall gleich sein.

Ich habe es mal damit getestet
http://www.delphipraxis.net/1333234-post4.html


Delphi-Quellcode:
function ChangeLabelWidth3(Lab: TLabel; MaxWidth: Integer): Boolean;
var LabCap: String;
     CurrWidth, n: Integer;
begin
   if Lab.Width > MaxWidth then
   begin
     LabCap:= Lab.Caption;
     MaxWidth := MaxWidth - Lab.Canvas.TextWidth('...'); //crash
     CurrWidth := Lab.Canvas.TextWidth(LabCap);

     n := Length(LabCap);
     repeat
       CurrWidth := CurrWidth - Lab.Canvas.TextWidth(LabCap[n]);
       dec(n);
     until (CurrWidth <= MaxWidth) or (n <= 0);
     Lab.Caption := copy(LabCap, 1, n) + '...';

     Result:= True;
   end
   else
     Result := false;
end;
Funktionieren tut es nicht denn es kracht schon hier.

Delphi-Quellcode:
MaxWidth := MaxWidth - Lab.Canvas.TextWidth('...'); //crash

Deaktiviere ich die zeile dann kracht es hier.

Delphi-Quellcode:
CurrWidth := Lab.Canvas.TextWidth(LabCap);

AV Read of Adress 000000


gruss

Neutral General 14. Jun 2016 08:17

AW: Textlänge abschneiden
 
Da scheint Lab.Canvas nil zu sein. Mit einem vorhandenen und gültigem Canvas bei dem du vorher Font und FontSize einstellst sollte aber wie p80286 und ich gesagt haben gute Ansätze liefern.

EWeiss 14. Jun 2016 08:33

AW: Textlänge abschneiden
 
Zitat:

Zitat von Neutral General (Beitrag 1340094)
Da scheint Lab.Canvas nil zu sein. Mit einem vorhandenen und gültigem Canvas bei dem du vorher Font und FontSize einstellst sollte aber wie p80286 und ich gesagt haben gute Ansätze liefern.

Nö der ist nicht Nil

Delphi-Quellcode:
Lab := TLabel.Create(nil);
Lab.Font.Name := 'Arial';
Lab.Font.Size := 16;
Lab.Caption := string(SongName);
Lab.AutoSize := True;
Ja Vielleicht wenn es nicht wie gesagt krachen würde bei dieser Procedure.

EDIT:
Ich denke das ist nicht möglich da ich für den Label kein Parent zuweisen kann.
Und ohne Parent denke ich mal kein gültiger Canvas ?

gruss

nahpets 14. Jun 2016 11:18

AW: Textlänge abschneiden
 
In einem Programm, in dem ich zur Laufzeit Buttons in eine Scrollbox lege, habe ich das so gelöst:
Delphi-Quellcode:
      lb            := TLabel.Create(Nil);
      lb.Font       := sbFavoriten.Font; // Scrollbox
      lb.Autosize   := True;
      lb.Caption    := fdSendername.AsString; // DB-Feld mit Sendername
      btn.Hint      := lb.Caption + #13 + fdKategorie.AsString; // DB-Feld mit Senderkategorie
      if lb.Width > sbFavoriten.Width - 32 then begin
        lb.Caption := lb.Caption + '...';
        while lb.Width > sbFavoriten.Width - 32 do begin
          lb.Caption := Copy(lb.Caption,1,Length(lb.Caption) - 4) + '...';
        end;
      end;
      // Button den gekürzten Text und die erforderliche Höhe zuweisen.
      btn.Caption   := lb.Caption;
      btn.Height    := lb.Height + 4;

EWeiss 14. Jun 2016 12:12

AW: Textlänge abschneiden
 
Nein es geht definitiv nicht.
Weil ich Controls dieser Art nicht addieren kann.
Ein Parent für den Label kann ich auch nicht setzen.

Habe es jetzt so geändert.
Aber auch das stimmt nicht weil es sich mit OpenGL nicht vereinbaren lässt.

Delphi-Quellcode:
function GetText(AText, AFontName: string; AFontSize: integer; MaxWidth: Integer): string;
var
  bmp: TBitmap;
  CurrWidth, n: Integer;

begin

  bmp := TBitmap.Create;
  try
    bmp.Canvas.Font.Name := AFontName;
    bmp.Canvas.Font.Size := AFontSize;

    MaxWidth := MaxWidth - bmp.Canvas.TextWidth('...');
    CurrWidth := bmp.Canvas.TextWidth(AText);

    n := Length(AText);
    repeat
      CurrWidth := CurrWidth - bmp.Canvas.TextWidth(AText[n]);
        dec(n);
    until (CurrWidth <= MaxWidth) or (n <= 0);

    if CurrWidth < MaxWidth then
      result := copy(AText, 1, (n + 1))
    else
    result := copy(AText, 1, (n + 1)) + '...';

  finally
    bmp.Free;
  end;
end;
zudem kommt noch das
CurrWidth < MaxWidth niemals mehr als MaxWidth ist.
Das hat zur folge das immer '...' angehängt wird.
Außerdem ist im vorherigen Code noch ein Fehler n muss mit 1 addiert werden ansonsten fehlt ein Buchstabe.

Wenn ich nun IIIIIIII anstelle von XXXXXX nehme geht I über meine Zeitanzeige hinaus
Ist also das gleiche in grün.

Delphi-Quellcode:
SongName := PAnsiChar(AnsiString(GetText(string(AnsiString(BassSoInfo.SongTitle)), 'Arial', 16, 570)));


geht mit X aber nicht mit I
Die breite der Zeichen ist nun mal nicht gleich wie die der Labels.
Auch wenn ich den gleichen Font, Font.size verwende.

Zitat:

aber das Verhältnis sollte ja auf jeden Fall gleich sein.
Also nein keinerlei Einklang in Verbindung mit OGL


gruss

EWeiss 17. Jun 2016 20:00

AW: Textlänge abschneiden
 
Ich bekomme das nicht berechnet.

Gehe jetzt wie folgt vor.
Delphi-Quellcode:
    // Display Song Name
    SongNameTmp := BassSoInfo.SongTitle;
    OldTitle := string(BassSoInfo.Songfile);
 
    if assigned(SongNameTmp) then
    begin
      SetLength(Char, length(SongNameTmp));
 
      // Only change if Title different
      if (OldTitle <> CurrentTitle) then
      begin
        //Get max length of String
        fMaxStrLen := tsTextGetWidthA(SongNameTmp);
        if fMaxStrLen >= 635 then
        begin
          // Parse width of Glyph
          for I := 0 to High(Char) do
          begin
            Char[I] := WideChar(SongNameTmp[I]);
            CharLength := tsFontGetCharParameteri(Char[I], TS_CHAR_GLYPHRECT_RIGHT);
            CharLen := CharLen + CharLength;
          end;
          // CharLen = length of all combine Glyph
          if CharLen >= ((CharLen - fMaxStrLen) + 635) then
          begin
            SongName := PAnsiChar(Ansistring(Ansimidstr(string(SongNameTmp), 1, 58) + '...'));
          end;
        end
        else
          SongName := BassSoInfo.SongTitle;
 
        CurrentTitle := OldTitle;
      end;
Ich scheitere hier dran '58'
Der Abstand zur Zeitanzeige ist immer unterschiedlich abhängig davon wie groß die Glyphen sind.
Die Chars beider Dateien sind identisch.

gruss

EWeiss 17. Jun 2016 20:36

AW: Textlänge abschneiden
 
Sorry das ich direkt nochmal Antworte.

Im Delphi Forum hat Lossy mir den Tip gegeben.

Delphi-Quellcode:
if fMaxStrLen = 0 then begin
  fMaxStrLen := tsTextGetWidthA(SongName);
  if fMaxStrLen >= 635 then begin // eventuell reicht die while-schleife auch aus.
    // eventuell hier eine Kopie des Songnamens erstellen.
    while fMaxStrLen >= 635 do
      // string kürzen und '...' anhängen
      fMaxStrLen := tsTextGetWidthA(>gekürzter string<);
    end;
  end;
end;
Die frage wäre nur wie den String kürzen auf welcher Basis.
Und ob er dann wegen den Glyphen innerhalb des Bereiches von 635 passt wäre noch die Frage.

gruss

Sir Rufo 17. Jun 2016 21:26

AW: Textlänge abschneiden
 
Nur mal so nebenbei etwas allgemeingültiges heruntergetippt:
Delphi-Quellcode:
unit Unit2;

interface

uses
  System.SysUtils,
  System.StrUtils;

type
  TCharMeasureWidthDelegate = function( const C: Char ): Integer of object;

function ShortenText( const Text: string; const MaxLength: Integer; const CharMeasurement: TCharMeasureWidthDelegate; const ShortenSuffix: string = '...' ): string;

implementation

function ShortenText( const Text: string; const MaxLength: Integer; const CharMeasurement: TCharMeasureWidthDelegate; const ShortenSuffix: string = '...' ): string;
var
  lText                  : string;
  lChar                  : Char;
  lCharLength            : Integer;
  lSuffixLength          : Integer;
  lTextLength            : Integer;
  lShortenedWithSuffix   : string;
  lShortendWithSuffixFound: Boolean;
begin
  if not Assigned( CharMeasurement )
  then
    raise EArgumentNilException.Create( 'CharMeasurement' );
  if MaxLength < 0
  then
    raise EArgumentOutOfRangeException.Create( 'MaxLength' );

  lSuffixLength := 0;
  for lChar in ShortenSuffix do
    begin
      lSuffixLength := lSuffixLength + CharMeasurement( lChar );
    end;

  if lSuffixLength > MaxLength
  then
    raise EArgumentOutOfRangeException.Create( 'SuffixLength > MaxLength' );

  Result                  := '';
  lText                   := TrimRight( Text );
  lTextLength             := 0;
  lShortendWithSuffixFound := False;

  for lChar in lText do
    begin
      lCharLength := CharMeasurement( lChar );

      if not lShortendWithSuffixFound and ( lTextLength + lCharLength + lSuffixLength > MaxLength )
      then
        begin
          lShortenedWithSuffix    := Result + ShortenSuffix;
          lShortendWithSuffixFound := True;
        end;

      if lTextLength + lCharLength > MaxLength
      then
        begin
          Result := lShortenedWithSuffix;
          Exit;
        end;

      Result := Result + lChar;
      Inc( lTextLength, lCharLength );
    end;

end;

end.
Und ein Test mit einer PaintBox (ja, es ist wurscht womit man das verwendet)
Delphi-Quellcode:
unit Unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.ExtCtrls, Vcl.StdCtrls;

type
  TForm1 = class( TForm )
    PaintBox1: TPaintBox;
    Edit1: TEdit;
    Label1: TLabel;
    Label2: TLabel; // Width: 560; Font.Name: Courier New; Font.Size: 8
    procedure PaintBox1Paint( Sender: TObject );
    procedure Edit1Change( Sender: TObject );
  private
    function CalculateCharWith( const C: Char ): Integer;
    function CalculateFixedCharWidth( const C: Char ): Integer;
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses
  Unit2;

{ TForm1 }

function TForm1.CalculateCharWith( const C: Char ): Integer;
begin
  Result := PaintBox1.Canvas.TextWidth( C );
end;

function TForm1.CalculateFixedCharWidth( const C: Char ): Integer;
begin
  Result := 1;
end;

procedure TForm1.Edit1Change( Sender: TObject );
begin
  PaintBox1.Invalidate;
  // Ist von der Breite so angelegt, dass mit Courier New 8 genau 80 Zeichen hineinpassen
  Label2.Caption := ShortenText( Edit1.Text, 80, CalculateFixedCharWidth );
end;

procedure TForm1.PaintBox1Paint( Sender: TObject );
var
  lDrawText: string;
begin
  lDrawText     := ShortenText( Edit1.Text, PaintBox1.ClientWidth, CalculateCharWith );
  Label1.Caption := PaintBox1.ClientWidth.ToString( ) + ' / ' + PaintBox1.Canvas.TextWidth( lDrawText ).ToString( );
  PaintBox1.Canvas.TextOut( 0, 0, lDrawText );
end;

end.

EWeiss 17. Jun 2016 21:35

AW: Textlänge abschneiden
 
Danke dir für deine mühe.
Aber ich kann leider davon nichts verwenden.
Ich muss den Text parsen/Ausgeben mit der Bibliothek mit der die Texte auch gezeichnet werden.

GetTextExtentPoint geht nicht GDI.
Auch alle anderen Texte (Berechnungen) die basierend auf GDI sind kann ich nicht verwenden.

Zitat von Lossy https://delphigl.com/forum/viewtopic...100413#p100413
Zitat:

Hier solltest du grundsätzlich die TextBreite etc. nur mit der Bibliothek erfragen mit der du die Texte auch zeichnest. Alles andere kann nicht funktionieren. Bzw. Birgt einfach zu viele Risiken.
Ich weiß nicht wie ich hier den Text zuschneiden soll.
Habe keinen Anhaltspunkt.

Delphi-Quellcode:
    while fMaxStrLen >= 635 do
       // string kürzen und '...' anhängen
       fMaxStrLen := tsTextGetWidthA(>gekürzter string<);
     end;
gruss

Sir Rufo 17. Jun 2016 21:45

AW: Textlänge abschneiden
 
Hast du eine Möglichkeit die effektive Breite eines Zeichens abzufragen?
Geht das nicht mit
Delphi-Quellcode:
tsFontGetCharParameteri
?

Dann so eine Funktion der
Delphi-Quellcode:
ShortenText
mitgeben und du bekommst den Text, den du zeichnen kannst.

EWeiss 17. Jun 2016 21:50

AW: Textlänge abschneiden
 
Zitat:

Zitat von Sir Rufo (Beitrag 1340492)
Hast du eine Möglichkeit die effektive Breite eines Zeichens abzufragen?
Geht das nicht mit
Delphi-Quellcode:
tsFontGetCharParameteri
?

Dann so eine Funktion der
Delphi-Quellcode:
ShortenText
mitgeben und du bekommst den Text, den du zeichnen kannst.

Ja doch..
Delphi-Quellcode:
          // Parse width of Glyph
           for I := 0 to High(Char) do
           begin
             Char[I] := WideChar(SongNameTmp[I]);
             CharLength := tsFontGetCharParameteri(Char[I], TS_CHAR_GLYPHRECT_RIGHT);
             CharLen := CharLen + CharLength;
           end;
CharLength ist die länge des Aktuellen Chars basierend auf i

Nur wie soll ich da den Titel mit kürzen.
Es geht aber wie man an den Bildern sieht ist der Abstand zur Zeitanzeige basierend auf der Breite der Chars unterschiedlich.
Aber vielleicht geht es auch nicht anders. ?

Ich setze hier die länge des Strings auf 58.

Delphi-Quellcode:
SongName := PAnsiChar(Ansistring(Ansimidstr(string(SongNameTmp), 1, 58) + '...'));


Die 58 jedoch ist eigentlich der Bereich den ich berechnen müsste, wenn ich denn einen Anhaltspunkt hätte.

gruss

Sir Rufo 17. Jun 2016 22:00

AW: Textlänge abschneiden
 
Nun ja, wenn dir ein kompletter Source noch nicht reicht, wie soll man dann noch helfen?

Mir scheint, du schaust einfach nur flüchtig drüber und "... oh, der hat da was mit VCL verwendet, dann passt es eh nicht ..."

Schau dir meinen Source an und du wirst sehen, wie der Text so passend wie nur möglich zurechtgestutzt wird.

BTW:
Delphi-Quellcode:
ShortenText
weiß nicht, wie man die Breite eines Zeichens bestimmt (dafür ist der Parameter
Delphi-Quellcode:
CharMeasurement
da - CallbackFunktion).

EWeiss 17. Jun 2016 22:07

AW: Textlänge abschneiden
 
Zitat:

Zitat von Sir Rufo (Beitrag 1340494)
Nun ja, wenn dir ein kompletter Source noch nicht reicht, wie soll man dann noch helfen?

Mir scheint, du schaust einfach nur flüchtig drüber und "... oh, der hat da was mit VCL verwendet, dann passt es eh nicht ..."

Schau dir meinen Source an und du wirst sehen, wie der Text so passend wie nur möglich zurechtgestutzt wird.

Mir hat dein Quelltext immer gereicht.
Und ich kann auch gerne drüber schauen.
Das Problem ist nur die länge der Chars ist nicht identisch alleine deshalb schon nicht weil ich unter OpenGL Schatten verwende.
Ich werde nie auch nur halbwegs an die korrekte breite jedes einzelnen Chars herankommen.

Es geht nicht um nicht wollen ich schätze deinen Code.
Aber es wird damit nicht funktionieren.
Tut mir leid ;)

Aber ich denke andere die ein ähnliches Problem unter VCL haben werden sich darüber sicher freuen (Was nicht bedeutet ich hätte mich nicht gefreut ;) )

Vielleicht muss ich wirklich auf Lossy warten er hat die Bibliothek geschrieben.
Er kann mir vielleicht zu einigen Parametern noch genauere Auskunft geben.

Zitat:

Courier New
Monotype Fonts.
Damit hätte ich auch kein Problem weil hier alle Zeichen gleich breit sind.
Aber ehrlich? Das sieht bescheiden aus.
Da bekomme ich vom Text her gerade mal die hälfte auf meinem Rendercontex wie bisher.
Aber, könnte dann mit einer fixen Anzahl von Char's Arbeiten wie ich es jetzt tue 58 und der abstand wäre immer gleich.
Wenn es nicht so bescheiden aussehen würde.

Im Fall von Monotypen Fonts könnte man komplett auf deinen Code verzichten.
Wie gesagt da alle Chars gleich breit sind.

gruss

Sir Rufo 17. Jun 2016 22:38

AW: Textlänge abschneiden
 
Zitat:

Zitat von EWeiss (Beitrag 1340496)
Ich werde nie auch nur halbwegs an die korrekte breite jedes einzelnen Chars herankommen.

Hört sich ja fast so an, als ob OpenGL = Quanten-Physik ist.

Wenn man die Breite nicht bestimmen kann, dann kann man das auch nicht zeichnen, weil der Bildschirm hat einfach nur Pixel und die müssen mit Farbe gefüllt werden :stupid:

Ausserdem machst du das doch genau hier:
Delphi-Quellcode:
// Parse width of Glyph
  for I := 0 to High(Char) do
  begin
    Char[I] := WideChar(SongNameTmp[I]);
    // .......... vvvvvvvvvvvvvvvvvvvvvvv
    CharLength := tsFontGetCharParameteri(Char[I], TS_CHAR_GLYPHRECT_RIGHT);

    CharLen := CharLen + CharLength;
  end;
Irgendwann läufst du aus dem Maximalbereich heraus und musst jetzt den Text soweit kürzen, dass aber auch der Suffix noch dargestellt werden kann.

Nehmen wir mal eine feste Breite von 20 (das Verfahren ist genau wie bei der varaiblen Laufweite, nur einfacher darzustellen) und als Suffix
Delphi-Quellcode:
... (more)
:

OriginalgekürztKommentar
1234567890123456789012345678901234567890Original passt, da brauchen wir nichts zu kürzen
1234567890123456789011234567890... (more)hier müssen wir kürzen

Beim Kürzen des Textes muss man also wieder ein paar Zeichen zurück gehen ... oder sich diesen speziellen Zustand einfach merken.

Du musst also nichts anderes machen als
  1. Die Breite des Suffixs zu bestimmen
    Delphi-Quellcode:
      lSuffixLength := 0;
      for lChar in ShortenSuffix do
        begin
          lSuffixLength := lSuffixLength + CharMeasurement( lChar ); // <- das könnte auch eine OpenGL-Zeichenbreiteermittlungsfunktion sein
        end;
  2. Jetzt den Text Zeichen für Zeichen durcharbeiten
    Delphi-Quellcode:
      Result := '';
      lText := TrimRight( Text );
      lTextLength := 0;
      lShortendWithSuffixFound := False;

      for lChar in lText do
        begin
          lCharLength := CharMeasurement( lChar ); // <- das könnte auch eine OpenGL-Zeichenbreiteermittlungsfunktion sein
  3. dabei aber den Zeitpunkt abpassen, wenn die bisher ermittelte Textlänge mit der Suffixlänge und dem aktuellen Zeichen über die Maximallänge hinausgeht. Denn den Text solltest du dir für den Fall der Fälle merken, wenn du den Text kürzen musst. Das ist bei dir aktuell diese ominöse 58
    Delphi-Quellcode:
          if not lShortendWithSuffixFound and ( lTextLength + lCharLength + lSuffixLength > MaxLength )
          then
            begin
              lShortenedWithSuffix := Result + ShortenSuffix;
              lShortendWithSuffixFound := True;
            end;
  4. Wenn man dann über die Maximallänge kommt gibt man einfach diesen eben gemerkten Text zurück
    Delphi-Quellcode:
          if lTextLength + lCharLength > MaxLength
          then
            begin
              Result := lShortenedWithSuffix; // <- das ist das gesuchte Ergebnis
              Exit;
            end;
  5. ansonsten einfach mal zusammenzählen und zum nächsten Zeichen
    Delphi-Quellcode:
          Result := Result + lChar;
          Inc( lTextLength, lCharLength );
        end;

EWeiss 17. Jun 2016 22:49

AW: Textlänge abschneiden
 
Zitat:

Hört sich ja fast so an, als ob OpenGL = Quanten-Physik ist.
LOL..

Ach so jetzt verstehe ich was du meinst es geht hier nicht im speziellen um GDI
sondern ich könnte/sollte diese Parse Funktion zu GL konvertieren bsp. mit meinen
Möglichkeiten die breite eines Zeichen zu bestimmen.

Anschließend deine Funktionen durchgehen um zu verstehen wie ich den String in der länge entsprechend anpassen kann.

Jo das kann ich mal versuchen.

Danke für deine Ausführung..
Hoffe das ich richtig liege :)

gruss

Sir Rufo 17. Jun 2016 22:56

AW: Textlänge abschneiden
 
Ja, darum habe ich die
Delphi-Quellcode:
ShortenText
extra so ausgelegt, dass die Bestimmung der Zeichenbreite per Callback-Funktion übergeben werden kann. Dadurch wird der Code allgemeingültig und ist eben nicht auf GDI beschränkt.

Das Bestimmen der Zeichenbreite ist eine andere Problematik als das Bestimmen des gekürzten Textes. So etwas vermische ich nicht so gerne, sonst muss ich immer so viel Tippen :stupid:

EWeiss 17. Jun 2016 22:59

AW: Textlänge abschneiden
 
Zitat:

Zitat von Sir Rufo (Beitrag 1340499)
Ja, darum habe ich die
Delphi-Quellcode:
ShortenText
extra so ausgelegt, dass die Bestimmung der Zeichenbreite per Callback-Funktion übergeben werden kann. Dadurch wird der Code allgemeingültig und ist eben nicht auf GDI beschränkt.

Das Bestimmen der Zeichenbreite ist eine andere Problematik als das Bestimmen des gekürzten Textes. So etwas vermische ich nicht so gerne, sonst muss ich immer so viel Tippen :stupid:

Ok ;)

Danke nochmals..
Kam irgendwie nicht so richtig bei mir an.

gruss

nahpets 17. Jun 2016 23:03

AW: Textlänge abschneiden
 
Ich versuchs mal mit 'ner anderen Beschreibung:

Du hast oben in Post #14 eine Routine, mit der die Textlänge bestimmt werden kann.

Die Maximallänge scheint 635 zu sein.

Zuerst ermittelst du die Länge von'...' und ziehst diese von 635 ab.
Das ist erstmal die neue Maximallänge.

Nun ermittelst Du in einer Schleife die Länge des Textes und wenn er länger als die neue Maximallänge ist, entfernst Du das letzte Zeichen und prüfst erneut.
Das wird solange wiederholt, bis die ermittelte Länge kleiner oder gleich der neuen Maximallänge ist.
An den bis dahin gekürzten String hängst Du nun noch die ... dran, dann müsste es (so hoffe ich) passen.

EWeiss 17. Jun 2016 23:12

AW: Textlänge abschneiden
 
Zitat:

Zitat von nahpets (Beitrag 1340501)
Ich versuchs mal mit 'ner anderen Beschreibung:

Du hast oben in Post #14 eine Routine, mit der die Textlänge bestimmt werden kann.

Die Maximallänge scheint 635 zu sein.

Zuerst ermittelst du die Länge von'...' und ziehst diese von 635 ab.
Das ist erstmal die neue Maximallänge.

Nun ermittelst Du in einer Schleife die Länge des Textes und wenn er länger als die neue Maximallänge ist, entfernst Du das letzte Zeichen und prüfst erneut.
Das wird solange wiederholt, bis die ermittelte Länge kleiner oder gleich der neuen Maximallänge ist.
An den bis dahin gekürzten String hängst Du nun noch die ... dran, dann müsste es (so hoffe ich) passen.

Ok Danke..
Ich baue gerade das Projekt von Sir Rufo um für D2010
Danach teste ich mal deine Variante.

gruss

Sir Rufo 17. Jun 2016 23:16

AW: Textlänge abschneiden
 
@nahpets

Nicht ganz.
  1. Länge des Texts bestimmen
  2. Ist die Länge kleiner als die Maximallänge, dann fertig - sonst
  3. Die Länge des Suffix bestimmen
  4. Beim Text schrittweise immer das letzte Zeichen entfernen bis die Länge kleiner als (Maximallänge - Suffixlänge) ist

@EWeiss

Beide Wege machen im Prinzip das Gleiche. Man müsste nur schauen, wer schneller ist.

Optimieren (Geschwindigkeit) kann man beide Verfahren noch.

Bei meiner Funktion kann man über eine Lookup-Tabelle die Anzahl der echten Abfragen stark reduzieren (wenn jedes Zeichen auch immer gleich breit bleibt).

Bei der Text-Funktion kann man die Zeichenanzahl erst mal per Schätzung bestimmen um dann nur noch wenige Zeichen zu entfernen.

Die Hauptfrage ist, was ist für OpenGL schneller: Einen ganzen Text mehrmals zu messen oder jeweils ein Zeichen

EWeiss 17. Jun 2016 23:24

AW: Textlänge abschneiden
 
@Sir Rufo

Habe dein Projekt jetzt mal umgelegt nach D2010
Ging nicht auf Anhieb lSuffixLength musste ein globale Variable sein.

Werde jetzt mal nach GL umlegen.
Über die PaintBox sieht es schon mal gut aus.

gruss

Sir Rufo 17. Jun 2016 23:29

AW: Textlänge abschneiden
 
Zitat:

Zitat von EWeiss (Beitrag 1340504)
@Sir Rufo

Habe dein Projekt jetzt mal umgelegt nach D2010
Ging nicht auf Anhieb lSuffixLength musste ein globale Variable sein.

Das wundert mich doch sehr. Kannst du den Code mal zeigen?

EWeiss 17. Jun 2016 23:36

AW: Textlänge abschneiden
 
Zitat:

Zitat von Sir Rufo (Beitrag 1340505)
Zitat:

Zitat von EWeiss (Beitrag 1340504)
@Sir Rufo

Habe dein Projekt jetzt mal umgelegt nach D2010
Ging nicht auf Anhieb lSuffixLength musste ein globale Variable sein.

Das wundert mich doch sehr. Kannst du den Code mal zeigen?

Klar! Hänge ihn an.
Der Compiler hat gemeckert das lSuffixLength nicht initialisiert war.. also (33339483884) so ne zahl.

Delphi-Quellcode:
raise EArgumentNilException.Create( 'CharMeasurement' );


Musste ich ausblenden gibt es scheinbar in D2010 nicht.

Delphi-Quellcode:
raise EArgumentOutOfRangeException.Create( 'SuffixLength > MaxLength' );


kommt immer sobald ich einen Char eingebe habe es deaktiviert.


opps zurück hab vergessen den anderen Kram noch auszublenden war mein Fehler.
Delphi-Quellcode:
//   if not Assigned( CharMeasurement )
//   then
//     raise EArgumentNilException.Create( 'CharMeasurement' );
//   if MaxLength < 0
//   then
//     raise EArgumentOutOfRangeException.Create( 'MaxLength'
Korrigierte Version hochgeladen D2010

gruss

EWeiss 18. Jun 2016 00:06

AW: Textlänge abschneiden
 
Das ist was ich vorher sagte.
Solange man Monotype Fonts verwendet funktioniert dein Code gut. (Auch meiner in GL)
Ich kann keinen Monotype Font verwenden weil es bescheiden aussieht ;)

Aber wehe wenn nicht.
Bei Arial geht es nicht mehr.

PaintBox und Label2 der gleiche Font Arial, Size = 24
Ich komme nicht auf die 560 mit dem Label2

siehe Anhang.
Font8 und Font24
Damit muss ich mich auch in GL rumschlagen.

Um das jetzt richtig zu berechnen müsste man über GetTextExtentPoint gehen incl. Kerning und dann wird es richtig kompliziert.
Dann würde es unter VCL gehen aber nicht mehr mit OpenGL.
Das ist nun mal so.

Ich denke das ist schon eine Wissenschaft für sich :)

gruss

Neutral General 18. Jun 2016 00:31

AW: Textlänge abschneiden
 
Das Canvas was die Länge des Textes berechnet muss natürlich auch die gleiche Font und die gleiche Fontgröße eignestellt haben...

Das ist eigentlich keine Wissenschaft. Du machst nur komische Dinge glaube ich...

EWeiss 18. Jun 2016 00:40

AW: Textlänge abschneiden
 
Zitat:

Zitat von Neutral General (Beitrag 1340509)
Das Canvas was die Länge des Textes berechnet muss natürlich auch die gleiche Font und die gleiche Fontgröße eignestellt haben...

Das ist eigentlich keine Wissenschaft. Du machst nur komische Dinge glaube ich...

Ja was denn :)
Habe ich natürlich.

Zitat:

Du machst nur komische Dinge glaube ich...
Denke ich nicht. Bin Dumm aber so dumm auch wieder nicht.

PaintBox und Label2 der gleiche Font Arial, Size = 24
bzw.
PaintBox und Label2 der gleiche Font Arial, Size = 8
Selbst der EditBox, Form1 habe ich den gleichen Font verpasst

So :) Unten der Label oben die PaintBox
Anhänge extra für dich.

gruss

Sir Rufo 18. Jun 2016 01:35

AW: Textlänge abschneiden
 
Liste der Anhänge anzeigen (Anzahl: 1)
Also in meinem Original-Quelltext ist
Delphi-Quellcode:
lSuffixLength
initialisiert ... wenn du das vergessen hast, warum initialisierst du die nicht einfach, anstatt da eine globale Variable von zu machen? :gruebel:

Anyway, im Anhang mal der Source (etwas umgebaut) und ein EXE (zum direkten Testen). Mit einem Klick auf die Paintbox kann man den Font auswählen und schwupps wird der Text mit dem neuen Font wieder angepasst dargestellt.

Ich hoffe wegen der Pixel-Geschichte sprechen wir jetzt nicht darüber, dass der Text ganz exakt genau so breit sein soll wie die Vorgabe ist. Das das so (zeichne mal einfach so den Text) nicht geht is hoffentlich jedem klar.

EWeiss 18. Jun 2016 01:46

AW: Textlänge abschneiden
 
Zitat:

wenn du das vergessen hast, warum initialisierst du die nicht einfach, anstatt da eine globale Variable von zu machen?
Wir reden irgendwie aneinander vorbei..
Es war doch von dir Initialisiert.

Delphi-Quellcode:
lSuffixLength := 0;

Ich hatte vergessen wie im vorherigen Post schon gesagt

Delphi-Quellcode:
   if not Assigned( CharMeasurement )
   then
//     raise EArgumentNilException.Create( 'CharMeasurement' );
   if MaxLength < 0
   then
//     raise EArgumentOutOfRangeException.Create( 'MaxLength' );

   lSuffixLength := 0;
die vier Zeilen ebenfalls noch zu Kommentieren.

Das hatte zu folge das dieser code ausgeführt wurde

Delphi-Quellcode:
   if MaxLength < 0
   then
     lSuffixLength := 0;
In dem Fall ist lSuffixLength natürlich nicht mehr initialisiert.

Aber wie gesagt das habe ich in meinem anderen Post schon mitgeteilt und berichtigt.
Es gibt unter D2010 kein "EArgumentNilException".

Zitat:

Die Hauptfrage ist, was ist für OpenGL schneller: Einen ganzen Text mehrmals zu messen oder jeweils ein Zeichen
Schneller ist nicht so das Problem ich lese den Text nur einmal wenn der Songtitel sich ändert.
Das schafft OpenGL allemal.

Zitat:

Das ist bei dir aktuell diese ominöse 58
Das ist nicht ominös.
Das sind die menge an Chars die in dem Bereich zwischen 5 und 635 reinpassen.
Nicht mehr nicht weniger.


gruss

EWeiss 18. Jun 2016 15:27

AW: Textlänge abschneiden
 
Ach so
Meine ominösen 58 sind letztendlich deine ominösen 80 ;)

Habe deine Unit getestet ist aber leider für meine Zwecke nicht Verwendbar.
Sie funktioniert nur verlässlich mit Monotypen Fonts die ich aber nicht einbinden will.
Destotrotz gute Arbeit andere können sie sicherlich verwenden.

Ich beende das hier und werde mich noch etwas mehr in die TextSuite einarbeiten.
Da gibt es eine Lösung dessen bin ich sicher.

gruss

nahpets 18. Jun 2016 15:39

AW: Textlänge abschneiden
 
Also, ich stelle mir das (analog zu Post #14) so vor:
Delphi-Quellcode:
if fMaxStrLen = 0 then begin
  fMaxLen   := 635;
  fMaxStrLen := tsTextGetWidthA(SongName);
  if fMaxStrLen >= fMaxLen then begin
    fSuffixLen := tsTextGetWidthA('...');
    fMaxLen   := fMaxLen - fSuffixLen;
    while fMaxStrLen >= fMaxLen do begin
      SongName  := Copy(SongName,1,Length(SongName) - 1);
      fMaxStrLen := SongName);
    end;
    SongName := SongName + '...';
  end;
end;
Liefert tsTextGetWidthA für 'iiiiiiii' und 'wwwwwwww' eigentlich das gleiche Ergebnis?

Wenn ja, wird das mit der Routine nix.

EWeiss 18. Jun 2016 15:55

AW: Textlänge abschneiden
 
Zitat:

Liefert tsTextGetWidthA für 'iiiiiiii' und 'wwwwwwww' eigentlich das gleiche Ergebnis?
Nein natürlich nicht.
Da es sich nicht um einen Monotonen Font handelt.
Aber das verstehen einige Leute hier nicht.

Arial:
I = 11
W = 25

Courier New
i= 25
W =25

Mit W bin ich also früher an die grenze von 635 gestoßen als mit I
Das führt dazu das der Umbruch viel früher stattfindet als bei I.
Der Abstand zu der Zeitanzeige ist dann logischer weise Unterschiedlich.

Zitat:

Zitat:

// Ist von der Breite so angelegt, dass mit Courier New 8 genau 80 Zeichen hineinpassen

Wie schon gesagt..
Funktioniert 100% solange es ein Monotoner Font ist.
Wenn nicht vergiss es denn dann Passen keine 80 Zeichen mehr hinein.

Man könnte das jetzt weiter verfolgen, aber mich scheinen einige Leute für so blöd zu halten
das ich nicht mal in der Lage bin einen Canvas den richtigen Font zuzuweisen.
Von daher vergesse ich das Thema jetzt hier.

Wenn Lossy im GL Forum zeit hat hineinzuschauen werde ich eine Antwort finden.
Danke für eure Hilfe.

gruss

nahpets 18. Jun 2016 17:53

AW: Textlänge abschneiden
 
Zitat:

Zitat von EWeiss (Beitrag 1340522)
Zitat:

Liefert tsTextGetWidthA für 'iiiiiiii' und 'wwwwwwww' eigentlich das gleiche Ergebnis?
Nein natürlich nicht.

Das hatte ich egentlich auch nicht erwartet und daher verwundert es mich, dass das Ganze nur mit Monotonen Font funktioniert.
Zitat:

Zitat von EWeiss (Beitrag 1340522)
Da es sich nicht um einen Monotonen Font handelt.
Aber das verstehen einige Leute hier nicht.

Arial:
I = 11
W = 25

Courier New
i= 25
W =25

Mit W bin ich also früher an die grenze von 635 gestoßen als mit I
Das führt dazu das der Umbruch viel früher stattfindet als bei I.
Der Abstand zu der Zeitanzeige ist dann logischer weise Unterschiedlich.

Von einem Ergebnis dieser Art bin ich eigentlich ausgegangen.
Zitat:

Zitat von EWeiss (Beitrag 1340522)
Zitat:

Zitat:

// Ist von der Breite so angelegt, dass mit Courier New 8 genau 80 Zeichen hineinpassen

Wie schon gesagt..
Funktioniert 100% solange es ein Monotoner Font ist.
Wenn nicht vergiss es denn dann Passen keine 80 Zeichen mehr hinein.

Bei Arial würd' ich hier jetzt davon ausgehen, dass 57 i oder 25 W passen.
Zitat:

Zitat von EWeiss (Beitrag 1340522)
Man könnte das jetzt weiter verfolgen, aber mich scheinen einige Leute für so blöd zu halten
das ich nicht mal in der Lage bin einen Canvas den richtigen Font zuzuweisen.

Den Eindruck, dass Dich hier im Thread irgendwer für blöd hält, habe ich nicht. Das Problem, mit dem Du Dich da rumschlägst, ist von der Logik her nicht zu fassen. Eigentlich müsste Dein Vorgehen zu einem richtigen Ergebnis führen und ebenso auch die anderen Lösungsvorschläge. Irritierend ist halt, dass dem nicht so ist. Aber dass hat doch nichts mit Deiner Person zu tun.
Zitat:

Zitat von EWeiss (Beitrag 1340522)
Von daher vergesse ich das Thema jetzt hier.

Wenn Lossy im GL Forum zeit hat hineinzuschauen werde ich eine Antwort finden.
Danke für eure Hilfe.

gruss

Momentan gehe ich davon aus, dass das Problem an einer Stelle liegt, auf die Du als Programmierer keinen Zugriff hast, also irgendwo innerhalb der von Dir genutzten Bibliothek.

Rollo62 18. Jun 2016 18:21

AW: Textlänge abschneiden
 
Bin mir nicht sicher, aber ist beim Proportionalfont nicht auch die Reihenfolge der Zeichen wichtig ?

Der Font sollte doch der Typogrrafie entsprechen: z.B. im Buchdruck.

Dann wäre z.B. "o" nach "P" etwas näher rangerückt (so inder der Art).
Also kämen bei "Po" andere Werte als bei "oP", oder so ähnlich.

Könnte das eventuell dein Problem damit sein ?
Dann müsste immer der ein ganzer String analysiert werden und
nur eine charakterweise Addition müsste Unterschiede ergeben.

Das wäre vielleicht die Erklärung wenn es ein Font tut und der Andere nicht.

Das nurmal als Gedankenanregung, bin auch kein Fontspezialist.

Rollo


Alle Zeitangaben in WEZ +1. Es ist jetzt 20:30 Uhr.
Seite 1 von 2  1 2      

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