Skip to content

Instantly share code, notes, and snippets.

@jeffThompson
Last active December 8, 2019 21:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jeffThompson/55a5f5a3b7887fb7c30df5866ee31c33 to your computer and use it in GitHub Desktop.
Save jeffThompson/55a5f5a3b7887fb7c30df5866ee31c33 to your computer and use it in GitHub Desktop.
A word-wrap function for Processing with hyphenation! Returns overall height of resulting text block.
/*
WORD-WRAP (with hyphenation and text-box height)
Jeff Thompson | 2019 | jeffreythompson.org
A word-wrap function for Processing with hyphenation! Great for weirdo text,
URLs, etc. Returns overall height of resulting text block, which is super helpful
for times where you want to add something directly below a bunch of text.
OPTIONS:
+ Indent first line (default 0)
+ Indent all other lines (default 0)
+ Add hyphenation (default false)
+ Specify hyphen character to use (default dash)
*/
// regular-length text
//String s = "The quick brown fox jumps over the lazy dog";
// some very long text
String s = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.";
// very long words that need to be hyphenated
//String s = "The word \"pneumonoultramicroscopicsilicovolcanoconiosis\" is the longest in the English language";
int posX = 50;
int posY = 50;
int textWidth = 300;
PFont font;
void setup() {
size(500,800);
background(255);
font = loadFont("CrimsonText-Italic-48.vlw");
//font = loadFont("CooperHewitt-Heavy-48.vlw");
textAlign(LEFT, TOP);
textFont(font);
textSize(48);
textLeading(48 * 1.3);
// wrapText() command will draw text auto-wrapped to a specified
// width (can also indent lines after the first and can add
// hyphens to very long words, if specified)
// returns the overall height of the text block created
fill(0);
noStroke();
float h = wrapText(s, posX,posY, textWidth);
/*
other example usages...
indent first line by five spaces (at current font size):
float h = wrapText(s, posX,posY, textWidth, 5);
indent all other lines by 4 spaces:
float h = wrapText(s, posX,posY, textWidth, 0,4);
no indent, but hyphenate very long words:
float h = wrapText(s, posX,posY, textWidth, 0,0, true);
indent first line 2 spaces, hyphenate using an n-dash character
float h = wrapText(s, posX,posY, textWidth, 2,0, true, "–");
*/
// show overall height of rendered text
noFill();
stroke(255,150,0, 100);
rect(posX,posY, textWidth,h);
}
// wraps text to a specified width (will flow to any height necessary to fit
// all the text), can optionally indent first/other lines and will hyphenate
// very large words (can also specify a particular hyphen character if desired)
// via: https://stackoverflow.com/a/45614206/1167783
float wrapText(String s, float x, float y, float w, int _indentFirst, int _indentOthers, boolean addHyphen, String hyphenChar) {
// optional: specify the threshold for long words to be hyphenated
// try playing with this if you need to tune your hyphenation
// here four characters past the boundary = add a hyphen please
float hyphenBufferWidth = textWidth(" ") * 4;
// create indent strings from specified number of characters
// via: https://stackoverflow.com/a/2807731/1167783
String indentFirst = new String(new char[_indentFirst]).replace('\0', ' ');
String indentOthers = new String(new char[_indentOthers]).replace('\0', ' ');
// if short enough, just send it back as is
StringBuilder outputLines = new StringBuilder();
if (textWidth(s) <= w) {
outputLines.append(s);
}
// otherwise, split it!
else {
String[] words = s.split(" ");
StringBuilder currentLine = new StringBuilder();
currentLine.append(indentFirst);
for (int i=0; i<words.length; i++) {
String word = words[i];
// check width, if not too long yet then add the current word
// and keep going through the string
float lineWidth = textWidth(currentLine.toString() + " " + word);
if (lineWidth < w) {
currentLine.append(word + " ");
}
// if too long, end current line and start a new one
else {
// if this line is waaayy too long (probably one long word
// or a url, etc) then force a break
if (textWidth(currentLine.toString()) > w + hyphenBufferWidth) {
String[] parts = hyphenate(currentLine.toString(), w, addHyphen, hyphenChar);
outputLines.append(parts[0] + "\n"); // add current line to output
currentLine = new StringBuilder(); // start new line of text
if (g.textAlign == LEFT) currentLine.append(indentOthers); // add indent if specified
currentLine.append(parts[1] + " " + word + " "); // and add remaining words to new line
}
// otherwise, add this line to the output and start
// a new one with the current word
else {
outputLines.append(currentLine.toString() + "\n");
currentLine = new StringBuilder();
if (g.textAlign == LEFT) currentLine.append(indentOthers);
currentLine.append(word + " ");
}
}
}
// when out of words, add the current line
// to the output, adding one last line-break if this
// is a really long word like above
if (currentLine.length() > 0) {
if (textWidth(currentLine.toString()) > w + hyphenBufferWidth) {
String[] parts = hyphenate(currentLine.toString(), w, addHyphen, hyphenChar);
while (true) {
outputLines.append(parts[0].trim() + "\n");
if (textWidth(parts[1]) > w + hyphenBufferWidth) {
parts = hyphenate(parts[1], w, addHyphen, hyphenChar);
}
else {
outputLines.append(parts[1]);
break;
}
}
}
else {
outputLines.append(currentLine.toString());
}
}
}
// trim any unwanted newline chars
String out = outputLines.toString().replaceAll("^\n+", "");
//println(out.replace("\n", "\\n"));
// use the usual text() command to draw the string!
if (g.textAlign == LEFT) {
text(out, x, y);
} else if (g.textAlign == CENTER) {
text(out, x+w/2, y);
} else if (g.textAlign == RIGHT) {
text(out, x+w, y);
}
// count linebreaks to determine the overall text box height
int numLinebreaks = countLinebreaks(out);
return g.textSize + (numLinebreaks * g.textLeading) - g.textDescent();
}
float wrapText(String s, float x, float y, float w) {
return wrapText(s, x, y, w, 0, 0, false, "-");
}
float wrapText(String s, float x, float y, float w, int indentFirst) {
return wrapText(s, x, y, w, indentFirst, 0, false, "-");
}
float wrapText(String s, float x, float y, float w, int indentFirst, int indentOthers) {
return wrapText(s, x, y, w, indentFirst, indentOthers, false, "-");
}
float wrapText(String s, float x, float y, float w, int indentFirst, int indentOthers, boolean addHyphen) {
return wrapText(s, x, y, w, indentFirst, indentOthers, addHyphen, "-");
}
// returns the number of linebreaks in a string
int countLinebreaks(String s) {
return s.length() - s.replace("\n", "").length();
}
// splits long strings at a specified width, add hyphens
// if specified (they're left off by default)
String[] hyphenate(String currentLine, float w, boolean addHyphen, String hyphenChar) {
String firstHalf = currentLine;
String secondHalf = "";
for (int i=currentLine.length()-2; i>=0; i-=1) {
firstHalf = currentLine.substring(0, i);
secondHalf = currentLine.substring(i, currentLine.length()-1);
if (textWidth(firstHalf) <= w) {
// if hyphenating, move the last char from the first line to the start
// of the second line before adding the hyphen character
if (addHyphen) {
secondHalf = firstHalf.charAt(firstHalf.length()-1) + secondHalf;
firstHalf = firstHalf.substring(0, firstHalf.length()-1) + hyphenChar;
}
break;
}
}
return new String[] { firstHalf, secondHalf };
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment