// Falls man auf einer Zeichenfläche (TCanvas) einen längeren Text
// ausgeben möchte
(z.B. Spielregel oder Hilfetext) und zur
// Entwicklerzeit nicht weiß, welche Breite bei Laufzeit zur
// Verfügung steht
(z.B. User kann Fenstergröße ändern), dann
// braucht man in der Regel einen Zeilenumbruch. Noch besser ist,
// wenn am Zeilenende eine Silbentrennung vorgenommen wird.
// Deshalb habe ich mir den unten aufgeführten Code gestrickt.
//
Um nicht zu großen Aufwand zu betreiben, enthält er nur die
//
gängigsten Trenn-Regeln. Es wird beispielsweise davon
// ausgegangen, dass keine Fett- oder Kursivschrift vorhanden
// ist und dass nur eine Schriftgröße benutzt wird.
Zusammen-
//
gesetzte Wörter aus zwei oder mehreren Substantiven werden
//
nur bedingt berücksichtigt: Baumstamm wird fälschlicherweise
// zu Baums-tamm
("st" wird ja nach der neuen Rechtschreibung
// normalerweise getrennt)
, aber Holzstapel wird Holz-stapel,
// da sich zwischen "s" und "o" zwei Konsonanten befinden.
// Textlücken
(wie im Buchtext bei Beispiel 1, unterste Zeile)
// werden
entfernt, um das Schriftbild zu verbessern. Wörter,
// die besonders getrennt werden sollen, müssen in die
// Stringliste "
Ausnahmen" eingetragen werden, abwechselnd mit
// dem entsprechenden Trenn-Vorschlag
(siehe FormCreate). Die
// Variable "
Ueberhang" bestimmt, ob das Trennzeichen ("-")
//
über die vorgegebene Breite herausragen darf, weil das bei
// manchen Schriftarten nicht stört und Platz sparen kann. Die
// Variable "
Erweitert" bestimmt eine grobere Suche in den
// Ausnahmen.
// Beispiel: In der Stringliste steht das Wort
"katholisch", aber
// im Text steht das Wort
"katholischer". Bei Erweitert = False
// wird falsch getrennt, mit
Erweitert = True wird jedoch die
// Abtrennung der letzten Silbe
(-scher) nicht mehr vorgenommen.
// Es sei denn,
das Wort "katholischer" ist auch in den Ausnahmen
// eingetragen.
// Da in der Regel zur Entwicklerzeit der größte Teil des Textes
// bekannt sein dürfte, wird wohl mein Code für die meisten Fälle
// ausreichen.



// Getestet mit D2010 unter Win7
// überarbeitet 16.04.2016

Diese Abbildung gehört zu Beispiel 1
Silbentrennung = False Silbentrennung = True


 

type 
  TForm1 = class(TForm) 
    Button1: TButton; 
    Button2: TButton; 
    procedure FormCreate(Sender: TObject); 
    procedure FormDestroy(Sender: TObject); 
    procedure Button1Click(Sender: TObject); 
    procedure Button2Click(Sender: TObject); 
  private 
    { Private-Deklarationen } 
  public 
    { Public-Deklarationen } 
  end; 
 
var 
  Form1: TForm1; 
 
implementation 
 
{$R *.DFM} 
 
type 
  T3Char = array [0 .. 2] of Char; 
 
var 
  afz: T3Char = (#132, #147, #148); 
  Ausnahmen, BreakText: TStringList; 
 
const 
  vok = ['a', 'e', 'i', 'o', 'u', 'y', 'ä', 'ö', 'ü', '-']; 
 
procedure Vorbereitung(aTxt: String; Cnv: TCanvas; Breite: Word; 
  Silbentrennung, Ueberhang, Erweitert: Boolean); 
var 
  p, m: PChar; 
  wdt, akt, mnz, p13, leer: Integer; 
  s, s2: String; 
 
  procedure Ersatz; 
  var 
    i: Integer; 
  begin 
    for i := 0 to 2 do 
      aTxt := StringReplace(aTxt, afz[i], '"', [rfReplaceall]); 
  end; 
 
  function dritte(i: Integer; const ss: string): Boolean; 
  var 
    x: Integer; 
  begin 
    for x := i - 1 downto 1 do 
      if charinset(ss[i], vok) then 
      begin 
        result := true; 
        exit; 
      end; 
    result := false; 
  end; 
 
  function finden(const lg, ss: string): Integer; 
  var 
    w, i, j: Integer; 
  begin 
    result := 0; 
    i := 1; 
    j := 0; 
    for w := 1 to length(lg) do 
    begin 
      if lg[w] = ss[w + j] then 
        inc(i) 
      else if ss[w + j] = '-' then 
      begin 
        inc(i, 2); 
        inc(j); 
      end; 
    end; 
    for w := i downto 2 do 
      if ss[w] = '-' then 
      begin 
        result := w; 
        break; 
      end; 
  end; 
 
  function Suche(const ss: string): Integer; 
  var 
    w: Integer; 
  begin 
    result := -1; 
    if Ausnahmen.Count = 0 then 
      exit; 
    w := 0; 
    repeat 
      if AnsiCompareText(Ausnahmen[w], ss) = 0 then 
      begin 
        result := w; 
        break; 
      end; 
      inc(w, 2); 
    until w >= Ausnahmen.Count; 
  end; 
 
  function SuchTeil(ss: string): Integer; 
  var 
    w: Integer; 
  begin 
    result := -1; 
    if (Ausnahmen.Count = 0) or not Erweitert then 
      exit; 
    w := 0; 
    repeat 
      if AnsiPos(AnsiUppercase(Ausnahmen[w]), AnsiUppercase(ss)) = 1 then 
      begin 
        result := w; 
        break; 
      end; 
      inc(w, 2); 
    until w >= Ausnahmen.Count; 
  end; 
 
  function trennen: Boolean; 
  var 
    x, y, st, z, i, a, b, dopp: Integer; 
    c: array [1 .. 6] of Char; 
    rest, anf: string; 
    procedure stelle; 
    var 
      k: Integer; 
    begin 
      for k := 3 to 6 do 
      begin 
        dec(b); 
        if b >= 1 then 
          c[k] := aTxt[b] 
        else 
          c[k] := #0; 
      end; 
    end; 
 
  begin 
    result := false; 
    for x := length(aTxt) downto 2 do 
    begin 
      y := 1; 
      b := x - 1; 
      c[1] := aTxt[pred(x)]; 
      c[2] := aTxt[x]; 
      stelle; 
      dopp := ord(c[1] = c[2]); // z.B. Trau-ung 
      if (not charinset(c[1], vok) or (dopp = 1)) and (charinset(c[2], vok)) or 
        (charinset(c[4], vok) and charinset(c[1], vok) and charinset(c[3], 
          vok)) then 
      begin 
        for z := x - 2 downto 1 do // zweiter Vokal? 
          if charinset(aTxt[z], vok) then 
          begin 
            if c[1] = 'h' then 
            begin // CH und SCH nicht trennen 
              inc(y, ord(UpperCase(copy(aTxt, x - 2, 1)) = 'C')); 
              inc(y, ord(UpperCase(copy(aTxt, x - 3, 1)) = 'S')); 
            end 
            else // "ck" nicht trennen 
              inc(y, ord((c[1] = 'k') and (aTxt[x - 2] = 'c'))); 
            rest := copy(aTxt, b + 1, 3); 
            if (c[6] > #0) and (rest = 'sch') then 
            begin 
              if not charinset(c[6], vok) // Hubsch-rauber --> Hub-schrauber 
                or dritte(b, aTxt) // Wäschesch-leuder --> Wäsche-schleuder 
                then 
                inc(y, 3); 
            end; 
            rest := copy(aTxt, x - y, maxint); 
            anf := copy(aTxt, 1, x - succ(y)); 
            if (wdt + Cnv.TextWidth(anf) + mnz <= Breite) and 
              (copy(aTxt, x - succ(y), 1) <> '/') then // nicht an "/" 
            begin // wenn's passt 
              i := 0; 
              a := Suche(aTxt); 
              if a < 0 then 
                a := SuchTeil(aTxt); 
              if a >= 0 then 
              begin 
                i := finden(anf, Ausnahmen[a + 1]); 
                if i > 0 then 
                begin 
                  s2 := StringReplace(copy(Ausnahmen[a + 1], 1, i - 1), '-', 
                    '', [rfReplaceall]) + '-'; 
                  if charinset(aTxt[1], ['A' .. 'Z']) then 
                    s2[1] := upcase(s2[1]); 
                  aTxt := copy(aTxt, length(s2), maxint); 
                end; 
              end; 
              if i = 0 then 
              begin 
                if (c[3] = 's') and ((c[1] = 't') or (c[1] = 'p')) 
                  and not charinset(c[4], vok) and not charinset(c[5], vok) 
                  then 
                begin // Holzs-tiel --> Holz-stiel 
                  rest := ansilastchar(anf) + rest; 
                  inc(y); 
                end; 
                s2 := copy(aTxt, 1, x - succ(y) + dopp); 
                if ansilastchar(s2) = #39 then // z.B. Bismark'sche 
                  delete(s2, length(s2), 1); 
                if (s2 <> '') and (ansilastchar(s2) <> #13) and 
                  (ansilastchar(s2) <> #10) then 
                  if ansilastchar(s2) <> '-' then 
                    s2 := s2 + '-'; 
                aTxt := copy(rest, 1 + dopp, maxint); 
              end; 
              result := true; 
              exit; 
            end; 
          end; 
      end; 
    end; 
  end; 
 
begin 
  BreakText.Clear; 
  Ersatz; // Anfürungszeichen anpassen für bestimmte Schriftarten 
  p := PChar(AdjustLineBreaks(aTxt)); 
  mnz := Cnv.TextWidth('-') * ord(Ueberhang); 
  leer := Cnv.TextWidth(#32); 
  s := ''; 
  repeat 
    m := p; 
    while not charinset(p^, [#0, #32]) do 
      inc(p); 
    SetString(aTxt, m, p - m); 
    if aTxt <> '' then 
    begin 
      akt := Cnv.TextWidth(aTxt); 
      wdt := Cnv.TextWidth(s); 
      s2 := ''; 
      if wdt + akt > Breite then 
      begin 
        if Silbentrennung then 
          if trennen then 
            s := s + s2; 
        p13 := pos(#13, s); 
        if p13 > 0 then 
        begin 
          if p13 > 1 then 
            aTxt := copy(s, p13 + 1, maxint); 
          s := copy(s, 1, p13 - 1); 
          BreakText.Add(s); 
          s := ''; 
          p := m - length(aTxt) - 1 + length(s2); 
          if p^ = #13 then 
            inc(p); 
          continue; 
        end; 
        BreakText.Add(s); 
        s := trim(aTxt) + #32; 
        continue; 
      end; 
      s := s + aTxt; 
      if Cnv.TextWidth(s) + leer <= Breite then 
        s := s + #32; 
    end; 
    while p^ = #32 do 
      inc(p); 
    if p^ = #0 then 
    begin 
      BreakText.Add(s); 
      break; 
    end; 
  until false; 
end; 
 
procedure Anzeige(Cnv: TCanvas; x, y: Integer); 
var 
  i, d: Integer; 
begin 
  if BreakText.Count = 0 then 
    exit; 
  Cnv.Brush.Style := bsclear; 
  d := Cnv.TextHeight(BreakText[0]); 
  for i := 0 to BreakText.Count - 1 do 
    Cnv.TextOut(x, y + i * d, BreakText[i]); 
end; 
 
procedure TForm1.FormCreate(Sender: TObject); 
begin 
  BreakText := TStringList.Create; 
  Ausnahmen := TStringList.Create; 
 
  Ausnahmen.Add('aufgrund'); 
  Ausnahmen.Add('auf-grund'); 
 
  Ausnahmen.Add('Prophet'); 
  Ausnahmen.Add('Pro-phet'); 
 
  Ausnahmen.Add('katholisch'); 
  Ausnahmen.Add('ka-tho-lisch'); 
 
  Ausnahmen.Add('beinhalten'); 
  Ausnahmen.Add('be-inhal-ten'); 
  { nicht be-in-hal-ten, da bei der Trennung 
    zwischen n und h "bein-halten" entsteht } 
  Ausnahmen.Add('Analphabet'); 
  Ausnahmen.Add('An-alpha-bet'); 
  { nicht An-al-pha-bet, da bei der Trennung 
    zwischen l und p "Anal-phabet" entsteht } 
  Ausnahmen.Add('Cashewnuss'); 
  Ausnahmen.Add('Ca-shew-nuss'); 
 
  Ausnahmen.Add('Cashewnüsse'); 
  Ausnahmen.Add('Ca-shew-nüs-se'); 
 
  Ausnahmen.Add('Baumstamm'); 
  Ausnahmen.Add('Baum-stamm'); 
  // usw. 
end; 
 
procedure TForm1.FormDestroy(Sender: TObject); 
begin 
  Ausnahmen.Free; 
  BreakText.Free; 
end; 
 
// -------------------------------------------------------------------- 
 
// Beispiel 1 
// mittels Textauszug aus dem Buch "Ein paar Kurze" 
 
const 
  txt = 'Seine Darstellung des Weihnachtsmannes bescherte ihm neben Beifall ' + 
    'auch noch einen zusätzlichen Effekt: Ab diesem Zeitpunkt nannten ihn ' + 
    'alle beharrlich "Santa Klaus". Später wurde dann der zweite Teil ' + 
    'dieser Bezeichnung weggelassen, da die Menschen aufgrund ihrer ' + 
    'angeborenen Faulheit alles unbedingt abkürzen müssen. Fortan hörte ' + 
    'also Klaus Wegener mit einem gewissen Stolz auf den Namen "Santa". ' + 
    'Und das sollte so bis an sein Lebensende bleiben.' + #13 + 
    'Santa konnte in fast allen Schulfächern eine Eins auf dem Zeugnis ' + 
    'vorweisen. Besonders hatten es ihm die Naturwissenschaften angetan. ' + 
    'Chemie und Physik liebte er mehr als alles andere auf der Welt. Dabei ' + 
    'war er absolut kein Streber. Zensuren interessierten ihn genauso viel, ' + 
    'als würde in Mexiko ein grün lackiertes Fahrrad umfallen. Ihm ging es ' + 
    'nur um das Lernen an sich. Sein Gehirn schien exklusiv in diese Welt ' + 
    'gekommen        zu sein, um wie ein Schwamm Wissen aufzusaugen.'; 
 
procedure TForm1.Button1Click(Sender: TObject); 
var 
  breit, rand: Integer; 
begin 
  breit := 273; 
  rand := 20; 
  Canvas.Font.Size := 12; 
  Canvas.Font.Name := 'Arial'; 
 
  Vorbereitung(txt, Canvas, breit, false, false, false); 
  Anzeige(Canvas, rand, rand); 
 
  Vorbereitung(txt, Canvas, breit, true, false, true); 
  Anzeige(Canvas, breit + rand * 2, rand); 
end; 
 
// -------------------------------------------------------------------- 
 
// Beispiel 2 
// Text aus Textdatei laden 
procedure TForm1.Button2Click(Sender: TObject); 
var 
  fs: TFileStream; 
  ss: TStringStream; 
begin 
  ss := TStringStream.Create; 
  fs := TFileStream.Create('Test.txt', fmOpenRead); 
  fs.Position := 0; 
  ss.CopyFrom(fs, 0); 
  fs.Free; 
  Vorbereitung(ss.DataString, Canvas, 450, true, true, true); 
  ss.Free; 
  Canvas.Brush.Color := clBtnFace; 
  Canvas.FillRect(Canvas.ClipRect); 
  Anzeige(Canvas, 10, 10); 
end;

 


 

Zugriffe seit 6.9.2001 auf Delphi-Ecke