| /*@z21.c:Galley Maker:SizeGalley()@*******************************************/ |
| /* */ |
| /* THE LOUT DOCUMENT FORMATTING SYSTEM (VERSION 3.24) */ |
| /* 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: z21.c */ |
| /* MODULE: Galley Maker */ |
| /* EXTERNS: SizeGalley() */ |
| /* */ |
| /*****************************************************************************/ |
| #include "externs.h" |
| |
| /*****************************************************************************/ |
| /* */ |
| /* SizeGalley(hd, env, rows, joined, nonblock, trig, style, c, target, */ |
| /* dest_index, recs, inners, enclose) */ |
| /* */ |
| /* Convert unsized galley hd into sized format. The input parameters are: */ |
| /* */ |
| /* hd the galley to be converted */ |
| /* env its environment (needs to be "held" while manifesting) */ |
| /* rows TRUE if the resulting galley may have more than one row */ |
| /* joined TRUE if the resulting galley must be simply joined */ |
| /* nonblock Set the non_blocking() field of RECEPTIVEs to this value */ |
| /* trig TRUE if indefinites of hd may trigger external galleys */ |
| /* *style The initial style */ |
| /* *c the width constraint hd should conform to */ |
| /* target if non-nilobj, expand indefinite objects to reveal a */ |
| /* @Galley within this symbol */ |
| /* enclose If non-nilobj, enclose any @Galley symbol encountered */ |
| /* during manifesting by this symbol. */ |
| /* */ |
| /* The output parameters, in addition to the converted hd, are: */ |
| /* */ |
| /* dest_index the index of the @Galley found within target, if any */ |
| /* recs list of all RECURSIVE indexes found (or nilobj if none) */ |
| /* inners list of all UNATTACHED indexes found (or nilobj if none), */ |
| /* not including any that come after the target or InputSym. */ |
| /* */ |
| /*****************************************************************************/ |
| |
| void SizeGalley(OBJECT hd, OBJECT env, BOOLEAN rows, BOOLEAN joined, |
| BOOLEAN nonblock, BOOLEAN trig, STYLE *style, CONSTRAINT *c, OBJECT target, |
| OBJECT *dest_index, OBJECT *recs, OBJECT *inners, OBJECT enclose) |
| { OBJECT y, link, z, crs, t, tlink, zlink, tmp, why; |
| OBJECT extras, tmp1, tmp2, bt[2], ft[2], hold_env; |
| BOOLEAN after_target; |
| |
| assert( type(hd) == HEAD && Down(hd) != hd, "SizeGalley: precondition!" ); |
| assert( !sized(hd), "SizeGalley: already sized!" ); |
| debug6(DGM, D, "SizeGalley(%s, -, %s, %s, %s, %s, -, %s, -, -, -), hd =", |
| SymName(actual(hd)), bool(rows), bool(joined), bool(nonblock), |
| bool(trig), EchoConstraint(c)); |
| debug1(DGM, DD, " env = %s", EchoObject(env)); |
| ifdebug(DGM, D, DebugObject(hd)); |
| |
| /* manifest the child of hd, making sure it is simply joined if required */ |
| Child(y, Down(hd)); |
| tmp1 = target; |
| tmp2 = enclose; |
| crs = nilobj; |
| bt[COLM] = ft[COLM] = bt[ROWM] = ft[ROWM] = nilobj; |
| New(hold_env, ACAT); Link(hold_env, env); |
| if( AllowCrossDb && type(y) == CLOSURE && has_optimize(actual(y)) |
| && FindOptimize(y, env) ) |
| { |
| SetOptimize(hd, style); |
| } |
| debug2(DOM, D, "[ calling Manifest(%s) from SizeGalley(%s)", |
| Image(type(y)), SymName(actual(hd))); |
| debug2(DOB, D, "[ calling Manifest(%s) from SizeGalley(%s)", |
| Image(type(y)), SymName(actual(hd))); |
| if( joined ) |
| { New(bt[COLM], THREAD); New(ft[COLM], THREAD); |
| debug0(DGM, DD, "SizeGalley calling Manifest (joined)"); |
| y = Manifest(y, env, style, bt, ft, &tmp1, &crs, TRUE, must_expand(hd), |
| &tmp2, FALSE); |
| assert( Down(bt[COLM]) != bt[COLM] && Down(ft[COLM]) != ft[COLM], |
| "SizeGalley: threads!" ); |
| Child(tmp1, Down(bt[COLM])); Child(tmp2, Down(ft[COLM])); |
| if( Down(bt[COLM]) != LastDown(bt[COLM]) || |
| Down(ft[COLM]) != LastDown(ft[COLM]) || tmp1 != tmp2 ) |
| Error(21, 1, "galley %s must have just one column mark", |
| FATAL, &fpos(y), SymName(actual(hd)) ); |
| DisposeObject(bt[COLM]); DisposeObject(ft[COLM]); |
| } |
| else |
| { debug0(DGM, DD, "SizeGalley calling Manifest (not joined)"); |
| y = Manifest(y, env, style, bt, ft, &tmp1, &crs, TRUE, must_expand(hd), |
| &tmp2, FALSE); |
| } |
| debug2(DOM, D, "] returning Manifest(%s) from SizeGalley(%s)", |
| Image(type(y)), SymName(actual(hd))); |
| debug2(DOB, D, "] returning Manifest(%s) from SizeGalley(%s)", |
| Image(type(y)), SymName(actual(hd))); |
| DisposeObject(hold_env); |
| debug0(DGM, DD, "SizeGalley: after manifesting, hd ="); |
| ifdebug(DGM, DD, DebugObject(hd)); |
| |
| /* horizontally size hd */ |
| debug0(DGM, DD, "SizeGalley horizontally sizing hd:"); |
| New(extras, ACAT); |
| debug2(DSF, D, "[ calling MinSize(%s) from SizeGalley(%s)", |
| Image(type(y)), SymName(actual(hd))); |
| y = MinSize(y, COLM, &extras); |
| debug2(DSF, D, "] returning MinSize(%s) from SizeGalley(%s)", |
| Image(type(y)), SymName(actual(hd))); |
| |
| /* break hd if vertical galley */ |
| if( gall_dir(hd) == ROWM ) |
| { |
| CopyConstraint(constraint(hd), *c); |
| debug0(DGM, DD, "SizeGalley calling BreakObject:"); |
| debug2(DOB, D, "[ calling BreakObject(%s) from SizeGalley(%s)", |
| Image(type(y)), SymName(actual(hd))); |
| y = BreakObject(y, c); |
| debug2(DOB, D, "] returning BreakObject(%s) from SizeGalley(%s)", |
| Image(type(y)), SymName(actual(hd))); |
| if( !FitsConstraint(back(y, COLM), fwd(y, COLM), *c) ) |
| Error(21, 13, "%s,%s object too wide for available space", |
| FATAL, &fpos(y), EchoLength(back(y, COLM)), EchoLength(fwd(y, COLM))); |
| back(hd, COLM) = back(y, COLM); |
| fwd(hd, COLM) = fwd(y, COLM); |
| assert( FitsConstraint(back(hd, COLM), fwd(hd, COLM), *c), |
| "SizeGalley: BreakObject failed to fit!" ); |
| debug2(DSF, D, "MinSize(hd, COLM) = %s,%s", |
| EchoLength(back(hd, COLM)), EchoLength(fwd(hd, COLM)) ); |
| } |
| |
| /* hyphenate hd if horizontal optimal galley says so */ |
| else if( opt_components(hd) != nilobj && opt_hyph(hd) && type(y) == ACAT ) |
| { debug0(DOG, D, "SizeGalley calling Hyphenate()"); |
| y = Hyphenate(y); |
| } |
| |
| /* get the rows of hd to the top level, if required */ |
| seen_nojoin(hd) = FALSE; |
| if( rows ) |
| { /* OBJECT prev_gap = nilobj; */ |
| debug0(DGM, DD, "SizeGalley cleaning up rows of hd:"); |
| for( link = hd; NextDown(link) != hd; link = NextDown(link) ) |
| { Child(y, NextDown(link)); |
| debug2(DGM, DD, " cleaning %s: %s", Image(type(y)), EchoObject(y)); |
| switch( type(y) ) |
| { |
| case GAP_OBJ: |
| |
| /* prev_gap = y; */ |
| if( !join(gap(y)) ) seen_nojoin(hd) = TRUE; |
| break; |
| |
| |
| case VCAT: |
| |
| if( gall_dir(hd) == ROWM ) |
| { TransferLinks(Down(y), y, Up(y)); |
| DisposeChild(Up(y)); |
| link = PrevDown(link); |
| } |
| break; |
| |
| |
| case ACAT: |
| |
| if( gall_dir(hd) == COLM ) |
| { TransferLinks(Down(y), y, Up(y)); |
| DisposeChild(Up(y)); |
| link = PrevDown(link); |
| } |
| break; |
| |
| |
| case SPLIT: |
| |
| assert(Up(y)==LastUp(y), "SizeGalley COL_THR: Up(y)!=LastUp(y)!"); |
| Child(z, DownDim(y, ROWM)); |
| if( is_indefinite(type(z)) ) |
| { |
| debug1(DGT, D, "SizeGalley setting external_ver(%s) to TRUE (a)", |
| EchoObject(z)); |
| external_ver(z) = TRUE; |
| } |
| else if( type(z) == VCAT ) |
| { OBJECT hor, thor, clink, dlink; |
| Child(hor, DownDim(y, COLM)); |
| assert( type(hor) == COL_THR, "SizeGalley: missing COL_THR!" ); |
| Parent(thor, UpDim(z, COLM)); |
| assert( hor == thor, "SizeGalley/SPLIT: hor != thor!" ); |
| clink = DownDim(y, COLM); |
| dlink = UpDim(z, COLM); |
| for( tlink = LastDown(z); tlink != z; tlink = PrevDown(tlink) ) |
| { Child(t, tlink); |
| if( type(t) == GAP_OBJ ) |
| { Link(NextDown(link), t); |
| } |
| else |
| { New(tmp, SPLIT); |
| back(tmp, COLM) = back(hor, COLM); |
| fwd(tmp, COLM) = fwd(hor, COLM); |
| Link(NextDown(link), tmp); |
| Link(tmp, NextUp(clink)); |
| Link(NextDown(dlink), t); |
| Link(tmp, t); |
| } |
| } |
| DeleteLink(dlink); |
| assert(Up(y)==LastUp(y), "SizeGalley COL_THR: Up(y) != LastUp(y)!"); |
| DisposeChild(Up(y)); |
| link = PrevDown(link); |
| } |
| break; |
| |
| |
| case CLOSURE: |
| case HEAD: |
| |
| if( gall_dir(hd) == COLM ) |
| external_hor(y) = TRUE; |
| else |
| { |
| debug1(DGT, D, "SizeGalley setting external_ver(%s) to TRUE (b)", |
| EchoObject(y)); |
| external_ver(y) = TRUE; |
| } |
| break; |
| |
| |
| default: |
| |
| break; |
| } |
| } |
| } |
| |
| /* determine a scale factor for {} @Scale objects */ |
| /* NB AdjustSize cannot be done correctly until after seen_nojoin is set */ |
| for( link = Down(extras); link != extras; link = NextDown(link) ) |
| { Child(y, link); |
| if( type(y) == SCALE_IND ) |
| { |
| /* check that all is in order */ |
| CONSTRAINT zc; OBJECT t; FULL_LENGTH b, f; |
| z = actual(y); |
| assert( type(z) == SCALE, "SizeObject: type(z) != SCALE!" ); |
| assert( bc(constraint(z)) == 0, "SizeObject: bc(constraint(z)) != 0" ); |
| assert( Down(z) != z, "SizeObject SCALE: Down(z) == z!" ); |
| Child(t, Down(z)); |
| |
| /* use @Scale COLM size constraint to determine a suitable scale factor */ |
| /* check that @Scale is not in a horizontal galley */ |
| if( gall_dir(hd) == COLM ) |
| { Error(21, 2, "%s with unspecified scale factor in horizontal galley", |
| FATAL, &fpos(z), KW_SCALE); |
| } |
| |
| Constrained(z, &zc, COLM, &why); |
| debug2(DGM, DD, "Constrained(%s, -, COLM) = %s", EchoObject(z), |
| EchoConstraint(&zc)); |
| if( !constrained(zc) ) |
| { Error(21, 3, "replacing infinite scale factor (unconstrained width) by 1.0", |
| WARN, &fpos(z)); |
| bc(constraint(z)) = fc(constraint(z)) = 1 * SF; |
| } |
| else if( size(t, COLM) == 0 ) |
| { Error(21, 4, "replacing infinite scale factor (zero width object) by 1.0", |
| WARN, &fpos(z)); |
| bc(constraint(z)) = fc(constraint(z)) = 1 * SF; |
| } |
| else if( (float) bfc(zc) / size(t, COLM) > 100.0 ) |
| { Error(21, 5, "replacing very large scale factor (over 100) by 1.0", |
| WARN, &fpos(z)); |
| bc(constraint(z)) = fc(constraint(z)) = 1 * SF; |
| } |
| else if( (float) bfc(zc) / size(t, COLM) < 0.01 ) |
| { if( bfc(zc) == 0 ) |
| Error(21, 6, "object deleted (scale factor is zero)", |
| WARN, &fpos(z)); |
| else |
| Error(21, 7, "object deleted (scale factor is smaller than 0.01)", |
| WARN, &fpos(z)); |
| bc(constraint(z)) = fc(constraint(z)) = 1 * SF; |
| tmp = MakeWord(WORD, STR_EMPTY, &fpos(t)); |
| back(tmp, COLM) = fwd(tmp, COLM) = 0; |
| back(tmp, ROWM) = fwd(tmp, ROWM) = 0; |
| word_font(tmp) = word_colour(tmp) = word_language(tmp) = 0; |
| word_outline(tmp) = FALSE; |
| word_hyph(tmp) = FALSE; |
| ReplaceNode(tmp, t); |
| DisposeObject(t); |
| t = tmp; |
| } |
| else bc(constraint(z)) = fc(constraint(z)) = (bfc(zc) * SF)/size(t, COLM); |
| |
| /* calculate scaled size and adjust */ |
| b = (back(t, COLM) * fc(constraint(z))) / SF; |
| f = (fwd(t, COLM) * fc(constraint(z))) / SF; |
| debug3(DGM, DD, "AdjustSize(%s, %s, %s, COLM)", EchoObject(z), |
| EchoLength(b), EchoLength(f)); |
| AdjustSize(z, b, f, COLM); |
| |
| /* if already vertically sized (because inside @Rotate) adjust that */ |
| if( vert_sized(z) ) |
| { b = (back(t, ROWM) * fc(constraint(z))) / SF; |
| f = (fwd(t, ROWM) * fc(constraint(z))) / SF; |
| debug4(DGM, DD, "AdjustSize(%s, %s, %s, %s)", EchoObject(z), |
| EchoLength(b), EchoLength(f), dimen(ROWM)); |
| AdjustSize(z, b, f, ROWM); |
| } |
| } |
| } |
| DisposeObject(extras); |
| |
| /* size the rows of hd and attach indices where needed */ |
| debug0(DGM, DD, " SizeGalley calling MinSize(ROWM):"); |
| debug0(DGM, DD, "SizeGalley sizing rows of hd ="); |
| ifdebug(DGM, DD, DebugObject(hd)); |
| *recs = *inners = *dest_index = nilobj; |
| after_target = FALSE; |
| for( link = Down(hd); link != hd; link = NextDown(link) ) |
| { Child(y, link); |
| |
| if( type(y) == GAP_OBJ || is_index(type(y)) ) continue; |
| debug0(DGM, DDD, " ROWM sizing:"); |
| ifdebug(DGM, DDD, DebugObject(y)); |
| New(extras, ACAT); |
| y = MinSize(y, ROWM, &extras); |
| debug3(DSF, DD, "MinSize( %s , ROWM ) = %s,%s", EchoObject(y), |
| EchoLength(back(y, ROWM)), EchoLength(fwd(y, ROWM)) ); |
| debug0(DGM, DDD, " ROWM result:"); |
| ifdebug(DGM, DDD, DebugObject(y)); |
| |
| /* now attach indexes in front of y */ |
| for( zlink = Down(extras); zlink != extras; zlink = NextDown(zlink) ) |
| { Child(z, zlink); |
| blocked(z) = FALSE; |
| /* debug1(DCR, DD, " extra: %s", EchoObject(z)); */ |
| debug2(DGM, DD, " extra%s: %s", |
| after_target ? " after_target" : "", EchoObject(z)); |
| switch( type(z) ) |
| { |
| case RECEPTIVE: |
| |
| /* debug2(DCR, DD, " ... uses_ext = %s, trig = %s", |
| bool(uses_extern_target(actual(actual(z)))), bool(trig)); */ |
| trigger_externs(z) = uses_extern_target(actual(actual(z))) && trig; |
| non_blocking(z) = nonblock; |
| if( actual(actual(z)) == GalleySym || actual(actual(z)) == ForceGalleySym ) |
| *dest_index = z; |
| if( actual(actual(z)) == GalleySym || actual(actual(z)) == ForceGalleySym |
| || actual(actual(z)) == InputSym ) |
| after_target = TRUE; |
| break; |
| |
| |
| case RECURSIVE: |
| |
| if( *recs == nilobj ) New(*recs, ACAT); |
| Link(*recs, z); |
| break; |
| |
| |
| case UNATTACHED: |
| |
| if( !after_target ) /* *** new semantics *** */ |
| { if( *inners == nilobj ) New(*inners, ACAT); |
| Link(*inners, z); |
| } |
| break; |
| |
| |
| case SCALE_IND: |
| case EXPAND_IND: |
| case GALL_PREC: |
| case GALL_FOLL: |
| case GALL_FOLL_OR_PREC: |
| case GALL_TARG: |
| case CROSS_PREC: |
| case CROSS_FOLL: |
| case CROSS_FOLL_OR_PREC: |
| case CROSS_TARG: |
| case PAGE_LABEL_IND: |
| |
| debug1(DCR, DD, " SizeGalley: %s", EchoObject(z)); |
| break; |
| |
| |
| case COVER_IND: |
| |
| /* adjust size of the COVER object, change it to @Scale etc. */ |
| { OBJECT cover, prnt, chld; int dirn, thr_type, ok1, ok2, sf,subst, esubst; |
| float sf1, sf2; CONSTRAINT c; FULL_LENGTH b, f; |
| cover = actual(z); |
| if( type(cover) == HCOVER ) |
| { dirn = COLM; |
| thr_type = COL_THR; |
| ok1 = VCAT; |
| ok2 = VCAT; |
| subst = HSCALE; |
| esubst = ONE_COL; |
| } |
| else |
| { dirn = ROWM; |
| thr_type = ROW_THR; |
| ok1 = ACAT; |
| ok2 = HCAT; |
| subst = VSCALE; |
| esubst = ONE_ROW; |
| } |
| Parent(prnt, UpDim(cover, dirn)); |
| while( type(prnt) == SPLIT || type(prnt) == thr_type ) |
| Parent(prnt, UpDim(prnt, dirn)); |
| Child(chld, Down(cover)); |
| if( type(prnt) != ok1 && type(prnt) != ok2 ) |
| { |
| Error(21, 8, "%s replaced by %s (mark not shared)", |
| WARN, &fpos(cover), Image(type(cover)), Image(subst)); |
| debug2(DGM, DDD, " cover = %s %s", Image(type(cover)), |
| EchoObject(cover)); |
| debug1(DGM, DDD, " prnt = %s:", Image(type(prnt))); |
| ifdebug(DGM, DDD, DebugObject(prnt)); |
| type(cover) = subst; |
| } |
| else if( back(chld, dirn) == 0 && fwd(chld, dirn) == 0 ) |
| { |
| /* empty object, this is treated as a no-op */ |
| type(cover) = esubst; |
| } |
| else if( back(chld, dirn) == 0 || fwd(chld, dirn) == 0 ) |
| { Error(21, 9, "%s replaced by %s (infinite scale factor)", |
| WARN, &fpos(cover), Image(type(cover)), Image(subst)); |
| type(cover) = subst; |
| } |
| else if( size(prnt, dirn) == 0 ) |
| { Error(21, 10, "%s replaced by %s (zero scale factor)", |
| WARN, &fpos(cover), Image(type(cover)), Image(subst)); |
| type(cover) = subst; |
| } |
| else /* sensible scale factor exists */ |
| { |
| /* work out proposed scale factor and sizes for cover */ |
| sf1 = (float) back(prnt, dirn) / back(chld, dirn); |
| sf2 = (float) fwd(prnt, dirn) / fwd(chld, dirn); |
| sf = find_max(sf1, sf2) * SF; |
| b = (back(chld, dirn) * sf) / SF; |
| f = (fwd(chld, dirn) * sf) / SF; |
| |
| /* check whether new object fits */ |
| Constrained(cover, &c, dirn, &why); |
| if( FitsConstraint(b, f, c) ) |
| { |
| /* it fits, so make cover a SCALE object with this size */ |
| type(cover) = SCALE; |
| if( dirn == COLM ) |
| { bc(constraint(cover)) = sf; |
| fc(constraint(cover)) = SF; |
| } |
| else |
| { bc(constraint(cover)) = SF; |
| fc(constraint(cover)) = sf; |
| } |
| AdjustSize(cover, b, f, dirn); |
| } |
| else |
| { Error(21, 11, "%s replaced by %s (insufficient space)", |
| WARN, &fpos(cover), Image(type(cover)), Image(subst)); |
| type(cover) = subst; |
| } |
| } |
| } |
| break; |
| |
| |
| default: |
| |
| assert1(FALSE, "SizeGalley:", Image(type(z))); |
| break; |
| |
| } |
| } |
| TransferLinks(Down(extras), extras, link); |
| assert( Down(extras) == extras && Up(extras) == extras, "SizeG: extras!"); |
| Dispose(extras); |
| } |
| |
| /* insinuate cross references */ |
| if( crs != nilobj ) |
| { |
| debug1(DCR, DD, "SizeGalley insinuating %s", EchoObject(crs)); |
| TransferLinks(Down(crs), crs, Down(hd)); |
| DisposeObject(crs); |
| } |
| |
| /* check that *dest_index was found if it was required, and exit */ |
| if( target != nilobj && *dest_index == nilobj ) |
| Error(21, 12, "unexpected absence of %s from the body of %s", |
| FATAL, &fpos(hd), SymName(target), SymName(actual(hd))); |
| debug3(DGM, D, "SizeGalley returning %s,%s %s; hd =", |
| EchoLength(back(hd, COLM)), EchoLength(fwd(hd, COLM)), |
| EchoConstraint(&constraint(hd))); |
| ifdebug(DGM, D, DebugGalley(hd, nilobj, 4)); |
| sized(hd) = TRUE; |
| |
| } /* end SizeGalley */ |