%!PS

% shamlessy derived from https://robrich.org/archive/2006/10/30/postscriptissoelegant.aspx

/debug_flag true  def

/block_element_count 4 def %number of items in a text block
/block_index 0 def %current block to use
/block_count 0 def %number of blocks
/block_max 10 def %maximum number of blocks
/block_storage block_max block_element_count mul array def

/newline { % - newline -
	%add block compatible newline
	currentpoint exch pop line_height sub
	bottom_margin lt {
		%move to next box 
		(move to next box) ==
		block_index 1 add block_count ge {
			(move to next page ) ==
			showpage
			} if
		block_index 1 add block_count mod set_current_block
		} {
		%move one line down
		(move one line down) ==
		left_margin currentpoint exch pop line_height sub moveto
		} ifelse
	} def

/too_far { % (string) too_far -> bool 
	%Is the word too long for the space on the line?
	dup
	stringwidth pop
	currentpoint pop
	add right_margin
	gt
	} bind def

/show_word { % (string) show_word -> 
	%Show a word, line wrap if can't fit
	too_far { newline } if show
	} def


/show_text { %(string) showtext -> 
	%show text line wrap if needed
	% in a loop, break by spaces, and print each word.
	% If there isn't a space left, be done. 
	{
		( ) search
		exch
		show_word
		not
		{
			exit
			} if
		show
		}
	loop
	} def

/set_current_block { %block_index set_current_block ->
	%set margin state for the givin block index
	/block_index exch def
	/top_margin     block_storage block_index block_element_count mul 0 add get def
	/right_margin   block_storage block_index block_element_count mul 1 add get def
	/bottom_margin  block_storage block_index block_element_count mul 2 add get def
	/left_margin    block_storage block_index block_element_count mul 3 add get def
	left_margin top_margin line_height sub moveto
	debug_flag {
		currentpoint
		left_margin top_margin moveto
		right_margin top_margin lineto
		right_margin bottom_margin lineto
		left_margin bottom_margin lineto
		closepath
		stroke
		moveto
		} if
	} def

/add_text_block { %left top width height text_block -> 
	%add new text block
	4 2 roll % width height left top
	%top
	block_storage exch block_count block_element_count mul 0 add exch put
	%left
	block_storage exch block_count block_element_count mul 3 add exch put
	%bottom
	block_storage exch block_count block_element_count mul 2 add exch
		block_storage block_count block_element_count mul 0 add get exch sub put
	%right
	block_storage exch block_count block_element_count mul 1 add exch
		block_storage block_count block_element_count mul 3 add get exch add put


	/block_count block_count 1 add def
	debug_flag {
		(====block_storage) = 
		block_storage ==
		(====block_count) = 
		block_count =
		(====E) = 
		} if

	} def


/r_sep { % (r_text) r_sep -
	% finish with a line to right justified r_text
	% use setdash to get dotted lines
	% existing text............r_text
	dup
	stringwidth pop
	right_margin exch sub % x point of end of line
	currentpoint exch pop % y point of end of line
	lineto currentpoint stroke moveto
	show
	} def




/use_font { %font size -> %set font and line_height
	/line_height exch def
	findfont line_height scalefont setfont
	} def

/inch {
	72 mul
	} def

/Times 28 use_font

0.5 inch 
11 inch 1 inch sub
3 inch
3 inch
add_text_block

4.5  inch 
11 inch 1 inch sub
3 inch
3 inch
add_text_block

%0.5 inch
%11 inch 5 inch sub
%3 inch
%3 inch
%add_text_block

0 set_current_block

(On Windows, I ran it though Ghostscript on huge documents with no difference. On Unix though, it dogged the Ghostscript engine to death. (My awk script ran like a dream though.) ) show_text

/Times 10 use_font

(A google board post led me to the most elegant auto-paging and auto-line-wrap function. The auto-paging is easy: when you're moving to the next line, if you'll go off the page, do the show page and new page routines first. The line wrap a bit more complex though. It splits by spaces, compares the length of the first word it got back against the right margin minus the current point, and decides if it needs to line wrap before printing this word. Then it displays the space. The elegant part? Argument 2 of the split function's return value is "the rest". Called in a loop, it cycles through all the words on a page, line-breaking as needed.) show_text

%5.5 inch
%11 inch 1 inch sub
%2 inch
%9 inch
%text_block

(Ok, most people don't code in PostScript. Granted, like SQL, it's one of those languages that's usually coded by a program written in another language. But it's so incredibly elegant!) show_text

newline
(Fun with process descriptors) show_text (20) r_sep
(Other Interesting things) show_text (43) r_sep
(Wow so much sep) show_text (52) r_sep

showpage
