/* lmplib.c
Copyright 2006-2009 Taco Hoekwater <
[email protected]>
This file is part of LuaTeX.
LuaTeX is free software; you can redistribute it and/or modify it under
the terms of the GNU Lesser General Public License as published by the Free
Software Foundation; either version 3 of the License, or (at your
option) any later version.
LuaTeX is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
License for more details.
You should have received a copy of the GNU Lesser General Public License along
with LuaTeX; if not, see <
http://www.gnu.org/licenses/>. */
#include <w2c/config.h>
#include <stdlib.h>
#include <string.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <math.h> /* temporary */
#ifndef pdfTeX
# include <lua.h>
# include <lauxlib.h>
# include <lualib.h>
#else
# include <../lua51/lua.h>
# include <../lua51/lauxlib.h>
# include <../lua51/lualib.h>
#endif
#include "mplib.h"
#include "mplibps.h"
#include "mplibsvg.h"
/*@unused@*/ static const char _svn_version[] =
"$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 $";
int luaopen_mplib(lua_State * L); /* forward */
/* metatable identifiers and tests */
#define MPLIB_METATABLE "MPlib"
#define MPLIB_FIG_METATABLE "MPlib.fig"
#define MPLIB_GR_METATABLE "MPlib.gr"
#define is_mp(L,b) (MP *)luaL_checkudata(L,b,MPLIB_METATABLE)
#define is_fig(L,b) (struct mp_edge_object **)luaL_checkudata(L,b,MPLIB_FIG_METATABLE)
#define is_gr_object(L,b) (struct mp_graphic_object **)luaL_checkudata(L,b,MPLIB_GR_METATABLE)
/* Lua string pre-hashing */
#define mplib_init_S(a) do { \
lua_pushliteral(L,#a); \
mplib_##a##_ptr = lua_tostring(L,-1); \
mplib_##a##_index = luaL_ref (L,LUA_REGISTRYINDEX); \
} while (0)
#define mplib_push_S(a) do { \
lua_rawgeti(L,LUA_REGISTRYINDEX,mplib_##a##_index); \
} while (0)
#define mplib_is_S(a,i) (mplib_##a##_ptr==lua_tostring(L,i))
#define mplib_make_S(a) \
static int mplib_##a##_index = 0; \
static const char *mplib_##a##_ptr = NULL
static int mplib_type_Ses[mp_special_code + 1] = { 0 }; /* [0] is not used */
mplib_make_S(fill);
mplib_make_S(outline);
mplib_make_S(text);
mplib_make_S(special);
mplib_make_S(start_bounds);
mplib_make_S(stop_bounds);
mplib_make_S(start_clip);
mplib_make_S(stop_clip);
mplib_make_S(left_type);
mplib_make_S(right_type);
mplib_make_S(x_coord);
mplib_make_S(y_coord);
mplib_make_S(left_x);
mplib_make_S(left_y);
mplib_make_S(right_x);
mplib_make_S(right_y);
mplib_make_S(color);
mplib_make_S(dash);
mplib_make_S(depth);
mplib_make_S(dsize);
mplib_make_S(font);
mplib_make_S(height);
mplib_make_S(htap);
mplib_make_S(linecap);
mplib_make_S(linejoin);
mplib_make_S(miterlimit);
mplib_make_S(path);
mplib_make_S(pen);
mplib_make_S(postscript);
mplib_make_S(prescript);
mplib_make_S(transform);
mplib_make_S(type);
mplib_make_S(width);
static void mplib_init_Ses(lua_State * L)
{
mplib_init_S(fill);
mplib_init_S(outline);
mplib_init_S(text);
mplib_init_S(start_bounds);
mplib_init_S(stop_bounds);
mplib_init_S(start_clip);
mplib_init_S(stop_clip);
mplib_init_S(special);
mplib_type_Ses[mp_fill_code] = mplib_fill_index;
mplib_type_Ses[mp_stroked_code] = mplib_outline_index;
mplib_type_Ses[mp_text_code] = mplib_text_index;
mplib_type_Ses[mp_start_bounds_code] = mplib_start_bounds_index;
mplib_type_Ses[mp_stop_bounds_code] = mplib_stop_bounds_index;
mplib_type_Ses[mp_start_clip_code] = mplib_start_clip_index;
mplib_type_Ses[mp_stop_clip_code] = mplib_stop_clip_index;
mplib_type_Ses[mp_special_code] = mplib_special_index;
mplib_init_S(left_type);
mplib_init_S(right_type);
mplib_init_S(x_coord);
mplib_init_S(y_coord);
mplib_init_S(left_x);
mplib_init_S(left_y);
mplib_init_S(right_x);
mplib_init_S(right_y);
mplib_init_S(color);
mplib_init_S(dash);
mplib_init_S(depth);
mplib_init_S(dsize);
mplib_init_S(font);
mplib_init_S(height);
mplib_init_S(htap);
mplib_init_S(linecap);
mplib_init_S(linejoin);
mplib_init_S(miterlimit);
mplib_init_S(path);
mplib_init_S(pen);
mplib_init_S(postscript);
mplib_init_S(prescript);
mplib_init_S(transform);
mplib_init_S(type);
mplib_init_S(width);
}
/* Enumeration arrays to map MPlib enums to Lua strings */
static const char *interaction_options[] =
{ "unknown", "batch", "nonstop", "scroll", "errorstop", NULL };
static const char *mplib_filetype_names[] =
{ "term", "error", "mp", "log", "ps", "mem", "tfm", "map", "pfb", "enc", NULL };
static const char *knot_type_enum[] =
{ "endpoint", "explicit", "given", "curl", "open", "end_cycle" };
static const char *fill_fields[] =
{ "type", "path", "htap", "pen", "color", "linejoin", "miterlimit",
"prescript", "postscript", NULL };
static const char *stroked_fields[] =
{ "type", "path", "pen", "color", "linejoin", "miterlimit", "linecap",
"dash", "prescript", "postscript", NULL };
static const char *text_fields[] =
{ "type", "text", "dsize", "font", "color", "width", "height", "depth",
"transform", "prescript", "postscript", NULL };
static const char *special_fields[] =
{ "type", "prescript", NULL };
static const char *start_bounds_fields[] =
{ "type", "path", NULL };
static const char *start_clip_fields[] =
{ "type", "path", NULL };
static const char *stop_bounds_fields[] =
{ "type", NULL };
static const char *stop_clip_fields[] =
{ "type", NULL };
static const char *no_fields[] =
{ NULL };
/* The list of supported MPlib options (not all make sense) */
typedef enum {
P_ERROR_LINE, P_MAX_LINE,
P_MAIN_MEMORY, P_HASH_SIZE, P_PARAM_SIZE, P_IN_OPEN, P_RANDOM_SEED,
P_INTERACTION, P_INI_VERSION, P_MEM_NAME, P_JOB_NAME, P_FIND_FILE,
P__SENTINEL } mplib_parm_idx;
typedef struct {
const char *name; /* parameter name */
mplib_parm_idx idx; /* parameter index */
} mplib_parm_struct;
static mplib_parm_struct mplib_parms[] = {
{"error_line", P_ERROR_LINE },
{"print_line", P_MAX_LINE },
{"main_memory", P_MAIN_MEMORY },
{"hash_size", P_HASH_SIZE },
{"param_size", P_PARAM_SIZE },
{"max_in_open", P_IN_OPEN },
{"random_seed", P_RANDOM_SEED },
{"interaction", P_INTERACTION },
{"ini_version", P_INI_VERSION },
{"mem_name", P_MEM_NAME },
{"job_name", P_JOB_NAME },
{"find_file", P_FIND_FILE },
{NULL, P__SENTINEL }
};
/* Start by defining the needed callback routines for the library */
static char *mplib_find_file(MP mp, const char *fname, const char *fmode, int ftype)
{
lua_State *L = (lua_State *)mp_userdata(mp);
lua_checkstack(L, 4);
lua_getfield(L, LUA_REGISTRYINDEX, "mplib_file_finder");
if (lua_isfunction(L, -1)) {
char *s = NULL;
const char *x = NULL;
lua_pushstring(L, fname);
lua_pushstring(L, fmode);
if (ftype >= mp_filetype_text) {
lua_pushnumber(L, (lua_Number)(ftype - mp_filetype_text));
} else {
lua_pushstring(L, mplib_filetype_names[ftype]);
}
if (lua_pcall(L, 3, 1, 0) != 0) {
fprintf(stdout, "Error in mp.find_file: %s\n", lua_tostring(L, -1));
return NULL;
}
x = lua_tostring(L, -1);
if (x != NULL)
s = strdup(x);
lua_pop(L, 1); /* pop the string */
return s;
} else {
lua_pop(L, 1);
}
if (fmode[0] != 'r' || (!access(fname, R_OK)) || ftype) {
return strdup(fname);
}
return NULL;
}
static int mplib_find_file_function(lua_State * L)
{
if (!(lua_isfunction(L, -1) || lua_isnil(L, -1))) {
return 1; /* error */
}
lua_pushstring(L, "mplib_file_finder");
lua_pushvalue(L, -2);
lua_rawset(L, LUA_REGISTRYINDEX);
return 0;
}
#define xfree(A) if ((A)!=NULL) { free((A)); A = NULL; }
static int mplib_new(lua_State * L)
{
MP *mp_ptr;
mp_ptr = lua_newuserdata(L, sizeof(MP *));
if (mp_ptr) {
int i;
struct MP_options *options = mp_options();
options->userdata = (void *) L;
options->noninteractive = 1; /* required ! */
options->find_file = mplib_find_file;
options->print_found_names = 1;
if (lua_type(L, 1) == LUA_TTABLE) {
for (i = 0; mplib_parms[i].name != NULL; i++) {
lua_getfield(L, 1, mplib_parms[i].name);
if (lua_isnil(L, -1)) {
lua_pop(L, 1);
continue; /* skip unset */
}
switch (mplib_parms[i].idx) {
case P_ERROR_LINE:
options->error_line = (int)lua_tointeger(L, -1);
if (options->error_line<60) options->error_line =60;
if (options->error_line>250) options->error_line = 250;
options->half_error_line = (options->error_line/2)+10;
break;
case P_MAX_LINE:
options->max_print_line = (int)lua_tointeger(L, -1);
if (options->max_print_line<60) options->max_print_line = 60;
break;
case P_RANDOM_SEED:
options->random_seed = (int)lua_tointeger(L, -1);
break;
case P_INTERACTION:
options->interaction =
luaL_checkoption(L, -1, "errorstopmode",
interaction_options);
break;
case P_INI_VERSION:
options->ini_version = lua_toboolean(L, -1);
break;
case P_MEM_NAME:
options->mem_name = strdup(lua_tostring(L, -1));
break;
case P_JOB_NAME:
options->job_name = strdup(lua_tostring(L, -1));
break;
case P_FIND_FILE:
if (mplib_find_file_function(L)) { /* error here */
fprintf(stdout,
"Invalid arguments to mp.new({find_file=...})\n");
}
break;
default:
break;
}
lua_pop(L, 1);
}
}
*mp_ptr = mp_initialize(options);
xfree(options->command_line);
xfree(options->mem_name);
free(options);
if (*mp_ptr) {
luaL_getmetatable(L, MPLIB_METATABLE);
lua_setmetatable(L, -2);
return 1;
}
}
lua_pushnil(L);
return 1;
}
static int mplib_collect(lua_State * L)
{
MP *mp_ptr = is_mp(L, 1);
if (*mp_ptr != NULL) {
(void)mp_finish(*mp_ptr);
*mp_ptr = NULL;
}
return 0;
}
static int mplib_tostring(lua_State * L)
{
MP *mp_ptr = is_mp(L, 1);
if (*mp_ptr != NULL) {
(void)lua_pushfstring(L, "<MP %p>", *mp_ptr);
return 1;
}
return 0;
}
static int mplib_wrapresults(lua_State * L, mp_run_data *res, int status)
{
lua_checkstack(L, 5);
lua_newtable(L);
if (res->term_out.used != 0) {
lua_pushlstring(L, res->term_out.data, res->term_out.used);
lua_setfield(L, -2, "term");
}
if (res->error_out.used != 0) {
lua_pushlstring(L, res->error_out.data, res->error_out.used);
lua_setfield(L, -2, "error");
}
if (res->log_out.used != 0) {
lua_pushlstring(L, res->log_out.data, res->log_out.used);
lua_setfield(L, -2, "log");
}
if (res->edges != NULL) {
struct mp_edge_object **v;
struct mp_edge_object *p = res->edges;
int i = 1;
lua_newtable(L);
while (p != NULL) {
v = lua_newuserdata(L, sizeof(struct mp_edge_object *));
*v = p;
luaL_getmetatable(L, MPLIB_FIG_METATABLE);
lua_setmetatable(L, -2);
lua_rawseti(L, -2, i);
i++;
p = p->next;
}
lua_setfield(L, -2, "fig");
res->edges = NULL;
}
lua_pushnumber(L, (lua_Number)status);
lua_setfield(L, -2, "status");
return 1;
}
static int mplib_execute(lua_State * L)
{
MP *mp_ptr;
if (lua_gettop(L)!=2) {
lua_pushnil(L);
return 1;
}
mp_ptr = is_mp(L, 1);
if (*mp_ptr != NULL && lua_isstring(L, 2)) {
size_t l;
char *s = xstrdup(lua_tolstring(L, 2, &l));
int h = mp_execute(*mp_ptr, s, l);
mp_run_data *res = mp_rundata(*mp_ptr);
free(s);
return mplib_wrapresults(L, res, h);
} else {
lua_pushnil(L);
}
return 1;
}
static int mplib_finish(lua_State * L)
{
MP *mp_ptr = is_mp(L, 1);
if (*mp_ptr != NULL) {
int i;
int h = mp_execute(*mp_ptr,NULL,0);
mp_run_data *res = mp_rundata(*mp_ptr);
i = mplib_wrapresults(L, res, h);
(void)mp_finish(*mp_ptr);
*mp_ptr = NULL;
return i;
} else {
lua_pushnil(L);
}
return 1;
}
static int mplib_char_dimension(lua_State * L, int t)
{
MP *mp_ptr = is_mp(L, 1);
if (*mp_ptr != NULL) {
char *fname = xstrdup(luaL_checkstring(L,2));
int charnum = (int)luaL_checkinteger(L,3);
if (charnum<0 || charnum>255) {
lua_pushnumber(L, (lua_Number)0);
} else {
lua_pushnumber(L,(lua_Number)mp_get_char_dimension(*mp_ptr,fname,charnum,t));
}
free(fname);
} else {
lua_pushnumber(L, (lua_Number)0);
}
return 1;
}
static int mplib_charwidth(lua_State * L)
{
return mplib_char_dimension(L, 'w');
}
static int mplib_chardepth(lua_State * L)
{
return mplib_char_dimension(L, 'd');
}
static int mplib_charheight(lua_State * L)
{
return mplib_char_dimension(L, 'h');
}
static int mplib_version(lua_State * L)
{
char *s = mp_metapost_version();
lua_pushstring(L, s);
free(s);
return 1;
}
static int mplib_statistics(lua_State * L)
{
MP *mp_ptr = is_mp(L, 1);
if (*mp_ptr != NULL) {
lua_newtable(L);
lua_pushnumber(L, (lua_Number)mp_memory_usage(*mp_ptr));
lua_setfield(L, -2, "main_memory");
lua_pushnumber(L, (lua_Number)mp_hash_usage(*mp_ptr));
lua_setfield(L, -2, "hash_size");
lua_pushnumber(L, (lua_Number)mp_param_usage(*mp_ptr));
lua_setfield(L, -2, "param_size");
lua_pushnumber(L, (lua_Number)mp_open_usage(*mp_ptr));
lua_setfield(L, -2, "max_in_open");
} else {
lua_pushnil(L);
}
return 1;
}
/* figure methods */
static int mplib_fig_collect(lua_State * L)
{
struct mp_edge_object **hh = is_fig(L, 1);
if (*hh != NULL) {
mp_gr_toss_objects(*hh);
*hh = NULL;
}
return 0;
}
static int mplib_fig_body(lua_State * L)
{
int i = 1;
struct mp_graphic_object **v;
struct mp_graphic_object *p;
struct mp_edge_object **hh = is_fig(L, 1);
lua_newtable(L);
p = (*hh)->body;
while (p != NULL) {
v = lua_newuserdata(L, sizeof(struct mp_graphic_object *));
*v = p;
luaL_getmetatable(L, MPLIB_GR_METATABLE);
lua_setmetatable(L, -2);
lua_rawseti(L, -2, i);
i++;
p = p->next;
}
(*hh)->body = NULL; /* prevent double free */
return 1;
}
static int mplib_fig_copy_body(lua_State * L)
{
int i = 1;
struct mp_graphic_object **v;
struct mp_graphic_object *p;
struct mp_edge_object **hh = is_fig(L, 1);
lua_newtable(L);
p = (*hh)->body;
while (p != NULL) {
v = lua_newuserdata(L, sizeof(struct mp_graphic_object *));
*v = mp_gr_copy_object((*hh)->parent, p);
luaL_getmetatable(L, MPLIB_GR_METATABLE);
lua_setmetatable(L, -2);
lua_rawseti(L, -2, i);
i++;
p = p->next;
}
return 1;
}
static int mplib_fig_tostring(lua_State * L)
{
struct mp_edge_object **hh = is_fig(L, 1);
(void)lua_pushfstring(L, "<figure %p>", *hh);
return 1;
}
static int mplib_fig_postscript(lua_State * L)
{
mp_run_data *res;
struct mp_edge_object **hh = is_fig(L, 1);
int prologues = (int)luaL_optnumber(L, 2, (lua_Number)-1);
int procset = (int)luaL_optnumber(L, 3, (lua_Number)-1);
if (mp_ps_ship_out(*hh, prologues, procset)
&& (res = mp_rundata((*hh)->parent))
&& (res->ps_out.size != 0)) {
lua_pushstring(L, res->ps_out.data);
} else {
lua_pushnil(L);
}
return 1;
}
static int mplib_fig_svg(lua_State * L)
{
mp_run_data *res;
struct mp_edge_object **hh = is_fig(L, 1);
int prologues = (int)luaL_optnumber(L, 2, (lua_Number)-1);
if (mp_svg_ship_out(*hh, prologues)
&& (res = mp_rundata((*hh)->parent))
&& (res->ps_out.size != 0)) {
lua_pushstring(L, res->ps_out.data);
} else {
lua_pushnil(L);
}
return 1;
}
static int mplib_fig_filename(lua_State * L)
{
struct mp_edge_object **hh = is_fig(L, 1);
if (*hh != NULL) {
char *s = (*hh)->filename;
lua_pushstring(L, s);
} else {
lua_pushnil(L);
}
return 1;
}
static int mplib_fig_width(lua_State * L)
{
struct mp_edge_object **hh = is_fig(L, 1);
if (*hh != NULL) {
lua_pushnumber(L, (double) (*hh)->width / 65536.0);
} else {
lua_pushnil(L);
}
return 1;
}
static int mplib_fig_height(lua_State * L)
{
struct mp_edge_object **hh = is_fig(L, 1);
if (*hh != NULL) {
lua_pushnumber(L, (double) (*hh)->height / 65536.0);
} else {
lua_pushnil(L);
}
return 1;
}
static int mplib_fig_depth(lua_State * L)
{
struct mp_edge_object **hh = is_fig(L, 1);
if (*hh != NULL) {
lua_pushnumber(L, (double) (*hh)->depth / 65536.0);
} else {
lua_pushnil(L);
}
return 1;
}
static int mplib_fig_italcorr(lua_State * L)
{
struct mp_edge_object **hh = is_fig(L, 1);
if (*hh != NULL) {
lua_pushnumber(L, (double) (*hh)->ital_corr / 65536.0);
} else {
lua_pushnil(L);
}
return 1;
}
static int mplib_fig_charcode(lua_State * L)
{
struct mp_edge_object **hh = is_fig(L, 1);
if (*hh != NULL) {
lua_pushnumber(L, (lua_Number)(*hh)->charcode);
} else {
lua_pushnil(L);
}
return 1;
}
static int mplib_fig_bb(lua_State * L)
{
struct mp_edge_object **hh = is_fig(L, 1);
lua_newtable(L);
lua_pushnumber(L, (double) (*hh)->minx / 65536.0);
lua_rawseti(L, -2, 1);
lua_pushnumber(L, (double) (*hh)->miny / 65536.0);
lua_rawseti(L, -2, 2);
lua_pushnumber(L, (double) (*hh)->maxx / 65536.0);
lua_rawseti(L, -2, 3);
lua_pushnumber(L, (double) (*hh)->maxy / 65536.0);
lua_rawseti(L, -2, 4);
return 1;
}
/* object methods */
static int mplib_gr_collect(lua_State * L)
{
struct mp_graphic_object **hh = is_gr_object(L, 1);
if (*hh != NULL) {
mp_gr_toss_object(*hh);
*hh = NULL;
}
return 0;
}
static int mplib_gr_tostring(lua_State * L)
{
struct mp_graphic_object **hh = is_gr_object(L, 1);
(void)lua_pushfstring(L, "<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/65536.0)
static double eps = 0.0001;
static double coord_range_x (mp_knot h, double dz) {
double z;
double zlo = 0.0, zhi = 0.0;
mp_knot f = h;
while (h != NULL) {
z = (double)h->x_coord;
if (z < zlo) zlo = z; else if (z > zhi) zhi = z;
z = (double)h->right_x;
if (z < zlo) zlo = z; else if (z > zhi) zhi = z;
z = (double)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 coord_range_y (mp_knot h, double dz) {
double z;
double zlo = 0.0, zhi = 0.0;
mp_knot f = h;
while (h != NULL) {
z = (double)h->y_coord;
if (z < zlo) zlo = z; else if (z > zhi) zhi = z;
z = (double)h->right_y;
if (z < zlo) zlo = z; else if (z > zhi) zhi = z;
z = (double)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_gr_peninfo(lua_State * L) {
double x_coord, y_coord, left_x, left_y, right_x, right_y;
double wx, wy;
double rx = 1.0, sx = 0.0, sy = 0.0, ry = 1.0, tx = 0.0, ty = 0.0;
double divider = 1.0;
double width = 1.0;
mp_knot p = NULL, path = NULL;
struct mp_graphic_object **hh = is_gr_object(L, -1);
if (!*hh) {
lua_pushnil(L);
return 1;
}
if ((*hh)->type == mp_fill_code) {
p = ((mp_fill_object *)(*hh))->pen_p;
path = ((mp_fill_object *)(*hh))->path_p;
} else if ((*hh)->type == mp_stroked_code) {
p = ((mp_stroked_object *)(*hh))->pen_p;
path = ((mp_stroked_object *)(*hh))->path_p;
}
if (p==NULL || path == NULL) {
lua_pushnil(L);
return 1;
}
x_coord = p->x_coord/65536.0;
y_coord = p->y_coord/65536.0;
left_x = p->left_x/65536.0;
left_y = p->left_y/65536.0;
right_x = p->right_x/65536.0;
right_y = p->right_y/65536.0;
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/coord_range_x(path, wx)) >= (wx/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;
divider = sx*sy - rx*ry;
lua_newtable(L);
lua_pushnumber(L,width); lua_setfield(L,-2,"width");
lua_pushnumber(L,rx); lua_setfield(L,-2,"rx");
lua_pushnumber(L,sx); lua_setfield(L,-2,"sx");
lua_pushnumber(L,sy); lua_setfield(L,-2,"sy");
lua_pushnumber(L,ry); lua_setfield(L,-2,"ry");
lua_pushnumber(L,tx); lua_setfield(L,-2,"tx");
lua_pushnumber(L,ty); lua_setfield(L,-2,"ty");
return 1;
}
static int mplib_gr_fields(lua_State * L)
{
const char **fields;
int i;
struct mp_graphic_object **hh = is_gr_object(L, 1);
if (*hh) {
switch ((*hh)->type) {
case mp_fill_code:
fields = fill_fields;
break;
case mp_stroked_code:
fields = stroked_fields;
break;
case mp_text_code:
fields = text_fields;
break;
case mp_special_code:
fields = special_fields;
break;
case mp_start_clip_code:
fields = start_clip_fields;
break;
case mp_start_bounds_code:
fields = start_bounds_fields;
break;
case mp_stop_clip_code:
fields = stop_clip_fields;
break;
case mp_stop_bounds_code:
fields = stop_bounds_fields;
break;
default:
fields = no_fields;
}
lua_newtable(L);
for (i = 0; fields[i] != NULL; i++) {
lua_pushstring(L, fields[i]);
lua_rawseti(L, -2, (i + 1));
}
} else {
lua_pushnil(L);
}
return 1;
}
#define mplib_push_number(L,x) lua_pushnumber(L,(lua_Number)(x)/65536.0)
#define MPLIB_PATH 0
#define MPLIB_PEN 1
static void mplib_push_path(lua_State * L, struct mp_knot_data *h, int is_pen)
{
struct mp_knot_data *p; /* for scanning the path */
int i = 1;
p = h;
if (p != NULL) {
lua_newtable(L);
do {
lua_createtable(L, 0, 6);
if (!is_pen) {
if (p->data.types.left_type != mp_explicit) {
mplib_push_S(left_type);
lua_pushstring(L, knot_type_enum[p->data.types.left_type]);
lua_rawset(L, -3);
}
if (p->data.types.right_type != mp_explicit) {
mplib_push_S(right_type);
lua_pushstring(L, knot_type_enum[p->data.types.right_type]);
lua_rawset(L, -3);
}
}
mplib_push_S(x_coord);
mplib_push_number(L, p->x_coord);
lua_rawset(L, -3);
mplib_push_S(y_coord);
mplib_push_number(L, p->y_coord);
lua_rawset(L, -3);
mplib_push_S(left_x);
mplib_push_number(L, p->left_x);
lua_rawset(L, -3);
mplib_push_S(left_y);
mplib_push_number(L, p->left_y);
lua_rawset(L, -3);
mplib_push_S(right_x);
mplib_push_number(L, p->right_x);
lua_rawset(L, -3);
mplib_push_S(right_y);
mplib_push_number(L, p->right_y);
lua_rawset(L, -3);
lua_rawseti(L, -2, i);
i++;
if (p->data.types.right_type == mp_endpoint) {
return;
}
p = p->next;
} while (p != h);
} else {
lua_pushnil(L);
}
}
/* this assumes that the top of the stack is a table
or nil already in the case
*/
static void mplib_push_pentype(lua_State * L, mp_knot h)
{
mp_knot p; /* for scanning the path */
p = h;
if (p == NULL) {
/* do nothing */
} else if (p == p->next) {
mplib_push_S(type);
lua_pushstring(L, "elliptical");
lua_rawset(L, -3);
} else {
}
}
#define 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_push_color(lua_State * L, struct mp_graphic_object *p)
{
int object_color_model;
int object_color_a, object_color_b, object_color_c, object_color_d;
if (p != NULL) {
if (p->type == mp_fill_code) {
mp_fill_object *h = (mp_fill_object *) p;
set_color_objects(h);
} else if (p->type == mp_stroked_code) {
mp_stroked_object *h = (mp_stroked_object *) p;
set_color_objects(h);
} else {
mp_text_object *h = (mp_text_object *) p;
set_color_objects(h);
}
lua_newtable(L);
if (object_color_model >= mp_grey_model) {
mplib_push_number(L, object_color_a);
lua_rawseti(L, -2, 1);
if (object_color_model >= mp_rgb_model) {
mplib_push_number(L, object_color_b);
lua_rawseti(L, -2, 2);
mplib_push_number(L, object_color_c);
lua_rawseti(L, -2, 3);
if (object_color_model == mp_cmyk_model) {
mplib_push_number(L, object_color_d);
lua_rawseti(L, -2, 4);
}
}
}
} else {
lua_pushnil(L);
}
}
/* the dash scale is not exported, the field has no external value */
static void mplib_push_dash(lua_State * L, struct mp_stroked_object *h)
{
mp_dash_object *d;
double ds;
if (h != NULL && h->dash_p != NULL) {
d = h->dash_p;
lua_newtable(L);
mplib_push_number(L, d->offset);
lua_setfield(L, -2, "offset");
if (d->array != NULL) {
int i = 0;
lua_newtable(L);
while (*(d->array + i) != -1) {
ds = *(d->array + i) / 65536.0;
lua_pushnumber(L, ds);
i++;
lua_rawseti(L, -2, i);
}
lua_setfield(L, -2, "dashes");
}
} else {
lua_pushnil(L);
}
}
static void mplib_push_transform(lua_State * L, struct mp_text_object *h)
{
int i = 1;
if (h != NULL) {
lua_createtable(L, 6, 0);
mplib_push_number(L, h->tx);
lua_rawseti(L, -2, i);
i++;
mplib_push_number(L, h->ty);
lua_rawseti(L, -2, i);
i++;
mplib_push_number(L, h->txx);
lua_rawseti(L, -2, i);
i++;
mplib_push_number(L, h->tyx);
lua_rawseti(L, -2, i);
i++;
mplib_push_number(L, h->txy);
lua_rawseti(L, -2, i);
i++;
mplib_push_number(L, h->tyy);
lua_rawseti(L, -2, i);
i++;
} else {
lua_pushnil(L);
}
}
#define FIELD(A) (mplib_is_S(A,2))
static void mplib_fill(lua_State * L, struct mp_fill_object *h)
{
if (FIELD(path)) {
mplib_push_path(L, h->path_p, MPLIB_PATH);
} else if (FIELD(htap)) {
mplib_push_path(L, h->htap_p, MPLIB_PATH);
} else if (FIELD(pen)) {
mplib_push_path(L, h->pen_p, MPLIB_PEN);
mplib_push_pentype(L, h->pen_p);
} else if (FIELD(color)) {
mplib_push_color(L, (mp_graphic_object *) h);
} else if (FIELD(linejoin)) {
lua_pushnumber(L, (lua_Number)h->ljoin);
} else if (FIELD(miterlimit)) {
mplib_push_number(L, h->miterlim);
} else if (FIELD(prescript)) {
lua_pushstring(L, h->pre_script);
} else if (FIELD(postscript)) {
lua_pushstring(L, h->post_script);
} else {
lua_pushnil(L);
}
}
static void mplib_stroked(lua_State * L, struct mp_stroked_object *h)
{
if (FIELD(path)) {
mplib_push_path(L, h->path_p, MPLIB_PATH);
} else if (FIELD(pen)) {
mplib_push_path(L, h->pen_p, MPLIB_PEN);
mplib_push_pentype(L, h->pen_p);
} else if (FIELD(color)) {
mplib_push_color(L, (mp_graphic_object *) h);
} else if (FIELD(dash)) {
mplib_push_dash(L, h);
} else if (FIELD(linecap)) {
lua_pushnumber(L, (lua_Number)h->lcap);
} else if (FIELD(linejoin)) {
lua_pushnumber(L, (lua_Number)h->ljoin);
} else if (FIELD(miterlimit)) {
mplib_push_number(L, h->miterlim);
} else if (FIELD(prescript)) {
lua_pushstring(L, h->pre_script);
} else if (FIELD(postscript)) {
lua_pushstring(L, h->post_script);
} else {
lua_pushnil(L);
}
}
static void mplib_text(lua_State * L, struct mp_text_object *h)
{
if (FIELD(text)) {
lua_pushstring(L, h->text_p);
} else if (FIELD(dsize)) {
mplib_push_number(L, (h->font_dsize / 16));
} else if (FIELD(font)) {
lua_pushstring(L, h->font_name);
} else if (FIELD(color)) {
mplib_push_color(L, (mp_graphic_object *) h);
} else if (FIELD(width)) {
mplib_push_number(L, h->width);
} else if (FIELD(height)) {
mplib_push_number(L, h->height);
} else if (FIELD(depth)) {
mplib_push_number(L, h->depth);
} else if (FIELD(transform)) {
mplib_push_transform(L, h);
} else if (FIELD(prescript)) {
lua_pushstring(L, h->pre_script);
} else if (FIELD(postscript)) {
lua_pushstring(L, h->post_script);
} else {
lua_pushnil(L);
}
}
static void mplib_special(lua_State * L, struct mp_special_object *h)
{
if (FIELD(prescript)) {
lua_pushstring(L, h->pre_script);
} else {
lua_pushnil(L);
}
}
static void mplib_start_bounds(lua_State * L, struct mp_bounds_object *h)
{
if (FIELD(path)) {
mplib_push_path(L, h->path_p, MPLIB_PATH);
} else {
lua_pushnil(L);
}
}
static void mplib_start_clip(lua_State * L, struct mp_clip_object *h)
{
if (FIELD(path)) {
mplib_push_path(L, h->path_p, MPLIB_PATH);
} else {
lua_pushnil(L);
}
}
static int mplib_gr_index(lua_State * L)
{
struct mp_graphic_object **hh = is_gr_object(L, 1);
if (*hh) {
struct mp_graphic_object *h = *hh;
if (mplib_is_S(type, 2)) {
lua_rawgeti(L, LUA_REGISTRYINDEX, mplib_type_Ses[h->type]);
} else {
switch (h->type) {
case mp_fill_code:
mplib_fill(L, (mp_fill_object *) h);
break;
case mp_stroked_code:
mplib_stroked(L, (mp_stroked_object *) h);
break;
case mp_text_code:
mplib_text(L, (mp_text_object *) h);
break;
case mp_special_code:
mplib_special(L, (mp_special_object *) h);
break;
case mp_start_clip_code:
mplib_start_clip(L, (mp_clip_object *) h);
break;
case mp_start_bounds_code:
mplib_start_bounds(L, (mp_bounds_object *) h);
break;
case mp_stop_clip_code:
case mp_stop_bounds_code:
default:
lua_pushnil(L);
}
}
} else {
lua_pushnil(L);
}
return 1;
}
static const struct luaL_reg mplib_meta[] = {
{"__gc", mplib_collect},
{"__tostring", mplib_tostring},
{NULL, NULL} /* sentinel */
};
static const struct luaL_reg mplib_fig_meta[] = {
{"__gc", mplib_fig_collect},
{"__tostring", mplib_fig_tostring},
{"objects", mplib_fig_body},
{"copy_objects", mplib_fig_copy_body},
{"filename", mplib_fig_filename},
{"postscript", mplib_fig_postscript},
{"svg", mplib_fig_svg},
{"boundingbox", mplib_fig_bb},
{"width", mplib_fig_width},
{"height", mplib_fig_height},
{"depth", mplib_fig_depth},
{"italcorr", mplib_fig_italcorr},
{"charcode", mplib_fig_charcode},
{NULL, NULL} /* sentinel */
};
static const struct luaL_reg mplib_gr_meta[] = {
{"__gc", mplib_gr_collect},
{"__tostring", mplib_gr_tostring},
{"__index", mplib_gr_index},
{NULL, NULL} /* sentinel */
};
static const struct luaL_reg mplib_d[] = {
{"execute", mplib_execute},
{"finish", mplib_finish},
{"char_width", mplib_charwidth},
{"char_height", mplib_charheight},
{"char_depth", mplib_chardepth},
{"statistics", mplib_statistics},
{NULL, NULL} /* sentinel */
};
static const struct luaL_reg mplib_m[] = {
{"new", mplib_new},
{"version", mplib_version},
{"fields", mplib_gr_fields},
{"pen_info", mplib_gr_peninfo},
{NULL, NULL} /* sentinel */
};
int luaopen_mplib(lua_State * L)
{
mplib_init_Ses(L);
luaL_newmetatable(L, MPLIB_GR_METATABLE);
lua_pushvalue(L, -1); /* push metatable */
lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */
luaL_register(L, NULL, mplib_gr_meta); /* object meta methods */
lua_pop(L, 1);
luaL_newmetatable(L, MPLIB_FIG_METATABLE);
lua_pushvalue(L, -1); /* push metatable */
lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */
luaL_register(L, NULL, mplib_fig_meta); /* figure meta methods */
lua_pop(L, 1);
luaL_newmetatable(L, MPLIB_METATABLE);
lua_pushvalue(L, -1); /* push metatable */
lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */
luaL_register(L, NULL, mplib_meta); /* meta methods */
luaL_register(L, NULL, mplib_d); /* dict methods */
luaL_register(L, "mplib", mplib_m); /* module functions */
return 1;
}