aboutsummaryrefslogtreecommitdiffstats
path: root/z13.c
diff options
context:
space:
mode:
authorJeffrey H. Kingston <jeff@it.usyd.edu.au>2010-09-14 19:21:41 +0000
committerJeffrey H. Kingston <jeff@it.usyd.edu.au>2010-09-14 19:21:41 +0000
commit71bdb35d52747e6d7d9f55df4524d57c2966be94 (patch)
tree480ee5eefccc40d5f3331cc52d66f722fd19bfb9 /z13.c
parentb41263ea7578fa9742486135c762803b52794105 (diff)
downloadlout-71bdb35d52747e6d7d9f55df4524d57c2966be94.tar.gz
Lout 3.17.
git-svn-id: http://svn.savannah.nongnu.org/svn/lout/trunk@2 9365b830-b601-4143-9ba8-b4a8e2c3339c
Diffstat (limited to 'z13.c')
-rw-r--r--z13.c748
1 files changed, 748 insertions, 0 deletions
diff --git a/z13.c b/z13.c
new file mode 100644
index 0000000..62b71d7
--- /dev/null
+++ b/z13.c
@@ -0,0 +1,748 @@
+/*@z13.c:Object Breaking:BreakJoinedGroup()@**********************************/
+/* */
+/* THE LOUT DOCUMENT FORMATTING SYSTEM (VERSION 3.17) */
+/* COPYRIGHT (C) 1991, 1999 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: z13.c */
+/* MODULE: Object Breaking */
+/* EXTERNS: BreakObject() */
+/* */
+/*****************************************************************************/
+#include "externs.h"
+#define broken(x) back(x, ROWM) /* OK since no vertical sizes yet */
+
+
+/*****************************************************************************/
+/* */
+/* static BreakJoinedGroup(start, stop, m, c, res_back, res_fwd) */
+/* */
+/* Break joined group of components of a VCAT, beginning from Child(start) */
+/* inclusive and ending at Child(stop) inclusive. Break component m first */
+/* because it is the widest. */
+/* */
+/*****************************************************************************/
+
+static void BreakJoinedGroup(OBJECT start, OBJECT stop, OBJECT m,
+CONSTRAINT *c, FULL_LENGTH *res_back, FULL_LENGTH *res_fwd)
+{ OBJECT y, link; FULL_LENGTH b, f, sb, sf; CONSTRAINT yc;
+ debug1(DOB, DD, "[ BreakJoinedGroup(start, stop, m, %s, -, -)",
+ EchoConstraint(c));
+
+ /* work out a suitable constraint to apply to each component */
+ sb = sf = 0;
+ for( link = start; link != NextDown(stop); link = NextDown(link) )
+ { Child(y, link);
+ if( !is_definite(type(y)) ) continue;
+ sb = find_max(sb, back(y, COLM));
+ sf = find_max(sf, fwd(y, COLM));
+ }
+ if( sb <= bc(*c) )
+ {
+ /* make sure the constraint will accept objects with size (sb, 0) */
+ b = sb;
+ f = 0;
+ }
+ else
+ {
+ /* sb is too wide anyway, so don't worry about it */
+ b = 0;
+ f = 0;
+ }
+ SetConstraint(yc, find_min(bc(*c), bfc(*c)-f), bfc(*c), find_min(fc(*c), bfc(*c)-b));
+
+ /* apply this constraint to each component in turn, m first */
+ if( m != nilobj )
+ {
+ debug1(DOB, DD, " +++BreakJoinedGroup calling first child, yc = %s",
+ EchoConstraint(&yc));
+ m = BreakObject(m, &yc);
+ b = back(m, COLM);
+ f = fwd(m, COLM);
+ SetConstraint(yc, find_min(bc(yc), bfc(yc)-f), bfc(yc), find_min(fc(yc), bfc(yc)-b));
+ }
+ else b = f = 0;
+ for( link = start; link != NextDown(stop); link = NextDown(link) )
+ { Child(y, link);
+ if( !is_definite(type(y)) || y == m ) continue;
+ debug1(DOB, DD, " +++BreakJoinedGroup calling child, yc = %s",
+ EchoConstraint(&yc));
+ y = BreakObject(y, &yc);
+ b = find_max(b, back(y, COLM));
+ f = find_max(f, fwd(y, COLM));
+ SetConstraint(yc, find_min(bc(yc), bfc(yc)-f), bfc(yc), find_min(fc(yc), bfc(yc)-b));
+ }
+ if( !FitsConstraint(b, f, *c) )
+ { debug3(DOB, DD, " in BreakJoinedGroup: !FitsConstraint(%s, %s, %s)",
+ EchoLength(b), EchoLength(f), EchoConstraint(c));
+ Error(13, 1, "failed to break column to fit into its available space",
+ WARN, m != nilobj ? &fpos(m) : (y != nilobj ? &fpos(y) : no_fpos));
+ }
+ *res_back = b; *res_fwd = f;
+ debug2(DOB, DD,"] BreakJoinedGroup returning (%s, %s)",
+ EchoLength(b), EchoLength(f));
+} /* end BreakJoinedGroup */
+
+
+/*@::BreakVcat()@*************************************************************/
+/* */
+/* static OBJECT BreakVcat(x, c) */
+/* */
+/* Break a VCAT to satisfy constraint c. This is tedious because every */
+/* group of components between // ... // must be broken separately. */
+/* */
+/*****************************************************************************/
+
+static OBJECT BreakVcat(OBJECT x, CONSTRAINT *c)
+{ OBJECT y, link, start_group, m; FULL_LENGTH b, f, dble_fwd; CONSTRAINT tc;
+ BOOLEAN dble_found;
+ debug1(DOB, DD, "[ BreakVcat(x, %s)", EchoConstraint(c));
+ assert(Down(x) != x, "BreakVcat: Down(x) == x!" );
+ SetConstraint(tc, MAX_FULL_LENGTH, find_min(bfc(*c), fc(*c)), MAX_FULL_LENGTH);
+
+ dble_found = FALSE; dble_fwd = 0; start_group = nilobj;
+ for( link = Down(x); link != x; link = NextDown(link) )
+ { Child(y, link);
+ if( is_index(type(y)) ) continue;
+ if( type(y) == GAP_OBJ )
+ { assert( start_group != nilobj, "BreakVcat: start_group == nilobj!" );
+ if( !join(gap(y)) )
+ {
+ /* finish off and break this group */
+ if( !FitsConstraint(b, f, tc) )
+ BreakJoinedGroup(start_group, link, m, &tc, &b, &f);
+ dble_found = TRUE;
+ dble_fwd = find_max(dble_fwd, b + f);
+ start_group = nilobj;
+ debug1(DOB, DD, " end group, dble_fwd: %s", EchoLength(dble_fwd));
+ }
+ }
+ else if( start_group == nilobj )
+ {
+ /* start new group */
+ b = back(y, COLM); f = fwd(y, COLM);
+ start_group = link; m = y;
+ debug2(DOB, DD, " starting group (b = %s, f = %s):",
+ EchoLength(b), EchoLength(f));
+ ifdebug(DOB, DD, DebugObject(y));
+ }
+ else
+ {
+ /* continue with current group */
+ b = find_max(b, back(y, COLM)); f = find_max(f, fwd(y, COLM));
+ if( fwd(y, COLM) > fwd(m, COLM) ) m = y;
+ debug3(DOB, DD, " in group%s (b = %s, f = %s):",
+ m == y ? " (new max)" : "",
+ EchoLength(b), EchoLength(f));
+ ifdebug(DOB, DD, DebugObject(y));
+ }
+ }
+ assert( start_group != nilobj, "BreakVcat: start_group == nilobj (2)!" );
+
+ if( dble_found )
+ {
+ /* finish off and break this last group, and set sizes of x */
+ if( !FitsConstraint(b, f, tc) )
+ BreakJoinedGroup(start_group, LastDown(x), m, &tc, &b, &f);
+ dble_fwd = find_max(dble_fwd, b + f);
+ debug1(DOB, DD, " ending last group, dble_fwd: %s",EchoLength(dble_fwd));
+ back(x, COLM) = 0; fwd(x, COLM) = find_min(MAX_FULL_LENGTH, dble_fwd);
+ }
+ else
+ {
+ /* finish off and break this last and only group, and set sizes of x */
+ debug2(DOB, DD, " BreakVcat ending last and only group (%s, %s)",
+ EchoLength(b), EchoLength(f));
+ BreakJoinedGroup(start_group, LastDown(x), m, c, &b, &f);
+ back(x, COLM) = b; fwd(x, COLM) = f;
+ }
+
+ debug0(DOB, DD, "] BreakVcat returning x:");
+ ifdebug(DOB, DD, DebugObject(x));
+ debug2(DOB, DD, " (size is %s, %s)",
+ EchoLength(back(x, COLM)), EchoLength(fwd(x, COLM)));
+ return x;
+} /* end BreakVcat */
+
+
+/*@::BreakTable()@************************************************************/
+/* */
+/* static OBJECT BreakTable(x, c) */
+/* */
+/* Break table (HCAT) x to satisfy constraint c. */
+/* */
+/* Outline of algorithm: */
+/* */
+/* bcount = number of components to left of mark; */
+/* fcount = no. of components on and right of mark; */
+/* bwidth = what back(x) would be if all components had size (0, 0); */
+/* fwidth = what fwd(x) would be if all components had size (0, 0); */
+/* Set all components of x to Unbroken (broken(y) holds this flag); */
+/* while( an Unbroken component of x exists ) */
+/* { my = the Unbroken component of x of minimum width; */
+/* mc = desirable constraint for my (see below); */
+/* BreakObject(my, &mc); */
+/* Set my to Broken and update bcount, fcount, bwidth, fwidth */
+/* to reflect the actual size of my, now broken; */
+/* } */
+/* */
+/* The constraint mc is chosen in an attempt to ensure that: */
+/* */
+/* a) Any sufficiently narrow components will not break; */
+/* b) All broken components will have the same bfc(mc), if possible; */
+/* c) All available space is used. */
+/* */
+/*****************************************************************************/
+
+static OBJECT BreakTable(OBJECT x, CONSTRAINT *c)
+{ FULL_LENGTH bwidth, fwidth; /* running back(x) and fwd(x) */
+ int bcount, fcount; /* running no. of components */
+ OBJECT mlink, my; /* minimum-width unbroken component */
+ BOOLEAN ratm; /* TRUE when my has a mark to its right */
+ int mside; /* side of the mark my is on: BACK, ON, FWD */
+ FULL_LENGTH msize; /* size of my (minimal among unbroken) */
+ CONSTRAINT mc; /* desirable constraint for my */
+ OBJECT pg, prec_def; /* preceding definite object of my */
+ OBJECT sg, succ_def; /* succeeding definite object of my */
+ FULL_LENGTH pd_extra,sd_extra;/* space availiable for free each side of my */
+ FULL_LENGTH av_colsize; /* the size of each unbroken component */
+ /* if they are all assigned equal width */
+ FULL_LENGTH fwd_max, back_max;/* maximum space available forward of or */
+ /* back of the mark, when columns are even */
+ FULL_LENGTH col_size; /* the column size actually used in breaking */
+ FULL_LENGTH prev_col_size; /* previous column size (try to keep equal) */
+ FULL_LENGTH beffect, feffect; /* the amount bwidth, fwidth must increase */
+ /* when my is broken */
+ OBJECT link, y, prev, g; FULL_LENGTH tmp, tmp2;
+
+ debug1(DOB, D, "[ BreakTable( x, %s )", EchoConstraint(c));
+
+ /* Initialise csize, bcount, fcount, bwidth, fwidth and broken(y) */
+ bcount = fcount = 0; bwidth = fwidth = 0; prev = nilobj;
+ prev_col_size = 0;
+ Child(y, Down(x));
+ assert( type(y) != GAP_OBJ, "BreakTable: GAP_OBJ!" );
+ assert( !is_index(type(y)), "BreakTable: index!" );
+ broken(y) = is_indefinite(type(y));
+ if( !broken(y) ) prev = y, fcount = 1;
+
+ for( link = NextDown(Down(x)); link != x; link = NextDown(NextDown(link)) )
+ {
+ /* find the next gap g and following child y */
+ Child(g, link);
+ assert( type(g) == GAP_OBJ, "BreakTable: GAP_OBJ!" );
+ assert( NextDown(link) != x, "BreakTable: GAP_OBJ is last!" );
+ Child(y, NextDown(link));
+
+ assert( type(y) != GAP_OBJ, "BreakTable: GAP_OBJ!" );
+ assert( !is_index(type(y)), "BreakTable: index!" );
+ broken(y) = is_indefinite(type(y));
+ if( !broken(y) )
+ { if( prev == nilobj ) fcount = 1;
+ else if( mark(gap(g)) )
+ { bcount += fcount;
+ bwidth += fwidth + MinGap(0, 0, 0, &gap(g));
+ fcount = 1; fwidth = 0;
+ }
+ else
+ { fwidth += MinGap(0, 0, 0, &gap(g));
+ fcount += 1;
+ }
+ prev = y;
+ }
+ }
+
+ /* if column gaps alone are too wide, kill them all */
+ if( !FitsConstraint(bwidth, fwidth, *c) )
+ {
+ debug2(DOB, D, "column gaps alone too wide: bwidth: %s; fwidth: %s",
+ EchoLength(bwidth), EchoLength(fwidth));
+ Error(13, 2, "reducing column gaps to 0i (object is too wide)",
+ WARN, &fpos(x));
+ for( link = Down(x); link != x; link = NextDown(link) )
+ { Child(g, link);
+ if( type(g) == GAP_OBJ )
+ { SetGap(gap(g), nobreak(gap(g)), mark(gap(g)), join(gap(g)),
+ FIXED_UNIT, EDGE_MODE, 0);
+ }
+ }
+ bwidth = fwidth = 0;
+ }
+
+ /* break each column, from smallest to largest */
+ while( bcount + fcount > 0 && FitsConstraint(bwidth, fwidth, *c) )
+ {
+ debug2(DOB, D, "bcount: %d; bwidth: %s", bcount, EchoLength(bwidth));
+ debug2(DOB, D, "fcount: %d; fwidth: %s", fcount, EchoLength(fwidth));
+
+ /* find a minimal-width unbroken component my */
+ my = nilobj; msize = size(x, COLM); /* an upper bound for size(y) */
+ for( link = Down(x); ; link = NextDown(link) )
+ { Child(y, link);
+ assert( type(y) != GAP_OBJ, "BreakTable: type(y) == GAP_OBJ!" );
+ if( !broken(y) && (size(y, COLM) < msize || my == nilobj) )
+ { msize = size(y, COLM);
+ my = y; mlink = link;
+ ratm = FALSE;
+ }
+
+ /* next gap */
+ link = NextDown(link);
+ if( link == x ) break;
+ Child(g, link);
+ assert( type(g) == GAP_OBJ, "BreakTable: type(g) != GAP_OBJ!" );
+ if( mark(gap(g)) ) ratm = TRUE;
+ }
+
+ /* find neighbouring definite objects and resulting pd_extra and sd_extra */
+ SetNeighbours(mlink, ratm, &pg, &prec_def, &sg, &succ_def, &mside);
+ debug2(DOB, D, "my (%s): %s", Image(mside), EchoObject(my));
+ pd_extra = pg == nilobj ? 0 :
+ ExtraGap(broken(prec_def) ? fwd(prec_def,COLM) : 0, 0, &gap(pg), BACK);
+ sd_extra = sg == nilobj ? 0 :
+ ExtraGap(0, broken(succ_def) ? back(succ_def,COLM) : 0, &gap(sg), FWD);
+ debug2(DOB, D, "pd_extra: %s; sd_extra: %s",
+ EchoLength(pd_extra), EchoLength(sd_extra) );
+
+ /* calculate desirable constraints for my */
+ av_colsize = (bfc(*c) - bwidth - fwidth) / (bcount + fcount);
+ debug1(DOB, D, "av_colsize = %s", EchoLength(av_colsize));
+ debug1(DOB, D, "prev_col_size = %s", EchoLength(prev_col_size));
+ switch( mside )
+ {
+
+ case BACK:
+
+ back_max = find_min(bc(*c), bwidth + av_colsize * bcount);
+ col_size = (back_max - bwidth) / bcount;
+ if( col_size > prev_col_size && col_size - prev_col_size < PT )
+ col_size = prev_col_size;
+ SetConstraint(mc,
+ find_min(MAX_FULL_LENGTH, col_size + pd_extra),
+ find_min(MAX_FULL_LENGTH, col_size + pd_extra + sd_extra),
+ find_min(MAX_FULL_LENGTH, col_size + sd_extra));
+ break;
+
+
+ case ON:
+
+ fwd_max = find_min(fc(*c), fwidth + av_colsize * fcount);
+ col_size = (fwd_max - fwidth) / fcount;
+ if( col_size > prev_col_size && col_size - prev_col_size < PT )
+ col_size = prev_col_size;
+ SetConstraint(mc,
+ find_min(MAX_FULL_LENGTH, pd_extra + back(my, COLM)),
+ find_min(MAX_FULL_LENGTH, pd_extra + back(my, COLM) + col_size + sd_extra),
+ find_min(MAX_FULL_LENGTH, col_size + sd_extra));
+ break;
+
+
+ case FWD:
+
+ fwd_max = find_min(fc(*c), fwidth + av_colsize * fcount);
+ col_size = (fwd_max - fwidth) / fcount;
+ if( col_size > prev_col_size && col_size - prev_col_size < PT )
+ col_size = prev_col_size;
+ SetConstraint(mc,
+ find_min(MAX_FULL_LENGTH, col_size + pd_extra),
+ find_min(MAX_FULL_LENGTH, col_size + pd_extra + sd_extra),
+ find_min(MAX_FULL_LENGTH, col_size + sd_extra));
+ break;
+
+
+ default:
+
+ assert(FALSE, "BreakTable: mside");
+ break;
+ }
+ debug1(DOB, D, "col_size = %s", EchoLength(col_size));
+ prev_col_size = col_size;
+
+ /* now break my according to these constraints, and accept it */
+ debug2(DOB, DD, " calling BreakObject(%s, %s)", EchoObject(my),
+ EchoConstraint(&mc));
+ my = BreakObject(my, &mc); broken(my) = TRUE;
+
+ /* calculate the effect of accepting my on bwidth and fwidth */
+ if( pg != nilobj )
+ { tmp = broken(prec_def) ? fwd(prec_def, COLM) : 0;
+ beffect = MinGap(tmp, back(my, COLM), fwd(my, COLM), &gap(pg)) -
+ MinGap(tmp, 0, 0, &gap(pg));
+ }
+ else beffect = back(my, COLM);
+
+ if( sg != nilobj )
+ { tmp = broken(succ_def) ? back(succ_def, COLM) : 0;
+ tmp2 = broken(succ_def) ? fwd(succ_def, COLM) : 0;
+ feffect = MinGap(fwd(my, COLM), tmp, tmp2, &gap(sg)) -
+ MinGap(0, tmp, tmp2, &gap(sg));
+ }
+ else feffect = fwd(my, COLM);
+
+ switch( mside )
+ {
+ case BACK: bwidth += beffect + feffect;
+ bcount--;
+ break;
+
+ case ON: bwidth += beffect; fwidth += feffect;
+ fcount--;
+ break;
+
+ case FWD: fwidth += beffect + feffect;
+ fcount--;
+ break;
+
+ default: assert(FALSE, "BreakTable: mside");
+ break;
+ }
+
+ } /* end while */
+
+ back(x, COLM) = bwidth;
+ fwd(x, COLM) = fwidth;
+
+ debug2(DOB, D, "] BreakTable returning %s,%s; x =",
+ EchoLength(bwidth), EchoLength(fwidth));
+ ifdebug(DOB, DD, DebugObject(x));
+ return x;
+} /* end BreakTable */
+
+
+/*@::BreakObject()@***********************************************************/
+/* */
+/* OBJECT BreakObject(x, c) */
+/* */
+/* Break lines of object x so that it satisfies constraint c. */
+/* */
+/*****************************************************************************/
+
+OBJECT BreakObject(OBJECT x, CONSTRAINT *c)
+{ OBJECT link, y; CONSTRAINT yc; FULL_LENGTH f; BOOLEAN junk;
+ debug3(DOB, DD, "[ BreakObject(x (%s,%s), %s), x =",
+ EchoLength(back(x, COLM)), EchoLength(fwd(x, COLM)), EchoConstraint(c));
+ ifdebug(DOB, DD, DebugObject(x));
+
+ /* if constraint is negative (should really be never), replace with empty */
+ if( !(bc(*c)>=0 && bfc(*c)>=0 && fc(*c)>=0) )
+ {
+ Error(13, 11, "replacing with empty object: negative size constraint %s,%s,%s",
+ WARN, &fpos(x), EchoLength(bc(*c)), EchoLength(bfc(*c)), EchoLength(fc(*c)));
+ y = MakeWord(WORD, STR_EMPTY, &fpos(x));
+ back(y, COLM) = fwd(y, COLM) = 0;
+ ReplaceNode(y, x);
+ DisposeObject(x);
+ x = y;
+ debug0(DOB, DD, "] BreakObject returning (negative constraint).");
+ return x;
+ }
+
+ /* if no breaking required, return immediately */
+ if( FitsConstraint(back(x, COLM), fwd(x, COLM), *c) )
+ { debug0(DOB, DD, "] BreakObject returning (fits).");
+ return x;
+ }
+
+ switch( type(x) )
+ {
+
+ case ROTATE:
+
+ if( BackEnd != PLAINTEXT && InsertScale(x, c) )
+ {
+ Parent(x, Up(x));
+ Error(13, 3, "%s object scaled horizontally by factor %.2f (too wide)",
+ WARN, &fpos(x), KW_ROTATE, (float) bc(constraint(x)) / SF );
+ }
+ else
+ { Error(13, 4, "%s deleted (too wide; cannot break %s)",
+ WARN, &fpos(x), KW_ROTATE, KW_ROTATE);
+ y = MakeWord(WORD, STR_EMPTY, &fpos(x));
+ back(y, COLM) = fwd(y, COLM) = 0;
+ ReplaceNode(y, x);
+ DisposeObject(x);
+ x = y;
+ }
+ break;
+
+
+ case SCALE:
+
+ InvScaleConstraint(&yc, bc(constraint(x)), c);
+ Child(y, Down(x));
+ y = BreakObject(y, &yc);
+ back(x, COLM) = (back(y, COLM) * bc(constraint(x))) / SF;
+ fwd(x, COLM) = (fwd(y, COLM) * bc(constraint(x))) / SF;
+ break;
+
+
+ case KERN_SHRINK:
+
+ /* not really accurate, but there you go */
+ Child(y, LastDown(x));
+ y = BreakObject(y, c);
+ back(x, COLM) = back(y, COLM);
+ fwd(x, COLM) = fwd(y, COLM);
+ break;
+
+
+ case WORD:
+ case QWORD:
+
+ if( word_hyph(x) )
+ {
+ /* create an ACAT with the same size as x */
+ New(y, ACAT);
+ FposCopy(fpos(y), fpos(x));
+ back(y, COLM) = back(x, COLM);
+ fwd(y, COLM) = fwd(x, COLM);
+ back(y, ROWM) = back(x, ROWM);
+ fwd(y, ROWM) = fwd(x, ROWM);
+
+ /* set ACAT's save_style; have to invent a line_gap, unfortunately */
+ SetGap(line_gap(save_style(y)), FALSE, FALSE, FALSE, FIXED_UNIT,
+ MARK_MODE, 1.1 * FontSize(word_font(x), x));
+ SetGap(space_gap(save_style(y)), FALSE, FALSE, TRUE, FIXED_UNIT,
+ EDGE_MODE, 0);
+ hyph_style(save_style(y)) = HYPH_ON;
+ fill_style(save_style(y)) = FILL_ON;
+ display_style(save_style(y)) = DISPLAY_LEFT;
+ small_caps(save_style(y)) = FALSE;
+ font(save_style(y)) = word_font(x);
+ colour(save_style(y)) = word_colour(x);
+ language(save_style(y)) = word_language(x);
+ debug3(DOF, DD, " in BreakObject y %s %s %s",
+ EchoStyle(&save_style(y)), Image(type(y)), EchoObject(y));
+
+ /* enclose x in the ACAT and try breaking (i.e. filling) it */
+ ReplaceNode(y, x);
+ Link(y, x);
+ x = y;
+ debug3(DOF, DD, " in BreakObject x %s %s %s",
+ EchoStyle(&save_style(x)), Image(type(x)), EchoObject(x));
+ x = BreakObject(x, c);
+ }
+ else if( BackEnd != PLAINTEXT && InsertScale(x, c) )
+ { OBJECT tmp;
+ tmp = x;
+ Parent(x, Up(x));
+ Error(13, 5, "word %s scaled horizontally by factor %.2f (too wide)",
+ WARN, &fpos(x), string(tmp), (float) bc(constraint(x)) / SF);
+ }
+ else
+ { Error(13, 6, "word %s deleted (too wide)", WARN, &fpos(x), string(x));
+ y = MakeWord(WORD, STR_EMPTY, &fpos(x));
+ back(y, COLM) = fwd(y, COLM) = 0;
+ ReplaceNode(y, x);
+ DisposeObject(x);
+ x = y;
+ }
+ break;
+
+
+ case WIDE:
+
+ MinConstraint(&constraint(x), c);
+ Child(y, Down(x));
+ y = BreakObject(y, &constraint(x));
+ back(x, COLM) = back(y, COLM);
+ fwd(x, COLM) = fwd(y, COLM);
+ EnlargeToConstraint(&back(x, COLM), &fwd(x, COLM), &constraint(x));
+ break;
+
+
+ case INCGRAPHIC:
+ case SINCGRAPHIC:
+
+ if( BackEnd != PLAINTEXT && InsertScale(x, c) )
+ {
+ Parent(x, Up(x));
+ Error(13, 7, "%s scaled horizontally by factor %.2f (too wide)",
+ WARN, &fpos(x),
+ type(x) == INCGRAPHIC ? KW_INCGRAPHIC : KW_SINCGRAPHIC,
+ (float) bc(constraint(x)) / SF);
+ }
+ else
+ { Error(13, 8, "%s deleted (too wide)", WARN, &fpos(x),
+ type(x) == INCGRAPHIC ? KW_INCGRAPHIC : KW_SINCGRAPHIC);
+ y = MakeWord(WORD, STR_EMPTY, &fpos(x));
+ back(y, COLM) = fwd(y, COLM) = 0;
+ ReplaceNode(y, x);
+ DisposeObject(x);
+ x = y;
+ }
+ break;
+
+
+ case HIGH:
+ case VSCALE:
+ case VCOVER:
+ case VSHIFT:
+ case HCONTRACT:
+ case VCONTRACT:
+ case HLIMITED:
+ case VLIMITED:
+ case HEXPAND:
+ case VEXPAND:
+ case ONE_ROW:
+ case ONE_COL:
+ case HSPANNER:
+
+ assert( Down(x) == LastDown(x), "BreakObject: downs!" );
+ Child(y, Down(x));
+ y = BreakObject(y, c);
+ back(x, COLM) = back(y, COLM);
+ fwd(x, COLM) = fwd(y, COLM);
+ break;
+
+
+ case BACKGROUND:
+
+ Child(y, Down(x));
+ y = BreakObject(y, c);
+ Child(y, LastDown(x));
+ y = BreakObject(y, c);
+ back(x, COLM) = back(y, COLM);
+ fwd(x, COLM) = fwd(y, COLM);
+ break;
+
+
+ case START_HVSPAN:
+ case START_HSPAN:
+ case START_VSPAN:
+ case HSPAN:
+ case VSPAN:
+
+ /* these all have size zero except the last one, so if we get to */
+ /* this point we must be at the last column and need to break it. */
+ /* this is done just by setting its size to zero, unless it is */
+ /* the last column in which case it claims everything that is */
+ /* going; the real break is deferred to the first ROWM touch, */
+ /* when we know that all contributing columns have been broken */
+ /* unless the child is not a spanner, in which case it's @OneCol */
+ Child(y, Down(x));
+ if( type(y) != HSPANNER )
+ {
+ y = BreakObject(y, c);
+ back(x, COLM) = back(y, COLM);
+ fwd(x, COLM) = fwd(y, COLM);
+ }
+ else
+ {
+ back(x, COLM) = 0;
+ fwd(x, COLM) = find_min(bfc(*c), fc(*c));
+ }
+ break;
+
+
+ case HSHIFT:
+
+ Child(y, Down(x));
+ f = FindShift(x, y, COLM);
+ SetConstraint(yc,
+ find_min(bc(*c), bfc(*c)) - f, bfc(*c), find_min(fc(*c), bfc(*c)) + f);
+ BreakObject(y, &yc);
+ f = FindShift(x, y, COLM);
+ back(x, COLM) = find_min(MAX_FULL_LENGTH, find_max(0, back(y, COLM) + f));
+ fwd(x, COLM) = find_min(MAX_FULL_LENGTH, find_max(0, fwd(y, COLM) - f));
+ break;
+
+
+ case PLAIN_GRAPHIC:
+ case GRAPHIC:
+
+ Child(y, LastDown(x));
+ y = BreakObject(y, c);
+ back(x, COLM) = back(y, COLM);
+ fwd(x, COLM) = fwd(y, COLM);
+ break;
+
+
+ case SPLIT:
+
+ Child(y, DownDim(x, COLM));
+ y = BreakObject(y, c);
+ back(x, COLM) = back(y, COLM);
+ fwd(x, COLM) = fwd(y, COLM);
+ break;
+
+
+ case ACAT:
+
+ if( back(x, COLM) > 0 )
+ { int sz; OBJECT rpos;
+ /* shift the column mark of x to the left edge */
+ sz = size(x, COLM);
+ fwd(x, COLM) = find_min(MAX_FULL_LENGTH, sz);
+ back(x, COLM) = 0;
+ rpos = x;
+ for( link = Down(x); link != x; link = NextDown(link) )
+ { Child(y, link);
+ if( type(y) == GAP_OBJ && mark(gap(y)) )
+ { mark(gap(y)) = FALSE;
+ rpos = y;
+ }
+ }
+ if( FitsConstraint(back(x, COLM), fwd(x, COLM), *c) )
+ { Error(13, 9, "column mark of unbroken paragraph moved left",
+ WARN, &fpos(rpos));
+ break;
+ }
+ Error(13, 10, "column mark of paragraph moved left before breaking",
+ WARN, &fpos(rpos));
+ ifdebug(DOB, DD, DebugObject(x));
+ }
+ x = FillObject(x, c, nilobj, TRUE, TRUE, FALSE, &junk);
+ break;
+
+
+ case HCAT:
+
+ x = BreakTable(x, c);
+ break;
+
+
+ case COL_THR:
+
+ BreakJoinedGroup(Down(x), LastDown(x), nilobj, c,
+ &back(x,COLM), &fwd(x,COLM));
+ break;
+
+
+ case VCAT:
+
+ x = BreakVcat(x, c);
+ break;
+
+
+ default:
+
+ assert1(FALSE, "BreakObject:", Image(type(x)));
+ break;
+
+ }
+ assert( back(x, COLM) >= 0, "BreakObject: back(x, COLM) < 0!" );
+ assert( fwd(x, COLM) >= 0, "BreakObject: fwd(x, COLM) < 0!" );
+ debug2(DOB, DD, "] BreakObject returning %s,%s, x =",
+ EchoLength(back(x, COLM)), EchoLength(fwd(x, COLM)));
+ ifdebug(DOB, DD, DebugObject(x));
+ return x;
+} /* end BreakObject */