1 
2 /******************************************************************************
3 * MODULE     : env_length.cpp
4 * DESCRIPTION: length computations
5 * COPYRIGHT  : (C) 1999  Joris van der Hoeven
6 *******************************************************************************
7 * This software falls under the GNU general public license version 3 or later.
8 * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
9 * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
10 ******************************************************************************/
11 
12 #include "env.hpp"
13 #include "convert.hpp"
14 #include "page_type.hpp"
15 #include "typesetter.hpp"
16 
17 /******************************************************************************
18 * Length arithmetic
19 ******************************************************************************/
20 
21 bool
is_length(string s)22 edit_env_rep::is_length (string s) {
23   int i;
24   for (i=0; (i<N(s)) && ((s[i]<'a') || (s[i]>'z')); i++) {}
25   return is_double (s (0, i)) && is_locase_alpha (s (i, N(s)));
26 }
27 
28 bool
is_anylen(tree t)29 edit_env_rep::is_anylen (tree t) {
30   return
31     (is_func (t, TMLEN) && ((N(t) == 1) || (N(t) == 3))) ||
32     (is_atomic (t) && is_length (t->label)) ||
33     is_func (t, MACRO, 1);
34 }
35 
36 tree
tmlen_plus(tree t1,tree t2)37 edit_env_rep::tmlen_plus (tree t1, tree t2) {
38   if ((N(t1) == 1) && (N(t2) == 1))
39     return tree (TMLEN, as_string (as_double (t1[0]) + as_double (t2[0])));
40   if (N(t1) == 1) t1= tree (TMLEN, t1[0], t1[0], t1[0]);
41   if (N(t2) == 1) t2= tree (TMLEN, t2[0], t2[0], t2[0]);
42   if (N(t1) < 3 || N(t2) < 3) return tree (ERROR, "invalid tmlen-plus");
43   tree _min= as_string (as_double (t1[0]) + as_double (t2[0]));
44   tree _def= as_string (as_double (t1[1]) + as_double (t2[1]));
45   tree _max= as_string (as_double (t1[2]) + as_double (t2[2]));
46   return tree (TMLEN, _min, _def, _max);
47 }
48 
49 tree
tmlen_min(tree t1,tree t2)50 edit_env_rep::tmlen_min (tree t1, tree t2) {
51   if ((N(t1) == 1) && (N(t2) == 1))
52     return tree (TMLEN, as_string (min (as_double(t1[0]), as_double(t2[0]))));
53   if (N(t1) == 1) t1= tree (TMLEN, t1[0], t1[0], t1[0]);
54   if (N(t2) == 1) t2= tree (TMLEN, t2[0], t2[0], t2[0]);
55   if (N(t1) < 3 || N(t2) < 3) return tree (ERROR, "invalid tmlen-plus");
56   tree _min= as_string (min (as_double (t1[0]), as_double (t2[0])));
57   tree _def= as_string (min (as_double (t1[1]), as_double (t2[1])));
58   tree _max= as_string (min (as_double (t1[2]), as_double (t2[2])));
59   return tree (TMLEN, _min, _def, _max);
60 }
61 
62 tree
tmlen_max(tree t1,tree t2)63 edit_env_rep::tmlen_max (tree t1, tree t2) {
64   if ((N(t1) == 1) && (N(t2) == 1))
65     return tree (TMLEN, as_string (max (as_double(t1[0]), as_double(t2[0]))));
66   if (N(t1) == 1) t1= tree (TMLEN, t1[0], t1[0], t1[0]);
67   if (N(t2) == 1) t2= tree (TMLEN, t2[0], t2[0], t2[0]);
68   if (N(t1) < 3 || N(t2) < 3) return tree (ERROR, "invalid tmlen-plus");
69   tree _min= as_string (max (as_double (t1[0]), as_double (t2[0])));
70   tree _def= as_string (max (as_double (t1[1]), as_double (t2[1])));
71   tree _max= as_string (max (as_double (t1[2]), as_double (t2[2])));
72   return tree (TMLEN, _min, _def, _max);
73 }
74 
75 tree
tmlen_times(double sc,tree t)76 edit_env_rep::tmlen_times (double sc, tree t) {
77   if (N(t) == 1) return tree (TMLEN, as_string (sc * as_double (t[0])));
78   if (N(t) < 3) return tree (ERROR, "invalid tmlen-times");
79   tree _min= as_string (sc * as_double (t[0]));
80   tree _def= as_string (sc * as_double (t[1]));
81   tree _max= as_string (sc * as_double (t[2]));
82   return tree (TMLEN, _min, _def, _max);
83 }
84 
85 tree
tmlen_over(tree t1,tree t2)86 edit_env_rep::tmlen_over (tree t1, tree t2) {
87   t1= t1[N(t1)==1? 0: 1];
88   t2= t2[N(t2)==1? 0: 1];
89   return as_string (as_double (t1) / as_double (t2));
90 }
91 
92 double
tmlen_div(tree t1,tree t2)93 edit_env_rep::tmlen_div (tree t1, tree t2) {
94   t1= t1[N(t1)==1? 0: 1];
95   t2= t2[N(t2)==1? 0: 1];
96   return floor ((as_double (t1) / as_double (t2)) + 0.0001);
97 }
98 
99 tree
tmlen_mod(tree t1,tree t2)100 edit_env_rep::tmlen_mod (tree t1, tree t2) {
101   double div= tmlen_div (t1, t2);
102   return tmlen_plus (t1, tmlen_times (-div, t2));
103 }
104 
105 /******************************************************************************
106 * Length arithmetic for strings
107 ******************************************************************************/
108 
109 void
get_length_unit(string s,SI & un,string & un_str)110 edit_env_rep::get_length_unit (string s, SI& un, string& un_str) {
111   int i;
112   for (i=0; i<N(s); i++)
113     if ((s[i]>='a') && (s[i]<='z')) break;
114   un= as_length (string ("1" * s (i, N(s))));
115   un_str= s (i, N(s));
116 }
117 
118 string
add_lengths(string s1,string s2)119 edit_env_rep::add_lengths (string s1, string s2) {
120   SI l1= as_length (s1);
121   SI l2= as_length (s2);
122   SI un; string un_str;
123   get_length_unit (s1, un, un_str);
124   if (un==0) return "0tmpt";
125   double x= ((double) (l1+l2)) / ((double) un);
126   return as_string (x) * un_str;
127 }
128 
129 string
multiply_length(double x,string s)130 edit_env_rep::multiply_length (double x, string s) {
131   SI l= as_length (s);
132   SI un; string un_str;
133   get_length_unit (s, un, un_str);
134   if (un==0) return "0tmpt";
135   double xl= (x*l) / ((double) un);
136   return as_string (xl) * un_str;
137 }
138 
139 double
divide_lengths(string s1,string s2)140 edit_env_rep::divide_lengths (string s1, string s2) {
141   SI l1= as_length (s1);
142   SI l2= as_length (s2);
143   return ((double) l1) / ((double) l2);
144 }
145 
146 /******************************************************************************
147 * Decoding lengths
148 ******************************************************************************/
149 
150 tree
as_tmlen(tree t)151 edit_env_rep::as_tmlen (tree t) {
152   if (is_func (t, TMLEN)) {
153     if (N(t) == 0) return t;
154     if (is_double (t[0])) return t;
155     if (N(t) < 3) return as_tmlen (t[0]);
156     tree _min= as_tmlen (t[0]);
157     tree _def= as_tmlen (t[1]);
158     tree _max= as_tmlen (t[2]);
159     if (N(_min) < 1) return t;
160     if (N(_def) < 1) return t;
161     if (N(_max) < 1) return t;
162     _min= _min[N(_min) == 3? 1: 0];
163     _def= _def[N(_def) == 3? 1: 0];
164     _max= _max[N(_max) == 3? 1: 0];
165     return tree (TMLEN, _min, _def, _max);
166   }
167   else if (is_atomic (t)) {
168     string s= t->label;
169     int start= 0, i, n=N(s);
170     while ((start+1<n) && (s[start]=='-') && (s[start+1]=='-')) start += 2;
171     for (i=start; (i<n) && ((s[i]<'a') || (s[i]>'z')); i++) {}
172     string s1= s (start, i);
173     string s2= s (i, n);
174     if (!(is_double (s1) && is_locase_alpha (s2))) return tree (TMLEN, "0");
175     return tmlen_times (as_double (s1),
176 			as_tmlen (exec (compound (s2 * "-length"))));
177   }
178   else if (is_func (t, MACRO, 1))
179     return as_tmlen (exec (t[0]));
180   else return tree (TMLEN, "0");
181 }
182 
183 SI
as_length(tree t)184 edit_env_rep::as_length (tree t) {
185   tree r= as_tmlen (t);
186   if (N(r) < 1) return 0;
187   string s= r[N(r)==1? 0: 1]->label;
188   return (SI) (as_double (s));
189 }
190 
191 SI
as_length(tree t,string perc)192 edit_env_rep::as_length (tree t, string perc) {
193   if (is_atomic (t) && N(t->label) > 0 && t->label [N(t->label) - 1] == '%')
194     return as_length (t->label (0, N(t->label) - 1) * perc) / 100;
195   else {
196     tree r= as_tmlen (t);
197     if (N(r) < 1) return 0;
198     string s= r[N(r)==1? 0: 1]->label;
199     return (SI) (as_double (s));
200   }
201 }
202 
203 space
as_hspace(tree t)204 edit_env_rep::as_hspace (tree t) {
205   tree r= as_tmlen (t);
206   if (N(r) == 1)
207     return space ((SI) (as_double (r[0]->label)));
208   else if (N(r) < 3)
209     return 0;
210   else {
211     SI _min= (SI) as_double (r[0]->label);
212     SI _def= (SI) as_double (r[1]->label);
213     SI _max= (SI) as_double (r[2]->label);
214     return space (_min, _def, _max);
215   }
216 }
217 
218 space
as_vspace(tree t)219 edit_env_rep::as_vspace (tree t) {
220   tree r= as_tmlen (t);
221   if (N(r) == 1)
222     return space ((SI) (as_double (r[0]->label)));
223   else if (N(r) < 3)
224     return 0;
225   else {
226     SI _min= (SI) as_double (r[0]->label);
227     SI _def= (SI) as_double (r[1]->label);
228     SI _max= (SI) as_double (r[2]->label);
229     return space (_def + ((SI) (flexibility * (_min - _def))),
230 		  _def,
231 		  _def + ((SI) (flexibility * (_max - _def))));
232   }
233 }
234 
235 point
as_point(tree t)236 edit_env_rep::as_point (tree t) {
237   if ((is_tuple (t) || is_point (t)) && ((N(t)==0) || is_double (t[0])))
238     return ::as_point (t);
239   if (is_tuple (t) || is_point (t)) {
240     int i, n= N(t);
241     point p(n);
242     for (i=0; i<n; i++)
243       p[i]= as_length (t[i]);
244     return fr[p];
245   }
246   if (is_func (t, WITH)) {
247     for (int i=0; i<N(t)-1; i+=2) {
248       tree var= t[i+1];
249       if (is_func (var, QUOTE, 1)) var= var[0];
250       if (t[i] == GID && has_graphical_value (var)) {
251         tree old_t= t[N(t)-1];
252         tree new_t= get_graphical_value (var);
253         if (new_t != old_t) {
254           point old_p= as_point (old_t);
255           point new_p= as_point (new_t);
256           if (new_p != old_p) {
257             graphics_require_update (var);
258             return old_p;//return new_p;
259           }
260         }
261       }
262     }
263     return as_point (t[N(t)-1]);
264   }
265   return point ();
266 }
267 
268 /******************************************************************************
269 * Percentages and magnifications
270 ******************************************************************************/
271 
272 bool
is_percentage(tree t,string s)273 is_percentage (tree t, string s) {
274   return
275     is_atomic (t) &&
276     ends (t->label, s) &&
277     is_double (t->label (0, N (t->label) - 1));
278 }
279 
280 bool
is_percentage(tree t)281 is_percentage (tree t) {
282   return is_percentage (t, "%");
283 }
284 
285 double
as_percentage(tree t)286 as_percentage (tree t) {
287   return as_double (t->label (0, N (t->label) - 1)) / 100.0;
288 }
289 
290 bool
is_magnification(string s)291 is_magnification (string s) {
292   double result;
293   if (N(s) == 0) return false;
294   for (int i=0; i<N(s); /*nop*/) {
295     if (s[i]=='*') { i++; read_double (s, i, result); }
296     else if (s[i]=='/') { i++; read_double (s, i, result); }
297     else return false;
298   }
299   return true;
300 }
301 
302 double
get_magnification(string s)303 get_magnification (string s) {
304   int i=0;
305   double magn= 1.0, result;
306   while (i<N(s)) {
307     if (s[i]=='*') { i++; read_double (s, i, result); magn *= result; }
308     else if (s[i]=='/') { i++; read_double (s, i, result); magn /= result; }
309     else return magn;
310   }
311   return magn;
312 }
313 
314 /******************************************************************************
315 * Built-in length units
316 ******************************************************************************/
317 
exec_cm_length()318 tree edit_env_rep::exec_cm_length () {
319   return tree (TMLEN, as_string (inch/2.54)); }
exec_mm_length()320 tree edit_env_rep::exec_mm_length () {
321   return tree (TMLEN, as_string (inch/25.4)); }
exec_in_length()322 tree edit_env_rep::exec_in_length () {
323   return tree (TMLEN, as_string (inch)); }
exec_pt_length()324 tree edit_env_rep::exec_pt_length () {
325   return tree (TMLEN, as_string (inch/72.27)); }
exec_bp_length()326 tree edit_env_rep::exec_bp_length () {
327   return tree (TMLEN, as_string (inch/72.0)); }
exec_dd_length()328 tree edit_env_rep::exec_dd_length () {
329   return tree (TMLEN, as_string (0.376*inch/25.4)); }
exec_pc_length()330 tree edit_env_rep::exec_pc_length () {
331   return tree (TMLEN, as_string (12.0*inch/72.27)); }
exec_cc_length()332 tree edit_env_rep::exec_cc_length () {
333   return tree (TMLEN, as_string (4.531*inch/25.4)); }
334 
335 tree
exec_fs_length()336 edit_env_rep::exec_fs_length () {
337   double fs=
338     (get_int(FONT_BASE_SIZE) * magn * inch * get_double(FONT_SIZE)) / 72.0;
339   return tree (TMLEN, as_string (fs));
340 }
341 
342 tree
exec_fbs_length()343 edit_env_rep::exec_fbs_length () {
344   double fbs= (get_int(FONT_BASE_SIZE) * magn * inch) / 72.0;
345   return tree (TMLEN, as_string (fbs));
346 }
347 
exec_em_length()348 tree edit_env_rep::exec_em_length () {
349   return tree (TMLEN, as_string (fn->wquad)); }
exec_ln_length()350 tree edit_env_rep::exec_ln_length () {
351   return tree (TMLEN, as_string (fn->wline)); }
exec_sep_length()352 tree edit_env_rep::exec_sep_length () {
353   return tree (TMLEN, as_string (fn->sep)); }
exec_yfrac_length()354 tree edit_env_rep::exec_yfrac_length () {
355   return tree (TMLEN, as_string (fn->yfrac)); }
exec_ex_length()356 tree edit_env_rep::exec_ex_length () {
357   return tree (TMLEN, as_string (fn->yx)); }
358 
359 tree
exec_fn_length()360 edit_env_rep::exec_fn_length () {
361   double fn=
362     (get_int(FONT_BASE_SIZE) * magn * inch * get_double(FONT_SIZE)) / 72.0;
363   return tree (TMLEN, as_string (0.5*fn), as_string (fn), as_string (1.5*fn));
364 }
365 
366 tree
exec_fns_length()367 edit_env_rep::exec_fns_length () {
368   double fn=
369     (get_int(FONT_BASE_SIZE) * magn * inch * get_double(FONT_SIZE)) / 72.0;
370   return tree (TMLEN, "0", "0", as_string (fn));
371 }
372 
373 tree
exec_bls_length()374 edit_env_rep::exec_bls_length () {
375   double fn=
376     (get_int(FONT_BASE_SIZE) * magn * inch * get_double(FONT_SIZE)) / 72.0;
377   return tmlen_plus (tree (TMLEN, as_string (fn)), get_vspace (PAR_SEP));
378 }
379 
380 tree
exec_fnbot_length()381 edit_env_rep::exec_fnbot_length () {
382   return tree (TMLEN, as_string (fn->y1));
383 }
384 
385 tree
exec_fntop_length()386 edit_env_rep::exec_fntop_length () {
387   return tree (TMLEN, as_string (fn->y2));
388 }
389 
390 tree
exec_spc_length()391 edit_env_rep::exec_spc_length () {
392   return tree (TMLEN,
393 	       as_string (fn->spc->min),
394 	       as_string (fn->spc->def),
395 	       as_string (fn->spc->max));
396 }
397 
398 tree
exec_xspc_length()399 edit_env_rep::exec_xspc_length () {
400   return tree (TMLEN,
401 	       as_string (fn->extra->min),
402 	       as_string (fn->extra->def),
403 	       as_string (fn->extra->max));
404 }
405 
406 tree
exec_par_length()407 edit_env_rep::exec_par_length () {
408   SI width, d1, d2, d3, d4, d5, d6, d7;
409   if (read (PAR_WIDTH) != "auto") {
410     width= get_length (PAR_WIDTH);
411     int nr_cols= get_int (PAR_COLUMNS);
412     if (nr_cols > 1) {
413       SI col_sep= get_length (PAR_COLUMNS_SEP);
414       width= ((width+col_sep) / nr_cols) - col_sep;
415     }
416   }
417   else get_page_pars (width, d1, d2, d3, d4, d5, d6, d7);
418   width -= (get_length (PAR_LEFT) + get_length (PAR_RIGHT));
419   return tree (TMLEN, as_string (width));
420 }
421 
422 tree
exec_pag_length()423 edit_env_rep::exec_pag_length () {
424   SI d1, height, d2, d3, d4, d5, d6, d7;
425   get_page_pars (d1, height, d2, d3, d4, d5, d6, d7);
426   return tree (TMLEN, as_string (height));
427 }
428 
exec_tmpt_length()429 tree edit_env_rep::exec_tmpt_length () {
430   return tree (TMLEN, "1"); }
exec_px_length()431 tree edit_env_rep::exec_px_length () {
432   return tree (TMLEN, as_string (pixel)); }
433 
exec_gw_length()434 tree edit_env_rep::exec_gw_length () {
435   return tree (TMLEN, as_string (gw)); }
exec_gh_length()436 tree edit_env_rep::exec_gh_length () {
437   return tree (TMLEN, as_string (gh)); }
exec_gu_length()438 tree edit_env_rep::exec_gu_length () {
439   point p0= fr (point (0.0, 0.0));
440   point p1= fr (point (1.0, 0.0));
441   return tree (TMLEN, as_string (norm (p1 - p0))); }
442 
exec_msec_length()443 tree edit_env_rep::exec_msec_length () { return tree (TMLEN, "1"); }
exec_sec_length()444 tree edit_env_rep::exec_sec_length () { return tree (TMLEN, "1000"); }
exec_min_length()445 tree edit_env_rep::exec_min_length () { return tree (TMLEN, "60000"); }
exec_hr_length()446 tree edit_env_rep::exec_hr_length () { return tree (TMLEN, "3600000"); }
447