You've got a book to make!

You work at a virtual, old-fashioned printing press. To help you arrange your monospace letters faster, you decide to make the shortest program possible to help you. Given a string of text and a page size, generate and output each page of the text.

Examples

For example, with a page width of 14, height 7, and some text, here is your book:

14, 7, "Fruits make an extremely good snack due to their utterly scrumptious sweetness. They are also very healthy for you."
+------------+
| Fruits     |
| make an    |
| extremely  |
| good snack |
| due to     |
+------------+

+------------+
| their      |
| utterly    |
| scrumptio- |
| -us        |
| sweetness. |
+------------+

+------------+
| They are   |
| also very  |
| healthy    |
| for you.   |
|            |
+------------+


Here's the basic page setup:

|---i.e.14---|
+------------+ -
| Xxxxxxxxxx | |
| Xxxxxxxxxx | i.
| Xxxxxxxxxx | e.
| Xxxxxxxxxx | 7
| Xxxxxxxxxx | |
+------------+ -


A few things

1. There is a one-space margin between the page edges and the text.
2. The width and height include the box edges, if that wasn't clear.
3. Wraparound only occurs if a word can't fit on a single line.
4. The program needs to be able to output as many pages as needed, and only that many.
5. Your program has to support any page size, not just 14 by 7.
6. This is code golf, so the smallest solution in bytes (any language) wins.
7. Don't ruin the fun. Standard loopholes are obviously not allowed.

Oh, and, by the way:

+------------+
| May the    |
| best book  |
| publisher  |
| win. Good  |
| Luck!      |
+------------+

+------------+
| Best       |
| program    |
| so far:    |
| Charcoal   |
|            |
+------------+

• @rod Oops, fixed. – i.. Dec 13 '17 at 13:05
• If you mean for example 14 that’s e.g. – FrownyFrog Dec 13 '17 at 13:10
• Closely related. – Martin Ender Dec 13 '17 at 13:20
• Do we need to handle 'multi-cuts' on a single word? (like scru-/-mpt-/-ious) – Arnauld Dec 13 '17 at 15:28
• @Arnauld If a word's long enough, then yes. – i.. Dec 14 '17 at 0:21

Python 2, 306304283 279 bytes

def f(w,h,s):
b=[];w-=4;h-=2;H='+-'+'-'*w+'-+';x=l='';s=s.split()
while s:
W=s.pop(0)
if W[w:]:W,s=W[:w-1]+'-',['-'+W[w-1:]]+s
if len(l+W)<=w-(l>x):l+=' '*(l>x)+W
else:b+=[l];l=W
b+=[l]+[x]*h
while any(b):print'\n'.join([H]+['| %-*s |'%(w,b.pop(0))for _ in' '*h]+[H,x,x])


Try it online!

• I think you can use L=len to save 2 bytes total... – Chas Brown Dec 14 '17 at 7:58
• FYI (not sure if it needs addressing) the test case I suggested (7, 3, "Three Is The Magic Number!") yields a leading empty page. – Jonathan Allan Dec 17 '17 at 18:27
• @JonathanAllan Thanks, fixed it :) – TFeld Dec 18 '17 at 8:32

Charcoal, 105 83 bytes

ＮθＮηＭ⁺η²↑Ｆ⪪Ｓ «Ｗ›Ｌι⁻θ⁴«⊞υ⁺…ι⁻θ⁵-≔⁺-✂ι⁻θ⁵Ｌι¹ι»⊞υι»Ｆυ«¿‹Ｌι±ⅈ «Ｍ⁻⁻⁴ⅈθ¹¿¬﹪ⅉ⊕η«↙↙Ｂθη↘→»»ι


Try it online! Link is to verbose version of code the deverbosifier can't handle the ⅈ and ⅉ nilary operators. If leading blank lines were acceptable, I could get it down to 76 bytes:

≔⁻Ｎ⁴θＭθ→ＮηＦ⪪Ｓ «Ｗ›Ｌιθ«⊞υ⁺…ι⊖θ-≔⁺-✂ι⊖θＬι¹ι»⊞υι»Ｆυ«¿‹⁺ⅈＬιθ «Ｆ¬﹪ⅉ⊕η«⸿↙↙Ｂ⁺θ⁴η»⸿»ι


Explanation:

ＮθＮη


Input the width into q and the height into h.

Ｍ⁺η²↑


Move to a position that will trigger the first box to be drawn, but without generating a top margin.

Ｆ⪪Ｓ «


Loop over all the words in the input string.

Ｗ›Ｌι⁻θ⁴«


Repeat while a word is too wide to fit into a box.

⊞υ⁺…ι⁻θ⁵-


Push as much of the word that will fit plus a trailing hyphen.

≔⁺-✂ι⁻θ⁵Ｌι¹ι»


Prefix a hyphen to the rest of the word.

⊞υι»


Push the rest of the word.

Ｆυ«


Loop over all the hyphenated words.

¿‹Ｌι±ⅈ


Check whether the word fits on the current line.

 «


If so then print a space.

Ｍ⁻⁻⁴ⅈθ¹


Otherwise move to the start of the next line.

¿¬﹪ⅉ⊕η


Check whether we need a new box.

«↙↙Ｂθη↘→»»


If so then draw the box.

ι


Finally, print the word.

Perl 5, 203 182 + 1 (-a) = 183 bytes

$t=($\=-3+shift@F)-2;$h=shift@F}{say$_='+'.'-'x$\.'-+';map{$_="";{$_.=shift@F;s/.{$t}\K..+/-/&&unshift@F,-$&;$_.=$";y///c+length$F[0]<$\&&redo}printf"| %-$\s|
",$_}3..$h;say;@F&&redo


Try it online!

SOGL V0.12, 92 bytes

Aē:b⁾\&?Q¶oQ}cI@*¼|a31žO■
.⁾:0EHC┌*¼+Q,θK;{⁴D@Κ+lc<‽Xd■Flc<‽ø;c⁾{Kh+;}D┌+■d┌Κ}}}■beb⁾%-⁾{ø■}


Try it Here!

• Fails with lower widths – Jonathan Allan Dec 17 '17 at 17:52
• @JonathanAllan oh. :/ fixing this to allow multi-cuts is probably gonna be a lot of bytes.. – dzaima Dec 17 '17 at 17:58

JavaScript (ES8), 242 bytes

Thanks to @Tvde1 for reporting a bug

(s,w,h)=>s.split .map(g=s=>l=(l+s)[W=w-5]?s[l&&A(l),l='',w-4]?g('-'+s.slice(W),A(s.slice(0,W)+'-')):s:l?l+' '+s:s,n=o=l='',h-=2,b=+${'-'.repeat(w-2)}+ ,A=s=>o+=(!n|n++%h?'':b+ +b)+|${s.padEnd(w-3)}|
)&&(g=s=>A(s)&&n%h?g(''):b+o+b)(l)


Try it online!

Commented

(s, w, h) =>                        // given s = string, w = width, h = height
s.split                         // get all words by splitting the string on spaces
.map(g = s =>                     // for each word s:
l = (l + s)[W = w - 5] ?        //   if the word is too long for the current line:
s[ l && A(l),                 //     append the line (if not empty)
l = '',                    //     clear the line
w - 4 ] ?                  //     if the word itself doesn't fit:
g(                          //       do a recursive call with:
'-' + s.slice(W),         //         a hyphen + the next part
A(s.slice(0, W) + '-')    //         and append the current part + a hyphen
)                           //       end of recursive call
:                             //     else:
s                           //       initialize a new line with this word
:                               //   else:
l ?                           //     if the current line is not empty:
l + ' ' + s                 //       append a space + the word
:                             //     else:
s,                          //       initialize a new line with this word
n = o = l = '',                 //   n = line counter, o = output, l = line
h -= 2,                         //   adjust h
b = +${'-'.repeat(w - 2)}+\n, // b = border + linefeed A = s => // A = function that updates the output o: o += ( // append to o: !n | n++ % h ? // if we haven't reached an end of page: '' // an empty string : // else: b + \n + b // bottom border + linefeed + top border ) + // followed by |${s.padEnd(w - 3)}|\n     //       left border + padded text + right border
) &&                              // end of map()
(g = s =>                         // g = recursive function taking s:
A(s) &&                         //   append s
n % h ?                         //   if we haven't reached an end of page:
g('')                         //     go on with an empty line
:                               //   else:
b + o + b                     //     return top border + output + bottom border
)(l)                              // initial call to g() with the last pending line

• Try your example text with 12 and 7, some lines/words are reversed. – Tvde1 May 22 '19 at 13:02
• @Tvde1 Thanks for reporting this. It should now be fixed. – Arnauld May 22 '19 at 14:22

Jelly, 93 bytes

1,⁴Ṭị⁾+-W,


A full program taking three arguments (text, width, height) which prints the pages.

Try it online! N.B. Too inefficient to run the example from the OP within the 60 second limit.

(97 bytes if the blank line between pages is actually a requirement)

How?

1,⁴Ṭị⁾+-W, - Link 1, make header & footer: no arguments
⁴         - program's 2nd argument, width
1           - literal one
,          - pair = [1,width]
Ṭ        - untruth = [1,0,0,...,0,0,1] (a 1 at index 1 and index width; 0 elsewhere)
⁾+-    - literal list of characters = "+-"
ị       - index into (1-based & modular) = "+--...--+"
W   - wrap = ["+---...--+']
 - use as both arguments of the dyad:
,  - pair = [["+---...--+'],["+---...--+']]

- Main link. This is long so splitting it up into parts like so:
ṣ⁶µ "A" µ€ "B" µ "C" µÐḟ "D"
ṣ⁶                           - split 1st argument (text) at spaces
µ "A" µ€                   - for €ach resulting word do "A" (get hyphenated parts)
"B"               - do "B" (all ways to partition those joining with spaces)
µ "C"         -   do "C" (any parts are too long)
"D" - do "D" (format the resulting list into the page-format)

"A" = ḊṖs⁴_6¤j⁾--;@Ḣ;Ṫḟ0s⁴_©4¤ - Hyphenate: list, word     e.g. "Something"
Ḋ                        - dequeue                        "omething"
Ṗ                       - pop                            "omethin"
⁴                     -   program's 2nd argument  e.g. 9  (width)
6                   -   literal six                  6
_                    -   subtract                     3
s                      - split into chunks             ["ome","thi","n"]
⁾--              - literal list of characters     "--"
j                 - join                           "ome--thi--n"
;@            - concatenate (sw@p arguments)   "Some--thi--n"
Ṫ         - tail (word)                    "g"
;          - concatenate                    "Some--thi--ng"
ḟ0       - filter out zeros (tail yields 0 for words of length 1)
⁴      -   program's 2nd argument      9
4   -   literal four                4
_     -   subtract                    5
©    -   copy to register & yield    5
s       - split into chunks             ["Some-","-thi-","-ng"]

"B" = ẎŒṖK€€ - Line arrangements: list of lists of hyphen-parts / single words
Ẏ      - flatten by one (make a list of words and hyphen-parts
- e.g. [["Not"],["hyph-","-ena-","-ted"]] -> ["Not","hyph-","-ena-","-ted"]
ŒṖ    - partition e.g. [1,2,3]->[[[1],[2],[3]],[[1],[2,3]],[[1,2],[3]],[[1,2,3]]]
K€€ - join with spaces for €ach for €ach e.g. ["one","two"]->"one two"

"C" = L€>®Ẹ - Any part too long?: one of the list of lines from "B"
L€    - length of €ach
®  - recall from the register (width minus 4)
>   - greater than (vectorises) - 1 if so 0 if not
Ẹ - any truthy? (1 if any too long)

"D" = Ṫ;€⁶x®¤ḣ€®s⁵_2¤Zz⁶x®¤j@€€⁾| ,U¤j@€¢ẎY - Format as pages: list of valid arrangements
Ṫ                                     - tail (last valid partition is greediest)
⁶                                  -   literal space character
®                                -   recall from register (width minus 4)
x                                 -   repeat elements
;€                                   - concatenate to €ach
®                            - recall from register (width minus 4)
ḣ€                             - head €ach to index
⁵                          -   program's 3rd argument, height
2                        -   literal two
_                         -   subtract
Z                      - transpose

⁶                    -   literal space character
®                  -   recall from register (width minus 4)
x                   -   repeat elements

z                     - transpose with filler (repeated spaces)
⁾|<space>    -   literal list of characters = "| "
U        -   upend = " |"
,         -   pair = ["| "," |"]
j@€€             - join for €ach for €ach (sw@p arguments)
j@€    - join for €ach (sw@p arguments)
Ẏ  - flatten by one
Y - join with line feeds
- implicit print


PHP, 299 bytes

for($a=explode(" ",$argv[3]);$y|$e=strlen($d=$a[+$i++]);$x||print"|",$x|$e<$w?$e<$w-$x?$x+=$e+print" $d":$i-=!$x=!$y+=print str_pad("",$w-$x)." |
":$y+=print" ".substr($d,0,$w-2)."- | ".!$a[--$i]="-".substr($d,$w-2),$y>$argv[2]-2&&$y=!print"$t ")$y||$y=print$t=str_pad("+",2+$w=$argv[1]-3,"-")."+
";


Run with php -nr '<code>' <width> <height> '<text>'` or or try it online.