support a mplib.version() function from 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 static int mplib_version(lua_State * L)
483 {
484   char *s = mp_metapost_version();
485   lua_pushstring(L, s);
486   free(s);
487   return 1;
488 }
489
490 static int mplib_statistics(lua_State * L)
491 {
492     MP *mp_ptr = is_mp(L, 1);
493     if (*mp_ptr != NULL) {
494         lua_newtable(L);
495         lua_pushnumber(L, (lua_Number)mp_memory_usage(*mp_ptr));
496         lua_setfield(L, -2, "main_memory");
497         lua_pushnumber(L, (lua_Number)mp_hash_usage(*mp_ptr));
498         lua_setfield(L, -2, "hash_size");
499         lua_pushnumber(L, (lua_Number)mp_param_usage(*mp_ptr));
500         lua_setfield(L, -2, "param_size");
501         lua_pushnumber(L, (lua_Number)mp_open_usage(*mp_ptr));
502         lua_setfield(L, -2, "max_in_open");
503     } else {
504         lua_pushnil(L);
505     }
506     return 1;
507 }
508
509
510 /* figure methods */
511
512 static int mplib_fig_collect(lua_State * L)
513 {
514     struct mp_edge_object **hh = is_fig(L, 1);
515     if (*hh != NULL) {
516         mp_gr_toss_objects(*hh);
517         *hh = NULL;
518     }
519     return 0;
520 }
521
522 static int mplib_fig_body(lua_State * L)
523 {
524     int i = 1;
525     struct mp_graphic_object **v;
526     struct mp_graphic_object *p;
527     struct mp_edge_object **hh = is_fig(L, 1);
528     lua_newtable(L);
529     p = (*hh)->body;
530     while (p != NULL) {
531         v = lua_newuserdata(L, sizeof(struct mp_graphic_object *));
532         *v = p;
533         luaL_getmetatable(L, MPLIB_GR_METATABLE);
534         lua_setmetatable(L, -2);
535         lua_rawseti(L, -2, i);
536         i++;
537         p = p->next;
538     }
539     (*hh)->body = NULL;         /* prevent double free */
540     return 1;
541 }
542
543 static int mplib_fig_copy_body(lua_State * L)
544 {
545     int i = 1;
546     struct mp_graphic_object **v;
547     struct mp_graphic_object *p;
548     struct mp_edge_object **hh = is_fig(L, 1);
549     lua_newtable(L);
550     p = (*hh)->body;
551     while (p != NULL) {
552         v = lua_newuserdata(L, sizeof(struct mp_graphic_object *));
553         *v = mp_gr_copy_object((*hh)->parent, p);
554         luaL_getmetatable(L, MPLIB_GR_METATABLE);
555         lua_setmetatable(L, -2);
556         lua_rawseti(L, -2, i);
557         i++;
558         p = p->next;
559     }
560     return 1;
561 }
562
563
564 static int mplib_fig_tostring(lua_State * L)
565 {
566     struct mp_edge_object **hh = is_fig(L, 1);
567     (void)lua_pushfstring(L, "<figure %p>", *hh);
568     return 1;
569 }
570
571 static int mplib_fig_postscript(lua_State * L)
572 {
573     mp_run_data *res;
574     struct mp_edge_object **hh = is_fig(L, 1);
575     int prologues = (int)luaL_optnumber(L, 2, (lua_Number)-1);
576     int procset = (int)luaL_optnumber(L, 3, (lua_Number)-1);
577     if (mp_ps_ship_out(*hh, prologues, procset) 
578         && (res = mp_rundata((*hh)->parent))
579         && (res->ps_out.size != 0)) {
580         lua_pushstring(L, res->ps_out.data);
581     } else {
582         lua_pushnil(L);
583     }
584     return 1;
585 }
586
587 static int mplib_fig_filename(lua_State * L)
588 {
589     struct mp_edge_object **hh = is_fig(L, 1);
590     if (*hh != NULL) {
591         char *s = (*hh)->filename;
592         lua_pushstring(L, s);
593     } else {
594         lua_pushnil(L);
595     }
596     return 1;
597 }
598
599 static int mplib_fig_width(lua_State * L)
600 {
601     struct mp_edge_object **hh = is_fig(L, 1);
602     if (*hh != NULL) {
603       lua_pushnumber(L, (double) (*hh)->width / 65536.0);
604     } else {
605       lua_pushnil(L);
606     }
607     return 1;
608 }
609
610 static int mplib_fig_height(lua_State * L)
611 {
612     struct mp_edge_object **hh = is_fig(L, 1);
613     if (*hh != NULL) {
614       lua_pushnumber(L, (double) (*hh)->height / 65536.0);
615     } else {
616       lua_pushnil(L);
617     }
618     return 1;
619 }
620
621 static int mplib_fig_depth(lua_State * L)
622 {
623     struct mp_edge_object **hh = is_fig(L, 1);
624     if (*hh != NULL) {
625       lua_pushnumber(L, (double) (*hh)->depth / 65536.0);
626     } else {
627       lua_pushnil(L);
628     }
629     return 1;
630 }
631
632 static int mplib_fig_italcorr(lua_State * L)
633 {
634     struct mp_edge_object **hh = is_fig(L, 1);
635     if (*hh != NULL) {
636       lua_pushnumber(L, (double) (*hh)->ital_corr / 65536.0);
637     } else {
638       lua_pushnil(L);
639     }
640     return 1;
641 }
642
643 static int mplib_fig_charcode(lua_State * L)
644 {
645     struct mp_edge_object **hh = is_fig(L, 1);
646     if (*hh != NULL) {
647       lua_pushnumber(L, (lua_Number)(*hh)->charcode);
648     } else {
649       lua_pushnil(L);
650     }
651     return 1;
652 }
653
654
655
656 static int mplib_fig_bb(lua_State * L)
657 {
658     struct mp_edge_object **hh = is_fig(L, 1);
659     lua_newtable(L);
660     lua_pushnumber(L, (double) (*hh)->minx / 65536.0);
661     lua_rawseti(L, -2, 1);
662     lua_pushnumber(L, (double) (*hh)->miny / 65536.0);
663     lua_rawseti(L, -2, 2);
664     lua_pushnumber(L, (double) (*hh)->maxx / 65536.0);
665     lua_rawseti(L, -2, 3);
666     lua_pushnumber(L, (double) (*hh)->maxy / 65536.0);
667     lua_rawseti(L, -2, 4);
668     return 1;
669 }
670
671 /* object methods */
672
673 static int mplib_gr_collect(lua_State * L)
674 {
675     struct mp_graphic_object **hh = is_gr_object(L, 1);
676     if (*hh != NULL) {
677         mp_gr_toss_object(*hh);
678         *hh = NULL;
679     }
680     return 0;
681 }
682
683 static int mplib_gr_tostring(lua_State * L)
684 {
685     struct mp_graphic_object **hh = is_gr_object(L, 1);
686     (void)lua_pushfstring(L, "<object %p>", *hh);
687     return 1;
688 }
689
690 #define pyth(a,b) (sqrt((a)*(a) + (b)*(b)))
691
692 #define aspect_bound   (10.0/65536.0)
693 #define aspect_default (1.0/65536.0)
694
695 static double eps  = 0.0001;
696
697 static double coord_range_x (mp_knot *h, double dz) {
698   double z;
699   double zlo = 0.0, zhi = 0.0;
700   mp_knot *f = h; 
701   while (h != NULL) {
702     z = (double)h->x_coord;
703     if (z < zlo) zlo = z; else if (z > zhi) zhi = z;
704     z = (double)h->right_x;
705     if (z < zlo) zlo = z; else if (z > zhi) zhi = z;
706     z = (double)h->left_x;
707     if (z < zlo) zlo = z; else if (z > zhi) zhi = z;
708     h = h->next;
709     if (h==f)
710       break;
711   }
712   return (zhi - zlo <= dz ? aspect_bound : aspect_default);
713 }
714
715 static double coord_range_y (mp_knot *h, double dz) {
716   double z;
717   double zlo = 0.0, zhi = 0.0;
718   mp_knot *f = h; 
719   while (h != NULL) {
720     z = (double)h->y_coord;
721     if (z < zlo) zlo = z; else if (z > zhi) zhi = z;
722     z = (double)h->right_y;
723     if (z < zlo) zlo = z; else if (z > zhi) zhi = z;
724     z = (double)h->left_y;
725     if (z < zlo) zlo = z; else if (z > zhi) zhi = z;
726     h = h->next;
727     if (h==f)
728       break;
729   }
730   return (zhi - zlo <= dz ? aspect_bound : aspect_default);
731 }
732
733
734 static int mplib_gr_peninfo(lua_State * L) {
735     double x_coord, y_coord, left_x, left_y, right_x, right_y;
736     double wx, wy;
737     double rx = 1.0, sx = 0.0, sy = 0.0, ry = 1.0, tx = 0.0, ty = 0.0;
738     double divider = 1.0;
739     double width = 1.0;
740     mp_knot *p = NULL, *path = NULL;
741     struct mp_graphic_object **hh = is_gr_object(L, -1);
742     if (!*hh) {
743       lua_pushnil(L);
744       return 1;
745     }
746     if ((*hh)->type == mp_fill_code) {
747       p    = ((mp_fill_object *)(*hh))->pen_p;
748       path = ((mp_fill_object *)(*hh))->path_p;
749     } else if ((*hh)->type == mp_stroked_code) {
750       p    = ((mp_stroked_object *)(*hh))->pen_p;
751       path = ((mp_stroked_object *)(*hh))->path_p;
752     }
753     if (p==NULL || path == NULL) {
754       lua_pushnil(L);
755       return 1;
756     }
757     x_coord = p->x_coord/65536.0;
758     y_coord = p->y_coord/65536.0;
759     left_x = p->left_x/65536.0;
760     left_y = p->left_y/65536.0;
761     right_x = p->right_x/65536.0;
762     right_y = p->right_y/65536.0;
763     if ((right_x == x_coord) && (left_y == y_coord)) {
764       wx = fabs(left_x  - x_coord);
765       wy = fabs(right_y - y_coord);
766     } else {
767       wx = pyth(left_x - x_coord, right_x - x_coord);
768       wy = pyth(left_y - y_coord, right_y - y_coord);
769     }
770     if ((wy/coord_range_x(path, wx)) >= (wx/coord_range_y(path, wy)))
771       width = wy;
772     else
773       width = wx;
774     tx = x_coord; 
775     ty = y_coord;
776     sx = left_x - tx; 
777     rx = left_y - ty; 
778     ry = right_x - tx; 
779     sy = right_y - ty;
780     if (width !=1.0) {
781       if (width == 0.0) {
782         sx = 1.0; sy = 1.0;
783       } else {
784         rx/=width; ry/=width; sx/=width; sy/=width;
785       }
786     }
787     if (fabs(sx) < eps) sx = eps;
788     if (fabs(sy) < eps) sy = eps;
789     divider = sx*sy - rx*ry;
790     lua_newtable(L);
791     lua_pushnumber(L,width); lua_setfield(L,-2,"width");
792     lua_pushnumber(L,rx); lua_setfield(L,-2,"rx");
793     lua_pushnumber(L,sx); lua_setfield(L,-2,"sx");
794     lua_pushnumber(L,sy); lua_setfield(L,-2,"sy");
795     lua_pushnumber(L,ry); lua_setfield(L,-2,"ry");
796     lua_pushnumber(L,tx); lua_setfield(L,-2,"tx");
797     lua_pushnumber(L,ty); lua_setfield(L,-2,"ty");
798     return 1;
799 }
800
801
802 static int mplib_gr_fields(lua_State * L)
803 {
804     const char **fields;
805     int i;
806     struct mp_graphic_object **hh = is_gr_object(L, 1);
807     if (*hh) {
808         switch ((*hh)->type) {
809         case mp_fill_code:
810             fields = fill_fields;
811             break;
812         case mp_stroked_code:
813             fields = stroked_fields;
814             break;
815         case mp_text_code:
816             fields = text_fields;
817             break;
818         case mp_special_code:
819             fields = special_fields;
820             break;
821         case mp_start_clip_code:
822             fields = start_clip_fields;
823             break;
824         case mp_start_bounds_code:
825             fields = start_bounds_fields;
826             break;
827         case mp_stop_clip_code:
828             fields = stop_clip_fields;
829             break;
830         case mp_stop_bounds_code:
831             fields = stop_bounds_fields;
832             break;
833         default:
834             fields = no_fields;
835         }
836         lua_newtable(L);
837         for (i = 0; fields[i] != NULL; i++) {
838             lua_pushstring(L, fields[i]);
839             lua_rawseti(L, -2, (i + 1));
840         }
841     } else {
842         lua_pushnil(L);
843     }
844     return 1;
845 }
846
847
848 #define mplib_push_number(L,x) lua_pushnumber(L,(lua_Number)(x)/65536.0)
849
850 #define MPLIB_PATH 0
851 #define MPLIB_PEN 1
852
853 static void mplib_push_path(lua_State * L, struct mp_knot *h, int is_pen)
854 {
855     struct mp_knot *p;          /* for scanning the path */
856     int i = 1;
857     p = h;
858     if (p != NULL) {
859         lua_newtable(L);
860         do {
861             lua_createtable(L, 0, 6);
862             if (!is_pen) {
863                 if (p->left_type != mp_explicit) {
864                     mplib_push_S(left_type);
865                     lua_pushstring(L, knot_type_enum[p->left_type]);
866                     lua_rawset(L, -3);
867                 }
868                 if (p->right_type != mp_explicit) {
869                     mplib_push_S(right_type);
870                     lua_pushstring(L, knot_type_enum[p->right_type]);
871                     lua_rawset(L, -3);
872                 }
873             }
874             mplib_push_S(x_coord);
875             mplib_push_number(L, p->x_coord);
876             lua_rawset(L, -3);
877             mplib_push_S(y_coord);
878             mplib_push_number(L, p->y_coord);
879             lua_rawset(L, -3);
880             mplib_push_S(left_x);
881             mplib_push_number(L, p->left_x);
882             lua_rawset(L, -3);
883             mplib_push_S(left_y);
884             mplib_push_number(L, p->left_y);
885             lua_rawset(L, -3);
886             mplib_push_S(right_x);
887             mplib_push_number(L, p->right_x);
888             lua_rawset(L, -3);
889             mplib_push_S(right_y);
890             mplib_push_number(L, p->right_y);
891             lua_rawset(L, -3);
892             lua_rawseti(L, -2, i);
893             i++;
894             if (p->right_type == mp_endpoint) {
895                 return;
896             }
897             p = p->next;
898         } while (p != h);
899     } else {
900         lua_pushnil(L);
901     }
902 }
903
904 /* this assumes that the top of the stack is a table 
905    or nil already in the case
906  */
907 static void mplib_push_pentype(lua_State * L, struct mp_knot *h)
908 {
909     struct mp_knot *p;          /* for scanning the path */
910     p = h;
911     if (p == NULL) {
912         /* do nothing */
913     } else if (p == p->next) {
914         mplib_push_S(type);
915         lua_pushstring(L, "elliptical");
916         lua_rawset(L, -3);
917     } else {
918     }
919 }
920
921 #define set_color_objects(pq)                           \
922   object_color_model = pq->color_model;           \
923   object_color_a = pq->color.a_val;              \
924   object_color_b = pq->color.b_val;              \
925   object_color_c = pq->color.c_val;              \
926   object_color_d = pq->color.d_val;
927
928
929 static void mplib_push_color(lua_State * L, struct mp_graphic_object *p)
930 {
931     int object_color_model;
932     int object_color_a, object_color_b, object_color_c, object_color_d;
933     if (p != NULL) {
934         if (p->type == mp_fill_code) {
935             mp_fill_object *h = (mp_fill_object *) p;
936             set_color_objects(h);
937         } else if (p->type == mp_stroked_code) {
938             mp_stroked_object *h = (mp_stroked_object *) p;
939             set_color_objects(h);
940         } else {
941             mp_text_object *h = (mp_text_object *) p;
942             set_color_objects(h);
943         }
944         lua_newtable(L);
945         if (object_color_model >= mp_grey_model) {
946             mplib_push_number(L, object_color_a);
947             lua_rawseti(L, -2, 1);
948             if (object_color_model >= mp_rgb_model) {
949                 mplib_push_number(L, object_color_b);
950                 lua_rawseti(L, -2, 2);
951                 mplib_push_number(L, object_color_c);
952                 lua_rawseti(L, -2, 3);
953                 if (object_color_model == mp_cmyk_model) {
954                     mplib_push_number(L, object_color_d);
955                     lua_rawseti(L, -2, 4);
956                 }
957             }
958         }
959     } else {
960         lua_pushnil(L);
961     }
962 }
963
964 /* the dash scale is not exported, the field has no external value */
965 static void mplib_push_dash(lua_State * L, struct mp_stroked_object *h)
966 {
967     mp_dash_object *d;
968     double ds;
969     if (h != NULL && h->dash_p != NULL) {
970         d = h->dash_p;
971         lua_newtable(L);
972         mplib_push_number(L, d->offset);
973         lua_setfield(L, -2, "offset");
974         if (d->array != NULL) {
975             int i = 0;
976             lua_newtable(L);
977             while (*(d->array + i) != -1) {
978                 ds = *(d->array + 1) / 65536.0;
979                 lua_pushnumber(L, ds);
980                 i++;
981                 lua_rawseti(L, -2, i);
982             }
983             lua_setfield(L, -2, "dashes");
984         }
985     } else {
986         lua_pushnil(L);
987     }
988 }
989
990 static void mplib_push_transform(lua_State * L, struct mp_text_object *h)
991 {
992     int i = 1;
993     if (h != NULL) {
994         lua_createtable(L, 6, 0);
995         mplib_push_number(L, h->tx);
996         lua_rawseti(L, -2, i);
997         i++;
998         mplib_push_number(L, h->ty);
999         lua_rawseti(L, -2, i);
1000         i++;
1001         mplib_push_number(L, h->txx);
1002         lua_rawseti(L, -2, i);
1003         i++;
1004         mplib_push_number(L, h->tyx);
1005         lua_rawseti(L, -2, i);
1006         i++;
1007         mplib_push_number(L, h->txy);
1008         lua_rawseti(L, -2, i);
1009         i++;
1010         mplib_push_number(L, h->tyy);
1011         lua_rawseti(L, -2, i);
1012         i++;
1013     } else {
1014         lua_pushnil(L);
1015     }
1016 }
1017
1018 #define FIELD(A) (mplib_is_S(A,2))
1019
1020 static void mplib_fill(lua_State * L, struct mp_fill_object *h)
1021 {
1022     if (FIELD(path)) {
1023         mplib_push_path(L, h->path_p, MPLIB_PATH);
1024     } else if (FIELD(htap)) {
1025         mplib_push_path(L, h->htap_p, MPLIB_PATH);
1026     } else if (FIELD(pen)) {
1027         mplib_push_path(L, h->pen_p, MPLIB_PEN);
1028         mplib_push_pentype(L, h->pen_p);
1029     } else if (FIELD(color)) {
1030         mplib_push_color(L, (mp_graphic_object *) h);
1031     } else if (FIELD(linejoin)) {
1032       lua_pushnumber(L, (lua_Number)h->ljoin);
1033     } else if (FIELD(miterlimit)) {
1034         mplib_push_number(L, h->miterlim);
1035     } else if (FIELD(prescript)) {
1036         lua_pushstring(L, h->pre_script);
1037     } else if (FIELD(postscript)) {
1038         lua_pushstring(L, h->post_script);
1039     } else {
1040         lua_pushnil(L);
1041     }
1042 }
1043
1044 static void mplib_stroked(lua_State * L, struct mp_stroked_object *h)
1045 {
1046     if (FIELD(path)) {
1047         mplib_push_path(L, h->path_p, MPLIB_PATH);
1048     } else if (FIELD(pen)) {
1049         mplib_push_path(L, h->pen_p, MPLIB_PEN);
1050         mplib_push_pentype(L, h->pen_p);
1051     } else if (FIELD(color)) {
1052         mplib_push_color(L, (mp_graphic_object *) h);
1053     } else if (FIELD(dash)) {
1054         mplib_push_dash(L, h);
1055     } else if (FIELD(linecap)) {
1056         lua_pushnumber(L, (lua_Number)h->lcap);
1057     } else if (FIELD(linejoin)) {
1058       lua_pushnumber(L, (lua_Number)h->ljoin);
1059     } else if (FIELD(miterlimit)) {
1060         mplib_push_number(L, h->miterlim);
1061     } else if (FIELD(prescript)) {
1062         lua_pushstring(L, h->pre_script);
1063     } else if (FIELD(postscript)) {
1064         lua_pushstring(L, h->post_script);
1065     } else {
1066         lua_pushnil(L);
1067     }
1068 }
1069
1070 static void mplib_text(lua_State * L, struct mp_text_object *h)
1071 {
1072     if (FIELD(text)) {
1073         lua_pushstring(L, h->text_p);
1074     } else if (FIELD(dsize)) {
1075         mplib_push_number(L, (h->font_dsize / 16));
1076     } else if (FIELD(font)) {
1077         lua_pushstring(L, h->font_name);
1078     } else if (FIELD(color)) {
1079         mplib_push_color(L, (mp_graphic_object *) h);
1080     } else if (FIELD(width)) {
1081         mplib_push_number(L, h->width);
1082     } else if (FIELD(height)) {
1083         mplib_push_number(L, h->height);
1084     } else if (FIELD(depth)) {
1085         mplib_push_number(L, h->depth);
1086     } else if (FIELD(transform)) {
1087         mplib_push_transform(L, h);
1088     } else if (FIELD(prescript)) {
1089         lua_pushstring(L, h->pre_script);
1090     } else if (FIELD(postscript)) {
1091         lua_pushstring(L, h->post_script);
1092     } else {
1093         lua_pushnil(L);
1094     }
1095 }
1096
1097 static void mplib_special(lua_State * L, struct mp_special_object *h)
1098 {
1099     if (FIELD(prescript)) {
1100         lua_pushstring(L, h->pre_script);
1101     } else {
1102         lua_pushnil(L);
1103     }
1104 }
1105
1106 static void mplib_start_bounds(lua_State * L, struct mp_bounds_object *h)
1107 {
1108     if (FIELD(path)) {
1109         mplib_push_path(L, h->path_p, MPLIB_PATH);
1110     } else {
1111         lua_pushnil(L);
1112     }
1113 }
1114
1115 static void mplib_start_clip(lua_State * L, struct mp_clip_object *h)
1116 {
1117     if (FIELD(path)) {
1118         mplib_push_path(L, h->path_p, MPLIB_PATH);
1119     } else {
1120         lua_pushnil(L);
1121     }
1122 }
1123
1124 static int mplib_gr_index(lua_State * L)
1125 {
1126     struct mp_graphic_object **hh = is_gr_object(L, 1);
1127     if (*hh) {
1128         struct mp_graphic_object *h = *hh;
1129
1130         if (mplib_is_S(type, 2)) {
1131             lua_rawgeti(L, LUA_REGISTRYINDEX, mplib_type_Ses[h->type]);
1132         } else {
1133             switch (h->type) {
1134             case mp_fill_code:
1135                 mplib_fill(L, (mp_fill_object *) h);
1136                 break;
1137             case mp_stroked_code:
1138                 mplib_stroked(L, (mp_stroked_object *) h);
1139                 break;
1140             case mp_text_code:
1141                 mplib_text(L, (mp_text_object *) h);
1142                 break;
1143             case mp_special_code:
1144                 mplib_special(L, (mp_special_object *) h);
1145                 break;
1146             case mp_start_clip_code:
1147                 mplib_start_clip(L, (mp_clip_object *) h);
1148                 break;
1149             case mp_start_bounds_code:
1150                 mplib_start_bounds(L, (mp_bounds_object *) h);
1151                 break;
1152             case mp_stop_clip_code:
1153             case mp_stop_bounds_code:
1154             default:
1155                 lua_pushnil(L);
1156             }
1157         }
1158     } else {
1159         lua_pushnil(L);
1160     }
1161     return 1;
1162 }
1163
1164
1165 static const struct luaL_reg mplib_meta[] = {
1166     {"__gc", mplib_collect},
1167     {"__tostring", mplib_tostring},
1168     {NULL, NULL}                /* sentinel */
1169 };
1170
1171 static const struct luaL_reg mplib_fig_meta[] = {
1172     {"__gc",         mplib_fig_collect},
1173     {"__tostring",   mplib_fig_tostring},
1174     {"objects",      mplib_fig_body},
1175     {"copy_objects", mplib_fig_copy_body},
1176     {"filename",     mplib_fig_filename},
1177     {"postscript",   mplib_fig_postscript},
1178     {"boundingbox",  mplib_fig_bb},
1179     {"width",        mplib_fig_width},
1180     {"height",       mplib_fig_height},
1181     {"depth",        mplib_fig_depth},
1182     {"italcorr",     mplib_fig_italcorr},
1183     {"charcode",     mplib_fig_charcode},
1184     {NULL, NULL}                /* sentinel */
1185 };
1186
1187 static const struct luaL_reg mplib_gr_meta[] = {
1188     {"__gc", mplib_gr_collect},
1189     {"__tostring", mplib_gr_tostring},
1190     {"__index", mplib_gr_index},
1191     {NULL, NULL}                /* sentinel */
1192 };
1193
1194 static const struct luaL_reg mplib_d[] = {
1195     {"execute", mplib_execute},
1196     {"finish", mplib_finish},
1197     {"char_width", mplib_charwidth},
1198     {"char_height", mplib_charheight},
1199     {"char_depth", mplib_chardepth},
1200     {"statistics", mplib_statistics},
1201     {NULL, NULL}                /* sentinel */
1202 };
1203
1204
1205 static const struct luaL_reg mplib_m[] = {
1206     {"new", mplib_new},
1207     {"version",    mplib_version},
1208     {"fields", mplib_gr_fields},
1209     {"pen_info", mplib_gr_peninfo},
1210     {NULL, NULL}                /* sentinel */
1211 };
1212
1213
1214 int luaopen_mplib(lua_State * L)
1215 {
1216     mplib_init_Ses(L);
1217
1218     luaL_newmetatable(L, MPLIB_GR_METATABLE);
1219     lua_pushvalue(L, -1);       /* push metatable */
1220     lua_setfield(L, -2, "__index");     /* metatable.__index = metatable */
1221     luaL_register(L, NULL, mplib_gr_meta);      /* object meta methods */
1222     lua_pop(L, 1);
1223
1224     luaL_newmetatable(L, MPLIB_FIG_METATABLE);
1225     lua_pushvalue(L, -1);       /* push metatable */
1226     lua_setfield(L, -2, "__index");     /* metatable.__index = metatable */
1227     luaL_register(L, NULL, mplib_fig_meta);     /* figure meta methods */
1228     lua_pop(L, 1);
1229
1230     luaL_newmetatable(L, MPLIB_METATABLE);
1231     lua_pushvalue(L, -1);       /* push metatable */
1232     lua_setfield(L, -2, "__index");     /* metatable.__index = metatable */
1233     luaL_register(L, NULL, mplib_meta); /* meta methods */
1234     luaL_register(L, NULL, mplib_d);    /* dict methods */
1235     luaL_register(L, "mplib", mplib_m); /* module functions */
1236     return 1;
1237 }