1 /*@z13.c:Object Breaking:BreakJoinedGroup()@**********************************/
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: z13.c */
26 /* MODULE: Object Breaking */
27 /* EXTERNS: BreakObject() */
28 /* */
29 /*****************************************************************************/
30 #include "externs.h"
31 #define broken(x) back(x, ROWM) /* OK since no vertical sizes yet */
32
33 #if DEBUG_ON
34 static int debug_depth = 1;
35 static int debug_depth_max = 5;
36 #endif
37
38
39 /*****************************************************************************/
40 /* */
41 /* static BreakJoinedGroup(start, stop, m, c, res_back, res_fwd) */
42 /* */
43 /* Break joined group of components of a VCAT, beginning from Child(start) */
44 /* inclusive and ending at Child(stop) inclusive. Break component m first */
45 /* because it is the widest. */
46 /* */
47 /*****************************************************************************/
48
BreakJoinedGroup(OBJECT start,OBJECT stop,OBJECT m,CONSTRAINT * c,FULL_LENGTH * res_back,FULL_LENGTH * res_fwd)49 static void BreakJoinedGroup(OBJECT start, OBJECT stop, OBJECT m,
50 CONSTRAINT *c, FULL_LENGTH *res_back, FULL_LENGTH *res_fwd)
51 { OBJECT y = nilobj, link; FULL_LENGTH b, f, sb, sf; CONSTRAINT yc;
52 debug1(DOB, DD, "[ BreakJoinedGroup(start, stop, m, %s, -, -)",
53 EchoConstraint(c));
54
55 /* work out a suitable constraint to apply to each component */
56 sb = sf = 0;
57 for( link = start; link != NextDown(stop); link = NextDown(link) )
58 { Child(y, link);
59 if( !is_definite(type(y)) ) continue;
60 sb = find_max(sb, back(y, COLM));
61 sf = find_max(sf, fwd(y, COLM));
62 }
63 if( sb <= bc(*c) )
64 {
65 /* make sure the constraint will accept objects with size (sb, 0) */
66 b = sb;
67 f = 0;
68 }
69 else
70 {
71 /* sb is too wide anyway, so don't worry about it */
72 b = 0;
73 f = 0;
74 }
75 SetConstraint(yc, find_min(bc(*c), bfc(*c)-f), bfc(*c), find_min(fc(*c), bfc(*c)-b));
76
77 /* apply this constraint to each component in turn, m first */
78 if( m != nilobj )
79 {
80 debug1(DOB, DD, " +++BreakJoinedGroup calling first child, yc = %s",
81 EchoConstraint(&yc));
82 m = BreakObject(m, &yc);
83 b = back(m, COLM);
84 f = fwd(m, COLM);
85 SetConstraint(yc, find_min(bc(yc), bfc(yc)-f), bfc(yc), find_min(fc(yc), bfc(yc)-b));
86 }
87 else b = f = 0;
88 for( link = start; link != NextDown(stop); link = NextDown(link) )
89 { Child(y, link);
90 if( !is_definite(type(y)) || y == m ) continue;
91 debug1(DOB, DD, " +++BreakJoinedGroup calling child, yc = %s",
92 EchoConstraint(&yc));
93 y = BreakObject(y, &yc);
94 b = find_max(b, back(y, COLM));
95 f = find_max(f, fwd(y, COLM));
96 SetConstraint(yc, find_min(bc(yc), bfc(yc)-f), bfc(yc), find_min(fc(yc), bfc(yc)-b));
97 }
98 if( !FitsConstraint(b, f, *c) )
99 { debug3(DOB, DD, " in BreakJoinedGroup: !FitsConstraint(%s, %s, %s)",
100 EchoLength(b), EchoLength(f), EchoConstraint(c));
101 Error(13, 1, "failed to break column to fit into its available space",
102 WARN, m != nilobj ? &fpos(m) : (y != nilobj ? &fpos(y) : no_fpos));
103 }
104 *res_back = b; *res_fwd = f;
105 debug2(DOB, DD,"] BreakJoinedGroup returning (%s, %s)",
106 EchoLength(b), EchoLength(f));
107 } /* end BreakJoinedGroup */
108
109
110 /*@::BreakVcat()@*************************************************************/
111 /* */
112 /* static OBJECT BreakVcat(x, c) */
113 /* */
114 /* Break a VCAT to satisfy constraint c. This is tedious because every */
115 /* group of components between // ... // must be broken separately. */
116 /* */
117 /*****************************************************************************/
118
BreakVcat(OBJECT x,CONSTRAINT * c)119 static OBJECT BreakVcat(OBJECT x, CONSTRAINT *c)
120 { OBJECT y, link, start_group, m = nilobj;
121 FULL_LENGTH b, f, dble_fwd; CONSTRAINT tc;
122 BOOLEAN dble_found;
123 debug1(DOB, DD, "[ BreakVcat(x, %s)", EchoConstraint(c));
124 assert(Down(x) != x, "BreakVcat: Down(x) == x!" );
125 SetConstraint(tc, MAX_FULL_LENGTH, find_min(bfc(*c), fc(*c)), MAX_FULL_LENGTH);
126
127 dble_found = FALSE; dble_fwd = 0; start_group = nilobj;
128 for( link = Down(x); link != x; link = NextDown(link) )
129 { Child(y, link);
130 if( is_index(type(y)) ) continue;
131 if( type(y) == GAP_OBJ )
132 { assert( start_group != nilobj, "BreakVcat: start_group == nilobj!" );
133 if( !join(gap(y)) )
134 {
135 /* finish off and break this group */
136 if( !FitsConstraint(b, f, tc) )
137 BreakJoinedGroup(start_group, link, m, &tc, &b, &f);
138 dble_found = TRUE;
139 dble_fwd = find_max(dble_fwd, b + f);
140 start_group = nilobj;
141 debug1(DOB, DD, " end group, dble_fwd: %s", EchoLength(dble_fwd));
142 }
143 }
144 else if( start_group == nilobj )
145 {
146 /* start new group */
147 b = back(y, COLM); f = fwd(y, COLM);
148 start_group = link; m = y;
149 debug2(DOB, DD, " starting group (b = %s, f = %s):",
150 EchoLength(b), EchoLength(f));
151 ifdebug(DOB, DD, DebugObject(y));
152 }
153 else
154 {
155 /* continue with current group */
156 b = find_max(b, back(y, COLM)); f = find_max(f, fwd(y, COLM));
157 if( fwd(y, COLM) > fwd(m, COLM) ) m = y;
158 debug3(DOB, DD, " in group%s (b = %s, f = %s):",
159 m == y ? " (new max)" : "",
160 EchoLength(b), EchoLength(f));
161 ifdebug(DOB, DD, DebugObject(y));
162 }
163 }
164 assert( start_group != nilobj, "BreakVcat: start_group == nilobj (2)!" );
165
166 if( dble_found )
167 {
168 /* finish off and break this last group, and set sizes of x */
169 if( !FitsConstraint(b, f, tc) )
170 BreakJoinedGroup(start_group, LastDown(x), m, &tc, &b, &f);
171 dble_fwd = find_max(dble_fwd, b + f);
172 debug1(DOB, DD, " ending last group, dble_fwd: %s",EchoLength(dble_fwd));
173 back(x, COLM) = 0; fwd(x, COLM) = find_min(MAX_FULL_LENGTH, dble_fwd);
174 }
175 else
176 {
177 /* finish off and break this last and only group, and set sizes of x */
178 debug2(DOB, DD, " BreakVcat ending last and only group (%s, %s)",
179 EchoLength(b), EchoLength(f));
180 BreakJoinedGroup(start_group, LastDown(x), m, c, &b, &f);
181 back(x, COLM) = b; fwd(x, COLM) = f;
182 }
183
184 debug0(DOB, DD, "] BreakVcat returning x:");
185 ifdebug(DOB, DD, DebugObject(x));
186 debug2(DOB, DD, " (size is %s, %s)",
187 EchoLength(back(x, COLM)), EchoLength(fwd(x, COLM)));
188 return x;
189 } /* end BreakVcat */
190
191
192 /*@::BreakTable()@************************************************************/
193 /* */
194 /* static OBJECT BreakTable(x, c) */
195 /* */
196 /* Break table (HCAT) x to satisfy constraint c. */
197 /* */
198 /* Outline of algorithm: */
199 /* */
200 /* bcount = number of components to left of mark; */
201 /* fcount = no. of components on and right of mark; */
202 /* bwidth = what back(x) would be if all components had size (0, 0); */
203 /* fwidth = what fwd(x) would be if all components had size (0, 0); */
204 /* Set all components of x to Unbroken (broken(y) holds this flag); */
205 /* while( an Unbroken component of x exists ) */
206 /* { my = the Unbroken component of x of minimum width; */
207 /* mc = desirable constraint for my (see below); */
208 /* BreakObject(my, &mc); */
209 /* Set my to Broken and update bcount, fcount, bwidth, fwidth */
210 /* to reflect the actual size of my, now broken; */
211 /* } */
212 /* */
213 /* The constraint mc is chosen in an attempt to ensure that: */
214 /* */
215 /* a) Any sufficiently narrow components will not break; */
216 /* b) All broken components will have the same bfc(mc), if possible; */
217 /* c) All available space is used. */
218 /* */
219 /*****************************************************************************/
220
BreakTable(OBJECT x,CONSTRAINT * c)221 static OBJECT BreakTable(OBJECT x, CONSTRAINT *c)
222 { FULL_LENGTH bwidth, fwidth; /* running back(x) and fwd(x) */
223 int bcount, fcount; /* running no. of components */
224 OBJECT mlink = nilobj, my; /* minimum-width unbroken component */
225 BOOLEAN ratm = FALSE; /* TRUE when my has a mark to its right */
226 int mside; /* side of the mark my is on: BACK, ON, FWD */
227 FULL_LENGTH msize; /* size of my (minimal among unbroken) */
228 CONSTRAINT mc; /* desirable constraint for my */
229 OBJECT pg, prec_def; /* preceding definite object of my */
230 OBJECT sg, succ_def; /* succeeding definite object of my */
231 FULL_LENGTH pd_extra,sd_extra;/* space availiable for free each side of my */
232 FULL_LENGTH av_colsize; /* the size of each unbroken component */
233 /* if they are all assigned equal width */
234 FULL_LENGTH fwd_max, back_max;/* maximum space available forward of or */
235 /* back of the mark, when columns are even */
236 FULL_LENGTH col_size = 0; /* the column size actually used in breaking */
237 FULL_LENGTH prev_col_size; /* previous column size (try to keep equal) */
238 FULL_LENGTH beffect, feffect; /* the amount bwidth, fwidth must increase */
239 /* when my is broken */
240 OBJECT link, y, prev, g; FULL_LENGTH tmp, tmp2;
241
242 debug1(DOB, DD, "[ BreakTable( x, %s )", EchoConstraint(c));
243
244 /* Initialise csize, bcount, fcount, bwidth, fwidth and broken(y) */
245 bcount = fcount = 0; bwidth = fwidth = 0; prev = nilobj;
246 prev_col_size = 0;
247 Child(y, Down(x));
248 assert( type(y) != GAP_OBJ, "BreakTable: GAP_OBJ!" );
249 assert( !is_index(type(y)), "BreakTable: index!" );
250 broken(y) = is_indefinite(type(y));
251 if( !broken(y) ) prev = y, fcount = 1;
252
253 for( link = NextDown(Down(x)); link != x; link = NextDown(NextDown(link)) )
254 {
255 /* find the next gap g and following child y */
256 Child(g, link);
257 assert( type(g) == GAP_OBJ, "BreakTable: GAP_OBJ!" );
258 assert( NextDown(link) != x, "BreakTable: GAP_OBJ is last!" );
259 Child(y, NextDown(link));
260
261 assert( type(y) != GAP_OBJ, "BreakTable: GAP_OBJ!" );
262 assert( !is_index(type(y)), "BreakTable: index!" );
263 broken(y) = is_indefinite(type(y));
264 if( !broken(y) )
265 { if( prev == nilobj ) fcount = 1;
266 else if( mark(gap(g)) )
267 { bcount += fcount;
268 bwidth += fwidth + MinGap(0, 0, 0, &gap(g));
269 fcount = 1; fwidth = 0;
270 }
271 else
272 { fwidth += MinGap(0, 0, 0, &gap(g));
273 fcount += 1;
274 }
275 prev = y;
276 }
277 }
278
279 /* if column gaps alone are too wide, kill them all */
280 if( !FitsConstraint(bwidth, fwidth, *c) )
281 {
282 debug2(DOB, DD, "column gaps alone too wide: bwidth: %s; fwidth: %s",
283 EchoLength(bwidth), EchoLength(fwidth));
284 Error(13, 2, "reducing column gaps to 0i (object is too wide)",
285 WARN, &fpos(x));
286 for( link = Down(x); link != x; link = NextDown(link) )
287 { Child(g, link);
288 if( type(g) == GAP_OBJ )
289 { SetGap(gap(g), nobreak(gap(g)), mark(gap(g)), join(gap(g)),
290 FIXED_UNIT, EDGE_MODE, 0);
291 }
292 }
293 bwidth = fwidth = 0;
294 }
295
296 /* break each column, from smallest to largest */
297 while( bcount + fcount > 0 && FitsConstraint(bwidth, fwidth, *c) )
298 {
299 debug2(DOB, DD, "bcount: %d; bwidth: %s", bcount, EchoLength(bwidth));
300 debug2(DOB, DD, "fcount: %d; fwidth: %s", fcount, EchoLength(fwidth));
301
302 /* find a minimal-width unbroken component my */
303 my = nilobj; msize = size(x, COLM); /* an upper bound for size(y) */
304 for( link = Down(x); ; link = NextDown(link) )
305 { Child(y, link);
306 assert( type(y) != GAP_OBJ, "BreakTable: type(y) == GAP_OBJ!" );
307 if( !broken(y) && (size(y, COLM) < msize || my == nilobj) )
308 { msize = size(y, COLM);
309 my = y; mlink = link;
310 ratm = FALSE;
311 }
312
313 /* next gap */
314 link = NextDown(link);
315 if( link == x ) break;
316 Child(g, link);
317 assert( type(g) == GAP_OBJ, "BreakTable: type(g) != GAP_OBJ!" );
318 if( mark(gap(g)) ) ratm = TRUE;
319 }
320
321 /* find neighbouring definite objects and resulting pd_extra and sd_extra */
322 SetNeighbours(mlink, ratm, &pg, &prec_def, &sg, &succ_def, &mside);
323 debug2(DOB, DD, "my (%s): %s", Image(mside), EchoObject(my));
324 pd_extra = pg == nilobj ? 0 :
325 ExtraGap(broken(prec_def) ? fwd(prec_def,COLM) : 0, 0, &gap(pg), BACK);
326 sd_extra = sg == nilobj ? 0 :
327 ExtraGap(0, broken(succ_def) ? back(succ_def,COLM) : 0, &gap(sg), FWD);
328 debug2(DOB, DD, "pd_extra: %s; sd_extra: %s",
329 EchoLength(pd_extra), EchoLength(sd_extra) );
330
331 /* calculate desirable constraints for my */
332 av_colsize = (bfc(*c) - bwidth - fwidth) / (bcount + fcount);
333 debug1(DOB, DD, "av_colsize = %s", EchoLength(av_colsize));
334 debug1(DOB, DD, "prev_col_size = %s", EchoLength(prev_col_size));
335 switch( mside )
336 {
337
338 case BACK:
339
340 back_max = find_min(bc(*c), bwidth + av_colsize * bcount);
341 col_size = (back_max - bwidth) / bcount;
342 if( col_size > prev_col_size && col_size - prev_col_size < PT )
343 col_size = prev_col_size;
344 SetConstraint(mc,
345 find_min(MAX_FULL_LENGTH, col_size + pd_extra),
346 find_min(MAX_FULL_LENGTH, col_size + pd_extra + sd_extra),
347 find_min(MAX_FULL_LENGTH, col_size + sd_extra));
348 break;
349
350
351 case ON:
352
353 fwd_max = find_min(fc(*c), fwidth + av_colsize * fcount);
354 col_size = (fwd_max - fwidth) / fcount;
355 if( col_size > prev_col_size && col_size - prev_col_size < PT )
356 col_size = prev_col_size;
357 SetConstraint(mc,
358 find_min(MAX_FULL_LENGTH, pd_extra + back(my, COLM)),
359 find_min(MAX_FULL_LENGTH, pd_extra + back(my, COLM) + col_size + sd_extra),
360 find_min(MAX_FULL_LENGTH, col_size + sd_extra));
361 break;
362
363
364 case FWD:
365
366 fwd_max = find_min(fc(*c), fwidth + av_colsize * fcount);
367 col_size = (fwd_max - fwidth) / fcount;
368 if( col_size > prev_col_size && col_size - prev_col_size < PT )
369 col_size = prev_col_size;
370 SetConstraint(mc,
371 find_min(MAX_FULL_LENGTH, col_size + pd_extra),
372 find_min(MAX_FULL_LENGTH, col_size + pd_extra + sd_extra),
373 find_min(MAX_FULL_LENGTH, col_size + sd_extra));
374 break;
375
376
377 default:
378
379 assert(FALSE, "BreakTable: mside");
380 break;
381 }
382 debug1(DOB, DD, "col_size = %s", EchoLength(col_size));
383 prev_col_size = col_size;
384
385 /* now break my according to these constraints, and accept it */
386 debug2(DOB, DD, " calling BreakObject(%s, %s)", EchoObject(my),
387 EchoConstraint(&mc));
388 my = BreakObject(my, &mc); broken(my) = TRUE;
389
390 /* calculate the effect of accepting my on bwidth and fwidth */
391 if( pg != nilobj )
392 { tmp = broken(prec_def) ? fwd(prec_def, COLM) : 0;
393 beffect = MinGap(tmp, back(my, COLM), fwd(my, COLM), &gap(pg)) -
394 MinGap(tmp, 0, 0, &gap(pg));
395 }
396 else beffect = back(my, COLM);
397
398 if( sg != nilobj )
399 { tmp = broken(succ_def) ? back(succ_def, COLM) : 0;
400 tmp2 = broken(succ_def) ? fwd(succ_def, COLM) : 0;
401 feffect = MinGap(fwd(my, COLM), tmp, tmp2, &gap(sg)) -
402 MinGap(0, tmp, tmp2, &gap(sg));
403 }
404 else feffect = fwd(my, COLM);
405
406 switch( mside )
407 {
408 case BACK: bwidth += beffect + feffect;
409 bcount--;
410 break;
411
412 case ON: bwidth += beffect; fwidth += feffect;
413 fcount--;
414 break;
415
416 case FWD: fwidth += beffect + feffect;
417 fcount--;
418 break;
419
420 default: assert(FALSE, "BreakTable: mside");
421 break;
422 }
423
424 } /* end while */
425
426 back(x, COLM) = bwidth;
427 fwd(x, COLM) = fwidth;
428
429 debug2(DOB, DD, "] BreakTable returning %s,%s; x =",
430 EchoLength(bwidth), EchoLength(fwidth));
431 ifdebug(DOB, DD, DebugObject(x));
432 return x;
433 } /* end BreakTable */
434
435
436 /*@::BreakObject()@***********************************************************/
437 /* */
438 /* OBJECT BreakObject(x, c) */
439 /* */
440 /* Break lines of object x so that it satisfies constraint c. */
441 /* */
442 /*****************************************************************************/
443
BreakObject(OBJECT x,CONSTRAINT * c)444 OBJECT BreakObject(OBJECT x, CONSTRAINT *c)
445 { OBJECT link, y; CONSTRAINT yc; FULL_LENGTH f; BOOLEAN junk;
446 debugcond4(DOB, D, debug_depth++ < debug_depth_max,
447 "%*s[ BreakObject(%s %d)", (debug_depth-1)*2, " ", Image(type(x)), (int) x);
448 debug4(DOB, DD, "[ BreakObject(%s (%s,%s), %s), x =",
449 Image(type(x)), EchoLength(back(x, COLM)), EchoLength(fwd(x, COLM)),
450 EchoConstraint(c));
451 ifdebug(DOB, DD, DebugObject(x));
452
453 /* if constraint is negative (should really be never), replace with empty */
454 if( !(bc(*c)>=0 && bfc(*c)>=0 && fc(*c)>=0) )
455 {
456 Error(13, 11, "replacing with empty object: negative size constraint %s,%s,%s",
457 WARN, &fpos(x), EchoLength(bc(*c)), EchoLength(bfc(*c)), EchoLength(fc(*c)));
458 y = MakeWord(WORD, STR_EMPTY, &fpos(x));
459 back(y, COLM) = fwd(y, COLM) = 0;
460 ReplaceNode(y, x);
461 DisposeObject(x);
462 x = y;
463 debugcond6(DOB, D, --debug_depth < debug_depth_max,
464 "%*s] BreakObject(%s %d) (neg!) = (%s, %s)", debug_depth*2, " ",
465 Image(type(x)), (int) x, EchoLength(back(x, COLM)),
466 EchoLength(fwd(x, COLM)));
467 debug0(DOB, DD, "] BreakObject returning (negative constraint).");
468 return x;
469 }
470
471 /* if no breaking required, return immediately */
472 if( FitsConstraint(back(x, COLM), fwd(x, COLM), *c) )
473 { debug0(DOB, DD, "] BreakObject returning (fits).");
474 debugcond6(DOB, D, --debug_depth < debug_depth_max,
475 "%*s] BreakObject(%s %d) (fits) = (%s, %s)", debug_depth*2, " ",
476 Image(type(x)), (int) x, EchoLength(back(x, COLM)),
477 EchoLength(fwd(x, COLM)));
478 return x;
479 }
480
481 switch( type(x) )
482 {
483
484 case ROTATE:
485
486 if( BackEnd->scale_avail && InsertScale(x, c) )
487 {
488 Parent(x, Up(x));
489 Error(13, 3, "%s object scaled horizontally by factor %.2f (too wide)",
490 WARN, &fpos(x), KW_ROTATE, (float) bc(constraint(x)) / SF );
491 }
492 else
493 { Error(13, 4, "%s deleted (too wide; cannot break %s)",
494 WARN, &fpos(x), KW_ROTATE, KW_ROTATE);
495 y = MakeWord(WORD, STR_EMPTY, &fpos(x));
496 back(y, COLM) = fwd(y, COLM) = 0;
497 ReplaceNode(y, x);
498 DisposeObject(x);
499 x = y;
500 }
501 break;
502
503
504 case SCALE:
505
506 InvScaleConstraint(&yc, bc(constraint(x)), c);
507 Child(y, Down(x));
508 y = BreakObject(y, &yc);
509 back(x, COLM) = (back(y, COLM) * bc(constraint(x))) / SF;
510 fwd(x, COLM) = (fwd(y, COLM) * bc(constraint(x))) / SF;
511 break;
512
513
514 case KERN_SHRINK:
515
516 /* not really accurate, but there you go */
517 Child(y, LastDown(x));
518 y = BreakObject(y, c);
519 back(x, COLM) = back(y, COLM);
520 fwd(x, COLM) = fwd(y, COLM);
521 break;
522
523
524 case WORD:
525 case QWORD:
526
527 if( word_hyph(x) )
528 {
529 /* create an ACAT with the same size as x */
530 New(y, ACAT);
531 FposCopy(fpos(y), fpos(x));
532 back(y, COLM) = back(x, COLM);
533 fwd(y, COLM) = fwd(x, COLM);
534 back(y, ROWM) = back(x, ROWM);
535 fwd(y, ROWM) = fwd(x, ROWM);
536
537 /* set ACAT's save_style; have to invent a line_gap, unfortunately */
538 SetGap(line_gap(save_style(y)), FALSE, FALSE, FALSE, FIXED_UNIT,
539 MARK_MODE, 1.1 * FontSize(word_font(x), x));
540 SetGap(space_gap(save_style(y)), FALSE, FALSE, TRUE, FIXED_UNIT,
541 EDGE_MODE, 0);
542 hyph_style(save_style(y)) = HYPH_ON;
543 fill_style(save_style(y)) = FILL_ON;
544 display_style(save_style(y)) = DISPLAY_LEFT;
545 small_caps(save_style(y)) = FALSE;
546 font(save_style(y)) = word_font(x);
547 colour(save_style(y)) = word_colour(x);
548 underline_colour(save_style(y)) = word_underline_colour(x);
549 texture(save_style(y)) = word_texture(x);
550 outline(save_style(y)) = word_outline(x);
551 language(save_style(y)) = word_language(x);
552 baselinemark(save_style(y)) = word_baselinemark(x);
553 strut(save_style(y)) = word_strut(x);
554 ligatures(save_style(y)) = word_ligatures(x);
555 debug3(DOF, DD, " in BreakObject y %s %s %s",
556 EchoStyle(&save_style(y)), Image(type(y)), EchoObject(y));
557
558 /* enclose x in the ACAT and try breaking (i.e. filling) it */
559 ReplaceNode(y, x);
560 Link(y, x);
561 x = y;
562 debug3(DOF, DD, " in BreakObject x %s %s %s",
563 EchoStyle(&save_style(x)), Image(type(x)), EchoObject(x));
564 x = BreakObject(x, c);
565 }
566 else if( BackEnd->scale_avail && InsertScale(x, c) )
567 { OBJECT tmp;
568 tmp = x;
569 Parent(x, Up(x));
570 Error(13, 5, "word %s scaled horizontally by factor %.2f (too wide)",
571 WARN, &fpos(x), string(tmp), (float) bc(constraint(x)) / SF);
572 }
573 else
574 { Error(13, 6, "word %s deleted (too wide)", WARN, &fpos(x), string(x));
575 y = MakeWord(WORD, STR_EMPTY, &fpos(x));
576 back(y, COLM) = fwd(y, COLM) = 0;
577 ReplaceNode(y, x);
578 DisposeObject(x);
579 x = y;
580 }
581 break;
582
583
584 case WIDE:
585
586 MinConstraint(&constraint(x), c);
587 Child(y, Down(x));
588 y = BreakObject(y, &constraint(x));
589 back(x, COLM) = back(y, COLM);
590 fwd(x, COLM) = fwd(y, COLM);
591 EnlargeToConstraint(&back(x, COLM), &fwd(x, COLM), &constraint(x));
592 break;
593
594
595 case INCGRAPHIC:
596 case SINCGRAPHIC:
597
598 if( BackEnd->scale_avail && InsertScale(x, c) )
599 {
600 Parent(x, Up(x));
601 Error(13, 7, "%s scaled horizontally by factor %.2f (too wide)",
602 WARN, &fpos(x),
603 type(x) == INCGRAPHIC ? KW_INCGRAPHIC : KW_SINCGRAPHIC,
604 (float) bc(constraint(x)) / SF);
605 }
606 else
607 { Error(13, 8, "%s deleted (too wide)", WARN, &fpos(x),
608 type(x) == INCGRAPHIC ? KW_INCGRAPHIC : KW_SINCGRAPHIC);
609 y = MakeWord(WORD, STR_EMPTY, &fpos(x));
610 back(y, COLM) = fwd(y, COLM) = 0;
611 ReplaceNode(y, x);
612 DisposeObject(x);
613 x = y;
614 }
615 break;
616
617
618 case HMIRROR:
619
620 FlipConstraint(yc, *c);
621 Child(y, Down(x));
622 y = BreakObject(y, &yc);
623 back(x, COLM) = fwd(y, COLM);
624 fwd(x, COLM) = back(y, COLM);
625 break;
626
627
628 case HIGH:
629 case VMIRROR:
630 case VSCALE:
631 case VCOVER:
632 case VSHIFT:
633 case HCONTRACT:
634 case VCONTRACT:
635 case HLIMITED:
636 case VLIMITED:
637 case HEXPAND:
638 case VEXPAND:
639 case ONE_COL:
640 case ONE_ROW:
641 case HSPANNER:
642
643 assert( Down(x) == LastDown(x), "BreakObject: downs!" );
644 Child(y, Down(x));
645 y = BreakObject(y, c);
646 back(x, COLM) = back(y, COLM);
647 fwd(x, COLM) = fwd(y, COLM);
648 break;
649
650
651 case BACKGROUND:
652
653 Child(y, Down(x));
654 y = BreakObject(y, c);
655 Child(y, LastDown(x));
656 y = BreakObject(y, c);
657 back(x, COLM) = back(y, COLM);
658 fwd(x, COLM) = fwd(y, COLM);
659 break;
660
661
662 case START_HVSPAN:
663 case START_HSPAN:
664 case START_VSPAN:
665 case HSPAN:
666 case VSPAN:
667
668 /* these all have size zero except the last one, so if we get to */
669 /* this point we must be at the last column and need to break it. */
670 /* this is done just by setting its size to zero, unless it is */
671 /* the last column in which case it claims everything that is */
672 /* going; the real break is deferred to the first ROWM touch, */
673 /* when we know that all contributing columns have been broken */
674 /* unless the child is not a spanner, in which case it's @OneCol */
675 Child(y, Down(x));
676 if( type(y) != HSPANNER )
677 {
678 y = BreakObject(y, c);
679 back(x, COLM) = back(y, COLM);
680 fwd(x, COLM) = fwd(y, COLM);
681 }
682 else
683 {
684 back(x, COLM) = 0;
685 fwd(x, COLM) = find_min(bfc(*c), fc(*c));
686 }
687 break;
688
689
690 case HSHIFT:
691
692 Child(y, Down(x));
693 f = FindShift(x, y, COLM);
694 SetConstraint(yc,
695 find_min(bc(*c), bfc(*c)) - f, bfc(*c), find_min(fc(*c), bfc(*c)) + f);
696 BreakObject(y, &yc);
697 f = FindShift(x, y, COLM);
698 back(x, COLM) = find_min(MAX_FULL_LENGTH, find_max(0, back(y, COLM) + f));
699 fwd(x, COLM) = find_min(MAX_FULL_LENGTH, find_max(0, fwd(y, COLM) - f));
700 break;
701
702
703 case END_HEADER:
704 case CLEAR_HEADER:
705
706 /* these have size zero anyway, so not likely to reach this point */
707 break;
708
709
710 case BEGIN_HEADER:
711 case SET_HEADER:
712
713 /* multiple copies, remember */
714 for( link = NextDown(Down(x)); link != x; link = NextDown(link) )
715 {
716 Child(y, link);
717 y = BreakObject(y, c);
718 back(x, COLM) = back(y, COLM);
719 fwd(x, COLM) = fwd(y, COLM);
720 }
721 debug3(DOB, D, "BreakObject(%s, COLM) = (%s, %s)", Image(type(x)),
722 EchoLength(back(x, COLM)), EchoLength(fwd(x, COLM)));
723 break;
724
725
726 case PLAIN_GRAPHIC:
727 case GRAPHIC:
728 case LINK_SOURCE:
729 case LINK_DEST:
730 case LINK_DEST_NULL:
731 case LINK_URL:
732
733 Child(y, LastDown(x));
734 y = BreakObject(y, c);
735 back(x, COLM) = back(y, COLM);
736 fwd(x, COLM) = fwd(y, COLM);
737 break;
738
739
740 case SPLIT:
741
742 Child(y, DownDim(x, COLM));
743 y = BreakObject(y, c);
744 back(x, COLM) = back(y, COLM);
745 fwd(x, COLM) = fwd(y, COLM);
746 break;
747
748
749 case ACAT:
750
751 if( back(x, COLM) > 0 )
752 { int sz; OBJECT rpos;
753 /* shift the column mark of x to the left edge */
754 sz = size(x, COLM);
755 fwd(x, COLM) = find_min(MAX_FULL_LENGTH, sz);
756 back(x, COLM) = 0;
757 rpos = x;
758 for( link = Down(x); link != x; link = NextDown(link) )
759 { Child(y, link);
760 if( type(y) == GAP_OBJ && mark(gap(y)) )
761 { mark(gap(y)) = FALSE;
762 rpos = y;
763 }
764 }
765 if( FitsConstraint(back(x, COLM), fwd(x, COLM), *c) )
766 { Error(13, 9, "column mark of unbroken paragraph moved left",
767 WARN, &fpos(rpos));
768 break;
769 }
770 Error(13, 10, "column mark of paragraph moved left before breaking",
771 WARN, &fpos(rpos));
772 ifdebug(DOB, DD, DebugObject(x));
773 }
774 x = FillObject(x, c, nilobj, TRUE, TRUE, FALSE, &junk);
775 break;
776
777
778 case HCAT:
779
780 x = BreakTable(x, c);
781 break;
782
783
784 case COL_THR:
785
786 BreakJoinedGroup(Down(x), LastDown(x), nilobj, c,
787 &back(x,COLM), &fwd(x,COLM));
788 break;
789
790
791 case VCAT:
792
793 x = BreakVcat(x, c);
794 break;
795
796
797 default:
798
799 assert1(FALSE, "BreakObject:", Image(type(x)));
800 break;
801
802 }
803 assert( back(x, COLM) >= 0, "BreakObject: back(x, COLM) < 0!" );
804 assert( fwd(x, COLM) >= 0, "BreakObject: fwd(x, COLM) < 0!" );
805 debugcond6(DOB, D, --debug_depth < debug_depth_max,
806 "%*s] BreakObject(%s %d) = (%s, %s)", debug_depth*2, " ", Image(type(x)),
807 (int) x, EchoLength(back(x, COLM)), EchoLength(fwd(x, COLM)));
808 debug2(DOB, DD, "] BreakObject returning %s,%s, x =",
809 EchoLength(back(x, COLM)), EchoLength(fwd(x, COLM)));
810 ifdebug(DOB, DD, DebugObject(x));
811 return x;
812 } /* end BreakObject */
813