lmtghostscript.c /size: 6306 b    last modification: 2024-01-16 10:22
1/*
2    See license.txt in the root of this project.
3*/
4
5# include "luametatex.h"
6# include "lmtoptional.h"
7
8# define GS_ARG_ENCODING_UTF8 1
9
10typedef struct gslib_state_info {
11
12    int         initialized;
13    int         padding;
14    luaL_Buffer outbuffer;
15    luaL_Buffer errbuffer;
16
17    int (*gsapi_new_instance) (
18        void **pinstance,
19        void  *caller_handle
20    );
21
22    void (*gsapi_delete_instance) (
23        void * instance
24    );
25
26    int (*gsapi_set_arg_encoding) (
27        void *instance,
28        int   encoding
29    );
30
31    int (*gsapi_init_with_args) (
32        void        *instance,
33        int          argc,
34        const char **argv
35    );
36
37    int (*gsapi_set_stdio) (
38        void *instance,
39        int (*stdin_fn )(void *caller_handle, char       *buf, int len),
40        int (*stdout_fn)(void *caller_handle, const char *str, int len),
41        int (*stderr_fn)(void *caller_handle, const char *str, int len)
42    );
43
44    /*
45    int (*gsapi_run_string_begin)       (void *instance, int user_errors, int *pexit_code);
46    int (*gsapi_run_string_continue)    (void *instance, const char *str, unsigned int length, int user_errors, int *pexit_code);
47    int (*gsapi_run_string_end)         (void *instance, int user_errors, int *pexit_code);
48    int (*gsapi_run_string_with_length) (void *instance, const char *str, unsigned int length, int user_errors, int *pexit_code);
49    int (*gsapi_run_string)             (void *instance, const char *str, int user_errors, int *pexit_code);
50    int (*gsapi_run_file)               (void *instance, const char *file_name, int user_errors, int *pexit_code);
51    int (*gsapi_exit)                   (void *instance);
52    */
53
54} gslib_state_info;
55
56static gslib_state_info gslib_state = {
57
58    .initialized                  = 0,
59    .padding                      = 0,
60 /* .outbuffer                    = NULL, */
61 /* .errbuffer                    = NULL, */
62
63    .gsapi_new_instance           = NULL,
64    .gsapi_delete_instance        = NULL,
65    .gsapi_set_arg_encoding       = NULL,
66    .gsapi_init_with_args         = NULL,
67    .gsapi_set_stdio              = NULL,
68
69};
70
71static int gslib_initialize(lua_State * L)
72{
73    if (! gslib_state.initialized) {
74        const char *filename = lua_tostring(L, 1);
75        if (filename) {
76
77            lmt_library lib = lmt_library_load(filename);
78
79            gslib_state.gsapi_new_instance     = lmt_library_find(lib, "gsapi_new_instance");
80            gslib_state.gsapi_delete_instance  = lmt_library_find(lib, "gsapi_delete_instance");
81            gslib_state.gsapi_set_arg_encoding = lmt_library_find(lib, "gsapi_set_arg_encoding");
82            gslib_state.gsapi_init_with_args   = lmt_library_find(lib, "gsapi_init_with_args");
83            gslib_state.gsapi_set_stdio        = lmt_library_find(lib, "gsapi_set_stdio");
84
85            gslib_state.initialized = lmt_library_okay(lib);
86        }
87    }
88    lua_pushboolean(L, gslib_state.initialized);
89    return 1;
90}
91
92/* We could have a callback for stdout and error. */
93
94static int gslib_stdout(void * caller_handle, const char *str, int len)
95{
96    (void)caller_handle;
97    luaL_addlstring(&gslib_state.outbuffer, str, len);
98    return len;
99}
100
101static int gslib_stderr(void * caller_handle, const char *str, int len)
102{
103    (void)caller_handle;
104    luaL_addlstring(&gslib_state.errbuffer, str, len);
105    return len;
106}
107
108static int gslib_execute(lua_State * L)
109{
110    if (gslib_state.initialized) {
111        if (lua_type(L, 1) == LUA_TTABLE) {
112            size_t n = (int) lua_rawlen(L, 1);
113            if (n > 0) {
114                void *instance = NULL;
115                int result = gslib_state.gsapi_new_instance(&instance, NULL);
116                if (result >= 0) {
117                    /*tex
118                        Strings are not yet garbage colected. We add some slack. Here MSVC wants
119                        |char**| and gcc wants |const char**| i.e.\ doesn't like a castso we just
120                        accept the less annoying MSVC warning.
121                    */
122                    const char** arguments = malloc((n + 2) * sizeof(char*));
123                    if (arguments) {
124                        int m = 1;
125                        /*tex This is a kind of dummy. */
126                        arguments[0] = "ghostscript";
127                        luaL_buffinit(L, &gslib_state.outbuffer);
128                        luaL_buffinit(L, &gslib_state.errbuffer);
129                        gslib_state.gsapi_set_stdio(instance, NULL, &gslib_stdout, &gslib_stderr);
130                        for (size_t i = 1; i <= n; i++) {
131                            lua_rawgeti(L, 1, i);
132                            switch (lua_type(L, -1)) {
133                            case LUA_TSTRING:
134                            case LUA_TNUMBER:
135                            {
136                                size_t l = 0;
137                                const char *s = lua_tolstring(L, -1, &l);
138                                if (l > 0) {
139                                    arguments[m] = s;
140                                    m += 1;
141                                }
142                            }
143                            break;
144                            }
145                            lua_pop(L, 1);
146                        }
147                        arguments[m] = NULL;
148                        result = gslib_state.gsapi_set_arg_encoding(instance, GS_ARG_ENCODING_UTF8);
149                        result = gslib_state.gsapi_init_with_args(instance, m, arguments);
150                        gslib_state.gsapi_delete_instance(instance);
151                        /* Nothing done with the array cells! No gc done yet anyway. */
152                        free((void *) arguments);
153                        lua_pushboolean(L, result >= 0);
154                        luaL_pushresult(&gslib_state.outbuffer);
155                        luaL_pushresult(&gslib_state.errbuffer);
156                        return 3;
157                    }
158                }
159            }
160        }
161    }
162    return 0;
163}
164
165static struct luaL_Reg gslib_function_list[] = {
166    { "initialize", gslib_initialize },
167    { "execute",    gslib_execute    },
168    { NULL,         NULL             },
169};
170
171int luaopen_ghostscript(lua_State * L)
172{
173    lmt_library_register(L, "ghostscript", gslib_function_list);
174    return 0;
175}
176