/*@z17.c:Gap Widths:GetGap()@*************************************************/
/* */
/* THE LOUT DOCUMENT FORMATTING SYSTEM (VERSION 3.36) */
/* COPYRIGHT (C) 1991, 2007 Jeffrey H. Kingston */
/* */
/* Jeffrey H. Kingston (jeff@it.usyd.edu.au) */
/* School of Information Technologies */
/* 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 3, 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: z17.c */
/* MODULE: Gap Widths */
/* EXTERNS: GetGap(), MinGap(), ExtraGap(), ActualGap(), EchoGap() */
/* */
/*****************************************************************************/
#include "externs.h"
/*****************************************************************************/
/* */
/* int GetWidth(OBJECT x, STYLE *style) */
/* */
/* Get a width from object x, according to the grammar */
/* */
/* <width> ::= <unsigned number> <units> */
/* <units> ::= c | i | p | m | f | s | v | y | z */
/* */
/*****************************************************************************/
int GetWidth(OBJECT x, STYLE *style)
{ int res; float num;
FULL_CHAR *str;
debug2(DGW, D, "GetWidth(%s, %s)", EchoObject(x), EchoStyle(style));
/* make sure we have a WORD or QWORD argument */
if( !is_word(type(x)) )
{ Error(17, 11, "width is not a simple word (replacing with 5c)",
WARN, &fpos(x));
debug1(DGW, D, "GetWidth failing (x = %s)", EchoObject(x));
return 5 * CM;
}
str = string(x);
/* ignore initial + or - */
if( *str == '+' || *str == '-' )
{
Error(17, 12, "ignoring initial %c character in width", WARN, &fpos(x), *str);
str++;
}
/* if word is empty, error */
if( *str == '\0' )
{
Error(17, 13, "width is empty (replacing with 5c)", WARN, &fpos(x));
debug0(DGW, D, "GetWidth failing (null word)");
return 5 * CM;
}
/* read the width */
if( sscanf((char *) str, "%f", &num) != 1 )
{ Error(17, 14, "width missing from %s (replacing with 5c)", WARN,
&fpos(x), str);
debug0(DGW, D, "GetWidth failing (width missing)");
return 5 * CM;
}
while( numericchar(*str) ) str++;
/* make sure there is a units letter */
if( *str == '\0' )
{
Error(17, 15, "unit missing from width %s (5c substituted)", WARN,
&fpos(x), string(x));
res = 5 * CM;
}
/* make sure there is nothing after the units letter */
else if( *(str + 1) != '\0' )
{
Error(17, 16, "extra character(s) at end of width %s (5c substituted)",
WARN, &fpos(x), string(x));
res = 5 * CM;
}
/* read the compulsory unit and calculate length */
else switch( *str )
{
case CH_UNIT_CM:
res = num * CM;
break;
case CH_UNIT_IN:
res = num * IN;
break;
case CH_UNIT_PT:
res = num * PT;
break;
case CH_UNIT_EM:
res = num * EM;
break;
case CH_UNIT_FT:
res = num * FontSize(font(*style), x);
break;
case CH_UNIT_SP:
res = num * width(space_gap(*style));
break;
case CH_UNIT_VS:
res = num * width(line_gap(*style));
break;
case CH_UNIT_YU:
res = num * yunit(*style);
break;
case CH_UNIT_ZU:
res = num * zunit(*style);
break;
case CH_UNIT_WD:
case CH_UNIT_BD:
case CH_UNIT_RL:
case CH_UNIT_DG:
Error(17, 17, "'%c' unit not allowed in width (5c substituted)",
WARN, &fpos(x), *--str);
res = 5 * CM;
break;
default:
Error(17, 18, "unknown unit in width %s (5c substituted)", WARN,
&fpos(x), string(x));
res = 5 * CM;
break;
}
debug1(DGW, D, "GetWidth returning %d", res);
return res;
}
/*****************************************************************************/
/* */
/* GetGap(x, style, res_gap, res_inc) */
/* */
/* Object x is expected to be a WORD or QWORD containing a gap: */
/* */
/* <gap> ::= [ <increment> ] <width> [ <mode> ] [ <nobreak> ] */
/* ::= */
/* */
/* <nobreak> ::= u */
/* <increment> ::= + | - */
/* <width> ::= <unsigned number> <units> */
/* <units> ::= c | i | p | m | f | s */
/* ::= v | w | b | r | d | y | z */
/* <mode> ::= e | h | x | o | k | t | n */
/* */
/* Set *res_gap to the gap in the strings of x; *res_inc is the increment. */
/* The gap is calculated using the given style. */
/* If the gap is empty, this is a synonym for 0ie. */
/* If there is an error, GetGap prints a message and returns 0ie. */
/* */
/*****************************************************************************/
#define setwidths(x, y) w = x; units(*res_gap) = y; break;
void GetGap(OBJECT x, STYLE *style, GAP *res_gap, unsigned *res_inc)
{ int w; float num;
FULL_CHAR *str;
debug2(DGW, D, "GetGap( %s, %s, res_gap, res_inc )",
EchoObject(x), EchoStyle(style));
nobreak(*res_gap) = FALSE;
width(*res_gap) = 0; units(*res_gap) = FIXED_UNIT;
mode(*res_gap) = EDGE_MODE; *res_inc = GAP_ABS;
/* make sure we have a WORD or QWORD argument */
if( !is_word(type(x)) )
{ Error(17, 1, "gap is not a simple word", WARN, &fpos(x));
debug1(DGW, D, "GetGap failing (x = %s)", EchoObject(x));
return;
}
str = string(x);
/* if word is empty, return 0ie */
if( *str == '\0' )
{ debug0(DGW, D, "GetGap returning (null word)");
return;
}
/* read the optional gap increment */
if( *str == CH_INCGAP ) *res_inc = GAP_INC, str++;
else if( *str == CH_DECGAP ) *res_inc = GAP_DEC, str++;
/* read the gap width */
if( sscanf((char *) str, "%f", &num) != 1 )
{ Error(17, 2, "width missing from %s", WARN, &fpos(x), string(x));
Error(17, 3, "%s, %s and %s must be enclosed in double quotes",
WARN, &fpos(x), KW_VCAT_NJ, KW_HCAT_NJ, KW_ACAT_NJ);
debug0(DGW, D, "GetGap failing (width missing)");
return;
}
while( numericchar(*str) ) str++;
/* read the compulsory units, calculate length, and check reasonableness */
switch( *str++ )
{
case CH_UNIT_CM: setwidths( num*CM, FIXED_UNIT );
case CH_UNIT_IN: setwidths( num*IN, FIXED_UNIT );
case CH_UNIT_PT: setwidths( num*PT, FIXED_UNIT );
case CH_UNIT_EM: setwidths( num*EM, FIXED_UNIT );
case CH_UNIT_FT: setwidths( num*FontSize(font(*style), x), FIXED_UNIT );
case CH_UNIT_SP: setwidths( num*width(space_gap(*style)), FIXED_UNIT );
case CH_UNIT_VS: setwidths( num*width(line_gap(*style)), FIXED_UNIT );
case CH_UNIT_YU: setwidths( num*yunit(*style), FIXED_UNIT );
case CH_UNIT_ZU: setwidths( num*zunit(*style), FIXED_UNIT );
case CH_UNIT_WD: setwidths( num*FR, NEXT_UNIT );
case CH_UNIT_BD: setwidths( num*FR, FRAME_UNIT );
case CH_UNIT_RL: setwidths( num*FR, AVAIL_UNIT );
case CH_UNIT_DG: if( *res_inc == GAP_DEC ) num = - num;
*res_inc = GAP_ABS;
while( num > 180.0 ) num -= 360.0;
while( num < -180.0 ) num += 360.0;
assert((num>=-180.0) && (num<=180.0), "GetGap: dg!");
setwidths( num*DG, DEG_UNIT );
default: Error(17, 4, "units letter missing from %s",
WARN, &fpos(x), string(x));
debug0(DGW, D, "GetGap failing (units letter missing)");
return;
}
if( units(*res_gap) == AVAIL_UNIT && w > FR )
{ Error(17, 5, "%.1fr too large (1.0r substituted)", WARN, &fpos(x), num);
w = FR;
}
/* don't have short lengths any more ***
if( w > MAX_SHORT_LENGTH )
Error(17, 5, "%s exceeds maximum allowed gap size", INTERN, &fpos(x),
string(x));
*** */
width(*res_gap) = w;
/* read the optional gap mode */
switch( *str )
{
case CH_NOBREAK:
case '\0': mode(*res_gap) = EDGE_MODE; break;
case CH_MODE_EDGE: mode(*res_gap) = EDGE_MODE; str++; break;
case CH_MODE_HYPH: mode(*res_gap) = HYPH_MODE; str++; break;
case CH_MODE_MARK: mode(*res_gap) = MARK_MODE; str++; break;
case CH_MODE_OVER: mode(*res_gap) = OVER_MODE; str++; break;
case CH_MODE_KERN: mode(*res_gap) = KERN_MODE; str++; break;
case CH_MODE_TABL: mode(*res_gap) = TAB_MODE; str++; break;
default: Error(17, 7, "unknown gap mode in %s",
WARN, &fpos(x), string(x));
debug0(DGW, D, "GetGap failing (spacing mode)");
return;
}
/* read the optional nobreak */
if( *str == CH_NOBREAK )
{
if( mode(*res_gap) == HYPH_MODE )
Error(17, 9, "replacing self-contradictory gap %s by breakable version",
WARN, &fpos(x), string(x));
else nobreak(*res_gap) = TRUE;
str++;
}
/* if string has not terminated, error */
if( *str != '\0' )
Error(17, 8, "invalid width or gap %s", WARN, &fpos(x), string(x));
debug2(DGW, D, "GetGap returning (res_gap = %s, res_inc = %s)",
EchoGap(res_gap), Image( (int) *res_inc) );
} /* end GetGap */
/*@::MinGap()@****************************************************************/
/* */
/* FULL_LENGTH MinGap(a, b, c, xgap) */
/* */
/* Returns the minimum possible separation between the marks of two */
/* objects with the given intervening gap. */
/* The first object has fwd value a, the second has back value b and fwd c. */
/* */
/*****************************************************************************/
FULL_LENGTH MinGap(FULL_LENGTH a, FULL_LENGTH b, FULL_LENGTH c, GAP *xgap)
{ FULL_LENGTH res; int w = 0;
switch( units(*xgap) )
{
case FIXED_UNIT: w = width(*xgap);
break;
case FRAME_UNIT: w = 0;
break;
case AVAIL_UNIT: w = 0;
break;
case NEXT_UNIT: w = width(*xgap) * (b + c) / FR;
break;
default: assert(FALSE, "MinGap: units");
break;
}
switch( mode(*xgap) )
{
case NO_MODE: assert(FALSE, "MinGap: NO_MODE");
res = 0;
break;
case ADD_HYPH:
case HYPH_MODE:
case EDGE_MODE: res = find_min(MAX_FULL_LENGTH, a + w + b);
break;
case MARK_MODE: if( BackEnd->fractional_spacing_avail )
res = find_max(w, a + b + (FULL_LENGTH) (0.1 * w) );
else
res = find_max(w, a + b);
break;
case OVER_MODE: res = w;
break;
case KERN_MODE: res = find_max(find_max(a, b), w);
break;
case TAB_MODE: res = a + b;
break;
default: assert(FALSE, "MinGap: mode");
res = 0;
break;
}
debug5(DGW, DD, "MinGap( _,%s %s %s,%s ) = %s", EchoLength(a),
EchoGap(xgap), EchoLength(b), EchoLength(c), EchoLength(res) );
return res;
} /* end MinGap */
/*@::ExtraGap()@**************************************************************/
/* */
/* FULL_LENGTH ExtraGap(a, b, xgap, dir) */
/* */
/* Consider two objects, the first with forward length a, the second with */
/* back length b. The objects are separated by the given gap. */
/* If dir == FWD, ExtraGap returns the maximum amount that a could be */
/* increased without increasing MinGap(a, b, c, xgap). */
/* If dir == BACK, similarly for b. */
/* */
/*****************************************************************************/
FULL_LENGTH ExtraGap(FULL_LENGTH a, FULL_LENGTH b, GAP *xgap, int dir)
{ FULL_LENGTH tmp, res;
FULL_LENGTH w = units(*xgap) == FIXED_UNIT ? width(*xgap) : 0;
switch( mode(*xgap) )
{
case NO_MODE: assert(FALSE, "ExtraGap: NO_MODE");
res = 0;
break;
case ADD_HYPH:
case HYPH_MODE:
case EDGE_MODE: res = 0;
break;
case MARK_MODE: if( BackEnd->fractional_spacing_avail )
res = find_max(0, (FULL_LENGTH) (0.9 * w) - a - b);
else
res = find_max(0, w - a - b);
break;
case OVER_MODE: res = MAX_FULL_LENGTH;
break;
case KERN_MODE: tmp = find_max(a, find_max(b, w));
res = dir == BACK ? tmp - b : tmp - a;
break;
case TAB_MODE: res = 0;
break;
default: assert(FALSE, "ExtraGap: mode");
res = 0;
break;
}
debug5(DGW, DD, "ExtraGap( %s, %s, %s, %s ) = %s", EchoLength(a),
EchoLength(b), EchoGap(xgap), Image(dir), EchoLength(res));
return res;
} /* end ExtraGap */
/*@::ActualGap()@*************************************************************/
/* */
/* FULL_LENGTH ActualGap(prevf, b, f, xgap, frame_size, mk) */
/* */
/* Returns the actual separation between the marks of an object of size */
/* (?, prevf) and an object of size (b, f) separated by gap *xgap in a */
/* frame of size frame_size; the first object lies at mk in the frame, */
/* where 0 <= mk <= frame_size. */
/* */
/*****************************************************************************/
FULL_LENGTH ActualGap(FULL_LENGTH prevf, FULL_LENGTH b, FULL_LENGTH f,
GAP *xgap, FULL_LENGTH frame_size, FULL_LENGTH mk)
{ FULL_LENGTH res; int w = 0, w2;
switch( units(*xgap) )
{
case FIXED_UNIT: w = width(*xgap);
break;
case FRAME_UNIT: if( width(*xgap) > FR )
w = MAX_FULL_LENGTH;
else
w = (width(*xgap) * frame_size) / FR;
break;
case AVAIL_UNIT: w = (width(*xgap) * (frame_size - b - f)) / FR;
w = find_max(w, 0);
break;
case NEXT_UNIT: w = width(*xgap) * (b + f) / FR;
break;
default: assert(FALSE, "ActualGap: units");
break;
}
switch( mode(*xgap) )
{
case NO_MODE: Error(17, 10, "cannot continue after previous error(s)", FATAL, no_fpos);
assert(FALSE, "ActualGap: NO_MODE");
w2 = 0;
break;
case ADD_HYPH:
case HYPH_MODE:
case EDGE_MODE: w2 = prevf + w + b;
break;
case MARK_MODE: if( BackEnd->fractional_spacing_avail )
w2 = find_max(w, prevf + b + (FULL_LENGTH) (0.1 * w) );
else
w2 = find_max(w, prevf + b);
break;
case OVER_MODE: w2 = w;
break;
case KERN_MODE: w2 = find_max( find_max(prevf, b), w);
break;
case TAB_MODE: w2 = w + b - mk;
w2 = find_max(w2, prevf + b );
break;
default: assert(FALSE, "ActualGap: mode");
w2 = 0;
break;
}
res = find_min(MAX_FULL_LENGTH, w2);
debug7(DGW, DD, "ActualGap( _,%s %s %s,%s; frame_size %s, mk %s ) = %s",
EchoLength(prevf), EchoGap(xgap), EchoLength(b), EchoLength(f),
EchoLength(frame_size), EchoLength(mk), EchoLength(res));
return res;
} /* end ActualGap */
/*@::EchoGap()@***************************************************************/
/* */
/* FULL_CHAR *EchoGap(xgap) */
/* */
/* Returns a static string showing the indicated xgap. */
/* */
/*****************************************************************************/
#if DEBUG_ON
FULL_CHAR *EchoGap(GAP *xgap)
{ char *letter = "?ehxoktH"; char c; FULL_CHAR *res;
char *u;
static int i = 0;
static char buff[3][20];
c = mode(*xgap) <= 7 ? letter[mode(*xgap)] : '?';
c = letter[mode(*xgap)];
u = nobreak(*xgap) ? "u" : "";
switch( units(*xgap) )
{
case 0: sprintf(buff[i], "(none)%c", c);
break;
case FIXED_UNIT: sprintf(buff[i], "%.1fc%c%s", (float) width(*xgap)/CM, c,u);
break;
case NEXT_UNIT: sprintf(buff[i], "%.1fw%c%s", (float) width(*xgap)/FR, c,u);
break;
case FRAME_UNIT: sprintf(buff[i], "%.1fb%c%s", (float) width(*xgap)/FR, c,u);
break;
case AVAIL_UNIT: sprintf(buff[i], "%.1fr%c%s", (float) width(*xgap)/FR, c,u);
break;
case DEG_UNIT: sprintf(buff[i], "%.1fd", (float) width(*xgap) / DG);
break;
default: assert(FALSE, "EchoGap: units");
break;
}
res = AsciiToFull(buff[i]);
i = (i + 1) % 3;
return res;
} /* end EchoGap */
#endif