summaryrefslogtreecommitdiff
path: root/source/luametatex/source/lua/lmtmplib.c
diff options
context:
space:
mode:
Diffstat (limited to 'source/luametatex/source/lua/lmtmplib.c')
-rw-r--r--source/luametatex/source/lua/lmtmplib.c3137
1 files changed, 3137 insertions, 0 deletions
diff --git a/source/luametatex/source/lua/lmtmplib.c b/source/luametatex/source/lua/lmtmplib.c
new file mode 100644
index 000000000..74d684c3f
--- /dev/null
+++ b/source/luametatex/source/lua/lmtmplib.c
@@ -0,0 +1,3137 @@
+/*
+ See license.txt in the root of this project.
+*/
+
+/*tex
+
+ This is an adapted version of \MPLIB\ for ConTeXt LMTX. Interfaces might change as experiments
+ demand or indicate this. It's meant for usage in \LUAMETATEX, which is an engine geared at
+ \CONTEXT, and which, although useable in production, will in principle be experimental and
+ moving for a while, depending on needs, experiments and mood.
+
+ In \LUATEX\, at some point \MPLIB\ got an interface to \LUA\ and that greatly enhanced the
+ posibilities. In \LUAMETATEX\ this interface was further improved and in addition scanners
+ got added. So in the meantime we have a rather complete and tight integration of \TEX, \LUA,
+ and \METAPOST\ and in \CONTEXT\ we use that integration as much as possible.
+
+ An important difference with \LUATEX\ is that we use an upgraded \MPLIB. The \POSTSCRIPT\
+ backend has been dropped and there is no longer font and file related code because that
+ all goes via \LUA\ now. The binary frontend has been dropped to so we now only have scaled,
+ double and decimal.
+
+*/
+
+# include "mpconfig.h"
+
+# include "mp.h"
+
+/*tex
+ We can use common definitions but can only load this file after we load the \METAPOST\ stuff,
+ which defines a whole bunch of macros that can clash. Using the next one is actually a good
+ check for such conflicts.
+*/
+
+# include "luametatex.h"
+
+/*tex
+
+ Here are some enumeration arrays to map MPlib enums to \LUA\ strings. If needed we can also
+ predefine keys here, as we do with nodes. Some tables relate to the scanners.
+
+*/
+
+# define MPLIB_PATH 0
+# define MPLIB_PEN 1
+# define MPLIB_PATH_SIZE 8
+
+static const char *mplib_math_options[] = {
+ "scaled",
+ "double",
+ "binary", /* not available in luatex */
+ "decimal",
+ NULL
+};
+
+static const char *mplib_interaction_options[] = {
+ "unknown",
+ "batch",
+ "nonstop",
+ "scroll",
+ "errorstop",
+ "silent",
+ NULL
+};
+
+static const char *mplib_filetype_names[] = {
+ "terminal", /* mp_filetype_terminal */
+ "mp", /* mp_filetype_program */
+ "data", /* mp_filetype_text */
+ NULL
+};
+
+/*
+static const char *knot_type_enum[] = {
+ "endpoint", "explicit", "given", "curl", "open", "end_cycle"
+};
+*/
+
+static const char *mplib_fill_fields[] = {
+ "type",
+ "path", "htap",
+ "pen", "color",
+ "linejoin", "miterlimit",
+ "prescript", "postscript",
+ "stacking",
+ NULL
+};
+
+static const char *mplib_stroked_fields[] = {
+ "type",
+ "path",
+ "pen", "color",
+ "linejoin", "miterlimit", "linecap",
+ "dash",
+ "prescript", "postscript",
+ "stacking",
+ NULL
+};
+
+static const char *mplib_start_clip_fields[] = {
+ "type",
+ "path",
+ "prescript", "postscript",
+ "stacking",
+ NULL
+};
+
+static const char *mplib_start_group_fields[] = {
+ "type",
+ "path",
+ "prescript", "postscript",
+ "stacking",
+ NULL
+};
+
+static const char *mplib_start_bounds_fields[] = {
+ "type",
+ "path",
+ "prescript", "postscript",
+ "stacking",
+ NULL
+};
+
+static const char *mplib_stop_clip_fields[] = {
+ "type",
+ "stacking",
+ NULL
+};
+
+static const char *mplib_stop_group_fields[] = {
+ "type",
+ "stacking",
+ NULL
+};
+
+static const char *mplib_stop_bounds_fields[] = {
+ "type",
+ "stacking",
+ NULL
+};
+
+static const char *mplib_no_fields[] = {
+ NULL
+};
+
+static const char *mplib_codes[] = {
+ "undefined",
+ "btex", /* mp_btex_command */ /* btex verbatimtex */
+ "etex", /* mp_etex_command */ /* etex */
+ "if", /* mp_if_test_command */ /* if */
+ "fiorelse", /* mp_fi_or_else_command */ /* elseif else fi */
+ "input", /* mp_input_command */ /* input endinput */
+ "iteration", /* mp_iteration_command */ /* for forsuffixes forever endfor */
+ "repeatloop", /* mp_repeat_loop_command */ /* used in repeat loop (endfor) */
+ "exittest", /* mp_exit_test_command */ /* exitif */
+ "relax", /* mp_relax_command */ /* \\ */
+ "scantokens", /* mp_scan_tokens_command */ /* scantokens */
+ "runscript", /* mp_runscript_command */ /* runscript */
+ "maketext", /* mp_maketext_command */ /* maketext */
+ "expandafter", /* mp_expand_after_command */ /* expandafter */
+ "definedmacro", /* mp_defined_macro_command */ /* */
+ "save", /* mp_save_command */ /* save */
+ "interim", /* mp_interim_command */ /* interim */
+ "let", /* mp_let_command */ /* let */
+ "newinternal", /* mp_new_internal_command */ /* newinternal */
+ "macrodef", /* mp_macro_def_command */ /* def vardef (etc) */
+ "shipout", /* mp_ship_out_command */ /* shipout */
+ "addto", /* mp_add_to_command */ /* addto */
+ "setbounds", /* mp_bounds_command */ /* setbounds clip group */
+ "protection", /* mp_protection_command */
+ "property", /* mp_property_command */
+ "show", /* mp_show_command */ /* show showvariable (etc) */
+ "mode", /* mp_mode_command */ /* batchmode (etc) */
+ "onlyset", /* mp_only_set_command */ /* randomseed, maxknotpool */
+ "message", /* mp_message_command */ /* message errmessage */
+ "everyjob", /* mp_every_job_command */ /* everyjob */
+ "delimiters", /* mp_delimiters_command */ /* delimiters */
+ "write", /* mp_write_command */ /* write */
+ "typename", /* mp_type_name_command */ /* (declare) numeric pair */
+ "leftdelimiter", /* mp_left_delimiter_command */ /* the left delimiter of a matching pair */
+ "begingroup", /* mp_begin_group_command */ /* begingroup */
+ "nullary", /* mp_nullary_command */ /* operator without arguments: normaldeviate (etc) */
+ "unary", /* mp_unary_command */ /* operator with one argument: sqrt (etc) */
+ "str", /* mp_str_command */ /* convert a suffix to a string: str */
+ "void", /* mp_void_command */ /* convert a suffix to a boolean: void */
+ "cycle", /* mp_cycle_command */ /* cycle */
+ "ofbinary", /* mp_of_binary_command */ /* binary operation taking "of", like "point of" */
+ "capsule", /* mp_capsule_command */ /* */
+ "string", /* mp_string_command */ /* */
+ "internal", /* mp_internal_quantity_command */ /* */
+ "tag", /* mp_tag_command */ /* a symbolic token without a primitive meaning */
+ "numeric", /* mp_numeric_command */ /* numeric constant */
+ "plusorminus", /* mp_plus_or_minus_command */ /* + - */
+ "secondarydef", /* mp_secondary_def_command */ /* secondarydef */
+ "tertiarybinary", /* mp_tertiary_binary_command */ /* an operator at the tertiary level: ++ (etc) */
+ "leftbrace", /* mp_left_brace_command */ /* { */
+ "pathjoin", /* mp_path_join_command */ /* .. */
+ "ampersand", /* mp_ampersand_command */ /* & */
+ "tertiarydef", /* mp_tertiary_def_command */ /* tertiarydef */
+ "primarybinary", /* mp_primary_binary_command */ /* < (etc) */
+ "equals", /* mp_equals_command */ /* = */
+ "and", /* mp_and_command */ /* and */
+ "primarydef", /* mp_primary_def_command */ /* primarydef */
+ "slash", /* mp_slash_command */ /* / */
+ "secondarybinary", /* mp_secondary_binary_command */ /* an operator at the binary level: shifted (etc) */
+ "parametertype", /* mp_parameter_commmand */ /* primary expr suffix (etc) */
+ "controls", /* mp_controls_command */ /* controls */
+ "tension", /* mp_tension_command */ /* tension */
+ "atleast", /* mp_at_least_command */ /* atleast */
+ "curl", /* mp_curl_command */ /* curl */
+ "macrospecial", /* mp_macro_special_command */ /* quote, #@ (etc) */
+ "rightdelimiter", /* mp_right_delimiter_command */ /* the right delimiter of a matching pair */
+ "leftbracket", /* mp_left_bracket_command */ /* [ */
+ "rightbracket", /* mp_right_bracket_command */ /* ] */
+ "rightbrace", /* mp_right_brace_command */ /* } */
+ "with", /* mp_with_option_command */ /* withpen (etc) */
+ "thingstoadd", /* mp_thing_to_add_command */ /* addto contour doublepath also */
+ "of", /* mp_of_command */ /* of */
+ "to", /* mp_to_command */ /* to */
+ "step", /* mp_step_command */ /* step */
+ "until", /* mp_until_command */ /* until */
+ "within", /* mp_within_command */ /* within */
+ "assignment", /* mp_assignment_command */ /* := */
+ "colon", /* mp_colon_command */ /* : */
+ "comma", /* mp_comma_command */ /* , */
+ "semicolon", /* mp_semicolon_command */ /* ; */
+ "endgroup", /* mp_end_group_command */ /* endgroup */
+ "stop", /* mp_stop_command */ /* end dump */
+ // "outertag", /* mp_outer_tag_command */ /* protection code added to command code */
+ "undefinedcs", /* mp_undefined_cs_command */ /* protection code added to command code */
+ NULL
+};
+
+static const char *mplib_states[] = {
+ "normal",
+ "skipping",
+ "flushing",
+ "absorbing",
+ "var_defining",
+ "op_defining",
+ "loop_defining",
+ NULL
+};
+
+static const char *mplib_types[] = {
+ "undefined", /* mp_undefined_type */
+ "vacuous", /* mp_vacuous_type */
+ "boolean", /* mp_boolean_type */
+ "unknownboolean", /* mp_unknown_boolean_type */
+ "string", /* mp_string_type */
+ "unknownstring", /* mp_unknown_string_type */
+ "pen", /* mp_pen_type */
+ "unknownpen", /* mp_unknown_pen_type */
+ "nep", /* mp_nep_type */
+ "unknownnep", /* mp_unknown_nep_type */
+ "path", /* mp_path_type */
+ "unknownpath", /* mp_unknown_path_type */
+ "picture", /* mp_picture_type */
+ "unknownpicture", /* mp_unknown_picture_type */
+ "transform", /* mp_transform_type */
+ "color", /* mp_color_type */
+ "cmykcolor", /* mp_cmykcolor_type */
+ "pair", /* mp_pair_type */
+ // "script", /* */
+ "numeric", /* mp_numeric_type */
+ "known", /* mp_known_type */
+ "dependent", /* mp_dependent_type */
+ "protodependent", /* mp_proto_dependent_type */
+ "independent", /* mp_independent_type */
+ "tokenlist", /* mp_token_list_type */
+ "structured", /* mp_structured_type */
+ "unsuffixedmacro", /* mp_unsuffixed_macro_type */
+ "suffixedmacro", /* mp_suffixed_macro_type */
+ NULL
+};
+
+static const char *mplib_colormodels[] = {
+ "no",
+ "grey",
+ "rgb",
+ "cmyk",
+ NULL
+};
+
+/*tex Some statistics. */
+
+typedef struct mplib_state_info {
+ int file_callbacks;
+ int text_callbacks;
+ int script_callbacks;
+ int log_callbacks;
+ int overload_callbacks;
+ int error_callbacks;
+ int warning_callbacks;
+} mplib_state_info;
+
+static mplib_state_info mplib_state = {
+ .file_callbacks = 0,
+ .text_callbacks = 0,
+ .script_callbacks = 0,
+ .log_callbacks = 0,
+ .overload_callbacks = 0,
+ .error_callbacks = 0,
+ .warning_callbacks = 0,
+};
+
+/*tex
+
+ We need a few metatable identifiers in order to access the metatables for the main object and result
+ userdata. The following code is now replaced by the method that uses keys.
+
+*/
+
+/*
+# define MP_METATABLE_INSTANCE "mp.instance"
+# define MP_METATABLE_FIGURE "mp.figure"
+# define MP_METATABLE_OBJECT "mp.object"
+*/
+
+/*tex
+ This is used for heuristics wrt curves or lines. The default value is rather small
+ and often leads to curved rectangles.
+
+ \starttabulate[||||]
+ \NC 131/65536.0 \NC 0.0019989013671875 \NC default \NC \NR
+ \NC 0.001 * 0x7FFF/0x4000 \NC 0.0019999389648438 \NC kind of the default \NC \NR
+ \NC 32/16000.0 \NC 0.002 \NC somewhat cleaner \NC \NR
+ \NC 10/ 2000.0 \NC 0.005 \NC often good enough \NC \NR
+ \stoptabulate
+
+*/
+
+# define default_bend_tolerance 131/65536.0
+# define default_move_tolerance 131/65536.0
+
+typedef enum mp_variables {
+ mp_bend_tolerance = 1,
+ mp_move_tolerance = 2,
+} mp_variables;
+
+static lua_Number mplib_aux_get_bend_tolerance(lua_State *L, int slot)
+{
+ lua_Number tolerance;
+ lua_getiuservalue(L, slot, mp_bend_tolerance);
+ tolerance = lua_tonumber(L, -1);
+ lua_pop(L, 1);
+ return tolerance;
+}
+
+static lua_Number mplib_aux_get_move_tolerance(lua_State *L, int slot)
+{
+ lua_Number tolerance;
+ lua_getiuservalue(L, slot, mp_move_tolerance);
+ tolerance = lua_tonumber(L, -1);
+ lua_pop(L, 1);
+ return tolerance;
+}
+
+static void mplib_aux_set_bend_tolerance(lua_State *L, lua_Number tolerance)
+{
+ lua_pushnumber(L, tolerance);
+ lua_setiuservalue(L, -2, mp_bend_tolerance);
+}
+
+static void mplib_aux_set_move_tolerance(lua_State *L, lua_Number tolerance)
+{
+ lua_pushnumber(L, tolerance);
+ lua_setiuservalue(L, -2, mp_move_tolerance);
+}
+
+inline static char *lmt_string_from_index(lua_State *L, int n)
+{
+ size_t l;
+ const char *x = lua_tolstring(L, n, &l);
+ // return (x && l > 0) ? lmt_generic_strdup(x) : NULL;
+ return (x && l > 0) ? lmt_memory_strdup(x) : NULL;
+}
+
+inline static char *lmt_lstring_from_index(lua_State *L, int n, size_t *l)
+{
+ const char *x = lua_tolstring(L, n, l);
+ // return (x && l > 0) ? lmt_generic_strdup(x) : NULL;
+ return (x && l > 0) ? lmt_memory_strdup(x) : NULL;
+}
+
+static void mplib_aux_invalid_object_warning(const char * detail)
+{
+ tex_formatted_warning("mp lib","lua <mp %s> expected", detail);
+}
+
+static void mplib_aux_invalid_object_error(const char * detail)
+{
+ tex_formatted_error("mp lib","lua <mp %s> expected", detail);
+}
+
+inline static MP *mplib_aux_is_mpud(lua_State *L, int n)
+{
+ MP *p = (MP *) lua_touserdata(L, n);
+ if (p && lua_getmetatable(L, n)) {
+ lua_get_metatablelua(mplib_instance);
+ if (! lua_rawequal(L, -1, -2)) {
+ p = NULL;
+ }
+ lua_pop(L, 2);
+ if (p) {
+ return p;
+ }
+ }
+ mplib_aux_invalid_object_error("instance");
+ return NULL;
+}
+
+inline static MP mplib_aux_is_mp(lua_State *L, int n)
+{
+ MP *p = (MP *) lua_touserdata(L, n);
+ if (p && lua_getmetatable(L, n)) {
+ lua_get_metatablelua(mplib_instance);
+ if (! lua_rawequal(L, -1, -2)) {
+ p = NULL;
+ }
+ lua_pop(L, 2);
+ if (p) {
+ return *p;
+ }
+ }
+ mplib_aux_invalid_object_error("instance");
+ return NULL;
+}
+
+inline static mp_edge_object **mplib_aux_is_figure(lua_State *L, int n)
+{
+ mp_edge_object **p = (mp_edge_object **) lua_touserdata(L, n);
+ if (p && lua_getmetatable(L, n)) {
+ lua_get_metatablelua(mplib_figure);
+ if (! lua_rawequal(L, -1, -2)) {
+ p = NULL;
+ }
+ lua_pop(L, 2);
+ if (p) {
+ return p;
+ }
+ }
+ mplib_aux_invalid_object_warning("figure");
+ return NULL;
+}
+
+inline static mp_graphic_object **mplib_aux_is_gr_object(lua_State *L, int n)
+{
+ mp_graphic_object **p = (mp_graphic_object **) lua_touserdata(L, n);
+ if (p && lua_getmetatable(L, n)) {
+ lua_get_metatablelua(mplib_object);
+ if (! lua_rawequal(L, -1, -2)) {
+ p = NULL;
+ }
+ lua_pop(L, 2);
+ if (p) {
+ return p;
+ }
+ }
+ mplib_aux_invalid_object_warning("object");
+ return NULL;
+}
+
+/*tex In the next array entry 0 is not used */
+
+static int mplib_values_type[mp_stop_bounds_code + 1] = { 0 };
+static int mplib_values_knot[6] = { 0 };
+
+static void mplib_aux_initialize_lua(lua_State *L)
+{
+ (void) L;
+ mplib_values_type[mp_fill_code] = lua_key_index(fill);
+ mplib_values_type[mp_stroked_code] = lua_key_index(outline);
+ mplib_values_type[mp_start_clip_code] = lua_key_index(start_clip);
+ mplib_values_type[mp_start_group_code] = lua_key_index(start_group);
+ mplib_values_type[mp_start_bounds_code] = lua_key_index(start_bounds);
+ mplib_values_type[mp_stop_clip_code] = lua_key_index(stop_clip);
+ mplib_values_type[mp_stop_group_code] = lua_key_index(stop_group);
+ mplib_values_type[mp_stop_bounds_code] = lua_key_index(stop_bounds);
+
+ mplib_values_knot[mp_endpoint_knot] = lua_key_index(endpoint);
+ mplib_values_knot[mp_explicit_knot] = lua_key_index(explicit);
+ mplib_values_knot[mp_given_knot] = lua_key_index(given);
+ mplib_values_knot[mp_curl_knot] = lua_key_index(curl);
+ mplib_values_knot[mp_open_knot] = lua_key_index(open);
+ mplib_values_knot[mp_end_cycle_knot] = lua_key_index(end_cycle);
+}
+
+static void mplib_aux_push_pentype(lua_State *L, mp_gr_knot h)
+{
+ if (h && h == h->next) {
+ lua_push_value_at_key(L, type, elliptical);
+ }
+}
+
+static int mplib_set_tolerance(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ mplib_aux_set_bend_tolerance(L, luaL_optnumber(L, 2, default_bend_tolerance));
+ mplib_aux_set_move_tolerance(L, luaL_optnumber(L, 3, default_move_tolerance));
+ }
+ return 0;
+}
+
+static int mplib_get_tolerance(lua_State *L)
+{
+
+ if (lua_type(L, 1) == LUA_TUSERDATA) {
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ lua_pushnumber(L, mplib_aux_get_bend_tolerance(L, 1));
+ lua_pushnumber(L, mplib_aux_get_move_tolerance(L, 1));
+ return 2;
+ } else {
+ return 0;
+ }
+ } else {
+ lua_pushnumber(L, default_bend_tolerance);
+ lua_pushnumber(L, default_move_tolerance);
+ return 2;
+ }
+}
+
+/*tex
+
+ We start by defining the needed callback routines for the library. We could store them per
+ instance but it has no advantages so that will be done when we feel the need.
+
+*/
+
+static int mplib_aux_register_function(lua_State *L, int old_id)
+{
+ if (! (lua_isfunction(L, -1) || lua_isnil(L, -1))) {
+ return 0;
+ } else {
+ lua_pushvalue(L, -1);
+ if (old_id) {
+ luaL_unref(L, LUA_REGISTRYINDEX, old_id);
+ }
+ return luaL_ref(L, LUA_REGISTRYINDEX); /*tex |new_id| */
+ }
+}
+
+static int mplib_aux_find_file_function(lua_State *L, MP_options *options)
+{
+ options->find_file_id = mplib_aux_register_function(L, options->find_file_id);
+ return (! options->find_file_id);
+}
+
+static int mplib_aux_run_script_function(lua_State *L, MP_options *options)
+{
+ options->run_script_id = mplib_aux_register_function(L, options->run_script_id);
+ return (! options->run_script_id);
+}
+
+static int mplib_aux_run_internal_function(lua_State *L, MP_options *options)
+{
+ options->run_internal_id = mplib_aux_register_function(L, options->run_internal_id);
+ return (! options->run_internal_id);
+}
+
+static int mplib_aux_make_text_function(lua_State *L, MP_options *options)
+{
+ options->make_text_id = mplib_aux_register_function(L, options->make_text_id);
+ return (! options->make_text_id);
+}
+
+static int mplib_aux_run_logger_function(lua_State *L, MP_options *options)
+{
+ options->run_logger_id = mplib_aux_register_function(L, options->run_logger_id);
+ return (! options->run_logger_id);
+}
+
+static int mplib_aux_run_overload_function(lua_State *L, MP_options *options)
+{
+ options->run_overload_id = mplib_aux_register_function(L, options->run_overload_id);
+ return (! options->run_overload_id);
+}
+
+static int mplib_aux_run_error_function(lua_State *L, MP_options *options)
+{
+ options->run_error_id = mplib_aux_register_function(L, options->run_error_id);
+ return (! options->run_error_id);
+}
+
+static int mplib_aux_open_file_function(lua_State *L, MP_options *options)
+{
+ options->open_file_id = mplib_aux_register_function(L, options->open_file_id);
+ return (! options->open_file_id);
+}
+
+static char *mplib_aux_find_file(MP mp, const char *fname, const char *fmode, int ftype)
+{
+ if (mp->find_file_id) {
+ lua_State *L = (lua_State *) mp_userdata(mp);
+ int stacktop = lua_gettop(L);
+ char *s = NULL;
+ lua_rawgeti(L, LUA_REGISTRYINDEX, mp->find_file_id);
+ lua_pushstring(L, fname);
+ lua_pushstring(L, fmode);
+ if (ftype > mp_filetype_text) {
+ lua_pushinteger(L, (lua_Integer) ftype - mp_filetype_text);
+ } else {
+ lua_pushstring(L, mplib_filetype_names[ftype]);
+ }
+ ++mplib_state.file_callbacks;
+ if (lua_pcall(L, 3, 1, 0)) {
+ tex_formatted_warning("mplib", "find file: %s", lua_tostring(L, -1));
+ } else {
+ s = lmt_string_from_index(L, -1);
+ }
+ lua_settop(L, stacktop);
+ return s;
+ } else if (fmode[0] != 'r' || (! access(fname, R_OK)) || ftype) {
+ // return lmt_generic_strdup(fname);
+ return lmt_memory_strdup(fname);
+ }
+ return NULL;
+}
+
+/*
+
+ In retrospect we could have has a more granular approach: a flag that tells that the result is
+ a string to be scanned. Maybe I'll that anyway but it definitely means an adaption of the low
+ level interface we have in MetaFun. Also, the interface would be a bit ugly because then we
+ would like to handle values as in the 'whatever' function which in turn means a flag indicating
+ that a string is a string and not something to be scanned as tokens, and being compatible then
+ means that this flag comes after the string which kind of conflicts with multiple (number)
+ arguments ... in the end we gain too little to accept such an ugly mess as option. So, we stick
+ to:
+
+ -- nil : nothing is injected and no scanning will happen
+ -- string : passed on in order to be scanned
+ -- table : passed on concatenated in order to be scanned
+ -- number : injected as numeric (so no scanning later on)
+ -- boolean : injected as boolean (so no scanning later on)
+
+ and dealing with other datatypes is delegated to the injectors.
+
+*/
+
+static int mplib_aux_with_path(lua_State *L, MP mp, int index, int what, int multiple);
+
+static void mplib_aux_inject_whatever(lua_State *L, MP mp, int index)
+{
+ switch (lua_type(L, index)) {
+ case LUA_TBOOLEAN:
+ mp_push_boolean_value(mp, lua_toboolean(L, index));
+ break;
+ case LUA_TNUMBER:
+ mp_push_numeric_value(mp, lua_tonumber(L, index));
+ break;
+ case LUA_TSTRING:
+ {
+ size_t l;
+ const char *s = lua_tolstring(L, index, &l);
+ mp_push_string_value(mp, s, (int) l);
+ break;
+ }
+ case LUA_TTABLE:
+ {
+ if (lua_rawgeti(L, index, 1) == LUA_TTABLE) {
+ /* table of tables */
+ lua_pop(L, 1);
+ mplib_aux_with_path(L, mp, index, 1, 0);
+ } else {
+ lua_pop(L, 1);
+ switch (lua_rawlen(L, index)) {
+ case 2 :
+ mp_push_pair_value(mp,
+ lmt_number_from_table(L, index, 1, 0.0),
+ lmt_number_from_table(L, index, 2, 0.0)
+ );
+ break;
+ case 3 :
+ mp_push_color_value(mp,
+ lmt_number_from_table(L, index, 1, 0.0),
+ lmt_number_from_table(L, index, 2, 0.0),
+ lmt_number_from_table(L, index, 3, 0.0)
+ );
+ break;
+ case 4 :
+ mp_push_cmykcolor_value(mp,
+ lmt_number_from_table(L, index, 1, 0.0),
+ lmt_number_from_table(L, index, 2, 0.0),
+ lmt_number_from_table(L, index, 3, 0.0),
+ lmt_number_from_table(L, index, 4, 0.0)
+ );
+ break;
+ case 6 :
+ mp_push_transform_value(mp,
+ lmt_number_from_table(L, index, 1, 0.0),
+ lmt_number_from_table(L, index, 2, 0.0),
+ lmt_number_from_table(L, index, 3, 0.0),
+ lmt_number_from_table(L, index, 4, 0.0),
+ lmt_number_from_table(L, index, 5, 0.0),
+ lmt_number_from_table(L, index, 6, 0.0)
+ );
+ break;
+ }
+ }
+ break;
+ }
+ }
+}
+
+static char *mplib_aux_return_whatever(lua_State *L, MP mp, int index)
+{
+ switch (lua_type(L, index)) {
+ case LUA_TBOOLEAN:
+ mp_push_boolean_value(mp, lua_toboolean(L, index));
+ break;
+ case LUA_TNUMBER:
+ mp_push_numeric_value(mp, lua_tonumber(L, index));
+ break;
+ /* A string is passed to scantokens. */
+ case LUA_TSTRING:
+ return lmt_string_from_index(L, index);
+ /*tex A table is concatenated and passed to scantokens. */
+ case LUA_TTABLE:
+ {
+ luaL_Buffer b;
+ lua_Integer n = (lua_Integer) lua_rawlen(L, index);
+ luaL_buffinit(L, &b);
+ for (lua_Integer i = 1; i <= n; i++) {
+ lua_rawgeti(L, index, i);
+ luaL_addvalue(&b);
+ lua_pop(L, 1);
+ }
+ luaL_pushresult(&b);
+ return lmt_string_from_index(L, -1);
+ }
+ }
+ return NULL;
+}
+
+static char *mplib_aux_run_script(MP mp, const char *str, size_t len, int n)
+{
+ if (mp->run_script_id) {
+ lua_State *L = (lua_State *) mp_userdata(mp);
+ int stacktop = lua_gettop(L);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, mp->run_script_id);
+ if (str) {
+ lua_pushlstring(L, str, len);
+ } else if (n > 0) {
+ lua_pushinteger(L, n);
+ } else {
+ lua_pushnil(L);
+ }
+ ++mplib_state.script_callbacks;
+ if (lua_pcall(L, 1, 2, 0)) {
+ tex_formatted_warning("mplib", "run script: %s", lua_tostring(L, -1));
+ } else if (lua_toboolean(L, -1)) {
+ /* value boolean */
+ mplib_aux_inject_whatever(L, mp, -2);
+ lua_settop(L, stacktop);
+ return NULL;
+ } else {
+ /* value nil */
+ char *s = mplib_aux_return_whatever(L, mp, -2);
+ lua_settop(L, stacktop);
+ return s;
+ }
+ }
+ return NULL;
+}
+
+void mplib_aux_run_internal(MP mp, int action, int n, int type, const char *name)
+{
+ if (mp->run_internal_id) {
+ lua_State *L = (lua_State *) mp_userdata(mp);
+ ++mplib_state.script_callbacks; /* maybe a special counter */
+ lua_rawgeti(L, LUA_REGISTRYINDEX, mp->run_internal_id);
+ lua_pushinteger(L, action); /* 0=initialize, 1=save, 2=restore */
+ lua_pushinteger(L, n);
+ if (name) {
+ lua_pushinteger(L, type);
+ lua_pushstring(L, name);
+ if (! lua_pcall(L, 4, 0, 0)) {
+ return;
+ }
+ } else {
+ if (lua_pcall(L, 2, 0, 0)) {
+ return;
+ }
+ }
+ tex_formatted_warning("mplib", "run internal: %s", lua_tostring(L, -1));
+ }
+}
+
+static char *mplib_aux_make_text(MP mp, const char *str, size_t len, int mode)
+{
+ if (mp->make_text_id) {
+ lua_State *L = (lua_State *) mp_userdata(mp);
+ int stacktop = lua_gettop(L);
+ char *s = NULL;
+ lua_rawgeti(L, LUA_REGISTRYINDEX, mp->make_text_id);
+ lua_pushlstring(L, str, len);
+ lua_pushinteger(L, mode);
+ ++mplib_state.text_callbacks;
+ if (lua_pcall(L, 2, 1, 0)) {
+ tex_formatted_warning("mplib", "make text: %s", lua_tostring(L, -1));
+ } else {
+ s = lmt_string_from_index(L, -1);
+ }
+ lua_settop(L, stacktop);
+ return s;
+ }
+ return NULL;
+}
+
+static void mplib_aux_run_logger(MP mp, int target, const char *str, size_t len)
+{
+ if (mp->run_logger_id) {
+ lua_State *L = (lua_State *) mp_userdata(mp);
+ int stacktop = lua_gettop(L);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, mp->run_logger_id);
+ lua_pushinteger(L, target);
+ lua_pushlstring(L, str, len);
+ ++mplib_state.log_callbacks;
+ if (lua_pcall(L, 2, 0, 0)) {
+ tex_formatted_warning("mplib", "run logger: %s", lua_tostring(L, -1));
+ }
+ lua_settop(L, stacktop);
+ }
+}
+
+static int mplib_aux_run_overload(MP mp, int property, const char *str, int mode)
+{
+ int quit = 0;
+ if (mp->run_overload_id) {
+ lua_State *L = (lua_State *) mp_userdata(mp);
+ int stacktop = lua_gettop(L);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, mp->run_overload_id);
+ lua_pushinteger(L, property);
+ lua_pushstring(L, str);
+ lua_pushinteger(L, mode);
+ ++mplib_state.overload_callbacks;
+ if (lua_pcall(L, 3, 1, 0)) {
+ tex_formatted_warning("mplib", "run overload: %s", lua_tostring(L, -1));
+ quit = 1;
+ } else {
+ quit = lua_toboolean(L, -1);
+ }
+ lua_settop(L, stacktop);
+ }
+ return quit;
+}
+
+static void mplib_aux_run_error(MP mp, const char *str, const char *help, int interaction)
+{
+ if (mp->run_error_id) {
+ lua_State *L = (lua_State *) mp_userdata(mp);
+ int stacktop = lua_gettop(L);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, mp->run_error_id);
+ lua_pushstring(L, str);
+ lua_pushstring(L, help);
+ lua_pushinteger(L, interaction);
+ ++mplib_state.error_callbacks;
+ if (lua_pcall(L, 3, 0, 0)) {
+ tex_formatted_warning("mplib", "run error: %s", lua_tostring(L, -1));
+ }
+ lua_settop(L, stacktop);
+ }
+}
+
+/*
+
+ We keep all management in Lua, so for now we don't create an object. Files are normally closed
+ anyway. We can always make it nicer. The opener has to return an integer. A value zero indicates
+ that no file is opened.
+
+*/
+
+/* index needs checking, no need for pointer (was old) */
+
+static void *mplib_aux_open_file(MP mp, const char *fname, const char *fmode, int ftype)
+{
+ if (mp->open_file_id) {
+ int *index = mp_memory_allocate(sizeof(int));
+ if (index) {
+ lua_State *L = (lua_State *) mp_userdata(mp);
+ int stacktop = lua_gettop(L);
+ lua_rawgeti(L, LUA_REGISTRYINDEX, mp->open_file_id);
+ lua_pushstring(L, fname);
+ lua_pushstring(L, fmode);
+ if (ftype > mp_filetype_text) {
+ lua_pushinteger(L, (lua_Integer) ftype - mp_filetype_text);
+ } else {
+ lua_pushstring(L, mplib_filetype_names[ftype]);
+ }
+ ++mplib_state.file_callbacks;
+ if (lua_pcall(L, 3, 1, 0)) {
+ *((int*) index) = 0;
+ } else if (lua_istable(L, -1)) {
+ lua_pushvalue(L, -1);
+ *((int*) index) = luaL_ref(L, LUA_REGISTRYINDEX);
+ } else {
+ tex_normal_warning("mplib", "open file: table expected");
+ *((int*) index) = 0;
+ }
+ lua_settop(L, stacktop);
+ return index;
+ }
+ }
+ return NULL;
+}
+
+# define mplib_pop_function(idx,tag) \
+ lua_rawgeti(L, LUA_REGISTRYINDEX, idx); \
+ lua_push_key(tag); \
+ lua_rawget(L, -2);
+
+static void mplib_aux_close_file(MP mp, void *index)
+{
+ if (mp->open_file_id && index) {
+ int idx = *((int*) index);
+ lua_State *L = (lua_State *) mp_userdata(mp);
+ int stacktop = lua_gettop(L);
+ mplib_pop_function(idx, close)
+ if (lua_isfunction(L, -1)) {
+ ++mplib_state.file_callbacks;
+ if (lua_pcall(L, 0, 0, 0)) {
+ /* no message */
+ } else {
+ /* nothing to be done here */
+ }
+ }
+ /*
+ if (index) {
+ luaL_unref(L, idx));
+ }
+ */
+ lua_settop(L, stacktop);
+ mp_memory_free(index);
+ }
+}
+
+static char *mplib_aux_read_file(MP mp, void *index, size_t *size)
+{
+ if (mp->open_file_id && index) {
+ lua_State *L = (lua_State *) mp_userdata(mp);
+ int stacktop = lua_gettop(L);
+ int idx = *((int*) index);
+ char *s = NULL;
+ mplib_pop_function(idx, reader)
+ if (lua_isfunction(L, -1)) {
+ ++mplib_state.file_callbacks;
+ if (lua_pcall(L, 0, 1, 0)) {
+ *size = 0;
+ } else if (lua_type(L, -1) == LUA_TSTRING) {
+ s = lmt_lstring_from_index(L, -1, size);
+ }
+ }
+ lua_settop(L, stacktop);
+ return s;
+ }
+ return NULL;
+}
+
+static void mplib_aux_write_file(MP mp, void *index, const char *s)
+{
+ if (mp->open_file_id && index) {
+ lua_State *L = (lua_State *) mp_userdata(mp);
+ int stacktop = lua_gettop(L);
+ int idx = *((int*) index);
+ mplib_pop_function(idx, writer)
+ if (lua_isfunction(L, -1)) {
+ lua_pushstring(L, s);
+ ++mplib_state.file_callbacks;
+ if (lua_pcall(L, 1, 0, 0)) {
+ /* no message */
+ } else {
+ /* nothing to be done here */
+ }
+ }
+ lua_settop(L, stacktop);
+ }
+}
+
+static int mplib_scan_next(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ int token = 0;
+ int mode = 0;
+ int kind = 0;
+ if (mp) {
+ int keep = 0 ;
+ if (lua_gettop(L) > 1) {
+ keep = lua_toboolean(L, 2);
+ }
+ mp_scan_next_value(mp, keep, &token, &mode, &kind);
+ }
+ lua_pushinteger(L, token);
+ lua_pushinteger(L, mode);
+ lua_pushinteger(L, kind);
+ return 3;
+}
+
+static int mplib_scan_expression(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ int kind = 0;
+ if (mp) {
+ int keep = 0 ;
+ if (lua_gettop(L) > 1) {
+ keep = lua_toboolean(L, 2);
+ }
+ mp_scan_expr_value(mp, keep, &kind);
+ }
+ lua_pushinteger(L, kind);
+ return 1;
+}
+
+static int mplib_scan_token(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ int token = 0;
+ int mode = 0;
+ int kind = 0;
+ if (mp) {
+ int keep = 0 ;
+ if (lua_gettop(L) > 1) {
+ keep = lua_toboolean(L, 2);
+ }
+ mp_scan_token_value(mp, keep, &token, &mode, &kind);
+ }
+ lua_pushinteger(L, token);
+ lua_pushinteger(L, mode);
+ lua_pushinteger(L, kind);
+ return 3;
+}
+
+static int mplib_skip_token(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ lua_pushboolean(L, mp ? mp_skip_token_value(mp, lmt_tointeger(L, 2)) : 0);
+ return 1;
+}
+
+static int mplib_scan_symbol(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ int keep = 0 ;
+ int expand = 1 ;
+ int top = lua_gettop(L) ;
+ char *s = NULL;
+ if (top > 2) { // no need to check
+ expand = lua_toboolean(L, 3);
+ }
+ if (top > 1) { // no need to check
+ keep = lua_toboolean(L, 2);
+ }
+ mp_scan_symbol_value(mp, keep, &s, expand) ;
+ if (s) {
+ /* we could do without the copy */
+ lua_pushstring(L, s);
+ mp_memory_free(s);
+ return 1;
+ }
+ }
+ lua_pushliteral(L,"");
+ return 1;
+}
+
+static int mplib_scan_property(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ int keep = 0 ;
+ int top = lua_gettop(L) ;
+ int type = 0 ;
+ int property = 0 ;
+ int detail = 0 ;
+ char *s = NULL;
+ if (top > 1) {
+ keep = lua_toboolean(L, 2);
+ }
+ mp_scan_property_value(mp, keep, &type, &s, &property, &detail);
+ if (s) {
+ lua_pushinteger(L, type);
+ lua_pushstring(L, s);
+ lua_pushinteger(L, property);
+ lua_pushinteger(L, detail);
+ mp_memory_free(s);
+ return 4;
+ }
+ }
+ return 0;
+}
+
+/*tex
+ A circle has 8 points and a square 4 so let's just start with 8 slots in the table.
+*/
+
+static int aux_is_curved_gr(mp_gr_knot ith, mp_gr_knot pth, lua_Number tolerance)
+{
+ lua_Number d = pth->left_x - ith->right_x;
+ if (fabs(ith->right_x - ith->x_coord - d) <= tolerance && fabs(pth->x_coord - pth->left_x - d) <= tolerance) {
+ d = pth->left_y - ith->right_y;
+ if (fabs(ith->right_y - ith->y_coord - d) <= tolerance && fabs(pth->y_coord - pth->left_y - d) <= tolerance) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static int aux_is_duplicate_gr(mp_gr_knot pth, mp_gr_knot nxt, lua_Number tolerance)
+{
+ return (fabs(pth->x_coord - nxt->x_coord) <= tolerance && fabs(pth->y_coord - nxt->y_coord) <= tolerance);
+}
+
+# define valid_knot_type(t) (t >= mp_endpoint_knot && t <= mp_end_cycle_knot) /* pens can act weird here */
+
+// -68.031485 2.83464 l
+// -68.031485 2.83464 -68.031485 -2.83464 -68.031485 -2.83464 c
+
+static void mplib_aux_push_path(lua_State *L, mp_gr_knot h, int ispen, lua_Number bendtolerance, lua_Number movetolerance)
+{
+ if (h) {
+ int i = 0;
+ mp_gr_knot p = h;
+ mp_gr_knot q = h;
+ int iscycle = 1;
+ lua_createtable(L, ispen ? 1 : MPLIB_PATH_SIZE, ispen ? 2 : 1);
+ do {
+ mp_gr_knot n = p->next;
+ int lt = p->left_type;
+ int rt = p->right_type;
+ if (ispen) {
+ lua_createtable(L, 0, 6);
+ // } else if (i > 0 && p != h && aux_is_duplicate_gr(p, n, movetolerance) && ! aux_is_curved_gr(p, n, bendtolerance) ) {
+ } else if (i > 0 && p != h && n != h && aux_is_duplicate_gr(p, n, movetolerance) && ! aux_is_curved_gr(p, n, bendtolerance) ) {
+ n->left_x = p->left_x;
+ n->left_y = p->left_y;
+ goto NEXTONE;
+ } else {
+ int ln = lt != mp_explicit_knot;
+ int rn = rt != mp_explicit_knot;
+ int ic = i > 0 && aux_is_curved_gr(q, p, bendtolerance);
+ int st = p->state;
+ lua_createtable(L, 0, 6 + (ic ? 1 : 0) + (ln ? 1 : 0) + (rn ? 1 : 0) + (st ? 1: 0));
+ if (ln && valid_knot_type(lt)) {
+ lua_push_svalue_at_key(L, left_type, mplib_values_knot[lt]);
+ }
+ if (rn && valid_knot_type(rt)) {
+ lua_push_svalue_at_key(L, right_type, mplib_values_knot[rt]);
+ }
+ if (ic) {
+ lua_push_boolean_at_key(L, curved, 1);
+ }
+ if (st) {
+ lua_push_integer_at_key(L, state, st);
+ }
+ lua_push_number_at_key(L, x_coord, p->x_coord);
+ lua_push_number_at_key(L, y_coord, p->y_coord);
+ lua_push_number_at_key(L, left_x, p->left_x);
+ lua_push_number_at_key(L, left_y, p->left_y);
+ lua_push_number_at_key(L, right_x, p->right_x);
+ lua_push_number_at_key(L, right_y, p->right_y);
+ lua_rawseti(L, -2, ++i);
+ if (rt == mp_endpoint_knot) {
+ iscycle = 0;
+ break;
+ }
+ }
+ NEXTONE:
+ q = p;
+ p = n;
+ } while (p && p != h);
+ if (iscycle && i > 1 && aux_is_curved_gr(q, h, bendtolerance)) {
+ lua_rawgeti(L, -1, 1);
+ lua_push_boolean_at_key(L, curved, 1);
+ lua_pop(L, 1);
+ }
+ if (ispen) {
+ lua_push_boolean_at_key(L, pen, 1);
+ }
+ lua_push_boolean_at_key(L, cycle, iscycle);
+ } else {
+ lua_pushnil(L);
+ }
+}
+
+static int aux_is_curved(MP mp, mp_knot ith, mp_knot pth, lua_Number tolerance)
+{
+ lua_Number d = mp_number_as_double(mp, pth->left_x) - mp_number_as_double(mp, ith->right_x);
+ if (fabs(mp_number_as_double(mp, ith->right_x) - mp_number_as_double(mp, ith->x_coord) - d) <= tolerance && fabs(mp_number_as_double(mp, pth->x_coord) - mp_number_as_double(mp, pth->left_x) - d) <= tolerance) {
+ d = mp_number_as_double(mp, pth->left_y) - mp_number_as_double(mp, ith->right_y);
+ if (fabs(mp_number_as_double(mp, ith->right_y) - mp_number_as_double(mp, ith->y_coord) - d) <= tolerance && fabs(mp_number_as_double(mp, pth->y_coord) - mp_number_as_double(mp, pth->left_y) - d) <= tolerance) {
+ return 0;
+ }
+ }
+ return 1;
+}
+
+static void aux_mplib_knot_to_path(lua_State *L, MP mp, mp_knot h, int ispen, int compact, int check, lua_Number bendtolerance) // maybe also movetolerance
+{
+ int i = 1;
+ mp_knot p = h;
+ mp_knot q = h;
+ int iscycle = 1;
+ lua_createtable(L, ispen ? 1 : MPLIB_PATH_SIZE, ispen ? 2 : 1);
+ if (compact) {
+ do {
+ lua_createtable(L, 2, 0);
+ lua_push_number_at_index(L, 1, mp_number_as_double(mp, p->x_coord));
+ lua_push_number_at_index(L, 2, mp_number_as_double(mp, p->y_coord));
+ lua_rawseti(L, -2, i++);
+ if (p->right_type == mp_endpoint_knot) {
+ iscycle = 0;
+ break;
+ } else {
+ p = p->next;
+ }
+ } while (p && p != h);
+ } else {
+ do {
+ int lt = p->left_type;
+ int rt = p->right_type;
+ int ln = lt != mp_explicit_knot;
+ int rn = rt != mp_explicit_knot;
+ int ic = check && (i > 1) && aux_is_curved(mp, q, p, bendtolerance);
+ lua_createtable(L, 0, 6 + (ic ? 1 : 0) + (ln ? 1 : 0) + (rn ? 1 : 0));
+ if (ln && valid_knot_type(lt)) {
+ lua_push_svalue_at_key(L, left_type, mplib_values_knot[lt]);
+ } else {
+ /* a pen */
+ }
+ if (rn && valid_knot_type(rt)) {
+ lua_push_svalue_at_key(L, right_type, mplib_values_knot[rt]);
+ } else {
+ /* a pen */
+ }
+ if (ic) {
+ lua_push_boolean_at_key(L, curved, 1);
+ }
+ lua_push_number_at_index(L, 1, mp_number_as_double(mp, p->x_coord));
+ lua_push_number_at_index(L, 2, mp_number_as_double(mp, p->y_coord));
+ lua_push_number_at_index(L, 3, mp_number_as_double(mp, p->left_x));
+ lua_push_number_at_index(L, 4, mp_number_as_double(mp, p->left_y));
+ lua_push_number_at_index(L, 5, mp_number_as_double(mp, p->right_x));
+ lua_push_number_at_index(L, 6, mp_number_as_double(mp, p->right_y));
+ lua_rawseti(L, -2, i++);
+ if (rt == mp_endpoint_knot) {
+ iscycle = 0;
+ break;
+ } else {
+ q = p;
+ p = p->next;
+ }
+ } while (p && p != h);
+ if (check && iscycle && i > 1 && aux_is_curved(mp, q, h, bendtolerance)) {
+ lua_rawgeti(L, -1, 1);
+ lua_push_boolean_at_key(L, curved, 1);
+ lua_pop(L, 1);
+ }
+ }
+ if (ispen) {
+ lua_push_boolean_at_key(L, pen, 1);
+ }
+ lua_push_boolean_at_key(L, cycle, iscycle);
+}
+
+/*tex
+
+ A couple of scanners. I know what I want to change but not now. First some longer term
+ experiments.
+
+*/
+
+# define push_number_in_slot(L,i,n) \
+ lua_pushnumber(L, n); \
+ lua_rawseti(L, -2, i);
+
+# define kind_of_expression(n) \
+ lmt_optinteger(L, n, 0)
+
+static int mplib_scan_string(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ char *s = NULL;
+ size_t l = 0;
+ mp_scan_string_value(mp, kind_of_expression(2), &s, &l) ;
+ if (s) {
+ lua_pushlstring(L, s, l);
+ return 1;
+ }
+ }
+ lua_pushliteral(L,"");
+ return 1;
+}
+
+static int mplib_scan_boolean(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ int b = 0;
+ if (mp) {
+ mp_scan_boolean_value(mp, kind_of_expression(2), &b);
+ }
+ lua_pushboolean(L, b);
+ return 1;
+}
+
+static int mplib_scan_numeric(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ double d = 0.0;
+ if (mp) {
+ mp_scan_numeric_value(mp, kind_of_expression(2), &d);
+ }
+ lua_pushnumber(L, d);
+ return 1;
+}
+
+static int mplib_scan_integer(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ double d = 0.0;
+ if (mp) {
+ mp_scan_numeric_value(mp, kind_of_expression(2), &d);
+ }
+ lua_pushinteger(L, (int) d); /* floored */
+ return 1;
+}
+
+static int mplib_scan_pair(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ double x = 0.0;
+ double y = 0.0;
+ if (mp) {
+ mp_scan_pair_value(mp, kind_of_expression(3), &x, &y);
+ }
+ if (lua_toboolean(L, 2)) {
+ lua_createtable(L, 2, 0);
+ push_number_in_slot(L, 1, x);
+ push_number_in_slot(L, 2, y);
+ return 1;
+ } else {
+ lua_pushnumber(L, x);
+ lua_pushnumber(L, y);
+ return 2;
+ }
+}
+
+static int mplib_scan_color(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ double r = 0.0;
+ double g = 0.0;
+ double b = 0.0;
+ if (mp) {
+ mp_scan_color_value(mp, kind_of_expression(3), &r, &g, &b);
+ }
+ if (lua_toboolean(L, 2)) {
+ lua_createtable(L, 3, 0);
+ push_number_in_slot(L, 1, r);
+ push_number_in_slot(L, 2, g);
+ push_number_in_slot(L, 3, b);
+ return 1;
+ } else {
+ lua_pushnumber(L, r);
+ lua_pushnumber(L, g);
+ lua_pushnumber(L, b);
+ return 3;
+ }
+}
+
+static int mplib_scan_cmykcolor(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ double c = 0.0;
+ double m = 0.0;
+ double y = 0.0;
+ double k = 0.0;
+ if (mp) {
+ mp_scan_cmykcolor_value(mp, kind_of_expression(3), &c, &m, &y, &k);
+ }
+ if (lua_toboolean(L, 2)) {
+ lua_createtable(L, 4, 0);
+ push_number_in_slot(L, 1, c);
+ push_number_in_slot(L, 2, m);
+ push_number_in_slot(L, 3, y);
+ push_number_in_slot(L, 4, k);
+ return 1;
+ } else {
+ lua_pushnumber(L, c);
+ lua_pushnumber(L, m);
+ lua_pushnumber(L, y);
+ lua_pushnumber(L, k);
+ return 4;
+ }
+}
+
+static int mplib_scan_transform(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ double x = 0.0;
+ double y = 0.0;
+ double xx = 0.0;
+ double xy = 0.0;
+ double yx = 0.0;
+ double yy = 0.0;
+ if (mp) {
+ mp_scan_transform_value(mp, kind_of_expression(3), &x, &y, &xx, &xy, &yx, &yy);
+ }
+ if (lua_toboolean(L, 2)) {
+ lua_createtable(L, 6, 0);
+ push_number_in_slot(L, 1, x);
+ push_number_in_slot(L, 2, y);
+ push_number_in_slot(L, 3, xx);
+ push_number_in_slot(L, 4, xy);
+ push_number_in_slot(L, 5, yx);
+ push_number_in_slot(L, 6, yy);
+ return 1;
+ } else {
+ lua_pushnumber(L, x);
+ lua_pushnumber(L, y);
+ lua_pushnumber(L, xx);
+ lua_pushnumber(L, xy);
+ lua_pushnumber(L, yx);
+ lua_pushnumber(L, yy);
+ return 6;
+ }
+}
+
+static int mplib_scan_path(lua_State *L) /* 1=mp 2=compact 3=kind(prim) 4=check */
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ mp_knot p = NULL;
+ lua_Number t = mplib_aux_get_bend_tolerance(L, 1); /* iuservalue */
+ mp_scan_path_value(mp, kind_of_expression(3), &p);
+ if (p) {
+ aux_mplib_knot_to_path(L, mp, p, 0, lua_toboolean(L, 2), lua_toboolean(L, 4), t);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int mplib_scan_pen(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ mp_knot p = NULL ;
+ lua_Number t = mplib_aux_get_bend_tolerance(L, 1);
+ mp_scan_path_value(mp, kind_of_expression(3), &p) ;
+ if (p) {
+ aux_mplib_knot_to_path(L, mp, p, 1, lua_toboolean(L, 2), lua_toboolean(L, 4), t);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int mplib_inject_string(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ size_t l = 0;
+ const char *s = lua_tolstring(L, 2, &l);
+ mp_push_string_value(mp, s, (int) l);
+ }
+ return 0;
+}
+
+static int mplib_inject_boolean(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ int b = lua_toboolean(L, 2);
+ mp_push_boolean_value(mp, b);
+ }
+ return 0;
+}
+
+static int mplib_inject_numeric(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ double d = lua_tonumber(L, 2);
+ mp_push_numeric_value(mp, d);
+ }
+ return 0;
+}
+
+static int mplib_inject_integer(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ int i = lmt_tointeger(L, 2);
+ mp_push_integer_value(mp, i);
+ }
+ return 0;
+}
+
+static int mplib_inject_pair(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ switch (lua_type(L, 2)) {
+ case LUA_TNUMBER:
+ mp_push_pair_value(mp,
+ luaL_optnumber(L, 2, 0),
+ luaL_optnumber(L, 3, 0)
+ );
+ break;
+ case LUA_TTABLE:
+ mp_push_pair_value(mp,
+ lmt_number_from_table(L, 2, 1, 0.0),
+ lmt_number_from_table(L, 2, 2, 0.0)
+ );
+ break;
+ }
+ }
+ return 0;
+}
+
+static int mplib_inject_color(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ switch (lua_type(L, 2)) {
+ case LUA_TNUMBER:
+ mp_push_color_value(mp,
+ luaL_optnumber(L, 2, 0),
+ luaL_optnumber(L, 3, 0),
+ luaL_optnumber(L, 4, 0)
+ );
+ break;
+ case LUA_TTABLE:
+ mp_push_color_value(mp,
+ lmt_number_from_table(L, 2, 1, 0.0),
+ lmt_number_from_table(L, 2, 2, 0.0),
+ lmt_number_from_table(L, 2, 3, 0.0)
+ );
+ break;
+ }
+ }
+ return 0;
+}
+
+static int mplib_inject_cmykcolor(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ switch (lua_type(L, 2)) {
+ case LUA_TNUMBER:
+ mp_push_cmykcolor_value(mp,
+ luaL_optnumber(L, 2, 0),
+ luaL_optnumber(L, 3, 0),
+ luaL_optnumber(L, 4, 0),
+ luaL_optnumber(L, 5, 0)
+ );
+ break;
+ case LUA_TTABLE:
+ mp_push_cmykcolor_value(mp,
+ lmt_number_from_table(L, 2, 1, 0.0),
+ lmt_number_from_table(L, 2, 2, 0.0),
+ lmt_number_from_table(L, 2, 3, 0.0),
+ lmt_number_from_table(L, 2, 4, 0.0)
+ );
+ break;
+ }
+ }
+ return 0;
+}
+
+static int mplib_inject_transform(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ switch (lua_type(L, 2)) {
+ case LUA_TNUMBER:
+ mp_push_transform_value(mp,
+ luaL_optnumber(L, 2, 0), // 1
+ luaL_optnumber(L, 3, 0),
+ luaL_optnumber(L, 4, 0),
+ luaL_optnumber(L, 5, 0), // 1
+ luaL_optnumber(L, 6, 0),
+ luaL_optnumber(L, 7, 0)
+ );
+ break;
+ case LUA_TTABLE:
+ mp_push_transform_value(mp,
+ lmt_number_from_table(L, 2, 1, 0.0), // 1.0
+ lmt_number_from_table(L, 2, 2, 0.0),
+ lmt_number_from_table(L, 2, 3, 0.0),
+ lmt_number_from_table(L, 2, 4, 0.0), // 1.0
+ lmt_number_from_table(L, 2, 5, 0.0),
+ lmt_number_from_table(L, 2, 6, 0.0)
+ );
+ break;
+ }
+ }
+ return 0;
+}
+
+static int mplib_new(lua_State *L)
+{
+ MP *mpud = lua_newuserdatauv(L, sizeof(MP *), 2);
+ if (mpud) {
+ MP mp = NULL;
+ struct MP_options *options = mp_options();
+ lua_Number bendtolerance = default_bend_tolerance;
+ lua_Number movetolerance = default_move_tolerance;
+ options->userdata = (void *) L;
+ options->job_name = NULL;
+ options->extensions = 0 ;
+ options->utf8_mode = 0;
+ options->text_mode = 0;
+ options->show_mode = 0;
+ options->halt_on_error = 0;
+ options->find_file = mplib_aux_find_file;
+ options->run_script = mplib_aux_run_script;
+ options->run_internal = mplib_aux_run_internal;
+ options->run_logger = mplib_aux_run_logger;
+ options->run_overload = mplib_aux_run_overload;
+ options->run_error = mplib_aux_run_error;
+ options->make_text = mplib_aux_make_text;
+ options->open_file = mplib_aux_open_file;
+ options->close_file = mplib_aux_close_file;
+ options->read_file = mplib_aux_read_file;
+ options->write_file = mplib_aux_write_file;
+ options->shipout_backend = mplib_shipout_backend;
+ if (lua_type(L, 1) == LUA_TTABLE) {
+ lua_pushnil(L);
+ while (lua_next(L, 1)) {
+ if (lua_type(L, -2) == LUA_TSTRING) {
+ const char *s = lua_tostring(L, -2);
+ if (lua_key_eq(s, random_seed)) {
+ options->random_seed = (int) lua_tointeger(L, -1);
+ } else if (lua_key_eq(s, interaction)) {
+ options->interaction = luaL_checkoption(L, -1, "silent", mplib_interaction_options);
+ } else if (lua_key_eq(s, job_name)) {
+ // options->job_name = lmt_generic_strdup(lua_tostring(L, -1));
+ options->job_name = lmt_memory_strdup(lua_tostring(L, -1));
+ } else if (lua_key_eq(s, find_file)) {
+ if (mplib_aux_find_file_function(L, options)) {
+ tex_normal_warning("mplib", "find file: function expected");
+ }
+ } else if (lua_key_eq(s, run_script)) {
+ if (mplib_aux_run_script_function(L, options)) {
+ tex_normal_warning("mplib", "run script: function expected");
+ }
+ } else if (lua_key_eq(s, run_internal)) {
+ if (mplib_aux_run_internal_function(L, options)) {
+ tex_normal_warning("mplib", "run internal: function expected");
+ }
+ } else if (lua_key_eq(s, make_text)) {
+ if (mplib_aux_make_text_function(L, options)) {
+ tex_normal_warning("mplib", "make text: function expected");
+ }
+ } else if (lua_key_eq(s, extensions)) {
+ options->extensions = (int) lua_tointeger(L, -1);
+ } else if (lua_key_eq(s, math_mode)) {
+ options->math_mode = luaL_checkoption(L, -1, "scaled", mplib_math_options);
+ } else if (lua_key_eq(s, utf8_mode)) {
+ options->utf8_mode = (int) lua_toboolean(L, -1);
+ } else if (lua_key_eq(s, text_mode)) {
+ options->text_mode = (int) lua_toboolean(L, -1);
+ } else if (lua_key_eq(s, show_mode)) {
+ options->show_mode = (int) lua_toboolean(L, -1);
+ } else if (lua_key_eq(s, halt_on_error)) {
+ options->halt_on_error = (int) lua_toboolean(L, -1);
+ } else if (lua_key_eq(s, run_logger)) {
+ if (mplib_aux_run_logger_function(L, options)) {
+ tex_normal_warning("mplib", "run logger: function expected");
+ }
+ } else if (lua_key_eq(s, run_overload)) {
+ if (mplib_aux_run_overload_function(L, options)) {
+ tex_normal_warning("mplib", "run overload: function expected");
+ }
+ } else if (lua_key_eq(s, run_error)) {
+ if (mplib_aux_run_error_function(L, options)) {
+ tex_normal_warning("mplib", "run error: function expected");
+ }
+ } else if (lua_key_eq(s, open_file)) {
+ if (mplib_aux_open_file_function(L, options)) {
+ tex_normal_warning("mplib", "open file: function expected");
+ }
+ } else if (lua_key_eq(s, bend_tolerance)) {
+ bendtolerance = lua_tonumber(L, -1);
+ } else if (lua_key_eq(s, move_tolerance)) {
+ movetolerance = lua_tonumber(L, -1);
+ }
+ }
+ lua_pop(L, 1);
+ }
+ }
+ if (! options->job_name || ! *(options->job_name)) {
+ mp_memory_free(options); /* leaks */
+ tex_normal_warning("mplib", "job_name is not set");
+ goto BAD;
+ }
+ mp = mp_initialize(options);
+ mp_memory_free(options); /* leaks */
+ if (mp) {
+ *mpud = mp;
+ mplib_aux_set_bend_tolerance(L, bendtolerance);
+ mplib_aux_set_move_tolerance(L, movetolerance);
+ luaL_getmetatable(L, MP_METATABLE_INSTANCE);
+ lua_setmetatable(L, -2);
+ return 1;
+ }
+ }
+ BAD:
+ lua_pushnil(L);
+ return 1;
+}
+
+# define mplib_collect_id(id) do { \
+ if (id) { \
+ luaL_unref(L, LUA_REGISTRYINDEX, id); \
+ } \
+} while(0)
+
+static int mplib_instance_collect(lua_State *L)
+{
+ MP *mpud = mplib_aux_is_mpud(L, 1);
+ if (*mpud) {
+ MP mp = *mpud;
+ int run_logger_id = (mp)->run_logger_id;
+ mplib_collect_id((mp)->find_file_id);
+ mplib_collect_id((mp)->run_script_id);
+ mplib_collect_id((mp)->run_internal_id);
+ mplib_collect_id((mp)->run_overload_id);
+ mplib_collect_id((mp)->run_error_id);
+ mplib_collect_id((mp)->make_text_id);
+ mplib_collect_id((mp)->open_file_id);
+ mp_finish(mp);
+ *mpud = NULL;
+ mplib_collect_id(run_logger_id);
+ }
+ return 0;
+}
+
+static int mplib_instance_tostring(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ lua_pushfstring(L, "<mp.instance %p>", mp);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int mplib_aux_wrapresults(lua_State *L, mp_run_data *res, int status, lua_Number bendtolerance, lua_Number movetolerance)
+{
+ lua_newtable(L);
+ if (res->edges) {
+ struct mp_edge_object *p = res->edges;
+ int i = 1;
+ lua_push_key(fig);
+ lua_newtable(L);
+ while (p) {
+ struct mp_edge_object **v = lua_newuserdatauv(L, sizeof(struct mp_edge_object *), 2);
+ *v = p;
+ mplib_aux_set_bend_tolerance(L, bendtolerance);
+ mplib_aux_set_move_tolerance(L, movetolerance);
+ luaL_getmetatable(L, MP_METATABLE_FIGURE);
+ lua_setmetatable(L, -2);
+ lua_rawseti(L, -2, i);
+ i++;
+ p = p->next;
+ }
+ lua_rawset(L,-3);
+ res->edges = NULL;
+ }
+ lua_push_integer_at_key(L, status, status);
+ return 1;
+}
+
+static int mplib_execute(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ /* no string in slot 2 or an empty string means that we already filled the terminal */
+ size_t l = 0;
+ lua_Number bendtolerance = mplib_aux_get_bend_tolerance(L, 1);
+ lua_Number movetolerance = mplib_aux_get_move_tolerance(L, 1);
+ const char *s = lua_isstring(L, 2) ? lua_tolstring(L, 2, &l) : NULL;
+ int h = mp_execute(mp, s, l);
+ mp_run_data *res = mp_rundata(mp);
+ return mplib_aux_wrapresults(L, res, h, bendtolerance, movetolerance);
+ }
+ lua_pushnil(L);
+ return 1;
+}
+
+static int mplib_finish(lua_State *L)
+{
+ MP *mpud = mplib_aux_is_mpud(L, 1);
+ if (*mpud) {
+ MP mp = *mpud;
+ lua_Number bendtolerance = mplib_aux_get_bend_tolerance(L, 1);
+ lua_Number movetolerance = mplib_aux_get_move_tolerance(L, 1);
+ int h = mp_execute(mp, NULL, 0);
+ mp_run_data *res = mp_rundata(mp);
+ int i = mplib_aux_wrapresults(L, res, h, bendtolerance, movetolerance);
+ mp_finish(mp);
+ *mpud = NULL;
+ return i;
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int mplib_showcontext(lua_State *L)
+{
+ MP *mpud = mplib_aux_is_mpud(L, 1);
+ if (*mpud) {
+ MP mp = *mpud;
+ mp_show_context(mp);
+ }
+ return 0;
+}
+
+static int mplib_gethashentry(lua_State *L)
+{
+ MP *mpud = mplib_aux_is_mpud(L, 1);
+ if (*mpud) {
+ MP mp = *mpud;
+ char *name = (char *) lua_tostring(L, 2);
+ if (name) {
+ mp_symbol_entry *s = (mp_symbol_entry *) mp_fetch_symbol(mp, name);
+ if (s) {
+ mp_node q = s->type == mp_tag_command ? s->v.data.node : NULL;
+ lua_pushinteger(L, s->type);
+ lua_pushinteger(L, s->property);
+ if (q) {
+ lua_pushinteger(L, q->type);
+ return 3;
+ } else {
+ return 2;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static int mplib_gethashentries(lua_State *L)
+{
+ MP *mpud = mplib_aux_is_mpud(L, 1);
+ if (*mpud) {
+ MP mp = *mpud;
+ int full = lua_toboolean(L, 2);
+ if (mp_initialize_symbol_traverse(mp)) {
+ size_t n = 0;
+ lua_newtable(L);
+ while (1) {
+ mp_symbol_entry *s = (mp_symbol_entry *) mp_fetch_symbol_traverse(mp);
+ if (s) {
+ if (full) {
+ mp_node q = s->type == mp_tag_command ? s->v.data.node : NULL;
+ lua_createtable(L, (q || s->property == 0x1) ? 4 : 3, 0);
+ lua_pushinteger(L, s->type);
+ lua_rawseti(L, -2, 1);
+ lua_pushinteger(L, s->property);
+ lua_rawseti(L, -2, 2);
+ lua_pushlstring(L, (const char *) s->text->str, s->text->len);
+ lua_rawseti(L, -2, 3);
+ if (q) {
+ lua_pushinteger(L, q->type);
+ lua_rawseti(L, -2, 4);
+ } else if (s->property == 0x1) {
+ lua_pushinteger(L, s->v.data.indep.serial);
+ lua_rawseti(L, -2, 4);
+ }
+ } else {
+ lua_pushlstring(L, (const char *) s->text->str, s->text->len);
+ }
+ lua_rawseti(L, -2, ++n);
+ } else {
+ break;
+ }
+ }
+ mp_kill_symbol_traverse(mp);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int mplib_version(lua_State *L)
+{
+ char *s = mp_metapost_version();
+ lua_pushstring(L, s);
+ mp_memory_free(s);
+ return 1;
+}
+
+static int mplib_getstatistics(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ lua_createtable(L, 0, 9);
+ lua_push_integer_at_key(L, memory, mp->var_used); /* bytes of node memory */
+ lua_push_integer_at_key(L, hash, mp->st_count);
+ lua_push_integer_at_key(L, parameters, mp->max_param_stack); /* allocated: mp->param_size */
+ lua_push_integer_at_key(L, input, mp->max_in_stack); /* allocated: mp->stack_size */
+ lua_push_integer_at_key(L, tokens, mp->num_token_nodes);
+ lua_push_integer_at_key(L, pairs, mp->num_pair_nodes);
+ lua_push_integer_at_key(L, knots, mp->num_knot_nodes);
+ lua_push_integer_at_key(L, nodes, mp->num_value_nodes);
+ lua_push_integer_at_key(L, symbols, mp->num_symbolic_nodes);
+ lua_push_integer_at_key(L, characters, mp->max_pl_used);
+ lua_push_integer_at_key(L, strings, mp->max_strs_used);
+ lua_push_integer_at_key(L, internals, mp->int_ptr); /* allocates: mp->max_internal */
+ /* lua_push_integer_at_key(L, buffer, mp->max_buf_stack + 1); */ /* allocated: mp->buf_size */
+ /* lua_push_integer_at_key(L, open, mp->in_open_max - file_bottom); */ /* allocated: mp->max_in_open - file_bottom */
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int mplib_getstatus(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ lua_pushinteger(L, mp->scanner_status);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static int mplib_aux_set_direction(lua_State *L, MP mp, mp_knot p) {
+ double direction_x = (double) lua_tonumber(L, -1);
+ double direction_y = 0;
+ lua_pop(L, 1);
+ lua_push_key(direction_y);
+ if (lua_rawget(L, -2) == LUA_TNUMBER) {
+ direction_y = (double) lua_tonumber(L, -1);
+ lua_pop(L, 1);
+ return mp_set_knot_direction(mp, p, direction_x, direction_y) ? 1 : 0;
+ } else {
+ return 0;
+ }
+}
+
+static int mplib_aux_set_left_curl(lua_State *L, MP mp, mp_knot p) {
+ double curl = (double) lua_tonumber(L, -1);
+ lua_pop(L, 1);
+ return mp_set_knot_left_curl(mp, p, curl) ? 1 : 0;
+}
+
+static int mplib_aux_set_left_tension(lua_State *L, MP mp, mp_knot p) {
+ double tension = (double) lua_tonumber(L, -1);
+ lua_pop(L, 1);
+ return mp_set_knot_left_tension(mp, p, tension) ? 1 : 0;
+}
+
+static int mplib_aux_set_left_control(lua_State *L, MP mp, mp_knot p) {
+ double x = (double) lua_tonumber(L, -1);
+ double y = 0;
+ lua_pop(L, 1);
+ lua_push_key(left_y);
+ if (lua_rawget(L, -2) == LUA_TNUMBER) {
+ y = (double) lua_tonumber(L, -1);
+ lua_pop(L, 1);
+ return mp_set_knot_left_control(mp, p, x, y) ? 1 : 0;
+ } else {
+ return 0;
+ }
+}
+
+static int mplib_aux_set_right_curl(lua_State *L, MP mp, mp_knot p) {
+ double curl = (double) lua_tonumber(L, -1);
+ lua_pop(L, 1);
+ return mp_set_knot_right_curl(mp, p, curl) ? 1 : 0;
+}
+
+static int mplib_aux_set_right_tension(lua_State *L, MP mp, mp_knot p) {
+ double tension = (double) lua_tonumber(L, -1);
+ lua_pop(L, 1);
+ return mp_set_knot_right_tension(mp, p, tension) ? 1 : 0;
+}
+
+static int mplib_aux_set_right_control(lua_State *L, MP mp, mp_knot p) {
+ double x = (double) lua_tonumber(L, -1);
+ lua_pop(L, 1);
+ lua_push_key(right_y);
+ if (lua_rawget(L, -2) == LUA_TNUMBER) {
+ double y = (double) lua_tonumber(L, -1);
+ lua_pop(L, 1);
+ return mp_set_knot_right_control(mp, p, x, y) ? 1 : 0;
+ } else {
+ return 0;
+ }
+}
+
+static int mplib_aux_with_path(lua_State *L, MP mp, int index, int inject, int multiple)
+{
+ // setbuf(stdout, NULL);
+ if (! mp) {
+ lua_pushboolean(L, 0);
+ lua_pushstring(L, "valid instance expected");
+ return 2;
+ } else if (! lua_istable(L, index) || lua_rawlen(L, index) <= 0) {
+ lua_pushboolean(L, 0);
+ lua_pushstring(L, "non empty table expected");
+ return 2;
+ } else {
+ mp_knot p = NULL;
+ mp_knot q = NULL;
+ mp_knot first = NULL;
+ const char *errormsg = NULL;
+ int cyclic = 0;
+ int curled = 0;
+ int solve = 0;
+ int numpoints = (int) lua_rawlen(L, index);
+ /*tex
+ As a bonus we check for two keys. When an index is negative we come from the
+ callback in which case we definitely cannot check the rest of the arguments.
+ */
+ if (multiple && lua_type(L, index + 1) == LUA_TBOOLEAN) {
+ cyclic = lua_toboolean(L, index + 1);
+ } else {
+ lua_push_key(close);
+ if (lua_rawget(L, index - 1) == LUA_TBOOLEAN) {
+ cyclic = lua_toboolean(L, -1);
+ }
+ lua_pop(L, 1);
+ lua_push_key(cycle); /* wins */
+ if (lua_rawget(L, index - 1) == LUA_TBOOLEAN) {
+ cyclic = lua_toboolean(L, -1);
+ }
+ lua_pop(L, 1);
+ }
+ if (multiple && lua_type(L, index + 2) == LUA_TBOOLEAN) {
+ curled = lua_toboolean(L, index + 2);
+ } else {
+ lua_push_key(curled);
+ if (lua_rawget(L, index - 1) == LUA_TBOOLEAN) {
+ curled = lua_toboolean(L, -1);
+ }
+ lua_pop(L, 1);
+ }
+ /*tex We build up the path. */
+ if (lua_rawgeti(L, index, 1) == LUA_TTABLE) {
+ lua_Unsigned len = lua_rawlen(L, -1);
+ lua_pop(L, 1);
+ if (len >= 2) {
+ /* .. : p1 .. controls a and b .. p2 : { p1 a b } */
+ /* -- : p1 .. { curl 1 } .. { curl 1 } .. p2 : { p1 nil nil } */
+ for (int i = 1; i <= numpoints; i++) {
+ if (lua_rawgeti(L, index, i) == LUA_TTABLE) {
+ double x0, y0;
+ lua_rawgeti(L, -1, 1);
+ lua_rawgeti(L, -2, 2);
+ x0 = lua_tonumber(L, -2);
+ y0 = lua_tonumber(L, -1);
+ q = p;
+ p = mp_append_knot_xy(mp, p, x0, y0); /* makes end point */
+ lua_pop(L, 2);
+ if (p) {
+ double x1, y1, x2, y2;
+ if (curled) {
+ x1 = x0;
+ y1 = y0;
+ x2 = x0;
+ y2 = y0;
+ } else {
+ lua_rawgeti(L, -1, 3);
+ lua_rawgeti(L, -2, 4);
+ lua_rawgeti(L, -3, 5);
+ lua_rawgeti(L, -4, 6);
+ x1 = luaL_optnumber(L, -4, x0);
+ y1 = luaL_optnumber(L, -3, y0);
+ x2 = luaL_optnumber(L, -2, x0);
+ y2 = luaL_optnumber(L, -1, y0);
+ lua_pop(L, 4);
+ }
+ mp_set_knot_left_control(mp, p, x1, y1);
+ mp_set_knot_right_control(mp, p, x2, y2);
+ if (! first) {
+ first = p;
+ }
+ } else {
+ errormsg = "knot creation failure";
+ goto BAD;
+ }
+ }
+ /*tex Up the next item */
+ lua_pop(L, 1);
+ }
+ } else if (len > 0) {
+ errormsg = "messy table";
+ goto BAD;
+ } else {
+ for (int i = 1; i <= numpoints; i++) {
+ if (lua_rawgeti(L, index, i) == LUA_TTABLE) {
+ /* We can probably also use the _xy here. */
+ int left_set = 0;
+ int right_set = 0;
+ double x_coord, y_coord;
+ if (! lua_istable(L, -1)) {
+ errormsg = "wrong argument types";
+ goto BAD;
+ }
+ lua_push_key(x_coord);
+ if (lua_rawget(L, -2) != LUA_TNUMBER) {
+ errormsg = "missing x coordinate";
+ goto BAD;
+ }
+ x_coord = (double) lua_tonumber(L, -1);
+ lua_pop(L, 1);
+ lua_push_key(y_coord);
+ if (lua_rawget(L, -2) != LUA_TNUMBER) {
+ errormsg = "missing y coordinate";
+ goto BAD;
+ }
+ y_coord = (double) lua_tonumber(L, -1);
+ lua_pop(L, 1);
+ /* */
+ q = p;
+ if (q) {
+ /*tex
+ We have to save the right_tension because |mp_append_knot| trashes it,
+ believing that it is as yet uninitialized .. I need to check this.
+ */
+ double saved_tension = mp_number_as_double(mp, p->right_tension);
+ p = mp_append_knot(mp, p, x_coord, y_coord);
+ if (p) {
+ mp_set_knot_right_tension(mp, q, saved_tension);
+ }
+ } else {
+ p = mp_append_knot(mp, p, x_coord, y_coord);
+ }
+ if (p) {
+ errormsg = "knot creation failure";
+ goto BAD;
+ }
+ /* */
+ if (! first) {
+ first = p;
+ }
+ lua_push_key(left_curl);
+ if (lua_rawget(L, -2) != LUA_TNUMBER) {
+ lua_pop(L, 1);
+ } else if (! mplib_aux_set_left_curl(L, mp, p)) {
+ errormsg = "failed to set left curl";
+ goto BAD;
+ } else {
+ left_set = 1;
+ solve = 1;
+ }
+ lua_push_key(left_tension);
+ if (lua_rawget(L, -2) != LUA_TNUMBER) {
+ lua_pop(L, 1);
+ } else if (left_set) {
+ errormsg = "left side already set";
+ goto BAD;
+ } else if (! mplib_aux_set_left_tension(L, mp, p)) {
+ errormsg = "failed to set left tension";
+ goto BAD;
+ } else {
+ left_set = 1;
+ solve = 1;
+ }
+ lua_push_key(left_x);
+ if (lua_rawget(L, -2) != LUA_TNUMBER) {
+ lua_pop(L, 1);
+ } else if (left_set) {
+ errormsg = "left side already set";
+ goto BAD;
+ } else if (! mplib_aux_set_left_control(L, mp, p)) {
+ errormsg = "failed to set left control";
+ goto BAD;
+ }
+ lua_push_key(right_curl);
+ if (lua_rawget(L, -2) != LUA_TNUMBER) {
+ lua_pop(L, 1);
+ } else if (! mplib_aux_set_right_curl(L, mp, p)) {
+ errormsg = "failed to set right curl";
+ goto BAD;
+ } else {
+ right_set = 1;
+ solve = 1;
+ }
+ lua_push_key(right_tension);
+ if (lua_rawget(L, -2) != LUA_TNUMBER) {
+ lua_pop(L,1);
+ } else if (right_set) {
+ errormsg = "right side already set";
+ goto BAD;
+ } else if (! mplib_aux_set_right_tension(L, mp, p)) {
+ errormsg = "failed to set right tension";
+ goto BAD;
+ } else {
+ right_set = 1;
+ solve = 1;
+ }
+ lua_push_key(right_x);
+ if (lua_rawget(L, -2) != LUA_TNUMBER) {
+ lua_pop(L, 1);
+ } else if (right_set) {
+ errormsg = "right side already set";
+ goto BAD;
+ } else if (! mplib_aux_set_right_control(L, mp, p)) {
+ errormsg = "failed to set right control";
+ goto BAD;
+ }
+ lua_push_key(direction_x);
+ if (lua_rawget(L, -2) != LUA_TNUMBER) {
+ lua_pop(L, 1);
+ } else if (! mplib_aux_set_direction(L, mp, p)) {
+ errormsg = "failed to set direction";
+ goto BAD;
+ }
+ }
+ lua_pop(L, 1);
+ }
+ }
+ }
+ if (first && p) {
+ /* not: mp_close_path(mp, p, first); */
+ if (cyclic) {
+ p->right_type = mp_explicit_knot;
+ first->left_type = mp_explicit_knot;
+ } else {
+ /* check this on shapes-001.tex and arrows-001.tex */
+ p->right_type = mp_endpoint_knot;
+ first->left_type = mp_endpoint_knot;
+ }
+ p->next = first;
+ if (inject) {
+ if (solve && ! mp_solve_path(mp, first)) {
+ tex_normal_warning("lua", "failed to solve the path");
+ }
+ mp_push_path_value(mp, first);
+ return 0;
+ } else {
+ /*tex We're finished reading arguments so we squeeze the new values back into the table. */
+ if (! mp_solve_path(mp, first)) {
+ errormsg = "failed to solve the path";
+ } else {
+ /* We replace in the original table .. maybe not a good idea at all. */
+ p = first;
+ for (int i = 1; i <= numpoints; i++) {
+ lua_rawgeti(L, -1, i);
+ lua_push_number_at_key(L, left_x, mp_number_as_double(mp, p->left_x));
+ lua_push_number_at_key(L, left_y, mp_number_as_double(mp, p->left_y));
+ lua_push_number_at_key(L, right_x, mp_number_as_double(mp, p->right_x));
+ lua_push_number_at_key(L, right_y, mp_number_as_double(mp, p->right_y));
+ /*tex This is a bit overkill, wiping \unknown */
+ lua_push_nil_at_key(L, left_tension);
+ lua_push_nil_at_key(L, right_tension);
+ lua_push_nil_at_key(L, left_curl);
+ lua_push_nil_at_key(L, right_curl);
+ lua_push_nil_at_key(L, direction_x);
+ lua_push_nil_at_key(L, direction_y);
+ /*tex \unknown\ till here. */
+ lua_push_svalue_at_key(L, left_type, mplib_values_knot[p->left_type]);
+ lua_push_svalue_at_key(L, right_type, mplib_values_knot[p->right_type]);
+ lua_pop(L, 1);
+ p = p->next;
+ }
+ lua_pushboolean(L, 1);
+ return 1;
+ }
+ }
+ } else {
+ errormsg = "invalid path";
+ }
+ BAD:
+ if (p) {
+ mp_free_path(mp, p);
+ }
+ lua_pushboolean(L, 0);
+ if (errormsg) {
+ lua_pushstring(L, errormsg);
+ return 2;
+ } else {
+ return 1;
+ }
+ }
+}
+
+static int mplib_solvepath(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ return mplib_aux_with_path(L, mp, 2, 0, 1);
+ } else {
+ return 0;
+ }
+}
+
+static int mplib_inject_path(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ return mplib_aux_with_path(L, mp, 2, 1, 1);
+ } else {
+ return 0;
+ }
+}
+
+static int mplib_inject_whatever(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ mplib_aux_inject_whatever(L, mp, 2);
+ }
+ return 0;
+}
+
+/*tex The next methods are for collecting the results from |fig|. */
+
+static int mplib_figure_collect(lua_State *L)
+{
+ struct mp_edge_object **hh = mplib_aux_is_figure(L, 1);
+ if (*hh) {
+ mp_gr_toss_objects(*hh);
+ *hh = NULL;
+ }
+ return 0;
+}
+
+static int mplib_figure_objects(lua_State *L)
+{
+ struct mp_edge_object **hh = mplib_aux_is_figure(L, 1);
+ if (*hh) {
+ int i = 1;
+ struct mp_graphic_object *p = (*hh)->body;
+ lua_Number bendtolerance = mplib_aux_get_bend_tolerance(L, 1);
+ lua_Number movetolerance = mplib_aux_get_move_tolerance(L, 1);
+ lua_newtable(L);
+ while (p) {
+ struct mp_graphic_object **v = lua_newuserdatauv(L, sizeof(struct mp_graphic_object *), 2);
+ *v = p;
+ mplib_aux_set_bend_tolerance(L, bendtolerance);
+ mplib_aux_set_move_tolerance(L, movetolerance);
+ luaL_getmetatable(L, MP_METATABLE_OBJECT);
+ lua_setmetatable(L, -2);
+ lua_rawseti(L, -2, i);
+ i++;
+ p = p->next;
+ }
+ /*tex Prevent a double free: */
+ (*hh)->body = NULL;
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int mplib_figure_stacking(lua_State *L)
+{
+ struct mp_edge_object **hh = mplib_aux_is_figure(L, 1);
+ int stacking = 0; /* This only works when before fetching objects! */
+ if (*hh) {
+ struct mp_graphic_object *p = (*hh)->body;
+ while (p) {
+ if (((mp_shape_object *) p)->stacking) {
+ stacking = 1;
+ break;
+ } else {
+ p = p->next;
+ }
+ }
+ }
+ lua_pushboolean(L, stacking);
+ return 1;
+}
+
+static int mplib_figure_tostring(lua_State *L)
+{
+ struct mp_edge_object **hh = mplib_aux_is_figure(L, 1);
+ if (*hh) {
+ lua_pushfstring(L, "<mp.figure %p>", *hh);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int mplib_figure_width(lua_State *L)
+{
+ struct mp_edge_object **hh = mplib_aux_is_figure(L, 1);
+ if (*hh) {
+ lua_pushnumber(L, (*hh)->width);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int mplib_figure_height(lua_State *L)
+{
+ struct mp_edge_object **hh = mplib_aux_is_figure(L, 1);
+ if (*hh) {
+ lua_pushnumber(L, (*hh)->height);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int mplib_figure_depth(lua_State *L)
+{
+ struct mp_edge_object **hh = mplib_aux_is_figure(L, 1);
+ if (*hh) {
+ lua_pushnumber(L, (*hh)->depth);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int mplib_figure_italic(lua_State *L)
+{
+ struct mp_edge_object **hh = mplib_aux_is_figure(L, 1);
+ if (*hh) {
+ lua_pushnumber(L, (*hh)->italic);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int mplib_figure_charcode(lua_State *L)
+{
+ struct mp_edge_object **hh = mplib_aux_is_figure(L, 1);
+ if (*hh) {
+ lua_pushinteger(L, (lua_Integer) (*hh)->charcode);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int mplib_figure_tolerance(lua_State *L)
+{
+ struct mp_edge_object **hh = mplib_aux_is_figure(L, 1);
+ if (*hh) {
+ lua_pushnumber(L, mplib_aux_get_bend_tolerance(L, 1));
+ lua_pushnumber(L, mplib_aux_get_move_tolerance(L, 1));
+ } else {
+ lua_pushnil(L);
+ lua_pushnil(L);
+ }
+ return 2;
+}
+
+static int mplib_figure_bounds(lua_State *L)
+{
+ struct mp_edge_object **hh = mplib_aux_is_figure(L, 1);
+ lua_createtable(L, 4, 0);
+ lua_push_number_at_index(L, 1, (*hh)->minx);
+ lua_push_number_at_index(L, 2, (*hh)->miny);
+ lua_push_number_at_index(L, 3, (*hh)->maxx);
+ lua_push_number_at_index(L, 4, (*hh)->maxy);
+ return 1;
+}
+
+/*tex The methods for the figure objects plus a few helpers. */
+
+static int mplib_object_collect(lua_State *L)
+{
+ struct mp_graphic_object **hh = mplib_aux_is_gr_object(L, 1);
+ if (*hh) {
+ mp_gr_toss_object(*hh);
+ *hh = NULL;
+ }
+ return 0;
+}
+
+static int mplib_object_tostring(lua_State *L)
+{
+ struct mp_graphic_object **hh = mplib_aux_is_gr_object(L, 1);
+ lua_pushfstring(L, "<mp.object %p>", *hh);
+ return 1;
+}
+
+# define pyth(a,b) (sqrt((a)*(a) + (b)*(b)))
+# define aspect_bound (10.0/65536.0)
+# define aspect_default 1.0
+# define eps 0.0001
+
+static double mplib_aux_coord_range_x(mp_gr_knot h, double dz)
+{
+ double zlo = 0.0;
+ double zhi = 0.0;
+ mp_gr_knot f = h;
+ while (h) {
+ double z = h->x_coord;
+ if (z < zlo) {
+ zlo = z;
+ } else if (z > zhi) {
+ zhi = z;
+ }
+ z = h->right_x;
+ if (z < zlo) {
+ zlo = z;
+ } else if (z > zhi) {
+ zhi = z;
+ }
+ z = h->left_x;
+ if (z < zlo) {
+ zlo = z;
+ } else if (z > zhi) {
+ zhi = z;
+ }
+ h = h->next;
+ if (h == f) {
+ break;
+ }
+ }
+ return (zhi - zlo <= dz) ? aspect_bound : aspect_default;
+}
+
+static double mplib_aux_coord_range_y(mp_gr_knot h, double dz)
+{
+ double zlo = 0.0;
+ double zhi = 0.0;
+ mp_gr_knot f = h;
+ while (h) {
+ double z = h->y_coord;
+ if (z < zlo) {
+ zlo = z;
+ } else if (z > zhi) {
+ zhi = z;
+ }
+ z = h->right_y;
+ if (z < zlo) {
+ zlo = z;
+ } else if (z > zhi) {
+ zhi = z;
+ }
+ z = h->left_y;
+ if (z < zlo) {
+ zlo = z;
+ } else if (z > zhi) {
+ zhi = z;
+ }
+ h = h->next;
+ if (h == f) {
+ break;
+ }
+ }
+ return (zhi - zlo <= dz) ? aspect_bound : aspect_default;
+}
+
+static int mplib_object_peninfo(lua_State *L)
+{
+ struct mp_graphic_object **hh = mplib_aux_is_gr_object(L, -1);
+ if (! *hh) {
+ lua_pushnil(L);
+ return 1;
+ } else {
+ mp_gr_knot p = NULL;
+ mp_gr_knot path = NULL;
+ switch ((*hh)->type) {
+ case mp_fill_code:
+ case mp_stroked_code:
+ p = ((mp_shape_object *) (*hh))->pen;
+ path = ((mp_shape_object *) (*hh))->path;
+ break;
+ }
+ if (! p || ! path) {
+ lua_pushnil(L);
+ return 1;
+ } else {
+ double wx, wy;
+ double rx = 1.0, sx = 0.0, sy = 0.0, ry = 1.0, tx = 0.0, ty = 0.0;
+ double width = 1.0;
+ double x_coord = p->x_coord;
+ double y_coord = p->y_coord;
+ double left_x = p->left_x;
+ double left_y = p->left_y;
+ double right_x = p->right_x;
+ double right_y = p->right_y;
+ if ((right_x == x_coord) && (left_y == y_coord)) {
+ wx = fabs(left_x - x_coord);
+ wy = fabs(right_y - y_coord);
+ } else {
+ wx = pyth(left_x - x_coord, right_x - x_coord);
+ wy = pyth(left_y - y_coord, right_y - y_coord);
+ }
+ if ((wy/mplib_aux_coord_range_x(path, wx)) >= (wx/mplib_aux_coord_range_y(path, wy))) {
+ width = wy;
+ } else {
+ width = wx;
+ }
+ tx = x_coord;
+ ty = y_coord;
+ sx = left_x - tx;
+ rx = left_y - ty;
+ ry = right_x - tx;
+ sy = right_y - ty;
+ if (width != 1.0) {
+ if (width == 0.0) {
+ sx = 1.0;
+ sy = 1.0;
+ } else {
+ rx /= width;
+ ry /= width;
+ sx /= width;
+ sy /= width;
+ }
+ }
+ if (fabs(sx) < eps) {
+ sx = eps;
+ }
+ if (fabs(sy) < eps) {
+ sy = eps;
+ }
+ lua_createtable(L,0,7);
+ lua_push_number_at_key(L, width, width);
+ lua_push_number_at_key(L, rx, rx);
+ lua_push_number_at_key(L, sx, sx);
+ lua_push_number_at_key(L, sy, sy);
+ lua_push_number_at_key(L, ry, ry);
+ lua_push_number_at_key(L, tx, tx);
+ lua_push_number_at_key(L, ty, ty);
+ return 1;
+ }
+ }
+}
+
+/*tex Here is a helper that reports the valid field names of the possible objects. */
+
+static void mplib_aux_mplib_push_fields(lua_State* L, const char **fields)
+{
+ lua_newtable(L);
+ for (lua_Integer i = 0; fields[i]; i++) {
+ lua_pushstring(L, fields[i]); /* not yet an index */
+ lua_rawseti(L, -2, i + 1);
+ }
+}
+
+static int mplib_gettype(lua_State *L)
+{
+ struct mp_graphic_object **hh = mplib_aux_is_gr_object(L, 1);
+ if (*hh) {
+ lua_pushinteger(L, (*hh)->type);
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+static int mplib_getobjecttypes(lua_State* L)
+{
+ lua_createtable(L, 7, 1);
+ lua_push_key_at_index(L, fill, mp_fill_code);
+ lua_push_key_at_index(L, outline, mp_stroked_code);
+ lua_push_key_at_index(L, start_clip, mp_start_clip_code);
+ lua_push_key_at_index(L, start_group, mp_start_group_code);
+ lua_push_key_at_index(L, start_bounds, mp_start_bounds_code);
+ lua_push_key_at_index(L, stop_clip, mp_stop_clip_code);
+ lua_push_key_at_index(L, stop_group, mp_stop_group_code);
+ lua_push_key_at_index(L, stop_bounds, mp_stop_bounds_code);
+ return 1;
+}
+
+static int mplib_getfields(lua_State *L)
+{
+ if (lua_type(L, 1) == LUA_TUSERDATA) {
+ struct mp_graphic_object **hh = mplib_aux_is_gr_object(L, 1);
+ if (*hh) {
+ const char **fields;
+ switch ((*hh)->type) {
+ case mp_fill_code : fields = mplib_fill_fields; break;
+ case mp_stroked_code : fields = mplib_stroked_fields; break;
+ case mp_start_clip_code : fields = mplib_start_clip_fields; break;
+ case mp_start_group_code : fields = mplib_start_group_fields; break;
+ case mp_start_bounds_code: fields = mplib_start_bounds_fields; break;
+ case mp_stop_clip_code : fields = mplib_stop_clip_fields; break;
+ case mp_stop_group_code : fields = mplib_stop_group_fields; break;
+ case mp_stop_bounds_code : fields = mplib_stop_bounds_fields; break;
+ default : fields = mplib_no_fields; break;
+ }
+ mplib_aux_mplib_push_fields(L, fields);
+ } else {
+ lua_pushnil(L);
+ }
+ } else {
+ lua_createtable(L, 8, 0);
+ mplib_aux_mplib_push_fields(L, mplib_fill_fields); lua_rawseti(L, -2, mp_fill_code);
+ mplib_aux_mplib_push_fields(L, mplib_stroked_fields); lua_rawseti(L, -2, mp_stroked_code);
+ mplib_aux_mplib_push_fields(L, mplib_start_clip_fields); lua_rawseti(L, -2, mp_start_clip_code);
+ mplib_aux_mplib_push_fields(L, mplib_start_group_fields); lua_rawseti(L, -2, mp_start_group_code);
+ mplib_aux_mplib_push_fields(L, mplib_start_bounds_fields); lua_rawseti(L, -2, mp_start_bounds_code);
+ mplib_aux_mplib_push_fields(L, mplib_stop_clip_fields); lua_rawseti(L, -2, mp_stop_clip_code);
+ mplib_aux_mplib_push_fields(L, mplib_stop_group_fields); lua_rawseti(L, -2, mp_stop_group_code);
+ mplib_aux_mplib_push_fields(L, mplib_stop_bounds_fields); lua_rawseti(L, -2, mp_stop_bounds_code);
+ }
+ return 1;
+}
+
+static int mplib_push_values(lua_State *L, const char *list[])
+{
+ lua_newtable(L);
+ for (lua_Integer i = 0; list[i]; i++) {
+ lua_pushstring(L, list[i]);
+ lua_rawseti(L, -2, i);
+ }
+ return 1;
+}
+
+static int mplib_getcodes(lua_State *L)
+{
+ return mplib_push_values(L, mplib_codes);
+}
+
+static int mplib_gettypes(lua_State *L)
+{
+ return mplib_push_values(L, mplib_types);
+}
+
+static int mplib_getcolormodels(lua_State *L)
+{
+ return mplib_push_values(L, mplib_colormodels);
+}
+
+static int mplib_getstates(lua_State *L)
+{
+ return mplib_push_values(L, mplib_states);
+}
+
+static int mplib_getcallbackstate(lua_State *L)
+{
+ lua_createtable(L, 0, 5);
+ lua_push_integer_at_key(L, file, mplib_state.file_callbacks);
+ lua_push_integer_at_key(L, text, mplib_state.text_callbacks);
+ lua_push_integer_at_key(L, script, mplib_state.script_callbacks);
+ lua_push_integer_at_key(L, log, mplib_state.log_callbacks);
+ lua_push_integer_at_key(L, overloaded, mplib_state.overload_callbacks);
+ lua_push_integer_at_key(L, error, mplib_state.error_callbacks);
+ lua_push_integer_at_key(L, warning, mplib_state.warning_callbacks);
+ lua_push_integer_at_key(L, count,
+ mplib_state.file_callbacks + mplib_state.text_callbacks
+ + mplib_state.script_callbacks + mplib_state.log_callbacks
+ + mplib_state.overload_callbacks + mplib_state.error_callbacks
+ + mplib_state.warning_callbacks
+ );
+ return 1;
+}
+
+/*tex
+
+ This assumes that the top of the stack is a table or nil already in the case.
+*/
+
+# define mplib_set_color_objects(pq) \
+object_color_model = pq->color_model; \
+object_color_a = pq->color.a_val; \
+object_color_b = pq->color.b_val; \
+object_color_c = pq->color.c_val; \
+object_color_d = pq->color.d_val;
+
+static void mplib_aux_push_color(lua_State *L, struct mp_graphic_object *p)
+{
+ if (p) {
+ int object_color_model;
+ double object_color_a, object_color_b, object_color_c, object_color_d;
+ switch (p->type) {
+ case mp_fill_code:
+ case mp_stroked_code:
+ {
+ mp_shape_object *h = (mp_shape_object *) p;
+ mplib_set_color_objects(h);
+ }
+ break;
+ default:
+ object_color_model = mp_no_model;
+ break;
+ }
+ switch (object_color_model) {
+ case mp_grey_model:
+ lua_createtable(L, 1, 0);
+ lua_push_number_at_index(L, 1, object_color_d);
+ break;
+ case mp_rgb_model:
+ lua_createtable(L, 3, 0);
+ lua_push_number_at_index(L, 1, object_color_a);
+ lua_push_number_at_index(L, 2, object_color_b);
+ lua_push_number_at_index(L, 3, object_color_c);
+ break;
+ case mp_cmyk_model:
+ lua_createtable(L, 4, 0);
+ lua_push_number_at_index(L, 1, object_color_a);
+ lua_push_number_at_index(L, 2, object_color_b);
+ lua_push_number_at_index(L, 3, object_color_c);
+ lua_push_number_at_index(L, 4, object_color_d);
+ break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ } else {
+ lua_pushnil(L);
+ }
+}
+
+/*tex The dash scale is not exported, the field has no external value. */
+
+static void mplib_aux_push_dash(lua_State *L, struct mp_shape_object *h)
+{
+ if (h && h->dash) {
+ mp_dash_object *d = h->dash;
+ lua_newtable(L); /* we could start at size 2 or so */
+ lua_push_number_at_key(L, offset, d->offset);
+ if (d->array) {
+ int i = 0;
+ lua_push_key(dashes);
+ lua_newtable(L);
+ while (*(d->array + i) != -1) {
+ double ds = *(d->array + i);
+ lua_pushnumber(L, ds);
+ i++;
+ lua_rawseti(L, -2, i);
+ }
+ lua_rawset(L, -3);
+ }
+ } else {
+ lua_pushnil(L);
+ }
+}
+
+static void mplib_aux_shape(lua_State *L, const char *s, struct mp_shape_object *h, lua_Number bendtolerance, lua_Number movetolerance)
+{
+ if (lua_key_eq(s, path)) {
+ mplib_aux_push_path(L, h->path, MPLIB_PATH, bendtolerance, movetolerance);
+ } else if (lua_key_eq(s, htap)) {
+ mplib_aux_push_path(L, h->htap, MPLIB_PATH, bendtolerance, movetolerance);
+ } else if (lua_key_eq(s, pen)) {
+ mplib_aux_push_path(L, h->pen, MPLIB_PEN, bendtolerance, movetolerance);
+ /* pushed in the table at the top */
+ mplib_aux_push_pentype(L, h->pen);
+ } else if (lua_key_eq(s, color)) {
+ mplib_aux_push_color(L, (mp_graphic_object *) h);
+ } else if (lua_key_eq(s, linejoin)) {
+ lua_pushnumber(L, (lua_Number) h->linejoin);
+ } else if (lua_key_eq(s, linecap)) {
+ lua_pushnumber(L, (lua_Number) h->linecap);
+ // } else if (lua_key_eq(s, stacking)) {
+ // lua_pushinteger(L, (lua_Integer) h->stacking);
+ } else if (lua_key_eq(s, miterlimit)) {
+ lua_pushnumber(L, h->miterlimit);
+ } else if (lua_key_eq(s, prescript)) {
+ lua_pushlstring(L, h->pre_script, h->pre_length);
+ } else if (lua_key_eq(s, postscript)) {
+ lua_pushlstring(L, h->post_script, h->post_length);
+ } else if (lua_key_eq(s, dash)) {
+ mplib_aux_push_dash(L, h);
+ } else {
+ lua_pushnil(L);
+ }
+}
+
+static void mplib_aux_start(lua_State *L, const char *s, struct mp_start_object *h, lua_Number bendtolerance, lua_Number movetolerance)
+{
+ if (lua_key_eq(s, path)) {
+ mplib_aux_push_path(L, h->path, MPLIB_PATH, bendtolerance, movetolerance);
+ } else if (lua_key_eq(s, prescript)) {
+ lua_pushlstring(L, h->pre_script, h->pre_length);
+ } else if (lua_key_eq(s, postscript)) {
+ lua_pushlstring(L, h->post_script, h->post_length);
+ // } else if (lua_key_eq(s, stacking)) {
+ // lua_pushinteger(L, (lua_Integer) h->stacking);
+ } else {
+ lua_pushnil(L);
+ }
+}
+
+// static void mplib_aux_stop(lua_State *L, const char *s, struct mp_stop_object *h, lua_Number bendtolerance, lua_Number movetolerance)
+// {
+// if (lua_key_eq(s, stacking)) {
+// lua_pushinteger(L, (lua_Integer) h->stacking);
+// } else {
+// lua_pushnil(L);
+// }
+// }
+
+static int mplib_object_index(lua_State *L)
+{
+ struct mp_graphic_object **hh = mplib_aux_is_gr_object(L, 1); /* no need for test */
+ if (*hh) {
+ struct mp_graphic_object *h = *hh;
+ const char *s = lua_tostring(L, 2);
+ /* todo: remove stacking from specific aux */
+ if (lua_key_eq(s, type)) {
+ lua_push_key_by_index(mplib_values_type[h->type]);
+ } else if (lua_key_eq(s, stacking)) {
+ lua_pushinteger(L, (lua_Integer) h->stacking);
+ } else {
+ lua_Number bendtolerance = mplib_aux_get_bend_tolerance(L, 1);
+ lua_Number movetolerance = mplib_aux_get_move_tolerance(L, 1);
+ /* todo: we can use generic casts */
+ switch (h->type) {
+ case mp_fill_code:
+ case mp_stroked_code:
+ mplib_aux_shape(L, s, (mp_shape_object *) h, bendtolerance, movetolerance);
+ break;
+ case mp_start_clip_code:
+ case mp_start_group_code:
+ case mp_start_bounds_code:
+ mplib_aux_start(L, s, (mp_start_object *) h, bendtolerance, movetolerance);
+ break;
+ // case mp_stop_clip_code:
+ // case mp_stop_group_code:
+ // case mp_stop_bounds_code:
+ // mplib_aux_stop_clip(L, s, (mp_stop_object *) h, bendtolerance, movetolerance);
+ // break;
+ default:
+ lua_pushnil(L);
+ break;
+ }
+ }
+ } else {
+ lua_pushnil(L);
+ }
+ return 1;
+}
+
+/* Experiment: mpx, kind, macro, arguments */
+
+static int mplib_expand_tex(lua_State *L)
+{
+ MP mp = mplib_aux_is_mp(L, 1);
+ if (mp) {
+ int kind = lmt_tointeger(L, 2);
+ halfword tail = null;
+ halfword head = lmt_macro_to_tok(L, 3, &tail);
+ if (head) {
+ switch (kind) {
+ case lua_value_none_code:
+ case lua_value_dimension_code:
+ {
+ halfword value = 0;
+ halfword space = tex_get_available_token(space_token);
+ halfword relax = tex_get_available_token(deep_frozen_relax_token);
+ token_link(tail) = space;
+ token_link(space) = relax;
+ tex_begin_inserted_list(head);
+ lmt_error_state.intercept = 1;
+ lmt_error_state.last_intercept = 0;
+ value = tex_scan_dimen(0, 0, 0, 0, NULL);
+ lmt_error_state.intercept = 0;
+ while (cur_tok != deep_frozen_relax_token) {
+ tex_get_token();
+ }
+ if (! lmt_error_state.last_intercept) {
+ mp_push_numeric_value(mp, (double) value * (7200.0/7227.0) / 65536.0);
+ break;
+ } else if (kind == lua_value_none_code) {
+ head = lmt_macro_to_tok(L, 3, &tail);
+ goto TRYAGAIN;
+ } else {
+ // head = lmt_macro_to_tok(L, 3, &tail);
+ // goto JUSTINCASE;
+ lua_pushboolean(L, 0);
+ return 1;
+ }
+ }
+ case lua_value_integer_code:
+ case lua_value_cardinal_code:
+ case lua_value_boolean_code:
+ TRYAGAIN:
+ {
+ halfword value = 0;
+ halfword space = tex_get_available_token(space_token);
+ halfword relax = tex_get_available_token(deep_frozen_relax_token);
+ token_link(tail) = space;
+ token_link(space) = relax;
+ tex_begin_inserted_list(head);
+ lmt_error_state.intercept = 1;
+ lmt_error_state.last_intercept = 0;
+ value = tex_scan_int(0, NULL);
+ lmt_error_state.intercept = 0;
+ while (cur_tok != deep_frozen_relax_token) {
+ tex_get_token();
+ }
+ if (lmt_error_state.last_intercept) {
+ // head = lmt_macro_to_tok(L, 3, &tail);
+ // goto JUSTINCASE;
+ lua_pushboolean(L, 0);
+ return 1;
+ } else if (kind == lua_value_boolean_code) {
+ mp_push_boolean_value(mp, value);
+ break;
+ } else {
+ mp_push_numeric_value(mp, value);
+ break;
+ }
+ }
+ default:
+ // JUSTINCASE:
+ {
+ int len = 0;
+ const char *str = (const char *) lmt_get_expansion(head, &len);
+ mp_push_string_value(mp, str, str ? len : 0); /* len includes \0 */
+ break;
+ }
+ }
+ }
+ }
+ lua_pushboolean(L, 1);
+ return 1;
+}
+
+/* */
+
+static const struct luaL_Reg mplib_instance_metatable[] = {
+ { "__gc", mplib_instance_collect },
+ { "__tostring", mplib_instance_tostring },
+ { NULL, NULL },
+};
+
+static const struct luaL_Reg mplib_figure_metatable[] = {
+ { "__gc", mplib_figure_collect },
+ { "__tostring", mplib_figure_tostring },
+ { "objects", mplib_figure_objects },
+ { "boundingbox", mplib_figure_bounds },
+ { "width", mplib_figure_width },
+ { "height", mplib_figure_height },
+ { "depth", mplib_figure_depth },
+ { "italic", mplib_figure_italic },
+ { "charcode", mplib_figure_charcode },
+ { "tolerance", mplib_figure_tolerance },
+ { "stacking", mplib_figure_stacking },
+ { NULL, NULL },
+};
+
+static const struct luaL_Reg mplib_object_metatable[] = {
+ { "__gc", mplib_object_collect },
+ { "__tostring", mplib_object_tostring },
+ { "__index", mplib_object_index },
+ { NULL, NULL },
+};
+
+static const struct luaL_Reg mplib_instance_functions_list[] = {
+ { "execute", mplib_execute },
+ { "finish", mplib_finish },
+ { "getstatistics", mplib_getstatistics },
+ { "getstatus", mplib_getstatus },
+ { "solvepath", mplib_solvepath },
+ { NULL, NULL },
+};
+
+static const struct luaL_Reg mplib_functions_list[] = {
+ { "new", mplib_new },
+ { "version", mplib_version },
+ /* */
+ { "getfields", mplib_getfields },
+ { "gettype", mplib_gettype },
+ { "gettypes", mplib_gettypes },
+ { "getcolormodels", mplib_getcolormodels },
+ { "getcodes", mplib_getcodes },
+ { "getstates", mplib_getstates },
+ { "getobjecttypes", mplib_getobjecttypes },
+ { "getcallbackstate", mplib_getcallbackstate },
+ /* */
+ { "settolerance", mplib_set_tolerance },
+ { "gettolerance", mplib_get_tolerance },
+ /* indirect */
+ { "execute", mplib_execute },
+ { "finish", mplib_finish },
+ { "showcontext", mplib_showcontext },
+ { "gethashentries", mplib_gethashentries },
+ { "gethashentry", mplib_gethashentry },
+ { "getstatistics", mplib_getstatistics },
+ { "getstatus", mplib_getstatus },
+ { "solvepath", mplib_solvepath },
+ /* helpers */
+ { "peninfo", mplib_object_peninfo },
+ /* scanners */
+ { "scannext", mplib_scan_next },
+ { "scanexpression", mplib_scan_expression },
+ { "scantoken", mplib_scan_token },
+ { "scansymbol", mplib_scan_symbol },
+ { "scanproperty", mplib_scan_property },
+ { "scannumeric", mplib_scan_numeric },
+ { "scannumber", mplib_scan_numeric }, /* bonus */
+ { "scaninteger", mplib_scan_integer },
+ { "scanboolean", mplib_scan_boolean },
+ { "scanstring", mplib_scan_string },
+ { "scanpair", mplib_scan_pair },
+ { "scancolor", mplib_scan_color },
+ { "scancmykcolor", mplib_scan_cmykcolor },
+ { "scantransform", mplib_scan_transform },
+ { "scanpath", mplib_scan_path },
+ { "scanpen", mplib_scan_pen },
+ /* skippers */
+ { "skiptoken", mplib_skip_token },
+ /* injectors */
+ { "injectnumeric", mplib_inject_numeric },
+ { "injectnumber", mplib_inject_numeric }, /* bonus */
+ { "injectinteger", mplib_inject_integer },
+ { "injectboolean", mplib_inject_boolean },
+ { "injectstring", mplib_inject_string },
+ { "injectpair", mplib_inject_pair },
+ { "injectcolor", mplib_inject_color },
+ { "injectcmykcolor", mplib_inject_cmykcolor },
+ { "injecttransform", mplib_inject_transform },
+ { "injectpath", mplib_inject_path },
+ { "injectwhatever", mplib_inject_whatever },
+ /* */
+ { "expandtex", mplib_expand_tex },
+ /* */
+ { NULL, NULL },
+};
+
+int luaopen_mplib(lua_State *L)
+{
+ mplib_aux_initialize_lua(L);
+
+ luaL_newmetatable(L, MP_METATABLE_OBJECT);
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -2, "__index");
+ luaL_setfuncs(L, mplib_object_metatable, 0);
+ luaL_newmetatable(L, MP_METATABLE_FIGURE);
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -2, "__index");
+ luaL_setfuncs(L, mplib_figure_metatable, 0);
+ luaL_newmetatable(L, MP_METATABLE_INSTANCE);
+ lua_pushvalue(L, -1);
+ lua_setfield(L, -2, "__index");
+ luaL_setfuncs(L, mplib_instance_metatable, 0);
+ luaL_setfuncs(L, mplib_instance_functions_list, 0);
+ lua_newtable(L);
+ luaL_setfuncs(L, mplib_functions_list, 0);
+ return 1;
+}