/*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * copyright (C) 2006-2007 * * Umbrello UML Modeller Authors * ***************************************************************************/ // own header #include "umlviewimageexportermodel.h" // system includes #include // include files for TQt #include #include #include #include #include #include #include #include // kde include files #include #include #include #include #include // application specific includes #include "uml.h" #include "umldoc.h" #include "umlview.h" #include "umllistview.h" #include "umllistviewitem.h" static TQStringList supportedImageTypesList; static TQStringList supportedMimeTypesList; TQStringList UMLViewImageExporterModel::supportedImageTypes() { if (!supportedImageTypesList.size()) { // specific supported formats supportedImageTypesList << "eps"; supportedImageTypesList << "svg"; // QT supported formats TQStrList qImageFormats = TQImage::outputFormats(); for (const char* format = qImageFormats.first(); format; format = qImageFormats.next()) { supportedImageTypesList << TQString(format).lower(); } } return supportedImageTypesList; } TQStringList UMLViewImageExporterModel::supportedMimeTypes() { if (!supportedMimeTypesList.size()) { TQStringList imageTypes = UMLViewImageExporterModel::supportedImageTypes(); for(TQStringList::Iterator it = imageTypes.begin(); it != imageTypes.end(); ++it ) { TQString mimeType = imageTypeToMimeType(*it); if (!mimeType.isNull()) supportedMimeTypesList.append(mimeType); } } return supportedMimeTypesList; } TQString UMLViewImageExporterModel::imageTypeToMimeType(const TQString& imageType) { const TQString imgType = imageType.lower(); if (TQString("bmp") == imgType) return "image/x-bmp"; if (TQString("jpeg") == imgType) return "image/jpeg"; if (TQString("pbm") == imgType) return "image/x-portable-bitmap"; if (TQString("pgm") == imgType) return "image/x-portable-greymap"; if (TQString("png") == imgType) return "image/png"; if (TQString("ppm") == imgType) return "image/x-portable-pixmap"; if (TQString("xbm") == imgType) return "image/x-xbm"; if (TQString("xpm") == imgType) return "image/x-xpm"; if (TQString("eps") == imgType) return "image/x-eps"; if (TQString("svg") == imgType) return "image/svg+xml"; return TQString(); } TQString UMLViewImageExporterModel::mimeTypeToImageType(const TQString& mimeType) { if (TQString("image/x-bmp") == mimeType) return "bmp"; if (TQString("image/jpeg") == mimeType) return "jpeg"; if (TQString("image/x-portable-bitmap") == mimeType) return "pbm"; if (TQString("image/x-portable-greymap") == mimeType) return "pgm"; if (TQString("image/png") == mimeType) return "png"; if (TQString("image/x-portable-pixmap") == mimeType) return "ppm"; if (TQString("image/x-xbm") == mimeType) return "xbm"; if (TQString("image/x-xpm") == mimeType) return "xpm"; if (TQString("image/x-eps") == mimeType) return "eps"; if (TQString("image/svg+xml") == mimeType) return "svg"; return TQString(); } TQStringList UMLViewImageExporterModel::exportAllViews(const TQString &imageType, const KURL &directory, bool useFolders) const { UMLApp *app = UMLApp::app(); // contains all the error messages returned by exportView calls TQStringList errors; UMLViewList views = app->getDocument()->getViewIterator(); for(UMLView *view = views.first(); view; view = views.next()) { KURL url = directory; url.addPath(getDiagramFileName(view, imageType, useFolders)); TQString returnString = exportView(view, imageType, url); if (!returnString.isNull()) { errors.append(view->getName() + ": " + returnString); } } return errors; } TQString UMLViewImageExporterModel::exportView(UMLView* view, const TQString &imageType, const KURL &url) const { // create the needed directories if (!prepareDirectory(url)) { return i18n("Can not create directory: %1").arg(url.directory()); } // The fileName will be used when exporting the image. If the url isn't local, // the fileName is the name of a temporal local file to export the image to, and then // upload it to its destiny TQString fileName; // tmpFile needs to be unlinked before exiting the method!!! KTempFile tmpFile; if (url.isLocalFile()) { fileName = url.path(); } else { fileName = tmpFile.name(); } // check that the diagram isn't empty TQRect rect = view->getDiagramRect(); if (rect.isEmpty()) { tmpFile.unlink(); return i18n("Can not save an empty diagram"); } // exporting the view to the file if (!exportViewTo(view, imageType, fileName)) { tmpFile.unlink(); return i18n("A problem occured while saving diagram in %1").arg(fileName); } // if the file wasn't local, upload the temp file to the target if (!url.isLocalFile()) { if (!TDEIO::NetAccess::upload(tmpFile.name(), url, UMLApp::app())) { tmpFile.unlink(); return i18n("There was a problem saving file: %1").arg(url.path()); } } //!isLocalFile tmpFile.unlink(); return TQString(); } TQString UMLViewImageExporterModel::getDiagramFileName(UMLView *view, const TQString &imageType, bool useFolders /* = false */) const { TQString name = view->getName() + '.' + imageType.lower(); if (!useFolders) { return name; } kapp->processEvents(); UMLListView *listView = UMLApp::app()->getListView(); UMLListViewItem* listViewItem = listView->findItem(view->getID()); // skip the name of the first item because it's the View listViewItem = static_cast(listViewItem->parent()); // Relies on the tree structure of the UMLListView. There are a base "Views" folder // and five children, one for each view type (Logical, use case, components, deployment // and entity relationship) while (listView->rootView(listViewItem->getType()) == NULL) { name.insert(0, listViewItem->getText() + '/'); listViewItem = static_cast(listViewItem->parent()); if (listViewItem == NULL) break; } return name; } bool UMLViewImageExporterModel::prepareDirectory(const KURL &url) const { // the KURL is copied to get protocol, user and so on and then the path is cleaned KURL directory = url; directory.setPath(""); // creates the directory and any needed parent directories TQStringList dirs = TQStringList::split(TQDir::separator(), url.directory()); for (TQStringList::ConstIterator it = dirs.begin() ; it != dirs.end(); ++it ) { directory.addPath(*it); if (!TDEIO::NetAccess::exists(directory, true, UMLApp::app())) { if (!TDEIO::NetAccess::mkdir(directory, UMLApp::app())) { return false; } } } return true; } bool UMLViewImageExporterModel::exportViewTo(UMLView* view, const TQString &imageType, const TQString &fileName) const { // remove 'blue squares' from exported picture. view->clearSelected(); TQString imageMimeType = UMLViewImageExporterModel::imageTypeToMimeType(imageType); if (imageMimeType == "image/x-eps") { if (!exportViewToEps(view, fileName, true)) { return false; } } else if (imageMimeType == "image/svg+xml") { if (!exportViewToSvg(view, fileName)) { return false; } } else { if (!exportViewToPixmap(view, imageType, fileName)) { return false; } } return true; } bool UMLViewImageExporterModel::exportViewToEps(UMLView* view, const TQString &fileName, bool isEPS) const { bool exportSuccessful = true; // print the image to a normal postscript file, // do not clip so that everything ends up in the file // regardless of "paper size" // because we want to work with postscript // user-coordinates, set to the resolution // of the printer (which should be 72dpi here) TQPrinter *printer; if (isEPS == false) { printer = new TQPrinter(TQPrinter::PrinterResolution); } else { printer = new TQPrinter(TQPrinter::ScreenResolution); } printer->setOutputToFile(true); printer->setOutputFileName(fileName); printer->setColorMode(TQPrinter::Color); // do not call printer.setup(); because we want no user // interaction here TQPainter *painter = new TQPainter(printer); // make sure the widget sizes will be according to the // actually used printer font, important for getDiagramRect() // and the actual painting view->forceUpdateWidgetFontMetrics(painter); TQRect rect = view->getDiagramRect(); painter->translate(-rect.x(),-rect.y()); view->getDiagram(rect,*painter); int resolution = printer->resolution(); // delete painter and printer before we try to open and fix the file delete painter; delete printer; if (isEPS) { // modify bounding box from screen to eps resolution. rect.setWidth( int(ceil(rect.width() * 72.0/resolution)) ); rect.setHeight( int(ceil(rect.height() * 72.0/resolution)) ); exportSuccessful = fixEPS(fileName,rect); } // next painting will most probably be to a different device (i.e. the screen) view->forceUpdateWidgetFontMetrics(0); return exportSuccessful; } bool UMLViewImageExporterModel::fixEPS(const TQString &fileName, const TQRect& rect) const { // now open the file and make a correct eps out of it TQFile epsfile(fileName); if (! epsfile.open(IO_ReadOnly)) { return false; } // read TQTextStream ts(&epsfile); TQString fileContent = ts.read(); epsfile.close(); // read information TQRegExp rx("%%BoundingBox:\\s*(-?[\\d\\.:]+)\\s*(-?[\\d\\.:]+)\\s*(-?[\\d\\.:]+)\\s*(-?[\\d\\.:]+)"); const int pos = rx.search(fileContent); if (pos < 0) { kError() << "UMLViewImageExporterModel::fixEPS(" << fileName << "): cannot find %%BoundingBox" << endl; return false; } // write new content to file if (! epsfile.open(IO_WriteOnly | IO_Truncate)) { kError() << "UMLViewImageExporterModel::fixEPS(" << fileName << "): cannot open file for writing" << endl; return false; } // be careful when rounding (ceil/floor) the BB, these roundings // were mainly obtained experimentally... const double epsleft = rx.cap(1).toFloat(); const double epstop = rx.cap(4).toFloat(); const int left = int(floor(epsleft)); const int right = int(ceil(epsleft)) + rect.width(); const int top = int(ceil(epstop)) + 1; const int bottom = int(floor(epstop)) - rect.height() + 1; // modify content fileContent.replace(pos,rx.cap(0).length(), TQString("%%BoundingBox: %1 %2 %3 %4").arg(left).arg(bottom).arg(right).arg(top)); ts << fileContent; epsfile.close(); return true; } bool UMLViewImageExporterModel::exportViewToSvg(UMLView* view, const TQString &fileName) const { bool exportSuccesful; TQPicture* diagram = new TQPicture(); // do not call printer.setup(); because we want no user // interaction here TQPainter* painter = new TQPainter(); painter->begin( diagram ); // make sure the widget sizes will be according to the // actually used printer font, important for getDiagramRect() // and the actual painting view->forceUpdateWidgetFontMetrics(painter); TQRect rect = view->getDiagramRect(); painter->translate(-rect.x(),-rect.y()); view->getDiagram(rect,*painter); painter->end(); exportSuccesful = diagram->save(fileName, TQString("SVG").ascii()); // delete painter and printer before we try to open and fix the file delete painter; delete diagram; // next painting will most probably be to a different device (i.e. the screen) view->forceUpdateWidgetFontMetrics(0); return exportSuccesful; } bool UMLViewImageExporterModel::exportViewToPixmap(UMLView* view, const TQString &imageType, const TQString &fileName) const { TQRect rect = view->getDiagramRect(); TQPixmap diagram(rect.width(), rect.height()); view->getDiagram(rect, diagram); return diagram.save(fileName, imageType.upper().ascii()); }