diff options
Diffstat (limited to 'z12.c')
-rw-r--r-- | z12.c | 1235 |
1 files changed, 1235 insertions, 0 deletions
@@ -0,0 +1,1235 @@ +/*@z12.c:Size Finder:MinSize()@***********************************************/ +/* */ +/* 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: z12.c */ +/* MODULE: Size Finder */ +/* EXTERNS: MinSize() */ +/* */ +/*****************************************************************************/ +#include "externs.h" +#define IG_LOOKING 0 +#define IG_NOFILE 1 +#define IG_BADFILE 2 +#define IG_BADSIZE 3 +#define IG_OK 4 + + +/*****************************************************************************/ +/* */ +/* KernLength(fnum, ch1, ch2, res) */ +/* */ +/* Set res to the kern length between ch1 and ch2 in font fnum, or 0 if */ +/* none. */ +/* */ +/*****************************************************************************/ + +static FULL_LENGTH KernLength(FONT_NUM fnum, FULL_CHAR ch1, FULL_CHAR ch2) +{ FULL_LENGTH res; + MAPPING m = font_mapping(finfo[fnum].font_table); + FULL_CHAR *unacc = MapTable[m]->map[MAP_UNACCENTED]; + int ua_ch1 = unacc[ch1]; + int ua_ch2 = unacc[ch2]; + int i = finfo[fnum].kern_table[ua_ch1], j; + if( i == 0 ) res = 0; + else + { FULL_CHAR *kc = finfo[fnum].kern_chars; + for( j = i; kc[j] > ua_ch2; j++ ); + res = (kc[j] == ua_ch2) ? + finfo[fnum].kern_sizes[finfo[fnum].kern_value[j]] : 0; + } + return res; +} /* end KernLength */ + + +/*****************************************************************************/ +/* */ +/* BuildSpanner(x) */ +/* */ +/* Build a spanning structure starting at x. */ +/* */ +/*****************************************************************************/ + +static BOOLEAN BuildSpanner(OBJECT x) +{ OBJECT link, prnt, y, hspanner, vspanner, end_link, t, hprnt, vprnt, spanobj; + BOOLEAN need_hspanner, need_vspanner; + debug1(DSF, DD, "BuildSpanner(%s)", EchoObject(x)); + assert( type(x) == START_HVSPAN || type(x) == START_HSPAN || + type(x) == START_VSPAN , "BuildSpanner: type(x) != SPAN!" ); + Child(spanobj, Down(x)); + assert(Up(spanobj) == LastUp(spanobj), "BuildSpanner: spanobj!" ); + DeleteLink(Up(spanobj)); + + need_hspanner = (type(x) == START_HVSPAN || type(x) == START_HSPAN); + if( need_hspanner ) + { + /* check that column context is legal, if not exit with FALSE */ + Parent(hprnt, UpDim(x, COLM)); + if( type(hprnt) != COL_THR ) + { + Error(12, 10, "%s deleted (not in column)", WARN,&fpos(x),Image(type(x))); + return FALSE; + } + + /* build hspanner object and interpose it between x and spanobj */ + New(hspanner, HSPANNER); + FposCopy(fpos(hspanner), fpos(x)); + spanner_broken(hspanner) = FALSE; + Link(x, hspanner); + Link(hspanner, spanobj); + + /* link later members of the spanner on the same row mark to hspanner */ + /* by definition this is every member across to the last @HSpan before a */ + /* @StartHVSpan or @StartHSpan or @StartVSpan or @VSpan or end of row */ + Parent(prnt, UpDim(x, ROWM)); + if( type(prnt) != ROW_THR ) + { + Error(12, 11, "%s symbol out of place", FATAL, &fpos(x), Image(type(x))); + } + assert(type(prnt) == ROW_THR, "BuildSpanner: type(prnt)!"); + spanner_sized(hspanner) = 0; + spanner_count(hspanner) = 1; + end_link = NextDown(UpDim(x, ROWM)); + for( link = NextDown(UpDim(x, ROWM)); link != prnt; link = NextDown(link) ) + { Child(y, link); + debug2(DSF, DD, " examining ver %s %s", Image(type(y)), y); + if( type(y) == HSPAN ) + end_link = NextDown(link); + else if( type(y) == START_HVSPAN || type(y) == START_HSPAN || + type(y) == START_VSPAN || type(y) == VSPAN ) + break; + } + for( link = NextDown(UpDim(x,ROWM)); link!=end_link; link = NextDown(link) ) + { + /* each of these components becomes @HSpan and is added to vspanner */ + Child(y, link); + New(t, HSPAN); + FposCopy(fpos(t), fpos(y)); + ReplaceNode(t, y); + DisposeObject(y); + Link(t, hspanner); + spanner_count(hspanner)++; + } + } + else Link(x, spanobj); + + need_vspanner = (type(x) == START_HVSPAN || type(x) == START_VSPAN); + if( need_vspanner ) + { + /* check that row context is legal, if not exit with FALSE */ + Parent(vprnt, UpDim(x, ROWM)); + if( type(vprnt) != ROW_THR ) + { + Error(12, 12, "%s deleted (not in row)", WARN, &fpos(x), Image(type(x))); + return FALSE; + } + + /* build vspanner object and interpose it between x and spanobj */ + New(vspanner, VSPANNER); + FposCopy(fpos(vspanner), fpos(x)); + spanner_broken(vspanner) = FALSE; + Link(x, vspanner); + Link(vspanner, spanobj); + + /* link later members of the spanner on the same column mark to vspanner */ + /* by definition this is every member down to the last @VSpan before a */ + /* @StartHVSpan or @StartHSpan or @StartVSpan or @HSpan or end of column */ + Parent(prnt, UpDim(x, COLM)); + assert(type(prnt) == COL_THR, "BuildSpanner: type(prnt)!"); + spanner_sized(vspanner) = 0; + spanner_count(vspanner) = 1; + end_link = NextDown(UpDim(x, COLM)); + for( link = NextDown(UpDim(x, COLM)); link != prnt; link = NextDown(link) ) + { Child(y, link); + debug2(DSF, DD, " examining hor %s %s", Image(type(y)), y); + if( type(y) == VSPAN ) + end_link = NextDown(link); + else if( type(y) == START_HVSPAN || type(y) == START_HSPAN || + type(y) == START_VSPAN || type(y) == HSPAN ) + break; + } + for( link = NextDown(UpDim(x,COLM)); link!=end_link; link = NextDown(link) ) + { + /* each of these components becomes @VSpan and is added to vspanner */ + Child(y, link); + New(t, VSPAN); + FposCopy(fpos(t), fpos(y)); + ReplaceNode(t, y); + DisposeObject(y); + Link(t, vspanner); + spanner_count(vspanner)++; + } + } + else Link(x, spanobj); + + debug2(DSF, DD, "BuildSpanner returning TRUE (rows = %d, cols = %d)", + need_vspanner ? spanner_count(vspanner) : 0, + need_hspanner ? spanner_count(hspanner) : 0); + ifdebug(DSF, DD, DebugObject(x)); + return TRUE; +} + + +/*****************************************************************************/ +/* */ +/* BOOLEAN FindSpannerGap(thr, cat_op, gp) */ +/* */ +/* For the purposes of calculating spanning spacing, find the gap between */ +/* this object and the preceding one under the nearest cat_op. */ +/* */ +/* If found, set &gp to the gap object and return TRUE; else return FALSE. */ +/* */ +/*****************************************************************************/ + +static BOOLEAN FindSpannerGap(OBJECT thr, unsigned dim, unsigned cat_op, + OBJECT *res) +{ OBJECT link, x; + + /* find nearest enclosing cat_op that we aren't the first element of */ + link = UpDim(thr, dim); + Parent(x, link); + while( (type(x) != cat_op || type(PrevDown(link)) != LINK) && Up(x) != x ) + { link = UpDim(x, dim); + Parent(x, link); + } + + /* if found and a gap precedes thr's component of it, return that gap */ + if( type(x) == cat_op && type(PrevDown(link)) == LINK ) + { Child(*res, PrevDown(link)); + assert(type(*res) == GAP_OBJ, "FindSpannerGap: type(*res)!" ); + } + else if( type(x) == HEAD && gall_dir(x)==dim && type(PrevDown(link))==LINK ) + { Child(*res, PrevDown(link)); + assert(type(*res) == GAP_OBJ, "FindSpannerGap (HEAD): type(*res)!" ); + nobreak(gap(*res)) = TRUE; + } + else *res = nilobj; + + debug1(DSF, DD, " FindSpannerGap returning %s", EchoObject(*res)); + return (*res != nilobj); +} + + +/*****************************************************************************/ +/* */ +/* void SpannerAvailableSpace(x, dim, rb, rf) */ +/* */ +/* Work out the total space available to hold this spanner, and set */ +/* (*rb, *rf) to record this value. This space equals the total width */ +/* of all columns (and their intervening gaps) spanned, with the mark */ +/* of the last column being the one separating rb from rf. */ +/* */ +/*****************************************************************************/ + +void SpannerAvailableSpace(OBJECT y, int dim, FULL_LENGTH *resb, + FULL_LENGTH *resf) +{ OBJECT slink, s, thr, gp, prevthr; + FULL_LENGTH b, f; + unsigned thr_type, cat_type; + + assert( type(y) == HSPANNER || type(y) == VSPANNER, "SpannerAvail!"); + debug4(DSF, DD, "SpannerAvailableSpace(%d %s %s, %s)", + spanner_count(y), Image(type(y)), EchoObject(y), dimen(dim)); + if( dim == COLM ) + { thr_type = COL_THR; + cat_type = HCAT; + } + else + { thr_type = ROW_THR; + cat_type = VCAT; + } + + /* first calculate the total space consumed in earlier spans */ + /* Invariant: (b, f) is the size up to and including prev */ + /* */ + prevthr = nilobj; + for( slink = Up(y); slink != y; slink = NextUp(slink) ) + { Parent(s, slink); + Parent(thr, UpDim(s, dim)); + if( type(thr) == thr_type ) + { + assert( thr_state(thr) == SIZED, "SpannerAvailableSpace: thr_state!" ); + if( prevthr == nilobj ) + { + /* this is the first column spanned over */ + b = back(thr, dim); + f = fwd(thr, dim); + debug4(DSF, DD, " first component %s,%s: b = %s, f = %s", + EchoLength(back(thr, dim)), EchoLength(fwd(thr, dim)), + EchoLength(b), EchoLength(f)); + } + else if( FindSpannerGap(thr, dim, cat_type, &gp) ) + { + /* this is a subquent column spanned over */ + b += MinGap(fwd(prevthr, dim), back(thr, dim), fwd(thr, dim), &gap(gp)); + f = fwd(thr, dim); + debug5(DSF, DD, " later component %s,%s: gp = %s, b = %s, f = %s", + EchoLength(back(thr, dim)), EchoLength(fwd(thr, dim)), EchoObject(gp), + EchoLength(b), EchoLength(f)); + } + else + { + Error(12, 13, "search for gap preceding %s failed, using zero", + WARN, &fpos(s), Image(type(s))); + b += fwd(prevthr, dim) + back(thr, dim); + f = fwd(thr, dim); + debug4(DSF, DD, " later component %s,%s: (no gap), b = %s, f = %s", + EchoLength(back(thr, dim)), EchoLength(fwd(thr, dim)), + EchoLength(b), EchoLength(f)); + } + } + else + Error(12, 14, "%s deleted (out of place)", WARN,&fpos(s),Image(type(s))); + prevthr = thr; + } + + *resb = b; + *resf = f; + SetConstraint(constraint(y), MAX_FULL_LENGTH, b+f, MAX_FULL_LENGTH); + debug2(DSF, DD, "SpannerAvailableSpace returning %s,%s", + EchoLength(*resb), EchoLength(*resf)); +} /* end SpannerAvailableSpace */ + + +/*****************************************************************************/ +/* */ +/* OBJECT MinSize(x, dim, extras) */ +/* */ +/* Set fwd(x, dim) and back(x, dim) to their minimum possible values. */ +/* If dim == ROWM, construct an extras list and return it in *extras. */ +/* */ +/*****************************************************************************/ + +OBJECT MinSize(OBJECT x, int dim, OBJECT *extras) +{ OBJECT y, z, link, prev, t, g, full_name; + FULL_LENGTH b, f, dble_fwd, llx, lly, urx, ury; int status; + float fllx, flly, furx, fury; + BOOLEAN dble_found, found, will_expand, first_line, cp; + FILE *fp; FULL_CHAR buff[MAX_BUFF]; + + debug2(DSF, DD, "[ MinSize( %s, %s, extras )", EchoObject(x), dimen(dim)); + ifdebug(DSF, DDD, DebugObject(x)); + + switch( type(x) ) + { + + case WORD: + case QWORD: + + if( dim == COLM ) FontWordSize(x); + break; + + + case CROSS: + case FORCE_CROSS: + + /* add index to the cross-ref */ + if( dim == ROWM ) + { New(z, cross_type(x)); /* CROSS_PREC, CROSS_FOLL or CROSS_FOLL_OR_PREC */ + debug2(DCR, DD, " MinSize CROSS: %ld %s", (long) x, EchoObject(x)); + actual(z) = x; + Link(z, x); /* new code to avoid disposal */ + Link(*extras, z); + debug2(DCR, DD, " MinSize index: %ld %s", (long) z, EchoObject(z)); + } + back(x, dim) = fwd(x, dim) = 0; + break; + + + case PAGE_LABEL: + + if( dim == ROWM ) + { New(z, PAGE_LABEL_IND); + actual(z) = x; + Link(z, x); + Link(*extras, z); + } + back(x, dim) = fwd(x, dim) = 0; + break; + + + case NULL_CLOS: + + back(x, dim) = fwd(x, dim) = 0; + break; + + + case HEAD: + + if( dim == ROWM ) + { + /* replace the galley x by a dummy closure y */ + New(y, NULL_CLOS); + FposCopy(fpos(y), fpos(x)); + ReplaceNode(y, x); + + if( has_key(actual(x)) ) + { + /* galley is sorted, make insinuated cross-reference */ + New(z, foll_or_prec(x)); + pinpoint(z) = y; + Child(t, Down(x)); + actual(z) = CrossMake(whereto(x), t, (int) type(z)); + Link(*extras, z); + DisposeObject(x); + debug1(DCR, DDD, " MinSize: %s", EchoObject(z)); + } + else + { + /* galley is following, make UNATTACHED */ + New(z, UNATTACHED); Link(z, x); + pinpoint(z) = y; + Link(*extras, z); + debug1(DCR, DDD, " MinSize: %s", EchoObject(z)); + } + x = y; /* now sizing y, not x */ + back(x, COLM) = fwd(x, COLM) = 0; /* fix non-zero size @Null bug!! */ + } + else + { + debug2(DGT, D, "MinSize setting external_ver(%s %s) = FALSE", + Image(type(x)), SymName(actual(x))); + external_ver(x) = external_hor(x) = FALSE; + } + back(x, dim) = fwd(x, dim) = 0; + break; + + + case CLOSURE: + + assert( !has_target(actual(x)), "MinSize: CLOSURE has target!" ); + if( dim == ROWM ) + { if( indefinite(actual(x)) ) + { New(z, RECEPTIVE); + actual(z) = x; + Link(*extras, z); + debug1(DCR, DDD, " MinSize: %s", EchoObject(z)); + } + else if( recursive(actual(x)) ) + { New(z, RECURSIVE); + actual(z) = x; + Link(*extras, z); + debug1(DCR, DDD, " MinSize: %s", EchoObject(z)); + } + else + { assert(FALSE, "MinSize: definite non-recursive closure"); + } + } + else + { + debug2(DGT, DD, "MinSize setting external_ver(%s %s) = FALSE", + Image(type(x)), SymName(actual(x))); + external_ver(x) = external_hor(x) = FALSE;/* nb must be done here!*/ + } + back(x, dim) = fwd(x, dim) = 0; + break; + + + case ONE_COL: + case ONE_ROW: + case HCONTRACT: + case VCONTRACT: + case HLIMITED: + case VLIMITED: + + Child(y, Down(x)); + y = MinSize(y, dim, extras); + back(x, dim) = back(y, dim); + fwd(x, dim) = fwd(y, dim); + break; + + + case BACKGROUND: + + Child(y, Down(x)); + y = MinSize(y, dim, extras); + Child(y, LastDown(x)); + y = MinSize(y, dim, extras); + back(x, dim) = back(y, dim); + fwd(x, dim) = fwd(y, dim); + break; + + + case START_HVSPAN: + case START_HSPAN: + case START_VSPAN: + case HSPAN: + case VSPAN: + + /* if first touch, build the spanner */ + if( (type(x) == START_HVSPAN || type(x) == START_HSPAN || + type(x) == START_VSPAN) && dim == COLM ) + { + if( !BuildSpanner(x) ) + { + t = MakeWord(WORD, STR_EMPTY, &fpos(x)); + ReplaceNode(t, x); + x = t; + back(x, COLM) = fwd(x, COLM) = 0; + break; + } + } + + /* if first vertical touch, break if necessary */ + if( (type(x) == START_HVSPAN || type(x) == START_HSPAN) && dim == ROWM ) + { CONSTRAINT c; + + /* find the HSPANNER */ + Child(t, DownDim(x, COLM)); + assert( type(t) == HSPANNER, "MinSize/SPAN: type(t) != HSPANNER!" ); + + /* find the available space for this HSPANNER and break it */ + SpannerAvailableSpace(t, COLM, &b, &f); + SetConstraint(c, MAX_FULL_LENGTH, b+f, MAX_FULL_LENGTH); + debug2(DSF,D, " BreakObject(%s,%s)",EchoObject(t),EchoConstraint(&c)); + t = BreakObject(t, &c); + spanner_broken(t) = TRUE; + } + + /* make sure that HSPAN links to HSPANNER, VSPAN to VSPANNER */ + /* NB must follow breaking since that could affect the value of y */ + Child(y, DownDim(x, dim)); + if( (type(x) == HSPAN && type(y) != HSPANNER) || + (type(x) == VSPAN && type(y) != VSPANNER) ) + { + if( dim == COLM ) + Error(12, 15, "%s replaced by empty object (out of place)", + WARN, &fpos(x), Image(type(x))); + back(x, dim) = fwd(x, dim) = 0; + break; + } + + /* now size the object */ + if( (type(x)==HSPAN && dim==ROWM) || (type(x)==VSPAN && dim==COLM) ) + { + /* perp dimension, covered by preceding @Span, so may be zero. */ + back(x, dim) = fwd(x, dim) = 0; + } + else if( type(y) != HSPANNER && type(y) != VSPANNER ) + { + /* no spanning in this dimension */ + MinSize(y, dim, extras); + back(x, dim) = back(y, dim); + fwd(x, dim) = fwd(y, dim); + } + else if( ++spanner_sized(y) != spanner_count(y) ) + { + /* not the last column or row, so say zero */ + back(x, dim) = fwd(x, dim) = 0; + } + else + { + /* this is the last column or row of a spanner. Its width is its */ + /* natural width minus anything that will fit over the top of the */ + /* things it spans. */ + + MinSize(y, dim, extras); + SpannerAvailableSpace(y, dim, &b, &f); + back(x, dim) = 0; + fwd(x, dim) = find_max(size(y, dim) - b, 0); + debug3(DSF, DD, " size(y, %s) = %s,%s", dimen(dim), + EchoLength(back(y, dim)), EchoLength(fwd(y, dim))); + } + debug4(DSF, DD, "finishing MinSize(%s) of %s span, reporting %s,%s", + dimen(dim), spanner_count(y) != 1 ? "multi-column" : "one-column", + EchoLength(back(x, dim)), EchoLength(fwd(x, dim))); + break; + + + case HSPANNER: + case VSPANNER: + + assert( (type(x) == HSPANNER) == (dim == COLM), "MinSize: SPANNER!" ); + Child(y, Down(x)); + y = MinSize(y, dim, extras); + back(x, dim) = back(y, dim); + fwd(x, dim) = fwd(y, dim); + break; + + + case HEXPAND: + case VEXPAND: + + Child(y, Down(x)); + y = MinSize(y, dim, extras); + back(x, dim) = back(y, dim); + fwd(x, dim) = fwd(y, dim); + + /* insert index into *extras for expanding later */ + if( dim == ROWM ) + { New(z, EXPAND_IND); + actual(z) = x; + Link(*extras, z); + /* Can't do Link(z, x) because Constrained goes up and finds z */ + debug2(DCR, DD, " MinSize index: %ld %s", (long) z, EchoObject(z)); + } + break; + + + case PLAIN_GRAPHIC: + case GRAPHIC: + + Child(y, LastDown(x)); + y = MinSize(y, dim, extras); + back(x, dim) = back(y, dim); + fwd(x, dim) = fwd(y, dim); + break; + + + case HCOVER: + case VCOVER: + + /* work out size and set to 0 if parallel */ + Child(y, Down(x)); + y = MinSize(y, dim, extras); + if( (dim == COLM) == (type(x) == HCOVER) ) + back(x, dim) = fwd(x, dim) = 0; + else + { back(x, dim) = back(y, dim); + fwd(x, dim) = fwd(y, dim); + } + + /* insert index into *extras for revising size later */ + if( dim == ROWM ) + { New(z, COVER_IND); + actual(z) = x; + Link(*extras, z); + /* Can't do Link(z, x) because Constrained goes up and finds z */ + debug2(DCR, DD, " MinSize index: %ld %s", (long) z, EchoObject(z)); + } + break; + + + case HSCALE: + case VSCALE: + + /* work out size and set to 0 if parallel */ + Child(y, Down(x)); + y = MinSize(y, dim, extras); + if( (dim == COLM) == (type(x) == HSCALE) ) + back(x, dim) = fwd(x, dim) = 0; + else + { back(x, dim) = back(y, dim); + fwd(x, dim) = fwd(y, dim); + } + break; + + + case ROTATE: + + Child(y, Down(x)); + if( dim == COLM ) + { y = MinSize(y, COLM, extras); + New(whereto(x), ACAT); + y = MinSize(y, ROWM, &whereto(x)); + RotateSize(&back(x, COLM), &fwd(x, COLM), &back(x, ROWM), &fwd(x, ROWM), + y, sparec(constraint(x))); + } + else + { TransferLinks(Down(whereto(x)), whereto(x), *extras); + Dispose(whereto(x)); + } + break; + + + case SCALE: + + Child(y, Down(x)); + y = MinSize(y, dim, extras); + if( dim == COLM ) + { back(x, dim) = (back(y, dim) * bc(constraint(x))) / SF; + fwd(x, dim) = (fwd(y, dim) * bc(constraint(x))) / SF; + if( bc(constraint(x)) == 0 ) /* Lout-supplied factor required */ + { New(z, SCALE_IND); + actual(z) = x; + Link(*extras, z); + debug1(DSF, DDD, " MinSize: %s", EchoObject(z)); + vert_sized(x) = FALSE; + } + } + else + { back(x, dim) = (back(y, dim) * fc(constraint(x))) / SF; + fwd(x, dim) = (fwd(y, dim) * fc(constraint(x))) / SF; + vert_sized(x) = TRUE; + } + break; + + + case KERN_SHRINK: + + + Child(y, LastDown(x)); + y = MinSize(y, dim, extras); + if( dim == COLM ) + { FULL_CHAR ch_left, ch_right; FULL_LENGTH ksize; + debug3(DSF, DD, "MinSize(%s,%s %s, COLM)", + EchoLength(back(y, COLM)), EchoLength(fwd(y, COLM)), + EchoObject(x)); + + /* default value if don't find anything */ + back(x, dim) = back(y, dim); + fwd(x, dim) = fwd(y, dim); + + /* find first character of left parameter */ + ch_right = (FULL_CHAR) '\0'; + Child(y, Down(x)); + while( type(y) == ACAT ) + { Child(y, Down(y)); + } + if( is_word(type(y)) ) ch_right = string(y)[0]; + + /* find last character of right parameter */ + ch_left = (FULL_CHAR) '\0'; + Child(y, LastDown(x)); + while( type(y) == ACAT ) + { Child(y, LastDown(y)); + } + if( is_word(type(y)) ) ch_left = string(y)[StringLength(string(y))-1]; + + /* adjust if successful */ + if( ch_left != (FULL_CHAR) '\0' && ch_right != (FULL_CHAR) '\0' ) + { + ksize = KernLength(word_font(y), ch_left, ch_right); + debug4(DSF, DD, " KernLength(%s, %c, %c) = %s", + FontName(word_font(y)), (char) ch_left, (char) ch_right, + EchoLength(ksize)); + fwd(x, dim) += ksize; + } + + } + else + { back(x, dim) = back(y, dim); + fwd(x, dim) = fwd(y, dim); + } + break; + + + case WIDE: + + Child(y, Down(x)); + y = MinSize(y, dim, extras); + if( dim == COLM ) + { y = BreakObject(y, &constraint(x)); + assert( FitsConstraint(back(y, dim), fwd(y, dim), constraint(x)), + "MinSize: BreakObject failed to fit!" ); + back(x, dim) = back(y, dim); + fwd(x, dim) = fwd(y, dim); + EnlargeToConstraint(&back(x, dim), &fwd(x, dim), &constraint(x)); + } + else + { back(x, dim) = back(y, dim); + fwd(x, dim) = fwd(y, dim); + } + break; + + + case HIGH: + + Child(y, Down(x)); + y = MinSize(y, dim, extras); + if( dim == ROWM ) + { if( !FitsConstraint(back(y, dim), fwd(y, dim), constraint(x)) ) + { Error(12, 1, "forced to enlarge %s from %s to %s", WARN, &fpos(x), + KW_HIGH, EchoLength(bfc(constraint(x))), EchoLength(size(y, dim))); + debug0(DSF, DD, "offending object was:"); + ifdebug(DSF, DD, DebugObject(y)); + SetConstraint(constraint(x), MAX_FULL_LENGTH, size(y, dim), MAX_FULL_LENGTH); + } + back(x, dim) = back(y, dim); + fwd(x, dim) = fwd(y, dim); + EnlargeToConstraint(&back(x, dim), &fwd(x, dim), &constraint(x)); + } + else + { back(x, dim) = back(y, dim); + fwd(x, dim) = fwd(y, dim); + } + break; + + + case HSHIFT: + case VSHIFT: + + Child(y, Down(x)); + y = MinSize(y, dim, extras); + if( (dim == COLM) == (type(x) == HSHIFT) ) + { f = FindShift(x, y, dim); + back(x, dim) = find_min(MAX_FULL_LENGTH, find_max(0, back(y, dim) + f)); + fwd(x, dim) = find_min(MAX_FULL_LENGTH, find_max(0, fwd(y, dim) - f)); + } + else + { back(x, dim) = back(y, dim); + fwd(x, dim) = fwd(y, dim); + } + break; + + + case SPLIT: + + link = DownDim(x, dim); Child(y, link); + y = MinSize(y, dim, extras); + back(x, dim) = back(y, dim); + fwd(x, dim) = fwd(y, dim); + break; + + + case ACAT: + case HCAT: + case VCAT: + + if( (dim == ROWM) == (type(x) == VCAT) ) + { + /********************************************************************/ + /* */ + /* Calculate sizes parallel to join direction; loop invariant is: */ + /* */ + /* If prev == nilobj, there are no definite children equal to */ + /* or to the left of Child(link). */ + /* If prev != nilobj, prev is the rightmost definite child to */ + /* the left of Child(link), and (b, f) is the total size up */ + /* to the mark of prev i.e. not including fwd(prev). */ + /* g is the most recent gap, or nilobj if none found yet. */ + /* will_expand == TRUE when a gap is found that is likely to */ + /* enlarge when ActualGap is called later on. */ + /* */ + /********************************************************************/ + + prev = g = nilobj; will_expand = FALSE; must_expand(x) = FALSE; + for( link = Down(x); link != x; link = NextDown(link) ) + { Child(y, link); + if( is_index(type(y)) ) + { if( dim == ROWM ) + { link = PrevDown(link); + MoveLink(NextDown(link), *extras, PARENT); + } + continue; + } + else if( type(y) == type(x) ) + { link = PrevDown(link); + TransferLinks(Down(y), y, NextDown(link)); + DisposeChild(Up(y)); + continue; + } + else if( type(y) == GAP_OBJ ) g = y; + else /* calculate size of y and accumulate it */ + { if( is_word(type(y)) ) + { if( dim == COLM ) + { + /* compress adjacent words if compatible */ + if( prev != nilobj && width(gap(g)) == 0 && nobreak(gap(g)) && + type(x) == ACAT && + is_word(type(prev)) && vspace(g) + hspace(g) == 0 && + units(gap(g)) == FIXED_UNIT && + mode(gap(g)) == EDGE_MODE && !mark(gap(g)) && + word_font(prev) == word_font(y) && + word_colour(prev) == word_colour(y) && + word_language(prev) == word_language(y) && + underline(prev) == underline(y) && + NextDown(NextDown(Up(prev))) == link + ) + { + unsigned typ; + debug3(DSF, DD, "compressing %s and %s at %s", + EchoObject(prev), EchoObject(y), EchoFilePos(&fpos(prev))); + if( StringLength(string(prev)) + StringLength(string(y)) + >= MAX_BUFF ) + Error(12, 2, "word %s%s is too long", FATAL, &fpos(prev), + string(prev), string(y)); + typ = type(prev) == QWORD || type(y) == QWORD ? QWORD : WORD; + y = MakeWordTwo(typ, string(prev), string(y), &fpos(prev)); + word_font(y) = word_font(prev); + word_colour(y) = word_colour(prev); + word_language(y) = word_language(prev); + word_hyph(y) = word_hyph(prev); + underline(y) = underline(prev); + FontWordSize(y); + Link(Up(prev), y); + DisposeChild(Up(prev)); + DisposeChild(Up(g)); + DisposeChild(link); + prev = y; + link = Up(prev); + continue; + } + + FontWordSize(y); + debug4(DSF, DD, "FontWordSize( %s ) font %d = %s,%s", + EchoObject(y), word_font(y), + EchoLength(back(y, COLM)), EchoLength(fwd(y, COLM))); + } + } + else y = MinSize(y, dim, extras); + + if( is_indefinite(type(y)) ) + { + /* error if preceding gap has mark */ + if( g != nilobj && mark(gap(g)) ) + { Error(12, 3, "^ deleted (it may not precede this object)", + WARN, &fpos(y)); + mark(gap(g)) = FALSE; + } + + /* error if next unit is used in preceding gap */ + if( g != nilobj && units(gap(g)) == NEXT_UNIT ) + { Error(12, 4, "gap replaced by 0i (%c unit not allowed here)", + WARN, &fpos(y), CH_UNIT_WD); + units(gap(g)) = FIXED_UNIT; + width(gap(g)) = 0; + } + } + else + { + /* calculate running total length */ + if( prev == nilobj ) b = back(y, dim), f = 0; + else + { FULL_LENGTH tmp; + tmp = MinGap(fwd(prev,dim), back(y,dim), fwd(y, dim), &gap(g)); + assert(g!=nilobj && mode(gap(g))!=NO_MODE, "MinSize: NO_MODE!"); + if( units(gap(g)) == FIXED_UNIT && mode(gap(g)) == TAB_MODE ) + { + f = find_max(width(gap(g)) + back(y, dim), f + tmp); + } + else + { + f = f + tmp; + } + if( units(gap(g)) == FRAME_UNIT && width(gap(g)) > FR ) + will_expand = TRUE; + if( units(gap(g)) == AVAIL_UNIT && mark(gap(g)) && width(gap(g)) > 0 ) + Error(12, 9, "mark alignment incompatible with centring or right justification", + WARN, &fpos(g)); + /* *** + if( units(gap(g)) == AVAIL_UNIT && width(gap(g)) >= FR ) + will_expand = TRUE; + *** */ + if( mark(gap(g)) ) b += f, f = 0; + } + prev = y; + } + debug2(DSF,DD," b = %s, f = %s",EchoLength(b),EchoLength(f)); + } + } /* end for */ + + if( prev == nilobj ) b = f = 0; + else f += fwd(prev, dim); + back(x, dim) = find_min(MAX_FULL_LENGTH, b); + fwd(x, dim) = find_min(MAX_FULL_LENGTH, f); + + if( type(x) == ACAT && will_expand ) fwd(x, COLM) = MAX_FULL_LENGTH; + } + else + { + /********************************************************************/ + /* */ + /* Calculate sizes perpendicular to join direction */ + /* */ + /* Loop invariant: */ + /* */ + /* if found, (b, f) is the size of x, from the last // or from */ + /* the start, up to link exclusive. Else no children yet. */ + /* If dble_found, a previous // exists, and (0, dble_fwd) is */ + /* the size of x from the start up to that //. */ + /* */ + /********************************************************************/ + + dble_found = found = FALSE; dble_fwd = 0; + for( link = Down(x); link != x; link = NextDown(link) ) + { Child(y, link); + debug4(DSF, DD, " %s in %s, y = %s %s", dimen(dim), + Image(type(x)), Image(type(y)), EchoObject(y)); + if( is_index(type(y)) ) + { if( dim == ROWM ) + { link = PrevDown(link); + MoveLink(NextDown(link), *extras, PARENT); + } + continue; + } + else if( type(y) == type(x) ) + { link = PrevDown(link); + TransferLinks(Down(y), y, NextDown(link)); + DisposeChild(Up(y)); + continue; + } + else if( type(y) == GAP_OBJ ) + { assert( found, "MinSize/VCAT/perp: !found!" ); + if( !join(gap(y)) ) + { + /* found // or || operator, so end current group */ + dble_found = TRUE; + dble_fwd = find_max(dble_fwd, b + f); + debug1(DSF, DD, " endgroup, dble_fwd: %s", EchoLength(dble_fwd)); + found = FALSE; + } + } + else /* found object */ + { + /* calculate size of subobject y */ + if( is_word(type(y)) ) + { if( dim == COLM ) FontWordSize(y); + } + else y = MinSize(y, dim, extras); + if( found ) + { b = find_max(b, back(y, dim)); + f = find_max(f, fwd(y, dim)); + } + else + { b = back(y, dim); + f = fwd(y, dim); + found = TRUE; + } + debug2(DSF,DD, " b: %s, f: %s", EchoLength(b), EchoLength(f)); + } + } /* end for */ + assert( found, "MinSize/VCAT/perp: !found (2)!" ); + + /* finish off last group */ + if( dble_found ) + { back(x, dim) = 0; + dble_fwd = find_max(dble_fwd, b + f); + fwd(x, dim) = find_min(MAX_FULL_LENGTH, dble_fwd); + debug1(DSF, DD, " end group, dble_fwd: %s", EchoLength(dble_fwd)); + } + else + { back(x, dim) = b; + fwd(x, dim) = f; + } + } /* end else */ + break; + + + case COL_THR: + + assert( dim == COLM, "MinSize/COL_THR: dim!" ); + if( thr_state(x) == NOTSIZED ) + { assert( Down(x) != x, "MinSize/COL_THR: Down(x)!" ); + + /* first size all the non-spanning members of the thread */ + debug1(DSF, DD, "[[ starting sizing %s", Image(type(x))); + b = f = 0; + for( link = Down(x); link != x; link = NextDown(link) ) + { Child(y, link); + assert( type(y) != GAP_OBJ, "MinSize/COL_THR: GAP_OBJ!" ); + if( type(y) != START_HVSPAN && type(y) != START_HSPAN && + type(y) != HSPAN && type(y) != VSPAN ) + { y = MinSize(y, dim, extras); + b = find_max(b, back(y, dim)); + f = find_max(f, fwd(y, dim)); + } + } + back(x, dim) = b; + fwd(x, dim) = f; + thr_state(x) = SIZED; + debug3(DSF, DD, "][ middle sizing %s (%s,%s)", Image(type(x)), + EchoLength(back(x, dim)), EchoLength(fwd(x, dim))); + + /* now size all the spanning members of the thread */ + /* these will use back(x, dim) and fwd(x, dim) during sizing */ + for( link = Down(x); link != x; link = NextDown(link) ) + { Child(y, link); + assert( type(y) != GAP_OBJ, "MinSize/COL_THR: GAP_OBJ!" ); + if( type(y) == START_HVSPAN || type(y) == START_HSPAN || + type(y) == HSPAN || type(y) == VSPAN ) + { y = MinSize(y, dim, extras); + b = find_max(b, back(y, dim)); + f = find_max(f, fwd(y, dim)); + } + } + back(x, dim) = b; + fwd(x, dim) = f; + debug3(DSF, DD, "]] end sizing %s (%s,%s)", Image(type(x)), + EchoLength(back(x, dim)), EchoLength(fwd(x, dim))); + } + break; + + + case ROW_THR: + + assert( dim == ROWM, "MinSize/ROW_THR: dim!" ); + if( thr_state(x) == NOTSIZED ) + { assert( Down(x) != x, "MinSize/ROW_THR: Down(x)!" ); + + /* first size all the non-spanning members of the thread */ + debug1(DSF, D, "[[ starting sizing %s", Image(type(x))); + b = f = 0; + for( link = Down(x); link != x; link = NextDown(link) ) + { Child(y, link); + assert( type(y) != GAP_OBJ, "MinSize/COL_THR: GAP_OBJ!" ); + if( type(y) != START_HVSPAN && type(y) != START_VSPAN && + type(y) != HSPAN && type(y) != VSPAN ) + { y = MinSize(y, dim, extras); + debug5(DSF, D, " MinSize(%s) has size %s,%s -> %s,%s", + Image(type(y)), EchoLength(back(y, dim)), EchoLength(fwd(y, dim)), + EchoLength(b), EchoLength(f)); + b = find_max(b, back(y, dim)); + f = find_max(f, fwd(y, dim)); + } + } + back(x, dim) = b; + fwd(x, dim) = f; + thr_state(x) = SIZED; + debug3(DSF, D, "][ middle sizing %s (%s,%s)", Image(type(x)), + EchoLength(back(x, dim)), EchoLength(fwd(x, dim))); + + /* now size all the spanning members of the thread */ + /* these will use back(x, dim) and fwd(x, dim) during sizing */ + for( link = Down(x); link != x; link = NextDown(link) ) + { Child(y, link); + assert( type(y) != GAP_OBJ, "MinSize/COL_THR: GAP_OBJ!" ); + if( type(y) == START_HVSPAN || type(y) == START_VSPAN || + type(y) == HSPAN || type(y) == VSPAN ) + { y = MinSize(y, dim, extras); + back(x, dim) = find_max(back(x, dim), back(y, dim)); + fwd(x, dim) = find_max(fwd(x, dim), fwd(y, dim)); + debug5(DSF, D, " MinSize(%s) has size %s,%s -> %s,%s", + Image(type(y)), EchoLength(back(y, dim)), EchoLength(fwd(y, dim)), + EchoLength(back(x, dim)), EchoLength(fwd(x, dim))); + } + } + debug3(DSF, D, "]] end sizing %s (%s,%s)", Image(type(x)), + EchoLength(back(x, dim)), EchoLength(fwd(x, dim))); + } + break; + + + case INCGRAPHIC: + case SINCGRAPHIC: + + /* open file, check for initial %!, and hunt for %%BoundingBox line */ + /* according to DSC Version 3.0, the BoundingBox parameters must be */ + /* integers; but we read them as floats and truncate since files */ + /* with fractional values seem to be common in the real world */ + if( dim == ROWM ) break; + status = IG_LOOKING; + Child(y, Down(x)); + fp = OpenIncGraphicFile(string(y), type(x), &full_name, &fpos(y), &cp); + if( fp == NULL ) status = IG_NOFILE; + first_line = TRUE; + while( status == IG_LOOKING && StringFGets(buff, MAX_BUFF, fp) != NULL ) + { + if( first_line && !StringBeginsWith(buff, AsciiToFull("%!")) ) + status = IG_BADFILE; + else + { first_line = FALSE; + if( buff[0] == '%' + && StringBeginsWith(buff, AsciiToFull("%%BoundingBox:")) + && !StringContains(buff, AsciiToFull("(atend)")) ) + { if( sscanf( (char *) buff, "%%%%BoundingBox: %f %f %f %f", + &fllx, &flly, &furx, &fury) == 4 ) + { + status = IG_OK; + llx = fllx; + lly = flly; + urx = furx; + ury = fury; + } + else status = IG_BADSIZE; + } + } + } + + /* report error or calculate true size, depending on status */ + switch( status ) + { + case IG_NOFILE: + + Error(12, 5, "%s deleted (cannot open file %s)", WARN, &fpos(x), + type(x) == INCGRAPHIC ? KW_INCGRAPHIC : KW_SINCGRAPHIC, + string(full_name)); + incgraphic_ok(x) = FALSE; + back(x, COLM) = fwd(x, COLM) = back(x, ROWM) = fwd(x, ROWM) = 0; + break; + + case IG_LOOKING: + + Error(12, 6, "%s given zero size (no BoundingBox line in file %s)", + WARN, &fpos(x), + type(x) == INCGRAPHIC ? KW_INCGRAPHIC : KW_SINCGRAPHIC, + string(full_name)); + back(y, COLM) = fwd(y, COLM) = back(y, ROWM) = fwd(y, ROWM) = 0; + back(x, COLM) = fwd(x, COLM) = back(x, ROWM) = fwd(x, ROWM) = 0; + incgraphic_ok(x) = TRUE; + fclose(fp); + if( cp ) StringRemove(AsciiToFull(LOUT_EPS)); + break; + + case IG_BADFILE: + + Error(12, 7, "%s deleted (bad first line in file %s)", WARN, + &fpos(x), type(x) == INCGRAPHIC ? KW_INCGRAPHIC : KW_SINCGRAPHIC, + string(full_name)); + incgraphic_ok(x) = FALSE; + back(x, COLM) = fwd(x, COLM) = back(x, ROWM) = fwd(x, ROWM) = 0; + fclose(fp); + if( cp ) StringRemove(AsciiToFull(LOUT_EPS)); + break; + + case IG_BADSIZE: + + Error(12, 8, "%s given zero size (bad BoundingBox line in file %s)", + WARN, &fpos(x), + type(x) == INCGRAPHIC ? KW_INCGRAPHIC : KW_SINCGRAPHIC, + string(full_name)); + back(y, COLM) = fwd(y, COLM) = back(y, ROWM) = fwd(y, ROWM) = 0; + back(x, COLM) = fwd(x, COLM) = back(x, ROWM) = fwd(x, ROWM) = 0; + incgraphic_ok(x) = TRUE; + fclose(fp); + if( cp ) StringRemove(AsciiToFull(LOUT_EPS)); + break; + + case IG_OK: + + Child(y, Down(x)); + back(y, COLM) = llx; fwd(y, COLM) = urx; + back(y, ROWM) = lly; fwd(y, ROWM) = ury; + b = (urx - llx) * PT; + b = find_min(MAX_FULL_LENGTH, find_max(0, b)); + back(x, COLM) = fwd(x, COLM) = b / 2; + b = (ury - lly) * PT; + b = find_min(MAX_FULL_LENGTH, find_max(0, b)); + back(x, ROWM) = fwd(x, ROWM) = b / 2; + incgraphic_ok(x) = TRUE; + fclose(fp); + if( cp ) StringRemove(AsciiToFull(LOUT_EPS)); + break; + + } + DisposeObject(full_name); + break; + + + default: + + assert1(FALSE, "MinSize", Image(type(x))); + break; + + + } /* end switch */ + debug1(DSF, DD, "] MinSize returning, x = %s", EchoObject(x)); + debug3(DSF, DD, " (%s size is %s, %s)", dimen(dim), + EchoLength(back(x, dim)), EchoLength(fwd(x, dim)) ); + ifdebug(DSF, DDD, DebugObject(x)); + + assert(back(x, dim) >= 0, "MinSize: back(x, dim) < 0!"); + assert(fwd(x, dim) >= 0, "MinSize: fwd(x, dim) < 0!"); + + return x; +} /* end MinSize */ |