diff options
| author | craig <craig@11d20701-8431-0410-a711-e3c959e3b870> | 2012-01-01 11:40:09 +0000 |
|---|---|---|
| committer | craig <craig@11d20701-8431-0410-a711-e3c959e3b870> | 2012-01-01 11:40:09 +0000 |
| commit | 7ed83b6c6666eb8b6b104c211ae7e52907350372 (patch) | |
| tree | 4430b556abac0ad660a0aacf1887d77f85d8be02 /scribus/plugins/import/svg/svgplugin.cpp | |
| download | scribus-7ed83b6c6666eb8b6b104c211ae7e52907350372.tar.gz scribus-7ed83b6c6666eb8b6b104c211ae7e52907350372.tar.xz scribus-7ed83b6c6666eb8b6b104c211ae7e52907350372.zip | |
Branch 1.3.5 tree to 1.4.x tree, goodbye 1.3.x
git-svn-id: svn://scribus.net/branches/Version14x/Scribus@17163 11d20701-8431-0410-a711-e3c959e3b870
Diffstat (limited to 'scribus/plugins/import/svg/svgplugin.cpp')
| -rw-r--r-- | scribus/plugins/import/svg/svgplugin.cpp | 2644 |
1 files changed, 2644 insertions, 0 deletions
diff --git a/scribus/plugins/import/svg/svgplugin.cpp b/scribus/plugins/import/svg/svgplugin.cpp new file mode 100644 index 0000000..77c002f --- /dev/null +++ b/scribus/plugins/import/svg/svgplugin.cpp @@ -0,0 +1,2644 @@ +/* +For general Scribus (>=1.3.2) copyright and licensing information please refer +to the COPYING file provided with the program. Following this notice may exist +a copyright and/or license notice that predates the release of Scribus 1.3.2 +for which a new license (GPL+exception) is in place. +*/ + +#include <QCursor> +#include <QDebug> +#include <QDrag> +#include <QFile> +#include <QList> +#include <QMimeData> +#include <QPainterPath> +#include <QRegExp> +#include <QTemporaryFile> + +#include "color.h" +#include "commonstrings.h" +#include "customfdialog.h" +#include "fonts/scfontmetrics.h" +#include "fpointarray.h" +#include "loadsaveplugin.h" +#include "menumanager.h" +#include "pageitem.h" +#include "prefsfile.h" +#include "prefsmanager.h" +#include "propertiespalette.h" +#include "sccolorengine.h" +#include "scclocale.h" +#include "scconfig.h" +#include "scgzfile.h" +#include "scmimedata.h" +#include "scpaths.h" +#include "scpattern.h" +#include "scraction.h" +#include "scribus.h" +#include "scribusXml.h" +#include "scribuscore.h" +#include "scribusdoc.h" +#include "selection.h" +#include "svgplugin.h" +#include "undomanager.h" +#include "util.h" +#include "util_formats.h" +#include "util_icon.h" +#include "util_math.h" + + +using namespace std; + +int svgimplugin_getPluginAPIVersion() +{ + return PLUGIN_API_VERSION; +} + +ScPlugin* svgimplugin_getPlugin() +{ + SVGImportPlugin* plug = new SVGImportPlugin(); + Q_CHECK_PTR(plug); + return plug; +} + +void svgimplugin_freePlugin(ScPlugin* plugin) +{ + SVGImportPlugin* plug = dynamic_cast<SVGImportPlugin*>(plugin); + Q_ASSERT(plug); + delete plug; +} + +SVGImportPlugin::SVGImportPlugin() : LoadSavePlugin(), + importAction(new ScrAction(ScrAction::DLL, "", QKeySequence(), this)) +{ + // Set action info in languageChange, so we only have to do + // it in one place. This includes registering file format + // support. + languageChange(); +} +/* +void SVGImportPlugin::addToMainWindowMenu(ScribusMainWindow *mw) +{ + importAction->setEnabled(true); + connect( importAction, SIGNAL(triggered()), SLOT(import()) ); + mw->scrMenuMgr->addMenuItem(importAction, "FileImport"); +} +*/ +SVGImportPlugin::~SVGImportPlugin() +{ + unregisterAll(); +}; + +void SVGImportPlugin::languageChange() +{ + importAction->setText( tr("Import &SVG...")); + // (Re)register file format support. + unregisterAll(); + registerFormats(); +} + +const QString SVGImportPlugin::fullTrName() const +{ + return QObject::tr("SVG Import"); +} + +const ScActionPlugin::AboutData* SVGImportPlugin::getAboutData() const +{ + AboutData* about = new AboutData; + about->authors = "Franz Schmid <franz@scribus.info>"; + about->shortDescription = tr("Imports SVG Files"); + about->description = tr("Imports most SVG files into the current document,\nconverting their vector data into Scribus objects."); + about->license = "GPL"; + Q_CHECK_PTR(about); + return about; +} + +void SVGImportPlugin::deleteAboutData(const AboutData* about) const +{ + Q_ASSERT(about); + delete about; +} + +void SVGImportPlugin::registerFormats() +{ + FileFormat fmt(this); + fmt.trName = FormatsManager::instance()->nameOfFormat(FormatsManager::SVG); + fmt.formatId = FORMATID_SVGIMPORT; + fmt.filter = FormatsManager::instance()->extensionsForFormat(FormatsManager::SVG); + fmt.nameMatch = QRegExp("\\."+FormatsManager::instance()->extensionListForFormat(FormatsManager::SVG, 1)+"$", Qt::CaseInsensitive); + fmt.load = true; + fmt.save = false; + fmt.mimeTypes = FormatsManager::instance()->mimetypeOfFormat(FormatsManager::SVG); + fmt.priority = 64; + registerFormat(fmt); +} + +bool SVGImportPlugin::fileSupported(QIODevice* /* file */, const QString & fileName) const +{ + // TODO: identify valid SVG + return true; +} + +bool SVGImportPlugin::loadFile(const QString & fileName, const FileFormat & /* fmt */, int flags, int /*index*/) +{ + // For now, "load file" and import are the same thing for this plugin + return import(fileName, flags); +} + +bool SVGImportPlugin::import(QString filename, int flags) +{ + if (!checkFlags(flags)) + return false; + m_Doc=ScCore->primaryMainWindow()->doc; + ScribusMainWindow* mw=(m_Doc==0) ? ScCore->primaryMainWindow() : m_Doc->scMW(); + if (filename.isEmpty()) + { + flags |= lfInteractive; + PrefsContext* prefs = PrefsManager::instance()->prefsFile->getPluginContext("SVGPlugin"); + QString wdir = prefs->get("wdir", "."); + CustomFDialog diaf(mw, wdir, QObject::tr("Open"), FormatsManager::instance()->fileDialogFormatList(FormatsManager::SVG)); + if (diaf.exec()) + { + filename = diaf.selectedFile(); + prefs->set("wdir", filename.left(filename.lastIndexOf("/"))); + } + else + return true; + } + + UndoTransaction* activeTransaction = NULL; + bool emptyDoc = (m_Doc == NULL); + bool hasCurrentPage = (m_Doc && m_Doc->currentPage()); + TransactionSettings trSettings; + trSettings.targetName = hasCurrentPage ? m_Doc->currentPage()->getUName() : ""; + trSettings.targetPixmap = Um::IImageFrame; + trSettings.actionName = Um::ImportSVG; + trSettings.description = filename; + trSettings.actionPixmap = Um::ISVG; + if (emptyDoc || !(flags & lfInteractive) || !(flags & lfScripted)) + UndoManager::instance()->setUndoEnabled(false); + if (UndoManager::undoEnabled()) + activeTransaction = new UndoTransaction(UndoManager::instance()->beginTransaction(trSettings)); + SVGPlug *dia = new SVGPlug(mw, flags); + Q_CHECK_PTR(dia); + dia->import(filename, trSettings, flags); + if (activeTransaction) + { + activeTransaction->commit(); + delete activeTransaction; + activeTransaction = NULL; + } + if (emptyDoc || !(flags & lfInteractive) || !(flags & lfScripted)) + UndoManager::instance()->setUndoEnabled(true); + if (dia->importCanceled) + { + if (dia->importFailed) + QMessageBox::warning(mw, CommonStrings::trWarning, tr("The file could not be imported"), 1, 0, 0); + else if (dia->unsupported) + QMessageBox::warning(mw, CommonStrings::trWarning, tr("SVG file contains some unsupported features"), 1, 0, 0); + } + + delete dia; + return true; +} + +SVGPlug::SVGPlug( ScribusMainWindow* mw, int flags ) : + QObject(mw) +{ + tmpSel=new Selection(this, false); + m_Doc=mw->doc; + unsupported = false; + importFailed = false; + importCanceled = true; + importedColors.clear(); + importedPatterns.clear(); + docDesc = ""; + docTitle = ""; + groupLevel = 0; + interactive = (flags & LoadSavePlugin::lfInteractive); +// m_gc.setAutoDelete( true ); +} + +bool SVGPlug::import(QString fname, const TransactionSettings& trSettings, int flags) +{ + if (!loadData(fname)) + { + importFailed = true; + return false; + } + QString CurDirP = QDir::currentPath(); + QFileInfo efp(fname); + QDir::setCurrent(efp.path()); + convert(trSettings, flags); + QDir::setCurrent(CurDirP); + return true; +} + +bool SVGPlug::loadData(QString fName) +{ + QString f(""); + bool isCompressed = false, success = false; + QByteArray bb(3, ' '); + QFile fi(fName); + if (fi.open(QIODevice::ReadOnly)) + { + fi.read(bb.data(), 2); + fi.close(); + // Qt4 bb[0]->QChar(bb[0]) + if ((QChar(bb[0]) == QChar(0x1F)) && (QChar(bb[1]) == QChar(0x8B))) + isCompressed = true; + } + if ((fName.right(2) == "gz") || (isCompressed)) + { + ScGzFile file(fName); + if (!file.open(QIODevice::ReadOnly)) + return false; + success = inpdoc.setContent(&file); + file.close(); + } + else + { + QFile file(fName); + if (!file.open(QIODevice::ReadOnly)) + return false; + success = inpdoc.setContent(&file); + file.close(); + } + return success; +} + +void SVGPlug::convert(const TransactionSettings& trSettings, int flags) +{ + bool ret = false; + SvgStyle *gc = new SvgStyle; + QDomElement docElem = inpdoc.documentElement(); + QSize wh = parseWidthHeight(docElem); + double width = wh.width(); + double height = wh.height(); + if (!interactive || (flags & LoadSavePlugin::lfInsertPage)) + { + m_Doc->setPage(width, height, 0, 0, 0, 0, 0, 0, false, false); + m_Doc->addPage(0); + m_Doc->view()->addPage(0); + } + else + { + if (!m_Doc || (flags & LoadSavePlugin::lfCreateDoc)) + { + m_Doc=ScCore->primaryMainWindow()->doFileNew(width, height, 0, 0, 0, 0, 0, 0, false, false, 0, false, 0, 1, "Custom", true); + ScCore->primaryMainWindow()->HaveNewDoc(); + ret = true; + } + } + if ((ret) || (!interactive)) + { + if (width > height) + m_Doc->PageOri = 1; + else + m_Doc->PageOri = 0; + m_Doc->m_pageSize = "Custom"; + } + FPoint minSize = m_Doc->minCanvasCoordinate; + FPoint maxSize = m_Doc->maxCanvasCoordinate; + FPoint cOrigin = m_Doc->view()->canvasOrigin(); + m_Doc->view()->Deselect(); + m_Doc->setLoading(true); + m_Doc->DoDrawing = false; + m_Doc->view()->updatesOn(false); + m_Doc->scMW()->setScriptRunning(true); + qApp->changeOverrideCursor(QCursor(Qt::WaitCursor)); + gc->FontFamily = m_Doc->toolSettings.defFont; + if (!m_Doc->PageColors.contains("Black")) + m_Doc->PageColors.insert("Black", ScColor(0, 0, 0, 255)); + m_gc.push( gc ); + viewTransformX = 0; + viewTransformY = 0; + viewScaleX = 1; + viewScaleY = 1; + if( !docElem.attribute( "viewBox" ).isEmpty() ) + { + QString viewbox( docElem.attribute( "viewBox" ) ); + QStringList points = viewbox.replace( QRegExp(","), " ").simplified().split( ' ', QString::SkipEmptyParts ); + if (points.size() > 3) + { + QMatrix matrix; + QSize wh2 = parseWidthHeight(docElem); + double w2 = wh2.width(); + double h2 = wh2.height(); + addGraphicContext(); + viewTransformX = ScCLocale::toDoubleC(points[0]); + viewTransformY = ScCLocale::toDoubleC(points[1]); + viewScaleX = w2 / ScCLocale::toDoubleC(points[2]); + viewScaleY = h2 / ScCLocale::toDoubleC(points[3]); + matrix.translate(-viewTransformX * viewScaleX, -viewTransformY * viewScaleY); + matrix.scale(viewScaleX, viewScaleY); + m_gc.top()->matrix = matrix; + } + } + QList<PageItem*> Elements = parseGroup( docElem ); + if (flags & LoadSavePlugin::lfCreateDoc) + { + m_Doc->documentInfo.setTitle(docTitle); + m_Doc->documentInfo.setComments(docDesc); + } + tmpSel->clear(); + if (Elements.count() == 0) + { + importFailed = true; + if (importedColors.count() != 0) + { + for (int cd = 0; cd < importedColors.count(); cd++) + { + m_Doc->PageColors.remove(importedColors[cd]); + } + } + if (importedPatterns.count() != 0) + { + for (int cd = 0; cd < importedPatterns.count(); cd++) + { + m_Doc->docPatterns.remove(importedPatterns[cd]); + } + } + } + if (Elements.count() > 1) + { + bool isGroup = true; + int firstElem = -1; + if (Elements.at(0)->Groups.count() != 0) + firstElem = Elements.at(0)->Groups.top(); + for (int bx = 0; bx < Elements.count(); ++bx) + { + PageItem* bxi = Elements.at(bx); + if (bxi->Groups.count() != 0) + { + if (bxi->Groups.top() != firstElem) + isGroup = false; + } + else + isGroup = false; + } + if (!isGroup) + { + double minx = 99999.9; + double miny = 99999.9; + double maxx = -99999.9; + double maxy = -99999.9; + uint lowestItem = 999999; + uint highestItem = 0; + for (int a = 0; a < Elements.count(); ++a) + { + Elements.at(a)->Groups.push(m_Doc->GroupCounter); + PageItem* currItem = Elements.at(a); + lowestItem = qMin(lowestItem, currItem->ItemNr); + highestItem = qMax(highestItem, currItem->ItemNr); + double x1, x2, y1, y2; + currItem->getVisualBoundingRect(&x1, &y1, &x2, &y2); + minx = qMin(minx, x1); + miny = qMin(miny, y1); + maxx = qMax(maxx, x2); + maxy = qMax(maxy, y2); + } + double gx = minx; + double gy = miny; + double gw = maxx - minx; + double gh = maxy - miny; + PageItem *high = m_Doc->Items->at(highestItem); + int z = m_Doc->itemAdd(PageItem::Polygon, PageItem::Rectangle, gx, gy, gw, gh, 0, m_Doc->toolSettings.dBrush, m_Doc->toolSettings.dPen, true); + PageItem *neu = m_Doc->Items->takeAt(z); + m_Doc->Items->insert(lowestItem, neu); + neu->Groups.push(m_Doc->GroupCounter); + neu->setItemName( tr("Group%1").arg(neu->Groups.top())); + neu->isGroupControl = true; + neu->groupsLastItem = high; + for (int a = 0; a < m_Doc->Items->count(); ++a) + { + m_Doc->Items->at(a)->ItemNr = a; + } + neu->setRedrawBounding(); + neu->setTextFlowMode(PageItem::TextFlowDisabled); + Elements.prepend(neu); + m_Doc->GroupCounter++; + } + } + m_Doc->DoDrawing = true; + m_Doc->scMW()->setScriptRunning(false); + if (interactive) + m_Doc->setLoading(false); + qApp->changeOverrideCursor(QCursor(Qt::ArrowCursor)); + if ((Elements.count() > 0) && (!ret) && (interactive)) + { + if (flags & LoadSavePlugin::lfScripted) + { + bool loadF = m_Doc->isLoading(); + m_Doc->setLoading(false); + m_Doc->changed(); + m_Doc->setLoading(loadF); + m_Doc->m_Selection->delaySignalsOn(); + for (int dre=0; dre<Elements.count(); ++dre) + { + m_Doc->m_Selection->addItem(Elements.at(dre), true); + } + m_Doc->m_Selection->delaySignalsOff(); + m_Doc->m_Selection->setGroupRect(); + m_Doc->view()->updatesOn(true); + importCanceled = false; + } + else + { + m_Doc->DragP = true; + m_Doc->DraggedElem = 0; + m_Doc->DragElements.clear(); + m_Doc->m_Selection->delaySignalsOn(); + for (int dre=0; dre<Elements.count(); ++dre) + { + m_Doc->DragElements.append(Elements.at(dre)->ItemNr); + tmpSel->addItem(Elements.at(dre), true); + } + ScriXmlDoc *ss = new ScriXmlDoc(); + tmpSel->setGroupRect(); + ScElemMimeData* md = new ScElemMimeData(); + md->setScribusElem(ss->WriteElem(m_Doc, m_Doc->view(), tmpSel)); + delete ss; +/*#ifndef Q_WS_MAC*/ +// see #2526 + m_Doc->itemSelection_DeleteItem(tmpSel); +/*#endif*/ + m_Doc->view()->updatesOn(true); + if (importedColors.count() != 0) + { + for (int cd = 0; cd < importedColors.count(); cd++) + { + m_Doc->PageColors.remove(importedColors[cd]); + } + } + if (importedPatterns.count() != 0) + { + for (int cd = 0; cd < importedPatterns.count(); cd++) + { + m_Doc->docPatterns.remove(importedPatterns[cd]); + } + } + m_Doc->m_Selection->delaySignalsOff(); + // We must copy the TransationSettings object as it is owned + // by handleObjectImport method afterwards + TransactionSettings* transacSettings = new TransactionSettings(trSettings); + m_Doc->view()->handleObjectImport(md, transacSettings); + m_Doc->DragP = false; + m_Doc->DraggedElem = 0; + m_Doc->DragElements.clear(); + } + } + else + { + bool loadF = m_Doc->isLoading(); + m_Doc->setLoading(false); + m_Doc->changed(); + m_Doc->reformPages(); + m_Doc->view()->updatesOn(true); + m_Doc->setLoading(loadF); + } +} + +void SVGPlug::addGraphicContext() +{ + SvgStyle *gc = new SvgStyle; + if ( m_gc.top() ) + *gc = *( m_gc.top() ); + m_gc.push( gc ); +} + +void SVGPlug::setupNode( const QDomElement &e ) +{ + addGraphicContext(); + setupTransform( e ); + parseStyle(m_gc.top(), e); +} + +void SVGPlug::setupTransform( const QDomElement &e ) +{ + SvgStyle *gc = m_gc.top(); + QMatrix mat = parseTransform( e.attribute( "transform" ) ); + if (!e.attribute("transform").isEmpty()) + gc->matrix = mat * gc->matrix; +} + +void SVGPlug::finishNode( const QDomNode &e, PageItem* item) +{ + SvgStyle *gc = m_gc.top(); + QMatrix gcm = gc->matrix; + double BaseX = m_Doc->currentPage()->xOffset(); + double BaseY = m_Doc->currentPage()->yOffset(); + double coeff1 = sqrt(gcm.m11() * gcm.m11() + gcm.m12() * gcm.m12()); + double coeff2 = sqrt(gcm.m21() * gcm.m21() + gcm.m22() * gcm.m22()); + switch (item->itemType()) + { + case PageItem::ImageFrame: + { + item->ClipEdited = true; + item->FrameType = 3; + QMatrix mm = gc->matrix; + item->PoLine.map(mm); + item->setLineWidth(item->lineWidth() * (coeff1 + coeff2) / 2.0); + FPoint wh = getMaxClipF(&item->PoLine); + item->setWidthHeight(wh.x(), wh.y()); + m_Doc->AdjustItemSize(item); +// item->moveBy(mm.dx(), mm.dy()); +// item->setWidthHeight(item->width() * mm.m11(), item->height() * mm.m22()); +// item->setLineWidth(item->lineWidth() * (coeff1 + coeff2) / 2.0); + if (item->PictureIsAvailable) + { + item->setImageXYOffset(0.0, 0.0); + item->setImageXYScale(item->width() / (item->pixm.width() * (item->pixm.imgInfo.xres / 72.0)), + item->height() / (item->pixm.height() * (item->pixm.imgInfo.yres / 72.0))); + item->setImageScalingMode(false, false); // fit to frame + } + break; + } + case PageItem::TextFrame: + { + QMatrix mm = gc->matrix; + item->setLineWidth(item->lineWidth() * (coeff1 + coeff2) / 2.0); + } + break; + default: + { + item->ClipEdited = true; + item->FrameType = 3; + QMatrix mm = gc->matrix; + item->PoLine.map(mm); + /*if (haveViewBox) + { + QMatrix mv; + mv.translate(viewTransformX, viewTransformY); + mv.scale(viewScaleX, viewScaleY); + ite->PoLine.map(mv); + }*/ + item->setLineWidth(item->lineWidth() * (coeff1 + coeff2) / 2.0); + FPoint wh = getMaxClipF(&item->PoLine); + item->setWidthHeight(wh.x(), wh.y()); +// if (item->asPolyLine()) +// item->setPolyClip(qRound(qMax(item->lineWidth() / 2.0, 1))); +// else +// item->Clip = FlattenPath(item->PoLine, item->Segments); + FPoint wx = getMinClipF(&item->PoLine); + inGroupXOrigin = qMin(inGroupXOrigin, wx.x()); + inGroupYOrigin = qMin(inGroupYOrigin, wx.y()); + m_Doc->AdjustItemSize(item); + break; + } + } + item->setRedrawBounding(); + item->OwnPage = m_Doc->OnPage(item); + if (e.isElement()) + { + QString nodeId = e.toElement().attribute("id"); + if( !nodeId.isEmpty() ) + item->setItemName(" "+nodeId); + } + item->setFillTransparency( 1 - gc->FillOpacity * gc->Opacity ); + item->setLineTransparency( 1 - gc->StrokeOpacity * gc->Opacity ); + item->PLineEnd = gc->PLineEnd; + item->PLineJoin = gc->PLineJoin; + if (item->fillColor() == CommonStrings::None) + item->setTextFlowMode(PageItem::TextFlowDisabled); + else + item->setTextFlowMode(PageItem::TextFlowUsesFrameShape); + item->DashOffset = gc->dashOffset; + item->DashValues = gc->dashArray; + if (gc->Gradient != 0) + { + if (gc->Gradient == 8) + { + item->GrType = gc->Gradient; + item->setPattern(importedPattTrans[gc->GCol1]); + QMatrix mm = gc->matrixg; + double rot = getRotationFromMatrix(mm, 0.0) * 180 / M_PI; + mm.rotate(rot); + double patDx = (item->xPos() - BaseX) - mm.dx(); + double patDy = (item->yPos() - BaseY) - mm.dy(); + item->setPatternTransform(mm.m11() * 100.0, mm.m22() * 100.0, patDx, patDy, -rot); + } + else + { + if (gc->GradCo.Stops() > 1) + { + item->fill_gradient = gc->GradCo; + if (!gc->CSpace) + { + item->GrStartX = gc->GX1 * item->width(); + item->GrStartY = gc->GY1 * item->height(); + item->GrEndX = gc->GX2 * item->width(); + item->GrEndY = gc->GY2 * item->height(); + double angle1 = atan2(gc->GY2-gc->GY1,gc->GX2-gc->GX1)*(180.0/M_PI); + double angle2 = atan2(item->GrEndY - item->GrStartX, item->GrEndX - item->GrStartX)*(180.0/M_PI); + double dx = item->GrStartX + (item->GrEndX - item->GrStartX) / 2.0; + double dy = item->GrStartY + (item->GrEndY - item->GrStartY) / 2.0; + QMatrix mm, mm2; + if ((gc->GY1 < gc->GY2) && (gc->GX1 < gc->GX2)) + { + mm.rotate(-angle2); + mm2.rotate(angle1); + } + FPointArray gra; + gra.setPoints(2, item->GrStartX-dx, item->GrStartY-dy, item->GrEndX-dx, item->GrEndY-dy); + gra.map(mm*mm2); + gra.translate(dx, dy); + item->GrStartX = gra.point(0).x(); + item->GrStartY = gra.point(0).y(); + item->GrEndX = gra.point(1).x(); + item->GrEndY = gra.point(1).y(); + } + else + { + QMatrix mm = gc->matrix; + mm = gc->matrixg * mm; + FPointArray gra; + gra.setPoints(2, gc->GX1, gc->GY1, gc->GX2, gc->GY2); + gra.map(mm); + gc->GX1 = gra.point(0).x(); + gc->GY1 = gra.point(0).y(); + gc->GX2 = gra.point(1).x(); + gc->GY2 = gra.point(1).y(); + item->GrStartX = gc->GX1 - item->xPos() + BaseX; + item->GrStartY = gc->GY1 - item->yPos() + BaseY; + item->GrEndX = gc->GX2 - item->xPos() + BaseX; + item->GrEndY = gc->GY2 - item->yPos() + BaseY; + } + item->GrType = gc->Gradient; + } + else + { + item->GrType = 0; + QList<VColorStop*> cstops = gc->GradCo.colorStops(); + item->setFillColor(cstops.at(0)->name); + item->setFillShade(cstops.at(0)->shade); + } + } + } +} + +bool SVGPlug::isIgnorableNode( const QDomElement &e ) +{ + QString nodeName(e.tagName()); + return isIgnorableNodeName(nodeName); +} + +bool SVGPlug::isIgnorableNodeName( const QString &n ) +{ + if (n.startsWith("sodipodi") || n.startsWith("inkscape") || n == "metadata") + return true; + return false; +} + +FPoint SVGPlug::parseTextPosition(const QDomElement &e, const FPoint* pos) +{ + // FIXME According to spec, we should in fact return a point list + double x = pos ? pos->x() : 0.0; + double y = pos ? pos->y() : 0.0; + + if (e.hasAttribute( "x" )) + { + QString xatt = e.attribute( "x" , "0" ); + if ( xatt.contains(',') || xatt.contains(' ') ) + { + xatt.replace(QChar(','), QChar(' ')); + QStringList xl(xatt.split(QChar(' '), QString::SkipEmptyParts)); + xatt = xl.first(); + } + x = parseUnit( xatt ); + } + + if (e.hasAttribute( "y" )) + { + QString yatt = e.attribute( "y" , "0" ); + if ( yatt.contains(',') || yatt.contains(' ') ) + { + yatt.replace(QChar(','), QChar(' ')); + QStringList yl(yatt.split(QChar(' '), QString::SkipEmptyParts)); + yatt = yl.first(); + } + y = parseUnit( yatt ); + } + + if (e.hasAttribute( "dx" )) + { + QString dxatt = e.attribute( "dx" , "0" ); + if ( dxatt.contains(',') || dxatt.contains(' ') ) + { + dxatt.replace(QChar(','), QChar(' ')); + QStringList xl(dxatt.split(QChar(' '), QString::SkipEmptyParts)); + dxatt = xl.first(); + } + x += parseUnit( dxatt ); + } + + if (e.hasAttribute( "dy" )) + { + QString dyatt = e.attribute( "dy" , "0" ); + if ( dyatt.contains(',') || dyatt.contains(' ') ) + { + dyatt.replace(QChar(','), QChar(' ')); + QStringList xl(dyatt.split(QChar(' '), QString::SkipEmptyParts)); + dyatt = xl.first(); + } + y += parseUnit( dyatt ); + } + + return FPoint(x, y); +} + +QSize SVGPlug::parseWidthHeight(const QDomElement &e) +{ + QSize size(550, 841); + QString sw = e.attribute("width", "100%"); + QString sh = e.attribute("height", "100%"); + double w = 550, h = 841; + if (!sw.isEmpty()) + w = sw.endsWith("%") ? fromPercentage(sw) : parseUnit(sw); + if (!sh.isEmpty()) + h = sh.endsWith("%") ? fromPercentage(sh) : parseUnit(sh); + if (!e.attribute("viewBox").isEmpty()) + { + QRect viewBox = parseViewBox(e); + double scw = (viewBox.width() > 0 && viewBox.height() > 0) ? viewBox.width() : size.width(); + double sch = (viewBox.width() > 0 && viewBox.height() > 0) ? viewBox.height() : size.height(); + w *= (sw.endsWith("%") ? scw : 1.0); + h *= (sh.endsWith("%") ? sch : 1.0); + } + else + { + w *= (sw.endsWith("%") ? size.width() : 1.0); + h *= (sh.endsWith("%") ? size.height() : 1.0); + } + // OpenOffice files may not have width and height attributes, so avoid unnecessary large dimensions + if (w > 10000 || h > 10000) + { + double m = max(w, h); + w = w / m * 842; + h = h / m * 842; + } + size.setWidth(qRound(w)); + size.setHeight(qRound(h)); + return size; +} + +QRect SVGPlug::parseViewBox(const QDomElement &e) +{ + QRect box(0, 0, 0, 0); + if ( !e.attribute( "viewBox" ).isEmpty() ) + { + QString viewbox( e.attribute( "viewBox" ) ); + QStringList points = viewbox.replace( QRegExp(","), " ").simplified().split( ' ', QString::SkipEmptyParts ); + if (points.size() > 3) + { + double left = ScCLocale::toDoubleC(points[0]); + double bottom = ScCLocale::toDoubleC(points[1]); + double width = ScCLocale::toDoubleC(points[2]); + double height = ScCLocale::toDoubleC(points[3]); + box.setCoords((int) left, (int) bottom, (int) (left + width), (int) (bottom + height)); + } + } + return box; +} + +void SVGPlug::parseDefs(const QDomElement &e) +{ + for ( QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling() ) + { + QDomElement b = n.toElement(); + if ( b.isNull() ) + continue; + SvgStyle svgStyle; + parseStyle( &svgStyle, b ); + if ( !svgStyle.Display) + continue; + QString STag2 = parseTagName(b); + if (STag2 == "g" ) + { + QString id = b.attribute("id", ""); + if (!id.isEmpty()) + m_nodeMap.insert(id, b); + parseDefs(b); + } + else if (STag2 == "linearGradient" || STag2 == "radialGradient") + parseGradient( b ); + else if (STag2 == "clipPath") + parseClipPath(b); + else if (STag2 == "pattern") + parsePattern(b); + else if ( b.hasAttribute("id") ) + { + QString id = b.attribute("id"); + if (!id.isEmpty()) + m_nodeMap.insert(id, b); + } + } +} + +void SVGPlug::parseClipPath(const QDomElement &e) +{ + QString id(e.attribute("id")); + if (!id.isEmpty()) + { + FPointArray clip; + QDomNode n2 = e.firstChild(); + QDomElement b2 = n2.toElement(); + while (b2.nodeName() == "use") + b2 = getReferencedNode(b2); + if (b2.nodeName() == "path") + parseSVG( b2.attribute( "d" ), &clip ); + else if (b2.nodeName() == "rect") + { + double x = parseUnit( b2.attribute( "x", "0.0" )); + double y = parseUnit( b2.attribute( "y", "0.0" )); + double width = parseUnit( b2.attribute( "width" )); + double height = parseUnit( b2.attribute( "height" ) ); + clip.addQuadPoint(x, y, x, y, width+x, y, width+x, y); + clip.addQuadPoint(width+x, y, width+x, y, width+x, height+y, width+x, height+y); + clip.addQuadPoint(width+x, height+y, width+x, height+y, x, height+y, x, height+y); + clip.addQuadPoint(x, height+y, x, height+y, x, y, x, y); + } + if (clip.size() >= 2) + m_clipPaths.insert(id, clip); + } +} + +void SVGPlug::parseClipPathAttr(const QDomElement &e, FPointArray& clipPath) +{ + clipPath.resize(0); +/* if (e.hasAttribute("style")) + { + QString style = e.attribute( "style" ).simplified(); + QStringList substyles = style.split(';', QString::SkipEmptyParts); + for( QStringList::Iterator it = substyles.begin(); it != substyles.end(); ++it ) + { + QStringList substyle = (*it).split(':', QString::SkipEmptyParts); + QString command(substyle[0].trimmed()); + QString params(substyle[1].trimmed()); + if (command == "clip-path") + { + if (params.startsWith( "url(")) + { + unsigned int start = params.indexOf("#") + 1; + unsigned int end = params.lastIndexOf(")"); + QString key = params.mid(start, end - start); + QMap<QString, FPointArray>::iterator it = m_clipPaths.find(key); + if (it != m_clipPaths.end()) + clipPath = it.value().copy(); + } + } + } + } */ + if (e.hasAttribute("clip-path")) + { + QString attr = e.attribute("clip-path"); + if (attr.startsWith( "url(")) + { + unsigned int start = attr.indexOf("#") + 1; + unsigned int end = attr.lastIndexOf(")"); + QString key = attr.mid(start, end - start); + QMap<QString, FPointArray>::iterator it = m_clipPaths.find(key); + if (it != m_clipPaths.end()) + clipPath = it.value().copy(); + } + } +} + +QList<PageItem*> SVGPlug::parseA(const QDomElement &e) +{ + QList<PageItem*> aElements; + for ( QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling() ) + { + QDomElement b = n.toElement(); + if( b.isNull() || isIgnorableNode(b) ) + continue; + SvgStyle svgStyle; + parseStyle( &svgStyle, b); + if (!svgStyle.Display) + continue; + QList<PageItem*> el = parseElement(b); + for (int ec = 0; ec < el.count(); ++ec) + aElements.append(el.at(ec)); + } + return aElements; +} + +QList<PageItem*> SVGPlug::parseGroup(const QDomElement &e) +{ + FPointArray clipPath; + QList<PageItem*> GElements, gElements; + double BaseX = m_Doc->currentPage()->xOffset(); + double BaseY = m_Doc->currentPage()->yOffset(); + groupLevel++; + setupNode(e); + parseClipPathAttr(e, clipPath); + int z = m_Doc->itemAdd(PageItem::Polygon, PageItem::Rectangle, BaseX, BaseY, 1, 1, 0, CommonStrings::None, CommonStrings::None, true); + PageItem *neu = m_Doc->Items->at(z); + for ( QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling() ) + { + QDomElement b = n.toElement(); + if( b.isNull() || isIgnorableNode(b) ) + continue; + SvgStyle svgStyle; + parseStyle( &svgStyle, b); + if (!svgStyle.Display) + continue; + QList<PageItem*> el = parseElement(b); + for (int ec = 0; ec < el.count(); ++ec) + gElements.append(el.at(ec)); + } + groupLevel--; + SvgStyle *gc = m_gc.top(); + if (clipPath.size() == 0) + { + if (gc->clipPath.size() != 0) + clipPath = gc->clipPath.copy(); + } + if (gElements.count() == 0 || (gElements.count() < 2 && (clipPath.size() == 0) && (gc->Opacity == 1.0))) + { + // Unfortunately we have to take the long route here, or we risk crash on undo/redo + // FIXME : create group object after parsing grouped objects + /*m_Doc->Items->takeAt(z); + delete neu;*/ + Selection tmpSelection(m_Doc, false); + tmpSelection.addItem(neu); + m_Doc->itemSelection_DeleteItem(&tmpSelection); + for (int a = 0; a < m_Doc->Items->count(); ++a) + { + m_Doc->Items->at(a)->ItemNr = a; + } + for (int gr = 0; gr < gElements.count(); ++gr) + { + GElements.append(gElements.at(gr)); + } + } + else + { + double minx = 99999.9; + double miny = 99999.9; + double maxx = -99999.9; + double maxy = -99999.9; + GElements.append(neu); + for (int gr = 0; gr < gElements.count(); ++gr) + { + PageItem* currItem = gElements.at(gr); + double x1, x2, y1, y2; + currItem->getVisualBoundingRect(&x1, &y1, &x2, &y2); + minx = qMin(minx, x1); + miny = qMin(miny, y1); + maxx = qMax(maxx, x2); + maxy = qMax(maxy, y2); + } + double gx = minx; + double gy = miny; + double gw = maxx - minx; + double gh = maxy - miny; + neu->setXYPos(gx, gy); + neu->setWidthHeight(gw, gh); + if (clipPath.size() != 0) + { + QMatrix mm = gc->matrix; + neu->PoLine = clipPath.copy(); + neu->PoLine.map(mm); + neu->PoLine.translate(-gx + BaseX, -gy + BaseY); + clipPath.resize(0); + + } + else + neu->SetRectFrame(); + neu->Clip = FlattenPath(neu->PoLine, neu->Segments); + neu->Groups.push(m_Doc->GroupCounter); + neu->isGroupControl = true; + neu->groupsLastItem = gElements.at(gElements.count()-1); + if( !e.attribute("id").isEmpty() ) + neu->setItemName(e.attribute("id")); + else + neu->setItemName( tr("Group%1").arg(neu->Groups.top())); + neu->setFillTransparency(1 - gc->Opacity); + neu->gXpos = neu->xPos() - gx; + neu->gYpos = neu->yPos() - gy; + neu->gWidth = gw; + neu->gHeight = gh; + for (int gr = 0; gr < gElements.count(); ++gr) + { + gElements.at(gr)->Groups.push(m_Doc->GroupCounter); + gElements.at(gr)->gXpos = gElements.at(gr)->xPos() - gx; + gElements.at(gr)->gYpos = gElements.at(gr)->yPos() - gy; + gElements.at(gr)->gWidth = gw; + gElements.at(gr)->gHeight = gh; + GElements.append(gElements.at(gr)); + } + neu->setRedrawBounding(); + neu->setTextFlowMode(PageItem::TextFlowDisabled); + m_Doc->GroupCounter++; + } + delete( m_gc.pop() ); + return GElements; +} + +QList<PageItem*> SVGPlug::parseElement(const QDomElement &e) +{ + QList<PageItem*> GElements; + if (e.hasAttribute("id")) + m_nodeMap.insert(e.attribute("id"), e); + QString STag = parseTagName(e); + if (STag == "g") + { + GElements = parseGroup( e ); + return GElements; + } + if (STag == "defs") + parseDefs(e); + else if (STag == "a") + GElements = parseA(e); + else if (STag == "switch") + GElements = parseSwitch(e); + else if (STag == "symbol") + GElements = parseSymbol(e); + else if (STag == "use") + GElements = parseUse(e); + else if (STag == "linearGradient" || STag == "radialGradient" ) + parseGradient( e ); + else if (STag == "rect") + GElements = parseRect(e); + else if (STag == "ellipse") + GElements = parseEllipse(e); + else if (STag == "circle") + GElements = parseCircle(e); + else if (STag == "line") + GElements = parseLine(e); + else if (STag == "path") + GElements = parsePath(e); + else if (STag == "polyline" || STag == "polygon") + GElements = parsePolyline(e); + else if (STag == "text") + GElements = parseText(e); + else if (STag == "clipPath") + parseClipPath(e); + else if (STag == "desc") + { + if (groupLevel == 1) + docDesc = e.text(); + } + else if (STag == "title") + { + if (groupLevel == 1) + docTitle = e.text(); + } + else if (STag == "image") + GElements = parseImage(e); +/* else if (STag == "i:pgf") + { + QByteArray cdat; + QByteArray ddat; + cdat = e.text().simplified(); + QList<QByteArray> cdlist = cdat.split(' '); + for (int cd = 0; cd < cdlist.count(); cd++) + { + ddat += QByteArray::fromBase64(cdlist[cd]); + } + QFile outf("/home/franz/testdata.txt"); + outf.open(QIODevice::WriteOnly); + outf.write(ddat); + outf.close(); + QString f2 = "/home/franz/testdata_decom.ai"; + FILE *source = fopen("/home/franz/testdata.txt", "rb"); + FILE *dest = fopen(f2, "wb"); + int ret; + unsigned have; + z_stream strm; + char in[4096]; + char out[4096]; + strm.zalloc = Z_NULL; + strm.zfree = Z_NULL; + strm.opaque = Z_NULL; + strm.avail_in = 0; + strm.next_in = Z_NULL; + ret = inflateInit(&strm); + if (ret != Z_OK) + return GElements; + do + { + strm.avail_in = fread(in, 1, 4096, source); + if (ferror(source)) + { + (void)inflateEnd(&strm); + return GElements; + } + if (strm.avail_in == 0) + break; + strm.next_in = (Bytef*)in; + do + { + strm.avail_out = 4096; + strm.next_out = (Bytef*)out; + ret = inflate(&strm, Z_NO_FLUSH); + assert(ret != Z_STREAM_ERROR); + switch (ret) + { + case Z_NEED_DICT: + ret = Z_DATA_ERROR; + case Z_DATA_ERROR: + case Z_MEM_ERROR: + (void)inflateEnd(&strm); + return GElements; + } + have = 4096 - strm.avail_out; + if (fwrite(out, 1, have, dest) != have || ferror(dest)) + { + (void)inflateEnd(&strm); + return GElements; + } + } + while (strm.avail_out == 0); + } + while (ret != Z_STREAM_END); + (void)inflateEnd(&strm); + fclose(source); + fclose(dest); + } */ +/* else if( STag == "image" ) + GElements = parseImage(e); + } */ + else if (!isIgnorableNodeName(STag)) + { + // warn if unsupported SVG feature are encountered + if (!m_unsupportedFeatures.contains(STag)) + { + m_unsupportedFeatures.insert(STag, STag); + qDebug() << QString("unsupported SVG feature: %1").arg(STag); + unsupported = true; + } + } + return GElements; +} + +QList<PageItem*> SVGPlug::parseCircle(const QDomElement &e) +{ + QList<PageItem*> CElements; + double BaseX = m_Doc->currentPage()->xOffset(); + double BaseY = m_Doc->currentPage()->yOffset(); + double r = parseUnit( e.attribute( "r" ) ); + double x = parseUnit( e.attribute( "cx" ) ) - r; + double y = parseUnit( e.attribute( "cy" ) ) - r; + setupNode(e); + SvgStyle *gc = m_gc.top(); + int z = m_Doc->itemAdd(PageItem::Polygon, PageItem::Ellipse, BaseX, BaseY, r * 2.0, r * 2.0, gc->LWidth, gc->FillCol, gc->StrokeCol, true); + PageItem* ite = m_Doc->Items->at(z); + QMatrix mm = QMatrix(); + mm.translate(x, y); + ite->PoLine.map(mm); + FPoint wh = getMaxClipF(&ite->PoLine); + ite->setWidthHeight(wh.x(), wh.y()); + finishNode(e, ite); + CElements.append(ite); + delete( m_gc.pop() ); + return CElements; +} + +QList<PageItem*> SVGPlug::parseEllipse(const QDomElement &e) +{ + QList<PageItem*> EElements; + double BaseX = m_Doc->currentPage()->xOffset(); + double BaseY = m_Doc->currentPage()->yOffset(); + double rx = parseUnit( e.attribute( "rx" ) ); + double ry = parseUnit( e.attribute( "ry" ) ); + double x = parseUnit( e.attribute( "cx" ) ) - rx; + double y = parseUnit( e.attribute( "cy" ) ) - ry; + setupNode(e); + SvgStyle *gc = m_gc.top(); + int z = m_Doc->itemAdd(PageItem::Polygon, PageItem::Ellipse, BaseX, BaseY, rx * 2.0, ry * 2.0, gc->LWidth, gc->FillCol, gc->StrokeCol, true); + PageItem* ite = m_Doc->Items->at(z); + QMatrix mm = QMatrix(); + mm.translate(x, y); + ite->PoLine.map(mm); + FPoint wh = getMaxClipF(&ite->PoLine); + ite->setWidthHeight(wh.x(), wh.y()); + finishNode(e, ite); + EElements.append(ite); + delete( m_gc.pop() ); + return EElements; +} + +QList<PageItem*> SVGPlug::parseImage(const QDomElement &e) +{ + FPointArray clipPath; + QList<PageItem*> IElements; + QString fname = e.attribute("xlink:href"); + double BaseX = m_Doc->currentPage()->xOffset(); + double BaseY = m_Doc->currentPage()->yOffset(); + double x = e.attribute( "x" ).isEmpty() ? 0.0 : parseUnit( e.attribute( "x" ) ); + double y = e.attribute( "y" ).isEmpty() ? 0.0 : parseUnit( e.attribute( "y" ) ); + double w = e.attribute( "width" ).isEmpty() ? 1.0 : parseUnit( e.attribute( "width" ) ); + double h = e.attribute( "height" ).isEmpty() ? 1.0 : parseUnit( e.attribute( "height" ) ); + setupNode(e); + parseClipPathAttr(e, clipPath); + int z = m_Doc->itemAdd(PageItem::ImageFrame, PageItem::Unspecified, x+BaseX, y+BaseY, w, h, 1, m_Doc->toolSettings.dBrushPict, CommonStrings::None, true); + PageItem* ite = m_Doc->Items->at(z); + if (!fname.isEmpty()) + { + if (!fname.startsWith("data:")) + m_Doc->LoadPict(fname, z); + else + { + int startData = fname.indexOf(","); + QString dataType = fname.left(startData); + fname.remove(0, startData+1); + QByteArray ba; + ba.append(fname); + if (dataType.contains("base64")) + ba = QByteArray::fromBase64(ba); + ite->tempImageFile = new QTemporaryFile(QDir::tempPath() + "/scribus_temp_svg_XXXXXX.png"); + ite->tempImageFile->open(); + QString fileName = getLongPathName(ite->tempImageFile->fileName()); + ite->tempImageFile->close(); + ite->isInlineImage = true; + QImage img; + img.loadFromData(ba); + img.save(fileName, "PNG"); + m_Doc->LoadPict(fileName, z); + } + } + if (clipPath.size() != 0) + ite->PoLine = clipPath.copy(); + clipPath.resize(0); + ite->Clip = FlattenPath(ite->PoLine, ite->Segments); + finishNode(e, ite); + IElements.append(ite); + delete( m_gc.pop() ); + return IElements; +} + +QList<PageItem*> SVGPlug::parseLine(const QDomElement &e) +{ + QList<PageItem*> LElements; + double BaseX = m_Doc->currentPage()->xOffset(); + double BaseY = m_Doc->currentPage()->yOffset(); + double x1 = e.attribute( "x1" ).isEmpty() ? 0.0 : parseUnit( e.attribute( "x1" ) ); + double y1 = e.attribute( "y1" ).isEmpty() ? 0.0 : parseUnit( e.attribute( "y1" ) ); + double x2 = e.attribute( "x2" ).isEmpty() ? 0.0 : parseUnit( e.attribute( "x2" ) ); + double y2 = e.attribute( "y2" ).isEmpty() ? 0.0 : parseUnit( e.attribute( "y2" ) ); + setupNode(e); + SvgStyle *gc = m_gc.top(); + int z = m_Doc->itemAdd(PageItem::PolyLine, PageItem::Unspecified, BaseX, BaseY, 10, 10, gc->LWidth, gc->FillCol, gc->StrokeCol, true); + PageItem* ite = m_Doc->Items->at(z); + ite->PoLine.resize(4); + ite->PoLine.setPoint(0, FPoint(x1, y1)); + ite->PoLine.setPoint(1, FPoint(x1, y1)); + ite->PoLine.setPoint(2, FPoint(x2, y2)); + ite->PoLine.setPoint(3, FPoint(x2, y2)); + finishNode(e, ite); + LElements.append(ite); + delete( m_gc.pop() ); + return LElements; +} + +QList<PageItem*> SVGPlug::parsePath(const QDomElement &e) +{ + FPointArray pArray; + QList<PageItem*> PElements; + double BaseX = m_Doc->currentPage()->xOffset(); + double BaseY = m_Doc->currentPage()->yOffset(); + setupNode(e); + SvgStyle *gc = m_gc.top(); + PageItem::ItemType itype = parseSVG(e.attribute("d"), &pArray) ? PageItem::PolyLine : PageItem::Polygon; + int z = m_Doc->itemAdd(itype, PageItem::Unspecified, BaseX, BaseY, 10, 10, gc->LWidth, gc->FillCol, gc->StrokeCol, true); + PageItem* ite = m_Doc->Items->at(z); + ite->fillRule = (gc->fillRule != "nonzero"); + ite->PoLine = pArray; + if (ite->PoLine.size() < 4) + { +// m_Doc->m_Selection->addItem(ite); + tmpSel->addItem(ite); +// m_Doc->itemSelection_DeleteItem(); + m_Doc->itemSelection_DeleteItem(tmpSel); +// m_Doc->m_Selection->clear(); + } + else + { + finishNode(e, ite); + PElements.append(ite); + } + delete( m_gc.pop() ); + return PElements; +} + +QList<PageItem*> SVGPlug::parsePolyline(const QDomElement &e) +{ + int z; + QList<PageItem*> PElements; + double BaseX = m_Doc->currentPage()->xOffset(); + double BaseY = m_Doc->currentPage()->yOffset(); + setupNode(e); + SvgStyle *gc = m_gc.top(); + QString points = e.attribute( "points" ); + if (!points.isEmpty()) + { + QString tagName = parseTagName(e); + points = points.simplified().replace(',', " "); + QStringList pointList = points.split( ' ', QString::SkipEmptyParts ); + if ((tagName == "polygon" ) && (pointList.count() > 4)) + z = m_Doc->itemAdd(PageItem::Polygon, PageItem::Unspecified, BaseX, BaseY, 10, 10, gc->LWidth, gc->FillCol, gc->StrokeCol, true); + else + z = m_Doc->itemAdd(PageItem::PolyLine, PageItem::Unspecified, BaseX, BaseY, 10, 10, gc->LWidth, gc->FillCol, gc->StrokeCol, true); + PageItem* ite = m_Doc->Items->at(z); + ite->fillRule = (gc->fillRule != "nonzero"); + ite->PoLine.resize(0); + ite->PoLine.svgInit(); + bool bFirst = true; + double x = 0.0; + double y = 0.0; + for( QStringList::Iterator it = pointList.begin(); it != pointList.end(); it++ ) + { + if( bFirst ) + { + x = ScCLocale::toDoubleC(*(it++)); + y = ScCLocale::toDoubleC(*it); + ite->PoLine.svgMoveTo(x, y); + bFirst = false; + } + else + { + x = ScCLocale::toDoubleC(*(it++)); + y = ScCLocale::toDoubleC(*it); + ite->PoLine.svgLineTo(x, y); + } + } + if (( tagName == "polygon" ) && (pointList.count() > 4)) + ite->PoLine.svgClosePath(); + else + ite->convertTo(PageItem::PolyLine); + finishNode(e, ite); + PElements.append(ite); + } + delete( m_gc.pop() ); + return PElements; +} + +QList<PageItem*> SVGPlug::parseRect(const QDomElement &e) +{ + QList<PageItem*> RElements; + double BaseX = m_Doc->currentPage()->xOffset(); + double BaseY = m_Doc->currentPage()->yOffset(); + double x = parseUnit( e.attribute( "x" ) ); + double y = parseUnit( e.attribute( "y" ) ); + double width = parseUnit( e.attribute( "width" )); + double height = parseUnit( e.attribute( "height" ) ); + double rx = e.attribute( "rx" ).isEmpty() ? 0.0 : parseUnit( e.attribute( "rx" ) ); + double ry = e.attribute( "ry" ).isEmpty() ? 0.0 : parseUnit( e.attribute( "ry" ) ); + setupNode(e); + SvgStyle *gc = m_gc.top(); + int z = m_Doc->itemAdd(PageItem::Polygon, PageItem::Rectangle, BaseX, BaseY, width, height, gc->LWidth, gc->FillCol, gc->StrokeCol, true); + PageItem* ite = m_Doc->Items->at(z); + if ((rx != 0) || (ry != 0)) + { + ite->setCornerRadius(qMax(rx, ry)); + ite->SetFrameRound(); + m_Doc->setRedrawBounding(ite); + } + QMatrix mm = QMatrix(); + mm.translate(x, y); + ite->PoLine.map(mm); + FPoint wh = getMaxClipF(&ite->PoLine); + ite->setWidthHeight(wh.x(), wh.y()); + finishNode(e, ite); + RElements.append(ite); + delete( m_gc.pop() ); + return RElements; +} + +QList<PageItem*> SVGPlug::parseText(const QDomElement &e) +{ + QList<PageItem*> GElements; + setupNode(e); + double chunkWidth = 0; + FPoint currentPos = parseTextPosition(e); + SvgStyle *gc = m_gc.top(); + if (gc->textAnchor != "start") + getTextChunkWidth(e, chunkWidth); + for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) + { + if (n.isElement() && (parseTagName(n.toElement()) == "tspan")) + { + QList<PageItem*> el = parseTextSpan(n.toElement(), currentPos, chunkWidth); + for (int ec = 0; ec < el.count(); ++ec) + GElements.append(el.at(ec)); + } + if (n.isText()) + { + QList<PageItem*> el = parseTextNode(n.toText(), currentPos, chunkWidth); + for (int ec = 0; ec < el.count(); ++ec) + GElements.append(el.at(ec)); + } + } + delete( m_gc.pop() ); + return GElements; +} + +QList<PageItem*> SVGPlug::parseTextSpan(const QDomElement& e, FPoint& currentPos, double chunkW) +{ + QList<PageItem*> GElements; + setupNode(e); + currentPos = parseTextPosition(e, ¤tPos); + SvgStyle *gc = m_gc.top(); + if ((e.hasAttribute("x") || e.hasAttribute("y")) && (gc->textAnchor != "start")) + { + chunkW = 0; + getTextChunkWidth(e, chunkW); + } + for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) + { + if (n.isElement() && (n.toElement().localName() == "tspan")) + { + QList<PageItem*> el = parseTextSpan(n.toElement(), currentPos, chunkW); + for (int ec = 0; ec < el.count(); ++ec) + GElements.append(el.at(ec)); + } + if (n.isText()) + { + QList<PageItem*> el = parseTextNode(n.toText(), currentPos, chunkW); + for (int ec = 0; ec < el.count(); ++ec) + GElements.append(el.at(ec)); + } + } + delete( m_gc.pop() ); + return GElements; +} + +QList<PageItem*> SVGPlug::parseTextNode(const QDomText& e, FPoint& currentPos, double chunkW) +{ + QList<PageItem*> GElements; + double BaseX = m_Doc->currentPage()->xOffset(); + double BaseY = m_Doc->currentPage()->yOffset(); + double StartX = currentPos.x(), StartY = currentPos.y(); + + QString textString = e.data().simplified(); + if ( textString.isEmpty() ) + return GElements; + + SvgStyle *gc = m_gc.top(); + QFont textFont = getFontFromStyle(*gc); + QFontMetrics fm(textFont); + double width = fm.width(textString); + + if( gc->textAnchor == "middle" ) + StartX -= chunkW / 2.0; + else if( gc->textAnchor == "end") + StartX -= chunkW; + + FPointArray textPath; + QString textFillColor = gc->FillCol; + QString textStrokeColor = gc->StrokeCol; + QPainterPath painterPath; + painterPath.addText( StartX, StartY, textFont, textString ); + textPath.fromQPainterPath(painterPath); + if (textPath.size() > 0) + { +// double lineWidth = 0.0; + int z = m_Doc->itemAdd(PageItem::Polygon, PageItem::Unspecified, BaseX, BaseY, 10, 10, gc->LWidth, textFillColor, textStrokeColor, true); + PageItem* ite = m_Doc->Items->at(z); + ite->PoLine = textPath; + finishNode(e, ite); + GElements.append(ite); + } + currentPos.setX( currentPos.x() + width ); + return GElements; +} + +QList<PageItem*> SVGPlug::parseSwitch(const QDomElement &e) +{ + QString href; + QStringList hrefs; + QList<PageItem*> SElements; + for (QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) + { + QDomElement de = n.toElement(); + QString STag = parseTagName(de); + if (STag == "foreignObject") + { + if (de.hasAttribute("xlink:href")) + { + href = de.attribute("xlink:href").mid(1); + if (!href.isEmpty()) + hrefs.append(href); + } + for (QDomNode n1 = de.firstChild(); !n1.isNull(); n1 = n1.nextSibling()) + { + QDomElement de1 = n1.toElement(); + if (de1.hasAttribute("xlink:href")) + { + href = de1.attribute("xlink:href").mid(1); + if (!href.isEmpty()) + hrefs.append(href); + } + } + } + else + { + if (de.hasAttribute("requiredExtensions") || de.hasAttribute("requiredFeatures")) + continue; + else if (de.hasAttribute("id") && hrefs.contains(de.attribute("id"))) + continue; + else + { + SElements = parseElement(de); + if (SElements.count() > 0) + break; + } + } + } + return SElements; +} + +QList<PageItem*> SVGPlug::parseSymbol(const QDomElement &e) +{ + QList<PageItem*> SElements; + QString id = e.attribute("id"); + if( !id.isEmpty() ) + m_nodeMap.insert(id, e); + return SElements; +} + +QList<PageItem*> SVGPlug::parseUse(const QDomElement &e) +{ + QList<PageItem*> UElements; + setupNode(e); + if( e.hasAttribute("x") || e.hasAttribute("y") ) + { + QMatrix matrix; + double xAtt = ScCLocale::toDoubleC(e.attribute("x", "0.0")); + double yAtt = ScCLocale::toDoubleC(e.attribute("y", "0.0")); + SvgStyle *gc = m_gc.top(); + gc->matrix = QMatrix(1.0, 0.0, 0.0, 1.0, xAtt, yAtt) * gc->matrix; + } + QString href = e.attribute("xlink:href").mid(1); + QMap<QString, QDomElement>::Iterator it = m_nodeMap.find(href); + if (it != m_nodeMap.end()) + { + QDomElement elem = it.value().toElement(); + if (parseTagName(elem) == "symbol" ) + UElements = parseGroup(elem); + else + UElements = parseElement(elem); + } + delete (m_gc.pop()); + return UElements; +} + +QFont SVGPlug::getFontFromStyle(SvgStyle& style) +{ + QFont font(QApplication::font()); + font.setStyleStrategy( QFont::PreferOutline ); + + if (!style.FontFamily.isEmpty()) + font.setFamily(style.FontFamily); + + if (!style.FontStyle.isEmpty()) + { + if (style.FontStyle == "normal") + font.setStyle(QFont::StyleNormal); + else if (style.FontStyle == "italic") + font.setStyle(QFont::StyleItalic); + else if (style.FontStyle == "oblique") + font.setStyle(QFont::StyleOblique); + } + + if (!style.FontWeight.isEmpty()) + { + if (style.FontWeight == "normal") + font.setWeight(QFont::Normal); + else if (style.FontWeight == "bold") + font.setWeight(QFont::Bold); + else if (style.FontWeight == "bolder") + font.setWeight(QFont::DemiBold); + else if (style.FontWeight == "lighter") + font.setWeight(QFont::Light); + else + { + bool weightIsNum = false; + int fontWeight = style.FontWeight.toInt(&weightIsNum); + if (weightIsNum) + { + if (fontWeight == 100 || fontWeight == 200) + font.setWeight(QFont::Light); + else if (fontWeight == 300 || fontWeight == 400) + font.setWeight(QFont::Normal); + else if (fontWeight == 500 || fontWeight == 600) + font.setWeight(QFont::DemiBold); + else if (fontWeight == 700 || fontWeight == 800) + font.setWeight(QFont::Bold); + else if (fontWeight == 900) + font.setWeight(QFont::Black); + } + } + } + + if (!style.FontStretch.isEmpty()) + { + if (style.FontStretch == "normal") + font.setStretch(QFont::Unstretched); + else if (style.FontStretch == "ultra-condensed") + font.setStretch(QFont::UltraCondensed); + else if (style.FontStretch == "extra-condensed") + font.setStretch(QFont::ExtraCondensed); + else if (style.FontStretch == "condensed") + font.setStretch(QFont::Condensed); + else if (style.FontStretch == "semi-condensed") + font.setStretch(QFont::SemiCondensed); + else if (style.FontStretch == "semi-expanded") + font.setStretch(QFont::SemiExpanded); + else if (style.FontStretch == "expanded") + font.setStretch(QFont::Expanded); + else if (style.FontStretch == "extra-expanded") + font.setStretch(QFont::ExtraExpanded); + else if (style.FontStretch == "ultra-expanded") + font.setStretch(QFont::UltraExpanded); + else if (style.FontStretch == "narrower") + font.setStretch(QFont::SemiCondensed); + else if (style.FontStretch == "wider") + font.setStretch(QFont::SemiExpanded); + } + if (!style.textDecoration.isEmpty()) + { + bool underline = false, overline = false; + bool strikeOut = false; + if (style.textDecoration == "underline") + underline = true; + else if (style.textDecoration == "overline") + overline = true; + else if (style.textDecoration == "line-through") + strikeOut = true; + font.setUnderline(underline); + font.setOverline(overline); + font.setStrikeOut(strikeOut); + } + font.setPointSize(style.FontSize / 10); + return font; +} + +QDomElement SVGPlug::getReferencedNode(const QDomElement &e) +{ + QDomElement ret; + QMap<QString, QDomElement>::Iterator it; + QString href = e.attribute("xlink:href").mid(1); + it = m_nodeMap.find(href); + if (it != m_nodeMap.end()) + ret = it.value().toElement(); + return ret; +} + +bool SVGPlug::getTextChunkWidth(const QDomElement &e, double& width) +{ + bool doBreak = false; + setupNode(e); + QDomNode c = e.firstChild(); + for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) + { + if (n.isElement() && (parseTagName(n.toElement()) == "tspan")) + { + QDomElement elem = n.toElement(); + if (elem.hasAttribute("x") || elem.hasAttribute("y")) + { + doBreak = true; + break; + } + doBreak = getTextChunkWidth(n.toElement(), width); + if (doBreak) break; + } + if (n.isText()) + { + QDomText text = n.toText(); + QString textString = text.data().simplified(); + if (textString.length() > 0) + { + SvgStyle *gc = m_gc.top(); + QFont textFont = getFontFromStyle(*gc); + QFontMetrics fm(textFont); + width += fm.width(textString); + } + } + } + delete (m_gc.pop()); + return doBreak; +} + +double SVGPlug::fromPercentage( const QString &s ) +{ + QString s1 = s; + if (s1.endsWith( ";" )) + s1 = s1.left(s1.length() - 1); + if (s1.endsWith( "%" )) + { + s1 = s1.left(s1.length() - 1); + return ScCLocale::toDoubleC(s1) / 100.0; + } + else + return ScCLocale::toDoubleC(s1); +} + +double SVGPlug::parseFontSize(const QString& fsize) +{ + bool noUnit = true; + QString unit = fsize.right(2); + if (unit == "pt" || unit == "cm" || unit == "mm" || + unit == "in" || unit == "px") + { + noUnit = false; + } + double value = parseUnit(fsize); + if (noUnit) + value *= 0.8; + return value; +} + +double SVGPlug::parseUnit(const QString &unit) +{ + bool noUnit = false; + QString unitval=unit; + if( unit.right( 2 ) == "pt" ) + unitval.replace( "pt", "" ); + else if( unit.right( 2 ) == "cm" ) + unitval.replace( "cm", "" ); + else if( unit.right( 2 ) == "mm" ) + unitval.replace( "mm" , "" ); + else if( unit.right( 2 ) == "in" ) + unitval.replace( "in", "" ); + else if( unit.right( 2 ) == "px" ) + unitval.replace( "px", "" ); + if (unitval == unit) + noUnit = true; + double value = ScCLocale::toDoubleC(unitval); + if( unit.right( 2 ) == "pt" ) + value = value; + else if( unit.right( 2 ) == "cm" ) + value = ( value / 2.54 ) * 72; + else if( unit.right( 2 ) == "mm" ) + value = ( value / 25.4 ) * 72; + else if( unit.right( 2 ) == "in" ) + value = value * 72; + else if( unit.right( 2 ) == "px" ) + value = value * 0.8; + else if(noUnit) + value = value; + return value; +} + +QMatrix SVGPlug::parseTransform( const QString &transform ) +{ + QMatrix ret; + // Workaround for QString::split() bug when string ends with space + QString trans = transform.simplified(); + // Split string for handling 1 transform statement at a time + QStringList subtransforms = trans.split(')', QString::SkipEmptyParts); + QStringList::ConstIterator it = subtransforms.begin(); + QStringList::ConstIterator end = subtransforms.end(); + for(; it != end; ++it) + { + QMatrix result; + QStringList subtransform = it->split('(', QString::SkipEmptyParts); + subtransform[0] = subtransform[0].trimmed().toLower(); + subtransform[1] = subtransform[1].simplified(); + QRegExp reg("[,( ]"); + QStringList params = subtransform[1].split(reg, QString::SkipEmptyParts); + if(subtransform[0].startsWith(";") || subtransform[0].startsWith(",")) + subtransform[0] = subtransform[0].right(subtransform[0].length() - 1); + if(subtransform[0] == "rotate") + { + if(params.count() == 3) + { + double x = ScCLocale::toDoubleC(params[1]); + double y = ScCLocale::toDoubleC(params[2]); + result.translate(x, y); + result.rotate(ScCLocale::toDoubleC(params[0])); + result.translate(-x, -y); + } + else + result.rotate(ScCLocale::toDoubleC(params[0])); + } + else if(subtransform[0] == "translate") + { + if(params.count() == 2) + result.translate(ScCLocale::toDoubleC(params[0]), ScCLocale::toDoubleC(params[1])); + else // Spec : if only one param given, assume 2nd param to be 0 + result.translate(ScCLocale::toDoubleC(params[0]), 0); + } + else if(subtransform[0] == "scale") + { + if(params.count() == 2) + result.scale(ScCLocale::toDoubleC(params[0]), ScCLocale::toDoubleC(params[1])); + else // Spec : if only one param given, assume uniform scaling + result.scale(ScCLocale::toDoubleC(params[0]), ScCLocale::toDoubleC(params[0])); + } + else if(subtransform[0] == "skewx") + result.shear(tan(ScCLocale::toDoubleC(params[0]) * 0.01745329251994329576), 0.0F); + else if(subtransform[0] == "skewy") + result.shear(0.0F, tan(ScCLocale::toDoubleC(params[0]) * 0.01745329251994329576)); + else if(subtransform[0] == "matrix") + { + if(params.count() >= 6) + { + double sx = ScCLocale::toDoubleC(params[0]); + double sy = ScCLocale::toDoubleC(params[3]); + double p1 = ScCLocale::toDoubleC(params[1]); + double p2 = ScCLocale::toDoubleC(params[2]); + double p4 = ScCLocale::toDoubleC(params[4]); + double p5 = ScCLocale::toDoubleC(params[5]); + result.setMatrix(sx, p1, p2, sy, p4, p5); + } + } + ret = result * ret; + } + return ret; +} + +const char * SVGPlug::getCoord( const char *ptr, double &number ) +{ + int integer, exponent; + double decimal, frac; + int sign, expsign; + + exponent = 0; + integer = 0; + frac = 1.0; + decimal = 0; + sign = 1; + expsign = 1; + + // read the sign + if(*ptr == '+') + ptr++; + else if(*ptr == '-') + { + ptr++; + sign = -1; + } + + // read the integer part + while(*ptr != '\0' && *ptr >= '0' && *ptr <= '9') + integer = (integer * 10) + *(ptr++) - '0'; + if(*ptr == '.') // read the decimals + { + ptr++; + while(*ptr != '\0' && *ptr >= '0' && *ptr <= '9') + decimal += (*(ptr++) - '0') * (frac *= 0.1); + } + + if(*ptr == 'e' || *ptr == 'E') // read the exponent part + { + ptr++; + + // read the sign of the exponent + if(*ptr == '+') + ptr++; + else if(*ptr == '-') + { + ptr++; + expsign = -1; + } + + exponent = 0; + while(*ptr != '\0' && *ptr >= '0' && *ptr <= '9') + { + exponent *= 10; + exponent += *ptr - '0'; + ptr++; + } + } + number = integer + decimal; + number *= sign * pow( static_cast<double>(10), static_cast<double>( expsign * exponent ) ); + // skip the following space + if(*ptr == ' ') + ptr++; + + return ptr; +} + +bool SVGPlug::parseSVG( const QString &s, FPointArray *ite ) +{ + return ite->parseSVG(s); +} + + +QColor SVGPlug::parseColorN( const QString &rgbColor ) +{ + int r, g, b; + keywordToRGB( rgbColor.toLower(), r, g, b ); + return QColor( r, g, b ); +} + + +QString SVGPlug::parseColor( const QString &s ) +{ + QColor c; + QString ret = CommonStrings::None; + if (s.length() > 11) // icc-color() + { + int iccColorIndex = s.indexOf("icc-color"); + if (iccColorIndex >= 0) + { + QString iccColorName = parseIccColor(s); + if (iccColorName.length() > 0) + return iccColorName; + } + } + if (s.startsWith( "rgb(" ) ) + { + QString parse = s.trimmed(); + QStringList colors = parse.split(',', QString::SkipEmptyParts); + QString r = colors[0].right( ( colors[0].length() - 4 ) ); + QString g = colors[1]; + QString b = colors[2].left( ( colors[2].length() - 1 ) ); + if (r.contains( "%" )) + { + r = r.left( r.length() - 1 ); + r = QString::number( static_cast<int>( ( static_cast<double>( 255 * ScCLocale::toDoubleC(r) ) / 100.0 ) ) ); + } + if (g.contains( "%" )) + { + g = g.left( g.length() - 1 ); + g = QString::number( static_cast<int>( ( static_cast<double>( 255 * ScCLocale::toDoubleC(g) ) / 100.0 ) ) ); + } + if (b.contains( "%" )) + { + b = b.left( b.length() - 1 ); + b = QString::number( static_cast<int>( ( static_cast<double>( 255 * ScCLocale::toDoubleC(b) ) / 100.0 ) ) ); + } + c = QColor(r.toInt(), g.toInt(), b.toInt()); + } + else + { + QString rgbColor = s.trimmed(); + if (rgbColor.startsWith( "#" )) + { + rgbColor = rgbColor.left(7); + c.setNamedColor( rgbColor ); + } + else + c = parseColorN( rgbColor ); + } + ColorList::Iterator it; + bool found = false; + int r, g, b; + QColor tmpR; + for (it = m_Doc->PageColors.begin(); it != m_Doc->PageColors.end(); ++it) + { + if (it.value().getColorModel() == colorModelRGB) + { + it.value().getRGB(&r, &g, &b); + tmpR.setRgb(r, g, b); + if (c == tmpR) + { + ret = it.key(); + found = true; + break; + } + } + } + if (!found) + { + ScColor tmp; + tmp.fromQColor(c); + tmp.setSpotColor(false); + tmp.setRegistrationColor(false); + QString newColorName = "FromSVG"+c.name(); + m_Doc->PageColors.insert(newColorName, tmp); + importedColors.append(newColorName); + ret = newColorName; + } + return ret; +} + +QString SVGPlug::parseIccColor( const QString &s ) +{ + QColor color, tmpR; + QString ret; + bool iccColorFound = false, found = false; + int iccColorIndex = s.indexOf("icc-color"); + if (iccColorIndex < 0) + return ret; + int iccFirst = s.indexOf("(", iccColorIndex); + int iccLast = s.indexOf(")", iccColorIndex); + if (iccFirst >= 0 && iccLast >= 0) + { + QString iccColor = s.mid(iccFirst + 1, iccLast - iccFirst - 1); + iccColor = iccColor.trimmed(); + QStringList colors = iccColor.split(',', QString::SkipEmptyParts); + if (colors.count() == 5) // then we assume this is a cmyk color + { + QString cs = colors[1], ms = colors[2], ys = colors[3], ks = colors[4]; + if (cs.contains( "%" )) + { + cs = cs.left( cs.length() - 1 ); + cs = QString::number(ScCLocale::toDoubleC(cs) / 100); + } + if (ms.contains( "%" )) + { + ms = ms.left( ms.length() - 1 ); + ms = QString::number(ScCLocale::toDoubleC(ms) / 100); + } + if (ys.contains( "%" )) + { + ys = ys.left( ys.length() - 1 ); + ys = QString::number(ScCLocale::toDoubleC(ys) / 100); + } + if (ks.contains( "%" )) + { + ks = ks.left( ks.length() - 1 ); + ks = QString::number(ScCLocale::toDoubleC(ks) / 100); + } + double cv = ScCLocale::toDoubleC(cs); + double mv = ScCLocale::toDoubleC(ms); + double yv = ScCLocale::toDoubleC(ys); + double kv = ScCLocale::toDoubleC(ks); + color.setCmykF(cv, mv, yv, kv); + iccColorFound = true; + } + } + if (iccColorFound == false) + return ret; + int c, m, y, k; + ColorList::Iterator it; + for (it = m_Doc->PageColors.begin(); it != m_Doc->PageColors.end(); ++it) + { + colorModel colorMod = it.value().getColorModel(); + if (colorMod == colorModelCMYK) + { + it.value().getCMYK(&c, &m, &y, &k); + tmpR.setCmyk(c, m, y, k); + if (color == tmpR) + { + ret = it.key(); + found = true; + break; + } + } + } + if (!found) + { + ScColor tmp; + tmp.fromQColor(color); + tmp.setSpotColor(false); + tmp.setRegistrationColor(false); + QString newColorName = "FromSVG"+tmp.name(); + m_Doc->PageColors.insert(newColorName, tmp); + importedColors.append(newColorName); + ret = newColorName; + } + return ret; +} + +void SVGPlug::parsePA( SvgStyle *obj, const QString &command, const QString ¶ms ) +{ + if( command == "display" ) + obj->Display = (params == "none") ? false : true; + else if( command == "stroke-opacity" ) + obj->StrokeOpacity = fromPercentage(params); + else if( command == "fill-opacity" ) + obj->FillOpacity = fromPercentage(params); + else if( command == "opacity" ) + obj->Opacity = fromPercentage(params); + else if( command == "fill" ) + { +// if ((obj->InherCol) && (params == "currentColor")) + if (params == "currentColor") + obj->FillCol = obj->CurCol; + else if (params == "none") + { + obj->FillCol = CommonStrings::None; + } + else if( params.startsWith( "url(" ) ) + { + unsigned int start = params.indexOf("#") + 1; + unsigned int end = params.lastIndexOf(")"); + QString key = params.mid(start, end - start); + obj->Gradient = 0; + obj->matrixg = QMatrix(); + bool firstMatrixValid = false; + if (m_gradients[key].matrixValid) + { + firstMatrixValid = true; + obj->matrixg = m_gradients[key].matrix; + } + while (!m_gradients[key].reference.isEmpty()) + { + QString key2 = m_gradients[key].reference; + if (m_gradients[key2].typeValid) + obj->Gradient = m_gradients[key2].Type; + if (obj->Gradient != 8) + { + if (m_gradients[key2].gradientValid) + obj->GradCo = m_gradients[key2].gradient; + if (m_gradients[key2].cspaceValid) + obj->CSpace = m_gradients[key2].CSpace; + if (m_gradients[key2].x1Valid) + obj->GX1 = m_gradients[key2].X1; + if (m_gradients[key2].y1Valid) + obj->GY1 = m_gradients[key2].Y1; + if (m_gradients[key2].x2Valid) + obj->GX2 = m_gradients[key2].X2; + if (m_gradients[key2].y2Valid) + obj->GY2 = m_gradients[key2].Y2; + if (m_gradients[key2].matrixValid) + obj->matrixg = m_gradients[key2].matrix; + } + else + { + obj->GCol1 = key2; + if ((m_gradients[key2].matrixValid) && (!firstMatrixValid)) + obj->matrixg *= m_gradients[key2].matrix; + } + key = m_gradients[key].reference; + } + if (obj->Gradient != 8) + { + key = params.mid(start, end - start); + if (m_gradients[key].typeValid) + obj->Gradient = m_gradients[key].Type; + if (obj->Gradient != 8) + { + if (m_gradients[key].gradientValid) + obj->GradCo = m_gradients[key].gradient; + if (m_gradients[key].cspaceValid) + obj->CSpace = m_gradients[key].CSpace; + if (m_gradients[key].x1Valid) + obj->GX1 = m_gradients[key].X1; + if (m_gradients[key].y1Valid) + obj->GY1 = m_gradients[key].Y1; + if (m_gradients[key].x2Valid) + obj->GX2 = m_gradients[key].X2; + if (m_gradients[key].y2Valid) + obj->GY2 = m_gradients[key].Y2; + if (m_gradients[key].matrixValid) + obj->matrixg = m_gradients[key].matrix; + } + else + { + obj->GCol1 = key; + if (m_gradients[key].matrixValid) + obj->matrixg = m_gradients[key].matrix; + } + } + obj->FillCol = CommonStrings::None; + } + else + obj->FillCol = parseColor(params); + } + else if( command == "fill-rule" ) + { + obj->fillRule = params; + } + else if( command == "color" ) + { + if (params == "none") + obj->CurCol = CommonStrings::None; + else if( params.startsWith( "url(" ) ) + obj->CurCol = CommonStrings::None; + else if (params == "currentColor") + obj->CurCol = obj->CurCol; + else + obj->CurCol = parseColor(params); + } + else if( command == "stroke" ) + { +// if ((obj->InherCol) && (params == "currentColor")) + if (params == "currentColor") + obj->StrokeCol = obj->CurCol; + else if (params == "none") + { + obj->StrokeCol = CommonStrings::None; + } + else if( params.startsWith( "url(" ) ) + { + obj->StrokeCol = CommonStrings::None; + } + else + obj->StrokeCol = parseColor(params); + /* if( params == "none" ) + gc->stroke.setType( VStroke::none ); + else if( params.startsWith( "url(" ) ) + { + unsigned int start = params.find("#") + 1; + unsigned int end = params.lastIndexOf(")"); + QString key = params.mid( start, end - start ); + gc->stroke.gradient() = m_gradients[ key ].gradient; + gc->stroke.gradient().transform( m_gradients[ key ].gradientTransform ); + gc->stroke.gradient().transform( gc->matrix ); + gc->stroke.setType( VStroke::grad ); + } + else + { + parseColor( strokecolor, params ); + gc->stroke.setType( VStroke::solid ); + } */ + } + else if( command == "stroke-width" ) + obj->LWidth = parseUnit( params ); + else if( command == "stroke-linejoin" ) + { + if( params == "miter" ) + obj->PLineJoin = Qt::MiterJoin; + else if( params == "round" ) + obj->PLineJoin = Qt::RoundJoin; + else if( params == "bevel" ) + obj->PLineJoin = Qt::BevelJoin; + } + else if( command == "stroke-linecap" ) + { + if( params == "butt" ) + obj->PLineEnd = Qt::FlatCap; + else if( params == "round" ) + obj->PLineEnd = Qt::RoundCap; + else if( params == "square" ) + obj->PLineEnd = Qt::SquareCap; + } + // else if( command == "stroke-miterlimit" ) + // gc->stroke.setMiterLimit( params.todouble() ); + else if( command == "stroke-dasharray" ) + { + QVector<double> array; + if(params != "none") + { + QString params2 = params.simplified().replace(',', " "); + QStringList dashes = params2.split(' ', QString::SkipEmptyParts); + for( QStringList::Iterator it = dashes.begin(); it != dashes.end(); ++it ) + array.append( ScCLocale::toDoubleC(*it) ); + } + obj->dashArray = array; + } + else if( command == "stroke-dashoffset" ) + obj->dashOffset = ScCLocale::toDoubleC(params); + else if( command == "font-family" ) + obj->FontFamily = params; + else if( command == "font-style" ) + obj->FontStyle = params; + else if( command == "font-weight" ) + obj->FontWeight = params; + else if( command == "font-stretch" ) + obj->FontStretch = params; + else if( command == "font-size" ) + obj->FontSize = static_cast<int>(parseFontSize(params) * 10.0); + else if( command == "text-anchor" ) + obj->textAnchor = params; + else if( command == "text-decoration" ) + obj->textDecoration = params; + else if (command == "clip-path") + { + if (params.startsWith( "url(")) + { + unsigned int start = params.indexOf("#") + 1; + unsigned int end = params.lastIndexOf(")"); + QString key = params.mid(start, end - start); + QMap<QString, FPointArray>::iterator it = m_clipPaths.find(key); + if (it != m_clipPaths.end()) + obj->clipPath = it.value().copy(); + } + } + else if( !isIgnorableNodeName(command) ) + { + if (!m_unsupportedFeatures.contains(command)) + { + m_unsupportedFeatures.insert(command, command); + qDebug() << QString("unsupported SVG feature: %1").arg(command); + unsupported = true; + } + } +} + +void SVGPlug::parseStyle( SvgStyle *obj, const QDomElement &e ) +{ + SvgStyle *gc = m_gc.top(); + if (!gc) + return; + if( !e.attribute( "display" ).isEmpty() ) + parsePA( obj, "display", e.attribute( "display" ) ); + if( !e.attribute( "color" ).isEmpty() ) + { + if (e.attribute( "color" ) == "inherit") + gc->InherCol = true; + else + parsePA( obj, "color", e.attribute( "color" ) ); + } + if( !e.attribute( "fill" ).isEmpty() ) + parsePA( obj, "fill", e.attribute( "fill" ) ); + if( !e.attribute( "stroke" ).isEmpty() ) + parsePA( obj, "stroke", e.attribute( "stroke" ) ); + if( !e.attribute( "stroke-width" ).isEmpty() ) + parsePA( obj, "stroke-width", e.attribute( "stroke-width" ) ); + if( !e.attribute( "stroke-linejoin" ).isEmpty() ) + parsePA( obj, "stroke-linejoin", e.attribute( "stroke-linejoin" ) ); + if( !e.attribute( "stroke-linecap" ).isEmpty() ) + parsePA( obj, "stroke-linecap", e.attribute( "stroke-linecap" ) ); + if( !e.attribute( "stroke-dasharray" ).isEmpty() ) + parsePA( obj, "stroke-dasharray", e.attribute( "stroke-dasharray" ) ); + if( !e.attribute( "stroke-dashoffset" ).isEmpty() ) + parsePA( obj, "stroke-dashoffset", e.attribute( "stroke-dashoffset" ) ); + if( !e.attribute( "stroke-opacity" ).isEmpty() ) + parsePA( obj, "stroke-opacity", e.attribute( "stroke-opacity" ) ); + /* if( !e.attribute( "stroke-miterlimit" ).isEmpty() ) + parsePA( obj, "stroke-miterlimit", e.attribute( "stroke-miterlimit" ) ); */ + if( !e.attribute( "fill-rule" ).isEmpty() ) + parsePA( obj, "fill-rule", e.attribute( "fill-rule" ) ); + if( !e.attribute( "fill-opacity" ).isEmpty() ) + parsePA( obj, "fill-opacity", e.attribute( "fill-opacity" ) ); + if( !e.attribute( "opacity" ).isEmpty() ) + parsePA( obj, "opacity", e.attribute( "opacity" ) ); + if( !e.attribute( "font-family" ).isEmpty() ) + parsePA( obj, "font-family", e.attribute( "font-family" ) ); + if( !e.attribute( "font-style" ).isEmpty() ) + parsePA( obj, "font-style", e.attribute( "font-style" ) ); + if( !e.attribute( "font-weight" ).isEmpty() ) + parsePA( obj, "font-weight", e.attribute( "font-weight" ) ); + if( !e.attribute( "font-stretch" ).isEmpty() ) + parsePA( obj, "font-stretch", e.attribute( "font-stretch" ) ); + if( !e.attribute( "font-size" ).isEmpty() ) + parsePA( obj, "font-size", e.attribute( "font-size" ) ); + if( !e.attribute( "text-anchor" ).isEmpty() ) + parsePA( obj, "text-anchor", e.attribute( "text-anchor" ) ); + if( !e.attribute( "text-decoration" ).isEmpty() ) + parsePA( obj, "text-decoration", e.attribute( "text-decoration" ) ); + QString style = e.attribute( "style" ).simplified(); + QStringList substyles = style.split(';', QString::SkipEmptyParts); + for( QStringList::Iterator it = substyles.begin(); it != substyles.end(); ++it ) + { + QStringList substyle = it->split(':', QString::SkipEmptyParts); + if (substyle.count() >= 2) + { + QString command(substyle.at(0).trimmed()); + QString params(substyle.at(1).trimmed()); + parsePA( obj, command, params ); + } + } + return; +} + +void SVGPlug::parseColorStops(GradientHelper *gradient, const QDomElement &e) +{ + QString Col = "Black"; + double offset = 0; + double opa; + SvgStyle svgStyle; + parseStyle( &svgStyle, e ); + for(QDomNode n = e.firstChild(); !n.isNull(); n = n.nextSibling()) + { + opa = 1.0; + QDomElement stop = n.toElement(); + if (parseTagName(stop) == "stop") + { + QString temp = stop.attribute( "offset" ); + if( temp.contains( '%' ) ) + { + temp = temp.left( temp.length() - 1 ); + offset = ScCLocale::toDoubleC(temp) / 100.0; + } + else + offset = ScCLocale::toDoubleC(temp); + if( !stop.attribute( "stop-opacity" ).isEmpty() ) + opa = fromPercentage(stop.attribute("stop-opacity")); + if( !stop.attribute( "stop-color" ).isEmpty() ) + { + if (stop.attribute("stop-color") == "currentColor") + Col = svgStyle.CurCol; + else + Col = parseColor(stop.attribute("stop-color")); + } + else + { + QString style = stop.attribute( "style" ).simplified(); + QStringList substyles = style.split(';', QString::SkipEmptyParts); + for( QStringList::Iterator it = substyles.begin(); it != substyles.end(); ++it ) + { + QStringList substyle = it->split(':', QString::SkipEmptyParts); + if (substyle.count() >= 2) + { + QString command(substyle.at(0).trimmed()); + QString params(substyle.at(1).trimmed()); + if( command == "stop-color" ) + Col = parseColor(params); + if( command == "stop-opacity" ) + opa = fromPercentage(params); + } + } + } + } + const ScColor& gradC = m_Doc->PageColors[Col]; + gradient->gradient.addStop( ScColorEngine::getRGBColor(gradC, m_Doc), offset, 0.5, opa, Col, 100 ); + gradient->gradientValid = true; + } + if (gradient->gradientValid) + gradient->gradient.filterStops(); +} + +void SVGPlug::parsePattern(const QDomElement &b) +{ + GradientHelper gradhelper; + QString href = b.attribute("xlink:href").mid(1); + if (!href.isEmpty()) + { + if (m_gradients.contains(href)) + { + gradhelper.Type = m_gradients[href].Type; + gradhelper.gradientValid = m_gradients[href].gradientValid; + gradhelper.typeValid = m_gradients[href].typeValid; + gradhelper.matrix = m_gradients[href].matrix; + gradhelper.matrixValid = m_gradients[href].matrixValid; + } + gradhelper.reference = href; + } + QString id = b.attribute("id", ""); + QString origName = id; + if (!id.isEmpty()) + { + inGroupXOrigin = 999999; + inGroupYOrigin = 999999; + double wpat = parseUnit(b.attribute("width", "0")); + double hpat = parseUnit(b.attribute("height", "0")); + int ac = m_Doc->Items->count(); + QList<PageItem*> GElements; + GElements = parseGroup( b ); + int ae = m_Doc->Items->count(); + if (GElements.count() > 0) + { + ScPattern pat = ScPattern(); + pat.setDoc(m_Doc); + PageItem* currItem = GElements.at(0); + m_Doc->DoDrawing = true; + pat.pattern = currItem->DrawObj_toImage(); + double xOrg = 0.0; + double yOrg = 0.0; + if (inGroupXOrigin < 0.0) + xOrg = inGroupXOrigin; + if (inGroupYOrigin < 0.0) + yOrg = inGroupYOrigin; + if ((xOrg != 0.0) || (yOrg != 0.0)) + pat.pattern = pat.pattern.copy(-xOrg, -yOrg, wpat, hpat); + pat.xoffset = xOrg; + pat.yoffset = yOrg; + m_Doc->DoDrawing = false; + pat.width = qMin(currItem->gWidth, wpat); + pat.height = qMin(currItem->gHeight, hpat); + bool more = false; + for (int as = ac; as < ae; ++as) + { + PageItem* Neu = m_Doc->Items->takeAt(ac); + if (more) + { + Neu->moveBy(xOrg, yOrg, true); + Neu->gXpos += xOrg; + Neu->gYpos += yOrg; + } + more = true; + Neu->ItemNr = pat.items.count(); + pat.items.append(Neu); + } + m_Doc->addPattern(id, pat); + importedPatterns.append(id); + importedPattTrans.insert(origName, id); + } + m_nodeMap.insert(origName, b); + QString transf = b.attribute("patternTransform"); + if( !transf.isEmpty() ) + { + gradhelper.matrix = parseTransform( b.attribute("patternTransform") ); + gradhelper.matrixValid = true; + } + else + gradhelper.matrixValid = false; + gradhelper.gradientValid = true; + gradhelper.gradient.clearStops(); + gradhelper.gradient.setRepeatMethod( VGradient::none ); + gradhelper.Type = 8; + gradhelper.typeValid = true; + m_gradients.insert(origName, gradhelper); + } +} + +void SVGPlug::parseGradient( const QDomElement &e ) +{ + GradientHelper gradhelper; + gradhelper.gradientValid = false; + gradhelper.gradient.clearStops(); + gradhelper.gradient.setRepeatMethod( VGradient::none ); + + QString href = e.attribute("xlink:href").mid(1); + double x1=0, y1=0, x2=0, y2=0; + if (!href.isEmpty()) + { + if (m_gradients.contains(href)) + { + gradhelper.Type = m_gradients[href].Type; + gradhelper.gradient = m_gradients[href].gradient; + gradhelper.X1 = m_gradients[href].X1; + gradhelper.Y1 = m_gradients[href].Y1; + gradhelper.X2 = m_gradients[href].X2; + gradhelper.Y2 = m_gradients[href].Y2; + gradhelper.CSpace = m_gradients[href].CSpace; + gradhelper.matrix = m_gradients[href].matrix; + gradhelper.x1Valid = m_gradients[href].x1Valid; + gradhelper.x2Valid = m_gradients[href].x2Valid; + gradhelper.y1Valid = m_gradients[href].y1Valid; + gradhelper.y2Valid = m_gradients[href].y2Valid; + gradhelper.cspaceValid = m_gradients[href].cspaceValid; + gradhelper.matrixValid = m_gradients[href].matrixValid; + gradhelper.gradientValid = m_gradients[href].gradientValid; + gradhelper.typeValid = m_gradients[href].typeValid; + } + gradhelper.reference = href; + } + QString tagName = parseTagName(e); + if (tagName == "linearGradient") + { + if (e.hasAttribute("x1")) + { + gradhelper.X1 = parseUnit(e.attribute("x1", "0")); + gradhelper.x1Valid = true; + } + if (e.hasAttribute("y1")) + { + gradhelper.Y1 = parseUnit(e.attribute("y1", "0")); + gradhelper.y1Valid = true; + } + if (e.hasAttribute("x2")) + { + gradhelper.X2 = parseUnit(e.attribute("x2", "1")); + gradhelper.x2Valid = true; + } + if (e.hasAttribute("y2")) + { + gradhelper.Y2 = parseUnit(e.attribute("y2", "0")); + gradhelper.y2Valid = true; + } + gradhelper.Type = 6; + gradhelper.typeValid = true; + } + else + { + if (e.hasAttribute("cx")) + { + x1 = parseUnit(e.attribute("cx","0.5")); + gradhelper.x1Valid = true; + } + if (e.hasAttribute("cy")) + { + y1 = parseUnit(e.attribute("cy", "0.5")); + gradhelper.y1Valid = true; + } + if (e.hasAttribute("r")) + { + x2 = parseUnit(e.attribute("r", "0.5")); + gradhelper.x2Valid = true; + } + y2 = y1; + gradhelper.y2Valid = true; + gradhelper.X1 = x1; + gradhelper.Y1 = y1; + gradhelper.X2 = x1 + x2; + gradhelper.Y2 = y1; + gradhelper.Type = 7; + gradhelper.typeValid = true; + } + if ( !e.attribute( "gradientUnits" ).isEmpty() ) + { + QString uni = e.attribute( "gradientUnits"); + if (uni == "userSpaceOnUse") + gradhelper.CSpace = true; + else + gradhelper.CSpace = false; + gradhelper.cspaceValid = true; + } + else + { + gradhelper.CSpace = false; + gradhelper.cspaceValid = false; + } + QString transf = e.attribute("gradientTransform"); + if( !transf.isEmpty() ) + { + gradhelper.matrix = parseTransform( e.attribute("gradientTransform") ); + gradhelper.matrixValid = true; + } + else + gradhelper.matrixValid = false; + QString spreadMethod = e.attribute( "spreadMethod" ); + if( !spreadMethod.isEmpty() ) + { + if( spreadMethod == "reflect" ) + gradhelper.gradient.setRepeatMethod( VGradient::reflect ); + else if( spreadMethod == "repeat" ) + gradhelper.gradient.setRepeatMethod( VGradient::repeat ); + } + parseColorStops(&gradhelper, e); + m_gradients.insert(e.attribute("id"), gradhelper); +} + +QString SVGPlug::parseTagName(const QDomElement& element) +{ + QString tagName(element.tagName()); + if (tagName.startsWith("svg:")) + return tagName.mid(4, -1); + return tagName; +} + +SVGPlug::~SVGPlug() +{ + delete tmpSel; +} |
