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