1 /*@z23.c:Galley Printer:ScaleFactor()@****************************************/
2 /*                                                                           */
3 /*  THE LOUT DOCUMENT FORMATTING SYSTEM (VERSION 3.39)                       */
4 /*  COPYRIGHT (C) 1991, 2008 Jeffrey H. Kingston                             */
5 /*                                                                           */
6 /*  Jeffrey H. Kingston (jeff@it.usyd.edu.au)                                */
7 /*  School of Information Technologies                                       */
8 /*  The University of Sydney 2006                                            */
9 /*  AUSTRALIA                                                                */
10 /*                                                                           */
11 /*  This program is free software; you can redistribute it and/or modify     */
12 /*  it under the terms of the GNU General Public License as published by     */
13 /*  the Free Software Foundation; either Version 3, or (at your option)      */
14 /*  any later version.                                                       */
15 /*                                                                           */
16 /*  This program is distributed in the hope that it will be useful,          */
17 /*  but WITHOUT ANY WARRANTY; without even the implied warranty of           */
18 /*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the            */
19 /*  GNU General Public License for more details.                             */
20 /*                                                                           */
21 /*  You should have received a copy of the GNU General Public License        */
22 /*  along with this program; if not, write to the Free Software              */
23 /*  Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA   */
24 /*                                                                           */
25 /*  FILE:         z23.c                                                      */
26 /*  MODULE:       Galley Printer                                             */
27 /*  EXTERNS:      FixAndPrintObject()                                        */
28 /*                                                                           */
29 /*****************************************************************************/
30 #include "externs.h"
31 #define	NO_SUPPRESS	FALSE
32 #define	SUPPRESS	TRUE
33 #define word_equal(x, str)  (is_word(type(x)) && StringEqual(string(x), str))
34 
35 
36 /*****************************************************************************/
37 /*                                                                           */
38 /*  FirstDefiniteLDN(x, link, y, jn, ymk, dim, sp, pg)                       */
39 /*  NextDefiniteWithGapLDN(x, link, y, g, jn, ymk, dim, sp, pg)              */
40 /*                                                                           */
41 /*  Like FirstDefinite and NextDefiniteWithGap but during the scan, if a     */
42 /*  LINK_DEST_NULL is encountered, call FixAndPrintObject on it before       */
43 /*  continuing on to find the next definite object.                          */
44 /*                                                                           */
45 /*****************************************************************************/
46 
47 #define FirstDefiniteLDN(x, link, y, jn, ymk, dim, sp, pg)		\
48 { jn = TRUE;								\
49   for( link = Down(x);  link != x;  link = NextDown(link) )		\
50   { Child(y, link);							\
51     if( type(y) == GAP_OBJ )  jn = jn && join(gap(y));			\
52     else if( type(y)==SPLIT ? SplitIsDefinite(y) : is_definite(type(y)))\
53       break;								\
54     else if( type(y) == LINK_DEST_NULL )				\
55       FixAndPrintObject(y, ymk, 0, 0, dim, sp, pg, 0, &aback, &afwd);	\
56   }									\
57 } /* end FirstDefiniteLDN */
58 
59 #define NextDefiniteWithGapLDN(x, link, y, g, jn, ymk, dim, sp, pg)	\
60 { g = nilobj;  jn = TRUE;						\
61   for( link = NextDown(link);  link != x;  link = NextDown(link) )	\
62   { Child(y, link);							\
63     if( type(y) == GAP_OBJ )  g = y, jn = jn && join(gap(y));		\
64     else if( type(y)==SPLIT ? SplitIsDefinite(y):is_definite(type(y)) )	\
65     {									\
66       debug2(DFS, DD, "  NextDefiniteWithGapLDN at %s %s",		\
67 	Image(type(y)), EchoObject(y));					\
68       assert( g != nilobj, "NextDefiniteWithGap: g == nilobj!" );	\
69       break;								\
70     }									\
71     else if( type(y) == LINK_DEST_NULL )				\
72       FixAndPrintObject(y, ymk, 0, 0, dim, sp, pg, 0, &aback, &afwd);	\
73   }									\
74 } /* end NextDefiniteWithGapLDN */
75 
76 
77 
78 /*****************************************************************************/
79 /*                                                                           */
80 /*  static float ScaleFactor(avail_size, inner_size)                         */
81 /*                                                                           */
82 /*  Return the scale factor for this scaling, or 0 if impossible.            */
83 /*                                                                           */
84 /*****************************************************************************/
85 
ScaleFactor(FULL_LENGTH avail_size,FULL_LENGTH inner_size)86 static float ScaleFactor(FULL_LENGTH avail_size, FULL_LENGTH inner_size)
87 { float scale_factor;
88   scale_factor = avail_size <= 0 ? 0 :
89 		 inner_size <= 0 ? 0 : (float) avail_size / inner_size;
90   return scale_factor;
91 }
92 
93 
94 /*@::FindAdjustIncrement()@***************************************************/
95 /*                                                                           */
96 /*  static FULL_LENGTH FindAdjustIncrement(x, frame_size, dim)               */
97 /*                                                                           */
98 /*  Find the amount by which to increase the width of the subobjects of      */
99 /*  concatenation object x so that it is adjusted to fill size frame_size.   */
100 /*                                                                           */
101 /*****************************************************************************/
102 
FindAdjustIncrement(OBJECT x,FULL_LENGTH frame_size,int dim)103 static FULL_LENGTH FindAdjustIncrement(OBJECT x, FULL_LENGTH frame_size,int dim)
104 { OBJECT y = nilobj, link, prev = nilobj, g;
105   int adjustable_gaps;  BOOLEAN jn;
106   FULL_LENGTH inc, mk, actual_size;
107 
108   debug2(DGP, DD, "FindAdjustIncrement(x, %s, %s)",
109 	EchoLength(frame_size), dimen(dim));
110   FirstDefinite(x, link, prev, jn);
111   if( link != x )
112   { adjustable_gaps = 0;
113     mk = back(prev, dim);
114     NextDefiniteWithGap(x, link, y, g, jn);
115     while( link != x )
116     { if ( mode(gap(g)) == TAB_MODE || units(gap(g)) == AVAIL_UNIT
117 				    || units(gap(g)) == FRAME_UNIT )
118       {	debug0(DGP, DD, "FindAdjustIncrement returning 0 (tab gap)");
119 	return 0;
120       }
121       mk += ActualGap(fwd(prev, dim), back(y, dim), fwd(y, dim), &gap(g),
122 		frame_size, mk);
123       prev = y;
124       adjustable_gaps++;
125       NextDefiniteWithGap(x, link, y, g, jn);
126     }
127     actual_size = mk + fwd(prev, dim);
128     debug2(DGP, DD, "  actual_size = %s, adjustable_gaps = %d",
129 	EchoLength(actual_size), adjustable_gaps);
130     inc = adjustable_gaps==0 ? 0 : (frame_size - actual_size) / adjustable_gaps;
131   }
132   else inc = 0;
133   debug1(DGP, DD, "FindAdjustIncrement returning %s", EchoLength(inc));
134   return inc;
135 } /* end FindAdjustIncrement */
136 
137 
138 /*@::FixAndPrintObject()@*****************************************************/
139 /*                                                                           */
140 /*  OBJECT FixAndPrintObject(x, xmk, xb, xf, dim, suppress, pg, count,       */
141 /*           actual_back, actual_fwd)                                        */
142 /*                                                                           */
143 /*  Fix the absolute position of object x in dimension dim, in such a way    */
144 /*  that the principal mark of x has coordinate xmk, and x has actual size   */
145 /*  (xb, xf), where usually xb >= back(x, dim) and xf >= fwd(x, dim).        */
146 /*                                                                           */
147 /*  Actually, in the case where x includes an object lying on a thread       */
148 /*  leading outside x, the final size of x may be different.  Because        */
149 /*  of this, the procedure sets *actual_back and *actual_fwd to the actual   */
150 /*  size of x upon return.  The caller assumes that x will exactly occupy    */
151 /*  this space (actual_back, actual_fwd).                                    */
152 /*                                                                           */
153 /*  The suppress parameter is true if a temporary suppression of adjustment  */
154 /*  in this direction is in effect (because a neighbouring adjustment has    */
155 /*  already been done).  This is for @HAdjust and @VAdjust, not @PAdjust.    */
156 /*                                                                           */
157 /*  If dim == COLM, the coordinate information is merely stored; but if      */
158 /*  dim == ROWM, it is used to generate PostScript for printing x.           */
159 /*                                                                           */
160 /*  Parameter pg records the height of the current page.  This is used       */
161 /*  to correct for the fact that Lout places its origin at the top left,     */
162 /*  while PostScript places its origin at the bottom left.  This correction  */
163 /*  cannot be made by transforming user space.                               */
164 /*                                                                           */
165 /*  x is child number count of its parent (used by COL_THR and ROW_THR only) */
166 /*                                                                           */
167 /*  FixAndPrintObject ordinarily returns the object passed to it; however    */
168 /*  it occasionally replaces that object with another, and then it is the    */
169 /*  replacement object that is returned.                                     */
170 /*                                                                           */
171 /*****************************************************************************/
172 
FixAndPrintObject(OBJECT x,FULL_LENGTH xmk,FULL_LENGTH xb,FULL_LENGTH xf,int dim,BOOLEAN suppress,FULL_LENGTH pg,int count,FULL_LENGTH * actual_back,FULL_LENGTH * actual_fwd)173 OBJECT FixAndPrintObject(OBJECT x, FULL_LENGTH xmk, FULL_LENGTH xb,
174   FULL_LENGTH xf, int dim, BOOLEAN suppress, FULL_LENGTH pg, int count,
175   FULL_LENGTH *actual_back, FULL_LENGTH *actual_fwd)
176 { OBJECT y = nilobj, link, prev = nilobj, g, z, face, thr, res, uplink;
177   /* OBJECT fixed_thr, tmp; */
178   FULL_LENGTH mk, ymk, frame_size, back_edge, yb, yf, inc, f;
179   FULL_LENGTH aback, afwd;
180   int i; float scale_factor;  BOOLEAN jn;
181   debug8(DGP, DD, "[ FixAndPrintObject(%s %s%s, %s, %s,%s, %s, %s, pg, count)",
182     Image(type(x)),
183     ((type(x) == WORD || type(x) == QWORD) ? string(x) : STR_EMPTY),
184     EchoFilePos(&fpos(x)),
185     EchoLength(xmk), EchoLength(xb), EchoLength(xf),dimen(dim),
186     (suppress == SUPPRESS ? "suppress" : "no_suppress"));
187   debug2(DGP, DD, "  size(x) = %s,%s;  x =",
188     EchoLength(back(x, dim)), EchoLength(fwd(x, dim)));
189   ifdebug(DGP, DD, DebugObject(x));
190   res = x;
191 
192   /*** start and stop debugging
193   if( dim == COLM && is_word(type(x)) &&
194       StringEqual(string(x), AsciiToFull("STARTBUG")) )
195     dbg[DGP].on[DD] = dbg[DGP].on[D] = TRUE;
196   if( dim == COLM && is_word(type(x)) &&
197       StringEqual(string(x), AsciiToFull("STOPBUG")) )
198     dbg[DGP].on[DD] = dbg[DGP].on[D] = FALSE;
199   *** */
200 
201 
202   switch( type(x) )
203   {
204 
205     case CLOSURE:
206     case NULL_CLOS:
207     case PAGE_LABEL:
208     case CROSS:
209     case FORCE_CROSS:
210 
211       *actual_back = xb;  *actual_fwd = xf;
212       break;
213 
214 
215     case START_HVSPAN:
216     case START_HSPAN:
217     case START_VSPAN:
218 
219       CountChild(y, DownDim(x, dim), count);
220       if( type(y) == HSPANNER || type(y) == VSPANNER )
221       {
222         Child(z, Down(y));
223 	Parent(thr, UpDim(x, dim));
224 	save_mark(y) = xmk - back(thr, dim) + back(z, dim);
225 
226         /* do the fix now if the first column is also the last one */
227 	debug2(DGP, DD, "  pre-inc spanner_fixed(y) = %d, spanner_count(y) = %d",
228 	  spanner_fixed(y), spanner_count(y));
229 	if( ++spanner_fixed(y) == spanner_count(y) )
230 	{
231 	  debug6(DGP, DD, "  f+last SPAN: yf = max(%s + %s - %s, %s, %s - %s)",
232 	    EchoLength(xmk), EchoLength(xf), EchoLength(save_mark(y)),
233 	    EchoLength(fwd(z, dim)),
234 	    EchoLength(bfc(constraint(y))), EchoLength(back(z, dim)));
235 	  yf = find_max(xmk + xf - save_mark(y), fwd(z, dim));
236 	  yf = find_max(yf, bfc(constraint(y)) - back(z, dim));
237           z = FixAndPrintObject(z, save_mark(y), back(z, dim), yf, dim,
238 		FALSE, pg, 1, &aback, &afwd);
239 	  spanner_fixed(y) = 0; /* restart for if printed again */
240 	}
241 	*actual_back = back(x, dim); *actual_fwd = fwd(x, dim);
242       }
243       else
244       {
245 	debug6(DGP, DD, "%s alternate FixAndPrintObject(%s, %s, %s, %s, %s, ..)",
246 	  Image(type(x)), Image(type(y)), EchoLength(xmk), EchoLength(xb),
247 	  EchoLength(xf), dimen(dim));
248         y = FixAndPrintObject(y, xmk, xb, xf, dim, suppress, pg, count,
249 	      actual_back, actual_fwd);
250       }
251       break;
252 
253 
254     case HSPAN:
255     case VSPAN:
256 
257       /* do the fix on the last one */
258       if( (dim == COLM) == (type(x) == HSPAN) )
259       {
260         CountChild(y, DownDim(x, dim), count);
261 	assert(type(y) == HSPANNER || type(y) == VSPANNER, "FAPO HSPAN/VSPAN!");
262 	debug2(DGP, DD, "  pre-inc spanner_fixed(y) = %d, spanner_count(y) = %d",
263 	  spanner_fixed(y), spanner_count(y));
264 	if( ++spanner_fixed(y) == spanner_count(y) )
265 	{
266           Child(z, Down(y));
267 	  debug6(DGP, DD, "  last SPAN: yf = max(%s + %s - %s, %s, %s - %s)",
268 	    EchoLength(xmk), EchoLength(xf), EchoLength(save_mark(y)),
269 	    EchoLength(fwd(z, dim)),
270 	    EchoLength(bfc(constraint(y))), EchoLength(back(z, dim)));
271 	  yf = find_max(xmk + xf - save_mark(y), fwd(z, dim));
272 	  yf = find_max(yf, bfc(constraint(y)) - back(z, dim));
273           z = FixAndPrintObject(z, save_mark(y), back(z, dim), yf, dim,
274 		FALSE, pg, 1, &aback, &afwd);
275 	  *actual_back = back(x, dim); *actual_fwd = fwd(x, dim);
276 	  spanner_fixed(y) = 0; /* restart for if printed again */
277 	}
278       }
279       break;
280 
281 
282     case WORD:
283     case QWORD:
284 
285       if( dim == COLM )
286       {
287 	/* save horizontal position for PrintWord below */
288 	word_save_mark(x) = xmk;
289 
290 	/* if first occurrence of this font on this page, notify font */
291 	if( string(x)[0] != '\0' )
292 	{ face = finfo[word_font(x)].original_face;
293 	  if( font_page(face) < font_curr_page )
294 	  { debug3(DFT, DD, "FAPO: x = %s, word_font = %d, face = %s",
295 	      string(x), word_font(x), EchoObject(face));
296 	    FontPageUsed(face);
297 	  }
298 	}
299       }
300       else
301       {
302 	if( string(x)[0] != '\0' )
303 	{ BackEnd->PrintWord(x, word_save_mark(x), pg - xmk);
304 	  /* NB if this word is to be underlined, it will be already enclosed
305 	     in an ACAT by Manifest, and that ACAT will do the underlining */
306 	}
307       }
308       *actual_back = xb;  *actual_fwd = xf;
309       break;
310 
311 
312     case WIDE:
313     case HIGH:
314 
315       CountChild(y, Down(x), count);
316       if( (dim == COLM) == (type(x) == WIDE) )
317       { yf = bfc(constraint(x)) - back(y, dim);
318         y = FixAndPrintObject(y, xmk, back(y,dim), yf, dim, NO_SUPPRESS, pg,
319 	      count, &aback, &afwd);
320         *actual_back = xb;  *actual_fwd = xf;
321       }
322       else
323       {	y = FixAndPrintObject(y, xmk, xb, xf, dim, suppress, pg, count,
324 	      actual_back, actual_fwd);
325       }
326       break;
327 
328 
329     case HSHIFT:
330     case VSHIFT:
331 
332       CountChild(y, Down(x), count);
333       if( (dim == COLM) == (type(x) == HSHIFT) )
334       {
335 	/* work out the size of the shift depending on the units */
336 	f = FindShift(x, y, dim);
337 	ymk = xmk - f;
338 	yb = find_max(0, xb - f);
339 	yf = find_max(0, xf + f);
340 	y = FixAndPrintObject(y, ymk, yb, yf, dim, suppress, pg, count,
341 	      &aback, &afwd);
342 
343 	/* recalculate the size of x as in MinSize */
344 	f = FindShift(x, y, dim);
345 	*actual_back = find_min(MAX_FULL_LENGTH, find_max(0, aback + f));
346 	*actual_fwd  = find_min(MAX_FULL_LENGTH, find_max(0, afwd  - f));
347       }
348       else
349       {	y = FixAndPrintObject(y, xmk, xb, xf, dim, suppress, pg, count,
350 	      actual_back, actual_fwd);
351       }
352       break;
353 
354 
355     case HCONTRACT:
356     case VCONTRACT:
357 
358       CountChild(y, Down(x), count);
359       if( (dim == COLM) == (type(x) == HCONTRACT) )
360       {	y = FixAndPrintObject(y, xmk, back(y,dim), fwd(y,dim), dim,
361 	  NO_SUPPRESS, pg, count, &aback, &afwd);
362         *actual_back = xb;  *actual_fwd = xf;
363       }
364       else
365       {	y = FixAndPrintObject(y, xmk, xb, xf, dim, suppress, pg, count,
366 	      actual_back, actual_fwd);
367       }
368       break;
369 
370 
371     case ONE_COL:
372     case ONE_ROW:
373     case HLIMITED:
374     case VLIMITED:
375     case HEXPAND:
376     case VEXPAND:
377 
378       CountChild(y, Down(x), count);
379       if( (dim == COLM) == (type(x) == ONE_COL || type(x) == HEXPAND) )
380       { y = FixAndPrintObject(y, xmk, xb, xf, dim, NO_SUPPRESS, pg, count,
381 	      &aback, &afwd);
382         *actual_back = xb;  *actual_fwd = xf;
383       }
384       else
385       {	y = FixAndPrintObject(y, xmk, xb, xf, dim, suppress, pg, count,
386 	      actual_back, actual_fwd);
387       }
388       break;
389 
390 
391     case HMIRROR:
392 
393       if( BackEnd->mirror_avail )
394       {
395 	CountChild(y, Down(x), count);
396 	if( dim == COLM )
397 	{
398 	  save_mark(x) = xmk;
399 	  y = FixAndPrintObject(y, 0, back(y, COLM), fwd(y, COLM), dim,
400 	    NO_SUPPRESS, pg, count, &aback, &afwd);
401 	}
402 	else
403 	{ BackEnd->SaveGraphicState(y);
404 	  BackEnd->CoordTranslate(save_mark(x), pg - xmk);
405 	  BackEnd->CoordHMirror();
406 	  y = FixAndPrintObject(y, 0, back(y, ROWM), fwd(y, ROWM), dim,
407 	    NO_SUPPRESS, 0, count, &aback, &afwd);
408 	  BackEnd->RestoreGraphicState();
409 	}
410       }
411       *actual_back = xb;  *actual_fwd = xf;
412       break;
413 
414 
415     case VMIRROR:
416 
417       debug0(DRS, DD, "FixAndPrintObject at VMIRROR");
418       if( BackEnd->mirror_avail )
419       {
420 	CountChild(y, Down(x), count);
421 	if( dim == COLM )
422 	  y = FixAndPrintObject(y, xmk, xb, xf, dim, NO_SUPPRESS, pg, count,
423 		&aback, &afwd);
424 	else
425 	{ BackEnd->SaveGraphicState(y);
426 	  BackEnd->CoordTranslate(0, pg - xmk);
427 	  BackEnd->CoordVMirror();
428 	  y = FixAndPrintObject(y, 0, back(y, ROWM), fwd(y, ROWM), dim,
429 	    NO_SUPPRESS, 0, count, &aback, &afwd);
430 	  BackEnd->RestoreGraphicState();
431 	}
432       }
433       *actual_back = xb;  *actual_fwd = xf;
434       break;
435 
436 
437     case VSCALE:
438 
439       debug0(DRS, DD, "FixAndPrintObject at VSCALE");
440       CountChild(y, Down(x), count);
441       if( BackEnd->scale_avail )
442       {
443 	if( dim == COLM )
444 	  y = FixAndPrintObject(y, xmk, xb, xf, dim, NO_SUPPRESS, pg, count,
445 		&aback, &afwd);
446 	else if( (scale_factor = ScaleFactor(xb+xf, size(y, ROWM))) > 0 )
447 	{ BackEnd->SaveGraphicState(y);
448 	  BackEnd->CoordTranslate(0,
449 	    pg - (xmk - xb + (FULL_LENGTH) (back(y, ROWM) * scale_factor)));
450 	  BackEnd->CoordScale(1.0, scale_factor);
451 	  y = FixAndPrintObject(y, 0, back(y,ROWM), fwd(y,ROWM), dim,
452 	    NO_SUPPRESS, 0, count, &aback, &afwd);
453 	  BackEnd->RestoreGraphicState();
454 	}
455 	else if( !is_word(type(y)) || string(y)[0] != '\0' )
456 	  Error(23, 1, "object deleted (it cannot be scaled vertically)",
457 	    WARN, &fpos(x));
458       }
459       *actual_back = xb;  *actual_fwd = xf;
460       break;
461 
462 
463     case HSCALE:
464 
465       debug0(DRS, DD, "FixAndPrintObject at HSCALE");
466       CountChild(y, Down(x), count);
467       if( BackEnd->scale_avail )
468       {	if( dim == COLM )
469         { save_mark(x) = xmk;
470 	  bc(constraint(x)) = xb;
471 	  fc(constraint(x)) = xf;
472           if( (scale_factor = ScaleFactor(xb+xf, size(y, COLM))) > 0 )
473 	    y = FixAndPrintObject(y, 0, back(y, COLM), fwd(y, COLM), dim,
474 	      NO_SUPPRESS, pg, count, &aback, &afwd);
475           else if( !is_word(type(y)) || string(y)[0] != '\0' )
476 	    Error(23, 2, "object deleted (it cannot be scaled horizontally)",
477 	      WARN, &fpos(y));
478         }
479         else if( (scale_factor =
480 	  ScaleFactor(bc(constraint(x))+fc(constraint(x)),size(y,COLM))) > 0 )
481         { BackEnd->SaveGraphicState(y);
482 	  BackEnd->CoordTranslate(save_mark(x) - bc(constraint(x))
483 	       + (FULL_LENGTH) (back(y, COLM)*scale_factor), 0);
484 	  BackEnd->CoordScale(scale_factor, 1.0);
485           y = FixAndPrintObject(y, xmk, xb, xf, dim, NO_SUPPRESS, pg, count,
486 		&aback, &afwd);
487 	  BackEnd->RestoreGraphicState();
488         }
489       }
490       *actual_back = xb;  *actual_fwd = xf;
491       break;
492 
493 
494     case SCALE:
495 
496       CountChild(y, Down(x), count);
497       if( BackEnd->scale_avail )
498       {
499         if( dim == COLM )
500         { assert( bc(constraint(x)) > 0, "FAPO: horizontal scale factor!" );
501 	  save_mark(x) = xmk;
502 	  yb = xb * SF / bc(constraint(x));
503 	  yf = xf * SF / bc(constraint(x));
504           y = FixAndPrintObject(y, 0, yb, yf, dim, NO_SUPPRESS, pg, count,
505 		&aback, &afwd);
506         }
507         else
508         { assert( fc(constraint(x)) > 0, "FAPO: vertical scale factor!" );
509 	  yb = xb * SF / fc(constraint(x));
510 	  yf = xf * SF / fc(constraint(x));
511 	  BackEnd->SaveGraphicState(y);
512 	  BackEnd->CoordTranslate(save_mark(x), pg - xmk);
513 	  BackEnd->CoordScale( (float)bc(constraint(x))/SF,
514 	    (float)fc(constraint(x))/SF);
515           y = FixAndPrintObject(y, 0, yb, yf, dim, NO_SUPPRESS, 0, count,
516 		&aback, &afwd);
517 	  BackEnd->RestoreGraphicState();
518         }
519       }
520       else if( bc(constraint(x)) == SF && fc(constraint(x)) == SF )
521       {
522 	y = FixAndPrintObject(y, xmk, xb, xf, dim, suppress, pg, count,
523 	      &aback, &afwd);
524       }
525       *actual_back = xb;  *actual_fwd = xf;
526       break;
527 
528 
529     case KERN_SHRINK:
530 
531       CountChild(y, LastDown(x), count);
532       if( dim == COLM )
533       { y = FixAndPrintObject(y, xmk, back(y,dim), fwd(y,dim), dim,
534 	  NO_SUPPRESS, pg, count, &aback, &afwd);
535 	*actual_back = back(x, dim);  *actual_fwd = fwd(x, dim);
536       }
537       else
538       {	y = FixAndPrintObject(y, xmk, xb, xf, dim, suppress, pg, count,
539 	      actual_back, actual_fwd);
540       }
541       break;
542 
543 
544     case BACKGROUND:
545 
546       /* this object has the size of its second child; but its first */
547       /* child gets printed too, in the same space                   */
548       CountChild(y, Down(x), count);
549       y = FixAndPrintObject(y, xmk, xb, xf, dim, suppress, pg, count,
550 	&aback, &afwd);
551       CountChild(y, LastDown(x), count);
552       y = FixAndPrintObject(y, xmk, xb, xf, dim, suppress, pg, count,
553 	&aback, &afwd);
554       *actual_back = back(x, dim);  *actual_fwd = fwd(x, dim);
555       break;
556 
557 
558     case ROTATE:
559 
560       CountChild(y, Down(x), count);
561       if( BackEnd->rotate_avail )
562       {
563         if( dim == COLM )
564         { CONSTRAINT colc, rowc, yc;
565           save_mark(x) = xmk;
566 	  SetConstraint(colc, back(x,COLM), MAX_FULL_LENGTH, fwd(x,COLM));
567 	  SetConstraint(rowc, back(x,ROWM), MAX_FULL_LENGTH, fwd(x,ROWM));
568 	  RotateConstraint(&yc, y, sparec(constraint(x)), &colc, &rowc,COLM);
569 	  y = FixAndPrintObject(y, 0, bc(yc), fc(yc), COLM, NO_SUPPRESS, pg,
570 		count, &aback, &afwd);
571         }
572         else
573         { CONSTRAINT colc, rowc, yc;
574 	  BackEnd->SaveGraphicState(y);
575 	  BackEnd->CoordTranslate(save_mark(x), pg - xmk);
576 	  BackEnd->CoordRotate(sparec(constraint(x)));
577 	  SetConstraint(colc, back(x,COLM), MAX_FULL_LENGTH, fwd(x,COLM));
578 	  SetConstraint(rowc, back(x,ROWM), MAX_FULL_LENGTH, fwd(x,ROWM));
579 	  RotateConstraint(&yc, y, sparec(constraint(x)), &colc, &rowc, ROWM);
580 	  y = FixAndPrintObject(y, 0, bc(yc), fc(yc), ROWM, NO_SUPPRESS, 0,
581 		count, &aback, &afwd);
582 	  BackEnd->RestoreGraphicState();
583         }
584       }
585       else if( sparec(constraint(x)) == 0 )
586 	y = FixAndPrintObject(y,xmk,xb,xf,dim,suppress,pg,count,&aback,&afwd);
587       *actual_back = xb;  *actual_fwd = xf;
588       break;
589 
590 
591     case PLAIN_GRAPHIC:
592 
593       CountChild(y, LastDown(x), count);
594       if( BackEnd->plaingraphic_avail )
595       {
596 	if( dim == COLM )
597 	{
598 	  back(x, dim) = xb;		/* NB state change here */
599 	  fwd(x, dim)  = xf;
600 	  save_mark(x) = xmk - back(x, dim);
601 	  debug2(DGP, DD, "PLAIN_GRAPHIC COLM storing size %s, %s",
602 	    EchoLength(back(x, dim)), EchoLength(fwd(x, dim)));
603           y = FixAndPrintObject(y, xmk, xb, xf, dim, suppress, pg, count,
604 		&aback, &afwd);
605 	}
606 	else
607 	{ OBJECT tmp, pre, post;
608           Child(tmp, Down(x));
609           if( type(tmp) == VCAT )
610           { Child(pre, Down(tmp));
611             Child(post, LastDown(tmp));
612           }
613           else pre = tmp, post = nilobj;
614 	  back(x, dim) = xb;
615 	  fwd(x, dim)  = xf;
616           BackEnd->PrintPlainGraphic(pre, save_mark(x),
617 	    pg - (xmk - back(x, dim)), x);
618           y = FixAndPrintObject(y, xmk, xb, xf, dim, suppress, pg, count,
619 		&aback, &afwd);
620           if( post != nilobj )
621 	    BackEnd->PrintPlainGraphic(post, save_mark(x),
622 	      pg - (xmk - back(x, dim)), x);
623 	}
624       }
625       else
626 	y = FixAndPrintObject(y, xmk,xb,xf,dim,suppress,pg,count,&aback,&afwd);
627       *actual_back = xb;  *actual_fwd = xf;
628       break;
629 
630 
631     case GRAPHIC:
632 
633       CountChild(y, LastDown(x), count);
634       if( BackEnd->graphic_avail )
635       {
636 	if( dim == COLM )
637 	{
638 	  /* if first occurrence of this font on this page, notify font */
639 	  if( font(save_style(x)) > 0 )
640 	  { face = finfo[font(save_style(x))].original_face;
641 	    if( font_page(face) < font_curr_page )  FontPageUsed(face);
642 	  }
643 
644 	  back(x, dim) = xb;  /* NB state change here */
645 	  fwd(x, dim)  = xf;
646 	  debug2(DGP, DD, "GRAPHIC COLM storing size %s, %s",
647 	    EchoLength(back(x, dim)), EchoLength(fwd(x, dim)));
648 	  save_mark(x) = xmk - back(x, COLM);
649           y = FixAndPrintObject(y, xb, xb, xf, dim, NO_SUPPRESS, pg, count,
650 		&aback, &afwd);
651 	}
652 	else
653 	{ OBJECT tmp, pre, post;
654           Child(tmp, Down(x));
655           if( type(tmp) == VCAT )
656           { Child(pre, Down(tmp));
657             Child(post, LastDown(tmp));
658           }
659           else pre = tmp, post = nilobj;
660 	  back(x, dim) = xb;
661 	  fwd(x, dim)  = xf;
662 
663 	  BackEnd->SaveTranslateDefineSave(x, save_mark(x),
664 	    pg - (xmk + fwd(x, ROWM)));
665           BackEnd->PrintGraphicObject(pre);
666           BackEnd->RestoreGraphicState();
667           y = FixAndPrintObject(y, xb, xb, xf, dim, NO_SUPPRESS, xb + xf,
668 		count, &aback, &afwd);
669           if( post != nilobj )  BackEnd->PrintGraphicObject(post);
670           BackEnd->RestoreGraphicState();
671 	}
672       }
673       else
674         y = FixAndPrintObject(y, xmk,xb,xf,dim,suppress,pg,count,&aback,&afwd);
675       *actual_back = xb;  *actual_fwd = xf;
676       break;
677 
678 
679     case LINK_SOURCE:
680     case LINK_DEST:
681     case LINK_DEST_NULL:
682     case LINK_URL:
683 
684       ifdebug(DGP, D,
685 	Child(z, Down(x));
686 	debug7(DGP, D, "[ FixAndPrintObject(%s %s%s, %s, %s, %s, %s, -)",
687 	Image(type(x)),
688 	((type(x)==LINK_DEST || type(x)==LINK_DEST_NULL) ? string(z):STR_EMPTY),
689 	type(x)==LINK_DEST_NULL ? " (indef)" : "",
690 	EchoLength(xmk), EchoLength(xb), EchoLength(xf), dimen(dim));
691       );
692       CountChild(y, LastDown(x), count);
693       if( dim == COLM )
694 	save_mark(x) = xmk;
695       else
696       {	Child(z, Down(x));
697 	switch( type(x) )
698 	{
699 	  case LINK_SOURCE:
700 
701 	    BackEnd->LinkSource(z, save_mark(x) - back(x, COLM),
702 	      (pg - xmk) - xf, save_mark(x) + fwd(x, COLM), (pg - xmk) + xb);
703 	    break;
704 
705 	  case LINK_DEST:
706 	  case LINK_DEST_NULL:
707 
708 	    BackEnd->LinkDest(z, save_mark(x) - back(x, COLM),
709 	      (pg - xmk) - xf, save_mark(x) + fwd(x, COLM), (pg - xmk) + xb);
710 	    break;
711 
712 	  case LINK_URL:
713 
714 	    BackEnd->LinkURL(z, save_mark(x) - back(x, COLM),
715 	      (pg - xmk) - xf, save_mark(x) + fwd(x, COLM), (pg - xmk) + xb);
716 	    break;
717 	}
718       }
719       y = FixAndPrintObject(y, xmk, xb, xf, dim, NO_SUPPRESS, pg, count,
720 	    &aback, &afwd);
721       *actual_back = xb;  *actual_fwd = xf;
722       debug0(DGP, D, "] FixAndPrintObject returning");
723       break;
724 
725 
726     case INCGRAPHIC:
727     case SINCGRAPHIC:
728 
729       CountChild(y, Down(x), count);
730       if( BackEnd->incgraphic_avail )
731       {
732 	if( dim == COLM )
733 	{ save_mark(x) = xmk;
734 	  if( incgraphic_ok(x) )
735 	  { debug2(DGP, DD, "  %s (style %s)",
736 	      EchoObject(x), EchoStyle(&save_style(x)));
737 	    face = finfo[font(save_style(x))].original_face;
738 	    if( font_page(face) < font_curr_page )
739 	    { debug3(DFT, DD, "FAPO-IG: x = %s, font = %d, face = %s",
740 		string(x), font(save_style(x)), EchoObject(face));
741 	      FontPageUsed(face);
742 	    }
743 	  }
744 	}
745 	else if( incgraphic_ok(x) )
746 	  BackEnd->PrintGraphicInclude(x, save_mark(x), pg - xmk);
747       }
748       *actual_back = xb;  *actual_fwd = xf;
749       break;
750 
751 
752     case SPLIT:
753 
754       link = DownDim(x, dim);  CountChild(y, link, count);
755       y = FixAndPrintObject(y, xmk, find_max(back(y, dim), xb),
756 	find_max(fwd(y, dim), xf), dim, suppress, pg, count,
757 	actual_back, actual_fwd);
758       break;
759 
760 
761     case VCAT:
762     case HCAT:
763 
764       if( (type(x) == VCAT) == (dim == ROWM) )
765       {
766 	debug6(DGP, DD, "[ FAPO-CAT %s (%s,%s): xmk %s, xb %s, xf %s",
767 	    Image(type(x)), EchoLength(back(x, dim)), EchoLength(fwd(x, dim)),
768 	    EchoLength(xmk), EchoLength(xb), EchoLength(xf));
769 
770 	FirstDefiniteLDN(x, link, prev, jn, xmk, dim, NO_SUPPRESS, pg);
771 	if( link != x )
772 	{
773 
774 	  /*******************************************************************/
775 	  /*                                                                 */
776 	  /*  handle the special case of a 0rt gap at the beginning (left    */
777 	  /*  justify) by converting it to 0ie but increasing fwd(prev) to   */
778 	  /*  the maximum possible                                           */
779 	  /*                                                                 */
780 	  /*******************************************************************/
781 
782 	  NextDefiniteWithGap(x, link, y, g, jn); /* not LDN since will redo */
783 	  if( link != x && mode(gap(g)) == TAB_MODE &&
784 	      units(gap(g)) == AVAIL_UNIT && width(gap(g)) == 0 )
785 	  {
786 	    debug2(DGP, DD, "  FAPO-CAT converting 0rt (back(x, dim) %s, xb %s)",
787 	      EchoLength(back(x, dim)), EchoLength(xb));
788 	    /* NB state change here */
789 	    fwd(prev, dim) += xb - back(x, dim);
790 	    back(x, dim) = xb;
791 	    mode(gap(g)) = EDGE_MODE;
792 	    units(gap(g)) = FIXED_UNIT;
793 	  }
794 	  FirstDefinite(x, link, prev, jn);  /* not LDN since already done */
795 
796 	  /*******************************************************************/
797 	  /*                                                                 */
798 	  /*  Initialize the following variables:                            */
799 	  /*                                                                 */
800 	  /*  frame_size   the total width actually available                */
801 	  /*                                                                 */
802 	  /*  back_edge    where the first element begins                    */
803 	  /*                                                                 */
804 	  /*  inc          the adjust increment, used when adjusting gaps    */
805 	  /*                                                                 */
806 	  /*  mk           where the mark of prev is to go                   */
807 	  /*                                                                 */
808 	  /*******************************************************************/
809 
810 	  frame_size = back(x, dim) + xf;
811 	  back_edge = xmk - back(x, dim);
812 	  if( adjust_cat(x) && !suppress )
813 	    inc = FindAdjustIncrement(x, frame_size, dim);
814 	  else inc = 0;
815 	  mk = back_edge + back(prev, dim);
816 	  debug4(DGP, DD, "  FAPO-CAT back_edge %s, mk %s, frame %s, inc %s",
817 	    EchoLength(back_edge), EchoLength(mk), EchoLength(frame_size),
818 	    EchoLength(inc));
819 
820 	  /*******************************************************************/
821 	  /*                                                                 */
822 	  /*  Fix each element "prev" in turn along the cat operator         */
823 	  /*                                                                 */
824 	  /*******************************************************************/
825 
826 	  NextDefiniteWithGapLDN(x, link, y, g, jn, mk, dim, NO_SUPPRESS, pg);
827 	  while( link != x )
828 	  {
829 	    if( mode(gap(g)) == TAB_MODE && units(gap(g)) == AVAIL_UNIT &&
830 		width(gap(g))==FR )
831 	    {
832 	      /* object is followed by 1rt gap, give it full space to print */
833 	      debug5(DGP,D,"  FAPO (a) calling FAPO(%s, %s, %s, max(%s, %s))",
834 	        Image(type(prev)), EchoLength(mk), EchoLength(back(prev, dim)),
835 		EchoLength(fwd(prev, dim)), EchoLength(xmk+xf-mk-size(y,dim)));
836 	      prev = FixAndPrintObject(prev, mk, back(prev, dim),
837 		find_max(fwd(prev, dim), xmk+xf-mk - size(y, dim)),
838 		dim, NO_SUPPRESS, pg, count, &aback, &afwd);
839 	    }
840 	    else
841 	    {
842 	      debug5(DGP, DD, "  FAPO-CAT (b) calling FAPO(%s, %s, %s, %s+%s)",
843 	        Image(type(prev)), EchoLength(mk), EchoLength(back(prev, dim)),
844 		EchoLength(fwd(prev, dim)), EchoLength(inc));
845 	      prev = FixAndPrintObject(prev, mk, back(prev, dim),
846 		fwd(prev, dim) + inc, dim, NO_SUPPRESS, pg, count,&aback,&afwd);
847 	    }
848 	    mk += ActualGap(afwd, back(y, dim), fwd(y, dim), &gap(g),
849 		    frame_size, mk - back_edge);
850 	    prev = y;
851 	    NextDefiniteWithGapLDN(x, link, y, g, jn, mk, dim, NO_SUPPRESS, pg);
852 	  }
853 
854 	  /*******************************************************************/
855 	  /*                                                                 */
856 	  /*  At end, fix last element in conformity with "suppress"         */
857 	  /*  and set *actual_back and *actual_fwd.                          */
858 	  /*                                                                 */
859 	  /*******************************************************************/
860 
861 	  if( suppress )
862 	  {
863 	    debug4(DGP, DD, "  FAPO-CAT (c) calling FAPO(%s, %s, %s, %s)",
864 	      Image(type(prev)), EchoLength(mk), EchoLength(back(prev, dim)),
865 	      EchoLength(fwd(prev, dim)));
866 	    prev = FixAndPrintObject(prev, mk, back(prev, dim), fwd(prev, dim),
867 	      dim, NO_SUPPRESS, pg, count, &aback, &afwd);
868 	  }
869 	  else
870 	  {
871 	    debug5(DGP, DD,"  FAPO-CAT (d) calls FAPO(%s, %s, %s, max(%s, %s))",
872 	      Image(type(prev)), EchoLength(mk), EchoLength(back(prev, dim)),
873 	      EchoLength(fwd(prev, dim)), EchoLength(xmk + xf - mk));
874 	    ifdebug(DGP, DD, DebugObject(prev));
875 	    prev = FixAndPrintObject(prev, mk, back(prev,dim),
876 	      find_max(fwd(prev, dim), xmk + xf - mk),
877 	      dim, NO_SUPPRESS, pg, count, &aback, &afwd);
878 	  }
879 	  *actual_back = find_max(back(x, dim), xb);
880 	  *actual_fwd = mk + fwd(prev, dim) - back_edge - *actual_back;
881 	  debugcond4(DGP, DD, type(x) == HCAT,
882 	    "HCAT original (%s, %s) to actual (%s, %s)",
883 	    EchoLength(back(x, dim)), EchoLength(fwd(x, dim)),
884 	    EchoLength(*actual_back), EchoLength(*actual_fwd));
885 	}
886 	else *actual_back = xb, *actual_fwd = xf;
887 	debug0(DGP, DD, "] FAPO-CAT returning.");
888       }
889       else
890       { OBJECT start_group, zlink, m;  BOOLEAN dble_found;
891 	FULL_LENGTH b, f, dlen;
892 	start_group = nilobj;  dble_found = FALSE;  dlen = 0;
893 	debug0(DGP, DD, "  groups beginning.");
894 	FirstDefiniteLDN(x, link, y, jn, xmk, dim, NO_SUPPRESS, pg);
895 	if( link != x )
896 	{
897 	  /* start first group, with or without join */
898 	  b = back(y, dim);
899 	  f = fwd(y, dim);
900 	  m = y;
901 	  start_group = link;
902 	  dble_found = !jn;
903 	  debug4(DGP, DD, "  starting first group %s (%sdbl_found): b %s, f %s",
904 	    Image(type(y)), dble_found ? "" : "not ",
905 	    EchoLength(b), EchoLength(f));
906 
907 	  NextDefiniteWithGapLDN(x, link, y, g, jn, xmk, dim, NO_SUPPRESS, pg);
908 	  while( link != x )
909 	  {
910 	    if( !jn )
911 	    {
912 	      /* finish off and fix the group ending just before g */
913 	      debug2(DGP, DD, "  finishing group: b = %s, f = %s",
914 		EchoLength(b), EchoLength(f));
915 	      m = FixAndPrintObject(m, xmk+b, b, xf-b, dim,
916 		NO_SUPPRESS, pg, count, &aback, &afwd);
917 	      b = back(m, dim);  f = fwd(m, dim);
918 	      for( zlink = start_group;  zlink != link;  zlink=NextDown(zlink) )
919 	      { CountChild(z, zlink, count);
920 		if( !is_definite(type(z)) || z == m )  continue;
921 		z = FixAndPrintObject(z, xmk + b, b, xf - b, dim,
922 		  SUPPRESS, pg, count, &aback, &afwd);
923 		b = find_max(b, back(z, dim));  f = find_max(f, fwd(z, dim));
924 	      }
925 	      dlen = find_max(dlen, b + f);
926 	      dble_found = TRUE;
927 	      start_group = nilobj;
928 
929 	      /* start new group */
930 	      b = back(y, dim);
931 	      f = fwd(y, dim);
932 	      m = y;
933 	      start_group = link;
934 	      debug2(DGP, DD, "  starting group: b = %s, f = %s",
935 		EchoLength(b), EchoLength(f));
936 	    }
937 	    else
938 	    {
939 	      /* continue with current group */
940 	      b = find_max(b, back(y, dim));
941 	      f = find_max(f, fwd(y, dim));
942 	      if( fwd(y, dim) > fwd(m, dim) )  m = y;
943 	      debug2(DGP, DD, "  continuing group: b = %s, f = %s",
944 		EchoLength(b), EchoLength(f));
945 	    }
946 
947 	    NextDefiniteWithGapLDN(x, link, y, g, jn, xmk, dim, NO_SUPPRESS, pg);
948 	  }
949 	  assert( start_group != nilobj, "FAPO: final start_group!" );
950 
951 	  if( dble_found || !jn )
952 	  {
953 	    /* finish off and fix this last group */
954 	    debug2(DGP, DD, "  finishing last group: b = %s, f = %s",
955 	        EchoLength(b), EchoLength(f));
956 	    m = FixAndPrintObject(m, xmk+b, b, xf - b, dim, NO_SUPPRESS, pg,
957 		  count, &aback, &afwd);
958 	    b = back(m, dim);  f = fwd(m, dim);
959 	    for( zlink = start_group;  zlink != x;  zlink = NextDown(zlink) )
960 	    { CountChild(z, zlink, count);
961 	      if( !is_definite(type(z)) || z == m )  continue;
962 	      z = FixAndPrintObject(z, xmk+b, b, xf - b, dim, SUPPRESS, pg,
963 		    count, &aback, &afwd);
964 	      b = find_max(b, back(z, dim));  f = find_max(f, fwd(z, dim));
965 	    }
966 	    dlen = find_max(dlen, b + f);
967 	    *actual_back = 0;  *actual_fwd = dlen;
968 	  }
969 	  else
970 	  {
971 	    /* finish off and fix this last and only group */
972 	    debug2(DGP, DD, "  finishing last and only group: b = %s, f = %s",
973 	      EchoLength(b), EchoLength(f));
974 	    m = FixAndPrintObject(m, xmk, xb, xf, dim, NO_SUPPRESS, pg, count,
975 	      &b, &f);
976 	    for( zlink = start_group;  zlink != x;  zlink = NextDown(zlink) )
977 	    { CountChild(z, zlink, count);
978 	      if( !is_definite(type(z)) || z == m )  continue;
979 	      z = FixAndPrintObject(z, xmk, xb, xf, dim, SUPPRESS, pg, count,
980 		    &aback, &afwd);
981 	      b = find_max(b, aback);  f = find_max(f, afwd);
982 	    }
983 	    *actual_back = b;  *actual_fwd = f;
984 	  }
985 	}
986       }
987       break;
988 
989 
990     case ACAT:
991 
992       if( dim == COLM )
993       { BOOLEAN will_adjust, adjusting;
994 	FULL_LENGTH actual_size, adjust_indent, frame_size, back_edge;
995 	FULL_LENGTH adjust_inc, inc = 0, adjust_sofar = 0;
996 	int adjustable_gaps, gaps_sofar = 0;
997 	BOOLEAN underlining; int underline_xstart = 0;
998 	FONT_NUM underline_font = 0;
999 	COLOUR_NUM underline_colour = 0;
1000 	TEXTURE_NUM underline_texture = 0;
1001 	OBJECT urec, last_bad_gap;
1002 
1003 
1004 	/*********************************************************************/
1005 	/*                                                                   */
1006 	/*  The first step is to calculate the following values:             */
1007 	/*                                                                   */
1008 	/*    last_bad_gap     The rightmost tab gap, or nilobj if none;     */
1009 	/*                                                                   */
1010 	/*    adjustable_gaps  the number of gaps suitable for adjustment;   */
1011 	/*                     i.e. to the right of the right-most tab gap,  */
1012 	/*                     and of non-zero width;                        */
1013 	/*                                                                   */
1014 	/*    actual_size      the actual size of x without adjustment.      */
1015 	/*                                                                   */
1016 	/*  These are needed when adjusting the line.                        */
1017 	/*                                                                   */
1018 	/*********************************************************************/
1019 
1020 	FirstDefinite(x, link, y, jn);  /* no LDN since this is initial pass */
1021 	if( link == x )
1022 	{
1023 	  *actual_back = back(x, dim); *actual_fwd = fwd(x, dim);
1024 	  FirstDefiniteLDN(x, link, y, jn, xmk, dim, NO_SUPPRESS, pg);
1025 	  break;  /* no definite children, nothing to print */
1026 	}
1027 
1028 	/*** nasty bug finder
1029 	{ OBJECT ff = y;
1030 	debugcond1(DGP, DD, word_equal(ff, "@ReportLayout"),
1031 	  "FAPO(%s, COLM)", EchoObject(x));
1032 	debugcond1(DGP, DD, word_equal(ff, "@ReportLayout"),
1033 	  "  adjust_cat(x) = %s", bool(adjust_cat(x)));
1034 	}
1035 	***/
1036 
1037 	last_bad_gap = nilobj;
1038 	adjustable_gaps = 0;
1039 	back_edge = xmk - xb;
1040 	mk = back_edge + back(y, dim);
1041 	frame_size = xb + xf;
1042 	prev = y;
1043 	NextDefiniteWithGap(x, link, y, g, jn);  /* no LDN, initial pass */
1044 	while( link != x )
1045 	{
1046 	  save_actual_gap(g) = ActualGap(fwd(prev, dim), back(y, dim),
1047 		fwd(y, dim), &gap(g), frame_size, mk - back_edge);
1048 	  mk += save_actual_gap(g);
1049 	  if( mode(gap(g)) == TAB_MODE || units(gap(g)) == AVAIL_UNIT
1050 				       || units(gap(g)) == FRAME_UNIT )
1051 	  { last_bad_gap = g;
1052 	    adjustable_gaps = 0;
1053 	  }
1054 	  else if( width(gap(g)) > 0 )  adjustable_gaps++;
1055 	  prev = y;
1056 	  NextDefiniteWithGap(x, link, y, g, jn);  /* no LDN, initial pass */
1057 	}
1058 	actual_size = mk + fwd(prev, dim) - back_edge;
1059 
1060 	/*********************************************************************/
1061 	/*                                                                   */
1062 	/*  It is possible that the line cannot be displayed in any          */
1063 	/*  reasonable way, because the paragraph breaker was forced to      */
1064 	/*  produce an overfull line.  In this case, actual_size will        */
1065 	/*  exceed frame_size and there will be no adjustable gaps.  The     */
1066 	/*  solution is to horizontally scale the line if possible, or       */
1067 	/*  else to not print it at all.                                     */
1068 	/*                                                                   */
1069 	/*********************************************************************/
1070 
1071 	if( actual_size > frame_size && adjustable_gaps == 0 )
1072 	{
1073 	  /* can't be fixed by adjustment, so scale the line or delete it */
1074 	  CONSTRAINT c;
1075 	  SetConstraint(c, 0, frame_size, frame_size);
1076 	  fwd(x, dim) = actual_size;
1077 	  debug2(DGP, DD, "  oversize, actual_size = %s, frame_size = %s",
1078 	      EchoLength(actual_size), EchoLength(frame_size));
1079 	  if( BackEnd->scale_avail && InsertScale(x, &c) )
1080 	  {
1081 	    /* the problem has just been fixed, by inserting a @Scale above x */
1082 	    OBJECT prnt;
1083 	    Parent(prnt, Up(x));
1084 	    Child(y, Down(x));
1085 	    if( actual_size - frame_size < 1 * PT )
1086 	    {
1087 	      /* the correction is probably due to roundoff error, and */
1088 	      /* anyway is too small to print an error message about   */
1089 	    }
1090 	    else if( Down(x) == LastDown(x) && is_word(type(y)) )
1091 	    {
1092 	      Error(23, 3, "word %s horizontally scaled by factor %.2f (too wide for %s paragraph)",
1093 		WARN, &fpos(y), string(y), (float) bc(constraint(prnt)) / SF,
1094 		EchoLength(frame_size));
1095 	    }
1096 	    else
1097 	    {
1098 	      Error(23, 4, "%s object horizontally scaled by factor %.2f (too wide for %s paragraph)",
1099 		WARN, &fpos(x), EchoLength(size(x, COLM)),
1100 		(float) bc(constraint(prnt)) / SF, EchoLength(frame_size));
1101 	    }
1102 	    prnt = FixAndPrintObject(prnt, xmk, back(prnt, dim), fwd(prnt, dim), dim,
1103 	      NO_SUPPRESS, pg, count, &aback, &afwd);
1104 	  }
1105 	  else
1106 	  {
1107 	    /* fix the problem by refraining from printing the line */
1108 	    if( size(x, COLM) <= 0 )
1109 	      Error(23, 5, "oversize object has size 0 or less", INTERN, &fpos(x));
1110 	    Child(y, Down(x));
1111 	    if( Down(x) == LastDown(x) && is_word(type(y)) )
1112 	    { Error(23, 6, "word %s deleted (too wide for %s paragraph)",
1113 		WARN, &fpos(y), string(y), EchoLength(frame_size));
1114 	    }
1115 	    else
1116 	    { Error(23, 7, "%s object deleted (too wide for %s paragraph)",
1117 		WARN, &fpos(x), EchoLength(size(x, COLM)), EchoLength(frame_size));
1118 	    }
1119 
1120 	    /* delete and dispose every child of x */
1121 	    while( Down(x) != x )
1122 	      DisposeChild(Down(x));
1123 	    y = MakeWord(WORD, STR_EMPTY, &fpos(x));
1124 	    Link(x, y);
1125 	    back(y, COLM) = fwd(y, COLM) = 0;
1126 	    back(y, ROWM) = fwd(y, ROWM) = 0;
1127 	  }
1128         }
1129         else
1130         {
1131 
1132 	  /********************************************************************/
1133 	  /*                                                                  */
1134 	  /*  The line may be displayed in one of four ways:  centred, right- */
1135 	  /*  justified, adjusted, or none of the above (i.e. left justified).*/
1136 	  /*  An overfull line is always adjusted; otherwise, the line will   */
1137 	  /*  be centred or right justified if the display style asks for it; */
1138 	  /*  otherwise, the line will be adjusted if adjust_cat(x) == TRUE   */
1139 	  /*  (i.e. there is an enclosing @PAdjust) or if the display style is*/
1140 	  /*  DO_ADJUST (meaning that this line is one of a paragraph set in  */
1141 	  /*  the adjust or outdent break style, other than the last line);   */
1142 	  /*  otherwise, the line is left justified.                          */
1143 	  /*                                                                  */
1144 	  /*  The second step is to decide which of these four cases holds    */
1145 	  /*  for this line, and to record the decision in these variables:   */
1146 	  /*                                                                  */
1147 	  /*    will_adjust      TRUE if the adjusted style applies; in this  */
1148 	  /*                     case, variables adjust_inc and inc will be   */
1149 	  /*                     set to the appropriate adjustment value;     */
1150 	  /*                                                                  */
1151 	  /*    adjust_indent    If centring or right justification applies,  */
1152 	  /*                     the indent to produce this, else zero.       */
1153 	  /*                                                                  */
1154 	  /*  NB adjust_inc may be negative, if the optimal paragraph breaker */
1155 	  /*  has chosen to shrink some gaps.                                 */
1156 	  /*                                                                  */
1157 	  /*  NB we are assigning to adjust_cat here; is this a problem?      */
1158 	  /*                                                                  */
1159 	  /********************************************************************/
1160 
1161 	  if( actual_size > frame_size )
1162 	  {
1163 	    assert( adjustable_gaps > 0, "FAPO: adjustable_gaps!" );
1164 	    adjust_cat(x) = TRUE;
1165 	    adjust_indent = 0;
1166 	  }
1167 	  else switch( display_style(save_style(x)) )
1168 	  {
1169 	    case DO_ADJUST:	adjust_cat(x) = TRUE;
1170 				adjust_indent = 0;
1171 				debug1(DSF, D,  "adjust %s", EchoObject(x));
1172 				break;
1173 
1174 	    case DISPLAY_CENTRE: adjust_cat(x) = FALSE;
1175 				adjust_indent = (frame_size - actual_size)/2;
1176 				debug1(DGP, DD, "cdisp %s", EchoObject(x));
1177 				break;
1178 
1179 	    case DISPLAY_RIGHT:	adjust_cat(x) = FALSE;
1180 				adjust_indent = frame_size - actual_size;
1181 				debug1(DGP, DD, "rdisp %s", EchoObject(x));
1182 				debug1(DSF, D,  "rdisp %s", EchoObject(x));
1183 				break;
1184 
1185 	    default:		/* leave adjust_cat(x) as is */
1186 				adjust_indent = 0;
1187 				break;
1188 	  }
1189 
1190 	  debug1(DGP, DD, "ACAT COLM %s", EchoObject(x));
1191 	    /* EchoStyle(&save_style(x)), EchoObject(x)); */
1192 	  debug2(DGP, DD, "frame_size = %s, actual_size = %s",
1193 	    EchoLength(frame_size), EchoLength(actual_size));
1194 
1195 	  if( adjust_cat(x) && adjustable_gaps > 0  )
1196 	  { will_adjust = TRUE;
1197 	    adjust_inc = (frame_size - actual_size) / adjustable_gaps;
1198 	    inc = find_max(adjust_inc, 0);
1199 	    gaps_sofar = 0;	/* number of gaps adjusted so far */
1200 	    adjust_sofar = 0;	/* total width of adjustments so far */
1201 	    debug2(DGP, DD,"will_adjust: adjustable_gaps = %d, adjust_inc = %s",
1202 	      adjustable_gaps, EchoLength(adjust_inc));
1203 	  }
1204 	  else will_adjust = FALSE;
1205 
1206 
1207 	  /********************************************************************/
1208 	  /*                                                                  */
1209 	  /*  The third and final step is to traverse x, fixing subobjects.   */
1210 	  /*  Variable "adjusting" is true while adjusting is occurring.      */
1211 	  /*                                                                  */
1212 	  /********************************************************************/
1213 
1214 	  underlining = FALSE;
1215 	  adjusting = will_adjust && last_bad_gap == nilobj;
1216 	  FirstDefiniteLDN(x, link, y, jn, xmk, dim, NO_SUPPRESS, pg);
1217 	  prev = y;
1218 	  mk = xmk - back(x, dim) + back(y, dim) + adjust_indent;
1219 	  NextDefiniteWithGapLDN(x, link, y, g, jn, mk, dim, NO_SUPPRESS, pg);
1220 	  while( link != x )
1221 	  {
1222 	    /* check for underlining */
1223 	    if( underline(prev) == UNDER_ON )
1224 	    {
1225 	      debug3(DGP, DD, "  FAPO/ACAT1 underline() := %s for %s %s",
1226 	        bool(FALSE), Image(type(prev)), EchoObject(prev));
1227 	      if( !underlining )
1228 	      {
1229 	        /* underlining begins here */
1230 	        underlining = TRUE;
1231 	        debug2(DGP, DD, "underlining begins at %s %s",
1232 		  Image(type(prev)), EchoObject(prev));
1233 		if( is_word(type(prev)) )
1234 		{
1235 	          underline_font = word_font(prev);
1236 		  underline_colour = word_underline_colour(prev);
1237 		  underline_texture = word_texture(prev);
1238 		}
1239 		else
1240 		{
1241 	          underline_font = font(save_style(x));
1242 		  underline_colour = underline_colour(save_style(x));
1243 		  underline_texture = texture(save_style(x));
1244 		}
1245 	        underline_xstart = mk - back(prev, dim);
1246 	      }
1247 	      if( underline(g) == UNDER_OFF )
1248 	      {
1249 	        /* underlining ends here */
1250 	        debug2(DGP, DD, "underlining ends at %s %s",
1251 		  Image(type(prev)), EchoObject(prev));
1252 	        New(urec, UNDER_REC);
1253 	        back(urec, COLM) = underline_xstart;
1254 	        fwd(urec, COLM) = mk + fwd(prev, dim);
1255 	        word_font(urec) = underline_font;
1256 	        word_underline_colour(urec) = underline_colour;
1257 	        word_texture(urec) = underline_texture;
1258 	        underlining = FALSE;
1259 	        Link(NextDown(Up(prev)), urec);
1260 	      }
1261 	    }
1262 
1263 	    /* fix previous definite now we know it is not the last one  */
1264 	    if( adjusting && width(gap(g)) > 0 )
1265 	    { int tmp;
1266 
1267 	      prev = FixAndPrintObject(prev, mk, back(prev, dim),
1268 		fwd(prev, dim) + inc, dim, NO_SUPPRESS, pg, count,&aback,&afwd);
1269 	      gaps_sofar++;
1270 	      tmp = ((frame_size - actual_size) * gaps_sofar) / adjustable_gaps;
1271 	      mk += save_actual_gap(g) + (tmp - adjust_sofar);
1272 	      adjust_sofar = tmp;
1273 	    }
1274 	    else
1275 	    {
1276 	      prev = FixAndPrintObject(prev, mk, back(prev, dim), fwd(prev,dim),
1277 	        dim, NO_SUPPRESS, pg, count, &aback, &afwd);
1278 
1279 	      mk += save_actual_gap(g);
1280 	    }
1281 	    prev = y;
1282 
1283 	    /* commence adjustment if required */
1284 	    if( !adjusting && will_adjust && g == last_bad_gap )
1285 	      adjusting = TRUE;
1286 
1287 	    NextDefiniteWithGapLDN(x, link, y, g, jn, mk, dim, NO_SUPPRESS, pg);
1288 	  }
1289 
1290 	  /* check for underlining */
1291 	  debugcond3(DGP, DD, underline(prev) == UNDER_UNDEF,
1292 	    "  underlining is UNDER_UNDEF in %s: %s %s in para:",
1293 	    EchoFilePos(&fpos(prev)), Image(type(prev)), EchoObject(prev));
1294 	  debugcond1(DGP, DD, underline(prev)==UNDER_UNDEF, "%s",EchoObject(x));
1295 	  assert( underline(prev) == UNDER_OFF || underline(prev) == UNDER_ON,
1296 	    "FixAndPrint: underline(prev)!" );
1297 	  if( underline(prev) == UNDER_ON )
1298 	  {
1299 	    debug3(DGP, DD, "  FAPO/ACAT1 underline() := %s for %s %s",
1300 	      bool(FALSE), Image(type(prev)), EchoObject(prev));
1301 	    if( !underlining )
1302 	    {
1303 	      /* underlining begins here */
1304 	      debug2(DGP, DD, "underlining begins at %s %s",
1305 	        Image(type(prev)), EchoObject(prev));
1306 	      underlining = TRUE;
1307 	      if( is_word(type(prev)) )
1308 	      {
1309 	        underline_font = word_font(prev);
1310 	        underline_colour = word_underline_colour(prev);
1311 	        underline_texture = word_texture(prev);
1312 	      }
1313 	      else
1314 	      {
1315 	        underline_font = font(save_style(x));
1316 	        underline_colour = underline_colour(save_style(x));
1317 	        underline_texture = texture(save_style(x));
1318 	      }
1319 	      underline_xstart = mk - back(prev, dim);
1320 	    }
1321 
1322 	    /* underlining must end here */
1323 	    debug2(DGP, DD, "underlining ends at %s %s",
1324 	      Image(type(prev)), EchoObject(prev));
1325 	    New(urec, UNDER_REC);
1326 	    back(urec, COLM) = underline_xstart;
1327 	    fwd(urec, COLM) = mk + fwd(prev, dim);
1328 	    word_font(urec) = underline_font;
1329 	    word_underline_colour(urec) = underline_colour;
1330 	    word_texture(urec) = underline_texture;
1331 	    underlining = FALSE;
1332 	    Link(NextDown(Up(prev)), urec);
1333 	  }
1334 
1335 	  /* fix the last definite subobject, prev, which must exist */
1336 	  prev = FixAndPrintObject(prev, mk, back(prev, dim),
1337 	    frame_size - (mk - xmk) - back(x, dim),
1338 	    dim, NO_SUPPRESS, pg, count, &aback, &afwd);
1339 
1340         }
1341       }
1342       else
1343       {
1344 	debug1(DGP, DD, "ACAT ROWM %s", EchoObject(x));
1345 	for( link = Down(x);  link != x;  link = NextDown(link) )
1346 	{ Child(y, link);
1347 	  if( !is_definite(type(y)) && type(y) != LINK_DEST_NULL )
1348 	  {
1349 	    if( type(y) == UNDER_REC )   /* generate an underline now */
1350 	    { BackEnd->PrintUnderline(word_font(y),word_underline_colour(y),
1351 		word_texture(y), back(y, COLM), fwd(y, COLM), pg - xmk);
1352 	      link = PrevDown(link);     /* remove all trace of underlining */
1353 	      DisposeChild(Up(y));       /* in case we print this again */
1354 	    }
1355 	    continue;
1356 	  }
1357 	  y = FixAndPrintObject(y, xmk, xb, xf, dim, NO_SUPPRESS, pg, count,
1358 		&aback, &afwd);
1359 	}
1360       }
1361       *actual_back = xb;  *actual_fwd = xf;
1362       break;
1363 
1364 
1365     case COL_THR:
1366     case ROW_THR:
1367 
1368       assert( (type(x) == COL_THR) == (dim == COLM), "FixAndPrintObject: thr!" );
1369       for( link = Down(x), uplink = Up(x), i = 1;
1370 	link != x && uplink != x && i < count;
1371 	link = NextDown(link), uplink = NextUp(uplink), i++ );
1372       assert( link != x && uplink != x, "FixAndPrintObject: link or uplink!" );
1373       CountChild(y, link, count);
1374       debug7(DGP, DD, "  fapo of %s (%s,%s) child %d %s (%s,%s)",
1375 	Image(type(x)),
1376 	EchoLength(back(x, dim)), EchoLength(fwd(x, dim)),
1377 	i, Image(type(y)), EchoLength(back(y, dim)), EchoLength(fwd(y, dim)));
1378 
1379       /* This line seems to have been an optimization.  It won't
1380        * work if we are inside a running header, since subsequent
1381        * passes will have forgotten the thread.  JeffK 13/11/02
1382       MoveLink(uplink, link, CHILD);  DeleteLink(link);
1383        */
1384 
1385       assert( type(y) != GAP_OBJ, "FAPO: THR!");
1386 
1387       if( thr_state(x) != FINALSIZE )
1388       {	back(x, dim) = xb;  fwd(x, dim) = xf;
1389 	thr_state(x) = FINALSIZE;
1390       }
1391 
1392       y = FixAndPrintObject(y, xmk, back(x, dim), fwd(x, dim), dim,
1393 	NO_SUPPRESS, pg, count, &aback, &afwd);
1394       *actual_back = xb;  *actual_fwd = xf;
1395       /* if( Up(x) == x )  Dispose(x); */
1396       break;
1397 
1398       /* convert everyone to FIXED_COL_THR or FIXED_ROW_THR as appropriate */
1399       /* *** old code
1400       if( thr_state(x) == FINALSIZE )
1401 	debug1(DGP, DD, "thr_state(%d)", (int) x);
1402       assert(thr_state(x) != FINALSIZE, "FAPO/COL_THR: thr_state(x)!");
1403       ifdebug(DGP, DD,
1404 	link = Down(x);
1405 	uplink = Up(x);
1406 	while( link != x && uplink != x )
1407 	{
1408 	  Parent(tmp, uplink);
1409 	  debug1(DGP, DD, "parnt: %s", EchoObject(tmp));
1410 	  Child(tmp, link);
1411 	  debug1(DGP, DD, "child: %s", EchoObject(tmp));
1412 	  link = NextDown(link);
1413 	  uplink = NextUp(uplink);
1414 	}
1415 	while( uplink != x )
1416 	{ Parent(tmp, uplink);
1417 	  debug1(DGP, DD, "extra parnt: %s", EchoObject(tmp));
1418 	  uplink = NextUp(uplink);
1419 	}
1420 	while( link != x )
1421 	{ Child(tmp, link);
1422 	  debug1(DGP, DD, "extra child: %s", EchoObject(tmp));
1423 	  link = NextDown(link);
1424 	}
1425       )
1426       i = 1;  res = nilobj;
1427       while( Down(x) != x && Up(x) != x )
1428       {
1429 	New(fixed_thr, type(x) == COL_THR ? FIXED_COL_THR : FIXED_ROW_THR);
1430 	MoveLink(Up(x), fixed_thr, CHILD);
1431 	MoveLink(Down(x), fixed_thr, PARENT);
1432 	back(fixed_thr, dim) = xb;
1433 	fwd(fixed_thr, dim) = xf;
1434 	if( count == i )
1435 	  res = fixed_thr;
1436 	i++;
1437       }
1438       if( Up(x) != x || Down(x) != x )
1439       {
1440 	debug2(DGP, DD, "links problem at %s %d:", Image(type(x)), (int) x);
1441 	if( Up(x) != x )
1442 	{
1443 	  Parent(tmp, Up(x));
1444 	  debug1(DGP, DD, "first parent is %s", EchoObject(tmp));
1445 	}
1446 	if( Down(x) != x )
1447 	{
1448 	  Child(tmp, Down(x));
1449 	  debug1(DGP, DD, "first child is %s", EchoObject(tmp));
1450 	}
1451       }
1452       assert( Up(x) == x && Down(x) == x, "FAPO/COL_THR: x links!" );
1453       Dispose(x);
1454       assert(res != nilobj, "FixAndPrintObject: COL_THR res!");
1455       x = res;
1456       *** */
1457       /* NB NO BREAK! */
1458 
1459 
1460     /* ***
1461     case FIXED_COL_THR:
1462     case FIXED_ROW_THR:
1463 
1464       assert( (type(x) == FIXED_COL_THR) == (dim == COLM),
1465 	"FixAndPrintObject: fixed_thr!" );
1466       CountChild(y, Down(x), count);
1467       y = FixAndPrintObject(y, xmk, back(x, dim), fwd(x, dim), dim,
1468 	NO_SUPPRESS, pg, count, &aback, &afwd);
1469       *actual_back = back(x, dim);  *actual_fwd = fwd(x, dim);
1470       break;
1471       *** */
1472 
1473 
1474     case BEGIN_HEADER:
1475     case END_HEADER:
1476     case SET_HEADER:
1477     case CLEAR_HEADER:
1478 
1479       if( dim == COLM )
1480         Error(23, 8, "%s symbol ignored (out of place)", WARN, &fpos(x),
1481 	  Image(type(x)));
1482       break;
1483 
1484 
1485     default:
1486 
1487       assert1(FALSE, "FixAndPrintObject:", Image(type(x)));
1488       break;
1489 
1490 
1491   } /* end switch */
1492   debug2(DGP, DD, "] FixAndPrintObject returning (actual %s,%s).",
1493 	EchoLength(*actual_back), EchoLength(*actual_fwd));
1494   return res;
1495 } /* end FixAndPrintObject */
1496