Scribus Developer Documentation - API
logo-pen

Scribus Developer Documentation - API

svgplugin.cpp

Go to the documentation of this file.
00001 /*
00002 For general Scribus (>=1.3.2) copyright and licensing information please refer
00003 to the COPYING file provided with the program. Following this notice may exist
00004 a copyright and/or license notice that predates the release of Scribus 1.3.2
00005 for which a new license (GPL+exception) is in place.
00006 */
00007 
00008 #include <QCursor>
00009 #include <QDrag>
00010 #include <QFile>
00011 #include <QList>
00012 #include <QMimeData>
00013 #include <QPainterPath>
00014 #include <QRegExp>
00015 #include <cmath>
00016 #include <zlib.h>
00017 
00018 #include "color.h"
00019 #include "commonstrings.h"
00020 #include "customfdialog.h"
00021 #include "fonts/scfontmetrics.h"
00022 #include "fpointarray.h"
00023 #include "loadsaveplugin.h"
00024 #include "menumanager.h"
00025 #include "pageitem.h"
00026 #include "prefsfile.h"
00027 #include "prefsmanager.h"
00028 #include "propertiespalette.h"
00029 #include "sccolorengine.h"
00030 #include "scconfig.h"
00031 #include "scraction.h"
00032 #include "scribus.h"
00033 #include "scribusXml.h"
00034 #include "scribuscore.h"
00035 #include "scribusdoc.h"
00036 #include "selection.h"
00037 #include "svgplugin.h"
00038 #include "undomanager.h"
00039 #include "util.h"
00040 #include "util_formats.h"
00041 #include "util_icon.h"
00042 #include "util_math.h"
00043 
00044 
00045 using namespace std;
00046 
00047 int svgimplugin_getPluginAPIVersion()
00048 {
00049     return PLUGIN_API_VERSION;
00050 }
00051 
00052 ScPlugin* svgimplugin_getPlugin()
00053 {
00054     SVGImportPlugin* plug = new SVGImportPlugin();
00055     Q_CHECK_PTR(plug);
00056     return plug;
00057 }
00058 
00059 void svgimplugin_freePlugin(ScPlugin* plugin)
00060 {
00061     SVGImportPlugin* plug = dynamic_cast<SVGImportPlugin*>(plugin);
00062     Q_ASSERT(plug);
00063     delete plug;
00064 }
00065 
00066 SVGImportPlugin::SVGImportPlugin() : LoadSavePlugin(),
00067     importAction(new ScrAction(ScrAction::DLL, QPixmap(), QPixmap(), "", QKeySequence(), this))
00068 {
00069     // Set action info in languageChange, so we only have to do
00070     // it in one place. This includes registering file format
00071     // support.
00072     languageChange();
00073 }
00074 
00075 void SVGImportPlugin::addToMainWindowMenu(ScribusMainWindow *mw)
00076 {
00077     importAction->setEnabled(true);
00078     connect( importAction, SIGNAL(triggered()), SLOT(import()) );
00079     mw->scrMenuMgr->addMenuItem(importAction, "FileImport");
00080 }
00081 
00082 SVGImportPlugin::~SVGImportPlugin()
00083 {
00084     unregisterAll();
00085 };
00086 
00087 void SVGImportPlugin::languageChange()
00088 {
00089     importAction->setText( tr("Import &SVG..."));
00090     // (Re)register file format support.
00091     unregisterAll();
00092     registerFormats();
00093 }
00094 
00095 const QString SVGImportPlugin::fullTrName() const
00096 {
00097     return QObject::tr("SVG Import");
00098 }
00099 
00100 const ScActionPlugin::AboutData* SVGImportPlugin::getAboutData() const
00101 {
00102     AboutData* about = new AboutData;
00103     about->authors = "Franz Schmid <franz@scribus.info>";
00104     about->shortDescription = tr("Imports SVG Files");
00105     about->description = tr("Imports most SVG files into the current document,\nconverting their vector data into Scribus objects.");
00106     about->license = "GPL";
00107     Q_CHECK_PTR(about);
00108     return about;
00109 }
00110 
00111 void SVGImportPlugin::deleteAboutData(const AboutData* about) const
00112 {
00113     Q_ASSERT(about);
00114     delete about;
00115 }
00116 
00117 void SVGImportPlugin::registerFormats()
00118 {
00119     FileFormat fmt(this);
00120     fmt.trName = FormatsManager::instance()->nameOfFormat(FormatsManager::SVG);
00121     fmt.formatId = FORMATID_SVGIMPORT;
00122     fmt.filter = FormatsManager::instance()->extensionsForFormat(FormatsManager::SVG);
00123     fmt.nameMatch = QRegExp("\\."+FormatsManager::instance()->extensionListForFormat(FormatsManager::SVG, 1)+"$", Qt::CaseInsensitive);
00124     fmt.load = true;
00125     fmt.save = false;
00126     fmt.mimeTypes = FormatsManager::instance()->mimetypeOfFormat(FormatsManager::SVG);
00127     fmt.priority = 64;
00128     registerFormat(fmt);
00129 }
00130 
00131 bool SVGImportPlugin::fileSupported(QIODevice* /* file */, const QString & fileName) const
00132 {
00133     // TODO: identify valid SVG
00134     return true;
00135 }
00136 
00137 bool SVGImportPlugin::loadFile(const QString & fileName, const FileFormat & /* fmt */, int flags, int /*index*/)
00138 {
00139     // For now, "load file" and import are the same thing for this plugin
00140     return import(fileName, flags);
00141 }
00142 
00143 bool SVGImportPlugin::import(QString filename, int flags)
00144 {
00145     if (!checkFlags(flags))
00146         return false;
00147     m_Doc=ScCore->primaryMainWindow()->doc;
00148     ScribusMainWindow* mw=(m_Doc==0) ? ScCore->primaryMainWindow() : m_Doc->scMW();
00149     if (filename.isEmpty())
00150     {
00151         flags |= lfInteractive;
00152         PrefsContext* prefs = PrefsManager::instance()->prefsFile->getPluginContext("SVGPlugin");
00153         QString wdir = prefs->get("wdir", ".");
00154         CustomFDialog diaf(mw, wdir, QObject::tr("Open"), FormatsManager::instance()->fileDialogFormatList(FormatsManager::SVG));
00155         if (diaf.exec())
00156         {
00157             filename = diaf.selectedFile();
00158             prefs->set("wdir", filename.left(filename.lastIndexOf("/")));
00159         }
00160         else
00161             return true;
00162     }
00163     
00164     UndoTransaction* activeTransaction = NULL;
00165     bool emptyDoc = (m_Doc == NULL);
00166     if (UndoManager::undoEnabled() && !emptyDoc)
00167     {
00168         activeTransaction = new UndoTransaction(UndoManager::instance()->beginTransaction(m_Doc->currentPage()->getUName(),Um::IImageFrame,Um::ImportSVG, filename, Um::ISVG));
00169     }
00170     else if (UndoManager::undoEnabled() && emptyDoc)
00171         UndoManager::instance()->setUndoEnabled(false);
00172     SVGPlug *dia = new SVGPlug(mw, flags);
00173     dia->import(filename, flags);
00174     Q_CHECK_PTR(dia);
00175     if (activeTransaction)
00176     {
00177         activeTransaction->commit();
00178         delete activeTransaction;
00179         activeTransaction = NULL;
00180     }
00181     else if (UndoManager::undoEnabled() && emptyDoc)
00182         UndoManager::instance()->setUndoEnabled(true);
00183     if (dia->importCanceled)
00184     {
00185         if (dia->importFailed)
00186             QMessageBox::warning(mw, CommonStrings::trWarning, tr("The file could not be imported"), 1, 0, 0);
00187         else if (dia->unsupported)
00188             QMessageBox::warning(mw, CommonStrings::trWarning, tr("SVG file contains some unsupported features"), 1, 0, 0);
00189     }
00190 
00191     delete dia;
00192     return true;
00193 }
00194 
00195 SVGPlug::SVGPlug( ScribusMainWindow* mw, int flags ) :
00196     QObject(mw)
00197 {   
00198     tmpSel=new Selection(this, false);
00199     m_Doc=mw->doc;
00200     unsupported = false;
00201     importFailed = false;
00202     importCanceled = true;
00203     importedColors.clear();
00204     docDesc = "";
00205     docTitle = "";
00206     groupLevel = 0;
00207     interactive = (flags & LoadSavePlugin::lfInteractive);
00208 //  m_gc.setAutoDelete( true );
00209 }
00210 
00211 bool SVGPlug::import(QString fname, int flags)
00212 {
00213     if (!loadData(fname))
00214         return false;
00215     QString CurDirP = QDir::currentPath();
00216     QFileInfo efp(fname);
00217     QDir::setCurrent(efp.path());
00218     convert(flags);
00219     QDir::setCurrent(CurDirP);
00220     return true;
00221 }
00222 
00223 bool SVGPlug::loadData(QString fName)
00224 {
00225     QString f("");
00226     bool isCompressed = false;
00227     QByteArray bb(3, ' ');
00228     QFile fi(fName);
00229     if (fi.open(QIODevice::ReadOnly))
00230     {
00231         fi.read(bb.data(), 2);
00232         fi.close();
00233         // Qt4 bb[0]->QChar(bb[0])
00234         if ((QChar(bb[0]) == QChar(0x1F)) && (QChar(bb[1]) == QChar(0x8B)))
00235             isCompressed = true;
00236     }
00237     if ((fName.right(2) == "gz") || (isCompressed))
00238     {
00239         gzFile gzDoc;
00240         char buff[4097];
00241         int i;
00242         gzDoc = gzopen(fName.toLatin1().constData(),"rb");
00243         if(gzDoc == NULL)
00244             return false;
00245         while((i = gzread(gzDoc,&buff,4096)) > 0)
00246         {
00247             buff[i] = '\0';
00248             f.append(buff);
00249         }
00250         gzclose(gzDoc);
00251     }
00252     else
00253         loadText(fName, &f);
00254     return inpdoc.setContent(f);
00255 }
00256 
00257 void SVGPlug::convert(int flags)
00258 {
00259     bool ret = false;
00260     SvgStyle *gc = new SvgStyle;
00261     QDomElement docElem = inpdoc.documentElement();
00262     QSize wh = parseWidthHeight(docElem);
00263     double width = wh.width();
00264     double height = wh.height();
00265     if (!interactive || (flags & LoadSavePlugin::lfInsertPage))
00266     {
00267         m_Doc->setPage(width, height, 0, 0, 0, 0, 0, 0, false, false);
00268         m_Doc->addPage(0);
00269         m_Doc->view()->addPage(0);
00270     }
00271     else
00272     {
00273         if (!m_Doc || (flags & LoadSavePlugin::lfCreateDoc))
00274         {
00275             m_Doc=ScCore->primaryMainWindow()->doFileNew(width, height, 0, 0, 0, 0, 0, 0, false, false, 0, false, 0, 1, "Custom", true);
00276             ScCore->primaryMainWindow()->HaveNewDoc();
00277             ret = true;
00278         }
00279     }
00280     if ((ret) || (!interactive))
00281     {
00282         if (width > height)
00283             m_Doc->PageOri = 1;
00284         else
00285             m_Doc->PageOri = 0;
00286         m_Doc->m_pageSize = "Custom";
00287     }
00288     FPoint minSize = m_Doc->minCanvasCoordinate;
00289     FPoint maxSize = m_Doc->maxCanvasCoordinate;
00290     FPoint cOrigin = m_Doc->view()->canvasOrigin();
00291     m_Doc->view()->Deselect();
00292     m_Doc->setLoading(true);
00293     m_Doc->DoDrawing = false;
00294     m_Doc->view()->updatesOn(false);
00295     m_Doc->scMW()->ScriptRunning = true;
00296     qApp->changeOverrideCursor(QCursor(Qt::WaitCursor));
00297     gc->FontFamily = m_Doc->toolSettings.defFont;
00298     if (!m_Doc->PageColors.contains("Black"))
00299         m_Doc->PageColors.insert("Black", ScColor(0, 0, 0, 255));
00300     m_gc.push( gc );
00301     viewTransformX = 0;
00302     viewTransformY = 0;
00303     viewScaleX = 1;
00304     viewScaleY = 1;
00305     if( !docElem.attribute( "viewBox" ).isEmpty() )
00306     {
00307         QString viewbox( docElem.attribute( "viewBox" ) );
00308         QStringList points = viewbox.replace( QRegExp(","), " ").simplified().split( ' ', QString::SkipEmptyParts );
00309         if (points.size() > 3)
00310         {
00311             QMatrix matrix;
00312             QSize wh2 = parseWidthHeight(docElem);
00313             double w2 = wh2.width();
00314             double h2 = wh2.height();
00315             addGraphicContext();
00316             viewTransformX = points[0].toDouble();
00317             viewTransformY = points[1].toDouble();
00318             viewScaleX = w2 / points[2].toDouble();
00319             viewScaleY = h2 / points[3].toDouble();
00320             matrix.translate(viewTransformX, viewTransformY);
00321             matrix.scale(viewScaleX, viewScaleY);
00322             m_gc.top()->matrix = matrix;
00323         }
00324     }
00325     QList<PageItem*> Elements = parseGroup( docElem );
00326     if (flags & LoadSavePlugin::lfCreateDoc)
00327     {
00328         m_Doc->documentInfo.setTitle(docTitle);
00329         m_Doc->documentInfo.setComments(docDesc);
00330     }
00331     tmpSel->clear();
00332     if (Elements.count() == 0)
00333     {
00334         importFailed = true;
00335         if (importedColors.count() != 0)
00336         {
00337             for (int cd = 0; cd < importedColors.count(); cd++)
00338             {
00339                 m_Doc->PageColors.remove(importedColors[cd]);
00340             }
00341         }
00342     }
00343     if (Elements.count() > 1)
00344     {
00345         bool isGroup = true;
00346         int firstElem = -1;
00347         if (Elements.at(0)->Groups.count() != 0)
00348             firstElem = Elements.at(0)->Groups.top();
00349         for (int bx = 0; bx < Elements.count(); ++bx)
00350         {
00351             PageItem* bxi = Elements.at(bx);
00352             if (bxi->Groups.count() != 0)
00353             {
00354                 if (bxi->Groups.top() != firstElem)
00355                     isGroup = false;
00356             }
00357             else
00358                 isGroup = false;
00359         }
00360         if (!isGroup)
00361         {
00362             double minx = 99999.9;
00363             double miny = 99999.9;
00364             double maxx = -99999.9;
00365             double maxy = -99999.9;
00366             uint lowestItem = 999999;
00367             uint highestItem = 0;
00368             for (int a = 0; a < Elements.count(); ++a)
00369             {
00370                 Elements.at(a)->Groups.push(m_Doc->GroupCounter);
00371                 PageItem* currItem = Elements.at(a);
00372                 lowestItem = qMin(lowestItem, currItem->ItemNr);
00373                 highestItem = qMax(highestItem, currItem->ItemNr);
00374                 double lw = currItem->lineWidth() / 2.0;
00375                 if (currItem->rotation() != 0)
00376                 {
00377                     FPointArray pb;
00378                     pb.resize(0);
00379                     pb.addPoint(FPoint(currItem->xPos()-lw, currItem->yPos()-lw));
00380                     pb.addPoint(FPoint(currItem->width()+lw*2.0, -lw, currItem->xPos()-lw, currItem->yPos()-lw, currItem->rotation(), 1.0, 1.0));
00381                     pb.addPoint(FPoint(currItem->width()+lw*2.0, currItem->height()+lw*2.0, currItem->xPos()-lw, currItem->yPos()-lw, currItem->rotation(), 1.0, 1.0));
00382                     pb.addPoint(FPoint(-lw, currItem->height()+lw*2.0, currItem->xPos()-lw, currItem->yPos()-lw, currItem->rotation(), 1.0, 1.0));
00383                     for (uint pc = 0; pc < 4; ++pc)
00384                     {
00385                         minx = qMin(minx, pb.point(pc).x());
00386                         miny = qMin(miny, pb.point(pc).y());
00387                         maxx = qMax(maxx, pb.point(pc).x());
00388                         maxy = qMax(maxy, pb.point(pc).y());
00389                     }
00390                 }
00391                 else
00392                 {
00393                     minx = qMin(minx, currItem->xPos()-lw);
00394                     miny = qMin(miny, currItem->yPos()-lw);
00395                     maxx = qMax(maxx, currItem->xPos()-lw + currItem->width()+lw*2.0);
00396                     maxy = qMax(maxy, currItem->yPos()-lw + currItem->height()+lw*2.0);
00397                 }
00398             }
00399             double gx = minx;
00400             double gy = miny;
00401             double gw = maxx - minx;
00402             double gh = maxy - miny;
00403             PageItem *high = m_Doc->Items->at(highestItem);
00404             int z = m_Doc->itemAdd(PageItem::Polygon, PageItem::Rectangle, gx, gy, gw, gh, 0, m_Doc->toolSettings.dBrush, m_Doc->toolSettings.dPen, true);
00405             PageItem *neu = m_Doc->Items->takeAt(z);
00406             m_Doc->Items->insert(lowestItem, neu);
00407             neu->Groups.push(m_Doc->GroupCounter);
00408             neu->setItemName( tr("Group%1").arg(neu->Groups.top()));
00409             neu->isGroupControl = true;
00410             neu->groupsLastItem = high;
00411             for (int a = 0; a < m_Doc->Items->count(); ++a)
00412             {
00413                 m_Doc->Items->at(a)->ItemNr = a;
00414             }
00415             neu->setRedrawBounding();
00416             neu->setTextFlowMode(PageItem::TextFlowUsesFrameShape);
00417             Elements.prepend(neu);
00418             m_Doc->GroupCounter++;
00419         }
00420     }
00421     m_Doc->DoDrawing = true;
00422     m_Doc->scMW()->ScriptRunning = false;
00423     if (interactive)
00424         m_Doc->setLoading(false);
00425     qApp->changeOverrideCursor(QCursor(Qt::ArrowCursor));
00426     if ((Elements.count() > 0) && (!ret) && (interactive))
00427     {
00428         if (flags & LoadSavePlugin::lfScripted)
00429         {
00430             bool loadF = m_Doc->isLoading();
00431             m_Doc->setLoading(false);
00432             m_Doc->changed();
00433             m_Doc->setLoading(loadF);
00434             for (int dre=0; dre<Elements.count(); ++dre)
00435             {
00436                 m_Doc->m_Selection->addItem(Elements.at(dre), true);
00437             }
00438             m_Doc->m_Selection->setGroupRect();
00439             m_Doc->view()->updatesOn(true);
00440             importCanceled = false;
00441         }
00442         else
00443         {
00444             m_Doc->DragP = true;
00445             m_Doc->DraggedElem = 0;
00446             m_Doc->DragElements.clear();
00447             for (int dre=0; dre<Elements.count(); ++dre)
00448             {
00449                 m_Doc->DragElements.append(Elements.at(dre)->ItemNr);
00450                 tmpSel->addItem(Elements.at(dre), true);
00451             }
00452             ScriXmlDoc *ss = new ScriXmlDoc();
00453             tmpSel->setGroupRect();
00454             QMimeData* md = new QMimeData();
00455             md->setText(ss->WriteElem(m_Doc, m_Doc->view(), tmpSel));
00456             QDrag* dr = new QDrag(m_Doc->view()->viewport());
00457             dr->setMimeData(md);
00458 #ifndef QT_WS_MAC
00459 // see #2526
00460             m_Doc->itemSelection_DeleteItem(tmpSel);
00461 #endif
00462 //          m_Doc->view()->resizeContents(qRound((maxSize.x() - minSize.x()) * m_Doc->view()->scale()), qRound((maxSize.y() - minSize.y()) * m_Doc->view()->scale()));
00463 //          m_Doc->view()->scrollBy(qRound((m_Doc->minCanvasCoordinate.x() - minSize.x()) * m_Doc->view()->scale()), qRound((m_Doc->minCanvasCoordinate.y() - minSize.y()) * m_Doc->view()->scale()));
00464             m_Doc->minCanvasCoordinate = minSize;
00465             m_Doc->maxCanvasCoordinate = maxSize;
00466             m_Doc->view()->adjustCanvas(qRound((maxSize.x() - minSize.x()) * m_Doc->view()->scale()), qRound((maxSize.y() - minSize.y()) * m_Doc->view()->scale()), 0, 0);
00467             m_Doc->view()->setCanvasOrigin(cOrigin.x(), cOrigin.y());
00468             m_Doc->view()->updatesOn(true);
00469             m_Doc->view()->updateCanvas();
00470             const QPixmap& dragCursor = loadIcon("DragPix.xpm");
00471             dr->setDragCursor(dragCursor, Qt::CopyAction);
00472             dr->setDragCursor(dragCursor, Qt::MoveAction);
00473             dr->setDragCursor(dragCursor, Qt::LinkAction);
00474             dr->exec();
00475             /* JG : incorrect, see the Qt Reference: "The function returns TRUE if the caller should 
00476             delete the original copy of the dragged data */
00477             /*importCanceled = dr->drag();
00478             if (!importCanceled)
00479             {
00480                 if (importedColors.count() != 0)
00481                 {
00482                     for (int cd = 0; cd < importedColors.count(); cd++)
00483                     {
00484                         m_Doc->PageColors.remove(importedColors[cd]);
00485                     }
00486                 }
00487             }*/
00488             delete ss;
00489             m_Doc->DragP = false;
00490             m_Doc->DraggedElem = 0;
00491             m_Doc->DragElements.clear();
00492         }
00493     }
00494     else
00495     {
00496         bool loadF = m_Doc->isLoading();
00497         m_Doc->setLoading(false);
00498         m_Doc->changed();
00499         m_Doc->reformPages();
00500         m_Doc->view()->updatesOn(true);
00501         m_Doc->setLoading(loadF);
00502     }
00503 }
00504 
00505 void SVGPlug::addGraphicContext()
00506 {
00507     SvgStyle *gc = new SvgStyle;
00508     if ( m_gc.top() )
00509         *gc = *( m_gc.top() );
00510     m_gc.push( gc );
00511 }
00512 
00513 void SVGPlug::setupNode( const QDomElement &e )
00514 {
00515     addGraphicContext();
00516     setupTransform( e );
00517     parseStyle(m_gc.top(), e);
00518 }
00519 
00520 void SVGPlug::setupTransform( const QDomElement &e )
00521 {
00522     SvgStyle *gc = m_gc.top();
00523     QMatrix mat = parseTransform( e.attribute( "transform" ) );
00524     if (!e.attribute("transform").isEmpty())
00525         gc->matrix = mat * gc->matrix;
00526 }
00527 
00528 void SVGPlug::finishNode( const QDomNode &e, PageItem* item)
00529 {
00530     SvgStyle *gc = m_gc.top();
00531     QMatrix gcm = gc->matrix;
00532     double BaseX = m_Doc->currentPage()->xOffset();
00533     double BaseY = m_Doc->currentPage()->yOffset();
00534     double coeff1 = sqrt(gcm.m11() * gcm.m11() + gcm.m12() * gcm.m12());
00535     double coeff2 = sqrt(gcm.m21() * gcm.m21() + gcm.m22() * gcm.m22());
00536     switch (item->itemType())
00537     {
00538     case PageItem::ImageFrame:
00539         {
00540             QMatrix mm = gc->matrix;
00541             item->moveBy(mm.dx(), mm.dy());
00542             item->setWidthHeight(item->width() * mm.m11(), item->height() * mm.m22());
00543             item->setLineWidth(item->lineWidth() * (coeff1 + coeff2) / 2.0);
00544             if (item->PicAvail)
00545                 item->setImageXYScale(item->width() / item->pixm.width(), item->height() / item->pixm.height());
00546             break;
00547         }
00548     case PageItem::TextFrame:
00549         {
00550             QMatrix mm = gc->matrix;
00551             item->setLineWidth(item->lineWidth() * (coeff1 + coeff2) / 2.0);
00552         }
00553         break;
00554     default:
00555         {
00556             item->ClipEdited = true;
00557             item->FrameType = 3;
00558             QMatrix mm = gc->matrix;
00559             item->PoLine.map(mm);
00560             /*if (haveViewBox)
00561             {
00562                 QMatrix mv;
00563                 mv.translate(viewTransformX, viewTransformY);
00564                 mv.scale(viewScaleX, viewScaleY);
00565                 ite->PoLine.map(mv);
00566             }*/
00567             item->setLineWidth(item->lineWidth() * (coeff1 + coeff2) / 2.0);
00568             FPoint wh = getMaxClipF(&item->PoLine);
00569             item->setWidthHeight(wh.x(), wh.y());
00570 //          if (item->asPolyLine())
00571 //              item->setPolyClip(qRound(qMax(item->lineWidth() / 2.0, 1)));
00572 //          else
00573 //              item->Clip = FlattenPath(item->PoLine, item->Segments);
00574             m_Doc->AdjustItemSize(item);
00575             break;
00576         }
00577     }
00578     item->setRedrawBounding();
00579     item->OwnPage = m_Doc->OnPage(item);
00580     if (e.isElement())
00581     {
00582         QString nodeId = e.toElement().attribute("id");
00583         if( !nodeId.isEmpty() )
00584             item->setItemName(" "+nodeId);
00585     }
00586     item->setFillTransparency( 1 - gc->FillOpacity * gc->Opacity );
00587     item->setLineTransparency( 1 - gc->StrokeOpacity * gc->Opacity );
00588     item->PLineEnd = gc->PLineEnd;
00589     item->PLineJoin = gc->PLineJoin;
00590     if (item->fillColor() == CommonStrings::None)
00591         item->setTextFlowMode(PageItem::TextFlowDisabled);
00592     else
00593         item->setTextFlowMode(PageItem::TextFlowUsesFrameShape);
00594     item->DashOffset = gc->dashOffset;
00595     item->DashValues = gc->dashArray;
00596     if (gc->Gradient != 0)
00597     {
00598         if (gc->GradCo.Stops() > 1)
00599         {
00600             item->fill_gradient = gc->GradCo;
00601             if (!gc->CSpace)
00602             {
00603                 item->GrStartX = gc->GX1 * item->width();
00604                 item->GrStartY = gc->GY1 * item->height();
00605                 item->GrEndX = gc->GX2 * item->width();
00606                 item->GrEndY = gc->GY2 * item->height();
00607                 double angle1 = atan2(gc->GY2-gc->GY1,gc->GX2-gc->GX1)*(180.0/M_PI);
00608                 double angle2 = atan2(item->GrEndY - item->GrStartX, item->GrEndX - item->GrStartX)*(180.0/M_PI);
00609                 double dx = item->GrStartX + (item->GrEndX - item->GrStartX) / 2.0;
00610                 double dy = item->GrStartY + (item->GrEndY - item->GrStartY) / 2.0;
00611                 QMatrix mm, mm2;
00612                 if ((gc->GY1 < gc->GY2) && (gc->GX1 < gc->GX2))
00613                 {
00614                     mm.rotate(-angle2);
00615                     mm2.rotate(angle1);
00616                 }
00617                 FPointArray gra;
00618                 gra.setPoints(2, item->GrStartX-dx, item->GrStartY-dy, item->GrEndX-dx, item->GrEndY-dy);
00619                 gra.map(mm*mm2);
00620                 gra.translate(dx, dy);
00621                 item->GrStartX = gra.point(0).x();
00622                 item->GrStartY = gra.point(0).y();
00623                 item->GrEndX = gra.point(1).x();
00624                 item->GrEndY = gra.point(1).y();
00625             }
00626             else
00627             {
00628                 QMatrix mm = gc->matrix;
00629                 mm = gc->matrixg * mm;
00630                 FPointArray gra;
00631                 gra.setPoints(2, gc->GX1, gc->GY1, gc->GX2, gc->GY2);
00632                 gra.map(mm);
00633                 gc->GX1 = gra.point(0).x();
00634                 gc->GY1 = gra.point(0).y();
00635                 gc->GX2 = gra.point(1).x();
00636                 gc->GY2 = gra.point(1).y();
00637                 item->GrStartX = gc->GX1 - item->xPos() + BaseX;
00638                 item->GrStartY = gc->GY1 - item->yPos() + BaseY;
00639                 item->GrEndX = gc->GX2 - item->xPos() + BaseX;
00640                 item->GrEndY = gc->GY2 - item->yPos() + BaseY;
00641             }
00642             item->GrType = gc->Gradient;
00643         }
00644         else
00645         {
00646             item->GrType = 0;
00647             QList<VColorStop*> cstops = gc->GradCo.colorStops();
00648             item->setFillColor(cstops.at(0)->name);
00649             item->setFillShade(cstops.at(0)->shade);
00650         }
00651     }
00652 }
00653 
00654 bool SVGPlug::isIgnorableNode( const QDomElement &e )
00655 {
00656     QString nodeName(e.tagName());
00657     return isIgnorableNodeName(nodeName);
00658 }
00659 
00660 bool SVGPlug::isIgnorableNodeName( const QString &n )
00661 {
00662     if (n.startsWith("sodipodi") || n.startsWith("inkscape") || n == "metadata")
00663         return true;
00664     return false;
00665 }
00666 
00667 FPoint SVGPlug::parseTextPosition(const QDomElement &e)
00668 {
00669     // FIXME According to spec, we should in fact return a point list 
00670     QString xatt =  e.attribute( "x", "0" );
00671     QString yatt =  e.attribute( "y", "0" );
00672     if ( xatt.contains(',') || xatt.contains(' ') )
00673     {
00674         xatt.replace(QChar(','), QChar(' '));
00675         QStringList xl(xatt.split(QChar(' '), QString::SkipEmptyParts));
00676         xatt = xl.first();
00677     }
00678     if ( yatt.contains(',') || yatt.contains(' ') )
00679     {
00680         yatt.replace(QChar(','), QChar(' '));
00681         QStringList yl(yatt.split(QChar(' '), QString::SkipEmptyParts));
00682         yatt = yl.first();
00683     }
00684     double x = parseUnit( xatt );
00685     double y = parseUnit( yatt );
00686     return FPoint(x, y);
00687 }
00688 
00689 QSize SVGPlug::parseWidthHeight(const QDomElement &e)
00690 {
00691     QSize size(550, 841);
00692     QString sw = e.attribute("width", "100%");
00693     QString sh = e.attribute("height", "100%");
00694     double w =  550, h = 841;
00695     if (!sw.isEmpty())
00696         w = sw.endsWith("%") ? fromPercentage(sw) : parseUnit(sw);
00697     if (!sh.isEmpty())
00698         h = sh.endsWith("%") ? fromPercentage(sh) : parseUnit(sh);
00699     if (!e.attribute("viewBox").isEmpty())
00700     {
00701         QRect viewBox = parseViewBox(e);
00702         double scw = (viewBox.width() > 0 && viewBox.height() > 0) ? viewBox.width()  : size.width();
00703         double sch = (viewBox.width() > 0 && viewBox.height() > 0) ? viewBox.height() : size.height();
00704         w *= (sw.endsWith("%") ? scw : 1.0);
00705         h *= (sh.endsWith("%") ? sch : 1.0);
00706     }
00707     else
00708     {
00709         w *= (sw.endsWith("%") ? size.width() : 1.0);
00710         h *= (sh.endsWith("%") ? size.height() : 1.0);
00711     }
00712     // OpenOffice files may not have width and height attributes, so avoid unnecessary large dimensions
00713     if (w > 10000 || h > 10000)
00714     {
00715         double m = max(w, h);
00716         w = w / m * 842;
00717         h = h / m * 842;
00718     }
00719     size.setWidth(qRound(w));
00720     size.setHeight(qRound(h));
00721     return size;
00722 }
00723 
00724 QRect SVGPlug::parseViewBox(const QDomElement &e)
00725 {
00726     QRect box(0, 0, 0, 0);
00727     if ( !e.attribute( "viewBox" ).isEmpty() )
00728     {
00729         QString viewbox( e.attribute( "viewBox" ) );
00730         QStringList points = viewbox.replace( QRegExp(","), " ").simplified().split( ' ', QString::SkipEmptyParts );
00731         if (points.size() > 3)
00732         {
00733             double left = points[0].toDouble();
00734             double bottom  = points[1].toDouble();
00735             double width = points[2].toDouble();
00736             double height = points[3].toDouble();
00737             box.setCoords((int) left, (int) bottom, (int) (left + width), (int) (bottom + height));
00738         }
00739     }
00740     return box;
00741 }
00742 
00743 void SVGPlug::parseDefs(const QDomElement &e)
00744 {
00745     for ( QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling() )
00746     {
00747         QDomElement b = n.toElement();
00748         if ( b.isNull() )
00749             continue;
00750         SvgStyle svgStyle;
00751         parseStyle( &svgStyle, b );
00752         if ( !svgStyle.Display) 
00753             continue;
00754         QString STag2 = b.tagName();
00755         if ( STag2 == "g" )
00756         {
00757             QString id = b.attribute("id", "");
00758             if (!id.isEmpty())
00759                 m_nodeMap.insert(id, b);
00760             parseDefs(b);
00761         }
00762         else if ( STag2 == "linearGradient" || STag2 == "radialGradient" )
00763             parseGradient( b );
00764         else if (STag2 == "clipPath")
00765             parseClipPath(b);
00766         else if ( b.hasAttribute("id") )
00767         {
00768             QString id = b.attribute("id");
00769             if (!id.isEmpty())
00770                 m_nodeMap.insert(id, b);
00771         }
00772     }
00773 }
00774 
00775 void SVGPlug::parseClipPath(const QDomElement &e)
00776 {
00777     QString id(e.attribute("id"));
00778     if (!id.isEmpty())
00779     {
00780         FPointArray clip;
00781         QDomNode n2 = e.firstChild();
00782         QDomElement b2 = n2.toElement();
00783         while (b2.nodeName() == "use")
00784             b2 = getReferencedNode(b2);
00785         if (b2.nodeName() == "path")
00786             parseSVG( b2.attribute( "d" ), &clip );
00787         else if (b2.nodeName() == "rect")
00788         {
00789             double width = parseUnit( b2.attribute( "width" ));
00790             double height = parseUnit( b2.attribute( "height" ) );
00791             clip.addQuadPoint(0.0, 0.0, 0.0, 0.0, width, 0.0, width, 0.0);
00792             clip.addQuadPoint(width, 0.0, width, 0.0, width, height, width, height);
00793             clip.addQuadPoint(width, height, width, height, 0.0, height, 0.0, height);
00794             clip.addQuadPoint(0.0, height, 0.0, height, 0.0, 0.0, 0.0, 0.0);
00795         }
00796         if (clip.size() >= 2)
00797             m_clipPaths.insert(id, clip);
00798     }
00799 }
00800 
00801 void SVGPlug::parseClipPathAttr(const QDomElement &e, FPointArray& clipPath)
00802 {
00803     clipPath.resize(0);
00804     if (e.hasAttribute("clip-path"))
00805     {
00806         QString attr = e.attribute("clip-path");
00807         if (attr.startsWith( "url("))
00808         {
00809             unsigned int start = attr.indexOf("#") + 1;
00810             unsigned int end = attr.lastIndexOf(")");
00811             QString key = attr.mid(start, end - start);
00812             QMap<QString, FPointArray>::iterator it = m_clipPaths.find(key);
00813             if (it != m_clipPaths.end())
00814                 clipPath = it.value().copy();
00815         }
00816     }
00817 }
00818 
00819 QList<PageItem*> SVGPlug::parseGroup(const QDomElement &e)
00820 {
00821     FPointArray clipPath;
00822     QList<PageItem*> GElements, gElements;
00823     double BaseX = m_Doc->currentPage()->xOffset();
00824     double BaseY = m_Doc->currentPage()->yOffset();
00825     groupLevel++;