?
Solved

Changing Text Line Spacing using Windows API

Posted on 2004-11-08
8
Medium Priority
?
2,042 Views
Last Modified: 2012-05-05
Hi everyone,

I am currently programming in delphi and drawing text on a Canvas using the DrawText windows API.
The method can draw text and wrap it inside a specified Rectangle. The problem is that I can't modify the
vertical spacing between lines once the text wraps. anyone knows how? Thanks in advance!
:)

Here's my code:
const
   TEXT = 'The quick brown fox jumped over the sly lazy dog.';
var
   Flags : integer;
   MyRect : TRect;
begin
   Flags := DT_WORDBREAK;
   MyRect := Rect(0,0,20,20);
   DrawText(Image1.Canvas.Handle, PChar(TEXT), Length(TEXT), MyRect, Flags);
end;
0
Comment
  • 4
  • 2
  • 2
8 Comments
 
LVL 46

Expert Comment

by:aikimark
ID: 12524194
include the DT_CALCRECT flag value to have the DrawText function return the height of the text.
http://www.undu.com/Articles/010129a.html
http://www.undu.com/Articles/981008d.html

You can also get the height (in pixels) of the current font settings with:
Th:=Image1.Canvas.TextHeight('Xg');        {Height of current font in pixels}
0
 

Author Comment

by:dabestprogrammerindauniverse
ID: 12525093
thanks aikimark,
but my question is how to change the width between the lines of the spacing between the lines. :)
0
 
LVL 46

Expert Comment

by:aikimark
ID: 12526004
you might have to roll-your-own line wrapping:
Loop through the words in the string, appending each word (to be written to your canvas) until the temporary string is larger than the rectangle you wish to draw.  

Remove the last word added (or hyphenate it, which is a more advanced technique and more difficult).

Write the string to the canvas

Reset the string variable

Move the CurrentY position of the canvas down by some multiplier of the height of the fontsize.

Start appending new words with the next iteration of the loop until you have written all the words.
0
Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

 

Author Comment

by:dabestprogrammerindauniverse
ID: 12529525
Yup, I figured that out. Also, in case a line consists of only one word whose width is wider than the specified rect, then the clipping should be on a per character basis. So there isn't any other way is there?
thanks! :)
0
 
LVL 46

Expert Comment

by:aikimark
ID: 12533673
I do not know of any property, method, or API that would allow you do this easily.

Here's an interesting idea for you to explore:
1. Use DrawText to automatically wrap your text
2. save the characters or character images from each line into a data structure
3. Clear the drawing area rectangle
4. increase the height of the drawing area by the
number_of_lines * (inter_line_space_increase - 1)
5. loop through your saved lines and print/draw them at the appropriate location.

============
I've never written a hyphenation routine, so can't give you much help on that topic.
0
 
LVL 46

Expert Comment

by:aikimark
ID: 12533754
Another thought would be to use a Rich Text Format object to do the wrapping and inter-line spacing for you and do either of the following:
1. replace the DrawText area with the visible RTF control
2. copy the RTF object's canvas to your Image1.Canvas
0
 
LVL 34

Expert Comment

by:Slick812
ID: 12548894
hello  dabestprogrammerindauniverse , , I have used the  GetTextExtentExPoint( ) function to get a Line break distance, like this -



procedure TForm1.but_CustomTextClick(Sender: TObject);
var
TextStr: String;
Bmp1: TBitmap;
numChar, lines: Integer;
aSize: tagSize;
begin
// button click event
TextStr := 'This is a text string long enough to wrap a couple of times. And '+
           'More text here, followed by even more 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 := 210;
  Bmp1.Canvas.Font.Name := 'Arial';
  Bmp1.Canvas.Font.Size := 12;

  if Length(TextStr) < 1 then Exit;
  lines := 0;
  repeat
  GetTextExtentExPoint(Bmp1.Canvas.Handle, PChar(TextStr),Length(TextStr),182,
                     @numChar, nil, aSize);
  TextOut(Bmp1.canvas.handle,3,(lines*(aSize.cy+8))+3,PChar(TextStr), numChar);
                                              // use the y postion above to adjust the custom spacing per line
  Delete(TextStr,1,numChar);
  Inc(Lines);
  until Length(TextStr) < 1;

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


 = = = = = = = = = = = = = =  = = = = = = =  =
this seems to work for me, however this does NOT find spaces and punctuation to divide the words, it will chop a word anywhere
  ask questions if you need more
0
 
LVL 34

Accepted Solution

by:
Slick812 earned 375 total points
ID: 12549109
very sorry, I posted before looking some, I found better do what, you may try this one, I tink it will devide on spaces an punctuaton



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;
0

Featured Post

Free Tool: Subnet Calculator

The subnet calculator helps you design networks by taking an IP address and network mask and returning information such as network, broadcast address, and host range.

One of a set of tools we're offering as a way of saying thank you for being a part of the community.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Have you ever had your Delphi form/application just hanging while waiting for data to load? This is the article to read if you want to learn some things about adding threads for data loading in the background. First, I'll setup a general applica…
Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
Whether it be Exchange Server Crash Issues, Dirty Shutdown Errors or Failed to mount error, Stellar Phoenix Mailbox Exchange Recovery has always got your back. With the help of its easy to understand user interface and 3 simple steps recovery proced…
With just a little bit of  SQL and VBA, many doors open to cool things like synchronize a list box to display data relevant to other information on a form.  If you have never written code or looked at an SQL statement before, no problem! ...  give i…
Suggested Courses
Course of the Month14 days, 12 hours left to enroll

840 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question