/*@z46.c:Optimal Galleys:FindOptimize()@**************************************/ /* */ /* THE LOUT DOCUMENT FORMATTING SYSTEM (VERSION 3.22) */ /* 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: z46.c */ /* MODULE: Optimal Galleys */ /* EXTERNS: FindOptimize(), SetOptimize(), GazumpOptimize(), */ /* CalculateOptimize(), DebugOptimize() */ /* */ /*****************************************************************************/ #include "externs.h" /*****************************************************************************/ /* */ /* BOOLEAN FindOptimize(x, env) */ /* */ /* Object x is a CLOSURE which represents an at present unsized galley. */ /* Return TRUE if x has an @Optimize parameter which is Yes. */ /* */ /*****************************************************************************/ BOOLEAN FindOptimize(OBJECT x, OBJECT env) { OBJECT y, link, res; OBJECT bt[2], ft[2], ntarget, nenclose, crs; debug1(DOG, D, "FindOptimize( %s )", EchoObject(x)); assert( type(x) == CLOSURE, "FindOptimize: type(x) != CLOSURE!" ); assert( has_target(actual(x)), "FindOptimize: x has no target!" ); /* search the parameter list of x for @Optimize */ res = nilobj; for( link = Down(x); link != x; link = NextDown(link) ) { Child(y, link); if( type(y) == PAR && is_optimize(actual(y)) ) { assert( Down(y) != y, "FindOptimize: Down(PAR)!" ); Child(res, Down(y)); res = CopyObject(res, &fpos(x)); break; } } /* search the children list of actual(x) for a default value of @Target */ if( res == nilobj ) for( link = Down(actual(x)); link != actual(x); link = NextDown(link) ) { Child(y, link); if( is_optimize(y) ) { res = CopyObject(sym_body(y), &fpos(x)); break; } } /* should have found it by now */ assert( res != nilobj, "FindOptimize: res == nilobj!" ); /* manifest and tidy the parameter, return TRUE if Yes */ bt[COLM] = ft[COLM] = bt[ROWM] = ft[ROWM] = ntarget = nenclose = crs = nilobj; res = Manifest(res, env, &save_style(x), bt, ft, &ntarget, &crs, TRUE, FALSE, &nenclose, FALSE); res = ReplaceWithTidy(res, TRUE); if( !is_word(type(res)) ) { Error(46, 1, "unable to evaluate %s parameter, assuming value is No", WARN, &fpos(x), KW_OPTIMIZE); debug2(DOG, D, "FindOptimize returning FALSE; found %s %s", Image(type(res)), EchoObject(res)); return FALSE; } else if( StringEqual(string(res), AsciiToFull("Yes")) ) { debug0(DOG, D, "FindOptimize returning TRUE"); return TRUE; } else if( StringEqual(string(res), AsciiToFull("No")) ) { debug0(DOG, D, "FindOptimize returning FALSE"); return FALSE; } else { Error(46, 2, "value of %s operator is neither Yes nor No, assuming No", WARN, &fpos(x), KW_OPTIMIZE); debug1(DOG, D, "FindOptimize returning FALSE (found WORD %s)", string(res)); return FALSE; } } /* end FindOptimize */ /*****************************************************************************/ /* */ /* SetOptimize(hd, style) */ /* */ /* Initialize the optimization data of galley hd. Search the cross ref */ /* database for information about its fate on the previous run. */ /* */ /*****************************************************************************/ void SetOptimize(OBJECT hd, STYLE *style) { FULL_CHAR buff[MAX_BUFF], seq[MAX_BUFF]; OBJECT res, y, link, z; FILE_NUM dfnum; long dfpos, cont; int dlnum; debug2(DOG, D, "SetOptimize(%s, %s)", SymName(actual(hd)), EchoStyle(style)); /* set opt_counts(hd) to result of previous run, if any */ StringCopy(buff, SymName(actual(hd))); StringCat(buff, AsciiToFull(".")); StringCat(buff, StringInt(line_num(fpos(hd)))); if( DbRetrieve(OldCrossDb, FALSE, OptGallSym, buff, seq, &dfnum, &dfpos, &dlnum, &cont) ) { SwitchScope(nilobj); res = ReadFromFile(dfnum, dfpos, dlnum); UnSwitchScope(nilobj); assert( res != nilobj, "SetOptimize: res == nilobj!" ); assert( type(res) == CLOSURE, "SetOptimize: type(res) != CLOSURE!" ); assert( actual(res) == OptGallSym, "SetOptimize: actual(res) != Opt!" ); assert( Down(res) != res, "SetOptimize: Down(res) == res!" ); Child(y, Down(res)); assert( type(y) == PAR, "SetOptimize: type(y) != PAR!" ); Child(y, Down(y)); assert( type(y) == ACAT, "SetOptimize: type(y) != ACAT!" ); y = ReplaceWithTidy(y, FALSE); opt_hyph(hd) = FALSE; assert( type(y) == ACAT, "SetOptimize: type(y) != ACAT (2)!" ); for( link = y; NextDown(link) != y; link = NextDown(link) ) { Child(z, NextDown(link)); if( type(z) == GAP_OBJ ) { DisposeChild(NextDown(link)); link = PrevDown(link); } else if( is_word(type(z)) ) { if( StringEqual(string(z), AsciiToFull("h")) ) { opt_hyph(hd) = TRUE; DisposeChild(NextDown(link)); link = PrevDown(link); } else { int num = 0; sscanf( (char *) string(z), "%d", &num); assert( num > 0, "SetOptimize: num <= 0!" ); comp_count(z) = num; } } else { assert( FALSE, "SetOptimize: type(z)!" ); } } DeleteLink(Up(y)); DisposeObject(res); opt_counts(hd) = y; } else opt_counts(hd) = nilobj; /* set up first opt_comps_permitted value */ if( opt_counts(hd) != nilobj && Down(opt_counts(hd)) != opt_counts(hd) ) { Child(z, Down(opt_counts(hd))); opt_comps_permitted(hd) = comp_count(z) - 1; DisposeChild(Up(z)); } else opt_comps_permitted(hd) = MAX_FILES; /* a large number */ debug1(DOG, D, " initial permitted = %2d", opt_comps_permitted(hd)); /* set opt_components(hd) and opt_constraints(hd) for storing this run */ New(opt_components(hd), ACAT); opt_gazumped(hd) = FALSE; New(opt_constraints(hd), ACAT); StyleCopy(save_style(opt_components(hd)), *style); if( gall_dir(hd) == ROWM ) hyph_style(save_style(opt_components(hd))) = HYPH_OFF; debug0(DOG, D, "SetOptimize returning:"); ifdebug(DOG, D, DebugOptimize(hd)); } /* end SetOptimize */ /*****************************************************************************/ /* */ /* GazumpOptimize(hd, dest) */ /* */ /* Optimizing galley hd, currently attached to @Galley dest, is to be */ /* gazumped by some other galley. Record the current size constraint and */ /* add &1rt {} to the list of components. */ /* */ /*****************************************************************************/ void GazumpOptimize(OBJECT hd, OBJECT dest) { OBJECT g, tmp, junk, prnt; debug2(DOG, D, "GazumpOptimize(%s, %s)", SymName(actual(hd)), EchoObject(dest)); assert( type(hd) == HEAD, "GazumpOptimize: type(hd) != HEAD!" ); assert( opt_components(hd) != nilobj, "GazumpOptimize: opt_c!" ); /* record the size of this just-completed target area for hd */ New(tmp, WIDE); if( (gall_dir(hd) == COLM && external_hor(dest)) || (gall_dir(hd) == COLM && external_hor(dest)) ) { SetConstraint(constraint(tmp), MAX_FULL_LENGTH, MAX_FULL_LENGTH, MAX_FULL_LENGTH); } else { Parent(prnt, Up(dest)); Constrained(prnt, &constraint(tmp), gall_dir(hd), &junk); } Link(opt_constraints(hd), tmp); debug2(DOG, D, "GazumpOptimize(%s) adding constraint %s", SymName(actual(hd)), EchoConstraint(&constraint(tmp))); /* optimizing galley is being gazumped; record this as &1rt {} &1c */ if( LastDown(opt_components(hd)) != opt_components(hd) ) { Child(g, LastDown(opt_components(hd))); assert( type(g) == GAP_OBJ, "FlushGalley: type(g) != GAP_OBJ!" ); /* *** SetGap(gap(g), FALSE, FALSE, TRUE, FRAME_UNIT, EDGE_MODE, 2 * FR); if( Down(g) == g ) { junk = MakeWord(WORD, AsciiToFull("2b"), &fpos(g)); Link(g, junk); } *** */ /* first we overwrite whatever is there now by &1rt */ SetGap(gap(g), FALSE, FALSE, TRUE, AVAIL_UNIT, TAB_MODE, 1 * FR); if( Down(g) != g ) DisposeChild(Down(g)); tmp = MakeWord(WORD, AsciiToFull("1rt"), &fpos(g)); Link(g, tmp); /* next we add an empty word */ tmp = MakeWord(WORD, STR_EMPTY, &fpos(g)); back(tmp, COLM) = fwd(tmp, COLM) = 0; back(tmp, ROWM) = fwd(tmp, ROWM) = 0; word_font(tmp) = word_colour(tmp) = 0; word_outline(tmp) = FALSE; word_language(tmp) = word_hyph(tmp) = 0; Link(opt_components(hd), tmp); /* finally we add &1c */ New(g, GAP_OBJ); hspace(g) = 1; vspace(g) = 0; FposCopy(fpos(g), fpos(tmp)); SetGap(gap(g), FALSE, FALSE, TRUE, FIXED_UNIT, EDGE_MODE, 1 * CM); tmp = MakeWord(WORD, AsciiToFull("1c"), &fpos(g)); Link(g, tmp); Link(opt_components(hd), g); opt_gazumped(hd) = TRUE; debug2(DOG, D, "GazumpOptimize(%s) new gap is %s", SymName(actual(hd)), EchoGap(&gap(g))); } /* refresh the number of comps permitted into the next target */ if( opt_counts(hd) != nilobj && Down(opt_counts(hd)) != opt_counts(hd) ) { Child(tmp, Down(opt_counts(hd))); opt_comps_permitted(hd) += comp_count(tmp) - 1; DisposeChild(Up(tmp)); } else opt_comps_permitted(hd) = MAX_FILES; debug1(DOG, D, "GazumpOptimize returning, permitted = %2d", opt_comps_permitted(hd)); } /* end GazumpOptimize */ /*****************************************************************************/ /* */ /* CalculateOptimize(hd) */ /* */ /* Calculate the optimal break for galley hd and write the result into */ /* the cross reference database. */ /* */ /*****************************************************************************/ void CalculateOptimize(OBJECT hd) { OBJECT z, y, ylink, og, og_par, para, link, wd, g, last; int count, compcount; FULL_CHAR buff[MAX_BUFF]; FILE_NUM fnum; int write_pos, write_lnum; BOOLEAN hyph_used; debug1(DOG, D, "CalculateOptimize(%s)", SymName(actual(hd))); /* delete the concluding GAP_OBJ stuck in by Promote() */ assert( LastDown(opt_components(hd)) != opt_components(hd), "CO!" ); Child(last, LastDown(opt_components(hd))); assert( type(last) == GAP_OBJ, "CalculateOptimize: type(last)!" ); DisposeChild(Up(last)); ifdebug(DOG, D, DebugOptimize(hd)); /* break the paragraph; don't let user see any error messages */ assert( opt_constraints(hd) != nilobj, "KillGalley: no opt_constraints!" ); assert( Down(opt_constraints(hd)) != opt_constraints(hd), "KillGalleyo!" ); /* *** no longer needed since z14 doesn't refer to these fields back(opt_components(hd), COLM) = 0; fwd(opt_components(hd), COLM) = MAX_FULL_LENGTH; *** */ Child(y, LastDown(opt_constraints(hd))); EnterErrorBlock(FALSE); opt_components(hd) = FillObject(opt_components(hd), &constraint(y), opt_constraints(hd), FALSE, FALSE, TRUE, &hyph_used); LeaveErrorBlock(FALSE); debug1(DOG, D, "after breaking (%shyph_used):", hyph_used ? "" : "not "); ifdebug(DOG, D, DebugOptimize(hd)); /* quit if one line only */ if( type(opt_components(hd)) != VCAT || Down(opt_components(hd)) == LastDown(opt_components(hd)) ) { debug0(DOG, D, "CalculateOptimize returning (one target only)"); return; } /* construct a new @OptGall symbol */ New(og, CLOSURE); actual(og) = OptGallSym; FposCopy(fpos(og), fpos(hd)); New(og_par, PAR); actual(og_par) = ChildSym(OptGallSym, RPAR); Link(og, og_par); New(para, ACAT); Link(og_par, para); /* begin with "h" if hyphenation was used */ if( hyph_used ) { wd = MakeWord(WORD, AsciiToFull("h"), &fpos(hd)); Link(para, wd); } /* attach words showing the number of components per target */ compcount = 0; for( link = Down(opt_components(hd)); link != opt_components(hd); link = NextDown(link) ) { Child(y, link); if( type(y) != ACAT ) continue; /* let wd be a word containing the number of components in this target */ count = 0; for( ylink = Down(y); ylink != y; ylink = NextDown(ylink) ) { Child(z, ylink); if( type(z) != GAP_OBJ ) count++; } wd = MakeWord(WORD, StringInt(count), &fpos(y)); /* link wd to para, prepended by a gap if not first */ if( Down(para) != para ) { New(g, GAP_OBJ); SetGap(gap(g), FALSE, FALSE, TRUE, FIXED_UNIT, EDGE_MODE, 1*EM); if( ++compcount % 20 == 0 ) { hspace(g) = 0; vspace(g) = 1; } else { hspace(g) = 1; vspace(g) = 0; } Link(para, g); } Link(para, wd); } debug2(DOG, D, "CalculateOptimize(%s) made object %s", SymName(actual(hd)), EchoObject(og)); /* dispose the optimizing data structures */ DisposeObject(opt_components(hd)); opt_components(hd) = nilobj; DisposeObject(opt_constraints(hd)); opt_constraints(hd) = nilobj; /* write result onto cross-reference database */ if( AllowCrossDb ) { /* construct a suitable tag for this galley's entry */ StringCopy(buff, SymName(actual(hd))); StringCat(buff, AsciiToFull(".")); StringCat(buff, StringInt(line_num(fpos(hd)))); fnum = DatabaseFileNum(&fpos(hd)); AppendToFile(og, fnum, &write_pos, &write_lnum); DbInsert(NewCrossDb, FALSE, OptGallSym, buff, &fpos(hd), STR_ZERO, fnum, write_pos, write_lnum, FALSE); } debug0(DOG, D, "CalculateOptimize returning."); } #if DEBUG_ON /*****************************************************************************/ /* */ /* DebugOptimizedAcat(x) */ /* */ /* Debug output of one line of optimized ACAT. */ /* */ /*****************************************************************************/ static void DebugOptimizedAcat(OBJECT x) { OBJECT link, y; assert( type(x) == ACAT, "DebugOptimizedAcat!" ); for( link = Down(x); link != x; link = NextDown(link) ) { Child(y, link); if( type(y) == GAP_OBJ ) { debug1(DOG, D, " GAP_OBJ %s", EchoGap(&gap(y))); } else if( is_word(type(y)) ) { debug2(DOG, D, " word (%s, %s)", EchoLength(back(y, COLM)), EchoLength(fwd(y, COLM))); } else { debug1(DOG, D, " %s", Image(type(y))); } } } /* end DebugOptimizedAcat */ /*****************************************************************************/ /* */ /* DebugOptimize(hd) */ /* */ /* Debug output of optimized galley hd. */ /* */ /*****************************************************************************/ void DebugOptimize(OBJECT hd) { OBJECT link, y; assert( opt_components(hd) != nilobj, "DebugOptimize!"); debug3(DOG, D, "Optimized Galley %s %sinto %s", SymName(actual(hd)), gall_dir(hd) == COLM ? "horizontally " : "", SymName(whereto(hd))); /* print components */ /* *** believe this now *** if( type(opt_components(hd)) == ACAT ) DebugOptimizedAcat(opt_components(hd)); else if( type(opt_components(hd)) == VCAT ) { for( link = Down(opt_components(hd)); link != opt_components(hd); link = NextDown(link) ) { Child(y, link); if( type(y) == ACAT ) DebugOptimizedAcat(y); debug0(DOG, D, "----------------"); } } else debug1(DOG, D, "? %s ?", Image(type(opt_components(hd)))); *** */ debug0(DOG, D, "components:"); ifdebug(DOG, D, DebugObject(opt_components(hd))); debug0(DOG, D, ""); /* print constraints */ debug0(DOG, D, "constraints:"); for( link = Down(opt_constraints(hd)); link != opt_constraints(hd); link = NextDown(link) ) { Child(y, link); debug1(DOG, D, "%s", EchoConstraint(&constraint(y))); } debug0(DOG, D, ""); /* print counts */ debug0(DOG, D, "counts"); if( opt_counts(hd) != nilobj ) { if( opt_hyph(hd) ) fprintf(stderr, "hyph"); for( link = Down(opt_counts(hd)); link != opt_counts(hd); link = NextDown(link) ) { Child(y, link); fprintf(stderr, " %d", comp_count(y)); } fprintf(stderr, "\n"); } debug0(DOG, D, ""); } /* end DebugOptimize */ #endif