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