Sindbad~EG File Manager
/* M. Beeson, line breaking for Mathpert's display module */
/*
Original date 5.30.95
Previously modified 10.19.95
modified 3.29.99
12.26.99 modified two lines marked with the date.
1.11.00 modified break_minus
1.11.00 modified break_function
1.11.00 modified on_nextline
3.29.00 corrected break_minus at line 1037
3.29.00 added code at "increase the right coordinate" in lbreak1
3.29.00 modified break_minus not to leave a minus sign alone at the end of a line.
3.30.00 several modifications in lbreak, including the use of newlineflag to
ensure that the spec. about returning 0 is really followed.
3.30.00 use of lcopy in break_minus
4.4.00 modified at line 672
5.8.13 include <stddef.h>, removed include "heap.h"
11.5.23 introduced get_productspace, get_get_charspace, etc.
11.6.23 added svg_textwidth call
1.12.24 modified paren_displacement to eliminate global variable 'font' and
eliminated 'font' in other places too.
*/
#include <assert.h>
#include <stdlib.h> /* NULL */
#include <math.h> /* sqrt */
#include <stddef.h>
#include <string.h> /* memcpy */
#include "terms.h"
#include "display.h"
#include "display1.h"
#include "bigrect.h"
#include "lterm.h"
#include "bdisplay.h" /* strip */
#include "defns.h"
#include "vaux.h" // needed by termtoSVG.h
#ifdef SVG
#include "termtoSVG.h"
#endif
static int lbreak1(lterm *, BIGRECT *, long, long);
static int on_nextline(lterm *, BIGRECT *,long *,long);
static int neverbreak(lterm *);
static int hatetobreak(lterm *);
rectlist *rectlist_copy(rectlist *);
/* There must be separate functions to break any term whose arguments
do not appear visually in the same left-to-right order as they
occur as arguments of the term. Luckily there are only three
such functions:
*/
static int break_definite_integral(lterm *t, BIGRECT *sofar, long level,long xmax);
/* used for indexed sums and products too */
static int break_integral(lterm *t, BIGRECT *sofar, long level, long xmax);
static int break_diff(lterm *, BIGRECT*, long level, long xmax);
static int break_bignum(lterm *, BIGRECT*, long level, long xmax);
static int break_function(lterm *t, BIGRECT *sofar, long level, long xmax);
static int break_minus(lterm *t, BIGRECT *sofar, long level, long xmax);
static int break_power(lterm *t, BIGRECT *sofar, long level, long xmax);
static int lcopy(lterm *source, lterm *target);
static void destroy_lterm(lterm t);
/*_________________________________________________________________*/
static BIGRECT paper;
static int linespacing;
void set_paper(BIGRECT *r)
/* Called from Mathpert to tell this DLL what size the
window (or the paper if printing) is
*/
{ paper = *r;
linespacing = (int) papyrus_to_ypixel(16);
}
void get_paper(BIGRECT *r)
/* Needed in ldisplay to determine if an arg of a sum
is beginning a new line or not, but not exported from
this DLL. */
{ *r = paper;
}
/*_____________________________________________________________________*/
void SetBigRectEmpty( BIGRECT *r)
/* set all coordinates to zero */
{ r->left = r->right = r->top = r->bottom = 0;
}
/*_____________________________________________________________________*/
int UnionBigRect(BIGRECT *ans, const BIGRECT *r, const BIGRECT *s)
/* like UnionRect but for BIGRECT structures */
{ /* first check for empty rectangles, they should be ignored */
long min,max;
if(r->top == r->bottom || r->left == r->right)
{ *ans = *s;
if(s->top == s->bottom && s->left == s->right)
return 0;
return 1;
}
if(s->top == s->bottom || s->left == s->right)
{ *ans = *r;
return 1;
}
min = max = r->left;
if(r->right > max)
max = r->right;
if(r->right < min)
min = r->right;
if(s->left > max)
max = s->left;
if(s->left < min)
min = s->left;
if(s->right > max)
max = s->right;
if(s->right < min)
min = s->right;
ans->right = max;
ans->left = min;
min = max = r->bottom;
if(r->top > max)
max = r->top;
if(r->top < min)
min = r->top;
if(s->bottom > max)
max = s->bottom;
if(s->bottom < min)
min = s->bottom;
if(s->top > max)
max = s->top;
if(s->top < min)
min = s->top;
ans->top = min;
ans->bottom = max;
return 1;
}
/*___________________________________________________________________*/
void CopyBigRect (BIGRECT *ans, BIGRECT *r)
/* like CopyRect but for BIGRECTs */
{ ans->left = r->left;
ans->right = r->right;
ans->top = r->top;
ans->bottom = r->bottom;
}
/*____________________________________________________________________*/
void SetBigRect(BIGRECT *ans, long left, long top, long right, long bottom)
/* like SetRect */
{ ans->left = left;
ans->right = right;
ans->top = top;
ans->bottom = bottom;
}
/*_____________________________________________________________________*/
int lbreak(lterm *t)
/* Given an lterm prepared for one-line display, modify
its rectangles and allocate and fill in its .line fields so that
it will fit in the (width of) rectangle 'paper'
with lines broken appropriately. It is presumed
that t->r.left == paper.left.
Return value -1 indicates the term could not be broken, e.g.
a matrix too wide for the window. Other return values are as for
lbreak1.
*/
{ BIGRECT r;
r.top = t->r.top;
r.left = paper.left;
r.right = r.left; /* No part of current line is already filled */
r.bottom = r.top;
return lbreak1(t,&r,r.top,paper.right);
}
/*____________________________________________________________________*/
static int rectlist_length(rectlist *p)
{ rectlist *marker;
int count = 0;
for(marker=p; marker; marker=marker->next)
++count;
return count;
}
/*____________________________________________________________________*/
void shift(lterm *u, long x, long y)
/* move u by amount (x,y), i.e. add x to the .left and
.right fields of all rectangles of subterms of u, and y to
the .top and .bottom fields.
*/
{ unsigned i,n;
if(x==0 && y==0)
return; /* do nothing quickly */
u->r.right += x;
u->r.left += x;
u->r.top += y;
u->r.bottom += y;
if(ATOMIC(*u))
return;
n = ARITY(*u);
for(i=0;i<n;i++)
shift(LARGPTR(*u) + i, x,y);
}
/*____________________________________________________________________*/
static void ps_aux(lterm *t, long mason_dixon, long deltay)
/* move all subterms of t whose tops are below y-coordinate mason_dixon
down by deltay; that is, add deltay to the .top and .bottom of the
rectangles of each subterm of t whose .top exceeds mason_dixon.
*/
{ unsigned i,n;
if(t->r.top <= mason_dixon)
return; /* doing nothing */
t->r.top += deltay;
t->r.bottom += deltay;
if(ATOMIC(*t))
return;
n = ARITY(*t);
for(i=0;i<n;i++)
ps_aux(LARGPTR(*t)+i,mason_dixon,deltay);
}
/*____________________________________________________________________*/
static void partial_shift(lterm *t, long deltay)
/* Move the 2nd and further rows of t down by deltay */
{ long mason_dixon;
rectlist *marker;
if(t->linelist == NULL)
return; /* a one-line term */
mason_dixon = t->linelist->rect.bottom;
ps_aux(t,mason_dixon,deltay);
for(marker=t->linelist->next;marker; marker=marker->next)
{ marker->rect.top += deltay;
marker->rect.bottom += deltay;
}
}
/*____________________________________________________________________*/
static void shift_lastrow(lterm *t, long deltay)
/* Move the last row of t down by deltay */
{ long mason_dixon;
rectlist *marker;
if(t->linelist == NULL)
{ shift(t,0,deltay);
return;
}
for(marker=t->linelist; marker->next;marker=marker->next)
; /* point marker to last row */
mason_dixon = marker->rect.top - 2;
ps_aux(t,mason_dixon,deltay);
marker->rect.top += deltay;
marker->rect.bottom += deltay;
}
/*____________________________________________________________________*/
static int lbreak1(lterm *t, BIGRECT *sofar, long level, long xmax)
/* adjust the spacing of subterms of *t, breaking lines at
pixel coordinate xmax, and allocating the .line fields.
The rectangle *sofar tells
what part of the current line is already filled, and 'level'
is the pixel y-coordinate of the water level of the stuff
already in *sofar (if any), that is, if we want the water level of
the first line of t to line up with that level. More precisely,
level will be used to determine the water level of the first line of t,
unless it equals sofar->top, in which case it's assumed that this is
the first thing on the line. In that case t will be shifted vertically
till its top lines up with sofar->top.
The lterm *t is presumed at entrance
to lie to the right of sofar and low enough that t->r.top >= sofar->top;
Thus it will NOT have to be moved down, though the previous terms
on the line may later have to be moved down.
Horizontally, its placement is presumed correct at
entrance, except of course that it may extend too far to the right.
The lterm *t is not yet broken (at entrance),
so t->linelist is NULL. The first line of the broken term *t (at exit)
will have its water level lined up with 'level' if possible.
Otherwise its water level will come out lower, lining up the
top of *t with the top of *sofar instead of the levels,
and the calling function will have to adjust (that is, lower)
the rectangles already placed in *sofar.
Remember that the rectangle of an lterm is NOT meant to include the
parentheses, if it has parentheses, unless they are BARS, as for ABSFUNCTOR,
DET, or EVAL.
Return values:
1: This whole term fits on the current line. In this case
t->linelist == NULL. In all other cases t->linelist
points to a valid rectlist.
0: no part of this term can fit on the current line, all of it
went on subsequent lines.
2 or more: some part fits on the current line, and further lines
are also required. The return value is the total number of
lines used or partially used.
-1: failure.
*/
{ int err;
long deltay,deltax;
unsigned firstonline;
int nlines = 0; /* number of nonempty lines so far */
lterm *u, *v;
unsigned i,j;
unsigned n;
unsigned f = FUNCTOR(*t);
lterm savev;
BIGRECT saveit,savesofar,sofar_thisterm,savesofar_thisterm;
rectlist *marker, *saveline;
int secondtry = 0;
int newlineflag = 0;
if(t->block.rightbra)
{ deltax = papyrus_to_xpixel(paren_displacement(*t));
xmax -= deltax; /* be sure to leave room on the right for the paren */
t->block.rightbra = 0;
err = lbreak1(t,sofar,level,xmax);
if(err == -1)
return -1;
t->block.rightbra = 1;
sofar->right += deltax;
return err;
}
if(t->block.leftbra)
{ deltax = papyrus_to_xpixel(paren_displacement(*t));
sofar->right += deltax;
}
else
deltax = 0;
if(t->r.right <= xmax) /* It will fit on the current line */
{ sofar->right = t->r.right;
if(level == sofar->top)
{ /* This is the first term placed on this line */
shift(t,0,-(t->r.top - sofar->top));
/* Move t up so its top lines up with sofar->top */
}
if(t->r.bottom > sofar->bottom)
sofar->bottom = t->r.bottom;
return 1; /* doing nothing to t, the vertical placement will be adjusted later */
}
if(
t->r.left > paper.left + deltax && /* we aren't at the left margin, and... */
(neverbreak(t) || /* either t is unbreakable, or... */
( t->r.right - t->r.left < xmax-paper.left /* it fits on next line, and ... */
&&
( sofar->right > (paper.right + paper.left)/2 /* this line is at least half full */
||
hatetobreak(t) /* or we hate to break t, e.g. integrals, limits, fractions */
)
) ||
( f != '*' && f != '+' && sofar->right > 0.8 * (paper.right-paper.left))
/* or this line is 80 percent full, and t isn't a sum or product */
)
)
return on_nextline(t,sofar,&level,xmax);
if(neverbreak(t))
return -1; /* at the left margin, a term like MATRIX */
if(t->r.right - t->r.left < xmax-paper.left)
{ /* it WILL fit on the next line, but this line isn't yet half full,
so the code above passed it on. */
secondtry = 1;
/* if all else fails, at the end we'll go ahead and put it on
the next line. */
}
if(OBJECT(*t) && TYPE(*t) == BIGNUM)
return break_bignum(t,sofar,level,xmax);
if(OBJECT(*t))
return -1; /* can't break integers or doubles */
n = ARITY(*t);
if(f == INTEGRAL && n == 4)
return break_definite_integral(t,sofar,level,xmax);
if(f == SUM && n == 5)
{ /* a series written using ... */
/* The lterm *t contains as LARG(4,*t) a located term for the
visible sum */
err = lbreak1(LARGPTR(*t)+4,sofar,level,xmax);
if(err < 0)
return err;
/* so far the SUM part is unchanged, which is fine. */
t->r = LARG(4,*t).r;
t->block = LARG(4,*t).block;
t->path = LARG(4,*t).path;
t->linelist = LARG(4,*t).linelist;
return err;
}
if(f == SUM || f == PRODUCT)
return break_definite_integral(t,sofar,level,xmax); /* works for indexed sums */
if(f == INTEGRAL)
return break_integral(t,sofar,level,xmax);
if(f == DIFF)
return break_diff(t,sofar,level,xmax);
if(f == '-')
return break_minus(t,sofar,level,xmax);
if(f == '^')
return break_power(t,sofar,level,xmax);
if(PREFIX(f) || f == LOGB || (BESSELJ <= f && f <= BESSELK))
return break_function(t,sofar,level,xmax);
firstonline = 0; /* the least i such that part of LARG(i,*t) is on the current line */
for(i=0;i<n;i++)
{ /* At the top of this loop, when i > firstonline, sofar_thisterm
is a rectangle containing args firstonline...(i-1); but
when i==firstonline, sofar_thisterm is garbage unless i==0;
and *sofar is a rectangle containing all terms
so far placed on this line, that is, ones that preceded any
args of t, as well as the args of t or parts of args of t
(there may be parts of args with numbers < firstonline)
that are already on this line.
Level is the water level of that partial line, which will be
the same for sofar and sofar_thisterm, because the args have
been put in with their water levels correctly aligned.
The remaining args (which have not been placed yet)
have their tops in the same (vertical)
position relative to sofar->top as they did originally to the
top of the unbroken term.
When i == 0, sofar may contain terms put on the current line
before this call. It may also be meaningless, which is
indicated by level == sofar->top.
When i == firstonline > 0, sofar is garbage and must be set. (?)
*/
if(i==firstonline)
SetBigRectEmpty(&sofar_thisterm);
v = LARGPTR(*t) + i;
saveit = v->r; /* rectangle where v is before breaking it */
savesofar = *sofar;
savesofar_thisterm = sofar_thisterm;
if(i==0)
/* save a copy of v in case it's needed 10 lines or so below */
{ err = lcopy(v,&savev);
if(err)
return -1; /* out of space in lcopy so can't break */
}
/* add space for a left parentheses if v needs it */
if(v->block.bra != BARS && v->block.leftbra) // 8.29.04
{ int parenspace = (int) papyrus_to_xpixel(paren_displacement(*v));
sofar->right += parenspace;
}
err = lbreak1(v,sofar,level,xmax);
if(err == -1)
/* Can't perform the requested break even by starting a new
line with this term */
{ if(secondtry)
return on_nextline(t,sofar,&level,xmax);
return -1;
}
if(err == 0 && i == 0)
{ if(secondtry)
{ *sofar = savesofar;
destroy_lterm(*v);
LARGREP(*t,0,savev);
return on_nextline(t,sofar,&level,xmax);
}
/* if !secondtry, don't give up by return -1; instead
go on down where v will be written on the next line. */
}
if(i==0)
destroy_lterm(savev);
if(err == 1)
{ /* v fits on this line. v has not had its vertical
placement altered. */
if(i == firstonline && /* first arg of THIS term on this line */
sofar->top == level /* no terms before t on this line */
)
{ level = sofar->top + papyrus_to_ypixel(v->block.wl);
deltay = v->r.top - sofar->top;
shift(v,0,-deltay); /* line up v at top of new line */
sofar_thisterm = v->r;
if(v->block.bra != BARS && (v->block.leftbra || v->block.rightbra))
{ int parenspace = (int) papyrus_to_xpixel(paren_displacement(*v));
if(v->block.leftbra)
sofar_thisterm.left -= parenspace;
if(v->block.rightbra)
sofar_thisterm.right += parenspace;
}
*sofar = sofar_thisterm;
}
else /* sofar contains some terms */
{ long vlevel = papyrus_to_ypixel(v->block.wl);
if(vlevel + v->r.top > level && v->r.top > sofar->top)
/* level of v is too low and there's some room to move v up */
{ deltay = vlevel+v->r.top - level;
if(v->r.top - sofar->top < deltay)
deltay = v->r.top - sofar->top;
shift(v,0,-deltay);
sofar ->bottom = MAXIMUM(savesofar.bottom, v->r.bottom);
}
if(level-sofar->top < vlevel)
{ /* level of v cannot be matched with previous
contents of this row unmoved. Move all the
previous args on this row down to line up
the levels.
*/
deltay = vlevel-level + sofar->top;
for(j=firstonline;j<i;j++)
shift_lastrow(LARGPTR(*t)+j,deltay);
level = sofar->top + vlevel;
/* equivalent to level += deltay, since
level + deltay == level + (vlvel-level) + sofar->top */
savesofar.bottom += deltay;
if(i > firstonline)
sofar_thisterm.bottom += deltay;
sofar ->bottom = MAXIMUM(savesofar.bottom, v->r.bottom);
sofar->right = v->r.right;
}
else if(v->r.top + vlevel < level)
{ /* level of v is too high, move it down */
deltay = level- v->r.top - vlevel;
shift(v,0,deltay);
/* That may push v through the bottom of
sofar; if so, then sofar must be enlarged: */
if(v->r.bottom > sofar->bottom)
sofar->bottom = v->r.bottom;
}
else /* fortuitously the levels already line up */
{ sofar->right = v->r.right;
if(v->r.bottom > sofar->bottom)
sofar->bottom = v->r.bottom;
}
/* Now update the rectangle sofar_thisterm so
it contains the new term just added to the line */
if(i==firstonline)
{ sofar_thisterm = v->r;
if(v->block.bra != BARS && (v->block.leftbra || v->block.rightbra))
{ int parenspace = (int) papyrus_to_xpixel(paren_displacement(*v));
if(v->block.leftbra)
{ sofar_thisterm.left -= parenspace;
sofar->left -= parenspace;
}
if(v->block.rightbra)
{ sofar_thisterm.right += parenspace;
sofar->right = parenspace;
}
}
}
else
{ BIGRECT temp;
UnionBigRect(&temp,&sofar_thisterm,&v->r);
sofar_thisterm = temp;
if(v->block.bra != BARS && (v->block.leftbra || v->block.rightbra))
{ int parenspace = (int) papyrus_to_xpixel(paren_displacement(*v));
if(v->block.rightbra)
{ sofar_thisterm.right += parenspace;
sofar->right += parenspace;
}
}
}
}
}
if(err == 0)
{ /* no part of v would fit on this line, but it will all fit
on the next line or lines, and its rectangles have been adjusted
for that, and v->linelist is at least a single node (if it
takes one line), or a longer list if it took more.
*/
if(i==0)
newlineflag = 1;
if(!v->linelist)
assert(0);
/* If this is the second line, t->linelist is still NULL */
if(!t->linelist)
{ t->linelist = (rectlist *) mallocate(sizeof(rectlist));
if(!t->linelist)
nospace();
if(i==0 && sofar_thisterm.left == sofar_thisterm.right)
{ /* no terms on this line yet */
t->linelist = rectlist_copy(v->linelist);
}
else
{ t->linelist->rect = sofar_thisterm;
/* level is still the level of the first line */
t->linelist->wl = (int) level;
t->linelist->prev = NULL;
t->linelist->next = rectlist_copy(v->linelist);
t->linelist->next->prev = t->linelist;
}
}
else /* this is the third or subsequent line, t->linelist exists */
{ for(marker=t->linelist; marker->next; marker=marker->next)
; /* point marker to the last node so far, which is the
line on which v wouldn't fit */
marker->next = rectlist_copy(v->linelist);
marker->next->prev = marker;
/* Up till now marker->rect only includes the first
term on that line; but more may have been put there
*/
marker->rect = savesofar;
marker->wl = (int) level;
}
firstonline = i;
SetBigRectEmpty(&sofar_thisterm);
nlines += rectlist_length(t->linelist);
/* Move remaining args down and over to line up with
new last line (so that they enjoy the same vertical
displacement from the top of the new line as they
formerly had from the top of the old line)
*/
for(marker = v->linelist; marker->next; marker=marker->next)
; /* point marker to last node */
deltax = saveit.right - marker->rect.right;
deltay = marker->rect.top - savesofar.top;
for(j=i+1;j<n;j++)
{ u = LARGPTR(*t)+j;
shift(u,-deltax,deltay);
}
level = marker->wl;
*sofar = marker->rect;
/* add space for parentheses if v had parens */
if(v->block.bra != BARS && (v->block.leftbra || v->block.rightbra))
{ int parenspace = (int) papyrus_to_xpixel(paren_displacement(*v));
if(v->block.leftbra)
sofar->left -= parenspace;
if(v->block.rightbra)
sofar->right += parenspace;
}
}
if(err > 1)
{ /* v was broken, and part of v is on this line, part on
the next. It's also possible that v occupied several
lines. Adjust the rest of the args' rectangles so they
line up with the last line done so far, which now
contains the last part of v.
What did occupy space from v->r.left to v->r.right
now occupies parts of two or more lines, the first
of which starts at v->r.left and the last of which
ends at lastnode->right, where lastnode is the
tail of the list v->linelist.
*/
/* add space for parentheses if v had parens */
if(v->block.bra != BARS && (v->block.leftbra || v->block.rightbra))
{
int parenspace = (int) papyrus_to_xpixel(paren_displacement(*v));
if(v->block.rightbra)
savesofar_thisterm.right += parenspace;
}
if(!v->linelist)
assert(0);
if(v->linelist->wl > level && i > firstonline)
{ /* move args firstonline,..,i-1 down to line up */
deltay = v->linelist->wl - level;
for(j=firstonline;j<i;j++)
shift_lastrow(LARGPTR(*t)+j,deltay);
level += deltay;
savesofar_thisterm.bottom += deltay;
/* Maybe the second and further lines of v have to
be moved down also. But if the first line of v was
tall enough they don't have to be moved down. */
if(savesofar.bottom + deltay > v->linelist->rect.bottom)
{ deltay = v->linelist->rect.bottom - savesofar.bottom + deltay;
/* This is the amount by which the remaining lines of
v have to be pushed down */
partial_shift(v,deltay);
}
}
if(!t->linelist)
{ /* The line just finished, on which part of v went,
was the first line */
t->linelist = (rectlist *) mallocate(sizeof(rectlist));
if(!t->linelist)
nospace();
*(t->linelist) = *(v->linelist);
t->linelist->prev = NULL;
marker = t->linelist;
}
else /* the line just finished was the second or greater */
{ for(marker=t->linelist; marker->next; marker=marker->next)
; /* point marker to last node */
}
/* Now line 'marker' must join with the first line of v->linelist,
Moreover line 'marker' as yet contains only the first term
or no term at all, other terms may have been put there */
saveline = marker;
marker->next = rectlist_copy(v->linelist->next);
/* pointer to the second line of v, which will replace line marker.
The former node marker->next, if there was one, is abandoned.
Memory will be cleaned up by reset_heap later. */
UnionBigRect(&marker->rect,&v->linelist->rect,&saveline->rect);
/* This combines the rectangles of the old marker and v->linelist */
if(marker->next)
marker->next->prev = marker; /* set the back pointer */
/* Next set marker->rect to include the args of t already on this
line plus what it contains already. */
if(savesofar_thisterm.top != savesofar_thisterm.bottom)
UnionBigRect(&marker->rect, &savesofar_thisterm,&marker->rect);
if(i==firstonline && v->block.leftbra)
{ int paren_correction = OBJECT(*v) ? get_parenwidth(v->block.size) : get_parenwidth(v->block.size) + get_parenspace(v->block.size);
marker->rect.left -= papyrus_to_xpixel(paren_correction);
}
for(; marker->next; marker = marker->next)
; /* point marker to the last node */
*sofar = marker->rect;
level = marker->wl; // added 4.4.00
if(v->block.bra != BARS && v->block.rightbra)
{ int parenspace2 = (int) papyrus_to_xpixel(paren_displacement(*v));
sofar->right += parenspace2;
sofar_thisterm.right += parenspace2;
marker->rect.right+= parenspace2;
}
deltax = saveit.right - marker->rect.right;
deltay = marker->rect.bottom + linespacing - saveit.top;
for(j=i+1;j<n;j++)
{ u = LARGPTR(*t)+j;
shift(u,-deltax,deltay);
}
*sofar = marker->rect;
nlines += nlines ? err - 1 : err;
firstonline = i;
}
}
if(t->linelist)
{ /* The last line still has for its rectangle only that
of the first arg on that line */
for(marker = t->linelist; marker->next; marker = marker->next)
; /* find the last node */
marker->rect = *sofar;
marker->wl = (int) level;
}
if(t->linelist == NULL && nlines == 0)
{ /* all args fit on one line; the term itself is a little longer,
perhaps one pixel longer due to roundoff error in coordinate
conversion, or perhaps part of the symbolism is postfix */
return 1;
}
/* Now adjust the rectangle of t itself */
if(t->linelist->rect.left == t->linelist->rect.right)
t->r = t->linelist->next->rect;
else
t->r = t->linelist->rect; /* the rectangle of the first line */
for(marker=t->linelist->next; marker; marker=marker->next)
{ if(marker->rect.right > t->r.right)
t->r.right = marker->rect.right;
if(marker->rect.left < t->r.left)
t->r.left = marker->rect.left;
t->r.bottom = marker->rect.bottom;
}
if(newlineflag)
return 0;
return t->linelist ? nlines : 0;
}
/*_________________________________________________________________*/
static int on_nextline(lterm *t, BIGRECT *sofar, long *level, long xmax)
/* called by lbreak1 when it decides t should start a new line.
Put t on the next line, using xmax for the maximum allowable
pixel x-coordinate. It may or may not fit. You must
shift it down enough to clear the bottom of sofar and
leave 'linespacing' between. *level is made equal to the
level of the last line of *t.
*/
{ BIGRECT q;
int err;
long deltax, deltay;
rectlist *marker;
if(t->block.leftbra)
deltax = t->r.left - (paper.left + paren_displacement(*t));
else
deltax = t->r.left - paper.left;
deltay = sofar->bottom - t->r.top + linespacing;
if(deltax==0)
return -1;
shift(t, -deltax, deltay);
q.left = q.right = paper.left;
q.top = sofar->bottom + linespacing;
q.bottom = q.top;
err = lbreak1(t,&q,q.top,xmax);
if(err <= 0)
{ shift(t,deltax,-deltay);
return -1;
}
if(err == 1)
{ /* It did fit on the next line, so t->linelist wasn't
allocated yet. Make t->linelist a list of length 1 */
t->linelist = (rectlist *) mallocate(sizeof(rectlist));
if(!t->linelist)
nospace();
t->linelist->rect = t->r;
t->linelist->wl = (int)( t->r.top + papyrus_to_ypixel(t->block.wl));
t->linelist->next = t->linelist->prev = NULL;
}
if(!t->linelist)
assert(0);
/* if it required more than one line, t->linelist was created already */
*sofar = q;
for(marker=t->linelist;marker->next;marker=marker->next)
; /* find the last node */
*level = marker->wl;
return 0; /* No part of t went on the original line */
}
/*_________________________________________________________________*/
static int neverbreak(lterm *t)
/* return 1 if t should never be broken */
{ unsigned f = FUNCTOR(*t);
if(f == '^' && t->block.extra)
return 1; /* an infix exponent cannot be broken. */
switch(f)
{ case MATRIX:
return 1;
case LINEARSYSTEM:
return 1;
case VECTOR:
return 1; /* vectors are used for rows of matrices and also
for column matrices. Since matrices can't be
broken there's no point trying to break vectors. */
case '/': /* fractions wider than the window are bblocked with FRACT;
fractions not wider than the window begin on a new line
if necessary */
return 1;
case BINOMIAL:
return 1;
case PR:
return 1;
case DIFF:
if(OBJECT(LARG(0,*t)) && TYPE(LARG(0,*t)) == BIGNUM)
return 0;
return ATOMIC(LARG(0,*t)) ? 1 : 0;
case ROOT:
return 1;
case SQRT:
return 1;
}
return 0;
}
/*_________________________________________________________________*/
static int hatetobreak(lterm *t)
/* Return 1 if it's ugly to break *t, so that even if the current
line isn't half full yet, if *t will fit on one line, we prefer
to begin a new line with *t rather than break it */
{ unsigned f = FUNCTOR(*t);
switch(f)
{ case INTEGRAL:
return 1;
case LIMIT:
return 1;
case EVAL:
return 1;
}
return 0;
}
/*_____________________________________________________________________*/
static int break_definite_integral(lterm *t, BIGRECT *sofar, long level,long xmax)
/* break a definite integral. The integrand can be broken, but if
the limits won't fit, you can't break them.
This is also used on indexed sums and products.
*/
{ lterm lo,hi;
int nlines;
int i;
BIGRECT savesofar,q,r,integralsign;
rectlist *marker;
long dxwidth; /* width in pixel coords required for ' dx' at the end */
long rightend,savex,deltax,deltay;
dxwidth = papyrus_to_xpixel(get_productspace(LARG(1,*t).block.size) + 16 + LARG(1,*t).block.w);
rightend = xmax - dxwidth;
assert(rightend > 0);
integralsign.left = sofar->right;
integralsign.right = integralsign.left + papyrus_to_xpixel(get_charspace() + get_productspace(LARG(1,*t).block.size));
/* We can't determine integralsign.top and integralsign.bottom until we
have broken the integrand. */
lo = LARG(2,*t);
hi = LARG(3,*t);
if(lo.r.right > paper.right)
return -1;
if(hi.r.right > paper.right)
return -1;
savex = sofar->right;
/* set *sofar to a rectangle that will include the integral
sign and the limits */
sofar->right += MAXIMUM(hi.r.right, lo.r.right);
sofar->top = hi.r.top;
sofar->bottom = lo.r.bottom;
savesofar = *sofar;
nlines = lbreak1(LARGPTR(*t),sofar,level,rightend);
*sofar = savesofar;
if(nlines <= 0 || nlines == 1)
return -1; /* the integrand can't be broken */
/* Now the integrand has been broken. */
marker = LARG(0,*t).linelist;
integralsign.top = marker->rect.top;
/* Integral sign must extend at least 16 papyrus units below
water level of the first line of the integrand */
deltay = papyrus_to_ypixel(16) - (marker->rect.bottom - marker->wl);
if(deltay > 0)
integralsign.bottom = marker->rect.bottom + deltay;
/* partial_shift(LARGPTR(*t),deltay) */
/* This would shift all but the first line of the integral
down by deltay. Without it, the integral sign extends into
the interline spacing, but this looks ok. */
else
{ integralsign.bottom = marker->rect.bottom;
// deltay = 0; never used
}
t->linelist = rectlist_copy(LARG(0,*t).linelist);
/* Now adjust the first and last lines of this linelist */
t->linelist->rect.left = savex; /* first line includes the integral sign */
for(marker=t->linelist; marker->next; marker=marker->next)
; /* point marker to last node */
if(LARG(0,*t).block.rightbra == 1)
/* integrand requires parentheses */
marker->rect.right += paren_displacement(LARG(0,*t)) + dxwidth;
else
marker->rect.right += dxwidth;
/* The rectangle of the 'x' in dx is still in the unbroken position;
it must be shifted to its new position */
q = LARG(1,*t).r;
deltax = q.right-marker->rect.right;
deltay = marker->wl - papyrus_to_ypixel(LARG(1,*t).block.wl) - q.top;
shift(LARGPTR(*t)+1,-deltax, deltay);
/* Now set the rectangle of the integral itself to be the smallest
rectangle containing the integrand, limits, and dx and the integral sign. */
r = integralsign;
for(i=0;i<4;i++)
{ UnionBigRect(&q,&r,&LARG(i,*t).r);
r = q;
}
t->r = r;
return nlines;
}
/*_____________________________________________________________________*/
static int break_integral(lterm *t, BIGRECT *sofar, long level, long xmax)
/* break an indefinite integral. */
{ int nlines;
BIGRECT q,r,integralsign;
rectlist *marker;
int dxwidth; /* width in pixel coords required for ' dx' at the end */
long rightend,savex;
long deltay, deltax;
dxwidth = (int) papyrus_to_xpixel(get_productspace(LARG(1,*t).block.size) + 16 + LARG(1,*t).block.w);
rightend = xmax - dxwidth;
assert(rightend > 0);
integralsign.left = sofar->right;
integralsign.right = integralsign.left + papyrus_to_xpixel(get_charspace() + get_productspace(LARG(1,*t).block.size));
/* We can't determine integralsign.top and integralsign.bottom until we
have broken the integrand. */
/* enlarge *sofar to a rectangle that will include the integral sign */
savex = sofar->right;
sofar->right = integralsign.right;
if(sofar->bottom < sofar->right + papyrus_to_ypixel(16))
sofar->bottom = sofar->top + papyrus_to_ypixel(16);
if(sofar->top == level)
/* this is the first term on the line */
level = sofar->top + papyrus_to_ypixel(10);
nlines = lbreak1(LARGPTR(*t),sofar,level, rightend);
/* In the case of a 3-or-more line integral, we leave
enough space for dx at the end of EVERY line, which is
wasteful, but not very wasteful, and will come up seldom.
*/
if(nlines <= 0 || nlines == 1)
return -1; /* the integrand can't be broken */
/* Now the integrand has been broken. */
marker = LARG(0,*t).linelist;
integralsign.top = marker->rect.top;
/* Integral sign must extend at least 16 papyrus units below
water level of the first line of the integrand */
deltay = papyrus_to_ypixel(16) - (marker->rect.bottom - marker->wl);
if(deltay > 0)
integralsign.bottom = marker->rect.bottom + deltay;
/* partial_shift(LARGPTR(*t),deltay) */
/* This would shift all but the first line of the integral
down by deltay. Without it, the integral sign extends into
the interline spacing, but this looks ok. */
else
{ integralsign.bottom = marker->rect.bottom;
deltay = 0;
}
/* The integral sign must extend at least 16 coord units below water level
of the first line of the integrand. If this isn't so yet, we must make
it so. */
t->linelist = rectlist_copy(LARG(0,*t).linelist);
/* Now adjust the first and last lines of this linelist */
t->linelist->rect.left = savex; /* first line includes the integral sign */
t->linelist->rect.bottom += deltay;
for(marker=t->linelist; marker->next; marker=marker->next)
; /* point marker to last node */
if(LARG(0,*t).block.rightbra == 1)
/* integrand requires parentheses */
marker->rect.right += paren_displacement(LARG(0,*t)) + dxwidth;
else
marker->rect.right += dxwidth;
/* The rectangle of the 'x' in dx is still in the unbroken position;
it must be shifted to its new position */
q = LARG(1,*t).r; /* the unshifted rectangle of 'x' in 'dx' */
deltax = q.right-marker->rect.right;
deltay = marker->wl - papyrus_to_ypixel(LARG(1,*t).block.wl) - q.top;
shift(LARGPTR(*t)+1,-deltax, deltay);
/* Now set the rectangle of the integral itself to be the smallest
rectangle containing the integrand and dx and the integral sign. */
UnionBigRect(&r,&LARG(1,*t).r,&LARG(0,*t).r);
/* r contains dx and the integrand */
UnionBigRect(&t->r,&r,&integralsign);
return nlines;
}
/*_____________________________________________________________________*/
static int break_function(lterm *t, BIGRECT *sofar, long level, long xmax)
/* break a prefix function, including LOGB and Bessel functions */
{ int nlines;
lterm u;
rectlist *marker;
unsigned short f = FUNCTOR(*t);
long fwidth,deltax,deltay;
long savex = sofar->right;
BIGRECT saverect;
u = LARG(0,*t);
fwidth = u.r.left - t->r.left;
sofar->right += fwidth;
if(PREFIX(f))
{ saverect = LARG(0,*t).r;
nlines = lbreak1(LARGPTR(*t),sofar,level,xmax-fwidth);
if(nlines == 0)
/* Don't put the arg of a prefix function on a new line */
{ deltay = LARG(0,*t).r.top - saverect.top;
deltax = LARG(0,*t).r.left - saverect.left;
shift(LARGPTR(*t),-deltax,-deltay);
return -1;
}
if(nlines<0)
return -1;
if(nlines == 1)
return -1; /* assert(0), but it does happen for very narrow windows! */
t->linelist = rectlist_copy(LARG(0,*t).linelist);
}
else if(ARITY(*t) >= 2)
{ /* Now for the LOGB or Bessel function case */
/* don't break the index */
saverect = LARG(1,*t).r;
sofar->right = u.r.right + papyrus_to_xpixel(get_productspace(LARG(1,*t).block.size));
deltax = sofar->right - savex;
if(sofar->right > xmax)
return -1;
nlines = lbreak1(LARGPTR(*t)+1,sofar,level,xmax-deltax);
if(nlines == 0)
{ deltay = LARG(1,*t).r.top - saverect.top;
deltax = LARG(1,*t).r.left - saverect.left;
shift(LARGPTR(*t)+1,-deltax,-deltay);
return -1;
}
if(nlines<0)
return -1;
if(nlines == 1)
return -1; /* assert(0), but it does happen for very narrow windows! */
t->linelist = rectlist_copy(LARG(1,*t).linelist);
}
else
return -1; /* assert(0); */
/* Now adjust the first line of this linelist */
t->linelist->rect.left = savex;
t->r = t->linelist->rect; /* the rectangle of the first line */
for(marker=t->linelist->next; marker; marker=marker->next)
{ if(marker->rect.right > t->r.right)
t->r.right = marker->rect.right;
if(marker->rect.left < t->r.left)
t->r.left = marker->rect.left;
t->r.bottom = marker->rect.bottom;
}
return nlines;
}
/*_____________________________________________________________________*/
static int break_minus(lterm *t, BIGRECT *sofar, long level, long xmax)
/* break a negation */
{ int nlines;
lterm u,savet;
rectlist *marker;
long fwidth;
long savex = sofar->right;
BIGRECT savesofar;
u = LARG(0,*t);
fwidth = u.r.left - t->r.left;
savesofar = *sofar;
sofar->right += fwidth;
lcopy(t,&savet);
nlines = lbreak1(LARGPTR(*t),sofar,level,xmax-fwidth);
if(nlines == -1)
{ sofar->right = savex;
return -1; /* failure */
}
if(nlines == 0)
/* means no part of the arg fit on this line */
/* don't leave just a minus sign at the end of a line */
{ *sofar = savesofar;
destroy_lterm(*t);
*t = savet;
return on_nextline(t,sofar,&level,xmax);
}
destroy_lterm(savet);
if(nlines == 1)
return 1; /* t->linelist is NULL */
if(LARG(0,*t).linelist == NULL)
return 1; /* assert(0) */
t->linelist = rectlist_copy(LARG(0,*t).linelist);
/* Now adjust the first line of this linelist */
t->linelist->rect.left = savex;
t->r = t->linelist->rect; /* the rectangle of the first line */
if(LARG(0,*t).block.rightbra)
{
for(marker=t->linelist; marker->next; marker = marker->next)
; /* point marker to the last node */
marker->rect.right += (int) papyrus_to_xpixel(paren_displacement(LARG(0,*t)));
}
for(marker=t->linelist->next; marker; marker=marker->next)
{ if(marker->rect.right > t->r.right)
t->r.right = marker->rect.right;
if(marker->rect.left < t->r.left)
t->r.left = marker->rect.left;
t->r.bottom = marker->rect.bottom;
}
return nlines;
}
/*_____________________________________________________________________*/
static int break_power(lterm *t, BIGRECT *sofar, long level, long xmax)
/* break a power */
{ int nlines_base, nlines_power;
long powerlevel,deltay,deltax;
rectlist *marker;
BIGRECT r,r1,r2;
deltay = LARG(0,*t).r.top - LARG(1,*t).r.top;
if(deltay < 0)
assert(0);
if(level == sofar->top) /* first term on this line */
{ level += deltay;
sofar->top += deltay;
sofar->bottom = sofar->top;
}
nlines_base = lbreak1(LARGPTR(*t),sofar,level,xmax);
if(nlines_base <= 0 )
return -1; /* can't break the base leaving part on this line */
t->linelist = rectlist_copy(LARG(0,*t).linelist);
for(marker=t->linelist; marker && marker->next; marker=marker->next)
; /* point marker to the last node */
if(marker == NULL)
{ *sofar = LARG(0,*t).r;
sofar->top -= deltay;
powerlevel = LARG(1,*t).r.top + papyrus_to_ypixel(LARG(1,*t).block.wl);
}
else
{ *sofar = marker->rect;
powerlevel = marker->wl;
deltay = papyrus_to_ypixel(LARG(1,*t).block.wl);
powerlevel -= deltay;
sofar->top -= deltay;
/* This is wrong. It will cause the exponent to be raised above
the last line, and perhaps intersect the previous line. But there's
no alternative since when we break the base, there's no way to
tell lbreak1 to lower the last line a bit. The recursion on which
lbreak1 is based is not powerful enough. */
}
if(nlines_base > 1)
{ /* shift power to the right position */
deltax = LARG(1,*t).r.left - (sofar->right + papyrus_to_xpixel(get_powerspace()));
if(LARG(0,*t).block.rightbra)
deltax -= papyrus_to_xpixel(get_parenspace(t->block.size) + get_charspace());
deltay = sofar->top - LARG(1,*t).r.top;
shift(LARGPTR(*t)+1,-deltax,deltay);
}
nlines_power = lbreak1(LARGPTR(*t) + 1, sofar, powerlevel, xmax);
if(nlines_power <= 0)
return -1; /* don't break exactly between the base and exponent */
if(marker == NULL)
/* the base fits on one line */
{ if(LARG(1,*t).linelist == NULL)
return -1; /* assert(0); */
t->linelist = rectlist_copy(LARG(1,*t).linelist);
UnionBigRect(&r,&LARG(0,*t).r,&t->linelist->rect);
t->linelist->rect = r;
/* this adjusts the rectangle of the first line to include the base */
t->linelist->wl = (int)(papyrus_to_ypixel(LARG(0,*t).block.wl) + LARG(0,*t).r.top);
t->r.top = LARG(1,*t).r.top;
t->r.left = paper.left;
t->r.right = paper.right;
for(marker = t->linelist; marker && marker->next; marker = marker->next)
; /* point marker to last line */
t->r.bottom = marker->rect.bottom;
return nlines_base + nlines_power -1;
}
if(marker == NULL)
r1 = LARG(0,*t).r;
else
r1 = marker->rect; /* last row of the base */
if(LARG(1,*t).linelist)
r2 = LARG(1,*t).linelist->rect; /* first row of the exponent */
else
r2 = LARG(1,*t).r;
UnionBigRect(&r, &r1,&r2);
marker->rect = r;
if(LARG(1,*t).linelist && LARG(1,*t).linelist->next)
marker->next = rectlist_copy(LARG(1,*t).linelist->next);
/* add on the subsequent rows of the broken exponent */
UnionBigRect(&t->r,&LARG(0,*t).r,&LARG(1,*t).r);
return nlines_base + nlines_power -1;
}
/*_____________________________________________________________________*/
static int break_diff(lterm *t, BIGRECT *sofar, long level, long xmax)
/* break a derivative. Derivatives written du/dx or d^n u/dx^n
are not broken, so we're dealing with (d/dx) u or d^n/dx^n u
*/
{ int err;
long offset;
long ddx_aboveline;
long ddx_depth;
long original_top = sofar->top;
lterm *u = LARGPTR(*t);
lterm *x = LARGPTR(*t)+1;
lterm *nn;
coord charheight = (coord)((t->block.size == TENPOINT) ? get_charspace() : get_eightpointheight());
BIGRECT q = *sofar;
if(sofar->top == level)
/* this is the first term on the line */
{ sofar->bottom = sofar->top + papyrus_to_ypixel(16);
level = sofar->top + papyrus_to_ypixel(10);
}
/* set *sofar to a rectangle that will include the d/dx */
ddx_depth = papyrus_to_ypixel(charheight + get_belowfractionspace());
ddx_aboveline = papyrus_to_ypixel(charheight + get_abovefractionspace());
if(level - sofar->top < ddx_aboveline)
level = sofar->top + ddx_aboveline;
if(sofar->bottom < level + ddx_depth)
q.bottom = level + ddx_depth;
q.right = u->r.left;
if(q.right >= xmax)
return -1;
err = lbreak1(LARGPTR(*t),&q,level,xmax);
if(err <= 0)
return -1; /* the argument can't be broken. It's not
acceptable to put the d/dx on this line and u on the next */
*sofar = q;
assert(err != 1); /* err = 1 would mean the whole term fit */
t->linelist = rectlist_copy(LARG(0,*t).linelist);
if(!t->linelist)
nospace();
q = u->linelist->rect;
/* this makes q the rectangle of the part of u on the first line */
/* Next adjust the left side to include d/dx, or d^n /dx^n as the case may be */
offset = t->block.w-u->block.w; /* difference in widths of d/dx u and u,
in papyrus coordinates */
if(u->block.leftbra)
offset += get_parenwidth(t->block.size) + get_parenspace(t->block.size);
/* q does NOT include an open paren before u if there is one, so
in that case we have to move d/dx left by this extra amount */
q.left-= papyrus_to_xpixel(offset); /* adjust left side to include d/dx */
/* Possibly we also have to adjust the top and bottom of q, if d/dx sticks
out above or below u. The lterm 'x' is located in the 'denominator'
of d/dx or d^n/dx^n, so its bottom can be compared with q.bottom:
*/
if(x->r.bottom > q.bottom)
q.bottom = x->r.bottom;
if(ARITY(*t) == 3)
/* d^n/dx^n; luckily the 'n' in the numerator is the located
occurrence of n */
{ nn = LARGPTR(*t)+2;
if(nn->r.top < q.top)
q.top = nn->r.top;
}
t->linelist->rect = q; /* This is the rectangle of the first line of t */
t->linelist->wl = (int) level;
t->r = u->r; /* Now set the rectangle of the whole term t */
if(q.left < t->r.left)
t->r.left = q.left; /* include the d/dx in t.r; but if the
line has been broken probably t->r.left
is already well to the left of the d/dx */
if(t->r.top > original_top) /* push the top back up to include d/dx */
{ t->r.top = original_top;
t->linelist->rect.top = original_top;
}
return rectlist_length(t->linelist);
}
/*_________________________________________________________________*/
int paren_displacement(lterm t)
/* How much space in papyrus coords is required around the given lterm
for parens and the space between the parens and the term? */
{ unsigned f = FUNCTOR(t);
short size = t.block.size;
if(
OBJECT(t) ||
( f == '-' && OBJECT(LARG(0,t))) ||
( f == '-' && size == EIGHTPOINT)
)
return get_parenwidth(t.block.size);
else
return get_parenwidth(t.block.size) + get_parenspace(t.block.size);
}
/*_________________________________________________________________*/
rectlist *rectlist_copy(rectlist *source)
/* make a copy of the rectlist pointed to by source and return
a pointer to the copy. If out of space, return NULL
*/
{ rectlist *ans;
if(source == NULL)
return NULL;
ans = mallocate(sizeof(rectlist));
if(ans == NULL)
{ nospace();
return NULL;
}
*ans = *source;
ans->next = rectlist_copy(source->next);
if(ans->next)
ans->next->prev = ans;
return ans;
}
/*__________________________________________________________________*/
static int break_bignum(lterm *t, BIGRECT *sofar, long level, long xmax)
/* break a bignum.
The linelist of t will be created, a linked list of lines each
just high enough to hold digits.
Return the number of lines used.
*/
{ int nlines, charheight, flag;
long pixelwidth;
rectlist *marker;
int k;
char buffer[256];
char *z = * (( char **) LARGPTR(*t));
/* this is the string of digits, or digits and commas, to be displayed
at the locations given in the rectangles of t->linelist */
long pcharheight,interline_spacing;
long width = t->block.w;
/* This is the papyrus-coordinate width required for the
display string if written all on one line. */
if(width > 0x000fffffL)
return -1; /* too long to break. This would be about 16 screenfuls */
pixelwidth = papyrus_to_xpixel(width);
charheight = t->block.size == TENPOINT ? get_charspace() : get_eightpointheight();
pcharheight = papyrus_to_ypixel(charheight);
interline_spacing = pcharheight/2;
pixelwidth -= xmax - t->r.left; /* space available on this line */
assert(pixelwidth > 0); /* or it would have fit on this line */
t->linelist = mallocate(sizeof(rectlist));
if(!t->linelist)
nospace();
nlines = 1;
if(level == sofar->top)
/* this is the first term on the line */
{ t->linelist->rect.bottom = sofar->top + pcharheight;
t->linelist->rect.top = sofar->top;
t->linelist->wl = (int)( t->linelist->rect.bottom - charheight/2);
sofar->bottom = sofar->top + pcharheight;
/* needed to align the second row correctly */
}
else
{ t->linelist->wl = (int) level;
t->linelist->rect.bottom = level + pcharheight/2;
t->linelist->rect.top = t->linelist->rect.bottom - pcharheight;
}
t->linelist->next = t->linelist->prev = NULL;
t->linelist->rect.left = t->r.left;
t->linelist->rect.right = xmax;
flag = 1; /* set to zero when second line is created */
for(marker = t->linelist;marker && *z;marker=marker->next)
{ /* measure characters from string z line by line and
allocate more members of t->linelist */
marker->rect.right = paper.right;
width = (coord) xpixel_to_papyrus((int)(marker->rect.right - marker->rect.left));
k = strip(256,buffer,z,(coord) width,(unsigned short)t->block.size);
if(k == -1)
return -1; /* assert(0); something's wrong */
z += k;
if(*z == '\0')
{ /* finito! */
#ifdef SVG
coord width =(coord)svg_text_width(buffer, (short)t->block.size);
#else
coord width =(coord)text_width(buffer, (short)t->block.size);
#endif
marker->rect.right = marker->rect.left + papyrus_to_xpixel(width);
break; /* with marker still pointing to the last node */
}
else /* allocate another node */
{ marker->next = mallocate(sizeof(rectlist));
if(!marker->next)
nospace();
++nlines;
marker->next->prev = marker;
marker->next->next = NULL;
marker->next->rect.left = paper.left;
marker->next->rect.right = paper.right;
if(flag)
{ marker->next->rect.top = sofar->bottom + interline_spacing;
flag = 0;
}
else
marker->next->rect.top = marker->rect.bottom + interline_spacing;
marker->next->rect.bottom = marker->next->rect.top + pcharheight;
marker->next->wl = (int)( marker->next->rect.bottom - pcharheight/2);
}
}
t->r.left = paper.left;
t->r.right = paper.right;
t->r.top = t->linelist->rect.top;
t->r.bottom = marker->rect.bottom;
return nlines;
}
/*___________________________________________________________________*/
static int lcopy(lterm *source, lterm *target)
/* create a copy of source in target */
/* if source has a path, this is not copied into fresh space */
/* return 0 for success, 1 for out of space */
{ int err;
unsigned short i;
rectlist *marker,*old;
if(source == NULL)
assert(0);
memcpy((void *)target,(void *)source,sizeof(lterm));
if(source->linelist)
target->linelist = rectlist_copy(source->linelist);
if(ATOMIC(*source))
return 0;
target->args = (larg *)callocate(ARITY(*source),sizeof(larg));
if(target->args == NULL)
return 1;
for(i=0;i<ARITY(*source);i++)
{ err = lcopy(LARGPTR(*source)+i, LARGPTR(*target)+i);
if(err)
{ free2(target->args);
marker = target->linelist;
while(marker && marker->next)
{ old = marker;
marker = marker->next;
free2(old);
}
if(marker)
free2(marker); /* the last node */
return 1;
}
}
return 0;
}
/*___________________________________________________________________*/
static void destroy_lterm(lterm t)
/* free memory used in t, but not t.path, since this is used
to destroy terms made by lcopy and path is not copied.
*/
{ unsigned short n,i;
rectlist *marker,*old;
if(ATOMIC(t))
return;
n = ARITY(t);
for(i=0;i<n;i++)
destroy_lterm(LARG(i,t));
free2(t.args);
marker = t.linelist;
if(marker == NULL)
return;
while(marker->next)
{ old = marker;
marker = marker->next;
free2(old);
}
free2(marker); /* the last node */
}
/*_________________________________________________________________*/
int strip(int dimbuffer, char *buffer, char *z, coord width, unsigned short size)
/* copy into buffer from z the maximum initial string
of text_width <= width. Add a terminating null character,
and return the number of characters copied. Don't copy more than
dimbuffer-1 characters plus a terminating null. Return -1 if
dimbuffer is too small.
size is either TENPOINT or EIGHTPOINT; it is needed by text_width.
*/
{ int n = width/16; /* first approximation to the number of characters */
char *marker;
int i=0;
int saveit;
long temp;
if(n >= dimbuffer)
return -1;
for(marker = z; *marker && i<n && i < dimbuffer-1; ++marker,++i)
buffer[i] = *marker;
if(i == dimbuffer)
{ buffer[dimbuffer-1] = '\0';
return -1;
}
buffer[i] = '\0';
saveit = i;
/* Now check if this is the right n and correct it if not */
#ifdef SVG
temp = svg_text_width(buffer, size);
#else
temp = text_width(buffer,size);
#endif
while(temp > width)
{ --i;
buffer[i] = '\0';
}
if(i < saveit)
return i;
while(*marker && i < dimbuffer-1)
{ buffer[i] = *marker;
buffer[i+1] = '\0';
#ifdef SVG
temp = svg_text_width(buffer, size);
#else
temp = text_width(buffer,size);
#endif
if(temp > width)
{ buffer[i] = '\0';
return i;
}
else
{ ++i;
++marker;
}
}
if(i == dimbuffer-1)
{ buffer[dimbuffer-1] = '\0';
return -1;
}
return i;
}
Sindbad File Manager Version 1.0, Coded By Sindbad EG ~ The Terrorists