User:Mr.Z-man/SVG/source

From MediaWiki.org
Jump to navigation Jump to search

Source code and brief installation instructions

Notes[edit]

Requirements[edit]

  • A Unix-based system
  • A sufficiently recent version of Inkscape (it must support the --shell command line option)
  • Python with python-daemon
  • A C compiler
  • GLib

Installation[edit]

  1. Copy the 2 files below onto your server
  2. Change SOCK_LOCATION in both programs to a different location if desired
  3. Compile the C client program
    If GLib is installed through a package and gcc is used:
    gcc -o/path/to/compiled/program `pkg-config --cflags --libs glib-2.0` filename.c
  4. Set up the python program to run automatically (using cron or a similar utility)
  5. Add the following lines to LocalSettings.php
$wgSVGConverters['inkscape-daemon'] = '/path/to/compiled/program -z -w $width -f $input -e $output';
$wgSVGConverter = 'inkscape-daemon';

Caveats[edit]

  • This is highly experimental and not recommended for a production server.
  • The Python program does not automatically restart if it crashes for any reason.

Python server[edit]

import SocketServer
import subprocess
import os
import stat
import daemon

SOCK_LOCATION = '/usr/tmp/ink.sock'

def main():
	try:
		server = InkscapeServer(SOCK_LOCATION, InkscapeRequestHandler)
		server.serve_forever(poll_interval=0.1)
	finally:
		os.remove(SOCK_LOCATION)

class InkscapeServer(SocketServer.UnixStreamServer):

	def __init__(self, server_address, RequestHandlerClass):
		SocketServer.UnixStreamServer.__init__(self, server_address, RequestHandlerClass)
		self.proc = subprocess.Popen(['inkscape', '--shell'], stderr=subprocess.STDOUT, stdout=subprocess.PIPE, stdin=subprocess.PIPE)		
		readToEnd(self.proc.stdout) # version stuff
		os.chmod(server_address, stat.S_IRWXO|stat.S_IRWXG|stat.S_IRWXU) # chmod 777 /path/to/socket
		self.requestcount = 0

class InkscapeRequestHandler(SocketServer.StreamRequestHandler):
	
	def handle(self):
		args = self.rfile.readline()
		self.server.proc.stdin.write(args)
		ret = readToEnd(self.server.proc.stdout)
		self.wfile.write(ret)
	
	def finish(self): # Inkscape slowly leaks memory, if it leaks too much it gets /really/ slow
		self.server.requestcount += 1
		if self.server.requestcount > 50 or self.server.proc.returncode is not None: # also check if inksccape crashed
			self.server.requestcount = 0
			self.server.proc.terminate()
			self.server.proc = subprocess.Popen(['inkscape', '--shell'], stderr=subprocess.STDOUT, stdout=subprocess.PIPE, stdin=subprocess.PIPE)	
			readToEnd(self.server.proc.stdout)
		
# Since Inkscape stays running, we never get an EOF
def readToEnd(buf):
	ret = ''
	lastchar = ''
	while True:
		char = buf.readline(1)
		if char == '>' and lastchar == '\n':
			break
		ret += char
		lastchar = char
	return ret
		
if __name__ == "__main__":
	with daemon.DaemonContext():
		main()

C client[edit]

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <glib.h>

#define SOCK_LOCATION "/usr/tmp/ink.sock"

int main ( int argc, char * argv[] ) {

	if (!argv[1]) {
		printf("No args given\n");
		exit(1);
	}
	
	int sock, retval, len;	
	char args[5000]; // These should probably use less-arbitrary numbers
	char ret[5000];
	char *arg;
	int returncode = 0;
	
	int i=1;
	while(arg = argv[i]) {
		arg = g_shell_quote(arg);
		strncat(args, arg, strlen(arg));
		strncat(args, " ", 1);
		i++;
	}
	strncat(args, "\n", 1);
	
	struct sockaddr_un remote;
	
	if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
		printf("Socket error\n");
		exit(1);
	}

	remote.sun_family = AF_UNIX;
	strcpy(remote.sun_path, SOCK_LOCATION);
	len = strlen(remote.sun_path) + sizeof(remote.sun_family);
	if (connect(sock, (struct sockaddr *)&remote, len) == -1) {
		printf("Unable to connect to socket\n");
		exit(1);
	}
	
	if (send(sock, args, strlen(args)+1, 0) == -1) {
	    printf("Error during sending of data\n");
	    exit(1);
	}
		
	if ( (retval=recv(sock, ret, 5000, 0)) > 0) {
		ret[retval] = '\0';
		printf(ret);
	} else {
		if (retval < 0)  {
			printf("Error during receiving of data\n");
		} else {
			printf("Server closed connection\n");
		}
		exit(1);
	}
	
	// If inkscape threw an error, set the returncode to 1. Inkscape errors
	// follow a fairly standard format:
	// ** (inkscape:PID): WARNING ** Error message	
	if (strstr(ret, "\n**") != NULL) {
		returncode = 1;
	}

	return returncode;
}