[KLF Application][KLF Tools][KLF Backend][KLF Home]
KLatexFormula Project

src/klfmime.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002  *   file klfmime.cpp
00003  *   This file is part of the KLatexFormula Project.
00004  *   Copyright (C) 2011 by Philippe Faist
00005  *   philippe.faist at bluewin.ch
00006  *                                                                         *
00007  *   This program is free software; you can redistribute it and/or modify  *
00008  *   it under the terms of the GNU General Public License as published by  *
00009  *   the Free Software Foundation; either version 2 of the License, or     *
00010  *   (at your option) any later version.                                   *
00011  *                                                                         *
00012  *   This program is distributed in the hope that it will be useful,       *
00013  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00014  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
00015  *   GNU General Public License for more details.                          *
00016  *                                                                         *
00017  *   You should have received a copy of the GNU General Public License     *
00018  *   along with this program; if not, write to the                         *
00019  *   Free Software Foundation, Inc.,                                       *
00020  *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
00021  ***************************************************************************/
00022 /* $Id: klfmime.cpp 627 2011-04-12 12:36:22Z phfaist $ */
00023 
00024 #include <QDebug>
00025 #include <QApplication>
00026 #include <QMap>
00027 #include <QUrl>
00028 #include <QBuffer>
00029 #include <QDir>
00030 #include <QDomDocument>
00031 #include <QDomNode>
00032 #include <QDomElement>
00033 #include <QPainter>
00034 #include <QTextCodec>
00035 
00036 #include <klfbackend.h>
00037 
00038 #include <klfguiutil.h>
00039 #include "klfconfig.h"
00040 #include "klflib.h"
00041 #include "klfmime.h"
00042 #include "klfmime_p.h"
00043 
00044 #ifdef Q_WS_MAC
00045 #include "macosx/klfmacclipboard.h"
00046 #endif
00047 
00048 #define OPENOFFICE_DRAWING_MIMETYPE "application/x-openoffice-drawing;windows_formatname=\"Drawing Format\""
00049 
00050 #ifndef Q_WS_MAC
00051 // provide empty non-functional function so that it can be called in any
00052 // context (also on linux, win: don't need #ifdef Q_WS_MAC)
00053 inline void __klf_init_the_macpasteboardmime() { }
00054 #endif
00055 
00056 // ---------------------------------------------------------------------
00057 
00058 bool KLFMimeExporter::supportsKey(const QString& key) const
00059 {
00060   bool result =  (keys().indexOf(key) >= 0) ;
00061   klfDbg("key = "<<key<<" ; result="<<result) ;
00062   return result;
00063 }
00064 
00065 // static
00066 KLFMimeExporter * KLFMimeExporter::mimeExporterLookup(const QString& key)
00067 {
00068   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00069   initMimeExporterList();
00070 
00071   int k;
00072   for (k = 0; k < p_mimeExporterList.size(); ++k) {
00073     klfDbg("Testing exporter #"<<k<<": "<<p_mimeExporterList[k]);
00074     klfDbg("\t: "<<p_mimeExporterList[k]->exporterName()) ;
00075     if (p_mimeExporterList[k]->supportsKey(key))
00076       return p_mimeExporterList[k];
00077   }
00078 
00079   // no exporter found.
00080   return NULL;
00081 }
00082 // static
00083 KLFMimeExporter * KLFMimeExporter::mimeExporterLookupByName(const QString& exporter, const QString& key)
00084 {
00085   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00086   initMimeExporterList();
00087 
00088   int k;
00089   for (k = 0; k < p_mimeExporterList.size(); ++k)
00090     if (p_mimeExporterList[k]->exporterName() == exporter) // find the given 'exporter'
00091       if (key.isEmpty() || p_mimeExporterList[k]->supportsKey(key)) // check for 'key' support
00092         return p_mimeExporterList[k];
00093 
00094   // no exporter found.
00095   return NULL;
00096 }
00097 
00098   
00099 // static
00100 QList<KLFMimeExporter *> KLFMimeExporter::mimeExporterList()
00101 {
00102   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00103   initMimeExporterList();
00104   return p_mimeExporterList;
00105 }
00106 
00107 // static
00108 void KLFMimeExporter::registerMimeExporter(KLFMimeExporter *exporter, bool overrides)
00109 {
00110   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00111 
00112   initMimeExporterList();
00113 
00114   KLF_ASSERT_NOT_NULL( exporter ,
00115                        "Cannot register a NULL exporter!",
00116                        return )  ;
00117 
00118   QString ename = exporter->exporterName();
00119   klfDbg("want to register exporter "<<ename<<", making sure no duplicate names...") ;
00120 
00121   // make sure there are no duplicate names
00122   int k;
00123   for (k = 0; k < p_mimeExporterList.size(); ++k) {
00124     klfDbg("making sure p_mimeExporterList["<<k<<"]->exporterName() [="<<p_mimeExporterList[k]->exporterName()<<"]"
00125            <<"  !=  ename [="<<ename<<"]") ;
00126     KLF_ASSERT_CONDITION(p_mimeExporterList[k]->exporterName() != ename,
00127                          "An exporter with same name "<<ename<<" is already registered!",
00128                          return ) ;
00129   }
00130 
00131   klfDbg("registering exporter "<<ename<<", overrides="<<overrides) ;
00132 
00133   if (overrides)
00134     p_mimeExporterList.push_front(exporter);
00135   else
00136     p_mimeExporterList.push_back(exporter);
00137 }
00138 // static
00139 void KLFMimeExporter::unregisterMimeExporter(KLFMimeExporter *exporter)
00140 {
00141   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00142   p_mimeExporterList.removeAll(exporter);
00143 }
00144 
00145 // static
00146 void KLFMimeExporter::initMimeExporterList()
00147 {
00148   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00149   if (p_mimeExporterList.isEmpty()) {
00150     // ensure an instance of KLFMacPasteboardMime object
00151     __klf_init_the_macpasteboardmime();
00152     p_mimeExporterList
00153       << new KLFMimeExporterImage(qApp)
00154       << new KLFMimeExporterUrilist(qApp)
00155       << new KLFMimeExporterHTML(qApp)
00156       << new KLFMimeExporterLibFmts(qApp)
00157       << new KLFMimeExporterGlowImage(qApp)
00158       ;
00159   }
00160 }
00161 
00162 // static
00163 QList<KLFMimeExporter*> KLFMimeExporter::p_mimeExporterList = QList<KLFMimeExporter*>();
00164 
00165 // ---------------------------------------------------------------------
00166 
00167 KLFMimeExportProfile::KLFMimeExportProfile(const QString& pname, const QString& desc,
00168                                            const QList<ExportType>& exporttypes)
00169   : p_profileName(pname), p_description(desc), p_exportTypes(exporttypes)
00170 {
00171 }
00172 
00173 KLFMimeExportProfile::KLFMimeExportProfile(const KLFMimeExportProfile& o)
00174   : p_profileName(o.p_profileName), p_description(o.p_description),
00175     p_exportTypes(o.p_exportTypes)
00176 {
00177 }
00178 
00179 KLFMimeExporter * KLFMimeExportProfile::exporterLookupFor(int k, bool warnNotFound) const
00180 {
00181   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00182 
00183   KLF_ASSERT_CONDITION(k >= 0 && k < p_exportTypes.size(),
00184                        "Index "<<k<<" out of bounds (size="<<p_exportTypes.size()<<")",
00185                        return NULL ) ;
00186 
00187   KLFMimeExporter * exporter = NULL;
00188   if ( ! p_exportTypes[k].exporter.isEmpty() ) {
00189     // lookup the exporter by name, and make sure that it supports the 'mimetype' key
00190     exporter = KLFMimeExporter::mimeExporterLookupByName(p_exportTypes[k].exporter, p_exportTypes[k].mimetype);
00191   } else {
00192     // lookup the exporter by mime-type
00193     exporter = KLFMimeExporter::mimeExporterLookup(p_exportTypes[k].mimetype);
00194   }
00195 
00196   if (warnNotFound)
00197     KLF_ASSERT_NOT_NULL(exporter,
00198                         "Can't find exporter "<<p_exportTypes[k].exporter<<" for export-type #"<<k
00199                         <<"for key "<<p_exportTypes[k].mimetype,   return NULL ) ;
00200   return exporter;
00201 }
00202 
00203 QStringList KLFMimeExportProfile::mimeTypes() const
00204 {
00205   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00206 
00207   QStringList mimetypes;
00208   int k;
00209   for (k = 0; k < p_exportTypes.size(); ++k)
00210     mimetypes << p_exportTypes[k].mimetype;
00211   return mimetypes;
00212 }
00213 
00214 int KLFMimeExportProfile::indexOfMimeType(const QString& mimeType) const
00215 {
00216   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00217 
00218   int k;
00219   for (k = 0; k < p_exportTypes.size(); ++k)
00220     if (p_exportTypes[k].mimetype == mimeType)
00221       return k;
00222   return -1;
00223 }
00224 
00225 QStringList KLFMimeExportProfile::respectiveWinTypes() const
00226 {
00227   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00228 
00229   QStringList wintypes;
00230   int k;
00231   for (k = 0; k < p_exportTypes.size(); ++k)
00232     wintypes << respectiveWinType(k);
00233   return wintypes;
00234 }
00235 QString KLFMimeExportProfile::respectiveWinType(int k) const
00236 {
00237   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ; klfDbg("k="<<k) ;
00238 
00239   KLF_ASSERT_CONDITION(k >= 0 && k < p_exportTypes.size(),
00240                        "Index "<<k<<" out of bounds (size="<<p_exportTypes.size()<<")",
00241                        return QString() ) ;
00242 
00243   if ( ! p_exportTypes[k].wintype.isEmpty() )
00244     return p_exportTypes[k].wintype;
00245 
00246   KLFMimeExporter *exporter = exporterLookupFor(k, true);
00247   if (exporter == NULL)
00248     return QString();
00249 
00250   return exporter->windowsFormatName(p_exportTypes[k].mimetype);
00251 }
00252 
00253 QStringList KLFMimeExportProfile::availableExporterMimeTypes() const
00254 {
00255   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00256 
00257   QStringList oktypes;
00258   int k;
00259   for (k = 0; k < p_exportTypes.size(); ++k) {
00260     if (exporterLookupFor(k, false) != NULL)
00261       oktypes << p_exportTypes[k].mimetype;
00262   }
00263   return oktypes;
00264 }
00265 
00266 
00267 // static
00268 QList<KLFMimeExportProfile> KLFMimeExportProfile::p_exportProfileList = QList<KLFMimeExportProfile>();
00269 
00270 // static
00271 QList<KLFMimeExportProfile> KLFMimeExportProfile::exportProfileList()
00272 {
00273   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00274 
00275   ensureLoadedExportProfileList();
00276   return p_exportProfileList;
00277 }
00278 // static
00279 void KLFMimeExportProfile::addExportProfile(const KLFMimeExportProfile& exportProfile)
00280 {
00281   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00282 
00283   ensureLoadedExportProfileList();
00284   p_exportProfileList.push_front(exportProfile);
00285 }
00286 // static
00287 KLFMimeExportProfile KLFMimeExportProfile::findExportProfile(const QString& pname)
00288 {
00289   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00290 
00291   ensureLoadedExportProfileList();
00292 
00293   int k;
00294   for (k = 0; k < p_exportProfileList.size(); ++k)
00295     if (p_exportProfileList[k].profileName() == pname)
00296       return p_exportProfileList[k];
00297 
00298   // not found, return first (ie. default) export profile
00299   return p_exportProfileList[0];
00300 }
00301 
00302 void KLFMimeExportProfile::ensureLoadedExportProfileList()
00303 {
00304   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00305 
00306   if ( ! p_exportProfileList.isEmpty())
00307     return;
00308 
00309   // read export profile list from XML
00310   
00311   // find correct XML file
00312   QStringList fcandidates;
00313 
00314   QStringList dirs;
00315   dirs << klfconfig.homeConfigDir  + "/conf/export_mime_profiles.d"
00316        << klfconfig.globalShareDir + "/conf/export_mime_profiles.d"
00317        << ":/conf/export_mime_profiles.d";
00318   int j, k;
00319   for (j = 0; j < dirs.size(); ++j) {
00320     // add all files in ...dirs[j].../*.xml
00321     QDir d(dirs[j]);
00322     QStringList entrylist;
00323     entrylist = d.entryList(QStringList()<<QLatin1String("*.xml"), QDir::Files|QDir::Readable, QDir::Name);
00324     for (k = 0; k < entrylist.size(); ++k)
00325       fcandidates << d.filePath(entrylist[k]);
00326   }
00327 
00328   for (k = 0; k < fcandidates.size(); ++k) {
00329     if (QFile::exists(fcandidates[k]))
00330       loadFromXMLFile(fcandidates[k]);
00331   }
00332 
00333   // create a temp exporter on the stack to see which keys it supports, and add all avail formats
00334   // to an extra export profile
00335   QList<ExportType> exporttypes;
00336   KLFMimeExporterImage imgexporter(NULL);
00337   QStringList mimetypes = imgexporter.keys();
00338   for (k = 0; k < mimetypes.size(); ++k) {
00339     if (mimetypes[k].startsWith("image/") || mimetypes[k] == "application/x-qt-image")
00340       exporttypes << ExportType(mimetypes[k], imgexporter.windowsFormatName(mimetypes[k]));
00341   }
00342   KLFMimeExportProfile allimgfmts
00343     = KLFMimeExportProfile("all_image_formats",
00344                            QObject::tr("All Available Image Formats"),
00345                            exporttypes);
00346   p_exportProfileList << allimgfmts;
00347 
00348 }
00349 
00350 
00351 // static, private
00352 void KLFMimeExportProfile::loadFromXMLFile(const QString& fname)
00353 {
00354   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00355 
00356   klfDbg("Loading from file "<<fname<<"; our locale is "<<klfconfig.UI.locale) ;
00357 
00358   QFile file(fname);
00359   if ( ! file.open(QIODevice::ReadOnly) ) {
00360     qWarning()<<KLF_FUNC_NAME<<": Error: Can't open export mime profiles XML file "<<fname<<": "
00361               <<file.errorString()<<"!";
00362     return;
00363   }
00364 
00365   QDomDocument doc("export-profile-list");
00366   QString errMsg; int errLine, errCol;
00367   bool r = doc.setContent(&file, false, &errMsg, &errLine, &errCol);
00368   if (!r) {
00369     qWarning()<<KLF_FUNC_NAME<<": Error parsing file "<<fname<<": "<<errMsg<<" at line "<<errLine<<", col "<<errCol;
00370     return;
00371   }
00372   file.close();
00373 
00374   QDomElement root = doc.documentElement();
00375   if (root.nodeName() != "export-profile-list") {
00376     qWarning("%s: Error parsing XML for export mime profiles from file `%s': Bad root node `%s'.\n",
00377              KLF_FUNC_NAME, qPrintable(fname), qPrintable(root.nodeName()));
00378     return;
00379   }
00380 
00381   QStringList descriptionLangs;
00382 
00383   // read XML file
00384   QDomNode n;
00385   for (n = root.firstChild(); ! n.isNull(); n = n.nextSibling()) {
00386     QDomElement e = n.toElement(); // try to convert the node to an element.
00387     if ( e.isNull() || n.nodeType() != QDomNode::ElementNode )
00388       continue;
00389     if ( e.nodeName() == "add-macosx-type-rules" ) {
00390 #ifdef Q_WS_MAC
00391       __klf_add_macosx_type_rules(fname, e);
00392 #else
00393       klfDbg("Ignoring Mac OS X type rules on non-mac window system") ;
00394 #endif
00395       continue;
00396     }
00397     if ( e.nodeName() != "profile" ) {
00398       qWarning("%s: WARNING in parsing XML \"%s\" : ignoring unexpected tag `%s', expected <profile>.\n",
00399                KLF_FUNC_NAME, qPrintable(fname), qPrintable(e.nodeName()));
00400       continue;
00401     }
00402     // read profile
00403     QString pname = e.attribute("name");
00404 
00405     // note: profile may already exist, we will check for that later.
00406 
00407     klfDbg("Reading profile "<<pname<<" ...") ;
00408     
00409     QString description;
00410     // xml:lang attribute for profile description is obsolete, we use now Qt-linguist translated value...
00411     QString curDescriptionLang;
00412     QList<ExportType> exporttypes;
00413 
00414     QDomNode en;
00415     for (en = e.firstChild(); ! en.isNull(); en = en.nextSibling() ) {
00416       if ( en.isNull() || en.nodeType() != QDomNode::ElementNode )
00417         continue;
00418       QDomElement ee = en.toElement();
00419       if ( en.nodeName() == "description" ) {
00420         // xml:lang attribute for profile description is obsolete, we use now Qt-linguist translated value...
00421         QString lang = ee.hasAttribute("xml:lang") ? ee.attribute("xml:lang") : QString() ;
00422         klfDbg("<description>: lang="<<lang<<"; hasAttribute(xml:lang)="<<ee.hasAttribute("xml:lang")
00423                <<"; current description="<<description<<",lang="<<curDescriptionLang) ;
00424         if (description.isEmpty()) {
00425           // no description yet
00426           if (lang.isEmpty() || lang.startsWith(klfconfig.UI.locale) || klfconfig.UI.locale.startsWith(lang)) {
00427             // correct locale
00428             //      klfDbg("remembering description tag with lang="<<lang);
00429             description = qApp->translate("xmltr_exportprofiles", ee.text().toUtf8().constData(),
00430                                           "[[tag: <description>]]", QCoreApplication::UnicodeUTF8);
00431             // xml:lang attribute for profile description is obsolete, we use now Qt-linguist translated value...
00432             curDescriptionLang = lang;
00433           }
00434           // otherwise skip this tag
00435         } else {
00436           // see if this locale is correct and more specific
00437           if ( (lang.startsWith(klfconfig.UI.locale) || klfconfig.UI.locale.startsWith(lang)) &&
00438                (curDescriptionLang.isEmpty() || lang.startsWith(curDescriptionLang) ) ) {
00439             // then keep it and replace the other
00440             //      klfDbg("remembering description tag with lang="<<lang);
00441             description = ee.text();
00442             curDescriptionLang = lang;
00443           }
00444           // otherwise skip this tag
00445         }
00446         continue;
00447       }
00448       if ( en.nodeName() != "export-type" ) {
00449         qWarning("%s: WARNING in parsing XML '%s': ignoring unexpected tag `%s' in profile `%s'!\n",
00450                  KLF_FUNC_NAME, qPrintable(fname), qPrintable(en.nodeName()), qPrintable(pname));
00451         continue;
00452       }
00453       QDomNodeList mimetypetags = ee.elementsByTagName("mime-type");
00454       if (mimetypetags.size() != 1) {
00455         qWarning()<<KLF_FUNC_NAME<<": in XML file "<<fname<<", profile "<<pname
00456                   <<": exactly ONE <mime-type> tag must be present in each <export-type>...</export-type>.";
00457         continue;
00458       }
00459       QDomNodeList wintypetags = ee.elementsByTagName("windows-type");
00460       if (wintypetags.size() > 1) {
00461         qWarning()<<KLF_FUNC_NAME<<": in XML file "<<fname<<", profile "<<pname
00462                   <<": expecting at most ONE <windows-type> tag in each <export-type>...</export-type>.";
00463         continue;
00464       }
00465       QDomNodeList exporternametags = ee.elementsByTagName("exporter-name");
00466       if (exporternametags.size() > 1) {
00467         qWarning()<<KLF_FUNC_NAME<<": in XML file "<<fname<<", profile "<<pname
00468                   <<": expected at most ONE <exporter-name> tag in each <export-type>...</export-type>.";
00469         continue;
00470       }
00471       QString mimetype = mimetypetags.at(0).toElement().text().trimmed();
00472       QString wintype = QString();
00473       if (wintypetags.size() == 1) {
00474         wintype = wintypetags.at(0).toElement().text().trimmed();
00475       }
00476       QString exportername = QString();
00477       if (exporternametags.size() == 1) {
00478         exportername = exporternametags.at(0).toElement().text().trimmed();
00479       }
00480 
00481       exporttypes << ExportType(mimetype,  wintype,  exportername);
00482     }
00483 
00484     // add this profile
00485     KLFMimeExportProfile profile(pname, description, exporttypes);
00486 
00487     // check if a profile has not already been declared with same name
00488     int kp;
00489     for (kp = 0; kp < p_exportProfileList.size(); ++kp) {
00490       if (p_exportProfileList[kp].profileName() == pname) {
00491         break;
00492       }
00493     }
00494     if (kp == p_exportProfileList.size()) {
00495       // the profile is new
00496       klfDbg("Adding profile "<<pname<<" to mime export profiles") ;
00497       if (pname == "default") {
00498         // prepend default profile, so it's at beginning
00499         p_exportProfileList.prepend(profile);
00500       } else {
00501         p_exportProfileList << profile;
00502         // xml:lang attribute for profile description is obsolete, we use now Qt-linguist translated value...
00503         descriptionLangs << curDescriptionLang;
00504       }
00505     } else {
00506       // profile already exists, append data to it
00507       KLFMimeExportProfile oldp = p_exportProfileList[kp];
00508       // see if this description provides a better translation
00509       if (!description.isEmpty() &&
00510           (descriptionLangs[kp].isEmpty() || curDescriptionLang.startsWith(descriptionLangs[kp]))) {
00511         // keep this description
00512       } else {
00513         description = oldp.description();
00514       }
00515       KLFMimeExportProfile finalp(pname, description,
00516                                   oldp.exportTypes()+exporttypes // concatenate lists
00517                                   );
00518       p_exportProfileList[kp] = finalp;
00519     }
00520   }
00521 }
00522 
00523 
00524 // ---------------------------------------------------------------------
00525 
00526 
00527 
00528 
00529 KLFMimeData::KLFMimeData(const QString& exportProfile, const KLFBackend::klfOutput& output)
00530   : QMimeData(), pExportProfile(KLFMimeExportProfile::findExportProfile(exportProfile))
00531 {
00532   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00533 
00534   // ensure an instance of KLFMacPasteboardMime object
00535   __klf_init_the_macpasteboardmime();
00536 
00537   pOutput = output;
00538 
00539   set_possible_qt_image_data();
00540 }
00541 KLFMimeData::~KLFMimeData()
00542 {
00543   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00544 }
00545 
00546 // private
00547 void KLFMimeData::set_possible_qt_image_data()
00548 {
00549   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00550 
00551   // Handle platform-specific default image type with 'application/x-qt-image' and setImage()
00552   int index;
00553   if ( (index=pExportProfile.indexOfMimeType(QLatin1String("application/x-qt-image"))) >= 0) {
00554     // set the image data form the exporter
00555     KLFMimeExporter * exporter = pExportProfile.exporterLookupFor(index);
00556     if (exporter != NULL) {
00557       QByteArray img_data = exporter->data(QLatin1String("application/x-qt-image"), pOutput);
00558       QImage img;
00559       QList<QByteArray> try_formats = QList<QByteArray>() << "PNG" << "JPEG" << "BMP";
00560       int k;
00561       for (k = 0; k < try_formats.size() && img.isNull(); ++k)
00562         img.loadFromData(img_data, try_formats[k].constData());
00563       KLF_ASSERT_CONDITION( ! img.isNull() ,
00564                             "Can't get image for application/x-qt-image for profile "<<pExportProfile.profileName(),
00565                             return; )  ;
00566       setImageData(img);
00567     }
00568   }
00569 }
00570 
00571 QStringList KLFMimeData::formats() const
00572 {
00573   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00574 
00575   QStringList fmts = pExportProfile.availableExporterMimeTypes();
00576   if (fmts.contains("application/x-qt-image")) {
00577     if (pQtOwnedFormats.size() == 0)
00578       pQtOwnedFormats = QMimeData::formats();
00579     fmts << pQtOwnedFormats;
00580   }
00581 
00582   klfDbg("format list: "<<fmts) ;
00583   return fmts;
00584 }
00585 
00586 QVariant KLFMimeData::retrieveData(const QString& mimetype, QVariant::Type type) const
00587 {
00588   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00589 
00590   if (mimetype == QLatin1String("application/x-qt-image") ||
00591       pQtOwnedFormats.contains(mimetype))
00592     return QMimeData::retrieveData(mimetype, type);
00593 
00594   int index = pExportProfile.indexOfMimeType(mimetype);
00595   if (index < 0) {
00596     // do not treat this as an error since on windows we seem to have requests for 'text/uri-list' even
00597     // if that mime type is not returned by formats()
00598     klfDbg("Can't find mime-type "<<mimetype<<" in export profile "<<pExportProfile.profileName()
00599            <<" ?!?");
00600     return QVariant();
00601   }
00602 
00603   klfDbg("exporting "<<mimetype<<" ...");
00604   KLFMimeExporter *exporter = pExportProfile.exporterLookupFor(index);
00605 
00606   KLF_ASSERT_NOT_NULL(exporter,
00607                       "Can't find an exporter for mime-type "<<mimetype<<"." ,
00608                       return QVariant(); )  ;
00609   
00610   // get the data
00611   QByteArray data = exporter->data(mimetype, pOutput);
00612   
00613   klfDbg("exporting mimetype "<<mimetype<<": data length is "<<data.size());
00614   
00615   return QVariant::fromValue<QByteArray>(data);
00616 }
00617 
00618 
00619 
00620 
00621 // ---------------------------------------------------------------------
00622 
00623 static QMap<QString,QByteArray> get_qt_image_formats()
00624 {
00625   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00626 
00627   QMap<QString,QByteArray> imageFormats;
00628   // get qt's image formats
00629   QList<QByteArray> qtimgfmts = QImageWriter::supportedImageFormats();
00630   int k;
00631   for (k = 0; k < qtimgfmts.size(); ++k) {
00632     if ( imageFormats.key(qtimgfmts[k]).isEmpty() ) {
00633       QString mime = QString::fromLatin1("image/")+QString::fromLatin1(qtimgfmts[k]).toLower();
00634       imageFormats[mime] = qtimgfmts[k];
00635     }
00636   }
00637   return imageFormats;
00638 }
00639 
00640 QMap<QString,QByteArray> KLFMimeExporterImage::imageFormats = QMap<QString,QByteArray>();
00641 
00642 QStringList KLFMimeExporterImage::keys() const
00643 {
00644   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00645 
00646   // image formats that are always supported. Qt image formats are added too.
00647   static QStringList staticKeys
00648     = QStringList() << "image/png" << "image/eps" << "application/eps" << "application/postscript"
00649                     << OPENOFFICE_DRAWING_MIMETYPE << "application/x-qt-image"
00650                     // add duplicate for png, see below
00651                     << "image/x-win-png-office-art";
00652 
00653   if (imageFormats.isEmpty()) {
00654     // populate qt's image formats
00655     QMap<QString,QByteArray> ifmts = get_qt_image_formats();
00656     QMap<QString,QByteArray>::iterator it;
00657     for (it = ifmts.begin(); it != ifmts.end(); ++it) {
00658       QString mime = it.key();
00659       QByteArray qtfmt = it.value();
00660       // add this image format, if not already provided by staticKeys
00661       if (staticKeys.indexOf(mime) == -1)
00662         imageFormats[mime] = qtfmt;
00663       // add duplicate mime types for some formats, to be able to specify multiple windows format
00664       // names for them, eg. "Bitmap" and "Windows Bitmap" :
00665       if (mime == "image/bmp") {
00666         imageFormats["image/x-win-bmp"] = qtfmt;
00667       } else if (mime == "image/jpeg") {
00668         imageFormats["image/x-win-jfif"] = qtfmt;
00669         imageFormats["image/x-win-jfif-office-art"] = qtfmt;
00670       } else if (mime == "image/png") {
00671         imageFormats["image/x-win-png-office-art"] = qtfmt;
00672       }
00673     }
00674   }
00675 
00676   QStringList keys = staticKeys;
00677 
00678   if (!klfconfig.BackendSettings.execEpstopdf.isEmpty())
00679     keys <<"application/pdf"; // add PDF only if we have PDF
00680 
00681   keys << imageFormats.keys();
00682 
00683   return keys;
00684 }
00685 
00686 QString KLFMimeExporterImage::windowsFormatName(const QString& mime) const
00687 {
00688   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00689 
00690   QString wtype;
00691   if (mime == "application/pdf")
00692     return "PDF";
00693   else if (mime == "application/eps")
00694     return "Encapsulated PostScript";
00695   else if (mime == "image/png")
00696     return "PNG";
00697   else if (mime == "image/jpg" || mime == "image/jpeg")
00698     // standards should only allow image/jpeg, but just in case we treat also erroneous "image/jpg"
00699     return "JPEG";
00700   else if (mime == "image/x-win-jfif")
00701     return "JFIF";
00702   else if (mime == "image/x-win-jfif-office-art")
00703     return "JFIF+Office Art"; // for Ms Office
00704   else if (mime == "image/x-win-png-office-art")
00705     return "PNG+Office Art"; // for Ms Office
00706   else if (mime == "image/bmp")
00707     return "Bitmap";
00708   else if (mime == "image/x-win-bmp")
00709     return "Windows Bitmap";
00710   else if (mime == OPENOFFICE_DRAWING_MIMETYPE)
00711     return "Drawing Format";
00712   else if (mime == "application/x-qt-image")
00713     return mime; // let Qt translate this one
00714 
00715   return mime;
00716 }
00717 
00718 QByteArray klf_openoffice_drawing(const KLFBackend::klfOutput& klfoutput);
00719 
00720 QByteArray KLFMimeExporterImage::data(const QString& keymime, const KLFBackend::klfOutput& klfoutput)
00721 {
00722   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00723 
00724   QString key = keymime;
00725   klfDbg("key="<<key);
00726 
00727   if (key == "image/png")
00728     return klfoutput.pngdata;
00729   if (key == "image/eps" || key == "application/eps" || key == "application/postscript")
00730     return klfoutput.epsdata;
00731   if (key == "application/pdf") {
00732 #ifdef KLF_DEBUG
00733     if (klfoutput.pdfdata.isEmpty())
00734       klfDbg("---warning: don't have PDF data ---") ;
00735 #endif
00736     return klfoutput.pdfdata;
00737   }
00738   if (key == OPENOFFICE_DRAWING_MIMETYPE)
00739     return klf_openoffice_drawing(klfoutput);
00740   if (key == "application/x-qt-image")
00741     return klfoutput.pngdata;
00742 
00743   // rely on qt's image saving routines for other formats
00744   klfDbg("Will use Qt's image format exporting");
00745   
00746   if ( ! imageFormats.contains(key) )
00747     return QByteArray();
00748 
00749   QByteArray imgdata;
00750   QBuffer imgdatawriter(&imgdata);
00751   imgdatawriter.open(QIODevice::WriteOnly);
00752   klfoutput.result.save(&imgdatawriter, imageFormats[key]);
00753   imgdatawriter.close();
00754 
00755   klfDbg("got data: size="<<imgdata.size());
00756   return imgdata;
00757 }
00758 
00759 
00760 
00761 // ---------------------------------------------------------------------
00762 
00763 QMap<qint64,QString> KLFMimeExporterUrilist::tempFilesForImageCacheKey = QMap<qint64,QString>();
00764 
00765 QStringList KLFMimeExporterUrilist::keys() const
00766 {
00767   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00768   return QStringList() << "text/x-moz-url" << "text/uri-list";
00769 }
00770 
00771 // static
00772 QString KLFMimeExporterUrilist::tempFileForOutput(const KLFBackend::klfOutput& output)
00773 {
00774   qint64 imgcachekey = output.result.cacheKey();
00775 
00776   QString tempfilename;
00777 
00778   if (tempFilesForImageCacheKey.contains(imgcachekey)) {
00779     tempfilename = tempFilesForImageCacheKey[imgcachekey];
00780   } else {
00781     QString templ = klfconfig.BackendSettings.tempDir +
00782       QString("/klf_%1_XXXXXX.png").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm"));
00783     QTemporaryFile *tempfile = new QTemporaryFile(templ, qApp);
00784     tempfile->setAutoRemove(true); // will be deleted when klatexformula exists (= qApp destroyed)
00785     if (tempfile->open() == false) {
00786       qWarning("Can't open temp png file for mimetype text/uri-list: template is %s",
00787                qPrintable(templ));
00788       return QByteArray();
00789     } else {
00790       QString errStr;
00791       bool res = KLFBackend::saveOutputToFile(output, tempfile->fileName(), "PNG", &errStr);
00792       if (!res) {
00793         qWarning()<<KLF_FUNC_NAME<<": Can't save to temp file "<<tempfile->fileName()<<": "<<errStr;
00794       } else {
00795         tempfilename = tempfile->fileName();
00796         tempfile->write(output.pngdata);
00797         tempfile->close();
00798         // cache this temp file for other formats' use or other QMimeData instantiation...
00799         tempFilesForImageCacheKey[imgcachekey] = tempfilename;
00800       }
00801     }
00802   }
00803 
00804   return tempfilename;
00805 }
00806 
00807 QByteArray KLFMimeExporterUrilist::data(const QString& key, const KLFBackend::klfOutput& output)
00808 {
00809   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00810 
00811   Q_UNUSED(key) ;
00812   klfDbg("key="<<key) ;
00813 
00814   QString tempfilename = tempFileForOutput(output);
00815   QByteArray urilist = (QUrl::fromLocalFile(tempfilename).toString()+QLatin1String("\n")).toLatin1();
00816   return urilist;
00817 }
00818 
00819 QString KLFMimeExporterUrilist::windowsFormatName(const QString& mime) const
00820 {
00821   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00822 
00823   if (mime == "text/x-moz-url")
00824     return "FileName";
00825   return mime;
00826 }
00827 
00828 
00829 
00830 // -----------------------------------
00831 
00832 static QString toAttrTextS(const QString& sbase)
00833 {
00834   QString s = sbase; // we need a non-const string to .replace() on
00835   klfDbg("s="<<s);
00836   QRegExp replaceCharsRX("([^a-zA-Z0-9/ ._-])");
00837   int pos = 0;
00838   while ((pos = replaceCharsRX.indexIn(s, pos)) != -1) {
00839     QString entity = "&#x"+QString::number(replaceCharsRX.cap(1)[0].unicode(), 16).toUpper()+";" ;
00840     klfDbg("replacing char at pos="<<pos<<" by entity="<<entity<<": s(pos...pos+5)="<<s.mid(pos,5));
00841     s.replace(pos, replaceCharsRX.matchedLength(), entity);
00842     pos += entity.length();
00843   }
00844   klfDbg("final string: "<<s);
00845   return s;
00846 
00847   //  QString s2 = sbase;
00848   //  return s2.replace("&", "&amp;").replace("\"", "&quot;").replace("'", "&apos;").replace("<", "&lt")
00849   //    .replace(">", "&gt;").replace("\r", "&#xD;").replace("\t", "&#x9;").replace("\n", "&#xA;")
00850   //    .replace("!", "&#x21;").toUtf8();
00851 }
00852 
00853 static QByteArray toAttrText(const QString& sbase)
00854 {
00855   return toAttrTextS(sbase).toUtf8();
00856 }
00857 
00858 QStringList KLFMimeExporterHTML::keys() const
00859 {
00860   return QStringList() << QLatin1String("text/html");
00861 }
00862 
00863 QByteArray KLFMimeExporterHTML::data(const QString& key, const KLFBackend::klfOutput& klfoutput)
00864 {
00865   if (key != QLatin1String("text/html")) {
00866     qWarning()<<KLF_FUNC_NAME<<": key="<<key<<" is not \"text/html\"";
00867     return QByteArray();
00868   }
00869 
00870   QString fname = KLFMimeExporterUrilist::tempFileForOutput(klfoutput);
00871 
00872   QSize imgsize = klfoutput.result.size();
00873   int imgDpi = klfoutput.input.dpi;
00874   int dispDpi = 100;
00875 
00876   QString latex = klfoutput.input.latex;
00877   // remove initial comments from latex code...
00878   QStringList latexlines = latex.split("\n");
00879   while (latexlines.size() && QRegExp("\\s*\\%.*").exactMatch(latexlines[0]))
00880     latexlines.removeAt(0);
00881   latex = latexlines.join("\n");
00882 
00883   QString fn = toAttrTextS(fname);
00884   QString l = toAttrTextS(latex);
00885   fn.replace("\"", "&#34;");
00886   l.replace("\"", "&#34;");
00887   QString w = QString::number((int)(1.5 * imgsize.width() * dispDpi/imgDpi));
00888   QString h = QString::number((int)(1.5 * imgsize.height() * dispDpi/imgDpi));
00889   QString win = QString::number(1.5 * imgsize.width() / imgDpi);
00890   QString hin = QString::number(1.5 * imgsize.height() / imgDpi);
00891 
00892   QString html =
00893     QString("<img src=\"file://%1\" alt=\"%2\" title=\"%3\" " //"width=\"%4\" height=\"%5\" "
00894             " style=\"width: %4in; height: %5in; vertical-align: middle;\">")
00895     .arg(fn, l, l, win, hin);
00896 
00897 #ifdef Q_WS_MAC
00898   return html.toUtf8();
00899 #else
00900   QTextCodec *codec = QTextCodec::codecForName("UTF-16");
00901   return codec->fromUnicode(html);
00902 #endif
00903 }
00904 
00905 QString KLFMimeExporterHTML::windowsFormatName(const QString& key) const
00906 {
00907   if (key == QLatin1String("text/html"))
00908     return "HTML";
00909   return key;
00910 }
00911 
00912 
00913 // -----------------------------------
00914 
00915 
00916 QByteArray klf_openoffice_drawing(const KLFBackend::klfOutput& klfoutput)
00917 {
00918   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00919 
00920   QByteArray pngdata = klfoutput.pngdata;
00921 
00922   QFile templfile(":/data/ooodrawingtemplate");
00923   templfile.open(QIODevice::ReadOnly);
00924   QByteArray templ = templfile.readAll();
00925 
00926   QString fgcols = QColor(klfoutput.input.fg_color).name();
00927   QString bgcols;
00928   if (qAlpha(klfoutput.input.bg_color) > 0)
00929     bgcols = QColor(klfoutput.input.fg_color).name();
00930   else
00931     bgcols = "-";
00932 
00933   templ.replace(QByteArray("<!--KLF_PNG_BASE64_DATA-->"), pngdata.toBase64());
00934 
00935   templ.replace(QByteArray("<!--KLF_INPUT_LATEX-->"), toAttrText(klfoutput.input.latex));
00936   templ.replace(QByteArray("<!--KLF_INPUT_MATHMODE-->"), toAttrText(klfoutput.input.mathmode));
00937   templ.replace(QByteArray("<!--KLF_INPUT_PREAMBLE-->"), toAttrText(klfoutput.input.preamble));
00938   templ.replace(QByteArray("<!--KLF_INPUT_FGCOLOR-->"), toAttrText(fgcols));
00939   templ.replace(QByteArray("<!--KLF_INPUT_BGCOLOR-->"), toAttrText(bgcols));
00940   templ.replace(QByteArray("<!--KLF_INPUT_DPI-->"), toAttrText(QString::number(klfoutput.input.dpi)));
00941   templ.replace(QByteArray("<!--KLF_SETTINGS_TBORDEROFFSET_PSPT-->"),
00942                 toAttrText(QString::number(klfoutput.settings.tborderoffset)));
00943   templ.replace(QByteArray("<!--KLF_SETTINGS_RBORDEROFFSET_PSPT-->"),
00944                 toAttrText(QString::number(klfoutput.settings.rborderoffset)));
00945   templ.replace(QByteArray("<!--KLF_SETTINGS_BBORDEROFFSET_PSPT-->"),
00946                 toAttrText(QString::number(klfoutput.settings.bborderoffset)));
00947   templ.replace(QByteArray("<!--KLF_SETTINGS_LBORDEROFFSET_PSPT-->"),
00948                 toAttrText(QString::number(klfoutput.settings.lborderoffset)));
00949 
00950   templ.replace(QByteArray("<!--KLF_INPUT_LATEX_BASE64-->"), klfoutput.input.latex.toLocal8Bit().toBase64());
00951   templ.replace(QByteArray("<!--KLF_INPUT_MATHMODE_BASE64-->"), klfoutput.input.mathmode.toLocal8Bit().toBase64());
00952   templ.replace(QByteArray("<!--KLF_INPUT_PREAMBLE_BASE64-->"), klfoutput.input.preamble.toLocal8Bit().toBase64());
00953   templ.replace(QByteArray("<!--KLF_INPUT_FGCOLOR_BASE64-->"), fgcols.toLocal8Bit().toBase64());
00954   templ.replace(QByteArray("<!--KLF_INPUT_BGCOLOR_BASE64-->"), bgcols.toLocal8Bit().toBase64());
00955 
00956   templ.replace(QByteArray("<!--KLF_OOOLATEX_ARGS-->"), toAttrText("12§display§"+klfoutput.input.latex));
00957 
00958   // make the equations larger, so it is not too cramped up
00959   const double DPI_FACTOR = 1.6;
00960   // cm/inch = 2.54
00961   // include an elargment factor in these tags
00962   templ.replace(QByteArray("<!--KLF_IMAGE_WIDTH_CM-->"),
00963                 QString::number(DPI_FACTOR * 2.54 * klfoutput.result.width()/klfoutput.input.dpi, 'f', 2).toUtf8());
00964   templ.replace(QByteArray("<!--KLF_IMAGE_HEIGHT_CM-->"),
00965                 QString::number(DPI_FACTOR * 2.54 * klfoutput.result.height()/klfoutput.input.dpi, 'f', 2).toUtf8());
00966   // same, without the enlargment factor
00967   templ.replace(QByteArray("<!--KLF_IMAGE_ORIG_WIDTH_CM-->"),
00968                 QString::number(2.54 * klfoutput.result.width()/klfoutput.input.dpi, 'f', 2).toUtf8());
00969   templ.replace(QByteArray("<!--KLF_IMAGE_ORIG_HEIGHT_CM-->"),
00970                 QString::number(2.54 * klfoutput.result.height()/klfoutput.input.dpi, 'f', 2).toUtf8());
00971 
00972   templ.replace(QByteArray("<!--KLF_IMAGE_WIDTH_PX-->"), QString::number(klfoutput.result.width()).toUtf8());
00973   templ.replace(QByteArray("<!--KLF_IMAGE_HEIGHT_PX-->"), QString::number(klfoutput.result.height()).toUtf8());
00974   templ.replace(QByteArray("<!--KLF_IMAGE_ASPECT_RATIO-->"),
00975                 QString::number((double)klfoutput.result.width()/klfoutput.result.height(), 'f', 3).toUtf8());
00976 
00977   klfDbg("final templ: "<<templ);
00978 
00979   return templ;
00980 }
00981 
00982 
00983 
00984 
00985 
00986 // ---------------------------
00987 
00988 
00989 
00990 QStringList KLFMimeExporterLibFmts::keys() const
00991 {
00992   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00993 
00994   klfDbg("list of keys: "<<KLFAbstractLibEntryMimeEncoder::allEncodingMimeTypes());
00995   return KLFAbstractLibEntryMimeEncoder::allEncodingMimeTypes();
00996 }
00997 
00998 QByteArray KLFMimeExporterLibFmts::data(const QString& key, const KLFBackend::klfOutput& output)
00999 {
01000   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
01001 
01002   klfDbg("key="<<key);
01003   KLFAbstractLibEntryMimeEncoder *encoder =
01004     KLFAbstractLibEntryMimeEncoder::findEncoderFor(key, true);
01005   if (encoder == NULL) {
01006     // warning already issued in findEncoderFor(.., TRUE)
01007     return QByteArray();
01008   }
01009 
01010   KLFLibEntry e = KLFLibEntry(output.input.latex, QDateTime::currentDateTime(),
01011                               output.result.scaled(klfconfig.UI.labelOutputFixedSize, Qt::KeepAspectRatio,
01012                                                    Qt::SmoothTransformation),
01013                               KLFStyle(output.input));
01014 
01015   QByteArray data = encoder->encodeMime(KLFLibEntryList()<<e, QVariantMap(), key);
01016 
01017   if (!data.size())
01018     qWarning()<<KLF_FUNC_NAME<<": "<<key<<" encoder returned empty data!";
01019 
01020   klfDbg("got data, size="<<data.size());
01021   return data;
01022 }
01023 
01024 
01025 
01026 
01027 // -------------------------------
01028 
01029 QStringList KLFMimeExporterGlowImage::keys() const
01030 {
01031   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
01032 
01033   return QStringList() << "image/png" << "application/x-qt-image";
01034 }
01035 
01036 QByteArray KLFMimeExporterGlowImage::data(const QString& key, const KLFBackend::klfOutput& output)
01037 {
01038   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
01039 
01040   klfDbg("key = "<<key) ;
01041   
01042   QImage img(output.result.size() + QSize(1,1)*2*klfconfig.UI.glowEffectRadius, QImage::Format_ARGB32);
01043 
01044   // initialize image to transparent
01045   img.fill(qRgba(0,0,0,0));
01046 
01047   { // now draw the glowed equation
01048     QPainter p(&img);
01049     p.translate(klfconfig.UI.glowEffectRadius, klfconfig.UI.glowEffectRadius);
01050     klfDrawGlowedImage(&p, output.result, klfconfig.UI.glowEffectColor, klfconfig.UI.glowEffectRadius);
01051   }
01052 
01053   QByteArray data;
01054   { // save the image as PNG data into our data qbytearray
01055     QBuffer buf(&data);
01056     buf.open(QIODevice::WriteOnly);
01057     img.save(&buf, "PNG");
01058   }
01059 
01060   // and return the PNG data
01061   return data;
01062 }
01063 
01064 
01065 

Generated by doxygen 1.7.3