|
|
|
/***************************************************************************
|
|
|
|
* *
|
|
|
|
* 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 <uml-devel@uml.sf.net> *
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
|
|
// own header
|
|
|
|
#include "umlviewimageexportermodel.h"
|
|
|
|
|
|
|
|
// system includes
|
|
|
|
#include <math.h>
|
|
|
|
|
|
|
|
// include files for TQt
|
|
|
|
#include <tqstringlist.h>
|
|
|
|
#include <tqrect.h>
|
|
|
|
#include <tqimage.h>
|
|
|
|
#include <tqpicture.h>
|
|
|
|
#include <tqpainter.h>
|
|
|
|
#include <tqprinter.h>
|
|
|
|
#include <tqdir.h>
|
|
|
|
#include <tqregexp.h>
|
|
|
|
|
|
|
|
// kde include files
|
|
|
|
#include <kdebug.h>
|
|
|
|
#include <klocale.h>
|
|
|
|
#include <ktempfile.h>
|
|
|
|
#include <kapplication.h>
|
|
|
|
#include <kio/netaccess.h>
|
|
|
|
|
|
|
|
// 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").tqarg(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").tqarg(fileName);
|
|
|
|
}
|
|
|
|
|
|
|
|
// if the file wasn't local, upload the temp file to the target
|
|
|
|
if (!url.isLocalFile()) {
|
|
|
|
if (!KIO::NetAccess::upload(tmpFile.name(), url, UMLApp::app())) {
|
|
|
|
tmpFile.unlink();
|
|
|
|
return i18n("There was a problem saving file: %1").tqarg(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<UMLListViewItem*>(listViewItem->parent());
|
|
|
|
|
|
|
|
// Relies on the tree structure of the UMLListView. There are a base "Views" folder
|
|
|
|
// and five tqchildren, 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<UMLListViewItem*>(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 (!KIO::NetAccess::exists(directory, true, UMLApp::app())) {
|
|
|
|
|
|
|
|
if (!KIO::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").tqarg(left).tqarg(bottom).tqarg(right).tqarg(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());
|
|
|
|
}
|