/*@z23.c:Galley Printer:ScaleFactor()@****************************************/ /* */ /* THE LOUT DOCUMENT FORMATTING SYSTEM (VERSION 3.21) */ /* COPYRIGHT (C) 1991, 2000 Jeffrey H. Kingston */ /* */ /* Jeffrey H. Kingston (jeff@cs.usyd.edu.au) */ /* Basser Department of Computer Science */ /* The University of Sydney 2006 */ /* AUSTRALIA */ /* */ /* This program is free software; you can redistribute it and/or modify */ /* it under the terms of the GNU General Public License as published by */ /* the Free Software Foundation; either Version 2, or (at your option) */ /* any later version. */ /* */ /* This program is distributed in the hope that it will be useful, */ /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ /* GNU General Public License for more details. */ /* */ /* You should have received a copy of the GNU General Public License */ /* along with this program; if not, write to the Free Software */ /* Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307 USA */ /* */ /* FILE: z23.c */ /* MODULE: Galley Printer */ /* EXTERNS: FixAndPrintObject() */ /* */ /*****************************************************************************/ #include "externs.h" #define NO_SUPPRESS FALSE #define SUPPRESS TRUE #define word_equal(x, str) (is_word(type(x)) && StringEqual(string(x), str)) /*****************************************************************************/ /* */ /* static float ScaleFactor(avail_size, inner_size) */ /* */ /* Return the scale factor for this scaling, or 0 if impossible. */ /* */ /*****************************************************************************/ static float ScaleFactor(FULL_LENGTH avail_size, FULL_LENGTH inner_size) { float scale_factor; scale_factor = avail_size <= 0 ? 0 : inner_size <= 0 ? 0 : (float) avail_size / inner_size; return scale_factor; } /*@::FindAdjustIncrement()@***************************************************/ /* */ /* static FULL_LENGTH FindAdjustIncrement(x, frame_size, dim) */ /* */ /* Find the amount by which to increase the width of the subobjects of */ /* concatenation object x so that it is adjusted to fill size frame_size. */ /* */ /*****************************************************************************/ static FULL_LENGTH FindAdjustIncrement(OBJECT x, FULL_LENGTH frame_size,int dim) { OBJECT y, link, prev, g; int adjustable_gaps; BOOLEAN jn; FULL_LENGTH inc, mk, actual_size; debug2(DGP, DD, "FindAdjustIncrement(x, %s, %s)", EchoLength(frame_size), dimen(dim)); FirstDefinite(x, link, prev, jn); if( link != x ) { adjustable_gaps = 0; mk = back(prev, dim); NextDefiniteWithGap(x, link, y, g, jn); while( link != x ) { if ( mode(gap(g)) == TAB_MODE || units(gap(g)) == AVAIL_UNIT || units(gap(g)) == FRAME_UNIT ) { debug0(DGP, DD, "FindAdjustIncrement returning 0 (tab gap)"); return 0; } mk += ActualGap(fwd(prev, dim), back(y, dim), fwd(y, dim), &gap(g), frame_size, mk); prev = y; adjustable_gaps++; NextDefiniteWithGap(x, link, y, g, jn); } actual_size = mk + fwd(prev, dim); debug2(DGP, DD, " actual_size = %s, adjustable_gaps = %d", EchoLength(actual_size), adjustable_gaps); inc = adjustable_gaps==0 ? 0 : (frame_size - actual_size) / adjustable_gaps; } else inc = 0; debug1(DGP, DD, "FindAdjustIncrement returning %s", EchoLength(inc)); return inc; } /* end FindAdjustIncrement */ /*@::FixAndPrintObject()@*****************************************************/ /* */ /* FixAndPrintObject(x, xmk, xb, xf, dim, suppress, pg, count) */ /* */ /* Fix the absolute position of object x in dimension dim, in such a way */ /* that the principal mark of x has coordinate xmk, and x has actual size */ /* (xb, xf), where xb >= back(x, dim) and xf >= fwd(x, dim). */ /* */ /* Actually, in the case where x includes an object lying on a thread */ /* leading outside x, the final size of x may be different. Because */ /* of this, the procedure sets back(x, dim) and fwd(x, dim) to the actual */ /* size of x upon return. The caller assumes that x will exactly occupy */ /* this space back(x, dim), fwd(x, dim). */ /* */ /* The suppress parameter is true if a temporary suppression of adjustment */ /* in this direction is in effect (because a neighbouring adjustment has */ /* already been done). This is for @HAdjust and @VAdjust, not @PAdjust. */ /* */ /* If dim == COLM, the coordinate information is merely stored; but if */ /* dim == ROWM, it is used to generate PostScript for printing x. */ /* */ /* Parameter pg records the height of the current page. This is used */ /* to correct for the fact that Lout places its origin at the top left, */ /* while PostScript places its origin at the bottom left. This correction */ /* cannot be made by transforming user space. */ /* */ /* x is child number count of its parent (used by COL_THR and ROW_THR only) */ /* */ /*****************************************************************************/ void FixAndPrintObject(OBJECT x, FULL_LENGTH xmk, FULL_LENGTH xb, FULL_LENGTH xf, int dim, BOOLEAN suppress, FULL_LENGTH pg, int count) { OBJECT y, link, prev, g, uplink, z, face, thr; FULL_LENGTH mk, ymk, frame_size, back_edge, yb, yf, inc, f; int i; float scale_factor; BOOLEAN jn; debug8(DGP, DD, "[ FixAndPrintObject(%s %s, %s, %s,%s, %s, %s, pg, %d)", Image(type(x)), ((type(x) == WORD || type(x) == QWORD) ? string(x) : STR_EMPTY), EchoLength(xmk), EchoLength(xb), EchoLength(xf),dimen(dim), (suppress == SUPPRESS ? "suppress" : "no_suppress"), count); debug2(DGP, DD, " size(x) = %s,%s; x =", EchoLength(back(x, dim)), EchoLength(fwd(x, dim))); ifdebug(DGP, DD, DebugObject(x)); /*** start and stop debugging if( dim == COLM && is_word(type(x)) && StringEqual(string(x), AsciiToFull("STARTBUG")) ) dbg[DGP].on[DD] = dbg[DGP].on[D] = TRUE; if( dim == COLM && is_word(type(x)) && StringEqual(string(x), AsciiToFull("STOPBUG")) ) dbg[DGP].on[DD] = dbg[DGP].on[D] = FALSE; *** */ switch( type(x) ) { case CLOSURE: case NULL_CLOS: case PAGE_LABEL: case CROSS: case FORCE_CROSS: back(x, dim) = xb; fwd(x, dim) = xf; break; case START_HVSPAN: case START_HSPAN: case START_VSPAN: CountChild(y, DownDim(x, dim), count); if( type(y) == HSPANNER || type(y) == VSPANNER ) { Child(z, Down(y)); Parent(thr, UpDim(x, dim)); save_mark(y) = xmk - back(thr, dim) + back(z, dim); /* do the fix now if the first column is also the last one */ if( ++spanner_fixed(y) == spanner_count(y) ) { debug6(DGP, DD, " f+last SPAN: yf = max(%s + %s - %s, %s, %s - %s)", EchoLength(xmk), EchoLength(xf), EchoLength(save_mark(y)), EchoLength(fwd(z, dim)), EchoLength(bfc(constraint(y))), EchoLength(back(z, dim))); yf = find_max(xmk + xf - save_mark(y), fwd(z, dim)); yf = find_max(yf, bfc(constraint(y)) - back(z, dim)); FixAndPrintObject(z, save_mark(y), back(z, dim), yf, dim,FALSE,pg,1); } } else { FixAndPrintObject(y, xmk, xb, xf, dim, suppress, pg, count); back(x, dim) = back(y, dim); fwd(x, dim) = fwd(y, dim); } break; case HSPAN: case VSPAN: /* do the fix on the last one */ CountChild(y, DownDim(x, dim), count); if( type(y) == HSPANNER || type(y) == VSPANNER ) { if( ++spanner_fixed(y) == spanner_count(y) ) { Child(z, Down(y)); debug6(DGP, DD, " last SPAN: yf = max(%s + %s - %s, %s, %s - %s)", EchoLength(xmk), EchoLength(xf), EchoLength(save_mark(y)), EchoLength(fwd(z, dim)), EchoLength(bfc(constraint(y))), EchoLength(back(z, dim))); yf = find_max(xmk + xf - save_mark(y), fwd(z, dim)); yf = find_max(yf, bfc(constraint(y)) - back(z, dim)); FixAndPrintObject(z, save_mark(y), back(z, dim), yf, dim,FALSE,pg,1); } } break; case WORD: case QWORD: if( dim == COLM ) { /* save horizontal position for PrintWord below */ word_save_mark(x) = xmk; /* if first occurrence of this font on this page, notify font */ if( string(x)[0] != '\0' ) { face = finfo[word_font(x)].original_font; if( font_page(face) < font_curr_page ) { debug3(DFT, DD, "FAPO: x = %s, word_font = %d, face = %s", string(x), word_font(x), EchoObject(face)); FontPageUsed(face); } } } else { debug4(DGP, D, /* underline(x) == UNDER_UNDEF, */ " FAPO %s %s %s (underline = %s)", EchoFilePos(&fpos(x)), Image(type(x)), string(x), underline(x) == UNDER_OFF ? "UNDER_OFF" : underline(x) == UNDER_ON ? "UNDER_ON" : "UNDER_UNDEF"); assert( underline(x) == UNDER_OFF || underline(x) == UNDER_ON, "FixAndPrintObject: underline(x)!" ); if( string(x)[0] != '\0' ) { PrintWord(x, word_save_mark(x), pg - xmk); if( underline(x) == UNDER_ON ) { FontWordSize(x); /* to restore fwd(x, COLM) */ PrintUnderline(word_font(x), word_save_mark(x), word_save_mark(x) + fwd(x, COLM), pg - xmk); } } } back(x, dim) = xb; fwd(x, dim) = xf; break; case WIDE: case HIGH: CountChild(y, Down(x), count); if( (dim == COLM) == (type(x) == WIDE) ) { yf = bfc(constraint(x)) - back(y, dim); FixAndPrintObject(y, xmk, back(y,dim), yf, dim, NO_SUPPRESS, pg,count); back(x, dim) = xb; fwd(x, dim) = xf; } else { FixAndPrintObject(y, xmk, xb, xf, dim, suppress, pg, count); back(x, dim) = back(y, dim); fwd(x, dim) = fwd(y, dim); } break; case HSHIFT: case VSHIFT: CountChild(y, Down(x), count); if( (dim == COLM) == (type(x) == HSHIFT) ) { /* work out the size of the shift depending on the units */ f = FindShift(x, y, dim); ymk = xmk - f; yb = find_max(0, xb - f); yf = find_max(0, xf + f); FixAndPrintObject(y, ymk, yb, yf, dim, suppress, pg, count); /* recalculate the size of x as in MinSize */ f = FindShift(x, y, dim); back(x, dim) = find_min(MAX_FULL_LENGTH, find_max(0, back(y, dim) + f)); fwd(x, dim) = find_min(MAX_FULL_LENGTH, find_max(0, fwd(y, dim) - f)); } else { FixAndPrintObject(y, xmk, xb, xf, dim, suppress, pg, count); back(x, dim) = back(y, dim); fwd(x, dim) = fwd(y, dim); } break; case HCONTRACT: case VCONTRACT: CountChild(y, Down(x), count); if( (dim == COLM) == (type(x) == HCONTRACT) ) { FixAndPrintObject(y, xmk, back(y,dim), fwd(y,dim), dim, NO_SUPPRESS, pg, count); back(x, dim) = xb; fwd(x, dim) = xf; } else { FixAndPrintObject(y, xmk, xb, xf, dim, suppress, pg, count); back(x, dim) = back(y, dim); fwd(x, dim) = fwd(y, dim); } break; case ONE_COL: case ONE_ROW: case HLIMITED: case VLIMITED: case HEXPAND: case VEXPAND: CountChild(y, Down(x), count); if( (dim == COLM) == (type(x) == ONE_COL || type(x) == HEXPAND) ) { FixAndPrintObject(y, xmk, xb, xf, dim, NO_SUPPRESS, pg, count); back(x, dim) = xb; fwd(x, dim) = xf; } else { FixAndPrintObject(y, xmk, xb, xf, dim, suppress, pg, count); back(x, dim) = back(y, dim); fwd(x, dim) = fwd(y, dim); } break; case VSCALE: debug0(DRS, DD, "FixAndPrintObject at VSCALE"); CountChild(y, Down(x), count); switch( BackEnd ) { case PLAINTEXT: break; case POSTSCRIPT: case PDF: if( dim == COLM ) FixAndPrintObject(y, xmk, xb, xf, dim, NO_SUPPRESS, pg, count); else if( (scale_factor = ScaleFactor(xb+xf, size(y, ROWM))) > 0 ) { SaveGraphicState(y); CoordTranslate(0, pg-(xmk-xb+(FULL_LENGTH) (back(y,ROWM)*scale_factor))); CoordScale(1.0, scale_factor); FixAndPrintObject(y, 0, back(y,ROWM), fwd(y,ROWM), dim, NO_SUPPRESS, 0, count); RestoreGraphicState(); } else if( !is_word(type(y)) || string(y)[0] != '\0' ) Error(23, 1, "object deleted (it cannot be scaled vertically)", WARN, &fpos(x)); break; } back(x, dim) = xb; fwd(x, dim) = xf; break; case HSCALE: debug0(DRS, DD, "FixAndPrintObject at HSCALE"); CountChild(y, Down(x), count); switch( BackEnd ) { case PLAINTEXT: break; case POSTSCRIPT: case PDF: if( dim == COLM ) { save_mark(x) = xmk; bc(constraint(x)) = xb; fc(constraint(x)) = xf; if( (scale_factor = ScaleFactor(xb+xf, size(y, COLM))) > 0 ) FixAndPrintObject(y, 0, back(y, COLM), fwd(y, COLM), dim, NO_SUPPRESS, pg, count); else if( !is_word(type(y)) || string(y)[0] != '\0' ) Error(23, 2, "object deleted (it cannot be scaled horizontally)", WARN, &fpos(y)); } else if( (scale_factor = ScaleFactor(bc(constraint(x))+fc(constraint(x)),size(y,COLM))) > 0 ) { SaveGraphicState(y); CoordTranslate(save_mark(x) - bc(constraint(x)) + (FULL_LENGTH) (back(y, COLM)*scale_factor), 0); CoordScale(scale_factor, 1.0); FixAndPrintObject(y, xmk, xb, xf, dim, NO_SUPPRESS, pg, count); RestoreGraphicState(); } break; } back(x, dim) = xb; fwd(x, dim) = xf; break; case SCALE: CountChild(y, Down(x), count); switch( BackEnd ) { case PLAINTEXT: /* printable only if scale factor is one */ if( bc(constraint(x)) == SF && fc(constraint(x)) == SF ) { FixAndPrintObject(y, xmk, xb, xf, dim, suppress, pg, count); } break; case POSTSCRIPT: case PDF: if( dim == COLM ) { assert( bc(constraint(x)) > 0, "FAPO: horizontal scale factor!" ); save_mark(x) = xmk; yb = xb * SF / bc(constraint(x)); yf = xf * SF / bc(constraint(x)); FixAndPrintObject(y, 0, yb, yf, dim, NO_SUPPRESS, pg, count); } else { assert( fc(constraint(x)) > 0, "FAPO: vertical scale factor!" ); yb = xb * SF / fc(constraint(x)); yf = xf * SF / fc(constraint(x)); SaveGraphicState(y); CoordTranslate(save_mark(x), pg - xmk); CoordScale( (float) bc(constraint(x))/SF, (float) fc(constraint(x))/SF); FixAndPrintObject(y, 0, yb, yf, dim, NO_SUPPRESS, 0, count); RestoreGraphicState(); } break; } back(x, dim) = xb; fwd(x, dim) = xf; break; case KERN_SHRINK: CountChild(y, LastDown(x), count); if( dim == COLM ) { FixAndPrintObject(y, xmk, back(y,dim), fwd(y,dim), dim, NO_SUPPRESS, pg, count); } else { FixAndPrintObject(y, xmk, xb, xf, dim, suppress, pg, count); back(x, dim) = back(y, dim); fwd(x, dim) = fwd(y, dim); } break; case BACKGROUND: /* this object has the size of its second child; but its first */ /* child gets printed too, in the same space */ CountChild(y, Down(x), count); FixAndPrintObject(y, xmk, xb, xf, dim, suppress, pg, count); CountChild(y, LastDown(x), count); FixAndPrintObject(y, xmk, xb, xf, dim, suppress, pg, count); break; case ROTATE: CountChild(y, Down(x), count); switch( BackEnd ) { case PLAINTEXT: /* printable only if angle is zero */ if( sparec(constraint(x)) == 0 ) { FixAndPrintObject(y, xmk, xb, xf, dim, suppress, pg, count); } break; case POSTSCRIPT: case PDF: if( dim == COLM ) { CONSTRAINT colc, rowc, yc; save_mark(x) = xmk; SetConstraint(colc, back(x,COLM), MAX_FULL_LENGTH, fwd(x,COLM)); SetConstraint(rowc, back(x,ROWM), MAX_FULL_LENGTH, fwd(x,ROWM)); RotateConstraint(&yc, y, sparec(constraint(x)), &colc, &rowc,COLM); FixAndPrintObject(y, 0, bc(yc), fc(yc), COLM,NO_SUPPRESS,pg,count); } else { CONSTRAINT colc, rowc, yc; SaveGraphicState(y); CoordTranslate(save_mark(x), pg - xmk); CoordRotate(sparec(constraint(x))); SetConstraint(colc, back(x,COLM), MAX_FULL_LENGTH, fwd(x,COLM)); SetConstraint(rowc, back(x,ROWM), MAX_FULL_LENGTH, fwd(x,ROWM)); RotateConstraint(&yc, y, sparec(constraint(x)), &colc, &rowc, ROWM); FixAndPrintObject(y, 0, bc(yc), fc(yc), ROWM, NO_SUPPRESS,0,count); RestoreGraphicState(); } break; } back(x, dim) = xb; fwd(x, dim) = xf; break; case PLAIN_GRAPHIC: CountChild(y, LastDown(x), count); switch( BackEnd ) { case POSTSCRIPT: case PDF: FixAndPrintObject(y, xmk, xb, xf, dim, suppress, pg, count); break; case PLAINTEXT: if( dim == COLM ) { back(x, dim) = xb; fwd(x, dim) = xf; save_mark(x) = xmk - back(x, dim); debug2(DGP, DD, "PLAIN_GRAPHIC COLM storing size %s, %s", EchoLength(back(x, dim)), EchoLength(fwd(x, dim))); FixAndPrintObject(y, xmk, xb, xf, dim, suppress, pg, count); } else { OBJECT tmp, pre, post; Child(tmp, Down(x)); if( type(tmp) == VCAT ) { Child(pre, Down(tmp)); Child(post, LastDown(tmp)); } else pre = tmp, post = nilobj; back(x, dim) = xb; fwd(x, dim) = xf; PrintPlainGraphicObject(pre, save_mark(x), pg - (xmk - back(x, dim)), x); FixAndPrintObject(y, xmk, xb, xf, dim, suppress, pg, count); if( post != nilobj ) PrintPlainGraphicObject(post, save_mark(x), pg - (xmk - back(x, dim)), x); } break; } /* end switch */ back(x, dim) = xb; fwd(x, dim) = xf; break; case GRAPHIC: CountChild(y, LastDown(x), count); switch( BackEnd ) { case PLAINTEXT: FixAndPrintObject(y, xmk, xb, xf, dim, suppress, pg, count); break; case POSTSCRIPT: case PDF: if( dim == COLM ) { /* if first occurrence of this font on this page, notify font */ if( font(save_style(x)) > 0 ) { face = finfo[font(save_style(x))].original_font; if( font_page(face) < font_curr_page ) FontPageUsed(face); } back(x, dim) = xb; fwd(x, dim) = xf; debug2(DGP, DD, "GRAPHIC COLM storing size %s, %s", EchoLength(back(x, dim)), EchoLength(fwd(x, dim))); save_mark(x) = xmk - back(x, COLM); FixAndPrintObject(y, xb, xb, xf, dim, NO_SUPPRESS, pg, count); } else { OBJECT tmp, pre, post; Child(tmp, Down(x)); if( type(tmp) == VCAT ) { Child(pre, Down(tmp)); Child(post, LastDown(tmp)); } else pre = tmp, post = nilobj; back(x, dim) = xb; fwd(x, dim) = xf; SaveTranslateDefineSave(x, save_mark(x), pg - (xmk + fwd(x, ROWM))); /* *** SaveGraphicState(x); CoordTranslate(save_mark(x), pg - (xmk + fwd(x, ROWM))); debug4(DGP, DD, "GRAPHIC ROWM calling %s,%s %s,%s", EchoLength(back(x, COLM)), EchoLength(fwd(x, COLM)), EchoLength(back(x, ROWM)), EchoLength(fwd(x, ROWM))); DefineGraphicNames(x); SaveGraphicState(x); *** */ PrintGraphicObject(pre); RestoreGraphicState(); FixAndPrintObject(y, xb, xb, xf, dim, NO_SUPPRESS, xb + xf, count); if( post != nilobj ) PrintGraphicObject(post); RestoreGraphicState(); } break; } /* end switch */ back(x, dim) = xb; fwd(x, dim) = xf; break; case INCGRAPHIC: case SINCGRAPHIC: CountChild(y, Down(x), count); switch( BackEnd ) { case PLAINTEXT: break; case POSTSCRIPT: case PDF: if( dim == COLM ) { save_mark(x) = xmk; if( incgraphic_ok(x) ) { debug2(DGP, DD, " %s (style %s)", EchoObject(x), EchoStyle(&save_style(x))); face = finfo[font(save_style(x))].original_font; if( font_page(face) < font_curr_page ) { debug3(DFT, DD, "FAPO-IG: x = %s, font = %d, face = %s", string(x), font(save_style(x)), EchoObject(face)); FontPageUsed(face); } } } else if( incgraphic_ok(x) ) PrintGraphicInclude(x, save_mark(x), pg - xmk); break; } back(x, dim) = xb; fwd(x, dim) = xf; break; case SPLIT: link = DownDim(x, dim); CountChild(y, link, count); FixAndPrintObject(y, xmk, xb, xf, dim, suppress, pg, count); back(x, dim) = back(y, dim); fwd(x, dim) = fwd(y, dim); break; case VCAT: case HCAT: if( (type(x) == VCAT) == (dim == ROWM) ) { debug6(DGP, DD, "[ FAPO-CAT %s (%s,%s): xmk %s, xb %s, xf %s", Image(type(x)), EchoLength(back(x, dim)), EchoLength(fwd(x, dim)), EchoLength(xmk), EchoLength(xb), EchoLength(xf)); FirstDefinite(x, link, prev, jn); if( link != x ) { /* handle the special case of a 0rt gap at the beginning (left */ /* justify) by converting it to 0ie but increasing fwd(prev) */ /* to the max. possible */ NextDefiniteWithGap(x, link, y, g, jn); if( link != x && mode(gap(g)) == TAB_MODE && units(gap(g)) == AVAIL_UNIT && width(gap(g)) == 0 ) { debug2(DGP, DD, " FAPO-CAT converting 0rt (back(x, dim) %s, xb %s)", EchoLength(back(x, dim)), EchoLength(xb)); fwd(prev, dim) += xb - back(x, dim); back(x, dim) = xb; mode(gap(g)) = EDGE_MODE; units(gap(g)) = FIXED_UNIT; } FirstDefinite(x, link, prev, jn); /* the frame size is the total width actually available */ frame_size = back(x, dim) + xf; /* back_edge is where the first element begins */ back_edge = xmk - back(x, dim); /* inc is the adjust increment, used when adjusting gaps */ if( adjust_cat(x) && !suppress ) inc = FindAdjustIncrement(x, frame_size, dim); else inc = 0; mk = back_edge + back(prev, dim); debug4(DGP, DD, " FAPO-CAT back_edge %s, mk %s, framesize %s, inc %s", EchoLength(back_edge), EchoLength(mk), EchoLength(frame_size), EchoLength(inc)); NextDefiniteWithGap(x, link, y, g, jn); while( link != x ) { if( mode(gap(g)) == TAB_MODE && units(gap(g)) == AVAIL_UNIT && width(gap(g))==FR ) { /* object is followed by 1rt gap, give it full space to print */ debug5(DGP,D," FAPO (a) calling FAPO(%s, %s, %s, max(%s, %s))", Image(type(prev)), EchoLength(mk), EchoLength(back(prev, dim)), EchoLength(fwd(prev, dim)), EchoLength(xmk+xf-mk-size(y,dim))); FixAndPrintObject(prev, mk, back(prev, dim), find_max(fwd(prev, dim), xmk+xf-mk - size(y, dim)), dim, NO_SUPPRESS, pg, count); } else { debug5(DGP, DD, " FAPO-CAT (b) calling FAPO(%s, %s, %s, %s+%s)", Image(type(prev)), EchoLength(mk), EchoLength(back(prev, dim)), EchoLength(fwd(prev, dim)), EchoLength(inc)); FixAndPrintObject(prev, mk, back(prev, dim), fwd(prev, dim) + inc, dim, NO_SUPPRESS, pg, count); } /* NB fwd(prev, dim) may be changed by the call to FAPO */ mk += ActualGap(fwd(prev, dim), back(y, dim), fwd(y, dim), &gap(g), frame_size, mk - back_edge); prev = y; NextDefiniteWithGap(x, link, y, g, jn); } if( suppress ) { debug4(DGP, DD, " FAPO-CAT (c) calling FAPO(%s, %s, %s, %s)", Image(type(prev)), EchoLength(mk), EchoLength(back(prev, dim)), EchoLength(fwd(prev, dim))); FixAndPrintObject(prev, mk, back(prev, dim), fwd(prev, dim), dim, NO_SUPPRESS, pg, count); } else { debug5(DGP, DD," FAPO-CAT (d) calling FAPO(%s, %s, %s, max(%s, %s))", Image(type(prev)), EchoLength(mk), EchoLength(back(prev, dim)), EchoLength(fwd(prev, dim)), EchoLength(xmk + xf - mk)); FixAndPrintObject(prev, mk, back(prev,dim), find_max(fwd(prev, dim), xmk + xf - mk), dim, NO_SUPPRESS, pg, count); } back(x, dim) = find_max(back(x, dim), xb); fwd(x, dim) = mk + fwd(prev, dim) - back_edge - back(x, dim); } else back(x, dim) = xb, fwd(x, dim) = xf; debug0(DGP, DD, "] FAPO-CAT returning."); } else { OBJECT start_group, zlink, m; BOOLEAN dble_found; FULL_LENGTH b, f, dlen; start_group = nilobj; dble_found = FALSE; dlen = 0; debug0(DGP, DD, " groups beginning."); FirstDefinite(x, link, y, jn); if( link != x ) { /* start first group, with or without join */ b = back(y, dim); f = fwd(y, dim); m = y; start_group = link; dble_found = !jn; debug4(DGP, DD, " starting first group %s (%sdbl_found): b = %s, f = %s", Image(type(y)), dble_found ? "" : "not ", EchoLength(b), EchoLength(f)); NextDefiniteWithGap(x, link, y, g, jn); while( link != x ) { if( !jn ) { /* finish off and fix the group ending just before g */ debug2(DGP, DD, " finishing group: b = %s, f = %s", EchoLength(b), EchoLength(f)); FixAndPrintObject(m, xmk+b, b, xf-b, dim, NO_SUPPRESS, pg, count); b = back(m, dim); f = fwd(m, dim); for( zlink = start_group; zlink != link; zlink=NextDown(zlink) ) { CountChild(z, zlink, count); if( !is_definite(type(z)) || z == m ) continue; FixAndPrintObject(z, xmk + b, b, xf - b, dim, SUPPRESS, pg, count); b = find_max(b, back(z, dim)); f = find_max(f, fwd(z, dim)); } dlen = find_max(dlen, b + f); dble_found = TRUE; start_group = nilobj; /* start new group */ b = back(y, dim); f = fwd(y, dim); m = y; start_group = link; debug2(DGP, DD, " starting group: b = %s, f = %s", EchoLength(b), EchoLength(f)); } else { /* continue with current group */ b = find_max(b, back(y, dim)); f = find_max(f, fwd(y, dim)); if( fwd(y, dim) > fwd(m, dim) ) m = y; debug2(DGP, DD, " continuing group: b = %s, f = %s", EchoLength(b), EchoLength(f)); } NextDefiniteWithGap(x, link, y, g, jn); } assert( start_group != nilobj, "FAPO: final start_group!" ); if( dble_found || !jn ) { /* finish off and fix this last group */ debug2(DGP, DD, " finishing last group: b = %s, f = %s", EchoLength(b), EchoLength(f)); FixAndPrintObject(m, xmk+b, b, xf - b, dim, NO_SUPPRESS, pg,count); b = back(m, dim); f = fwd(m, dim); for( zlink = start_group; zlink != x; zlink = NextDown(zlink) ) { CountChild(z, zlink, count); if( !is_definite(type(z)) || z == m ) continue; FixAndPrintObject(z, xmk+b, b, xf - b, dim, SUPPRESS, pg, count); b = find_max(b, back(z, dim)); f = find_max(f, fwd(z, dim)); } dlen = find_max(dlen, b + f); back(x, dim) = 0; fwd(x, dim) = dlen; } else { /* finish off and fix this last and only group */ debug2(DGP, DD, " finishing last and only group: b = %s, f = %s", EchoLength(b), EchoLength(f)); FixAndPrintObject(m, xmk, xb, xf, dim, NO_SUPPRESS, pg, count); b = back(m, dim); f = fwd(m, dim); for( zlink = start_group; zlink != x; zlink = NextDown(zlink) ) { CountChild(z, zlink, count); if( !is_definite(type(z)) || z == m ) continue; FixAndPrintObject(z, xmk, xb, xf, dim, SUPPRESS, pg, count); b = find_max(b, back(z, dim)); f = find_max(f, fwd(z, dim)); } back(x, dim) = b; fwd(x, dim) = f; } } } break; case ACAT: if( dim == COLM ) { BOOLEAN will_adjust, adjusting; FULL_LENGTH actual_size, adjust_indent, frame_size, back_edge, adjust_inc, inc, adjust_sofar; int adjustable_gaps, gaps_sofar; BOOLEAN underlining; int underline_xstart; FONT_NUM underline_font; OBJECT urec, last_bad_gap; /*********************************************************************/ /* */ /* The first step is to calculate the following values: */ /* */ /* last_bad_gap The rightmost tab gap, or nilobj if none; */ /* */ /* adjustable_gaps the number of gaps suitable for adjustment; */ /* i.e. to the right of the right-most tab gap, */ /* and of non-zero width; */ /* */ /* actual_size the actual size of x without adjustment. */ /* */ /* These are needed when adjusting the line. */ /* */ /*********************************************************************/ FirstDefinite(x, link, y, jn); if( link == x ) break; /* no definite children, nothing to print */ /*** nasty bug finder { OBJECT ff = y; debugcond1(DGP, DD, word_equal(ff, "@ReportLayout"), "FAPO(%s, COLM)", EchoObject(x)); debugcond1(DGP, DD, word_equal(ff, "@ReportLayout"), " adjust_cat(x) = %s", bool(adjust_cat(x))); } ***/ last_bad_gap = nilobj; adjustable_gaps = 0; back_edge = xmk - xb; mk = back_edge + back(y, dim); frame_size = xb + xf; prev = y; NextDefiniteWithGap(x, link, y, g, jn); while( link != x ) { save_actual_gap(g) = ActualGap(fwd(prev, dim), back(y, dim), fwd(y, dim), &gap(g), frame_size, mk - back_edge); mk += save_actual_gap(g); if( mode(gap(g)) == TAB_MODE || units(gap(g)) == AVAIL_UNIT || units(gap(g)) == FRAME_UNIT ) { last_bad_gap = g; adjustable_gaps = 0; } else if( width(gap(g)) > 0 ) adjustable_gaps++; prev = y; NextDefiniteWithGap(x, link, y, g, jn); } actual_size = mk + fwd(prev, dim) - back_edge; /*********************************************************************/ /* */ /* It is possible that the line cannot be displayed in any */ /* reasonable way, because the paragraph breaker was forced to */ /* produce an overfull line. In this case, actual_size will */ /* exceed frame_size and there will be no adjustable gaps. The */ /* solution is to horizontally scale the line if possible, or */ /* else to not print it at all. */ /* */ /*********************************************************************/ if( actual_size > frame_size && adjustable_gaps == 0 ) { /* can't be fixed by adjustment, so scale the line or delete it */ CONSTRAINT c; SetConstraint(c, 0, frame_size, frame_size); fwd(x, dim) = actual_size; debug2(DGP, DD, " oversize, actual_size = %s, frame_size = %s", EchoLength(actual_size), EchoLength(frame_size)); if( BackEnd != PLAINTEXT && InsertScale(x, &c) ) { /* the problem has just been fixed, by inserting a @Scale above x */ OBJECT prnt; Parent(prnt, Up(x)); Child(y, Down(x)); if( actual_size - frame_size < 1 * PT ) { /* the correction is probably due to roundoff error, and */ /* anyway is too small to print an error message about */ } else if( Down(x) == LastDown(x) && is_word(type(y)) ) { Error(23, 3, "word %s horizontally scaled by factor %.2f (too wide for %s paragraph)", WARN, &fpos(y), string(y), (float) bc(constraint(prnt)) / SF, EchoLength(frame_size)); } else { Error(23, 4, "%s object horizontally scaled by factor %.2f (too wide for %s paragraph)", WARN, &fpos(x), EchoLength(size(x, COLM)), (float) bc(constraint(prnt)) / SF, EchoLength(frame_size)); } FixAndPrintObject(prnt, xmk, back(prnt, dim), fwd(prnt, dim), dim, NO_SUPPRESS, pg, count); } else { /* fix the problem by refraining from printing the line */ if( size(x, COLM) <= 0 ) Error(23, 5, "oversize object has size 0 or less", INTERN, &fpos(x)); Child(y, Down(x)); if( Down(x) == LastDown(x) && is_word(type(y)) ) { Error(23, 6, "word %s deleted (too wide for %s paragraph)", WARN, &fpos(y), string(y), EchoLength(frame_size)); } else { Error(23, 7, "%s object deleted (too wide for %s paragraph)", WARN, &fpos(x), EchoLength(size(x, COLM)), EchoLength(frame_size)); } /* delete and dispose every child of x */ while( Down(x) != x ) DisposeChild(Down(x)); y = MakeWord(WORD, STR_EMPTY, &fpos(x)); Link(x, y); back(y, COLM) = fwd(y, COLM) = 0; back(y, ROWM) = fwd(y, ROWM) = 0; } } else { /*********************************************************************/ /* */ /* The line may be displayed in one of four ways: centred, right- */ /* justified, adjusted, or none of the above (i.e. left justified). */ /* An overfull line is always adjusted; otherwise, the line will */ /* be centred or right justified if the display style asks for it; */ /* otherwise, the line will be adjusted if adjust_cat(x) == TRUE */ /* (i.e. there is an enclosing @PAdjust) or if the display style is */ /* DO_ADJUST (meaning that this line is one of a paragraph set in */ /* the adjust or outdent break style, other than the last line); */ /* otherwise, the line is left justified. */ /* */ /* The second step is to decide which of these four cases holds */ /* for this line, and to record the decision in these variables: */ /* */ /* will_adjust TRUE if the adjusted style applies; in this */ /* case, variables adjust_inc and inc will be */ /* set to the appropriate adjustment value; */ /* */ /* adjust_indent If centring or right justification applies, */ /* the indent to produce this, else zero. */ /* */ /* NB adjust_inc may be negative, if the optimal paragraph breaker */ /* has chosen to shrink some gaps. */ /* */ /*********************************************************************/ if( actual_size > frame_size ) { assert( adjustable_gaps > 0, "FAPO: adjustable_gaps!" ); adjust_cat(x) = TRUE; adjust_indent = 0; } else switch( display_style(save_style(x)) ) { case DO_ADJUST: adjust_cat(x) = TRUE; adjust_indent = 0; debug1(DSF, D, "adjust %s", EchoObject(x)); break; case DISPLAY_CENTRE: adjust_cat(x) = FALSE; adjust_indent = (frame_size - actual_size)/2; debug1(DGP, DD, "cdisp %s", EchoObject(x)); break; case DISPLAY_RIGHT: adjust_cat(x) = FALSE; adjust_indent = frame_size - actual_size; debug1(DGP, DD, "rdisp %s", EchoObject(x)); debug1(DSF, D, "rdisp %s", EchoObject(x)); break; default: /* leave adjust_cat(x) as is */ adjust_indent = 0; break; } debug2(DGP, DD, "ACAT %s %s", EchoStyle(&save_style(x)), EchoObject(x)); debug2(DGP, DD, "frame_size = %s, actual_size = %s", EchoLength(frame_size), EchoLength(actual_size)); if( adjust_cat(x) && adjustable_gaps > 0 ) { will_adjust = TRUE; adjust_inc = (frame_size - actual_size) / adjustable_gaps; inc = find_max(adjust_inc, 0); gaps_sofar = 0; /* number of gaps adjusted so far */ adjust_sofar = 0; /* total width of adjustments so far */ debug2(DGP, DD, "will_adjust: adjustable_gaps = %d, adjust_inc = %s", adjustable_gaps, EchoLength(adjust_inc)); } else will_adjust = FALSE; /*********************************************************************/ /* */ /* The third and final step is to traverse x, fixing subobjects. */ /* Variable adjusting is true while adjusting is occurring. */ /* */ /*********************************************************************/ underlining = FALSE; adjusting = will_adjust && last_bad_gap == nilobj; FirstDefinite(x, link, y, jn); prev = y; mk = xmk - back(x, dim) + back(y, dim) + adjust_indent; NextDefiniteWithGap(x, link, y, g, jn); while( link != x ) { /* check for underlining */ if( underline(prev) == UNDER_ON ) { debug3(DGP, DD, " FAPO/ACAT1 underline() := %s for %s %s", bool(FALSE), Image(type(prev)), EchoObject(prev)); underline(prev) = UNDER_OFF; if( !underlining ) { /* underlining begins here */ underlining = TRUE; debug2(DGP, DD, "underlining begins at %s %s", Image(type(prev)), EchoObject(prev)); underline_font = is_word(type(prev)) ? word_font(prev) : font(save_style(x)); underline_xstart = mk - back(prev, dim); } if( underline(g) == UNDER_OFF ) { /* underlining ends here */ debug2(DGP, DD, "underlining ends at %s %s", Image(type(prev)), EchoObject(prev)); New(urec, UNDER_REC); back(urec, COLM) = underline_xstart; fwd(urec, COLM) = mk + fwd(prev, dim); back(urec, ROWM) = underline_font; underlining = FALSE; Link(Up(prev), urec); } } /* fix previous definite now we know it is not the last one */ if( adjusting && width(gap(g)) > 0 ) { int tmp; FixAndPrintObject(prev, mk, back(prev, dim), fwd(prev, dim) + inc, dim, NO_SUPPRESS, pg, count); gaps_sofar++; tmp = ((frame_size - actual_size) * gaps_sofar) / adjustable_gaps; mk += save_actual_gap(g) + (tmp - adjust_sofar); adjust_sofar = tmp; } else { FixAndPrintObject(prev, mk, back(prev, dim), fwd(prev, dim), dim, NO_SUPPRESS, pg, count); mk += save_actual_gap(g); } prev = y; /* commence adjustment if required */ if( !adjusting && will_adjust && g == last_bad_gap ) adjusting = TRUE; NextDefiniteWithGap(x, link, y, g, jn); } /* check for underlining */ debugcond3(DGP, D, underline(prev) == UNDER_UNDEF, " underlining is UNDER_UNDEF in %s: %s %s in para:", EchoFilePos(&fpos(prev)), Image(type(prev)), EchoObject(prev)); debugcond1(DGP, D, underline(prev)==UNDER_UNDEF, "%s", EchoObject(x)); assert( underline(prev) == UNDER_OFF || underline(prev) == UNDER_ON, "FixAndPrint: underline(prev)!" ); if( underline(prev) == UNDER_ON ) { debug3(DGP, DD, " FAPO/ACAT1 underline() := %s for %s %s", bool(FALSE), Image(type(prev)), EchoObject(prev)); underline(prev) = UNDER_OFF; if( !underlining ) { /* underlining begins here */ debug2(DGP, DD, "underlining begins at %s %s", Image(type(prev)), EchoObject(prev)); underlining = TRUE; underline_font = is_word(type(prev)) ? word_font(prev) : font(save_style(x)); underline_xstart = mk - back(prev, dim); } /* underlining must end here */ debug2(DGP, DD, "underlining ends at %s %s", Image(type(prev)), EchoObject(prev)); New(urec, UNDER_REC); back(urec, COLM) = underline_xstart; fwd(urec, COLM) = mk + fwd(prev, dim); back(urec, ROWM) = underline_font; underlining = FALSE; Link(Up(prev), urec); } /* fix the last definite subobject, prev, which must exist */ FixAndPrintObject(prev, mk, back(prev, dim), frame_size - (mk - xmk) - back(x, dim), dim, NO_SUPPRESS, pg, count); } } else for( link = Down(x); link != x; link = NextDown(link) ) { Child(y, link); if( !is_definite(type(y)) ) { if( type(y) == UNDER_REC ) /* generate an underline now */ PrintUnderline(back(y, ROWM), back(y, COLM), fwd(y, COLM), pg - xmk); continue; } FixAndPrintObject(y, xmk, xb, xf, dim, NO_SUPPRESS, pg, count); } back(x, dim) = xb; fwd(x, dim) = xf; break; case COL_THR: case ROW_THR: /* find and delete the child number count of y */ assert( (type(x) == COL_THR) == (dim == COLM), "FixAndPrintObject: thr!" ); for( link = Down(x), uplink = Up(x), i = 1; link != x && uplink != x && i < count; link = NextDown(link), uplink = NextUp(uplink), i++ ); assert( link != x && uplink != x, "FixAndPrintObject: link or uplink!" ); CountChild(y, link, count); debug7(DGP, DD, " fapo of %s (%s,%s) child %d %s (%s,%s)", Image(type(x)), EchoLength(back(x, dim)), EchoLength(fwd(x, dim)), i, Image(type(y)), EchoLength(back(y, dim)), EchoLength(fwd(y, dim))); MoveLink(uplink, link, CHILD); DeleteLink(link); /* IMPORTANT!!! */ assert( type(y) != GAP_OBJ, "FAPO: THR!"); /* assign size if not done previously */ /* *** if( thr_state(x) != FINALSIZE && type(y) != START_HVSPAN && type(y) != START_HSPAN && type(y) != START_VSPAN && type(y) != HSPAN && type(y) != VSPAN ) *** */ if( thr_state(x) != FINALSIZE ) { back(x, dim) = xb; fwd(x, dim) = xf; thr_state(x) = FINALSIZE; } /* fix y */ FixAndPrintObject(y, xmk, back(x, dim), fwd(x, dim), dim, NO_SUPPRESS, pg, count); if( Up(x) == x ) Dispose(x); break; case BEGIN_HEADER: case END_HEADER: case SET_HEADER: case CLEAR_HEADER: if( dim == COLM ) Error(23, 8, "%s symbol ignored (out of place)", WARN, &fpos(x), Image(type(x))); break; default: assert1(FALSE, "FixAndPrintObject:", Image(type(x))); break; } /* end switch */ debug2(DGP, DD, "] FixAndPrintObject returning (size now %s,%s).", EchoLength(back(x, dim)), EchoLength(fwd(x, dim))); } /* end FixAndPrintObject */