Results 1 to 9 of 9

Thread: [RESOLVED] Font Scaling re-opened

  1. #1

    Thread Starter
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    40,104

    Resolved [RESOLVED] Font Scaling re-opened

    My day is done, so I figured I might as well ask a question about the last thing I was working on. There has to be a better way to do this than what I am seeing. Here's the problem:

    I have a picturebox that has an image on it. The user will be able to draw rectangles on that image, and the rectangle will have a string drawn into it. Frankly, I have no idea about the shape or size of these rectangles except to say that they will have a "reasonable" size to them. What I am trying to figure out is how best to draw the string inside the rectangle. Obviously, the size of the string is determined by the font, and I can measure this size with MeasureString in the Paint event for the PB. The size of the string as drawn must be less than the size of the rectangle such that the string fits entirely within the rectangle.

    The problem is that MeasureString takes a font so that the size of the string can be determined in that font. That seems to require that I create a new font object, measure the string in that font, check to see that it is less than the width of the rectangle (don't have to worry about the height), and if the string is too big, reduce the size of the font and try again. This seems to have the potential to create and destroy plenty of font objects for the sole purpose of checking sizes. Is there a better way? How about a method that figures the maximum font size that can be used to get a string of pixel length X?

    The second half of this problem is something I haven't even begun looking at yet, but somebody has probably got an answer that will save me some time, so I'll toss it out here:

    It is possible that my rectangles will be high and narrow, in which case few strings will fit horizontally in the rectangle, so I might as well draw them vertically. Is there an easy way to draw text vertically rather than horizontally?
    Last edited by Shaggy Hiker; Jan 3rd, 2011 at 07:24 PM.
    My usual boring signature: Nothing

  2. #2
    PowerPoster boops boops's Avatar
    Join Date
    Nov 2008
    Location
    Holland/France
    Posts
    3,201

    Re: Font Scaling

    half 1: Measure the string with any size font and use Graphics.ScaleTransform to fit it into the rectangle.

    half 2: You could use Graphics.RotateTransform to turn the string through a right angle, but it's slightly easier to use a StringFormat with the DirectionVertical format flag because you don't have to translate the rotation centre.

    Being blunder prone, I had to check my facts by trying it out and maybe you will find my code useful as an example. It also allows for "unreasonable" rectangles and goes in the Paint event:
    vb.net Code:
    1. Dim testString As String = "A long piece of string"
    2.         Dim targetRectangle As New Rectangle(100, 100, 50, 300)
    3.         e.Graphics.DrawRectangle(Pens.Black, targetRectangle)
    4.         Dim scaleFactor As Single
    5.         Dim stringSize As SizeF = e.Graphics.MeasureString(testString, Me.Font, Point.Empty, StringFormat.GenericDefault)
    6.         Using sf As New StringFormat
    7.             If targetRectangle.Width > targetRectangle.Height Then
    8.                 'the string is drawn horizontally:
    9.                 scaleFactor = Math.Min(targetRectangle.Width / stringSize.Width, targetRectangle.Height / stringSize.Height)
    10.             Else
    11.                 'the string is drawn vertically:
    12.                 sf.FormatFlags = StringFormatFlags.DirectionVertical
    13.                 scaleFactor = Math.Min(targetRectangle.Height / stringSize.Width, targetRectangle.Width / stringSize.Height)
    14.             End If
    15.             'scale the string around the rectangle origin:
    16.             e.Graphics.TranslateTransform(targetRectangle.Left, targetRectangle.Top)
    17.             e.Graphics.ScaleTransform(scaleFactor, scaleFactor)
    18.             e.Graphics.TranslateTransform(-targetRectangle.Left, -targetRectangle.Top)
    19.             'draw the string using a vertical StringFormat if appropriate:
    20.             e.Graphics.DrawString(testString, Me.Font, Brushes.Red, targetRectangle.Left, targetRectangle.Top, sf)
    21.         End Using

    BB

  3. #3
    PowerPoster SJWhiteley's Avatar
    Join Date
    Feb 2009
    Location
    South of the Mason-Dixon Line
    Posts
    2,256

    Re: Font Scaling

    Is it a fixed string? If so, you could pre-calculate and store the sizes and pick the correct font based on those sizes.

    Alternatively, how many fonts are you realistically going to be creating? I don't believe that creating a dozen fonts in a single routine is going to have a significant impact.

    To reduce the number of fonts that get created, you could do a binary search on font sizes - pick a font size, measure it, too big, too small, and reduce or increase font size (double, halve, split difference, etc). This'll really cut down on the number of fonts that will be created.
    "Ok, my response to that is pending a Google search" - Bucky Katt.
    "There are two types of people in the world: Those who can extrapolate from incomplete data sets." - Unk.
    "Before you can 'think outside the box' you need to understand where the box is."

  4. #4
    PowerPoster boops boops's Avatar
    Join Date
    Nov 2008
    Location
    Holland/France
    Posts
    3,201

    Re: Font Scaling

    Quote Originally Posted by SJWhiteley View Post
    Is it a fixed string? If so, you could pre-calculate and store the sizes and pick the correct font based on those sizes.

    Alternatively, how many fonts are you realistically going to be creating? I don't believe that creating a dozen fonts in a single routine is going to have a significant impact.

    To reduce the number of fonts that get created, you could do a binary search on font sizes - pick a font size, measure it, too big, too small, and reduce or increase font size (double, halve, split difference, etc). This'll really cut down on the number of fonts that will be created.
    Graphics.DrawString uses vector scaling using the glyphs defined in the font. So there is no point in defining multiple fonts. BB

  5. #5

    Thread Starter
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    40,104

    Re: Font Scaling

    The string is as far from fixed as anything could be. However, I think I now have an answer.
    My usual boring signature: Nothing

  6. #6
    PowerPoster boops boops's Avatar
    Join Date
    Nov 2008
    Location
    Holland/France
    Posts
    3,201

    Re: [RESOLVED] Font Scaling

    By the way, if you want slightly nicer results, you could declare a graphics path and then use:

    GraphicsPath.AddString + Graphics.SmoothingMode=HighQuality + Graphics.FillPath.

    This will draw the path with antialiased edges. Graphics.DrawString, on the other hand, doesn't use antialiasing regardless of the SmoothingMode.

    Besides anitaliasing, using a GraphicsPath has a couple of other advantages:

    1. You can scale the path itself with GraphicsPath.Transform instead of scaling the Graphics object. The scaling works from the GraphicsPath origin. I find this easier to conceptualize and it may save having to do Translate operations to get the scaling origin right (I need to check that).

    2. You can get a more accurate fit of the string to the rectangle using GraphicsPath.GetBounds instead of Graphics.MeasureString. It may help to specify a Pen in the AddString method with Pen.MiterLimit property set to 1 (the default is 10); that reduces the empty margin around the characters.

    BB

  7. #7
    PowerPoster SJWhiteley's Avatar
    Join Date
    Feb 2009
    Location
    South of the Mason-Dixon Line
    Posts
    2,256

    Re: Font Scaling

    Quote Originally Posted by boops boops View Post
    Graphics.DrawString uses vector scaling using the glyphs defined in the font. So there is no point in defining multiple fonts. BB
    I believe some fonts have different kerning values and spacing at different point sizes so that they appear visually correct at different sizes.

    Scaling a 18 point font down so that it approximates 10 point will not have the same kerning or spacing as 10 point font.

    For most fonts with small changes in size it probably doesn't matter too much. Depends on how much effort is needed to make it look right, and if anyone will really notice if it isn't.
    "Ok, my response to that is pending a Google search" - Bucky Katt.
    "There are two types of people in the world: Those who can extrapolate from incomplete data sets." - Unk.
    "Before you can 'think outside the box' you need to understand where the box is."

  8. #8

    Thread Starter
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    40,104

    Re: [RESOLVED] Font Scaling

    That's good to know. I have the text working now, and it looks good enough. Interestingly, in the normal use of this program, the user will actually look at this part of the program about once a decade, or so. To put it lightly, the graphics quality does not have to be excessively high.
    My usual boring signature: Nothing

  9. #9

    Thread Starter
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    40,104

    Re: Font Scaling re-opened

    I had written a question here, but as I wrote it, I realized that I had to check one further thing, and solved the problem. Still, there is an addition to BBs code that needs to be added for this thread to be fully useful.

    Note the transforms used in BBs code. What these do is change the transformation matrix that is applied to the whole graphics object. That's risky! It works really well as long as that string is the last thing you are drawing, but I was drawing multiple rectangles in sequence. The transformations applied for that first string were then being applied to the next rectangle, which so utterly transformed the rectangle that it was either a single point, or wasn't even visible. I was fairly sure what the problem was, but the first time I looked at the methods available in the graphics class, I overlooked the one that I felt had to be there: ResetTransform.

    Not seeing that, I tried to use a transform of 0 or 1, but since transformation matrices are additive, this had either no impact, or made the problem worse. I was writing on here to ask how to reset the transformation matrix for a graphics object, and decided to have one more look. That's when I saw the ResetTransform method, which, as a rule, should probably be called after doing any scaling, rotation, or translation transformations on a graphics object unless you know that you will want the same transformations applied to the next element that you are drawing.
    My usual boring signature: Nothing

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width