pidgin-latex plugin

Florian Schmidt mista.tapas at gmx.net
Tue Apr 10 20:34:04 EDT 2007


Hi, 

with the help of the #pidgin irc channel i have tried to write a pidgin latex 
plugin [1][2], because the olde one wouldn't compile for me. I have mostly 
succeeded, but there's some problems left..

The plugin (or some part of pidgin) segfaults with bigger equations like i.e.

$$   \begin{pmatrix}      \num{0,88} & \num{0,15} & \num{0,06} \\      
\num{0,06} & \num{0,80} & \num{0,04} \\      \num{0,06} & \num{0,05} & 
\num{0,90}    \end{pmatrix}^2    \cdot    \begin{pmatrix}  100 \\ 0 \\ 0\\ 
\end{pmatrix}    =    \begin{pmatrix} \num{78.7}\\ \num{10.32}\\ 
\num{10.98}\end{pmatrix}$$


Similar to the kopete plugin i use latex and imagemagick to produce the output 
graphics and then use gaim_imgstore_add () to add the produced image so it's 
available in the gtkconv window (i need to filter some "<br>"s and "amp;"s 
from the input buffer provided by pidgin too with the help of sed in the 
shell script).

I'm not 100% sure i have got the search and replace right (C - not the best 
language for text processing). My debugging skills are limited with gtk apps 
(i use the ubuntu gtk/gaim/etc packages, so they kinda lack symbols, too).. 
So if someone would like to look it over (it's only a few pages of C 
altogether)..

As the segfaults seem to depend on the size of the rendered math i am kinda 
stumped though..

Another problem is that right now the pid is not postfixed to the tempfile 
names, so different users on the same machine might conflict (they also need 
to get deleted after being loaded)

It would be nice to have a config dialog, etc.. Patches welcome :)

A cosmetic thing would be to have the latex code get into the clipboard when 
selecting the image in the msg window.. Any hints?

Thanks for any tips and regards,
Flo

[1] http://tapas.affenbande.org/wordpress/?page_id=70
[2] http://tapas.affenbande.org/pidgin-latex.tgz

Here's the [short] code for reference:

/*
    Copyright (C) 2004 Florian Schmidt
    
    This program 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 2.1 of the License, or
    (at your option) any later version.
    
    This program 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 this program; if not, write to the Free Software 
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/


#define GAIM_PLUGINS

#include <glib.h>

#include "notify.h"
#include "plugin.h"
#include "version.h"

#include <unistd.h>
#include <string.h>

#include <stdio.h>

#include <sys/types.h>
#include <sys/wait.h>


int latex_to_image_id (gchar *text)
{
	//printf("latex_to_image %s\n", text);
	gint stdin, stdout, stderr;
	GPid pid;

	gchar *argv[2];
	gchar *argv0 = "pidgin-latex-convert.sh";
	gchar *argv1 = 0;

	GError *error = 0;

	argv[0] = argv0;
	argv[1] = argv1;
	
	if (!g_spawn_async_with_pipes ("/", argv, 0,   G_SPAWN_DO_NOT_REAP_CHILD|
G_SPAWN_SEARCH_PATH, 0, 0, &pid, &stdin, &stdout, &stderr, &error))
	{
		gaim_notify_error (NULL, NULL, "error executing script", NULL);
		return -1;
	}

	char *filedata;
	size_t size;
	int id;

	write (stdin, text, strlen(text));
	close (stdin);
	waitpid (pid, 0, 0);
	//sleep (1);

	g_spawn_close_pid(pid);

        if (!g_file_get_contents("/tmp/pidgin-latex-tmp.jpg", &filedata, 
&size, &error)) {
                gaim_notify_error(NULL, NULL, error->message, NULL);

                g_error_free(error);

                return -1;
        }

	id = gaim_imgstore_add(filedata, size, "pidgin-latex-tmp.jpg");

	return id;
}

static gboolean displaying_msg_cb (GaimAccount *account, const char *who, char 
**buffer, GaimConversation *conv, GaimMessageFlags flags, void *data)
{
	// the plan:
	// 1] iterate over the message to find $$[text]$$ combinations
	//printf ("%s\n", *buffer);
	// 2] for each of these produce an image [by calling pidgin-latex-convert.sh
	//    and feeding teh latex code as stdin. it will write a file
	//    /tmp/pidgin-latex-tmp.jpg
	// 3] load image and aquire index
	// 4] replace occurence in text with <IMG ID=..> tag
	// 5] profit


	gchar *newbuffer = malloc(1);
	strcpy (newbuffer, "");

	gchar *tmpbuffer;

	int length = strlen (*buffer);
	gchar *pos = *buffer;
	gchar *dollarpos1 = *buffer;
	gchar *dollarpos2 = *buffer;
	while (1)
	{
		// find first $$ in rest of string
		dollarpos1 = g_strstr_len (pos, strlen(pos), "$$");

		if (dollarpos1 == NULL)
		{
			//printf ("no dollar\n");
			// copy rest of buffer to new buffer and be done
			tmpbuffer = newbuffer;
			newbuffer = g_strconcat (newbuffer, pos, 0);
			free (tmpbuffer);
			//printf("newbuffer: %s\n", newbuffer);
			break;
		}
		else // a "$$" ha been found
		{
			//printf ("dollar %s\n", dollarpos1);
			// find second $$
			dollarpos2 = g_strstr_len (dollarpos1 + 2, strlen(dollarpos1 + 2), "$$");
			if (dollarpos2 == NULL) // no match
			{
				//printf ("no second dollar\n");
				// copy string and be done
				tmpbuffer = newbuffer;
				newbuffer = g_strconcat (newbuffer, pos, 0);
				free(tmpbuffer);
				//printf("newbuffer: %s\n", newbuffer);

				break;
			}
			else //match
			{
				//printf ("second dollar\n");


				gchar *tmpbuffer2 = (gchar *)malloc(1 + dollarpos1 - pos);
				strncpy (tmpbuffer2, pos, dollarpos1 - pos);
				tmpbuffer2[dollarpos1 - pos] = 0;

				tmpbuffer = newbuffer;
				newbuffer = g_strconcat (newbuffer, tmpbuffer2, 0);
				free(tmpbuffer);
				free(tmpbuffer2);

				//printf("newbuffer: %s\n", newbuffer);

				tmpbuffer = (gchar*)malloc(1 + dollarpos2 - dollarpos1);
				strncpy (tmpbuffer, dollarpos1, 2 + dollarpos2 - dollarpos1);
				tmpbuffer[2 + dollarpos2 - dollarpos1] = 0;


				int id = latex_to_image_id (tmpbuffer);
				if (id == -1)
				{
					//printf("Something went wrong creating the image\n");

					// oh oh, need to cleanup, not leak memory ;)
					return FALSE;
				}

				free(tmpbuffer);

				// ok we have the id now. so let's replace the text by concatenating our 
new buffer with "<IMG ID=" the id and ">"

				gchar *tagbuffer = malloc(100);
				sprintf (tagbuffer, "<IMG ID=%i>", id);

				tmpbuffer = newbuffer;
				newbuffer = g_strconcat (newbuffer, tagbuffer, 0);
				//printf("newbuffer: %s\n", newbuffer);

				free (tmpbuffer);
				free (tagbuffer);

				pos = dollarpos2 + 2;
			}
		}
	}
	tmpbuffer = *buffer;
	*buffer = newbuffer;
	free (tmpbuffer);

	return FALSE;
} 

static gboolean
plugin_load(GaimPlugin *plugin) 
{
    void *conversation = (void *)gaim_gtk_conversations_get_handle();

    gaim_signal_connect(conversation, "displaying-im-msg", plugin, 
GAIM_CALLBACK(displaying_msg_cb), NULL);
    gaim_signal_connect(conversation, "displaying-chat-msg", plugin, 
GAIM_CALLBACK(displaying_msg_cb), NULL);

    return TRUE;
}

//struct GaimGtkPluginUiInfo ui_info;

static GaimPluginInfo info = 
{
    GAIM_PLUGIN_MAGIC,
    GAIM_MAJOR_VERSION,
    GAIM_MINOR_VERSION,
    GAIM_PLUGIN_STANDARD,
    NULL,
    0,
    NULL,
    GAIM_PRIORITY_DEFAULT,

    "pidgin-latex",
    "LaTeX",
    "1.0",

    "Pidgin LaTeX Plugin",
    "A simple plugin for displaying latex math in the chat window",
    "Florian Paul Schmidt (mista.tapas at gmx.net)",
    "http://tapas.affenbande.org",

    plugin_load,
    NULL,
    NULL,

    NULL,
    NULL,
    NULL,
    NULL
};

static void init_plugin(GaimPlugin *plugin) 
{

}

GAIM_INIT_PLUGIN(hello_world, init_plugin, info);



-- 
Palimm Palimm!
http://tapas.affenbande.org



More information about the Devel mailing list