| Autor |
Beitrag |
Logikmensch
      
Beiträge: 390
Win XP
Delphi 2007 Prof., XE2, XE5
|
Verfasst: Di 19.06.07 08:34
Liebe Delphi-Freunde.
Ich möchte einen Textblock innerhalb eines rechteckigen Bereiches ausgeben. Den Schriftgrad (Buchstabenhöhe) kann der Benutzer frei wählen, ebenso die Schriftart.
Der rechteckige Bereich, in den der Text mit Wortumbruch geschrieben werden soll, hat eine feste Breite (in Pixeln bzw. cm), die nicht geändert werden darf, weil es sich um den Zeichenbereich einer Druckvorschau handelt.
Lediglich die Höhe, die der umgebrochene, ggf. mehrzeilige Text in Pixeln einnimmt, ist zu berechnen, also unbekannt.
Ich habe bereits mit DrawText und der Option WortBreak+CalcRect versucht, das geeignete
Rechteck zu berechnen, aber dummerweise wird die Breite dabei auch mitverändert.
Mein zu zeichnender Text verfügt nämlich nicht über Zeilenvorschübe, sondern nur Leerfelder, daher versagt DrawText mit vorhergehender Calcrect-Option
leider seinen Dienst. Die Breite des Rechteckes darf wie gesagt nicht geändert werden, nur die Höhe soll ermittelt werden.
Muss ich, um das Problem zu lösen, wirklich Zeichen für Zeichen bzw. Wort für Wort manuell in den rechteckigen Bereich reinzeichnen?
_________________ Es gibt keine Probleme - nur Lösungen!
|
|
Stefan.Buchholtz
      
Beiträge: 612
WIN 2000, WIN XP, Mac OS X
D7 Enterprise, XCode, Eclipse, Ruby On Rails
|
Verfasst: Di 19.06.07 08:47
Nein, mit DrawText sollte es eigentlich funktionieren. Zeig mal deinen Code.
Stefan
_________________ Ein Computer ohne Windows ist wie eine Schokoladentorte ohne Senf.
|
|
Logikmensch 
      
Beiträge: 390
Win XP
Delphi 2007 Prof., XE2, XE5
|
Verfasst: Di 19.06.07 10:16
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22:
| procedure gettextblock(line:integer; canvas:TCanvas; var drawrect:TRect; fontname:string; textheight:integer); var r:TRect; begin if (line>=0) and (line<textlist.Count) then begin r:=drawrect; canvas.font.name:=fontname; Canvas.font.size:=textheight; DrawText(Canvas.Handle,PChar(textlist[line]),length(textlist[line]),r, dt_wordwrap or dt_left or dt_calcrect); drawrect:=r; end; end |
Ist aus dem Gedächtnis hier eingefügt. Das Dumme ist, dass die ermittelten Koordinaten
für den berechneten rechteckigen Bereich zwar sinn machen (auch wenn die Breite ungewollt verändert wird), aber ein nachfolgendes
DrawText mit genau diesen Koordinaten, jedoch ohne dt_calcrect, den Text nicht zeichnen will (es erscheint gar nichts),
aber das ist noch ein anderes Problem, das ich lösen muss.
Es handelt sich übrigens um Druckerkoordinaten, die ich oben angebe.
Ich versuche das ganze nochmal in einem einfachen Formular mit einem einfachen,
einzeiligen Text. Da sehe ich dann hoffentlich auch mal, wie der Text denn nach
der Anpassung aussieht.
_________________ Es gibt keine Probleme - nur Lösungen!
|
|
Stefan.Buchholtz
      
Beiträge: 612
WIN 2000, WIN XP, Mac OS X
D7 Enterprise, XCode, Eclipse, Ruby On Rails
|
Verfasst: Di 19.06.07 11:22
Komisch - es sieht für mich richtig aus.
Überprüfe mal den Rückgabewert von DrawText - da sollte die texthöhe auch zurückgegeben werden, oder 0 wenn ein Fehler aufgetreten ist. Dann kannst du den Fehlercode mit GetLastError ermitteln.
Stefan
_________________ Ein Computer ohne Windows ist wie eine Schokoladentorte ohne Senf.
|
|
Logikmensch 
      
Beiträge: 390
Win XP
Delphi 2007 Prof., XE2, XE5
|
Verfasst: Di 19.06.07 12:42
Ok, Stefan, das mach ich. Ich melde mich hier, sobald ich etwas Neues weiß.
Danke sehr!
_________________ Es gibt keine Probleme - nur Lösungen!
|
|
Lannes
      
Beiträge: 2352
Erhaltene Danke: 4
Win XP, 95, 3.11, IE6
D3 Prof, D4 Standard, D2005 PE, TurboDelphi, Lazarus, D2010
|
Verfasst: Di 19.06.07 12:48
Hallo,
ich hab so den Verdacht das Du mit einer falschen Konstante arbeitest.
imho ist dt_wordwrap falsch, nimm mal DT_WORDBREAK = $16
_________________ MfG Lannes
(Nichts ist nicht Nichts) and ('' <> nil ) and (Pointer('') = nil ) and (@('') <> nil )
|
|
Logikmensch 
      
Beiträge: 390
Win XP
Delphi 2007 Prof., XE2, XE5
|
Verfasst: Di 19.06.07 14:11
Ja, danke Lannes, ich hab's gemerkt, als ich nachfolgendes Demo eingetippselt habe.
Hier eine kleine Demonstration des Problems. Ich habe mal ein winziges Demoprogramm geschrieben, welches zeigt, dass bei Worten, die zu breit sind für das rote Rechteck, diese den ganzen Rechteckblock
verbreitern (das Ergebnis wird als hellblaues Rechteck dargestellt).
Hier ein Bild, wie es korrekt arbeitet (Schriftart ist klein genug):
Hier ein Bild, wo die Schriftart zu groß wird und die gesetzte Rahmenbegrenzung (hier rot) überschreitet:
Und weil es so interessant ist, gibt's das kleine Demoprogrämmchen hier noch mit dabei.
Ich habe dabei Regler für die Textbegrenzung eingerichtet, und einen für die prozentuale Texthöhe.
Leider wird auch nie ein Fehlercode erzeugt.
Was meint ihr dazu - ist das nicht blöd?
Ich meine, wenn Text in ein Memo eingegeben wird, dann verhält sich das ganze eigentlich so, wie ich es mir
hier auch wünschen würde. Aber dort wird ja nicht einfach nur dargestellt, da wird richtig zusammen-
und auseinandergebaut (Memo.Text --> Memo.Lines).
Tja ich habe nochmal in der API32-Referenz nachgelesen, es gibt keine weiteren Konstanten für Drawtext, die den Text vollständig, aber immer innerhalb der angegebenen Breite berechnen. Oder hat da einer/eine von Euch noch eine Idee.

Einloggen, um Attachments anzusehen!
_________________ Es gibt keine Probleme - nur Lösungen!
|
|
Lannes
      
Beiträge: 2352
Erhaltene Danke: 4
Win XP, 95, 3.11, IE6
D3 Prof, D4 Standard, D2005 PE, TurboDelphi, Lazarus, D2010
|
Verfasst: Di 19.06.07 15:40
Hallo,
ist eigentlich alles klar
DrawText berechnet auf der Grundlage der vorgegebenen Breite aus der Rect-Variable, die benötigte Höhe. Benötigt ein Wort mehr Platz als die vorgegebene Breite ist die zurückgelieferte Breite entsprechend größer.
Die Funktionalität wie sie zum Beispiel ein Memo bietet, Umbruch im Wort, erfüllt DrawText nicht.
Da wirst Du selbst die Funktionalität des Memo nachbilden müssen und damit das Wort an der entsprechenden Stelle getrennt wird(ein Leerzeichen einfügen). Ein Ansatzpunkt bietet Dir schon DrawText, denn wenn die zurückgelieferte Breite größer als die vorgegebenen Breite ist, enthält der String ein zu langes Wort.
Ich kann mich da an ein Thema in der DP erinnern, finde es aber nicht wieder.
_________________ MfG Lannes
(Nichts ist nicht Nichts) and ('' <> nil ) and (Pointer('') = nil ) and (@('') <> nil )
|
|
Logikmensch 
      
Beiträge: 390
Win XP
Delphi 2007 Prof., XE2, XE5
|
Verfasst: Di 19.06.07 17:42
Hallo!
ich weiß, ich bin manchmal ein bisschen genervt, wenn ich herausfinde, dass die GDI die Routinen nicht so umgesetzt hat, wie es (meiner Meinung nach) mehr Sinn machen würde. Aber hätte ja auch sein klnnen, dass es da eine einfache Variante (vielleicht eine GDI-Funktion) gegeben hätte.
Lannes: Du hast völlig recht, ich denke auch, dass ich die Methode, die ich dringend benötige, selber machen muss. Ich habe nochmal weiter geschaut und in der API32-Referenz von Charles Petzold auch eine entsprechende Stelle gefunden, die genau das Problem aufgreift. Sinngemäß steht da drin, dass DrawText eher für einfache Textumbrüche gedacht ist und nicht etwa solche Dinge tut, wie wir sie von kleinen Textverarbeitungen her kennen.
Dann werde ich diesen Thread erst einmal zu machen und meine Methode, wenn ich sie fertig habe, hier posten.
_________________ Es gibt keine Probleme - nur Lösungen!
|
|
Logikmensch 
      
Beiträge: 390
Win XP
Delphi 2007 Prof., XE2, XE5
|
Verfasst: Di 19.06.07 17:44
... und danke für Eure Hilfen! Sie haben geholfen, keine Sorge!
_________________ Es gibt keine Probleme - nur Lösungen!
|
|
Logikmensch 
      
Beiträge: 390
Win XP
Delphi 2007 Prof., XE2, XE5
|
Verfasst: Mi 20.06.07 15:15
Hallo Freunde,
zum Abschluss dieses Threads möchte ich nun meine Lösung vorstellen, die zwar momentan nur für linksbündigen Text und ohne die Möglichkeit, Leerräume zwischen den einzelnen Texten zu verbreitern (= Blocksatz) geschaffen ist. Aber das, was ich fordere, erfüllt sie souverän. Die Funktion CalcTextInRect schreibt Text in ein vorgegebenes Rechteck, ohne seine Breite zu verändern - KOMME WAS WOLLE.
Bei zu breiten Wörtern wird versucht, diese einzupassen. Bitte lest euch dafür meine Kommentare durch, die sprechen glaube ich für sich.
Zwei Fehler werden von der Funktion erfolgreich abgefangen (Result=False): Ein einzelnes Zeichen ist breiter als das Rechteck, oder der Versuch, durch Anhängen neuer Zeilen das Platzproblem zu lösen, schlägt fehl (was natürlich auch bedeutet, dass ein Zeichen breiter als das Rechteck ist).
Ich habe jetzt noch kein Flag Calcrec eingebaut, aber wer es mag, kann dies nachrüsten und im Falle von einer Größenberechnung ohne Textausgabe die ganzen TextOuts unterlassen.
Das rote Rechteck ist übrigens das Ausgangsrechteck, das blaue das Ergebnisrechteck,
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163: 164: 165: 166: 167: 168: 169: 170: 171: 172: 173: 174: 175: 176: 177: 178: 179: 180: 181: 182: 183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199:
| unit Unit1;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, Mask, JvExMask, JvSpin, StdCtrls, ExtCtrls;
type TForm1 = class(TForm) Panel1: TPanel; TextEdit: TEdit; widthedit: TJvSpinEdit; heightedit: TJvSpinEdit; Label1: TLabel; Label2: TLabel; textheightedit: TJvSpinEdit; Label3: TLabel; procedure CommonEditChange(Sender: TObject); private public end;
var Form1: TForm1;
implementation
{$R *.dfm}
var wordchars:set of char=['a'..'z','A'..'Z','0'..'9','$','_'];
function getwordlength(const t:string):integer; var i:integer; begin result:=0; for i := 1 to length(t) do if t[i] in wordchars then inc(result) else break; end;
function getspacelength(const t:string):integer; var i:integer; begin result:=0; for i := 1 to length(t) do if t[i]=' ' then inc(result) else break; end;
function CalculateTextinRect(canvas:TCanvas; var destrect:TRect; t:string):boolean; var wordlen,wordwidth,i,x,y,w:integer; wd:string; textheight:integer; begin result:=true; x:=destrect.left; y:=destrect.Top; w:=destrect.Right-destrect.Left; if length(t)>0 then begin textheight:=canvas.TextHeight(t[1]); while length(t)>0 do begin case t[1] of 'a'..'z','A'..'Z','0'..'9','_':begin wordlen:=getwordlength(t); wd:=copy(t,1,wordlen); end; ' ':begin wordlen:=getspacelength(t); wd:=stringofchar(' ',wordlen); end; else begin wordlen:=1; wd:=t[1]; end; end; wordwidth:=canvas.TextWidth(wd); if x+wordwidth<=destrect.Right then begin canvas.TextOut(x,y,wd); inc(x,wordwidth); if x>=destrect.Right then begin inc(y,textheight); x:=destrect.Left; end; end else begin if wordwidth>w then begin if wordlen>1 then begin for i := wordlen-1 downto 1 do begin wordwidth:=canvas.TextWidth(copy(wd,1,i)); if wordwidth+x<=destrect.right then begin wd:=copy(wd,1,i); wordlen:=length(wd); break; end; end; if i>0 then begin canvas.TextOut(x,y,wd); inc(x,wordwidth); if x>=destrect.right then begin inc(y,textheight); x:=destrect.Left; end; end else begin if x>destrect.left then begin inc(y,textheight); x:=destrect.Left; wordlen:=0; end else begin result:=false; exit; end; end; end else begin result:=false; exit; end; end else begin inc(y,textheight); x:=destrect.Left; canvas.TextOut(x,y,wd); inc(x,wordwidth); if x>=destrect.right then begin inc(y,textheight); x:=destrect.Left; end; end; end; delete(t,1,wordlen); end; if x=destrect.left then dec(y,textheight); destrect.Bottom:=y+textheight; end; end;
procedure TForm1.CommonEditChange(Sender: TObject); var r:TRect; w,h,textheight:integer; begin canvas.Brush.Style:=bsSolid; canvas.Brush.Color:=clBtnFace; canvas.FillRect(clientrect); w:=round(clientwidth*widthedit.value/100); h:=round((clientheight-Panel1.Height)*heightedit.Value/100); r:=bounds((clientwidth-w) div 2,(clientheight-Panel1.Height-h) div 2,w,h); canvas.Pen.Color:=clRed; canvas.Brush.Style:=bsClear; canvas.Rectangle(r); textheight:=round(textheightedit.value*h/100); canvas.Font.Color:=clMaroon; canvas.Font.Height:=textheight; CalculateTextInRect(canvas,r,textedit.Text); canvas.Pen.Color:=clblue; canvas.Rectangle(r); end;
end. |
Ich wünsche viel Spaß mit der Routine. Übrigens, weiter oben ist der Projektcode der ersten Version dieses Programmes (und das Formular). Ihr müsst nur den neuen Sourcecode in den IDE-Editor per drag & drop reinholen.
_________________ Es gibt keine Probleme - nur Lösungen!
|
|
|