re-import the lua bindings c file; build ctangle in non-crosscompilers; build a lua...
[mplib] / src / texk / web2c / mpdir / lmplib.c
1 /* lmplib.c
2    
3    Copyright 2006-2008 Taco Hoekwater <taco@luatex.org>
4
5    This file is part of LuaTeX.
6
7    LuaTeX is free software; you can redistribute it and/or modify it under
8    the terms of the GNU General Public License as published by the Free
9    Software Foundation; either version 2 of the License, or (at your
10    option) any later version.
11
12    LuaTeX is distributed in the hope that it will be useful, but WITHOUT
13    ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14    FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
15    License for more details.
16
17    You should have received a copy of the GNU General Public License along
18    with LuaTeX; if not, see <http://www.gnu.org/licenses/>. */
19
20 #include <stdlib.h>
21 #include <string.h>
22 #include <unistd.h>
23 #include <math.h> /* temporary */
24
25 #ifndef pdfTeX
26 #  include <lua.h>
27 #  include <lauxlib.h>
28 #  include <lualib.h>
29 #else
30 #  include <../lua51/lua.h>
31 #  include <../lua51/lauxlib.h>
32 #  include <../lua51/lualib.h>
33 #endif
34
35 #include "mplib.h"
36 #include "psout.h"
37
38    /*@unused@*/ static const char _svn_version[] =
39     "$Id: lmplib.c 1364 2008-07-04 16:09:46Z taco $ $URL: http://scm.foundry.supelec.fr/svn/luatex/trunk/src/texk/web2c/luatexdir/lua/lmplib.c $";
40
41 /* metatable identifiers and tests */
42
43 #define MPLIB_METATABLE     "MPlib"
44 #define MPLIB_FIG_METATABLE "MPlib.fig"
45 #define MPLIB_GR_METATABLE  "MPlib.gr"
46
47 #define is_mp(L,b) (MP *)luaL_checkudata(L,b,MPLIB_METATABLE)
48 #define is_fig(L,b) (struct mp_edge_object **)luaL_checkudata(L,b,MPLIB_FIG_METATABLE)
49 #define is_gr_object(L,b) (struct mp_graphic_object **)luaL_checkudata(L,b,MPLIB_GR_METATABLE)
50
51 /* Lua string pre-hashing */
52
53 #define mplib_init_S(a) do {                                            \
54     lua_pushliteral(L,#a);                                              \
55     mplib_##a##_ptr = (char *)lua_tostring(L,-1);                       \
56     mplib_##a##_index = luaL_ref (L,LUA_REGISTRYINDEX);                 \
57   } while (0)
58
59 #define mplib_push_S(a) do {                                    \
60     lua_rawgeti(L,LUA_REGISTRYINDEX,mplib_##a##_index);         \
61   } while (0)
62
63 #define mplib_is_S(a,i) (mplib_##a##_ptr==(char *)lua_tostring(L,i))
64
65 #define mplib_make_S(a)                                                 \
66   static int mplib_##a##_index = 0;                                     \
67   static char *mplib_##a##_ptr = NULL
68
69 static int mplib_type_Ses[mp_special_code + 1] = { 0 }; /* [0] is not used */
70
71 mplib_make_S(fill);
72 mplib_make_S(outline);
73 mplib_make_S(text);
74 mplib_make_S(special);
75 mplib_make_S(start_bounds);
76 mplib_make_S(stop_bounds);
77 mplib_make_S(start_clip);
78 mplib_make_S(stop_clip);
79
80 mplib_make_S(left_type);
81 mplib_make_S(right_type);
82 mplib_make_S(x_coord);
83 mplib_make_S(y_coord);
84 mplib_make_S(left_x);
85 mplib_make_S(left_y);
86 mplib_make_S(right_x);
87 mplib_make_S(right_y);
88
89 mplib_make_S(color);
90 mplib_make_S(dash);
91 mplib_make_S(depth);
92 mplib_make_S(dsize);
93 mplib_make_S(font);
94 mplib_make_S(height);
95 mplib_make_S(htap);
96 mplib_make_S(linecap);
97 mplib_make_S(linejoin);
98 mplib_make_S(miterlimit);
99 mplib_make_S(path);
100 mplib_make_S(pen);
101 mplib_make_S(postscript);
102 mplib_make_S(prescript);
103 mplib_make_S(transform);
104 mplib_make_S(type);
105 mplib_make_S(width);
106
107 static void mplib_init_Ses(lua_State * L)
108 {
109     mplib_init_S(fill);
110     mplib_init_S(outline);
111     mplib_init_S(text);
112     mplib_init_S(start_bounds);
113     mplib_init_S(stop_bounds);
114     mplib_init_S(start_clip);
115     mplib_init_S(stop_clip);
116     mplib_init_S(special);
117
118     mplib_type_Ses[mp_fill_code] = mplib_fill_index;
119     mplib_type_Ses[mp_stroked_code] = mplib_outline_index;
120     mplib_type_Ses[mp_text_code] = mplib_text_index;
121     mplib_type_Ses[mp_start_bounds_code] = mplib_start_bounds_index;
122     mplib_type_Ses[mp_stop_bounds_code] = mplib_stop_bounds_index;
123     mplib_type_Ses[mp_start_clip_code] = mplib_start_clip_index;
124     mplib_type_Ses[mp_stop_clip_code] = mplib_stop_clip_index;
125     mplib_type_Ses[mp_special_code] = mplib_special_index;
126
127     mplib_init_S(left_type);
128     mplib_init_S(right_type);
129     mplib_init_S(x_coord);
130     mplib_init_S(y_coord);
131     mplib_init_S(left_x);
132     mplib_init_S(left_y);
133     mplib_init_S(right_x);
134     mplib_init_S(right_y);
135
136     mplib_init_S(color);
137     mplib_init_S(dash);
138     mplib_init_S(depth);
139     mplib_init_S(dsize);
140     mplib_init_S(font);
141     mplib_init_S(height);
142     mplib_init_S(htap);
143     mplib_init_S(linecap);
144     mplib_init_S(linejoin);
145     mplib_init_S(miterlimit);
146     mplib_init_S(path);
147     mplib_init_S(pen);
148     mplib_init_S(postscript);
149     mplib_init_S(prescript);
150     mplib_init_S(transform);
151     mplib_init_S(type);
152     mplib_init_S(width);
153 }
154
155
156 /* Enumeration arrays to map MPlib enums to Lua strings */
157
158 static const char *interaction_options[] =
159     { "unknown", "batch", "nonstop", "scroll", "errorstop", NULL };
160
161 static const char *mplib_filetype_names[] =
162     { "term", "error", "mp", "log", "ps", "mem", "tfm", "map", "pfb", "enc", NULL };
163
164 static const char *knot_type_enum[] =
165     { "endpoint", "explicit", "given", "curl", "open", "end_cycle" };
166
167 static const char *fill_fields[] =
168     { "type", "path", "htap", "pen", "color", "linejoin", "miterlimit",
169     "prescript", "postscript", NULL };
170
171 static const char *stroked_fields[] =
172     { "type", "path", "pen", "color", "linejoin", "miterlimit", "linecap",
173       "dash", "prescript", "postscript", NULL };
174
175 static const char *text_fields[] =
176     { "type", "text", "dsize", "font", "color", "width", "height", "depth",
177       "transform", "prescript", "postscript", NULL };
178
179 static const char *special_fields[] =
180     { "type", "prescript", NULL };
181
182 static const char *start_bounds_fields[] =
183     { "type", "path", NULL };
184
185 static const char *start_clip_fields[] = 
186     { "type", "path", NULL };
187
188 static const char *stop_bounds_fields[] = 
189     { "type", NULL };
190
191 static const char *stop_clip_fields[] = 
192     { "type", NULL };
193
194 static const char *no_fields[] = 
195     { NULL };
196
197
198 /* The list of supported MPlib options (not all make sense) */
199
200 typedef enum {
201     P_ERROR_LINE, P_MAX_LINE, 
202     P_MAIN_MEMORY, P_HASH_SIZE, P_PARAM_SIZE, P_IN_OPEN, P_RANDOM_SEED,
203     P_INTERACTION, P_INI_VERSION, P_MEM_NAME, P_JOB_NAME, P_FIND_FILE, 
204     P__SENTINEL } mplib_parm_idx;
205
206 typedef struct {
207     const char *name;           /* parameter name */
208     mplib_parm_idx idx;         /* parameter index */
209 } mplib_parm_struct;
210
211 static mplib_parm_struct mplib_parms[] = {
212     {"error_line",        P_ERROR_LINE  },
213     {"print_line",        P_MAX_LINE    },
214     {"main_memory",       P_MAIN_MEMORY },
215     {"hash_size",         P_HASH_SIZE   },
216     {"param_size",        P_PARAM_SIZE  },
217     {"max_in_open",       P_IN_OPEN     },
218     {"random_seed",       P_RANDOM_SEED },
219     {"interaction",       P_INTERACTION },
220     {"ini_version",       P_INI_VERSION },
221     {"mem_name",          P_MEM_NAME    },
222     {"job_name",          P_JOB_NAME    },
223     {"find_file",         P_FIND_FILE   },
224     {NULL,                P__SENTINEL   }
225 };
226
227
228 /* Start by defining the needed callback routines for the library  */
229
230 static char *mplib_find_file(MP mp, const char *fname, const char *fmode, int ftype)
231 {
232     lua_State *L = (lua_State *)mp_userdata(mp);
233     lua_checkstack(L, 4);
234     lua_getfield(L, LUA_REGISTRYINDEX, "mplib_file_finder");
235     if (lua_isfunction(L, -1)) {
236         char *s = NULL, *x = NULL;
237         lua_pushstring(L, fname);
238         lua_pushstring(L, fmode);
239         if (ftype >= mp_filetype_text) {
240           lua_pushnumber(L, (lua_Number)(ftype - mp_filetype_text));
241         } else {
242             lua_pushstring(L, mplib_filetype_names[ftype]);
243         }
244         if (lua_pcall(L, 3, 1, 0) != 0) {
245             fprintf(stdout, "Error in mp.find_file: %s\n",
246                     (char *) lua_tostring(L, -1));
247             return NULL;
248         }
249         x = (char *) lua_tostring(L, -1);
250         if (x != NULL)
251             s = strdup(x);
252         lua_pop(L, 1);          /* pop the string */
253         return s;
254     } else {
255         lua_pop(L, 1);
256     }
257     if (fmode[0] != 'r' || (!access(fname, R_OK)) || ftype) {
258         return strdup(fname);
259     }
260     return NULL;
261 }
262
263 static int mplib_find_file_function(lua_State * L)
264 {
265     if (!(lua_isfunction(L, -1) || lua_isnil(L, -1))) {
266         return 1;               /* error */
267     }
268     lua_pushstring(L, "mplib_file_finder");
269     lua_pushvalue(L, -2);
270     lua_rawset(L, LUA_REGISTRYINDEX);
271     return 0;
272 }
273
274 #define xfree(A) if ((A)!=NULL) { free((A)); A = NULL; }
275
276 static int mplib_new(lua_State * L)
277 {
278     MP *mp_ptr;
279     mp_ptr = lua_newuserdata(L, sizeof(MP *));
280     if (mp_ptr) {
281         int i;
282         struct MP_options *options = mp_options();
283         options->userdata = (void *) L;
284         options->noninteractive = 1;    /* required ! */
285         options->find_file = mplib_find_file;
286         options->print_found_names = 1;
287         if (lua_type(L, 1) == LUA_TTABLE) {
288             for (i = 0; mplib_parms[i].name != NULL; i++) {
289                 lua_getfield(L, 1, mplib_parms[i].name);
290                 if (lua_isnil(L, -1)) {
291                     lua_pop(L, 1);
292                     continue;   /* skip unset */
293                 }
294                 switch (mplib_parms[i].idx) {
295                 case P_ERROR_LINE:
296                   options->error_line = (int)lua_tointeger(L, -1);
297                     if (options->error_line<60) options->error_line =60;
298                     if (options->error_line>250) options->error_line = 250;
299                     options->half_error_line = (options->error_line/2)+10;
300                     break;
301                 case P_MAX_LINE:
302                     options->max_print_line = (int)lua_tointeger(L, -1);
303                     if (options->max_print_line<60) options->max_print_line = 60;
304                     break;
305                 case P_MAIN_MEMORY:
306                     options->main_memory = (int)lua_tointeger(L, -1);
307                     break;
308                 case P_HASH_SIZE:
309                     options->hash_size = (unsigned)lua_tointeger(L, -1);
310                     break;
311                 case P_PARAM_SIZE:
312                     options->param_size = (int)lua_tointeger(L, -1);
313                     break;
314                 case P_IN_OPEN:
315                     options->max_in_open = (int)lua_tointeger(L, -1);
316                     break;
317                 case P_RANDOM_SEED:
318                   options->random_seed = (int)lua_tointeger(L, -1);
319                     break;
320                 case P_INTERACTION:
321                     options->interaction =
322                         luaL_checkoption(L, -1, "errorstopmode",
323                                          interaction_options);
324                     break;
325                 case P_INI_VERSION:
326                     options->ini_version = lua_toboolean(L, -1);
327                     break;
328                 case P_MEM_NAME:
329                     options->mem_name = strdup((char *) lua_tostring(L, -1));
330                     break;
331                 case P_JOB_NAME:
332                     options->job_name = strdup((char *) lua_tostring(L, -1));
333                     break;
334                 case P_FIND_FILE:
335                     if (mplib_find_file_function(L)) {  /* error here */
336                         fprintf(stdout,
337                                 "Invalid arguments to mp.new({find_file=...})\n");
338                     }
339                     break;
340                 default:
341                     break;
342                 }
343                 lua_pop(L, 1);
344             }
345         }
346         *mp_ptr = mp_initialize(options);
347         xfree(options->command_line);
348         xfree(options->mem_name);
349         free(options);
350         if (*mp_ptr) {
351             luaL_getmetatable(L, MPLIB_METATABLE);
352             lua_setmetatable(L, -2);
353             return 1;
354         }
355     }
356     lua_pushnil(L);
357     return 1;
358 }
359
360 static int mplib_collect(lua_State * L)
361 {
362     MP *mp_ptr = is_mp(L, 1);
363     if (*mp_ptr != NULL) {
364       (void)mp_finish(*mp_ptr);
365       *mp_ptr = NULL;
366     }
367     return 0;
368 }
369
370 static int mplib_tostring(lua_State * L)
371 {
372     MP *mp_ptr = is_mp(L, 1);
373     if (*mp_ptr != NULL) {
374       (void)lua_pushfstring(L, "<MP %p>", *mp_ptr);
375         return 1;
376     }
377     return 0;
378 }
379
380 static int mplib_wrapresults(lua_State * L, mp_run_data *res, int status)
381 {
382     lua_checkstack(L, 5);
383     lua_newtable(L);
384     if (res->term_out.size != 0) {
385         lua_pushstring(L, res->term_out.data);
386         lua_setfield(L, -2, "term");
387     }
388     if (res->error_out.size != 0) {
389         lua_pushstring(L, res->error_out.data);
390         lua_setfield(L, -2, "error");
391     }
392     if (res->log_out.size != 0) {
393         lua_pushstring(L, res->log_out.data);
394         lua_setfield(L, -2, "log");
395     }
396     if (res->edges != NULL) {
397         struct mp_edge_object **v;
398         struct mp_edge_object *p = res->edges;
399         int i = 1;
400         lua_newtable(L);
401         while (p != NULL) {
402             v = lua_newuserdata(L, sizeof(struct mp_edge_object *));
403             *v = p;
404             luaL_getmetatable(L, MPLIB_FIG_METATABLE);
405             lua_setmetatable(L, -2);
406             lua_rawseti(L, -2, i);
407             i++;
408             p = p->next;
409         }
410         lua_setfield(L, -2, "fig");
411         res->edges = NULL;
412     }
413     lua_pushnumber(L, (lua_Number)status);
414     lua_setfield(L, -2, "status");
415     return 1;
416 }
417
418 static int mplib_execute(lua_State * L)
419 {
420     MP *mp_ptr = is_mp(L, 1);
421     if (*mp_ptr != NULL && lua_isstring(L, 2)) {
422         size_t l;
423         char *s = (char *) lua_tolstring(L, 2, &l);
424         int h = mp_execute(*mp_ptr, s, l);
425         mp_run_data *res = mp_rundata(*mp_ptr);
426         return mplib_wrapresults(L, res, h);
427     } else {
428         lua_pushnil(L);
429     }
430     return 1;
431 }
432
433 static int mplib_finish(lua_State * L)
434 {
435     MP *mp_ptr = is_mp(L, 1);
436     if (*mp_ptr != NULL) {
437       int i;
438       int h = mp_execute(*mp_ptr,NULL,0);
439       mp_run_data *res = mp_rundata(*mp_ptr);
440       i = mplib_wrapresults(L, res, h);
441       (void)mp_finish(*mp_ptr);
442        *mp_ptr = NULL;
443        return i;
444     } else {
445         lua_pushnil(L);
446     }
447     return 1;
448 }
449
450 static int mplib_char_dimension(lua_State * L, int t)
451 {
452   MP *mp_ptr = is_mp(L, 1);
453   if (*mp_ptr != NULL) {
454     char *fname = (char *)luaL_checkstring(L,2);
455     int charnum = (int)luaL_checkinteger(L,3);
456     if (charnum<0 || charnum>255) {
457       lua_pushnumber(L, (lua_Number)0);
458     } else {
459       lua_pushnumber(L,(lua_Number)mp_get_char_dimension(*mp_ptr,fname,charnum,t));
460     }
461   } else {
462     lua_pushnumber(L, (lua_Number)0);
463   }
464   return 1;
465 }
466
467 static int mplib_charwidth(lua_State * L) 
468 {
469   return mplib_char_dimension(L, 'w');
470 }
471
472 static int mplib_chardepth(lua_State * L) 
473 {
474   return mplib_char_dimension(L, 'd');
475 }
476
477 static int mplib_charheight(lua_State * L) 
478 {
479   return mplib_char_dimension(L, 'h');
480 }
481
482
483 static int mplib_statistics(lua_State * L)
484 {
485     MP *mp_ptr = is_mp(L, 1);
486     if (*mp_ptr != NULL) {
487         lua_newtable(L);
488         lua_pushnumber(L, (lua_Number)mp_memory_usage(*mp_ptr));
489         lua_setfield(L, -2, "main_memory");
490         lua_pushnumber(L, (lua_Number)mp_hash_usage(*mp_ptr));
491         lua_setfield(L, -2, "hash_size");
492         lua_pushnumber(L, (lua_Number)mp_param_usage(*mp_ptr));
493         lua_setfield(L, -2, "param_size");
494         lua_pushnumber(L, (lua_Number)mp_open_usage(*mp_ptr));
495         lua_setfield(L, -2, "max_in_open");
496     } else {
497         lua_pushnil(L);
498     }
499     return 1;
500 }
501
502
503 /* figure methods */
504
505 static int mplib_fig_collect(lua_State * L)
506 {
507     struct mp_edge_object **hh = is_fig(L, 1);
508     if (*hh != NULL) {
509         mp_gr_toss_objects(*hh);
510         *hh = NULL;
511     }
512     return 0;
513 }
514
515 static int mplib_fig_body(lua_State * L)
516 {
517     int i = 1;
518     struct mp_graphic_object **v;
519     struct mp_graphic_object *p;
520     struct mp_edge_object **hh = is_fig(L, 1);
521     lua_newtable(L);
522     p = (*hh)->body;
523     while (p != NULL) {
524         v = lua_newuserdata(L, sizeof(struct mp_graphic_object *));
525         *v = p;
526         luaL_getmetatable(L, MPLIB_GR_METATABLE);
527         lua_setmetatable(L, -2);
528         lua_rawseti(L, -2, i);
529         i++;
530         p = p->next;
531     }
532     (*hh)->body = NULL;         /* prevent double free */
533     return 1;
534 }
535
536 static int mplib_fig_copy_body(lua_State * L)
537 {
538     int i = 1;
539     struct mp_graphic_object **v;
540     struct mp_graphic_object *p;
541     struct mp_edge_object **hh = is_fig(L, 1);
542     lua_newtable(L);
543     p = (*hh)->body;
544     while (p != NULL) {
545         v = lua_newuserdata(L, sizeof(struct mp_graphic_object *));
546         *v = mp_gr_copy_object((*hh)->parent, p);
547         luaL_getmetatable(L, MPLIB_GR_METATABLE);
548         lua_setmetatable(L, -2);
549         lua_rawseti(L, -2, i);
550         i++;
551         p = p->next;
552     }
553     return 1;
554 }
555
556
557 static int mplib_fig_tostring(lua_State * L)
558 {
559     struct mp_edge_object **hh = is_fig(L, 1);
560     (void)lua_pushfstring(L, "<figure %p>", *hh);
561     return 1;
562 }
563
564 static int mplib_fig_postscript(lua_State * L)
565 {
566     mp_run_data *res;
567     struct mp_edge_object **hh = is_fig(L, 1);
568     int prologues = (int)luaL_optnumber(L, 2, (lua_Number)-1);
569     int procset = (int)luaL_optnumber(L, 3, (lua_Number)-1);
570     if (mp_ps_ship_out(*hh, prologues, procset) 
571         && (res = mp_rundata((*hh)->parent))
572         && (res->ps_out.size != 0)) {
573         lua_pushstring(L, res->ps_out.data);
574     } else {
575         lua_pushnil(L);
576     }
577     return 1;
578 }
579
580 static int mplib_fig_filename(lua_State * L)
581 {
582     struct mp_edge_object **hh = is_fig(L, 1);
583     if (*hh != NULL) {
584         char *s = (*hh)->filename;
585         lua_pushstring(L, s);
586     } else {
587         lua_pushnil(L);
588     }
589     return 1;
590 }
591
592 static int mplib_fig_width(lua_State * L)
593 {
594     struct mp_edge_object **hh = is_fig(L, 1);
595     if (*hh != NULL) {
596       lua_pushnumber(L, (double) (*hh)->width / 65536.0);
597     } else {
598       lua_pushnil(L);
599     }
600     return 1;
601 }
602
603 static int mplib_fig_height(lua_State * L)
604 {
605     struct mp_edge_object **hh = is_fig(L, 1);
606     if (*hh != NULL) {
607       lua_pushnumber(L, (double) (*hh)->height / 65536.0);
608     } else {
609       lua_pushnil(L);
610     }
611     return 1;
612 }
613
614 static int mplib_fig_depth(lua_State * L)
615 {
616     struct mp_edge_object **hh = is_fig(L, 1);
617     if (*hh != NULL) {
618       lua_pushnumber(L, (double) (*hh)->depth / 65536.0);
619     } else {
620       lua_pushnil(L);
621     }
622     return 1;
623 }
624
625 static int mplib_fig_italcorr(lua_State * L)
626 {
627     struct mp_edge_object **hh = is_fig(L, 1);
628     if (*hh != NULL) {
629       lua_pushnumber(L, (double) (*hh)->ital_corr / 65536.0);
630     } else {
631       lua_pushnil(L);
632     }
633     return 1;
634 }
635
636 static int mplib_fig_charcode(lua_State * L)
637 {
638     struct mp_edge_object **hh = is_fig(L, 1);
639     if (*hh != NULL) {
640       lua_pushnumber(L, (lua_Number)(*hh)->charcode);
641     } else {
642       lua_pushnil(L);
643     }
644     return 1;
645 }
646
647
648
649 static int mplib_fig_bb(lua_State * L)
650 {
651     struct mp_edge_object **hh = is_fig(L, 1);
652     lua_newtable(L);
653     lua_pushnumber(L, (double) (*hh)->minx / 65536.0);
654     lua_rawseti(L, -2, 1);
655     lua_pushnumber(L, (double) (*hh)->miny / 65536.0);
656     lua_rawseti(L, -2, 2);
657     lua_pushnumber(L, (double) (*hh)->maxx / 65536.0);
658     lua_rawseti(L, -2, 3);
659     lua_pushnumber(L, (double) (*hh)->maxy / 65536.0);
660     lua_rawseti(L, -2, 4);
661     return 1;
662 }
663
664 /* object methods */
665
666 static int mplib_gr_collect(lua_State * L)
667 {
668     struct mp_graphic_object **hh = is_gr_object(L, 1);
669     if (*hh != NULL) {
670         mp_gr_toss_object(*hh);
671         *hh = NULL;
672     }
673     return 0;
674 }
675
676 static int mplib_gr_tostring(lua_State * L)
677 {
678     struct mp_graphic_object **hh = is_gr_object(L, 1);
679     (void)lua_pushfstring(L, "<object %p>", *hh);
680     return 1;
681 }
682
683 #define pyth(a,b) (sqrt((a)*(a) + (b)*(b)))
684
685 #define aspect_bound   (10.0/65536.0)
686 #define aspect_default (1.0/65536.0)
687
688 static double eps  = 0.0001;
689
690 static double coord_range_x (mp_knot *h, double dz) {
691   double z;
692   double zlo = 0.0, zhi = 0.0;
693   mp_knot *f = h; 
694   while (h != NULL) {
695     z = (double)h->x_coord;
696     if (z < zlo) zlo = z; else if (z > zhi) zhi = z;
697     z = (double)h->right_x;
698     if (z < zlo) zlo = z; else if (z > zhi) zhi = z;
699     z = (double)h->left_x;
700     if (z < zlo) zlo = z; else if (z > zhi) zhi = z;
701     h = h->next;
702     if (h==f)
703       break;
704   }
705   return (zhi - zlo <= dz ? aspect_bound : aspect_default);
706 }
707
708 static double coord_range_y (mp_knot *h, double dz) {
709   double z;
710   double zlo = 0.0, zhi = 0.0;
711   mp_knot *f = h; 
712   while (h != NULL) {
713     z = (double)h->y_coord;
714     if (z < zlo) zlo = z; else if (z > zhi) zhi = z;
715     z = (double)h->right_y;
716     if (z < zlo) zlo = z; else if (z > zhi) zhi = z;
717     z = (double)h->left_y;
718     if (z < zlo) zlo = z; else if (z > zhi) zhi = z;
719     h = h->next;
720     if (h==f)
721       break;
722   }
723   return (zhi - zlo <= dz ? aspect_bound : aspect_default);
724 }
725
726
727 static int mplib_gr_peninfo(lua_State * L) {
728     double x_coord, y_coord, left_x, left_y, right_x, right_y;
729     double wx, wy;
730     double rx = 1.0, sx = 0.0, sy = 0.0, ry = 1.0, tx = 0.0, ty = 0.0;
731     double divider = 1.0;
732     double width = 1.0;
733     mp_knot *p = NULL, *path = NULL;
734     struct mp_graphic_object **hh = is_gr_object(L, -1);
735     if (!*hh) {
736       lua_pushnil(L);
737       return 1;
738     }
739     if ((*hh)->type == mp_fill_code) {
740       p    = ((mp_fill_object *)(*hh))->pen_p;
741       path = ((mp_fill_object *)(*hh))->path_p;
742     } else if ((*hh)->type == mp_stroked_code) {
743       p    = ((mp_stroked_object *)(*hh))->pen_p;
744       path = ((mp_stroked_object *)(*hh))->path_p;
745     }
746     if (p==NULL || path == NULL) {
747       lua_pushnil(L);
748       return 1;
749     }
750     x_coord = p->x_coord/65536.0;
751     y_coord = p->y_coord/65536.0;
752     left_x = p->left_x/65536.0;
753     left_y = p->left_y/65536.0;
754     right_x = p->right_x/65536.0;
755     right_y = p->right_y/65536.0;
756     if ((right_x == x_coord) && (left_y == y_coord)) {
757       wx = fabs(left_x  - x_coord);
758       wy = fabs(right_y - y_coord);
759     } else {
760       wx = pyth(left_x - x_coord, right_x - x_coord);
761       wy = pyth(left_y - y_coord, right_y - y_coord);
762     }
763     if ((wy/coord_range_x(path, wx)) >= (wx/coord_range_y(path, wy)))
764       width = wy;
765     else
766       width = wx;
767     tx = x_coord; 
768     ty = y_coord;
769     sx = left_x - tx; 
770     rx = left_y - ty; 
771     ry = right_x - tx; 
772     sy = right_y - ty;
773     if (width !=1.0) {
774       if (width == 0.0) {
775         sx = 1.0; sy = 1.0;
776       } else {
777         rx/=width; ry/=width; sx/=width; sy/=width;
778       }
779     }
780     if (fabs(sx) < eps) sx = eps;
781     if (fabs(sy) < eps) sy = eps;
782     divider = sx*sy - rx*ry;
783     lua_newtable(L);
784     lua_pushnumber(L,width); lua_setfield(L,-2,"width");
785     lua_pushnumber(L,rx); lua_setfield(L,-2,"rx");
786     lua_pushnumber(L,sx); lua_setfield(L,-2,"sx");
787     lua_pushnumber(L,sy); lua_setfield(L,-2,"sy");
788     lua_pushnumber(L,ry); lua_setfield(L,-2,"ry");
789     lua_pushnumber(L,tx); lua_setfield(L,-2,"tx");
790     lua_pushnumber(L,ty); lua_setfield(L,-2,"ty");
791     return 1;
792 }
793
794
795 static int mplib_gr_fields(lua_State * L)
796 {
797     const char **fields;
798     int i;
799     struct mp_graphic_object **hh = is_gr_object(L, 1);
800     if (*hh) {
801         switch ((*hh)->type) {
802         case mp_fill_code:
803             fields = fill_fields;
804             break;
805         case mp_stroked_code:
806             fields = stroked_fields;
807             break;
808         case mp_text_code:
809             fields = text_fields;
810             break;
811         case mp_special_code:
812             fields = special_fields;
813             break;
814         case mp_start_clip_code:
815             fields = start_clip_fields;
816             break;
817         case mp_start_bounds_code:
818             fields = start_bounds_fields;
819             break;
820         case mp_stop_clip_code:
821             fields = stop_clip_fields;
822             break;
823         case mp_stop_bounds_code:
824             fields = stop_bounds_fields;
825             break;
826         default:
827             fields = no_fields;
828         }
829         lua_newtable(L);
830         for (i = 0; fields[i] != NULL; i++) {
831             lua_pushstring(L, fields[i]);
832             lua_rawseti(L, -2, (i + 1));
833         }
834     } else {
835         lua_pushnil(L);
836     }
837     return 1;
838 }
839
840
841 #define mplib_push_number(L,x) lua_pushnumber(L,(lua_Number)(x)/65536.0)
842
843 #define MPLIB_PATH 0
844 #define MPLIB_PEN 1
845
846 static void mplib_push_path(lua_State * L, struct mp_knot *h, int is_pen)
847 {
848     struct mp_knot *p;          /* for scanning the path */
849     int i = 1;
850     p = h;
851     if (p != NULL) {
852         lua_newtable(L);
853         do {
854             lua_createtable(L, 0, 6);
855             if (!is_pen) {
856                 if (p->left_type != mp_explicit) {
857                     mplib_push_S(left_type);
858                     lua_pushstring(L, knot_type_enum[p->left_type]);
859                     lua_rawset(L, -3);
860                 }
861                 if (p->right_type != mp_explicit) {
862                     mplib_push_S(right_type);
863                     lua_pushstring(L, knot_type_enum[p->right_type]);
864                     lua_rawset(L, -3);
865                 }
866             }
867             mplib_push_S(x_coord);
868             mplib_push_number(L, p->x_coord);
869             lua_rawset(L, -3);
870             mplib_push_S(y_coord);
871             mplib_push_number(L, p->y_coord);
872             lua_rawset(L, -3);
873             mplib_push_S(left_x);
874             mplib_push_number(L, p->left_x);
875             lua_rawset(L, -3);
876             mplib_push_S(left_y);
877             mplib_push_number(L, p->left_y);
878             lua_rawset(L, -3);
879             mplib_push_S(right_x);
880             mplib_push_number(L, p->right_x);
881             lua_rawset(L, -3);
882             mplib_push_S(right_y);
883             mplib_push_number(L, p->right_y);
884             lua_rawset(L, -3);
885             lua_rawseti(L, -2, i);
886             i++;
887             if (p->right_type == mp_endpoint) {
888                 return;
889             }
890             p = p->next;
891         } while (p != h);
892     } else {
893         lua_pushnil(L);
894     }
895 }
896
897 /* this assumes that the top of the stack is a table 
898    or nil already in the case
899  */
900 static void mplib_push_pentype(lua_State * L, struct mp_knot *h)
901 {
902     struct mp_knot *p;          /* for scanning the path */
903     p = h;
904     if (p == NULL) {
905         /* do nothing */
906     } else if (p == p->next) {
907         mplib_push_S(type);
908         lua_pushstring(L, "elliptical");
909         lua_rawset(L, -3);
910     } else {
911     }
912 }
913
914 #define set_color_objects(pq)                           \
915   object_color_model = pq->color_model;           \
916   object_color_a = pq->color.a_val;              \
917   object_color_b = pq->color.b_val;              \
918   object_color_c = pq->color.c_val;              \
919   object_color_d = pq->color.d_val;
920
921
922 static void mplib_push_color(lua_State * L, struct mp_graphic_object *p)
923 {
924     int object_color_model;
925     int object_color_a, object_color_b, object_color_c, object_color_d;
926     if (p != NULL) {
927         if (p->type == mp_fill_code) {
928             mp_fill_object *h = (mp_fill_object *) p;
929             set_color_objects(h);
930         } else if (p->type == mp_stroked_code) {
931             mp_stroked_object *h = (mp_stroked_object *) p;
932             set_color_objects(h);
933         } else {
934             mp_text_object *h = (mp_text_object *) p;
935             set_color_objects(h);
936         }
937         lua_newtable(L);
938         if (object_color_model >= mp_grey_model) {
939             mplib_push_number(L, object_color_a);
940             lua_rawseti(L, -2, 1);
941             if (object_color_model >= mp_rgb_model) {
942                 mplib_push_number(L, object_color_b);
943                 lua_rawseti(L, -2, 2);
944                 mplib_push_number(L, object_color_c);
945                 lua_rawseti(L, -2, 3);
946                 if (object_color_model == mp_cmyk_model) {
947                     mplib_push_number(L, object_color_d);
948                     lua_rawseti(L, -2, 4);
949                 }
950             }
951         }
952     } else {
953         lua_pushnil(L);
954     }
955 }
956
957 /* the dash scale is not exported, the field has no external value */
958 static void mplib_push_dash(lua_State * L, struct mp_stroked_object *h)
959 {
960     mp_dash_object *d;
961     double ds;
962     if (h != NULL && h->dash_p != NULL) {
963         d = h->dash_p;
964         lua_newtable(L);
965         mplib_push_number(L, d->offset);
966         lua_setfield(L, -2, "offset");
967         if (d->array != NULL) {
968             int i = 0;
969             lua_newtable(L);
970             while (*(d->array + i) != -1) {
971                 ds = *(d->array + 1) / 65536.0;
972                 lua_pushnumber(L, ds);
973                 i++;
974                 lua_rawseti(L, -2, i);
975             }
976             lua_setfield(L, -2, "dashes");
977         }
978     } else {
979         lua_pushnil(L);
980     }
981 }
982
983 static void mplib_push_transform(lua_State * L, struct mp_text_object *h)
984 {
985     int i = 1;
986     if (h != NULL) {
987         lua_createtable(L, 6, 0);
988         mplib_push_number(L, h->tx);
989         lua_rawseti(L, -2, i);
990         i++;
991         mplib_push_number(L, h->ty);
992         lua_rawseti(L, -2, i);
993         i++;
994         mplib_push_number(L, h->txx);
995         lua_rawseti(L, -2, i);
996         i++;
997         mplib_push_number(L, h->tyx);
998         lua_rawseti(L, -2, i);
999         i++;
1000         mplib_push_number(L, h->txy);
1001         lua_rawseti(L, -2, i);
1002         i++;
1003         mplib_push_number(L, h->tyy);
1004         lua_rawseti(L, -2, i);
1005         i++;
1006     } else {
1007         lua_pushnil(L);
1008     }
1009 }
1010
1011 #define FIELD(A) (mplib_is_S(A,2))
1012
1013 static void mplib_fill(lua_State * L, struct mp_fill_object *h)
1014 {
1015     if (FIELD(path)) {
1016         mplib_push_path(L, h->path_p, MPLIB_PATH);
1017     } else if (FIELD(htap)) {
1018         mplib_push_path(L, h->htap_p, MPLIB_PATH);
1019     } else if (FIELD(pen)) {
1020         mplib_push_path(L, h->pen_p, MPLIB_PEN);
1021         mplib_push_pentype(L, h->pen_p);
1022     } else if (FIELD(color)) {
1023         mplib_push_color(L, (mp_graphic_object *) h);
1024     } else if (FIELD(linejoin)) {
1025       lua_pushnumber(L, (lua_Number)h->ljoin);
1026     } else if (FIELD(miterlimit)) {
1027         mplib_push_number(L, h->miterlim);
1028     } else if (FIELD(prescript)) {
1029         lua_pushstring(L, h->pre_script);
1030     } else if (FIELD(postscript)) {
1031         lua_pushstring(L, h->post_script);
1032     } else {
1033         lua_pushnil(L);
1034     }
1035 }
1036
1037 static void mplib_stroked(lua_State * L, struct mp_stroked_object *h)
1038 {
1039     if (FIELD(path)) {
1040         mplib_push_path(L, h->path_p, MPLIB_PATH);
1041     } else if (FIELD(pen)) {
1042         mplib_push_path(L, h->pen_p, MPLIB_PEN);
1043         mplib_push_pentype(L, h->pen_p);
1044     } else if (FIELD(color)) {
1045         mplib_push_color(L, (mp_graphic_object *) h);
1046     } else if (FIELD(dash)) {
1047         mplib_push_dash(L, h);
1048     } else if (FIELD(linecap)) {
1049         lua_pushnumber(L, (lua_Number)h->lcap);
1050     } else if (FIELD(linejoin)) {
1051       lua_pushnumber(L, (lua_Number)h->ljoin);
1052     } else if (FIELD(miterlimit)) {
1053         mplib_push_number(L, h->miterlim);
1054     } else if (FIELD(prescript)) {
1055         lua_pushstring(L, h->pre_script);
1056     } else if (FIELD(postscript)) {
1057         lua_pushstring(L, h->post_script);
1058     } else {
1059         lua_pushnil(L);
1060     }
1061 }
1062
1063 static void mplib_text(lua_State * L, struct mp_text_object *h)
1064 {
1065     if (FIELD(text)) {
1066         lua_pushstring(L, h->text_p);
1067     } else if (FIELD(dsize)) {
1068         mplib_push_number(L, (h->font_dsize / 16));
1069     } else if (FIELD(font)) {
1070         lua_pushstring(L, h->font_name);
1071     } else if (FIELD(color)) {
1072         mplib_push_color(L, (mp_graphic_object *) h);
1073     } else if (FIELD(width)) {
1074         mplib_push_number(L, h->width);
1075     } else if (FIELD(height)) {
1076         mplib_push_number(L, h->height);
1077     } else if (FIELD(depth)) {
1078         mplib_push_number(L, h->depth);
1079     } else if (FIELD(transform)) {
1080         mplib_push_transform(L, h);
1081     } else if (FIELD(prescript)) {
1082         lua_pushstring(L, h->pre_script);
1083     } else if (FIELD(postscript)) {
1084         lua_pushstring(L, h->post_script);
1085     } else {
1086         lua_pushnil(L);
1087     }
1088 }
1089
1090 static void mplib_special(lua_State * L, struct mp_special_object *h)
1091 {
1092     if (FIELD(prescript)) {
1093         lua_pushstring(L, h->pre_script);
1094     } else {
1095         lua_pushnil(L);
1096     }
1097 }
1098
1099 static void mplib_start_bounds(lua_State * L, struct mp_bounds_object *h)
1100 {
1101     if (FIELD(path)) {
1102         mplib_push_path(L, h->path_p, MPLIB_PATH);
1103     } else {
1104         lua_pushnil(L);
1105     }
1106 }
1107
1108 static void mplib_start_clip(lua_State * L, struct mp_clip_object *h)
1109 {
1110     if (FIELD(path)) {
1111         mplib_push_path(L, h->path_p, MPLIB_PATH);
1112     } else {
1113         lua_pushnil(L);
1114     }
1115 }
1116
1117 static int mplib_gr_index(lua_State * L)
1118 {
1119     struct mp_graphic_object **hh = is_gr_object(L, 1);
1120     if (*hh) {
1121         struct mp_graphic_object *h = *hh;
1122
1123         if (mplib_is_S(type, 2)) {
1124             lua_rawgeti(L, LUA_REGISTRYINDEX, mplib_type_Ses[h->type]);
1125         } else {
1126             switch (h->type) {
1127             case mp_fill_code:
1128                 mplib_fill(L, (mp_fill_object *) h);
1129                 break;
1130             case mp_stroked_code:
1131                 mplib_stroked(L, (mp_stroked_object *) h);
1132                 break;
1133             case mp_text_code:
1134                 mplib_text(L, (mp_text_object *) h);
1135                 break;
1136             case mp_special_code:
1137                 mplib_special(L, (mp_special_object *) h);
1138                 break;
1139             case mp_start_clip_code:
1140                 mplib_start_clip(L, (mp_clip_object *) h);
1141                 break;
1142             case mp_start_bounds_code:
1143                 mplib_start_bounds(L, (mp_bounds_object *) h);
1144                 break;
1145             case mp_stop_clip_code:
1146             case mp_stop_bounds_code:
1147             default:
1148                 lua_pushnil(L);
1149             }
1150         }
1151     } else {
1152         lua_pushnil(L);
1153     }
1154     return 1;
1155 }
1156
1157
1158 static const struct luaL_reg mplib_meta[] = {
1159     {"__gc", mplib_collect},
1160     {"__tostring", mplib_tostring},
1161     {NULL, NULL}                /* sentinel */
1162 };
1163
1164 static const struct luaL_reg mplib_fig_meta[] = {
1165     {"__gc",         mplib_fig_collect},
1166     {"__tostring",   mplib_fig_tostring},
1167     {"objects",      mplib_fig_body},
1168     {"copy_objects", mplib_fig_copy_body},
1169     {"filename",     mplib_fig_filename},
1170     {"postscript",   mplib_fig_postscript},
1171     {"boundingbox",  mplib_fig_bb},
1172     {"width",        mplib_fig_width},
1173     {"height",       mplib_fig_height},
1174     {"depth",        mplib_fig_depth},
1175     {"italcorr",     mplib_fig_italcorr},
1176     {"charcode",     mplib_fig_charcode},
1177     {NULL, NULL}                /* sentinel */
1178 };
1179
1180 static const struct luaL_reg mplib_gr_meta[] = {
1181     {"__gc", mplib_gr_collect},
1182     {"__tostring", mplib_gr_tostring},
1183     {"__index", mplib_gr_index},
1184     {NULL, NULL}                /* sentinel */
1185 };
1186
1187 static const struct luaL_reg mplib_d[] = {
1188     {"execute", mplib_execute},
1189     {"finish", mplib_finish},
1190     {"char_width", mplib_charwidth},
1191     {"char_height", mplib_charheight},
1192     {"char_depth", mplib_chardepth},
1193     {"statistics", mplib_statistics},
1194     {NULL, NULL}                /* sentinel */
1195 };
1196
1197
1198 static const struct luaL_reg mplib_m[] = {
1199     {"new", mplib_new},
1200     {"fields", mplib_gr_fields},
1201     {"pen_info", mplib_gr_peninfo},
1202     {NULL, NULL}                /* sentinel */
1203 };
1204
1205
1206 int luaopen_mplib(lua_State * L)
1207 {
1208     mplib_init_Ses(L);
1209
1210     luaL_newmetatable(L, MPLIB_GR_METATABLE);
1211     lua_pushvalue(L, -1);       /* push metatable */
1212     lua_setfield(L, -2, "__index");     /* metatable.__index = metatable */
1213     luaL_register(L, NULL, mplib_gr_meta);      /* object meta methods */
1214     lua_pop(L, 1);
1215
1216     luaL_newmetatable(L, MPLIB_FIG_METATABLE);
1217     lua_pushvalue(L, -1);       /* push metatable */
1218     lua_setfield(L, -2, "__index");     /* metatable.__index = metatable */
1219     luaL_register(L, NULL, mplib_fig_meta);     /* figure meta methods */
1220     lua_pop(L, 1);
1221
1222     luaL_newmetatable(L, MPLIB_METATABLE);
1223     lua_pushvalue(L, -1);       /* push metatable */
1224     lua_setfield(L, -2, "__index");     /* metatable.__index = metatable */
1225     luaL_register(L, NULL, mplib_meta); /* meta methods */
1226     luaL_register(L, NULL, mplib_d);    /* dict methods */
1227     luaL_register(L, "mplib", mplib_m); /* module functions */
1228     return 1;
1229 }