| /*@z46.c:Optimal Galleys:FindOptimize()@**************************************/ |
| /* */ |
| /* 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: 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 |