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