Last active
December 8, 2019 21:01
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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