6
$\begingroup$

INTRODUCTION AND RELEVANT INFORMATION:

I am a software developer that needs to implement printing in my application. In my application user can choose different paper sizes ( A3, A4, A5 ...) which requires from my application to scale drawing accordingly.

I have managed to solve most of the tasks except adjusting the font height. This is the place where I got stuck.

I have asked for help in StackOveflow but got no satisfying answer. Since all I need is formula for scaling font height, I have decided to ask for help here.

Since this is mathematical site, I will phrase my question in a way that does not require any programming knowledge ( all I ask for is to carefully read the question, since this will be hard for me ). If I need to clarify something or add more info please leave a comment and I will update my post.

PROBLEM:

I have coordinates of the rectangle in which text should be drawn.

Unfortunately, I must pick random value for font before drawing the text inside ( there is no way around it ), instead of calculating proper font size. All I can do now is to calculate the rectangle this text ( with this font height ) will fit in.

Below image shows what I mean:

enter image description here

  1. I have font height of the text in proposed rectangle;
  2. I have (x,y) coordinates of both rectangles ( since my English is unable to precisely describe what I mean please see image below ):

enter image description here

IMPORTANT NOTE:

In my programming framework, y-axis is reversed -> positive values are below x-axis and negative ones above. Please see below picture:

enter image description here

QUESTION:

Can you give me the formula for properly changing current font height so the text can fit into target rectangle?

EDIT:

I have tried to apply formula recommended by member Nikos M. and got very decent results. The earlier problem why his formula malfunctioned was related to the way my programming language performed conversion between integer and real numbers. After correcting this, the output is nearly perfect ( the last letter barely exceeds the limit ). I will keep trying with this approach since it looks very promising, but would welcome other solutions if there are any.

END OF EDIT

EDIT #2:

I have altered the formula from member MvG's answer to this:

$$\text{optimal font size}=\text{guessed font size}\times\sqrt{\frac {\text{desired width}}{\text{computed width}}}$$

There was only one case where text exceeded the limit, in all other cases the behavior was perfect.

END OF EDIT #2

Again, this is my fisrt post so if edit is required/adding of proper tags/anything leave me a comment and I will react accordingly as soon as possible. Thank you for your patience and understanding.

FINAL REMARKS:

I have tried everything but the accepted solution always failed in one or two cases. No matter how much answerer tried, each time failure would happen. I believe that the problem doesn't lie in mathematical part, but is rather related to a faulty API I use. I have consulted experienced engineers and they agreed. Therefore I have officially accepted the answer, since from the mathematical standpoint it does solve the problem. This section is written to warn programers to become misguided that the formula actually solves programming part of the problem too. Thank you everyone for trying to help and for your support. Best regards 'till next time.

$\endgroup$
  • $\begingroup$ so if i understand correctly you already have the needed height-font-size relation for a specific height, since these are linearly related one can use a proportion scheme (e.g rule of three) and derive the new font-size by $f_{new} = f_{old} \times h_{new}/h_{old}$ (ps use appropriate signs to match the machine screen orientation if needed) $\endgroup$ – Nikos M. Jul 5 '14 at 13:44
  • $\begingroup$ @NikosM.: Let me just check if I understood your formula correct : fontSizeNew = fontSizeOld x RectangleNewHeight / rectangleTarget. If this is what you have meant, than you are correct but I have already tried this approach and it has failed :( $\endgroup$ – AlwaysLearningNewStuff Jul 5 '14 at 13:48
  • $\begingroup$ failed why? in see in the question you state that you give a random font-size but know the rectangle that this would take, so you need the inital font-size also? Is this going to fit any text in just one page? you have to fix some parameter and vary the others $\endgroup$ – Nikos M. Jul 5 '14 at 13:51
  • $\begingroup$ @NikosM.: In order to "hide" programming aspect of the problem, I had to omit some info.I am drawing a grid on the paper. I need to fill top left cell with some text. My programming framework demands to have font size already defined, and offers me no way to calculate needed height. All I can do is to "guess" the correct height, and confirm it by calculating the rectangle this text would fit in. If calculated rectangle matches original its fine else I need scaling formula. I do not know why your formula fails, but it does... $\endgroup$ – AlwaysLearningNewStuff Jul 5 '14 at 13:56
  • 1
    $\begingroup$ Let us continue this discussion in chat. $\endgroup$ – Nikos M. Jul 5 '14 at 14:23
3
+50
$\begingroup$

General idea

Assuming that font size is proportional to the height of the rectangle, you have

$$\frac{\text{guessed font size}}{\text{computed height}}= \frac{\text{optimal font size}}{\text{desired height}}$$

So you could compute the optimal font size from this as

$$\text{optimal font size}=\text{desired height}\times \frac{\text{guessed font size}}{\text{computed height}}$$

But there are a lot of fine points to this. If you have pre-wrapped text, you'd likely want to do this for both dimensions independently, then choose the minimum of the font sizes computed in this way to make sure the text fits inside the box in both directions. If your algorithm (re)wraps the text, then the font size will not neccessarily be proportional to the height of the rectangle. Instead, the squared font size will be roughly proportional to the area of the rectangle, so if you compute the rectangle height for fixed width, the resulting height is proportional to the area and you should use this:

$$\frac{(\text{guessed font size})^2} {\text{fixed width}\times\text{computed height}}= \frac{(\text{optimal font size})^2} {\text{fixed width}\times\text{desired height}} \\ \text{optimal font size}=\text{guessed font size}\times\sqrt{\frac {\text{desired height}}{\text{computed height}}}$$

Ugly details

The details of line breaking will make this a rough estimate, though. Other details, like font hinting or integer-only sizes or metrics, could also make the computation imprecise. So you might want to iterate this a few times until you are satisfied with the fit.

You could also consider other iterative approaches, like e.g. bisection, which will take longer in well-behaved cases but will be pretty robust when it comes to corner cases. To do bisection, compute a target font size as above, then double that repeatedly until you get a font size which exceeds the box, and also halve it repeatedly until you get a size which fits well within the box. Then you can iteratively choose a font size between these two current extemes, and depending on whether the box computed for that size fits or not, adjust either boundary until the boundaries are close enough to one another that you can consider them as a solution.

One remark regarding your question: the details about orientation of the axis, or coordinates for all four corners of the rectangle, is pretty irrelevant from a mathematical point of view. The core of the problem is font size vs. rectangle dimensions, the rest is just implementation details.

When widths don't match

The reference you provided indicates that if the DT_WORDBREAK flag is given, the output will fit the desired width (i.e. be that width or smaller) unless there is a single word which exceeds that width. So if you notice that the computed width exceeds the desired width, you can conclude that as single word is too long, and adjust font size based on that word:

$$\text{optimal font size}=\text{desired width}\times \frac{\text{previously chosen font size}}{\text{computed width}}$$

That should make the single word fit, unless rounding or kerning or hinting or whatever is biting you.

If the computed width is smaller, then the algorithm apparently chose to wrap text in more places than the above estimate would have assumed. Which is only reasonable, since the above basically assumed that you can wrap a line anywhere, and even obtain slices which have less height than a line. Anyway, if the width is too small but the height matches, you can simply use padding to center horizontally. If the height is too high now, you can use the formula at the top, for adjusting height. The one without the square root. But if you do this, you choose a smaller font, and suddenly the implementation might break the text in fewer places, so the chosen font size might be too small again.

So you either accept this fact that you might be choosing to small a font at that point, or you bisect as outlined above.

$\endgroup$
  • $\begingroup$ Thank you for offering your solution ( +1 from me ). I am in a mess, but will try out your solution soon and report results. My only concern is for text to fit inside rectangle. I am not looking for 100% precise solution, but rather visually pretty one. As for axis orientation, I didn't know if this was relevant or not so I thought to mention it ( since someone else will invest effort to help me why should I be lazy to type few extra words, right? ). Thanks again! :) $\endgroup$ – AlwaysLearningNewStuff Jul 8 '14 at 18:44
  • $\begingroup$ My pardon for earlier misleading comments. I placed width instead of height under square root, so the result was almost perfect. After correcting the mistake, the text is simply way bigger. Maybe my English is bad or maybe I should provide more info? The function that calculates suggested rectangle does wrap the text even if the original is single-lined. Perhaps this is important? I can also provide other information regarding font ( average character height/width and so on ) so if you need further info feel free to ask. $\endgroup$ – AlwaysLearningNewStuff Jul 8 '14 at 23:00
  • $\begingroup$ One more important thing I have noticed: in your third formula you have assumed both rectangles will have the same width, but that is not correct. That would be the reason why I have failed. Please take into consideration that computed rectangle can have different height and width than the original one. Does this help you in any way? Also see my last edit. Thank you. $\endgroup$ – AlwaysLearningNewStuff Jul 9 '14 at 12:29
  • $\begingroup$ @AlwaysLearningNewStuff: I wrote: “if you compute the rectangle height for fixed width[…]”. Since you did not specify how you computed that rectangle, I had to guess. I must confess though that I cannot see how some code could reasonably rewrap text without using a specified text width. In your edit you only use the width, which would make sense if the height were fixed. Is it? Otherwise you might use the full area, i.e. width × height. $\endgroup$ – MvG Jul 9 '14 at 12:48
  • $\begingroup$ In your edit you only use the width, which would make sense if the height were fixed. Is it? Original rectangle is fixed, but calculated rectangle has no fixed width/height. Since you did not specify how you computed that rectangle, I had to guess. Function that does calculation takes the original rectangle as argument. It then expands original rectangle's width if text is too long or if the text is to high it then expands original rectangle's height. Any case might occur -> both width and height can be expanded, only one of those can be expanded or none ( if text fits ). $\endgroup$ – AlwaysLearningNewStuff Jul 9 '14 at 14:21
0
$\begingroup$

I feel almost like a bad person for suggesting this, and it's not really even a "mathy" answer, but you can always try to derive the font size each time, experimentally.

You can be smart about it too. Pick some font size, and render your text. Too big? Cut it by half and try again. Too small? Double it and try again. Each time, reduce the window of variation. That is, do a binary search to find the optimal text size.

Since font sizes generally vary in integers (I've seen implementations support half integers, and maybe someone somewhere supports more), the algorithm will quickly converge to the right answer. If you're dealing with incremental changes to text, then the solution can be adjusted quickly as well.

If you're feeling especially fancy, I guess you could use any root finder instead to find the optimal solution in a similar manner.

$\endgroup$
  • $\begingroup$ Thank you for trying to help. The similar approach was suggested to me at StackOverflow. I wanted to have a formula so I can dodge this type of solution, that was my plan but it failed... $\endgroup$ – AlwaysLearningNewStuff Jul 13 '14 at 9:17

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.