/* Kopete Latex Plugin Copyright (c) 2004 by Duncan Mac-Vicar Prett Copyright (c) 2004-2005 by Olivier Goffart Kopete (c) 2001-2004 by the Kopete developers ************************************************************************* * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ************************************************************************* */ #include #include #include #include #include #include #include #include #include #include #include #include #include "kopetechatsessionmanager.h" #include "kopeteuiglobal.h" #include "latexplugin.h" #include "latexconfig.h" #include "latexguiclient.h" #define ENCODED_IMAGE_MODE 0 typedef KGenericFactory LatexPluginFactory; K_EXPORT_COMPONENT_FACTORY( kopete_latex, LatexPluginFactory( "kopete_latex" ) ) LatexPlugin::LatexPlugin( TQObject *parent, const char *name, const TQStringList &/*args*/ ) : Kopete::Plugin( LatexPluginFactory::instance(), parent, name ) { // kdDebug() << k_funcinfo << endl; if( !s_pluginStatic ) s_pluginStatic = this; mMagickNotFoundShown = false; connect( Kopete::ChatSessionManager::self(), TQ_SIGNAL( aboutToDisplay( Kopete::Message & ) ), TQ_SLOT( slotMessageAboutToShow( Kopete::Message & ) ) ); connect( Kopete::ChatSessionManager::self(), TQ_SIGNAL( aboutToSend(Kopete::Message& ) ), this, TQ_SLOT(slotMessageAboutToSend(Kopete::Message& ) ) ); connect ( this , TQ_SIGNAL( settingsChanged() ) , this , TQ_SLOT( slotSettingsChanged() ) ); connect( Kopete::ChatSessionManager::self(), TQ_SIGNAL( chatSessionCreated( Kopete::ChatSession * ) ), this, TQ_SLOT( slotNewChatSession( Kopete::ChatSession * ) ) ); m_convScript = TDEStandardDirs::findExe("kopete_latexconvert.sh"); slotSettingsChanged(); //Add GUI action to all already existing kmm (if the plugin is launched when kopete already rining) TQValueList sessions = Kopete::ChatSessionManager::self()->sessions(); for (TQValueListIterator it= sessions.begin(); it!=sessions.end() ; ++it) slotNewChatSession( *it ); } LatexPlugin::~LatexPlugin() { s_pluginStatic = 0L; } LatexPlugin* LatexPlugin::plugin() { return s_pluginStatic ; } LatexPlugin* LatexPlugin::s_pluginStatic = 0L; void LatexPlugin::slotNewChatSession( Kopete::ChatSession *KMM ) { new LatexGUIClient( KMM ); } void LatexPlugin::slotMessageAboutToShow( Kopete::Message& msg ) { TQString mMagick = TDEStandardDirs::findExe("convert"); if ( mMagick.isEmpty() ) { // show just once if ( !mMagickNotFoundShown ) { KMessageBox::queuedMessageBox( Kopete::UI::Global::mainWidget(), KMessageBox::Error, i18n("I cannot find the Magick convert program.\nconvert is required to render the Latex formulas.\nPlease go to www.imagemagick.org or to your distribution site and get the right package.") ); mMagickNotFoundShown = true; } // dont try to parse if convert is not installed return; } TQString messageText = msg.plainBody(); if( !messageText.contains("$$")) return; //kdDebug() << k_funcinfo << " Using converter: " << m_convScript << endl; // /\[([^]]).*?\[/$1\]/ // \$\$.+?\$\$ // this searches for $$formula$$ TQRegExp rg("\\$\\$.+\\$\\$"); rg.setMinimal(true); // this searches for [latex]formula[/latex] //TQRegExp rg("\\[([^]\]).*?\\[/$1\\]"); int pos = 0; TQMap replaceMap; while (pos >= 0 && (unsigned int)pos < messageText.length()) { // kdDebug() << k_funcinfo << " searching pos: " << pos << endl; pos = rg.search(messageText, pos); if (pos >= 0 ) { TQString match = rg.cap(0); pos += rg.matchedLength(); TQString formul=match; if(!securityCheck(formul)) continue; TQString fileName=handleLatex(formul.replace("$$","")); // get the image and encode it with base64 #if ENCODED_IMAGE_MODE TQImage renderedImage( fileName ); imagePxWidth = renderedImage.width(); imagePxHeight = renderedImage.height(); if ( !renderedImage.isNull() ) { TQByteArray ba; TQBuffer buffer( ba ); buffer.open( IO_WriteOnly ); renderedImage.save( &buffer, "PNG" ); TQString imageURL = TQString::fromLatin1("data:image/png;base64,%1").arg( KCodecs::base64Encode( ba ) ); replaceMap[match] = imageURL; } #else replaceMap[match] = fileName; #endif } } if(replaceMap.isEmpty()) //we haven't found any latex strings return; messageText= msg.escapedBody(); int imagePxWidth,imagePxHeight; for (TQMap::ConstIterator it = replaceMap.begin(); it != replaceMap.end(); ++it) { TQImage theImage(*it); if(theImage.isNull()) continue; imagePxWidth = theImage.width(); imagePxHeight = theImage.height(); TQString escapedLATEX=TQStyleSheet::escape(it.key()).replace("\"","""); //we need the escape quotes because that string will be in a title="" argument, but not the \n messageText.replace(Kopete::Message::escape(it.key()), " \"" "); } msg.setBody( messageText, Kopete::Message::RichText ); } void LatexPlugin::slotMessageAboutToSend( Kopete::Message& msg) { Q_UNUSED(msg) //disabled because to work correctly, we need to find what special has the gif we can send over MSN #if 0 TDEConfig *config = TDEGlobal::config(); config->setGroup("Latex Plugin"); if(!config->readBoolEntry("ParseOutgoing", false)) return; TQString messageText = msg.plainBody(); if( !messageText.contains("$$")) return; /* if( msg.from()->protocol()->pluginId()!="MSNProtocol" ) return;*/ // this searches for $$formula$$ TQRegExp rg("^\\s*\\$\\$([^$]+)\\$\\$\\s*$"); if( rg.search(messageText) != -1 ) { TQString latexFormula = rg.cap(1); if(!securityCheck( latexFormula )) return; TQString url = handleLatex(latexFormula); if(!url.isNull()) { TQString escapedLATEX= TQStyleSheet::escape(messageText).replace("\"","""); TQString messageText="\"""; msg.setBody( messageText, Kopete::Message::RichText ); } } #endif } TQString LatexPlugin::handleLatex(const TQString &latexFormula) { KTempFile *tempFile=new KTempFile( locateLocal( "tmp", "kopetelatex-" ), ".png" ); tempFile->setAutoDelete(true); m_tempFiles.append(tempFile); m_tempFiles.setAutoDelete(true); TQString fileName = tempFile->name(); TDEProcess p; TQString argumentRes = "-r %1x%2"; TQString argumentOut = "-o %1"; //TQString argumentFormat = "-fgif"; //we uses gif format because MSN only handle gif int hDPI, vDPI; hDPI = LatexConfig::self()->horizontalDPI(); vDPI = LatexConfig::self()->verticalDPI(); p << m_convScript << argumentRes.arg(TQString::number(hDPI), TQString::number(vDPI)) << argumentOut.arg(fileName) /*<< argumentFormat*/ << latexFormula ; kdDebug() << k_funcinfo << " Rendering " << m_convScript << " " << argumentRes.arg(TQString::number(hDPI), TQString::number(vDPI)) << " " << argumentOut.arg(fileName) << endl; // FIXME our sucky sync filter API limitations :-) p.start(TDEProcess::Block); return fileName; } bool LatexPlugin::securityCheck(const TQString &latexFormula) { return !latexFormula.contains(TQRegExp("\\\\(def|let|futurelet|newcommand|renewcomment|else|fi|write|input|include" "|chardef|catcode|makeatletter|noexpand|toksdef|every|errhelp|errorstopmode|scrollmode|nonstopmode|batchmode" "|read|csname|newhelp|relax|afterground|afterassignment|expandafter|noexpand|special|command|loop|repeat|toks" "|output|line|mathcode|name|item|section|mbox|DeclareRobustCommand)[^a-zA-Z]")); } void LatexPlugin::slotSettingsChanged() { LatexConfig::self()->readConfig(); } #include "latexplugin.moc"