Adjust text size based on length of text

Hello,

I am coding a game and for this, I tryna write code that sizes the text based on the length of the text. My goal is to scale the text (the bigger the text width / the longer the text, the smaller the text size) that every time the whole string can be seen on the screen.

I’ve written this code so far:

float t = 75;
String s = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. "; //this is only an exapmle for a long string
textSize(t);
println(t);
float w = textWidth(s);

t = sq(w)/sq(s.length());

println(t, w, s.length());

size(1200, 600);
textSize(t);
textAlign(CENTER);
text(s, 0, 0, width, height);

But as you can see, my goal isn’t fulfilled…
Can you guys please help me?

Greetings

1 Like

Hey man, nice job. It certainly looks like you’re mixing up some of the static/dynamic mode type stuff… Let’s just start by putting things nicely in a setup function etc.

Also looks like you’re complicating stuff. Try this?

PFont font;

float currentSize = 75;
String currentText = "Lorem Ipsum.";

void setup()
{
  size(1200, 600);
  
  font = createFont("Times", currentSize);
  textFont(font);
}

void draw()
{
  float currentWidth = textWidth(currentText);
  
  float targetWidth = 1000; // what width to we want the text to be?
  float ratio = targetWidth / currentWidth; // so how much bigger should the text be?
  currentSize *= ratio; // let's make it exactly that big
  
  background(0);
  
  textSize(currentSize);
  textAlign(CENTER, CENTER);
  text(currentText, 0, 0, width, height);
}

void keyReleased()
{
  currentText += str(key);
}

1 Like

Hello,

There are lots of resources (tutorials, examples, references, etc.) here:

Example to play with:

// https://discourse.processing.org/t/adjust-text-size-based-on-length-of-text/32411

float t = 1; //Start with a height of 1 pixel
String s = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. "; //this is only an exapmle for a long string
int lines = 1;

s = s+s; // Double the length of the text
//s = s+s+s;

textSize(t);
println(t);
float w = textWidth(s);

float k = 1;         // k can be the ratio of two variables
                     // k set to 1 initially so it is not zero 
//k = s.length()/s;  // One possible ratio   
//k = 200/w;         // Another ratio using width of text box (below)
//lines = 1;
//t = t*k*lines;     // Sames as 1*k*lines for t=1 initially

println(k, t, w, s.length());

size(600, 400);

textAlign(CENTER);
fill(0);

// Left text box
k = s.length()/w;  // One possible ratio   
lines = 5;         // A factor to increase size 
t = 1*k*lines;
println(k, lines, t);

textSize(t);
text(s, 50, 50, 200, 300); //text box
noFill();
rect(50, 50, 200, 300);    //rect() showing text box borders

//Right text box
k = 200/w;      // One possible ratio textBoxWidth/w  
lines = 5;      // A factor to increase size
t = 1*k*lines;  
println(k, lines, t);

textSize(t);
text(s, 350, 50, 200, 300);

image

References:

:)

1 Like

Thanks for your solution @rapatski .
But now I want to create a half-transparent rectangle which “covers” the text like this:

The height of the rectangle has to be (y-size) as big as the text is for covering the background and gaining better visibility. Any ideas on how I can get this?

It looks like you want a combination of dynamic line wrapping and shrink-to-fit – is that correct?

Because text() does not detect overflow (it just silently fails to display it)

Text that does not fit completely within the rectangle specified will not be drawn to the screen.

one of the ways to do your own fitting is to do your own layout – if the layout is going to exceed the bounds, reduce the size and try again. Repeat until the layout fits, then draw.

Past forum discussions with examples of doing your own text layout:

However this is advanced! If you are a new learner, I really don’t suggest trying this.

Paging, not shrinking:

Instead, consider focusing on limiting your text data and paging with a next button. That is much, much easier to do – check if the String is longer than x characters, and if it is longer, split the string and let the user click to display the second part (or third, et cetera) on a new page.

This also has the advantage of making the text more readable, as the font size isn’t jumping around and long paragraphs don’t become overly tiny.

The height of the rectangle has to be (y-size) as big as the text is for covering the background and gaining better visibility. Any ideas on how I can get this?

Uh… by drawing a half transparent box that is the height (size) of the text? Like, you already have this information?

Thanks for your help! I created my own version of this, but it doesn’t work perfectly at all, especially when the string is very short…

Here is my code now:

String str ="Lorem ipsum.";
int x = 0;
int y = 0;
int w = 400;
int h = 250;

String[] words;
float currentSize = 5;
float bestSize = 5;
float sizeIncrement = 0.5;
float fac;

void setup() {
  fullScreen();
  background(20);
  fill(255);
  
  words = str.split(" ");

  w = width;
  h = height/3;
  
  boolean searching = true;  
  while (searching) {
    if (testFontSize(currentSize)) {
      bestSize = currentSize;
      currentSize += sizeIncrement;
    } else {
      searching = false;
    }
  }
  println("Best size: "+ bestSize);
  
  fac = ceil(textWidth(str)/width);
  h = int(fac*(bestSize*1.5));
 
  textSize(bestSize);
  fill(0);
  textAlign(CENTER);
  text(str, x, y, w, h);
  noStroke();
  fill(150, 150);
  rect(x, y, w, h, 0, 0, 30, 30);
}

void draw() {
  background(20);
    words = str.split(" ");

  w = width;
  h = height/3;
  
  boolean searching = true;  
  while (searching) {
    if (testFontSize(currentSize)) {
      bestSize = currentSize;
      currentSize += sizeIncrement;
    } else {
      searching = false;
    }
  }
  println("Best size: "+ bestSize);
  
  fac = ceil(textWidth(str)/width);
  h = int(fac*(bestSize*1.5));
 
  textSize(bestSize);
  fill(255);
  textAlign(CENTER);
  text(str, x, y, w, h);
  noStroke();
  fill(0, 50);
  rect(x, y, w, h, 0, 0, 30, 30);
}

boolean testFontSize(float s) {
  textSize(s);
  int currentLine = 1;
  int maxLines = floor( h / g.textLeading);
  boolean fitHeight = true;
  int nextWord = 0;

  while (fitHeight) {
    if (currentLine > maxLines) {
      fitHeight = false;
    } else {
      String temp = words[nextWord];
      if (textWidth(temp)>w)
        return false;

      boolean fitWidth = true;
      while (fitWidth) {

        if (textWidth(temp) > w) {
          currentLine++;
          fitWidth = false;
        } else {
          if (nextWord < words.length -1) {
            nextWord++;
            temp += " "+words[nextWord];
          } else
            return true;
        }
      }
    }
  }

  return false;
} 

void keyReleased() {
  str+= key;
}

As you guys can see, it doesn’t adjust the size. Can someone solve my problem?

1 Like

Your code is definitely on the right track. Getting a good approximation without just reusing Processing’s own code (which is also an option!) can be an extremely fiddly problem because it may rely on specifics of how Processing line leading works with defaults / magic values.

Try this:

//  Default leading value matches Processing default text behavior for the default Font.
boolean isTextOverflow(String txt, float x, float y, float w, float h, float tsize) {
  return isTextOverflow(txt, x, y, w, h, tsize, tsize * 0.525);
}
/**
 * Will the text fit in the box at the given text size?
 * @param txt      text string to fit
 * @param x        x coordinate of text box
 * @param y        y coordinate of text box
 * @param w        width of text box
 * @param h        height of text box
 * @param tsize    text size to test for string fit
 * @param leading  vertical space between lines in px
 * @return true if text will overflow
 */
boolean isTextOverflow(String txt, float x, float y, float w, float h, float tsize, float leading) {
  String[] words;
  float xoffset = x;
  float yoffset = y + tsize;
  pushStyle();
  textSize(tsize);
  float spacing = textWidth(" ");
  textLeading(leading);
  words = split(txt, " ");
  // loop through words, move to next line if a word won't fit
  for (String word : words) {
    if (xoffset + textWidth(word) > x + w) {
      xoffset = x;
      yoffset = yoffset + tsize + leading;
      // stopping printing lines beyond the bottom of the box
      if (yoffset > y + h - tsize - leading) {
        popStyle();
        return true;
      }
    }
    ////preview layouts
    //fill(220);
    //text(word, xoffset, yoffset);

    xoffset = xoffset + spacing + textWidth(word);
  }
  popStyle();
  return false;
}

Now, if you want to call it like text(), wrap your while() loop in another function.

/**
 * Display text fit into the box -- if needed, shrink textSize repeatedly until it fits.
 * Does not display if text shrinks to textSize(2) without fitting.
 * @param txt      text string to fit
 * @param x        x coordinate of text box
 * @param y        y coordinate of text box
 * @param w        width of text box
 * @param h        height of text box
 * @param maxTextSize  maximum text size to fit
 */
void textFit(String txt, float x, float y, float w, float h, float maxTextSize) {
  float tsize = maxTextSize;
  while(tsize > 2 && isTextOverflow(txt, x, y, w, h, tsize)) {
    tsize--;
  }
  if(tsize>2) {
    println(tsize);
    pushStyle();
    textSize(tsize);
    text(txt, x, y, w, h);
    popStyle();
  }
}

Now you can just call textFit()! For example, here is a sketch that prints some lorem ipsum text into the full window.

String txt = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. FIN.";

void setup() {
  size(600, 600);
  stroke(0);
  surface.setResizable(true);
}

void draw() {
  background(255);
  fill(0);
  textFit(txt, 0, 0, width, height, 60);
}

The window is resizeable, so drag the corner around and watch the text reflow into the window!

To be clear, this is a solution for:

not:

For that second solution, one way is to try making an altered copy of your overflow detector

boolean isTextOverflow(String txt, float x, float y, float w, float h, float tsize, float leading)

…and instead of returning true / false, returns a value textHeight:

float textHeight(String txt, float x, float y, float w, float h, float tsize, float leading) {
  // ...
}

Note that this works much like isTextOverflow, and returns the total height for the full text block when laid out – it is not the visible height of a single line. @micycle has a great explainer thread on precisely calculating visible line height.