umbrello API Documentation

umlviewimageexportermodel.cpp

00001 /***************************************************************************
00002  *                                                                         *
00003  *   This program is free software; you can redistribute it and/or modify  *
00004  *   it under the terms of the GNU General Public License as published by  *
00005  *   the Free Software Foundation; either version 2 of the License, or     *
00006  *   (at your option) any later version.                                   *
00007  *                                                                         *
00008  *   copyright (C) 2006-2007                                               *
00009  *   Umbrello UML Modeller Authors <uml-devel@uml.sf.net>                  *
00010  ***************************************************************************/
00011 
00012 // own header
00013 #include "umlviewimageexportermodel.h"
00014 
00015 // system includes
00016 #include <math.h>
00017 
00018 // include files for Qt
00019 #include <qstringlist.h>
00020 #include <qrect.h>
00021 #include <qimage.h>
00022 #include <qpicture.h>
00023 #include <qpainter.h>
00024 #include <qprinter.h>
00025 #include <qdir.h>
00026 #include <qregexp.h>
00027 
00028 // kde include files
00029 #include <kdebug.h>
00030 #include <klocale.h>
00031 #include <ktempfile.h>
00032 #include <kapplication.h>
00033 #include <kio/netaccess.h>
00034 
00035 // application specific includes
00036 #include "uml.h"
00037 #include "umldoc.h"
00038 #include "umlview.h"
00039 #include "umllistview.h"
00040 #include "umllistviewitem.h"
00041 
00042 static QStringList supportedImageTypesList;
00043 static QStringList supportedMimeTypesList;
00044 
00045 QStringList UMLViewImageExporterModel::supportedImageTypes() {
00046     if (!supportedImageTypesList.size()) {
00047         // specific supported formats
00048         supportedImageTypesList << "eps";
00049         supportedImageTypesList << "svg";
00050 
00051         // QT supported formats
00052         QStrList qImageFormats = QImage::outputFormats();
00053         for (const char* format = qImageFormats.first(); format; format = qImageFormats.next()) {
00054             supportedImageTypesList << QString(format).lower();
00055         }
00056     }
00057 
00058     return supportedImageTypesList;
00059 }
00060 
00061 QStringList UMLViewImageExporterModel::supportedMimeTypes() {
00062     if (!supportedMimeTypesList.size()) {
00063         QStringList imageTypes = UMLViewImageExporterModel::supportedImageTypes();
00064         for(QStringList::Iterator it = imageTypes.begin(); it != imageTypes.end(); ++it ) {
00065             QString mimeType = imageTypeToMimeType(*it);
00066             if (!mimeType.isNull())
00067                 supportedMimeTypesList.append(mimeType);
00068         }
00069     }
00070 
00071     return supportedMimeTypesList;
00072 }
00073 
00074 QString UMLViewImageExporterModel::imageTypeToMimeType(const QString& imageType) {
00075     const QString imgType = imageType.lower();
00076     if (QString("bmp") == imgType) return "image/x-bmp";
00077     if (QString("jpeg") == imgType) return "image/jpeg";
00078     if (QString("pbm") == imgType) return "image/x-portable-bitmap";
00079     if (QString("pgm") == imgType) return "image/x-portable-greymap";
00080     if (QString("png") == imgType) return "image/png";
00081     if (QString("ppm") == imgType) return "image/x-portable-pixmap";
00082     if (QString("xbm") == imgType) return "image/x-xbm";
00083     if (QString("xpm") == imgType) return "image/x-xpm";
00084     if (QString("eps") == imgType) return "image/x-eps";
00085     if (QString("svg") == imgType) return "image/svg+xml";
00086     return QString::null;
00087 }
00088 
00089 QString UMLViewImageExporterModel::mimeTypeToImageType(const QString& mimeType) {
00090     if (QString("image/x-bmp") == mimeType) return "bmp";
00091     if (QString("image/jpeg") == mimeType) return "jpeg";
00092     if (QString("image/x-portable-bitmap") == mimeType) return "pbm";
00093     if (QString("image/x-portable-greymap") == mimeType) return "pgm";
00094     if (QString("image/png") == mimeType) return "png";
00095     if (QString("image/x-portable-pixmap") == mimeType) return "ppm";
00096     if (QString("image/x-xbm") == mimeType) return "xbm";
00097     if (QString("image/x-xpm") == mimeType) return "xpm";
00098     if (QString("image/x-eps") == mimeType) return "eps";
00099     if (QString("image/svg+xml") == mimeType) return "svg";
00100     return QString::null;
00101 }
00102 
00103 QStringList UMLViewImageExporterModel::exportAllViews(const QString &imageType, const KURL &directory, bool useFolders) const {
00104     UMLApp *app = UMLApp::app();
00105 
00106     // contains all the error messages returned by exportView calls
00107     QStringList errors;
00108 
00109     UMLViewList views = app->getDocument()->getViewIterator();
00110     for(UMLView *view = views.first(); view; view = views.next()) {
00111         KURL url = directory;
00112         url.addPath(getDiagramFileName(view, imageType, useFolders));
00113 
00114         QString returnString = exportView(view, imageType, url);
00115         if (!returnString.isNull()) {
00116             errors.append(view->getName() + ": " + returnString);
00117         }
00118     }
00119 
00120     return errors;
00121 }
00122 
00123 QString UMLViewImageExporterModel::exportView(UMLView* view, const QString &imageType, const KURL &url) const {
00124     // create the needed directories
00125     if (!prepareDirectory(url)) {
00126         return i18n("Can not create directory: %1").arg(url.directory());
00127     }
00128 
00129     // The fileName will be used when exporting the image. If the url isn't local,
00130     // the fileName is the name of a temporal local file to export the image to, and then
00131     // upload it to its destiny
00132     QString fileName;
00133     // tmpFile needs to be unlinked before exiting the method!!!
00134     KTempFile tmpFile;
00135     if (url.isLocalFile()) {
00136         fileName = url.path();
00137     } else {
00138         fileName = tmpFile.name();
00139     }
00140 
00141     // check that the diagram isn't empty
00142     QRect rect = view->getDiagramRect();
00143     if (rect.isEmpty()) {
00144         tmpFile.unlink();
00145         return i18n("Can not save an empty diagram");
00146     }
00147 
00148     // exporting the view to the file
00149     if (!exportViewTo(view, imageType, fileName)) {
00150         tmpFile.unlink();
00151         return i18n("A problem occured while saving diagram in %1").arg(fileName);
00152     }
00153 
00154     // if the file wasn't local, upload the temp file to the target
00155     if (!url.isLocalFile()) {
00156         if (!KIO::NetAccess::upload(tmpFile.name(), url, UMLApp::app())) {
00157             tmpFile.unlink();
00158             return i18n("There was a problem saving file: %1").arg(url.path());
00159         }
00160     } 
00161 
00162     tmpFile.unlink();
00163     return QString::null;
00164 }
00165 
00166 QString UMLViewImageExporterModel::getDiagramFileName(UMLView *view, const QString &imageType, bool useFolders /* = false */) const {
00167     QString name = view->getName() + '.' + imageType.lower();
00168 
00169     if (!useFolders) {
00170         return name;
00171     }
00172 
00173     kapp->processEvents();
00174     UMLListView *listView = UMLApp::app()->getListView();
00175     UMLListViewItem* listViewItem = listView->findItem(view->getID());
00176     // skip the name of the first item because it's the View
00177     listViewItem = static_cast<UMLListViewItem*>(listViewItem->parent());
00178 
00179     // Relies on the tree structure of the UMLListView. There are a base "Views" folder
00180     // and five children, one for each view type (Logical, use case, components, deployment
00181     // and entity relationship)
00182     while (listView->rootView(listViewItem->getType()) == NULL) {
00183         name.insert(0, listViewItem->getText() + '/');
00184         listViewItem = static_cast<UMLListViewItem*>(listViewItem->parent());
00185         if (listViewItem == NULL)
00186             break;
00187     }
00188     return name;
00189 }
00190 
00191 bool UMLViewImageExporterModel::prepareDirectory(const KURL &url) const {
00192     // the KURL is copied to get protocol, user and so on and then the path is cleaned
00193     KURL directory = url;
00194     directory.setPath("");
00195 
00196     // creates the directory and any needed parent directories
00197     QStringList dirs = QStringList::split(QDir::separator(), url.directory());
00198     for (QStringList::ConstIterator it = dirs.begin() ; it != dirs.end(); ++it ) {
00199         directory.addPath(*it);
00200 
00201         if (!KIO::NetAccess::exists(directory, true, UMLApp::app())) {
00202 
00203             if (!KIO::NetAccess::mkdir(directory, UMLApp::app())) {
00204                 return false;
00205             }
00206         }
00207     }
00208 
00209     return true;
00210 }
00211 
00212 bool UMLViewImageExporterModel::exportViewTo(UMLView* view, const QString &imageType, const QString &fileName) const {
00213     // remove 'blue squares' from exported picture.
00214     view->clearSelected();
00215 
00216     QString imageMimeType = UMLViewImageExporterModel::imageTypeToMimeType(imageType);
00217     if (imageMimeType == "image/x-eps") {
00218         if (!exportViewToEps(view, fileName, true)) {
00219             return false;
00220         }
00221     } else if (imageMimeType == "image/svg+xml") {
00222         if (!exportViewToSvg(view, fileName)) {
00223             return false;
00224         }
00225     } else {
00226         if (!exportViewToPixmap(view, imageType, fileName)) {
00227             return false;
00228         }
00229     }
00230 
00231     return true;
00232 }
00233 
00234 bool UMLViewImageExporterModel::exportViewToEps(UMLView* view, const QString &fileName, bool isEPS) const {
00235     bool exportSuccessful = true;
00236 
00237     // print the image to a normal postscript file,
00238     // do not clip so that everything ends up in the file
00239     // regardless of "paper size"
00240 
00241     // because we want to work with postscript
00242     // user-coordinates, set to the resolution
00243     // of the printer (which should be 72dpi here)
00244     QPrinter *printer;
00245 
00246     if (isEPS == false) {
00247         printer = new QPrinter(QPrinter::PrinterResolution);
00248     } else {
00249         printer = new QPrinter(QPrinter::ScreenResolution);
00250     }
00251     printer->setOutputToFile(true);
00252     printer->setOutputFileName(fileName);
00253     printer->setColorMode(QPrinter::Color);
00254 
00255     // do not call printer.setup(); because we want no user
00256     // interaction here
00257     QPainter *painter = new QPainter(printer);
00258 
00259     // make sure the widget sizes will be according to the
00260     // actually used printer font, important for getDiagramRect()
00261     // and the actual painting
00262     view->forceUpdateWidgetFontMetrics(painter);
00263 
00264     QRect rect = view->getDiagramRect();
00265     painter->translate(-rect.x(),-rect.y());
00266     view->getDiagram(rect,*painter);
00267 
00268     int resolution = printer->resolution();
00269 
00270     // delete painter and printer before we try to open and fix the file
00271     delete painter;
00272     delete printer;
00273     if (isEPS) {
00274         // modify bounding box from screen to eps resolution.
00275         rect.setWidth( int(ceil(rect.width() * 72.0/resolution)) );
00276         rect.setHeight( int(ceil(rect.height() * 72.0/resolution)) );
00277         exportSuccessful = fixEPS(fileName,rect);
00278     }
00279     // next painting will most probably be to a different device (i.e. the screen)
00280     view->forceUpdateWidgetFontMetrics(0);
00281 
00282     return exportSuccessful;
00283 }
00284 
00285 bool UMLViewImageExporterModel::fixEPS(const QString &fileName, QRect rect) const {
00286     // now open the file and make a correct eps out of it
00287     QFile epsfile(fileName);
00288     if (! epsfile.open(IO_ReadOnly)) {
00289         return false;
00290     }
00291     // read
00292     QTextStream ts(&epsfile);
00293     QString fileContent = ts.read();
00294     epsfile.close();
00295 
00296     // read information
00297     QRegExp rx("%%BoundingBox:\\s*(-?[\\d\\.:]+)\\s*(-?[\\d\\.:]+)\\s*(-?[\\d\\.:]+)\\s*(-?[\\d\\.:]+)");
00298     const int pos = rx.search(fileContent);
00299     if (pos < 0) {
00300         kError() << "UMLViewImageExporterModel::fixEPS(" << fileName
00301                   << "): cannot find %%BoundingBox" << endl;
00302         return false;
00303     }
00304 
00305     // write new content to file
00306     if (! epsfile.open(IO_WriteOnly | IO_Truncate)) {
00307         kError() << "UMLViewImageExporterModel::fixEPS(" << fileName
00308                   << "): cannot open file for writing" << endl;
00309         return false;
00310     }
00311 
00312     // be careful when rounding (ceil/floor) the BB, these roundings
00313     // were mainly obtained experimentally...
00314     const double epsleft = rx.cap(1).toFloat();
00315     const double epstop = rx.cap(4).toFloat();
00316     const int left = int(floor(epsleft));
00317     const int right = int(ceil(epsleft)) + rect.width();
00318     const int top = int(ceil(epstop)) + 1;
00319     const int bottom = int(floor(epstop)) - rect.height() + 1;
00320 
00321     // modify content
00322     fileContent.replace(pos,rx.cap(0).length(),
00323                         QString("%%BoundingBox: %1 %2 %3 %4").arg(left).arg(bottom).arg(right).arg(top));
00324 
00325     ts << fileContent;
00326     epsfile.close();
00327 
00328     return true;
00329 }
00330 
00331 bool UMLViewImageExporterModel::exportViewToSvg(UMLView* view, const QString &fileName) const {
00332     bool exportSuccesful;
00333 
00334     QPicture* diagram = new QPicture();
00335 
00336     // do not call printer.setup(); because we want no user
00337     // interaction here
00338     QPainter* painter = new QPainter();
00339     painter->begin( diagram );
00340 
00341     // make sure the widget sizes will be according to the
00342     // actually used printer font, important for getDiagramRect()
00343     // and the actual painting
00344     view->forceUpdateWidgetFontMetrics(painter);
00345 
00346     QRect rect = view->getDiagramRect();
00347     painter->translate(-rect.x(),-rect.y());
00348     view->getDiagram(rect,*painter);
00349     painter->end();
00350     exportSuccesful = diagram->save(fileName, QString("SVG").ascii());
00351 
00352     // delete painter and printer before we try to open and fix the file
00353     delete painter;
00354     delete diagram;
00355     // next painting will most probably be to a different device (i.e. the screen)
00356     view->forceUpdateWidgetFontMetrics(0);
00357 
00358     return exportSuccesful;
00359 }
00360 
00361 bool UMLViewImageExporterModel::exportViewToPixmap(UMLView* view, const QString &imageType, const QString &fileName) const {
00362     QRect rect = view->getDiagramRect();
00363     QPixmap diagram(rect.width(), rect.height());
00364     view->getDiagram(rect, diagram);
00365     return diagram.save(fileName, imageType.upper().ascii());
00366 }
KDE Logo
This file is part of the documentation for umbrello Version 3.1.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Tue Jun 26 08:08:02 2007 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003