[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) 2009 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 581 2010-12-02 23:09:05Z philippe $ */
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     exporttypes << ExportType(mimetypes[k], imgexporter.windowsFormatName(mimetypes[k]));
00340   }
00341   KLFMimeExportProfile allimgfmts
00342     = KLFMimeExportProfile("all_image_formats",
00343                            QObject::tr("All Available Image Formats"),
00344                            exporttypes);
00345   p_exportProfileList << allimgfmts;
00346 
00347 }
00348 
00349 
00350 // static, private
00351 void KLFMimeExportProfile::loadFromXMLFile(const QString& fname)
00352 {
00353   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00354 
00355   klfDbg("Loading from file "<<fname<<"; our locale is "<<klfconfig.UI.locale) ;
00356 
00357   QFile file(fname);
00358   if ( ! file.open(QIODevice::ReadOnly) ) {
00359     qWarning()<<KLF_FUNC_NAME<<": Error: Can't open export mime profiles XML file "<<fname<<": "
00360               <<file.errorString()<<"!";
00361     return;
00362   }
00363 
00364   QDomDocument doc("export-profile-list");
00365   QString errMsg; int errLine, errCol;
00366   bool r = doc.setContent(&file, false, &errMsg, &errLine, &errCol);
00367   if (!r) {
00368     qWarning()<<KLF_FUNC_NAME<<": Error parsing file "<<fname<<": "<<errMsg<<" at line "<<errLine<<", col "<<errCol;
00369     return;
00370   }
00371   file.close();
00372 
00373   QDomElement root = doc.documentElement();
00374   if (root.nodeName() != "export-profile-list") {
00375     qWarning("%s: Error parsing XML for export mime profiles from file `%s': Bad root node `%s'.\n",
00376              KLF_FUNC_NAME, qPrintable(fname), qPrintable(root.nodeName()));
00377     return;
00378   }
00379 
00380   QStringList descriptionLangs;
00381 
00382   // read XML file
00383   QDomNode n;
00384   for (n = root.firstChild(); ! n.isNull(); n = n.nextSibling()) {
00385     QDomElement e = n.toElement(); // try to convert the node to an element.
00386     if ( e.isNull() || n.nodeType() != QDomNode::ElementNode )
00387       continue;
00388     if ( e.nodeName() == "add-macosx-type-rules" ) {
00389 #ifdef Q_WS_MAC
00390       __klf_add_macosx_type_rules(fname, e);
00391 #else
00392       klfDbg("Ignoring Mac OS X type rules on non-mac window system") ;
00393 #endif
00394       continue;
00395     }
00396     if ( e.nodeName() != "profile" ) {
00397       qWarning("%s: WARNING in parsing XML \"%s\" : ignoring unexpected tag `%s', expected <profile>.\n",
00398                KLF_FUNC_NAME, qPrintable(fname), qPrintable(e.nodeName()));
00399       continue;
00400     }
00401     // read profile
00402     QString pname = e.attribute("name");
00403 
00404     // note: profile may already exist, we will check for that later.
00405 
00406     klfDbg("Reading profile "<<pname<<" ...") ;
00407     
00408     QString description;
00409     // xml:lang attribute for profile description is obsolete, we use now Qt-linguist translated value...
00410     QString curDescriptionLang;
00411     QList<ExportType> exporttypes;
00412 
00413     QDomNode en;
00414     for (en = e.firstChild(); ! en.isNull(); en = en.nextSibling() ) {
00415       if ( en.isNull() || en.nodeType() != QDomNode::ElementNode )
00416         continue;
00417       QDomElement ee = en.toElement();
00418       if ( en.nodeName() == "description" ) {
00419         // xml:lang attribute for profile description is obsolete, we use now Qt-linguist translated value...
00420         QString lang = ee.hasAttribute("xml:lang") ? ee.attribute("xml:lang") : QString() ;
00421         klfDbg("<description>: lang="<<lang<<"; hasAttribute(xml:lang)="<<ee.hasAttribute("xml:lang")
00422                <<"; current description="<<description<<",lang="<<curDescriptionLang) ;
00423         if (description.isEmpty()) {
00424           // no description yet
00425           if (lang.isEmpty() || lang.startsWith(klfconfig.UI.locale) || klfconfig.UI.locale.startsWith(lang)) {
00426             // correct locale
00427             //      klfDbg("remembering description tag with lang="<<lang);
00428             description = qApp->translate("xmltr_exportprofiles", ee.text().toUtf8().constData(),
00429                                           "[[tag: <description>]]", QCoreApplication::UnicodeUTF8);
00430             // xml:lang attribute for profile description is obsolete, we use now Qt-linguist translated value...
00431             curDescriptionLang = lang;
00432           }
00433           // otherwise skip this tag
00434         } else {
00435           // see if this locale is correct and more specific
00436           if ( (lang.startsWith(klfconfig.UI.locale) || klfconfig.UI.locale.startsWith(lang)) &&
00437                (curDescriptionLang.isEmpty() || lang.startsWith(curDescriptionLang) ) ) {
00438             // then keep it and replace the other
00439             //      klfDbg("remembering description tag with lang="<<lang);
00440             description = ee.text();
00441             curDescriptionLang = lang;
00442           }
00443           // otherwise skip this tag
00444         }
00445         continue;
00446       }
00447       if ( en.nodeName() != "export-type" ) {
00448         qWarning("%s: WARNING in parsing XML '%s': ignoring unexpected tag `%s' in profile `%s'!\n",
00449                  KLF_FUNC_NAME, qPrintable(fname), qPrintable(en.nodeName()), qPrintable(pname));
00450         continue;
00451       }
00452       QDomNodeList mimetypetags = ee.elementsByTagName("mime-type");
00453       if (mimetypetags.size() != 1) {
00454         qWarning()<<KLF_FUNC_NAME<<": in XML file "<<fname<<", profile "<<pname
00455                   <<": exactly ONE <mime-type> tag must be present in each <export-type>...</export-type>.";
00456         continue;
00457       }
00458       QDomNodeList wintypetags = ee.elementsByTagName("windows-type");
00459       if (wintypetags.size() > 1) {
00460         qWarning()<<KLF_FUNC_NAME<<": in XML file "<<fname<<", profile "<<pname
00461                   <<": expecting at most ONE <windows-type> tag in each <export-type>...</export-type>.";
00462         continue;
00463       }
00464       QDomNodeList exporternametags = ee.elementsByTagName("exporter-name");
00465       if (exporternametags.size() > 1) {
00466         qWarning()<<KLF_FUNC_NAME<<": in XML file "<<fname<<", profile "<<pname
00467                   <<": expected at most ONE <exporter-name> tag in each <export-type>...</export-type>.";
00468         continue;
00469       }
00470       QString mimetype = mimetypetags.at(0).toElement().text().trimmed();
00471       QString wintype = QString();
00472       if (wintypetags.size() == 1) {
00473         wintype = wintypetags.at(0).toElement().text().trimmed();
00474       }
00475       QString exportername = QString();
00476       if (exporternametags.size() == 1) {
00477         exportername = exporternametags.at(0).toElement().text().trimmed();
00478       }
00479 
00480       exporttypes << ExportType(mimetype,  wintype,  exportername);
00481     }
00482 
00483     // add this profile
00484     KLFMimeExportProfile profile(pname, description, exporttypes);
00485 
00486     // check if a profile has not already been declared with same name
00487     int kp;
00488     for (kp = 0; kp < p_exportProfileList.size(); ++kp) {
00489       if (p_exportProfileList[kp].profileName() == pname) {
00490         break;
00491       }
00492     }
00493     if (kp == p_exportProfileList.size()) {
00494       // the profile is new
00495       klfDbg("Adding profile "<<pname<<" to mime export profiles") ;
00496       if (pname == "default") {
00497         // prepend default profile, so it's at beginning
00498         p_exportProfileList.prepend(profile);
00499       } else {
00500         p_exportProfileList << profile;
00501         // xml:lang attribute for profile description is obsolete, we use now Qt-linguist translated value...
00502         descriptionLangs << curDescriptionLang;
00503       }
00504     } else {
00505       // profile already exists, append data to it
00506       KLFMimeExportProfile oldp = p_exportProfileList[kp];
00507       // see if this description provides a better translation
00508       if (!description.isEmpty() &&
00509           (descriptionLangs[kp].isEmpty() || curDescriptionLang.startsWith(descriptionLangs[kp]))) {
00510         // keep this description
00511       } else {
00512         description = oldp.description();
00513       }
00514       KLFMimeExportProfile finalp(pname, description,
00515                                   oldp.exportTypes()+exporttypes // concatenate lists
00516                                   );
00517       p_exportProfileList[kp] = finalp;
00518     }
00519   }
00520 }
00521 
00522 
00523 // ---------------------------------------------------------------------
00524 
00525 
00526 
00527 
00528 KLFMimeData::KLFMimeData(const QString& exportProfile, const KLFBackend::klfOutput& output)
00529   : QMimeData(), pExportProfile(KLFMimeExportProfile::findExportProfile(exportProfile))
00530 {
00531   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00532 
00533   // ensure an instance of KLFMacPasteboardMime object
00534   __klf_init_the_macpasteboardmime();
00535 
00536   pOutput = output;
00537 
00538   set_possible_qt_image_data();
00539 }
00540 KLFMimeData::~KLFMimeData()
00541 {
00542   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00543 }
00544 
00545 // private
00546 void KLFMimeData::set_possible_qt_image_data()
00547 {
00548   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00549 
00550   // Handle platform-specific default image type with 'application/x-qt-image' and setImage()
00551   int index;
00552   if ( (index=pExportProfile.indexOfMimeType(QLatin1String("application/x-qt-image"))) >= 0) {
00553     // set the image data form the exporter
00554     KLFMimeExporter * exporter = pExportProfile.exporterLookupFor(index);
00555     if (exporter != NULL) {
00556       QByteArray img_data = exporter->data(QLatin1String("application/x-qt-image"), pOutput);
00557       QImage img;
00558       QList<QByteArray> try_formats = QList<QByteArray>() << "PNG" << "JPEG" << "BMP";
00559       int k;
00560       for (k = 0; k < try_formats.size() && img.isNull(); ++k)
00561         img.loadFromData(img_data, try_formats[k].constData());
00562       KLF_ASSERT_CONDITION( ! img.isNull() ,
00563                             "Can't get image for application/x-qt-image for profile "<<pExportProfile.profileName(),
00564                             return; )  ;
00565       setImageData(img);
00566     }
00567   }
00568 }
00569 
00570 QStringList KLFMimeData::formats() const
00571 {
00572   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00573 
00574   QStringList fmts = pExportProfile.availableExporterMimeTypes();
00575   if (fmts.contains("application/x-qt-image")) {
00576     if (pQtOwnedFormats.size() == 0)
00577       pQtOwnedFormats = QMimeData::formats();
00578     fmts << pQtOwnedFormats;
00579   }
00580 
00581   klfDbg("format list: "<<fmts) ;
00582   return fmts;
00583 }
00584 
00585 QVariant KLFMimeData::retrieveData(const QString& mimetype, QVariant::Type type) const
00586 {
00587   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00588 
00589   if (mimetype == QLatin1String("application/x-qt-image") ||
00590       pQtOwnedFormats.contains(mimetype))
00591     return QMimeData::retrieveData(mimetype, type);
00592 
00593   int index = pExportProfile.indexOfMimeType(mimetype);
00594   if (index < 0) {
00595     // do not treat this as an error since on windows we seem to have requests for 'text/uri-list' even
00596     // if that mime type is not returned by formats()
00597     klfDbg("Can't find mime-type "<<mimetype<<" in export profile "<<pExportProfile.profileName()
00598            <<" ?!?");
00599     return QVariant();
00600   }
00601 
00602   klfDbg("exporting "<<mimetype<<" ...");
00603   KLFMimeExporter *exporter = pExportProfile.exporterLookupFor(index);
00604 
00605   KLF_ASSERT_NOT_NULL(exporter,
00606                       "Can't find an exporter for mime-type "<<mimetype<<"." ,
00607                       return QVariant(); )  ;
00608   
00609   // get the data
00610   QByteArray data = exporter->data(mimetype, pOutput);
00611   
00612   klfDbg("exporting mimetype "<<mimetype<<": data length is "<<data.size());
00613   
00614   return QVariant::fromValue<QByteArray>(data);
00615 }
00616 
00617 
00618 
00619 
00620 // ---------------------------------------------------------------------
00621 
00622 static QMap<QString,QByteArray> get_qt_image_formats()
00623 {
00624   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00625 
00626   QMap<QString,QByteArray> imageFormats;
00627   // get qt's image formats
00628   QList<QByteArray> qtimgfmts = QImageWriter::supportedImageFormats();
00629   int k;
00630   for (k = 0; k < qtimgfmts.size(); ++k) {
00631     if ( imageFormats.key(qtimgfmts[k]).isEmpty() ) {
00632       QString mime = QString::fromLatin1("image/")+QString::fromLatin1(qtimgfmts[k]).toLower();
00633       imageFormats[mime] = qtimgfmts[k];
00634     }
00635   }
00636   return imageFormats;
00637 }
00638 
00639 QMap<QString,QByteArray> KLFMimeExporterImage::imageFormats = QMap<QString,QByteArray>();
00640 
00641 QStringList KLFMimeExporterImage::keys() const
00642 {
00643   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00644 
00645   // image formats that are always supported. Qt image formats are added too.
00646   static QStringList staticKeys
00647     = QStringList() << "image/png" << "image/eps" << "application/eps" << "application/postscript"
00648                     << OPENOFFICE_DRAWING_MIMETYPE << "application/x-qt-image"
00649                     // add duplicate for png, see below
00650                     << "image/x-win-png-office-art";
00651 
00652   if (imageFormats.isEmpty()) {
00653     // populate qt's image formats
00654     QMap<QString,QByteArray> ifmts = get_qt_image_formats();
00655     QMap<QString,QByteArray>::iterator it;
00656     for (it = ifmts.begin(); it != ifmts.end(); ++it) {
00657       QString mime = it.key();
00658       QByteArray qtfmt = it.value();
00659       // add this image format, if not already provided by staticKeys
00660       if (staticKeys.indexOf(mime) == -1)
00661         imageFormats[mime] = qtfmt;
00662       // add duplicate mime types for some formats, to be able to specify multiple windows format
00663       // names for them, eg. "Bitmap" and "Windows Bitmap" :
00664       if (mime == "image/bmp") {
00665         imageFormats["image/x-win-bmp"] = qtfmt;
00666       } else if (mime == "image/jpeg") {
00667         imageFormats["image/x-win-jfif"] = qtfmt;
00668         imageFormats["image/x-win-jfif-office-art"] = qtfmt;
00669       } else if (mime == "image/png") {
00670         imageFormats["image/x-win-png-office-art"] = qtfmt;
00671       }
00672     }
00673   }
00674 
00675   QStringList keys = staticKeys;
00676 
00677   if (!klfconfig.BackendSettings.execEpstopdf.isEmpty())
00678     keys <<"application/pdf"; // add PDF only if we have PDF
00679 
00680   keys << imageFormats.keys();
00681 
00682   return keys;
00683 }
00684 
00685 QString KLFMimeExporterImage::windowsFormatName(const QString& mime) const
00686 {
00687   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00688 
00689   QString wtype;
00690   if (mime == "application/pdf")
00691     return "PDF";
00692   else if (mime == "application/eps")
00693     return "Encapsulated PostScript";
00694   else if (mime == "image/png")
00695     return "PNG";
00696   else if (mime == "image/jpg" || mime == "image/jpeg")
00697     // standards should only allow image/jpeg, but just in case we treat also erroneous "image/jpg"
00698     return "JPEG";
00699   else if (mime == "image/x-win-jfif")
00700     return "JFIF";
00701   else if (mime == "image/x-win-jfif-office-art")
00702     return "JFIF+Office Art"; // for Ms Office
00703   else if (mime == "image/x-win-png-office-art")
00704     return "PNG+Office Art"; // for Ms Office
00705   else if (mime == "image/bmp")
00706     return "Bitmap";
00707   else if (mime == "image/x-win-bmp")
00708     return "Windows Bitmap";
00709   else if (mime == OPENOFFICE_DRAWING_MIMETYPE)
00710     return "Drawing Format";
00711   else if (mime == "application/x-qt-image")
00712     return mime; // let Qt translate this one
00713 
00714   return mime;
00715 }
00716 
00717 QByteArray klf_openoffice_drawing(const KLFBackend::klfOutput& klfoutput);
00718 
00719 QByteArray KLFMimeExporterImage::data(const QString& keymime, const KLFBackend::klfOutput& klfoutput)
00720 {
00721   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00722 
00723   QString key = keymime;
00724   klfDbg("key="<<key);
00725 
00726   if (key == "image/png")
00727     return klfoutput.pngdata;
00728   if (key == "image/eps" || key == "application/eps" || key == "application/postscript")
00729     return klfoutput.epsdata;
00730   if (key == "application/pdf") {
00731 #ifdef KLF_DEBUG
00732     if (klfoutput.pdfdata.isEmpty())
00733       klfDbg("---warning: don't have PDF data ---") ;
00734 #endif
00735     return klfoutput.pdfdata;
00736   }
00737   if (key == OPENOFFICE_DRAWING_MIMETYPE)
00738     return klf_openoffice_drawing(klfoutput);
00739   if (key == "application/x-qt-image")
00740     return klfoutput.pngdata;
00741 
00742   // rely on qt's image saving routines for other formats
00743   klfDbg("Will use Qt's image format exporting");
00744   
00745   if ( ! imageFormats.contains(key) )
00746     return QByteArray();
00747 
00748   QByteArray imgdata;
00749   QBuffer imgdatawriter(&imgdata);
00750   imgdatawriter.open(QIODevice::WriteOnly);
00751   klfoutput.result.save(&imgdatawriter, imageFormats[key]);
00752   imgdatawriter.close();
00753 
00754   klfDbg("got data: size="<<imgdata.size());
00755   return imgdata;
00756 }
00757 
00758 
00759 
00760 // ---------------------------------------------------------------------
00761 
00762 QMap<qint64,QString> KLFMimeExporterUrilist::tempFilesForImageCacheKey = QMap<qint64,QString>();
00763 
00764 QStringList KLFMimeExporterUrilist::keys() const
00765 {
00766   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00767   return QStringList() << "text/x-moz-url" << "text/uri-list";
00768 }
00769 
00770 // static
00771 QString KLFMimeExporterUrilist::tempFileForOutput(const KLFBackend::klfOutput& output)
00772 {
00773   qint64 imgcachekey = output.result.cacheKey();
00774 
00775   QString tempfilename;
00776 
00777   if (tempFilesForImageCacheKey.contains(imgcachekey)) {
00778     tempfilename = tempFilesForImageCacheKey[imgcachekey];
00779   } else {
00780     QString templ = klfconfig.BackendSettings.tempDir +
00781       QString("/klf_%1_XXXXXX.png").arg(QDateTime::currentDateTime().toString("yyyy-MM-dd_hh-mm"));
00782     QTemporaryFile *tempfile = new QTemporaryFile(templ, qApp);
00783     tempfile->setAutoRemove(true); // will be deleted when klatexformula exists (= qApp destroyed)
00784     if (tempfile->open() == false) {
00785       qWarning("Can't open temp png file for mimetype text/uri-list: template is %s",
00786                qPrintable(templ));
00787       return QByteArray();
00788     } else {
00789       QString errStr;
00790       bool res = KLFBackend::saveOutputToFile(output, tempfile->fileName(), "PNG", &errStr);
00791       if (!res) {
00792         qWarning()<<KLF_FUNC_NAME<<": Can't save to temp file "<<tempfile->fileName()<<": "<<errStr;
00793       } else {
00794         tempfilename = tempfile->fileName();
00795         tempfile->write(output.pngdata);
00796         tempfile->close();
00797         // cache this temp file for other formats' use or other QMimeData instantiation...
00798         tempFilesForImageCacheKey[imgcachekey] = tempfilename;
00799       }
00800     }
00801   }
00802 
00803   return tempfilename;
00804 }
00805 
00806 QByteArray KLFMimeExporterUrilist::data(const QString& key, const KLFBackend::klfOutput& output)
00807 {
00808   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00809 
00810   Q_UNUSED(key) ;
00811   klfDbg("key="<<key) ;
00812 
00813   QString tempfilename = tempFileForOutput(output);
00814   QByteArray urilist = (QUrl::fromLocalFile(tempfilename).toString()+QLatin1String("\n")).toLatin1();
00815   return urilist;
00816 }
00817 
00818 QString KLFMimeExporterUrilist::windowsFormatName(const QString& mime) const
00819 {
00820   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00821 
00822   if (mime == "text/x-moz-url")
00823     return "FileName";
00824   return mime;
00825 }
00826 
00827 
00828 
00829 // -----------------------------------
00830 
00831 static QString toAttrTextS(const QString& sbase)
00832 {
00833   QString s = sbase; // we need a non-const string to .replace() on
00834   klfDbg("s="<<s);
00835   QRegExp replaceCharsRX("([^a-zA-Z0-9/ ._-])");
00836   int pos = 0;
00837   while ((pos = replaceCharsRX.indexIn(s, pos)) != -1) {
00838     QString entity = "&#x"+QString::number(replaceCharsRX.cap(1)[0].unicode(), 16).toUpper()+";" ;
00839     klfDbg("replacing char at pos="<<pos<<" by entity="<<entity<<": s(pos...pos+5)="<<s.mid(pos,5));
00840     s.replace(pos, replaceCharsRX.matchedLength(), entity);
00841     pos += entity.length();
00842   }
00843   klfDbg("final string: "<<s);
00844   return s;
00845 
00846   //  QString s2 = sbase;
00847   //  return s2.replace("&", "&amp;").replace("\"", "&quot;").replace("'", "&apos;").replace("<", "&lt")
00848   //    .replace(">", "&gt;").replace("\r", "&#xD;").replace("\t", "&#x9;").replace("\n", "&#xA;")
00849   //    .replace("!", "&#x21;").toUtf8();
00850 }
00851 
00852 static QByteArray toAttrText(const QString& sbase)
00853 {
00854   return toAttrTextS(sbase).toUtf8();
00855 }
00856 
00857 QStringList KLFMimeExporterHTML::keys() const
00858 {
00859   return QStringList() << QLatin1String("text/html");
00860 }
00861 
00862 QByteArray KLFMimeExporterHTML::data(const QString& key, const KLFBackend::klfOutput& klfoutput)
00863 {
00864   QString fname = KLFMimeExporterUrilist::tempFileForOutput(klfoutput);
00865 
00866   QSize imgsize = klfoutput.result.size();
00867   int imgDpi = klfoutput.input.dpi;
00868   int dispDpi = 100;
00869 
00870   QString latex = klfoutput.input.latex;
00871   // remove initial comments from latex code...
00872   QStringList latexlines = latex.split("\n");
00873   while (latexlines.size() && QRegExp("\\s*\\%.*").exactMatch(latexlines[0]))
00874     latexlines.removeAt(0);
00875   latex = latexlines.join("\n");
00876 
00877   QString fn = toAttrTextS(fname);
00878   QString l = toAttrTextS(latex);
00879   fn.replace("\"", "&#34;");
00880   l.replace("\"", "&#34;");
00881   QString w = QString::number((int)(1.5 * imgsize.width() * dispDpi/imgDpi));
00882   QString h = QString::number((int)(1.5 * imgsize.height() * dispDpi/imgDpi));
00883   QString win = QString::number(1.5 * imgsize.width() / imgDpi);
00884   QString hin = QString::number(1.5 * imgsize.height() / imgDpi);
00885 
00886   QString html =
00887     QString("<img src=\"file://%1\" alt=\"%2\" title=\"%3\" " //"width=\"%4\" height=\"%5\" "
00888             " style=\"width: %4in; height: %5in; vertical-align: middle;\">")
00889     .arg(fn, l, l, win, hin);
00890 
00891 #ifdef Q_WS_MAC
00892   return html.toUtf8();
00893 #else
00894   QTextCodec *codec = QTextCodec::codecForName("UTF-16");
00895   return codec->fromUnicode(html);
00896 #endif
00897 }
00898 
00899 QString KLFMimeExporterHTML::windowsFormatName(const QString& key) const
00900 {
00901   if (key == QLatin1String("text/html"))
00902     return "HTML";
00903   return key;
00904 }
00905 
00906 
00907 // -----------------------------------
00908 
00909 
00910 QByteArray klf_openoffice_drawing(const KLFBackend::klfOutput& klfoutput)
00911 {
00912   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00913 
00914   QByteArray pngdata = klfoutput.pngdata;
00915 
00916   QFile templfile(":/data/ooodrawingtemplate");
00917   templfile.open(QIODevice::ReadOnly);
00918   QByteArray templ = templfile.readAll();
00919 
00920   QString fgcols = QColor(klfoutput.input.fg_color).name();
00921   QString bgcols;
00922   if (qAlpha(klfoutput.input.bg_color) > 0)
00923     bgcols = QColor(klfoutput.input.fg_color).name();
00924   else
00925     bgcols = "-";
00926 
00927   templ.replace(QByteArray("<!--KLF_PNG_BASE64_DATA-->"), pngdata.toBase64());
00928 
00929   templ.replace(QByteArray("<!--KLF_INPUT_LATEX-->"), toAttrText(klfoutput.input.latex));
00930   templ.replace(QByteArray("<!--KLF_INPUT_MATHMODE-->"), toAttrText(klfoutput.input.mathmode));
00931   templ.replace(QByteArray("<!--KLF_INPUT_PREAMBLE-->"), toAttrText(klfoutput.input.preamble));
00932   templ.replace(QByteArray("<!--KLF_INPUT_FGCOLOR-->"), toAttrText(fgcols));
00933   templ.replace(QByteArray("<!--KLF_INPUT_BGCOLOR-->"), toAttrText(bgcols));
00934   templ.replace(QByteArray("<!--KLF_INPUT_DPI-->"), toAttrText(QString::number(klfoutput.input.dpi)));
00935   templ.replace(QByteArray("<!--KLF_SETTINGS_TBORDEROFFSET_PSPT-->"),
00936                 toAttrText(QString::number(klfoutput.settings.tborderoffset)));
00937   templ.replace(QByteArray("<!--KLF_SETTINGS_RBORDEROFFSET_PSPT-->"),
00938                 toAttrText(QString::number(klfoutput.settings.rborderoffset)));
00939   templ.replace(QByteArray("<!--KLF_SETTINGS_BBORDEROFFSET_PSPT-->"),
00940                 toAttrText(QString::number(klfoutput.settings.bborderoffset)));
00941   templ.replace(QByteArray("<!--KLF_SETTINGS_LBORDEROFFSET_PSPT-->"),
00942                 toAttrText(QString::number(klfoutput.settings.lborderoffset)));
00943 
00944   templ.replace(QByteArray("<!--KLF_INPUT_LATEX_BASE64-->"), klfoutput.input.latex.toLocal8Bit().toBase64());
00945   templ.replace(QByteArray("<!--KLF_INPUT_MATHMODE_BASE64-->"), klfoutput.input.mathmode.toLocal8Bit().toBase64());
00946   templ.replace(QByteArray("<!--KLF_INPUT_PREAMBLE_BASE64-->"), klfoutput.input.preamble.toLocal8Bit().toBase64());
00947   templ.replace(QByteArray("<!--KLF_INPUT_FGCOLOR_BASE64-->"), fgcols.toLocal8Bit().toBase64());
00948   templ.replace(QByteArray("<!--KLF_INPUT_BGCOLOR_BASE64-->"), bgcols.toLocal8Bit().toBase64());
00949 
00950   templ.replace(QByteArray("<!--KLF_OOOLATEX_ARGS-->"), toAttrText("12§display§"+klfoutput.input.latex));
00951 
00952   // make the equationn larger, so it is not too cramped up
00953   const double DPI_FACTOR = 2.0;
00954   // cm/inch = 2.54
00955   // include an elargment factor in these tags
00956   templ.replace(QByteArray("<!--KLF_IMAGE_WIDTH_CM-->"),
00957                 QString::number(DPI_FACTOR * 2.54 * klfoutput.result.width()/klfoutput.input.dpi, 'f', 2).toUtf8());
00958   templ.replace(QByteArray("<!--KLF_IMAGE_HEIGHT_CM-->"),
00959                 QString::number(DPI_FACTOR * 2.54 * klfoutput.result.height()/klfoutput.input.dpi, 'f', 2).toUtf8());
00960   // same, without the enlargment factor
00961   templ.replace(QByteArray("<!--KLF_IMAGE_ORIG_WIDTH_CM-->"),
00962                 QString::number(2.54 * klfoutput.result.width()/klfoutput.input.dpi, 'f', 2).toUtf8());
00963   templ.replace(QByteArray("<!--KLF_IMAGE_ORIG_HEIGHT_CM-->"),
00964                 QString::number(2.54 * klfoutput.result.height()/klfoutput.input.dpi, 'f', 2).toUtf8());
00965 
00966   templ.replace(QByteArray("<!--KLF_IMAGE_WIDTH_PX-->"), QString::number(klfoutput.result.width()).toUtf8());
00967   templ.replace(QByteArray("<!--KLF_IMAGE_HEIGHT_PX-->"), QString::number(klfoutput.result.height()).toUtf8());
00968   templ.replace(QByteArray("<!--KLF_IMAGE_ASPECT_RATIO-->"),
00969                 QString::number((double)klfoutput.result.width()/klfoutput.result.height(), 'f', 3).toUtf8());
00970 
00971   klfDbg("final templ: "<<templ);
00972 
00973   return templ;
00974 }
00975 
00976 
00977 
00978 
00979 
00980 // ---------------------------
00981 
00982 
00983 
00984 QStringList KLFMimeExporterLibFmts::keys() const
00985 {
00986   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00987 
00988   klfDbg("list of keys: "<<KLFAbstractLibEntryMimeEncoder::allEncodingMimeTypes());
00989   return KLFAbstractLibEntryMimeEncoder::allEncodingMimeTypes();
00990 }
00991 
00992 QByteArray KLFMimeExporterLibFmts::data(const QString& key, const KLFBackend::klfOutput& output)
00993 {
00994   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
00995 
00996   klfDbg("key="<<key);
00997   KLFAbstractLibEntryMimeEncoder *encoder =
00998     KLFAbstractLibEntryMimeEncoder::findEncoderFor(key, true);
00999   if (encoder == NULL) {
01000     // warning already issued in findEncoderFor(.., TRUE)
01001     return QByteArray();
01002   }
01003 
01004   KLFLibEntry e = KLFLibEntry(output.input.latex, QDateTime::currentDateTime(),
01005                               output.result.scaled(klfconfig.UI.labelOutputFixedSize, Qt::KeepAspectRatio,
01006                                                    Qt::SmoothTransformation),
01007                               KLFStyle(output.input));
01008 
01009   QByteArray data = encoder->encodeMime(KLFLibEntryList()<<e, QVariantMap(), key);
01010 
01011   if (!data.size())
01012     qWarning()<<KLF_FUNC_NAME<<": "<<key<<" encoder returned empty data!";
01013 
01014   klfDbg("got data, size="<<data.size());
01015   return data;
01016 }
01017 
01018 
01019 
01020 
01021 // -------------------------------
01022 
01023 QStringList KLFMimeExporterGlowImage::keys() const
01024 {
01025   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
01026 
01027   return QStringList() << "image/png" << "application/x-qt-image";
01028 }
01029 
01030 QByteArray KLFMimeExporterGlowImage::data(const QString& key, const KLFBackend::klfOutput& output)
01031 {
01032   KLF_DEBUG_BLOCK(KLF_FUNC_NAME) ;
01033 
01034   klfDbg("key = "<<key) ;
01035   
01036   QImage img(output.result.size() + QSize(1,1)*2*klfconfig.UI.glowEffectRadius, QImage::Format_ARGB32);
01037 
01038   // initialize image to transparent
01039   img.fill(qRgba(0,0,0,0));
01040 
01041   { // now draw the glowed equation
01042     QPainter p(&img);
01043     p.translate(klfconfig.UI.glowEffectRadius, klfconfig.UI.glowEffectRadius);
01044     klfDrawGlowedImage(&p, output.result, klfconfig.UI.glowEffectColor, klfconfig.UI.glowEffectRadius);
01045   }
01046 
01047   QByteArray data;
01048   { // save the image as PNG data into our data qbytearray
01049     QBuffer buf(&data);
01050     buf.open(QIODevice::WriteOnly);
01051     img.save(&buf, "PNG");
01052   }
01053 
01054   // and return the PNG data
01055   return data;
01056 }
01057 
01058 
01059 

Generated by doxygen 1.7.3