1 /*@z46.c:Optimal Galleys:FindOptimize()@**************************************/
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:         z46.c                                                      */
26 /*  MODULE:       Optimal Galleys                                            */
27 /*  EXTERNS:      FindOptimize(), SetOptimize(), GazumpOptimize(),           */
28 /*                CalculateOptimize(), DebugOptimize()                       */
29 /*                                                                           */
30 /*****************************************************************************/
31 #include "externs.h"
32 
33 
34 /*****************************************************************************/
35 /*                                                                           */
36 /*  BOOLEAN FindOptimize(x, env)                                             */
37 /*                                                                           */
38 /*  Object x is a CLOSURE which represents an at present unsized galley.     */
39 /*  Return TRUE if x has an @Optimize parameter which is Yes.                */
40 /*                                                                           */
41 /*****************************************************************************/
42 
FindOptimize(OBJECT x,OBJECT env)43 BOOLEAN FindOptimize(OBJECT x, OBJECT env)
44 { OBJECT y, link, res;
45   OBJECT bt[2], ft[2], ntarget, nenclose, crs;
46   debug1(DOG, D, "FindOptimize( %s )", EchoObject(x));
47   assert( type(x) == CLOSURE, "FindOptimize: type(x) != CLOSURE!" );
48   assert( has_target(actual(x)), "FindOptimize: x has no target!" );
49 
50   /* search the parameter list of x for @Optimize */
51   res = nilobj;
52   for( link = Down(x);  link != x;  link = NextDown(link) )
53   { Child(y, link);
54     if( type(y) == PAR && is_optimize(actual(y)) )
55     { assert( Down(y) != y, "FindOptimize: Down(PAR)!" );
56       Child(res, Down(y));
57       res = CopyObject(res, &fpos(x));
58       break;
59     }
60   }
61 
62   /* search the children list of actual(x) for a default value of @Target */
63   if( res == nilobj )
64   for( link = Down(actual(x));  link != actual(x);  link = NextDown(link) )
65   { Child(y, link);
66     if( is_optimize(y) )
67     { res = CopyObject(sym_body(y), &fpos(x));
68       break;
69     }
70   }
71 
72   /* should have found it by now */
73   assert( res != nilobj, "FindOptimize: res == nilobj!" );
74 
75   /* manifest and tidy the parameter, return TRUE if Yes */
76   bt[COLM] = ft[COLM] = bt[ROWM] = ft[ROWM] = ntarget = nenclose = crs = nilobj;
77   res = Manifest(res, env, &save_style(x), bt, ft, &ntarget, &crs, TRUE, FALSE,
78     &nenclose, FALSE);
79   res = ReplaceWithTidy(res, WORD_TIDY);
80   if( !is_word(type(res)) )
81   { Error(46, 1, "unable to evaluate %s parameter, assuming value is No",
82       WARN, &fpos(x), KW_OPTIMIZE);
83     debug2(DOG, D, "FindOptimize returning FALSE; found %s %s",
84       Image(type(res)), EchoObject(res));
85     return FALSE;
86   }
87   else if( StringEqual(string(res), AsciiToFull("Yes")) )
88   { debug0(DOG, D, "FindOptimize returning TRUE");
89     return TRUE;
90   }
91   else if( StringEqual(string(res), AsciiToFull("No")) )
92   { debug0(DOG, D, "FindOptimize returning FALSE");
93     return FALSE;
94   }
95   else
96   { Error(46, 2, "value of %s operator is neither Yes nor No, assuming No",
97       WARN, &fpos(x), KW_OPTIMIZE);
98     debug1(DOG, D, "FindOptimize returning FALSE (found WORD %s)", string(res));
99     return FALSE;
100   }
101 } /* end FindOptimize */
102 
103 
104 /*****************************************************************************/
105 /*                                                                           */
106 /*  SetOptimize(hd, style)                                                   */
107 /*                                                                           */
108 /*  Initialize the optimization data of galley hd.  Search the cross ref     */
109 /*  database for information about its fate on the previous run.             */
110 /*                                                                           */
111 /*****************************************************************************/
112 
SetOptimize(OBJECT hd,STYLE * style)113 void SetOptimize(OBJECT hd, STYLE *style)
114 { FULL_CHAR buff[MAX_BUFF], seq[MAX_BUFF];
115   OBJECT res, y, link, z;  FILE_NUM dfnum;  long dfpos, cont;  int dlnum;
116   debug2(DOG, D, "SetOptimize(%s, %s)", SymName(actual(hd)), EchoStyle(style));
117 
118   /* set opt_counts(hd) to result of previous run, if any */
119   StringCopy(buff, SymName(actual(hd)));
120   StringCat(buff, AsciiToFull("."));
121   StringCat(buff, StringInt(line_num(fpos(hd))));
122   if( DbRetrieve(OldCrossDb, FALSE, OptGallSym, buff, seq, &dfnum,
123     &dfpos, &dlnum, &cont) )
124   {
125     SwitchScope(nilobj);
126     res = ReadFromFile(dfnum, dfpos, dlnum);
127     UnSwitchScope(nilobj);
128     assert( res != nilobj, "SetOptimize: res == nilobj!" );
129     assert( type(res) == CLOSURE, "SetOptimize: type(res) != CLOSURE!" );
130     assert( actual(res) == OptGallSym, "SetOptimize: actual(res) != Opt!" );
131     assert( Down(res) != res, "SetOptimize: Down(res) == res!" );
132     Child(y, Down(res));
133     assert( type(y) == PAR, "SetOptimize: type(y) != PAR!" );
134     Child(y, Down(y));
135     assert( type(y) == ACAT, "SetOptimize: type(y) != ACAT!" );
136     y = ReplaceWithTidy(y, ACAT_TIDY);
137     opt_hyph(hd) = FALSE;
138     assert( type(y) == ACAT, "SetOptimize: type(y) != ACAT (2)!" );
139     for( link = y;  NextDown(link) != y;  link = NextDown(link) )
140     { Child(z, NextDown(link));
141       if( type(z) == GAP_OBJ )
142       { DisposeChild(NextDown(link));
143 	link = PrevDown(link);
144       }
145       else if( is_word(type(z)) )
146       { if( StringEqual(string(z), AsciiToFull("h")) )
147 	{ opt_hyph(hd) = TRUE;
148 	  DisposeChild(NextDown(link));
149 	  link = PrevDown(link);
150 	}
151 	else
152 	{ int num = 0;
153 	  sscanf( (char *) string(z), "%d", &num);
154 	  assert( num > 0, "SetOptimize: num <= 0!" );
155 	  comp_count(z) = num;
156 	}
157       }
158       else
159       { assert( FALSE, "SetOptimize: type(z)!" );
160       }
161     }
162     DeleteLink(Up(y));
163     DisposeObject(res);
164     opt_counts(hd) = y;
165   }
166   else opt_counts(hd) = nilobj;
167 
168   /* set up first opt_comps_permitted value */
169   if( opt_counts(hd) != nilobj && Down(opt_counts(hd)) != opt_counts(hd) )
170   { Child(z, Down(opt_counts(hd)));
171     opt_comps_permitted(hd) = comp_count(z) - 1;
172     DisposeChild(Up(z));
173   }
174   else opt_comps_permitted(hd) = MAX_FILES;  /* a large number */
175   debug1(DOG, D, "  initial permitted = %2d", opt_comps_permitted(hd));
176 
177   /* set opt_components(hd) and opt_constraints(hd) for storing this run */
178   New(opt_components(hd), ACAT);
179   opt_gazumped(hd) = FALSE;
180   New(opt_constraints(hd), ACAT);
181   StyleCopy(save_style(opt_components(hd)), *style);
182   if( gall_dir(hd) == ROWM )
183   {
184     hyph_style(save_style(opt_components(hd))) = HYPH_OFF;
185     marginkerning(save_style(opt_components(hd))) = FALSE;
186   }
187 
188   debug0(DOG, D, "SetOptimize returning:");
189   ifdebug(DOG, D, DebugOptimize(hd));
190 } /* end SetOptimize */
191 
192 
193 /*****************************************************************************/
194 /*                                                                           */
195 /*  GazumpOptimize(hd, dest)                                                 */
196 /*                                                                           */
197 /*  Optimizing galley hd, currently attached to @Galley dest, is to be       */
198 /*  gazumped by some other galley.  Record the current size constraint and   */
199 /*  add &1rt {} to the list of components.                                   */
200 /*                                                                           */
201 /*****************************************************************************/
202 
GazumpOptimize(OBJECT hd,OBJECT dest)203 void GazumpOptimize(OBJECT hd, OBJECT dest)
204 { OBJECT g, tmp, junk, prnt;
205 
206   debug2(DOG, D, "GazumpOptimize(%s, %s)", SymName(actual(hd)),
207     EchoObject(dest));
208   assert( type(hd) == HEAD, "GazumpOptimize: type(hd) != HEAD!" );
209   assert( opt_components(hd) != nilobj, "GazumpOptimize: opt_c!" );
210 
211   /* record the size of this just-completed target area for hd */
212   New(tmp, WIDE);
213   if( (gall_dir(hd) == COLM && external_hor(dest)) ||
214       (gall_dir(hd) == COLM && external_hor(dest)) )
215   { SetConstraint(constraint(tmp), MAX_FULL_LENGTH, MAX_FULL_LENGTH, MAX_FULL_LENGTH);
216   }
217   else
218   { Parent(prnt, Up(dest));
219     Constrained(prnt, &constraint(tmp), gall_dir(hd), &junk);
220   }
221   Link(opt_constraints(hd), tmp);
222   debug2(DOG, D, "GazumpOptimize(%s) adding constraint %s",
223     SymName(actual(hd)), EchoConstraint(&constraint(tmp)));
224 
225   /* optimizing galley is being gazumped; record this as &1rt {} &1c */
226   if( LastDown(opt_components(hd)) != opt_components(hd) )
227   { Child(g, LastDown(opt_components(hd)));
228     assert( type(g) == GAP_OBJ, "FlushGalley: type(g) != GAP_OBJ!" );
229 
230     /* ***
231     SetGap(gap(g), FALSE, FALSE, TRUE, FRAME_UNIT, EDGE_MODE, 2 * FR);
232     if( Down(g) == g )
233     { junk = MakeWord(WORD, AsciiToFull("2b"), &fpos(g));
234       Link(g, junk);
235     }
236     *** */
237 
238     /* first we overwrite whatever is there now by &1rt */
239     SetGap(gap(g), FALSE, FALSE, TRUE, AVAIL_UNIT, TAB_MODE, 1 * FR);
240     if( Down(g) != g )  DisposeChild(Down(g));
241     tmp = MakeWord(WORD, AsciiToFull("1rt"), &fpos(g));
242     Link(g, tmp);
243 
244     /* next we add an empty word */
245     tmp = MakeWord(WORD, STR_EMPTY, &fpos(g));
246     back(tmp, COLM) = fwd(tmp, COLM) = 0;
247     back(tmp, ROWM) = fwd(tmp, ROWM) = 0;
248     word_font(tmp) = word_colour(tmp) = 0;
249     word_underline_colour(tmp) = 0;
250     word_texture(tmp) = 1;
251     word_outline(tmp) = FALSE;
252     word_language(tmp) = word_hyph(tmp) = 0;
253     word_baselinemark(tmp) = FALSE;
254     word_strut(tmp) = FALSE;
255     word_ligatures(tmp) = TRUE;
256     Link(opt_components(hd), tmp);
257 
258     /* finally we add &1c */
259     New(g, GAP_OBJ);
260     hspace(g) = 1;  vspace(g) = 0;
261     FposCopy(fpos(g), fpos(tmp));
262     SetGap(gap(g), FALSE, FALSE, TRUE, FIXED_UNIT, EDGE_MODE, 1 * CM);
263     tmp = MakeWord(WORD, AsciiToFull("1c"), &fpos(g));
264     Link(g, tmp);
265     Link(opt_components(hd), g);
266 
267     opt_gazumped(hd) = TRUE;
268     debug2(DOG, D, "GazumpOptimize(%s) new gap is %s",
269       SymName(actual(hd)), EchoGap(&gap(g)));
270   }
271 
272   /* refresh the number of comps permitted into the next target */
273   if( opt_counts(hd) != nilobj && Down(opt_counts(hd)) != opt_counts(hd) )
274   { Child(tmp, Down(opt_counts(hd)));
275     opt_comps_permitted(hd) += comp_count(tmp) - 1;
276     DisposeChild(Up(tmp));
277   }
278   else opt_comps_permitted(hd) = MAX_FILES;
279 
280   debug1(DOG, D, "GazumpOptimize returning, permitted = %2d",
281     opt_comps_permitted(hd));
282 } /* end GazumpOptimize */
283 
284 
285 /*****************************************************************************/
286 /*                                                                           */
287 /*  CalculateOptimize(hd)                                                    */
288 /*                                                                           */
289 /*  Calculate the optimal break for galley hd and write the result into      */
290 /*  the cross reference database.                                            */
291 /*                                                                           */
292 /*****************************************************************************/
293 
CalculateOptimize(OBJECT hd)294 void CalculateOptimize(OBJECT hd)
295 { OBJECT z, y, ylink, og, og_par, para, link, wd, g, last;
296   int count, compcount;  FULL_CHAR buff[MAX_BUFF];
297   FILE_NUM fnum;  int write_pos, write_lnum;  BOOLEAN hyph_used;
298   debug1(DOG, D, "CalculateOptimize(%s)", SymName(actual(hd)));
299 
300   /* delete the concluding GAP_OBJ stuck in by Promote() */
301   assert( LastDown(opt_components(hd)) != opt_components(hd), "CO!" );
302   Child(last, LastDown(opt_components(hd)));
303   assert( type(last) == GAP_OBJ, "CalculateOptimize: type(last)!" );
304   DisposeChild(Up(last));
305   ifdebug(DOG, D, DebugOptimize(hd));
306 
307   /* break the paragraph; don't let user see any error messages */
308   assert( opt_constraints(hd) != nilobj, "KillGalley: no opt_constraints!" );
309   assert( Down(opt_constraints(hd)) != opt_constraints(hd), "KillGalleyo!" );
310   /* *** no longer needed since z14 doesn't refer to these fields
311   back(opt_components(hd), COLM) = 0;
312   fwd(opt_components(hd), COLM) = MAX_FULL_LENGTH;
313   *** */
314   Child(y, LastDown(opt_constraints(hd)));
315   EnterErrorBlock(FALSE);
316   opt_components(hd) = FillObject(opt_components(hd), &constraint(y),
317     opt_constraints(hd), FALSE, FALSE, TRUE, &hyph_used);
318   LeaveErrorBlock(FALSE);
319   debug1(DOG, D, "after breaking (%shyph_used):", hyph_used ? "" : "not ");
320   ifdebug(DOG, D, DebugOptimize(hd));
321 
322   /* quit if one line only */
323   if( type(opt_components(hd)) != VCAT ||
324       Down(opt_components(hd)) == LastDown(opt_components(hd)) )
325   {
326     debug0(DOG, D, "CalculateOptimize returning (one target only)");
327     return;
328   }
329 
330   /* construct a new @OptGall symbol */
331   New(og, CLOSURE);
332   actual(og) = OptGallSym;
333   FposCopy(fpos(og), fpos(hd));
334   New(og_par, PAR);
335   actual(og_par) = ChildSym(OptGallSym, RPAR);
336   Link(og, og_par);
337   New(para, ACAT);
338   Link(og_par, para);
339 
340   /* begin with "h" if hyphenation was used */
341   if( hyph_used )
342   { wd = MakeWord(WORD, AsciiToFull("h"), &fpos(hd));
343     Link(para, wd);
344   }
345 
346   /* attach words showing the number of components per target */
347   compcount = 0;
348   for( link = Down(opt_components(hd));  link != opt_components(hd);
349        link = NextDown(link) )
350   { Child(y, link);
351     if( type(y) != ACAT )  continue;
352 
353     /* let wd be a word containing the number of components in this target */
354     count = 0;
355     for( ylink = Down(y);  ylink != y;  ylink = NextDown(ylink) )
356     { Child(z, ylink);
357       if( type(z) != GAP_OBJ ) count++;
358     }
359     wd = MakeWord(WORD, StringInt(count), &fpos(y));
360 
361     /* link wd to para, prepended by a gap if not first */
362     if( Down(para) != para )
363     { New(g, GAP_OBJ);
364       SetGap(gap(g), FALSE, FALSE, TRUE, FIXED_UNIT, EDGE_MODE, 1*EM);
365       if( ++compcount % 20 == 0 )
366       { hspace(g) = 0;
367 	vspace(g) = 1;
368       }
369       else
370       { hspace(g) = 1;
371         vspace(g) = 0;
372       }
373       Link(para, g);
374     }
375     Link(para, wd);
376   }
377   debug2(DOG, D, "CalculateOptimize(%s) made object %s",
378     SymName(actual(hd)), EchoObject(og));
379 
380   /* dispose the optimizing data structures */
381   DisposeObject(opt_components(hd));
382   opt_components(hd) = nilobj;
383   DisposeObject(opt_constraints(hd));
384   opt_constraints(hd) = nilobj;
385 
386   /* write result onto cross-reference database */
387   if( AllowCrossDb )
388   {
389     /* construct a suitable tag for this galley's entry */
390     StringCopy(buff, SymName(actual(hd)));
391     StringCat(buff, AsciiToFull("."));
392     StringCat(buff, StringInt(line_num(fpos(hd))));
393     fnum = DatabaseFileNum(&fpos(hd));
394     AppendToFile(og, fnum, &write_pos, &write_lnum);
395     DbInsert(NewCrossDb, FALSE, OptGallSym, buff, &fpos(hd),
396       STR_ZERO, fnum, write_pos, write_lnum, FALSE);
397   }
398   debug0(DOG, D, "CalculateOptimize returning.");
399 }
400 
401 #if DEBUG_ON
402 /*****************************************************************************/
403 /*                                                                           */
404 /*  DebugOptimizedAcat(x)                                                    */
405 /*                                                                           */
406 /*  Debug output of one line of optimized ACAT.                              */
407 /*                                                                           */
408 /*****************************************************************************/
409 
DebugOptimizedAcat(OBJECT x)410 static void DebugOptimizedAcat(OBJECT x)
411 { OBJECT link, y;
412   assert( type(x) == ACAT, "DebugOptimizedAcat!" );
413   for( link = Down(x);  link != x;  link = NextDown(link) )
414   { Child(y, link);
415     if( type(y) == GAP_OBJ )
416     { debug1(DOG, D, "  GAP_OBJ %s", EchoGap(&gap(y)));
417     }
418     else if( is_word(type(y)) )
419     { debug2(DOG, D, "  word (%s, %s)", EchoLength(back(y, COLM)),
420 	EchoLength(fwd(y, COLM)));
421     }
422     else
423     { debug1(DOG, D, "  %s", Image(type(y)));
424     }
425   }
426 } /* end DebugOptimizedAcat */
427 
428 
429 /*****************************************************************************/
430 /*                                                                           */
431 /*  DebugOptimize(hd)                                                        */
432 /*                                                                           */
433 /*  Debug output of optimized galley hd.                                     */
434 /*                                                                           */
435 /*****************************************************************************/
436 
DebugOptimize(OBJECT hd)437 void DebugOptimize(OBJECT hd)
438 { OBJECT link, y;
439 
440   assert( opt_components(hd) != nilobj, "DebugOptimize!");
441   debug3(DOG, D, "Optimized Galley %s %sinto %s", SymName(actual(hd)),
442     gall_dir(hd) == COLM ? "horizontally " : "", SymName(whereto(hd)));
443 
444   /* print components */
445   /* *** believe this now ***
446   if( type(opt_components(hd)) == ACAT )
447     DebugOptimizedAcat(opt_components(hd));
448   else if( type(opt_components(hd)) == VCAT )
449   {
450     for( link = Down(opt_components(hd));  link != opt_components(hd);
451 	 link = NextDown(link) )
452     {
453       Child(y, link);
454       if( type(y) == ACAT )  DebugOptimizedAcat(y);
455       debug0(DOG, D, "----------------");
456     }
457   }
458   else debug1(DOG, D, "? %s ?", Image(type(opt_components(hd))));
459   *** */
460   debug0(DOG, D, "components:");
461   ifdebug(DOG, D, DebugObject(opt_components(hd)));
462   debug0(DOG, D, "");
463 
464   /* print constraints */
465   debug0(DOG, D, "constraints:");
466   for( link = Down(opt_constraints(hd));  link != opt_constraints(hd);
467        link = NextDown(link) )
468   {
469     Child(y, link);
470     debug1(DOG, D, "%s", EchoConstraint(&constraint(y)));
471   }
472   debug0(DOG, D, "");
473 
474   /* print counts */
475   debug0(DOG, D, "counts");
476   if( opt_counts(hd) != nilobj )
477   {
478     if( opt_hyph(hd) )
479       fprintf(stderr, "hyph");
480     for( link = Down(opt_counts(hd));  link != opt_counts(hd);
481 	 link = NextDown(link) )
482     { Child(y, link);
483       fprintf(stderr, " %d", comp_count(y));
484     }
485     fprintf(stderr, "%s", STR_NEWLINE);
486   }
487   debug0(DOG, D, "");
488 } /* end DebugOptimize */
489 #endif
490