diff options
Diffstat (limited to 'scribus/plugins/svgexplugin/svgexplugin.cpp')
| -rw-r--r-- | scribus/plugins/svgexplugin/svgexplugin.cpp | 1779 |
1 files changed, 1779 insertions, 0 deletions
diff --git a/scribus/plugins/svgexplugin/svgexplugin.cpp b/scribus/plugins/svgexplugin/svgexplugin.cpp new file mode 100644 index 0000000..080d106 --- /dev/null +++ b/scribus/plugins/svgexplugin/svgexplugin.cpp @@ -0,0 +1,1779 @@ +/* +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. +*/ +/*************************************************************************** + svgexplugin.cpp - description + ------------------- + begin : Sun Aug 3 08:00:00 CEST 2002 + copyright : (C) 2002 by Franz Schmid + email : Franz.Schmid@altmuehlnet.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include <QFile> +#include <QTextStream> +#include <QDataStream> +#include <QByteArray> +#include <QBuffer> +#include <QList> +#include <QCheckBox> +#include <QScopedPointer> + +#include "svgexplugin.h" + +#include "scconfig.h" +#include "canvas.h" +#include "cmsettings.h" +#include "commonstrings.h" +#include "customfdialog.h" +#include "scribuscore.h" +#include "page.h" +#include "scgzfile.h" +#include "prefsmanager.h" +#include "prefsfile.h" +#include "prefscontext.h" +#include "scmessagebox.h" +#include "scpattern.h" +#include "util.h" +#include "util_math.h" +#include "customfdialog.h" +#include "sctextstruct.h" +#include "guidemanager.h" +#include "sccolorengine.h" +#include "util_formats.h" + +int svgexplugin_getPluginAPIVersion() +{ + return PLUGIN_API_VERSION; +} + +ScPlugin* svgexplugin_getPlugin() +{ + SVGExportPlugin* plug = new SVGExportPlugin(); + Q_CHECK_PTR(plug); + return plug; +} + +void svgexplugin_freePlugin(ScPlugin* plugin) +{ + SVGExportPlugin* plug = dynamic_cast<SVGExportPlugin*>(plugin); + Q_ASSERT(plug); + delete plug; +} + +SVGExportPlugin::SVGExportPlugin() : ScActionPlugin() +{ + // Set action info in languageChange, so we only have to do + // it in one place. + languageChange(); +} + +SVGExportPlugin::~SVGExportPlugin() {}; + +void SVGExportPlugin::languageChange() +{ + // Note that we leave the unused members unset. They'll be initialised + // with their default ctors during construction. + // Action name + m_actionInfo.name = "ExportAsSVG"; + // Action text for menu, including accel + m_actionInfo.text = tr("Save as &SVG..."); + // Menu + m_actionInfo.menu = "FileExport"; + m_actionInfo.enabledOnStartup = false; + m_actionInfo.needsNumObjects = -1; +} + +const QString SVGExportPlugin::fullTrName() const +{ + return QObject::tr("SVG Export"); +} + +const ScActionPlugin::AboutData* SVGExportPlugin::getAboutData() const +{ + AboutData* about = new AboutData; + about->authors = "Franz Schmid <franz@scribus.info>"; + about->shortDescription = tr("Exports SVG Files"); + about->description = tr("Exports the current page into an SVG file."); + about->license = "GPL"; + Q_CHECK_PTR(about); + return about; +} + +void SVGExportPlugin::deleteAboutData(const AboutData* about) const +{ + Q_ASSERT(about); + delete about; +} + +bool SVGExportPlugin::run(ScribusDoc* doc, QString filename) +{ + Q_ASSERT(filename.isEmpty()); + QString fileName; + if (doc!=0) + { + PrefsContext* prefs = PrefsManager::instance()->prefsFile->getPluginContext("svgex"); + QString wdir = prefs->get("wdir", "."); + QScopedPointer<CustomFDialog> openDia( new CustomFDialog(doc->scMW(), wdir, QObject::tr("Save as"), QObject::tr("%1;;All Files (*)").arg(FormatsManager::instance()->extensionsForFormat(FormatsManager::SVG)), fdHidePreviewCheckBox) ); + openDia->setSelection(getFileNameByPage(doc, doc->currentPage()->pageNr(), "svg")); + openDia->setExtension("svg"); + openDia->setZipExtension("svgz"); + QCheckBox* compress = new QCheckBox(openDia.data()); + compress->setText( tr("Compress File")); + compress->setChecked(false); + openDia->addWidgets(compress); + QCheckBox* inlineImages = new QCheckBox(openDia.data()); + inlineImages->setText( tr("Save Images inline")); + inlineImages->setToolTip( tr("Adds all Images on the Page inline to the SVG.\nCaution: this will increase the file size!")); + inlineImages->setChecked(true); + openDia->addWidgets(inlineImages); + QCheckBox* exportBack = new QCheckBox(openDia.data()); + exportBack->setText( tr("Export Page background")); + exportBack->setToolTip( tr("Adds the Page itself as background to the SVG.")); + exportBack->setChecked(false); + openDia->addWidgets(exportBack); + + if (!openDia->exec()) + return true; + fileName = openDia->selectedFile(); + QFileInfo fi(fileName); + QString baseDir = fi.absolutePath(); + if (compress->isChecked()) + fileName = baseDir + "/" + fi.baseName() + ".svgz"; + else + fileName = baseDir + "/" + fi.baseName() + ".svg"; + + SVGOptions Options; + Options.inlineImages = inlineImages->isChecked(); + Options.exportPageBackground = exportBack->isChecked(); + Options.compressFile = compress->isChecked(); + + if (fileName.isEmpty()) + return true; + prefs->set("wdir", fileName.left(fileName.lastIndexOf("/"))); + QFile f(fileName); + if (f.exists()) + { + int exit = QMessageBox::warning(doc->scMW(), CommonStrings::trWarning, + QObject::tr("Do you really want to overwrite the file:\n%1 ?").arg(fileName), + QMessageBox::Yes | QMessageBox::No); + if (exit == QMessageBox::No) + return true; + } + SVGExPlug *dia = new SVGExPlug(doc); + dia->doExport(fileName, Options); + delete dia; + } + return true; +} + +SVGExPlug::SVGExPlug( ScribusDoc* doc ) +{ + m_Doc = doc; + Options.inlineImages = true; + Options.exportPageBackground = false; + Options.compressFile = false; + glyphNames.clear(); +} + +bool SVGExPlug::doExport( QString fName, SVGOptions &Opts ) +{ + Options = Opts; + QFileInfo fiBase(fName); + baseDir = fiBase.absolutePath(); + Page *page; + GradCount = 0; + ClipCount = 0; + PattCount = 0; + docu = QDomDocument("svgdoc"); + QString vo = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; + QString st = "<svg></svg>"; + docu.setContent(st); + page = m_Doc->currentPage(); + double pageWidth = page->width(); + double pageHeight = page->height(); + docElement = docu.documentElement(); + docElement.setAttribute("width", FToStr(pageWidth)+"pt"); + docElement.setAttribute("height", FToStr(pageHeight)+"pt"); + docElement.setAttribute("viewBox", QString("0 0 %1 %2").arg(pageWidth).arg(pageHeight)); + docElement.setAttribute("xmlns", "http://www.w3.org/2000/svg"); + docElement.setAttribute("xmlns:xlink","http://www.w3.org/1999/xlink"); + docElement.setAttribute("version","1.1"); + if (!m_Doc->documentInfo.getTitle().isEmpty()) + { + QDomText title = docu.createTextNode(m_Doc->documentInfo.getTitle()); + QDomElement titleElem = docu.createElement("title"); + titleElem.appendChild(title); + docElement.appendChild(titleElem); + } + if (!m_Doc->documentInfo.getComments().isEmpty()) + { + QDomText desc = docu.createTextNode(m_Doc->documentInfo.getComments()); + QDomElement descElem = docu.createElement("desc"); + descElem.appendChild(desc); + docElement.appendChild(descElem); + } + globalDefs = docu.createElement("defs"); + docElement.appendChild(globalDefs); + if (Options.exportPageBackground) + { + QDomElement backG = docu.createElement("rect"); + backG.setAttribute("x", "0"); + backG.setAttribute("y", "0"); + backG.setAttribute("width", FToStr(pageWidth)); + backG.setAttribute("height", FToStr(pageHeight)); + backG.setAttribute("style", "fill:"+m_Doc->papColor.name()+";" + "stroke:none;"); + docElement.appendChild(backG); + } + page = m_Doc->MasterPages.at(m_Doc->MasterNames[m_Doc->currentPage()->MPageNam]); + ProcessPage(page); + page = m_Doc->currentPage(); + ProcessPage(page); + if(Options.compressFile) + { + // zipped saving + QByteArray array(docu.toString().toUtf8()); + if (!ScGzFile::writeToFile(fName, array, vo.toUtf8().data())) + return false; + } + else + { + QFile f(fName); + if(!f.open(QIODevice::WriteOnly)) + return false; + QDataStream s(&f); + QString wr = vo; + wr += docu.toString(); + QByteArray utf8wr = wr.toUtf8(); + s.writeRawData(utf8wr.data(), utf8wr.length()); + f.close(); + } + return true; +} + +void SVGExPlug::ProcessPage(Page *page) +{ + int Lnr = 0; + ScLayer ll; + ll.isPrintable = false; + ll.LNr = 0; + QDomElement layerGroup; + PageItem *Item; + QList<PageItem*> Items; + QStack<PageItem*> groupStack; + QStack<QDomElement> groupStack2; + Page* SavedAct = m_Doc->currentPage(); + if (page->pageName().isEmpty()) + Items = m_Doc->DocItems; + else + Items = m_Doc->MasterItems; + if (Items.count() == 0) + return; + m_Doc->setCurrentPage(page); + for (int la = 0; la < m_Doc->Layers.count(); la++) + { + m_Doc->Layers.levelToLayer(ll, Lnr); + if (ll.isPrintable) + { + layerGroup = docu.createElement("g"); + layerGroup.setAttribute("id", ll.Name); + if (ll.transparency != 1.0) + layerGroup.setAttribute("opacity", FToStr(ll.transparency)); + for(int j = 0; j < Items.count(); ++j) + { + Item = Items.at(j); + if (Item->LayerNr != ll.LNr) + continue; + if (!Item->printEnabled()) + continue; + double x = page->xOffset(); + double y = page->yOffset(); + double w = page->width(); + double h = page->height(); + double x2 = Item->BoundingX; + double y2 = Item->BoundingY; + double w2 = Item->BoundingW; + double h2 = Item->BoundingH; + if (!( qMax( x, x2 ) <= qMin( x+w, x2+w2 ) && qMax( y, y2 ) <= qMin( y+h, y2+h2 ))) + continue; + if ((!page->pageName().isEmpty()) && (Item->OwnPage != static_cast<int>(page->pageNr())) && (Item->OwnPage != -1)) + continue; + if (Item->isGroupControl) + { + groupStack.push(Item->groupsLastItem); + groupStack2.push(layerGroup); + layerGroup = docu.createElement("g"); + if (!Item->AutoName) + layerGroup.setAttribute("id", Item->itemName()); + if (Item->fillTransparency() != 0) + layerGroup.setAttribute("opacity", FToStr(1.0 - Item->fillTransparency())); + QDomElement ob = docu.createElement("clipPath"); + ob.setAttribute("id", "Clip"+IToStr(ClipCount)); + QDomElement cl = docu.createElement("path"); + cl.setAttribute("d", SetClipPath(&Item->PoLine, true)); + QString trans = "translate("+FToStr(Item->xPos()-page->xOffset())+", "+FToStr(Item->yPos()-page->yOffset())+")"; + if (Item->rotation() != 0) + trans += " rotate("+FToStr(Item->rotation())+")"; + cl.setAttribute("transform", trans); + ob.appendChild(cl); + globalDefs.appendChild(ob); + layerGroup.setAttribute("clip-path", "url(#Clip"+IToStr(ClipCount)+")"); + ClipCount++; + continue; + } + ProcessItemOnPage(Item->xPos()-page->xOffset(), Item->yPos()-page->yOffset(), Item, &layerGroup); + if (groupStack.count() != 0) + { + while (Item == groupStack.top()) + { + groupStack.pop(); + groupStack2.top().appendChild(layerGroup); + layerGroup = groupStack2.pop(); + if (groupStack.count() == 0) + break; + } + } + } + for(int j = 0; j < Items.count(); ++j) + { + Item = Items.at(j); + if (Item->LayerNr != ll.LNr) + continue; + if (!Item->printEnabled()) + continue; + double x = page->xOffset(); + double y = page->yOffset(); + double w = page->width(); + double h = page->height(); + double x2 = Item->BoundingX; + double y2 = Item->BoundingY; + double w2 = Item->BoundingW; + double h2 = Item->BoundingH; + if (!( qMax( x, x2 ) <= qMin( x+w, x2+w2 ) && qMax( y, y2 ) <= qMin( y+h, y2+h2 ))) + continue; + if (!Item->isTableItem) + continue; + if ((Item->lineColor() == CommonStrings::None) || (Item->lineWidth() == 0.0)) + continue; + if ((Item->TopLine) || (Item->RightLine) || (Item->BottomLine) || (Item->LeftLine)) + { + QString trans = "translate("+FToStr(Item->xPos()-page->xOffset())+", "+FToStr(Item->yPos()-page->yOffset())+")"; + if (Item->rotation() != 0) + trans += " rotate("+FToStr(Item->rotation())+")"; + QString stroke = getStrokeStyle(Item); + QDomElement ob = docu.createElement("path"); + ob.setAttribute("transform", trans); + ob.setAttribute("style", "fill:none; " + stroke); + QString pathAttr = ""; + if (Item->TopLine) + pathAttr += "M 0 0 L "+FToStr(Item->width())+" 0"; + if (Item->RightLine) + pathAttr += " M " + FToStr(Item->width()) + "0 L "+FToStr(Item->width())+" "+FToStr(Item->height()); + if (Item->BottomLine) + pathAttr += " M 0 " + FToStr(Item->height()) + " L "+FToStr(Item->width())+" "+FToStr(Item->height()); + if (Item->LeftLine) + pathAttr += " M 0 0 L 0 "+FToStr(Item->height()); + ob.setAttribute("d", pathAttr); + layerGroup.appendChild(ob); + } + } + docElement.appendChild(layerGroup); + } + Lnr++; + } + m_Doc->setCurrentPage(SavedAct); +} + +void SVGExPlug::ProcessItemOnPage(double xOffset, double yOffset, PageItem *Item, QDomElement *parentElem) +{ + QDomElement ob; + QString trans = "translate("+FToStr(xOffset)+", "+FToStr(yOffset)+")"; + if (Item->rotation() != 0) + trans += " rotate("+FToStr(Item->rotation())+")"; + QString fill = getFillStyle(Item); + QString stroke = "stroke:none"; + if (!Item->isTableItem) + stroke = getStrokeStyle(Item); + switch (Item->itemType()) + { + case PageItem::Polygon: + case PageItem::PolyLine: + ob = processPolyItem(Item, trans, fill, stroke); + if ((Item->lineColor() != CommonStrings::None) && ((Item->startArrowIndex() != 0) || (Item->endArrowIndex() != 0))) + ob = processArrows(Item, ob, trans); + break; + case PageItem::Line: + ob = processLineItem(Item, trans, stroke); + if ((Item->lineColor() != CommonStrings::None) && ((Item->startArrowIndex() != 0) || (Item->endArrowIndex() != 0))) + ob = processArrows(Item, ob, trans); + break; + case PageItem::ImageFrame: + case PageItem::LatexFrame: + ob = processImageItem(Item, trans, fill, stroke); + break; + case PageItem::TextFrame: + ob = processTextItem(Item, trans, fill, stroke); + break; + case PageItem::PathText: + ob = processPathTextItem(Item, trans, stroke); + break; + default: + break; + } + if (!Item->AutoName) + ob.setAttribute("id", Item->itemName()); + parentElem->appendChild(ob); +} + +QDomElement SVGExPlug::processPolyItem(PageItem *Item, QString trans, QString fill, QString stroke) +{ + bool closedPath; + QDomElement ob; + if (Item->itemType() == PageItem::Polygon) + closedPath = true; + else + closedPath = false; + if (Item->NamedLStyle.isEmpty()) + { + ob = docu.createElement("path"); + ob.setAttribute("d", SetClipPath(&Item->PoLine, closedPath)); + ob.setAttribute("transform", trans); + ob.setAttribute("style", fill + stroke); + } + else + { + ob = docu.createElement("g"); + ob.setAttribute("transform", trans); + QDomElement ob2 = docu.createElement("path"); + ob2.setAttribute("d", SetClipPath(&Item->PoLine, closedPath)); + ob2.setAttribute("style", fill); + ob.appendChild(ob2); + multiLine ml = m_Doc->MLineStyles[Item->NamedLStyle]; + for (int it = ml.size()-1; it > -1; it--) + { + if ((ml[it].Color != CommonStrings::None) && (ml[it].Width != 0)) + { + QDomElement ob3 = docu.createElement("path"); + ob3.setAttribute("d", SetClipPath(&Item->PoLine, closedPath)); + ob3.setAttribute("style", GetMultiStroke(&ml[it], Item)); + ob.appendChild(ob3); + } + } + } + return ob; +} + +QDomElement SVGExPlug::processLineItem(PageItem *Item, QString trans, QString stroke) +{ + QDomElement ob; + if (Item->NamedLStyle.isEmpty()) + { + ob = docu.createElement("path"); + ob.setAttribute("d", "M 0 0 L "+FToStr(Item->width())+" 0"); + ob.setAttribute("transform", trans); + ob.setAttribute("style", stroke); + } + else + { + ob = docu.createElement("g"); + ob.setAttribute("transform", trans); + multiLine ml = m_Doc->MLineStyles[Item->NamedLStyle]; + for (int it = ml.size()-1; it > -1; it--) + { + if ((ml[it].Color != CommonStrings::None) && (ml[it].Width != 0)) + { + QDomElement ob2 = docu.createElement("path"); + ob2.setAttribute("d", "M 0 0 L "+FToStr(Item->width())+" 0"); + ob2.setAttribute("style", GetMultiStroke(&ml[it], Item)); + ob.appendChild(ob2); + } + } + } + return ob; +} + +QDomElement SVGExPlug::processImageItem(PageItem *Item, QString trans, QString fill, QString stroke) +{ + QDomElement ob; + ob = docu.createElement("g"); + ob.setAttribute("transform", trans); + if ((Item->fillColor() != CommonStrings::None) || (Item->GrType != 0)) + { + QDomElement ob1 = docu.createElement("path"); + ob1.setAttribute("d", SetClipPath(&Item->PoLine, true)); + ob1.setAttribute("style", fill); + ob.appendChild(ob1); + } + if ((Item->PictureIsAvailable) && (!Item->Pfile.isEmpty())) + { + QDomElement ob2 = docu.createElement("clipPath"); + ob2.setAttribute("id", "Clip"+IToStr(ClipCount)); + ob2.setAttribute("clipPathUnits", "userSpaceOnUse"); + ob2.setAttribute("clip-rule", "evenodd"); + QDomElement cl = docu.createElement("path"); + if (Item->imageClip.size() != 0) + cl.setAttribute("d", SetClipPath(&Item->imageClip, true)); + else + cl.setAttribute("d", SetClipPath(&Item->PoLine, true)); + ob2.appendChild(cl); + globalDefs.appendChild(ob2); + QDomElement ob3 = docu.createElement("image"); + ob3.setAttribute("clip-path", "url(#Clip"+IToStr(ClipCount)+")"); + ScImage img; + CMSettings cms(m_Doc, Item->IProfile, Item->IRender); + img.LoadPicture(Item->Pfile, Item->pixm.imgInfo.actualPageNumber, cms, Item->UseEmbedded, true, ScImage::RGBProof, 72); + img.applyEffect(Item->effectsInUse, m_Doc->PageColors, true); + if (Options.inlineImages) + { + QBuffer buffer; + buffer.open(QIODevice::WriteOnly); + img.qImage().save(&buffer, "PNG"); + QByteArray ba = buffer.buffer().toBase64(); + buffer.close(); + ob3.setAttribute("xlink:href", "data:image/png;base64,"+QString(ba)); + } + else + { + QFileInfo fi = QFileInfo(Item->Pfile); + QString imgFileName = baseDir + "/" + fi.baseName()+".png"; + QFileInfo im = QFileInfo(imgFileName); + if (im.exists()) + imgFileName = baseDir + "/" + fi.baseName()+"_copy.png"; + img.qImage().save(imgFileName, "PNG"); + QFileInfo fi2 = QFileInfo(imgFileName); + ob3.setAttribute("xlink:href", fi2.baseName()+".png"); + } + ob3.setAttribute("x", FToStr(Item->imageXOffset() * Item->imageXScale())); + ob3.setAttribute("y", FToStr(Item->imageYOffset() * Item->imageYScale())); + ob3.setAttribute("width", FToStr(img.width() * Item->imageXScale())); + ob3.setAttribute("height", FToStr(img.height() * Item->imageYScale())); + QMatrix mpa; + if (Item->imageFlippedH()) + { + mpa.translate(Item->width(), 0); + mpa.scale(-1, 1); + } + if (Item->imageFlippedV()) + { + mpa.translate(0, Item->height()); + mpa.scale(1, -1); + } + ob3.setAttribute("transform", MatrixToStr(mpa)); + ClipCount++; + ob.appendChild(ob3); + } + if (Item->NamedLStyle.isEmpty()) + { + QDomElement ob4 = docu.createElement("path"); + ob4.setAttribute("d", SetClipPath(&Item->PoLine, true)); + ob4.setAttribute("style", "fill:none; "+stroke); + ob.appendChild(ob4); + } + else + { + multiLine ml = m_Doc->MLineStyles[Item->NamedLStyle]; + for (int it = ml.size()-1; it > -1; it--) + { + if ((ml[it].Color != CommonStrings::None) && (ml[it].Width != 0)) + { + QDomElement ob5 = docu.createElement("path"); + ob5.setAttribute("d", SetClipPath(&Item->PoLine, true)); + ob5.setAttribute("style", "fill:none; "+GetMultiStroke(&ml[it], Item)); + ob.appendChild(ob5); + } + } + } + return ob; +} + +QDomElement SVGExPlug::processTextItem(PageItem *Item, QString trans, QString fill, QString stroke) +{ + QDomElement ob; + ob = docu.createElement("g"); + ob.setAttribute("transform", trans); + if ((Item->fillColor() != CommonStrings::None) || (Item->GrType != 0)) + { + QDomElement ob1 = docu.createElement("path"); + ob1.setAttribute("d", SetClipPath(&Item->PoLine, true)); + ob1.setAttribute("style", fill); + ob.appendChild(ob1); + } + double x, y, wide; + QString chstr; + for (uint ll=0; ll < Item->itemText.lines(); ++ll) + { + LineSpec ls = Item->itemText.line(ll); + double CurX = ls.x; + for (int a = ls.firstItem; a <= ls.lastItem; ++a) + { + x = 0.0; + y = 0.0; + ScText * hl = Item->itemText.item(a); + const CharStyle& charStyle(Item->itemText.charStyle(a)); + chstr = Item->itemText.text(a,1); + if ((chstr == QChar(13)) || (chstr == QChar(29))) + { + if (chstr == QChar(29)) + CurX += hl->glyph.wide(); + continue; + } + if (chstr == QChar(30)) + { + chstr = Item->ExpandToken(a); + if (chstr == QChar(32)) + { + CurX += hl->glyph.wide(); + continue; + } + } + double chs = charStyle.fontSize(); + if (hl->effects() & ScStyle_SmallCaps) + { + if (chstr.toUpper() != chstr) + { + chs = qMax(static_cast<int>(hl->fontSize() * m_Doc->typographicSettings.valueSmallCaps / 100), 1); + chstr = chstr.toUpper(); + } + } + else if (hl->effects() & ScStyle_AllCaps) + chstr = chstr.toUpper(); + uint chr = chstr[0].unicode(); + QMatrix chma, chma2, chma3, chma4, chma6; + QMatrix trafo = QMatrix( 1, 0, 0, 1, CurX, ls.y ); + if (Item->rotation() != 0) + { + QMatrix sca; + sca.translate(-Item->xPos(), -Item->yPos()); + trafo *= sca; + } + chma.scale(hl->glyph.scaleH * charStyle.fontSize() / 100.00, hl->glyph.scaleV * charStyle.fontSize() / 100.0); + if (Item->reversed()) + { + if (a < Item->itemText.length()-1) + wide = hl->font().charWidth(chstr[0], hl->fontSize(), Item->itemText.text(a+1)); + else + wide = hl->font().charWidth(chstr[0], hl->fontSize()); + chma3.scale(-1, 1); + chma3.translate(-wide, 0); + } + chma4.translate(0, Item->BaseOffs - (charStyle.fontSize() / 10.0) * hl->glyph.scaleV); + if (charStyle.effects() & (ScStyle_Subscript | ScStyle_Superscript | ScStyle_DropCap)) + chma6.translate(0, hl->glyph.yoffset); + if (hl->baselineOffset() != 0) + chma6.translate(0, (-charStyle.fontSize() / 10.0) * (charStyle.baselineOffset() / 1000.0)); + QMatrix finalMat = QMatrix(chma * chma2 * chma3 * chma4 * chma6 * trafo); + if (Item->rotation() != 0) + { + QMatrix sca; + sca.translate(Item->xPos(), Item->yPos()); + finalMat *= sca; + } + if (hl->hasObject()) + { + ob.appendChild(processInlineItem(CurX + hl->glyph.xoffset, ls.y + hl->glyph.yoffset, finalMat, hl, false, trans)); + InlineFrame& embedded(const_cast<InlineFrame&>(hl->embedded)); + CurX += (embedded.getItem()->gWidth + embedded.getItem()->lineWidth()) * hl->glyph.scaleH; + } + else + { + QString glName; + if (chstr > QChar(32)) + glName = handleGlyph(chr, hl); + if ((charStyle.effects() & ScStyle_Shadowed) && (charStyle.strokeColor() != CommonStrings::None) && (chstr > QChar(32))) + { + QMatrix sha = finalMat; + QMatrix shad; + shad.translate(charStyle.fontSize() * charStyle.shadowXOffset() / 10000.0, -charStyle.fontSize() * charStyle.shadowYOffset() / 10000.0); + sha *= shad; + QDomElement ob2 = docu.createElement("use"); + ob2.setAttribute("xlink:href", "#" + glName); + ob2.setAttribute("transform", MatrixToStr(sha)); + ob2.setAttribute("style", "fill:"+SetColor(hl->strokeColor(), hl->strokeShade())+";" + "stroke:none;"); + ob.appendChild(ob2); + } + QChar chstc = hl->ch; + if (((charStyle.effects() & ScStyle_Underline) && !SpecialChars::isBreak(chstc)) + || ((charStyle.effects() & ScStyle_UnderlineWords) && !chstc.isSpace() && !SpecialChars::isBreak(chstc))) + { + x = CurX; + y = ls.y; + double Ulen = hl->glyph.xadvance; + double Upos, lw, kern; + if (charStyle.effects() & ScStyle_StartOfLine) + kern = 0; + else + kern = charStyle.fontSize() * charStyle.tracking() / 10000.0; + if ((charStyle.underlineOffset() != -1) || (charStyle.underlineWidth() != -1)) + { + if (charStyle.underlineOffset() != -1) + Upos = (charStyle.underlineOffset() / 1000.0) * (charStyle.font().descent(charStyle.fontSize() / 10.0)); + else + Upos = charStyle.font().underlinePos(charStyle.fontSize() / 10.0); + if (charStyle.underlineWidth() != -1) + lw = (charStyle.underlineWidth() / 1000.0) * (charStyle.fontSize() / 10.0); + else + lw = qMax(charStyle.font().strokeWidth(charStyle.fontSize() / 10.0), 1.0); + } + else + { + Upos = charStyle.font().underlinePos(charStyle.fontSize() / 10.0); + lw = qMax(charStyle.font().strokeWidth(charStyle.fontSize() / 10.0), 1.0); + } + if (charStyle.baselineOffset() != 0) + Upos += (charStyle.fontSize() / 10.0) * (charStyle.baselineOffset() / 1000.0); + QDomElement ob6 = docu.createElement("path"); + if (charStyle.effects() & ScStyle_Subscript) + ob6.setAttribute("d", QString("M %1 %2 L%3 %4").arg(x + hl->glyph.xoffset-kern).arg(y + hl->glyph.yoffset - Upos).arg(x + hl->glyph.xoffset+Ulen).arg(y + hl->glyph.yoffset - Upos)); + else + ob6.setAttribute("d", QString("M %1 %2 L%3 %4").arg(x + hl->glyph.xoffset-kern).arg(y - Upos).arg(x + hl->glyph.xoffset+Ulen).arg(y - Upos)); + QString sT = "stroke:none;"; + if (charStyle.fillColor() != CommonStrings::None) + { + sT = "stroke:"+SetColor(charStyle.fillColor(), charStyle.fillShade())+";"; + sT += " stroke-width:"+FToStr(lw)+";"; + } + ob6.setAttribute("style", "fill:none;" + sT); + ob.appendChild(ob6); + } + if (chstr > QChar(32)) + { + QDomElement ob3 = docu.createElement("use"); + ob3.setAttribute("xlink:href", "#" + glName); + ob3.setAttribute("transform", MatrixToStr(finalMat)); + QString fT = "fill:"+SetColor(hl->fillColor(), hl->fillShade())+";"; + QString sT = "stroke:none;"; + if (charStyle.effects() & ScStyle_Outline) + { + sT = "stroke:"+SetColor(hl->strokeColor(), hl->strokeShade())+";"; + sT += " stroke-width:"+FToStr(chs * hl->outlineWidth() / 10000.0)+";"; + } + ob3.setAttribute("style", fT + sT); + ob.appendChild(ob3); + } + if (charStyle.effects() & ScStyle_Strikethrough) + { + x = CurX; + y = ls.y; + double Ulen = hl->glyph.xadvance; + double Upos, lw, kern; + if (charStyle.effects() & ScStyle_StartOfLine) + kern = 0; + else + kern = charStyle.fontSize() * charStyle.tracking() / 10000.0; + if ((charStyle.strikethruOffset() != -1) || (charStyle.strikethruWidth() != -1)) + { + if (charStyle.strikethruOffset() != -1) + Upos = (charStyle.strikethruOffset() / 1000.0) * (charStyle.font().ascent(charStyle.fontSize() / 10.0)); + else + Upos = charStyle.font().strikeoutPos(charStyle.fontSize() / 10.0); + if (charStyle.strikethruWidth() != -1) + lw = (charStyle.strikethruWidth() / 1000.0) * (charStyle.fontSize() / 10.0); + else + lw = qMax(charStyle.font().strokeWidth(charStyle.fontSize() / 10.0), 1.0); + } + else + { + Upos = charStyle.font().strikeoutPos(charStyle.fontSize() / 10.0); + lw = qMax(charStyle.font().strokeWidth(charStyle.fontSize() / 10.0), 1.0); + } + if (charStyle.baselineOffset() != 0) + Upos += (charStyle.fontSize() / 10.0) * hl->glyph.scaleV * (charStyle.baselineOffset() / 1000.0); + QDomElement ob7 = docu.createElement("path"); + ob7.setAttribute("d", QString("M %1 %2 L%3 %4").arg(x + hl->glyph.xoffset-kern).arg(y + hl->glyph.yoffset - Upos).arg(x + hl->glyph.xoffset+Ulen).arg(y + hl->glyph.yoffset - Upos)); + QString sT = "stroke:none;"; + if (charStyle.fillColor() != CommonStrings::None) + { + sT = "stroke:"+SetColor(charStyle.fillColor(), charStyle.fillShade())+";"; + sT += " stroke-width:"+FToStr(lw)+";"; + } + ob7.setAttribute("style", "fill:none;" + sT); + ob.appendChild(ob7); + } + CurX += hl->glyph.wide(); + } + } + } + if (Item->NamedLStyle.isEmpty()) + { + QDomElement ob4 = docu.createElement("path"); + ob4.setAttribute("d", SetClipPath(&Item->PoLine, true)); + ob4.setAttribute("style", "fill:none; "+stroke); + ob.appendChild(ob4); + } + else + { + multiLine ml = m_Doc->MLineStyles[Item->NamedLStyle]; + for (int it = ml.size()-1; it > -1; it--) + { + if ((ml[it].Color != CommonStrings::None) && (ml[it].Width != 0)) + { + QDomElement ob5 = docu.createElement("path"); + ob5.setAttribute("d", SetClipPath(&Item->PoLine, true)); + ob5.setAttribute("style", "fill:none; "+GetMultiStroke(&ml[it], Item)); + ob.appendChild(ob5); + } + } + } + return ob; +} + +QDomElement SVGExPlug::processPathTextItem(PageItem *Item, QString trans, QString stroke) +{ + QDomElement ob; + ob = docu.createElement("g"); + ob.setAttribute("transform", trans); + if (Item->PoShow) + { + if (Item->NamedLStyle.isEmpty()) + { + QDomElement ob4 = docu.createElement("path"); + ob4.setAttribute("d", SetClipPath(&Item->PoLine, false)); + ob4.setAttribute("style", "fill:none; "+stroke); + ob.appendChild(ob4); + } + else + { + multiLine ml = m_Doc->MLineStyles[Item->NamedLStyle]; + for (int it = ml.size()-1; it > -1; it--) + { + if ((ml[it].Color != CommonStrings::None) && (ml[it].Width != 0)) + { + QDomElement ob5 = docu.createElement("path"); + ob5.setAttribute("d", SetClipPath(&Item->PoLine, false)); + ob5.setAttribute("style", "fill:none; "+GetMultiStroke(&ml[it], Item)); + ob.appendChild(ob5); + } + } + } + } + double x, y, wide; + QString chstr; + for (int a = 0; a < Item->itemText.length(); ++a) + { + x = 0.0; + y = 0.0; + ScText *hl = Item->itemText.item(a); + const CharStyle& charStyle(Item->itemText.charStyle(a)); + chstr = Item->itemText.text(a,1); + if ((chstr == QChar(13)) || (chstr == QChar(29))) + continue; + if (chstr == QChar(30)) + { + chstr = Item->ExpandToken(a); + if (chstr == QChar(32)) + continue; + } + double chs = charStyle.fontSize(); + if (hl->effects() & ScStyle_SmallCaps) + { + if (chstr.toUpper() != chstr) + { + chs = qMax(static_cast<int>(hl->fontSize() * m_Doc->typographicSettings.valueSmallCaps / 100), 1); + chstr = chstr.toUpper(); + } + } + else if (hl->effects() & ScStyle_AllCaps) + chstr = chstr.toUpper(); + uint chr = chstr[0].unicode(); + QPointF tangt = QPointF( cos(hl->PRot), sin(hl->PRot) ); + QMatrix chma, chma2, chma3, chma4, chma6; + QMatrix trafo = QMatrix( 1, 0, 0, -1, -hl->PDx, 0 ); + if (Item->textPathFlipped) + trafo *= QMatrix(1, 0, 0, -1, 0, 0); + if (Item->textPathType == 0) + trafo *= QMatrix( tangt.x(), tangt.y(), tangt.y(), -tangt.x(), hl->PtransX, hl->PtransY ); + else if (Item->textPathType == 1) + trafo *= QMatrix(1, 0, 0, -1, hl->PtransX, hl->PtransY ); + else if (Item->textPathType == 2) + { + double a = 1; + double b = -1; + if (tangt.x() < 0) + { + a = -1; + b = 1; + } + if (fabs(tangt.x()) > 0.1) + trafo *= QMatrix( a, (tangt.y() / tangt.x()) * b, 0, -1, hl->PtransX, hl->PtransY ); // ID's Skew mode + else + trafo *= QMatrix( a, 6 * b, 0, -1, hl->PtransX, hl->PtransY ); + } + if (hl->hasObject()) + { + QMatrix finalMat = QMatrix(chma * chma2 * chma3 * chma4 * chma6 * trafo); + ob.appendChild(processInlineItem(0, 0, finalMat, hl, true, trans)); + } + else + { + if (Item->rotation() != 0) + { + QMatrix sca; + sca.translate(-Item->xPos(), -Item->yPos()); + trafo *= sca; + } + chma.scale(hl->glyph.scaleH * charStyle.fontSize() / 100.00, hl->glyph.scaleV * charStyle.fontSize() / 100.0); + if (Item->reversed()) + { + if (a < Item->itemText.length()-1) + wide = hl->font().charWidth(chstr[0], hl->fontSize(), Item->itemText.text(a+1)); + else + wide = hl->font().charWidth(chstr[0], hl->fontSize()); + chma3.scale(-1, 1); + chma3.translate(-wide, 0); + } + chma4.translate(0, Item->BaseOffs - (charStyle.fontSize() / 10.0) * hl->glyph.scaleV); + if (charStyle.effects() & (ScStyle_Subscript | ScStyle_Superscript | ScStyle_DropCap)) + chma6.translate(0, hl->glyph.yoffset); + if (hl->baselineOffset() != 0) + chma6.translate(0, (-charStyle.fontSize() / 10.0) * (charStyle.baselineOffset() / 1000.0)); + QMatrix finalMat = QMatrix(chma * chma2 * chma3 * chma4 * chma6 * trafo); + if (Item->rotation() != 0) + { + QMatrix sca; + sca.translate(Item->xPos(), Item->yPos()); + finalMat *= sca; + } + QChar chstc = hl->ch; + if (((charStyle.effects() & ScStyle_Underline) && !SpecialChars::isBreak(chstc)) + || ((charStyle.effects() & ScStyle_UnderlineWords) && !chstc.isSpace() && !SpecialChars::isBreak(chstc))) + { + QMatrix stro = QMatrix(chma2 * chma3 * chma6 * trafo); + if (Item->rotation() != 0) + { + QMatrix sca; + sca.translate(Item->xPos(), Item->yPos()); + stro *= sca; + } + double Ulen = hl->glyph.xadvance; + double Upos, Uwid, kern; + if (hl->effects() & ScStyle_StartOfLine) + kern = 0; + else + kern = charStyle.fontSize() * charStyle.tracking() / 10000.0; + if ((charStyle.underlineOffset() != -1) || (charStyle.underlineWidth() != -1)) + { + if (charStyle.underlineOffset() != -1) + Upos = (charStyle.underlineOffset() / 1000.0) * (charStyle.font().descent(charStyle.fontSize() / 10.0)); + else + Upos = charStyle.font().underlinePos(charStyle.fontSize() / 10.0); + if (charStyle.underlineWidth() != -1) + Uwid = (charStyle.underlineWidth() / 1000.0) * (charStyle.fontSize() / 10.0); + else + Uwid = qMax(charStyle.font().strokeWidth(charStyle.fontSize() / 10.0), 1.0); + } + else + { + Upos = charStyle.font().underlinePos(charStyle.fontSize() / 10.0); + Uwid = qMax(charStyle.font().strokeWidth(charStyle.fontSize() / 10.0), 1.0); + } + if (charStyle.baselineOffset() != 0) + Upos += (charStyle.fontSize() / 10.0) * (charStyle.baselineOffset() / 1000.0); + QDomElement ob8 = docu.createElement("path"); + ob8.setAttribute("transform", MatrixToStr(stro)); + if (charStyle.effects() & ScStyle_Subscript) + ob8.setAttribute("d", QString("M %1 %2 L%3 %4").arg(hl->glyph.xoffset-kern).arg(-Upos).arg(hl->glyph.xoffset+Ulen).arg(-Upos)); + else + ob8.setAttribute("d", QString("M %1 %2 L%3 %4").arg(hl->glyph.xoffset-kern).arg(-(Upos + hl->glyph.yoffset)).arg(hl->glyph.xoffset+Ulen).arg(-(Upos + hl->glyph.yoffset))); + QString sT = "stroke:none;"; + if (charStyle.fillColor() != CommonStrings::None) + { + sT = "stroke:"+SetColor(charStyle.fillColor(), charStyle.fillShade())+";"; + sT += " stroke-width:"+FToStr(Uwid)+";"; + } + ob8.setAttribute("style", "fill:none;" + sT); + ob.appendChild(ob8); + } + if (chstr > QChar(32)) + { + QString glName = handleGlyph(chr, hl); + if ((charStyle.effects() & ScStyle_Shadowed) && (charStyle.strokeColor() != CommonStrings::None)) + { + QMatrix sha = finalMat; + QMatrix shad; + shad.translate(charStyle.fontSize() * charStyle.shadowXOffset() / 10000.0, -charStyle.fontSize() * charStyle.shadowYOffset() / 10000.0); + sha *= shad; + QDomElement ob2 = docu.createElement("use"); + ob2.setAttribute("xlink:href", "#" + glName); + ob2.setAttribute("transform", MatrixToStr(sha)); + ob2.setAttribute("style", "fill:"+SetColor(hl->strokeColor(), hl->strokeShade())+";" + "stroke:none;"); + ob.appendChild(ob2); + } + QDomElement ob1 = docu.createElement("use"); + ob1.setAttribute("xlink:href", "#" + glName); + ob1.setAttribute("transform", MatrixToStr(finalMat)); + QString fT = "fill:"+SetColor(hl->fillColor(), hl->fillShade())+";"; + QString sT = "stroke:none;"; + if (charStyle.effects() & ScStyle_Outline) + { + sT = "stroke:"+SetColor(hl->strokeColor(), hl->strokeShade())+";"; + sT += " stroke-width:"+FToStr(chs * hl->outlineWidth() / 10000.0)+";"; + } + ob1.setAttribute("style", fT + sT); + ob.appendChild(ob1); + } + if (charStyle.effects() & ScStyle_Strikethrough) + { + QMatrix stro = QMatrix(chma2 * chma3 * chma6 * trafo); + if (Item->rotation() != 0) + { + QMatrix sca; + sca.translate(Item->xPos(), Item->yPos()); + stro *= sca; + } + double Ulen = hl->glyph.xadvance; + double Upos, Uwid, kern; + if (hl->effects() & ScStyle_StartOfLine) + kern = 0; + else + kern = charStyle.fontSize() * charStyle.tracking() / 10000.0; + if ((charStyle.strikethruOffset() != -1) || (charStyle.strikethruWidth() != -1)) + { + if (charStyle.strikethruOffset() != -1) + Upos = (charStyle.strikethruOffset() / 1000.0) * (charStyle.font().ascent(charStyle.fontSize() / 10.0)); + else + Upos = charStyle.font().strikeoutPos(charStyle.fontSize() / 10.0); + if (charStyle.strikethruWidth() != -1) + Uwid = (charStyle.strikethruWidth() / 1000.0) * (charStyle.fontSize() / 10.0); + else + Uwid = qMax(charStyle.font().strokeWidth(charStyle.fontSize() / 10.0), 1.0); + } + else + { + Upos = charStyle.font().strikeoutPos(charStyle.fontSize() / 10.0); + Uwid = qMax(charStyle.font().strokeWidth(charStyle.fontSize() / 10.0), 1.0); + } + if (charStyle.baselineOffset() != 0) + Upos += (charStyle.fontSize() / 10.0) * (charStyle.baselineOffset() / 1000.0); + QDomElement ob7 = docu.createElement("path"); + ob7.setAttribute("transform", MatrixToStr(stro)); + ob7.setAttribute("d", QString("M %1 %2 L%3 %4").arg(hl->glyph.xoffset-kern).arg(-Upos).arg(hl->glyph.xoffset+Ulen).arg(-Upos)); + QString sT = "stroke:none;"; + if (charStyle.fillColor() != CommonStrings::None) + { + sT = "stroke:"+SetColor(charStyle.fillColor(), charStyle.fillShade())+";"; + sT += " stroke-width:"+FToStr(Uwid)+";"; + } + ob7.setAttribute("style", "fill:none;" + sT); + ob.appendChild(ob7); + } + } + } + return ob; +} + +QDomElement SVGExPlug::processInlineItem(double xpos, double ypos, QMatrix &finalMat, ScText *hl, bool pathT, QString trans) +{ + const CharStyle & charStyle(*hl); + QList<PageItem*> emG = hl->embedded.getGroupedItems(); + QStack<PageItem*> groupStack; + QStack<QDomElement> groupStack2; + QDomElement layerGroup = docu.createElement("g"); + if (pathT) + layerGroup.setAttribute("transform", MatrixToStr(finalMat)); + for (int em = 0; em < emG.count(); ++em) + { + PageItem* embedded = emG.at(em); + if (embedded->isGroupControl) + { + groupStack.push(embedded->groupsLastItem); + groupStack2.push(layerGroup); + layerGroup = docu.createElement("g"); + if (embedded->fillTransparency() != 0) + layerGroup.setAttribute("opacity", FToStr(1.0 - embedded->fillTransparency())); + QDomElement ob = docu.createElement("clipPath"); + ob.setAttribute("id", "Clip"+IToStr(ClipCount)); + QDomElement cl = docu.createElement("path"); + cl.setAttribute("d", SetClipPath(&embedded->PoLine, true)); + QMatrix mm; + mm.translate(xpos + embedded->gXpos * (charStyle.scaleH() / 1000.0), (ypos - (embedded->gHeight * (charStyle.scaleV() / 1000.0)) + embedded->gYpos * (charStyle.scaleV() / 1000.0))); + if (charStyle.baselineOffset() != 0) + mm.translate(0, embedded->gHeight * (charStyle.baselineOffset() / 1000.0)); + if (charStyle.scaleH() != 1000) + mm.scale(charStyle.scaleH() / 1000.0, 1); + if (charStyle.scaleV() != 1000) + mm.scale(1, charStyle.scaleV() / 1000.0); + mm.rotate(embedded->rotation()); + cl.setAttribute("transform", MatrixToStr(mm)); + ob.appendChild(cl); + globalDefs.appendChild(ob); + layerGroup.setAttribute("clip-path", "url(#Clip"+IToStr(ClipCount)+")"); + ClipCount++; + continue; + } + QDomElement obE; + QString fill = getFillStyle(embedded); + QString stroke = "stroke:none"; + if (!embedded->isTableItem) + stroke = getStrokeStyle(embedded); + switch (embedded->itemType()) + { + case PageItem::Polygon: + case PageItem::PolyLine: + obE = processPolyItem(embedded, trans, fill, stroke); + if ((embedded->lineColor() != CommonStrings::None) && ((embedded->startArrowIndex() != 0) || (embedded->endArrowIndex() != 0))) + obE = processArrows(embedded, obE, trans); + break; + case PageItem::Line: + obE = processLineItem(embedded, trans, stroke); + if ((embedded->lineColor() != CommonStrings::None) && ((embedded->startArrowIndex() != 0) || (embedded->endArrowIndex() != 0))) + obE = processArrows(embedded, obE, trans); + break; + case PageItem::ImageFrame: + case PageItem::LatexFrame: + obE = processImageItem(embedded, trans, fill, stroke); + break; + case PageItem::TextFrame: + obE = processTextItem(embedded, trans, fill, stroke); + break; + case PageItem::PathText: + obE = processPathTextItem(embedded, trans, stroke); + break; + default: + break; + } + QMatrix mm; + mm.translate(xpos + embedded->gXpos * (charStyle.scaleH() / 1000.0), (ypos - (embedded->gHeight * (charStyle.scaleV() / 1000.0)) + embedded->gYpos * (charStyle.scaleV() / 1000.0))); + if (charStyle.baselineOffset() != 0) + mm.translate(0, embedded->gHeight * (charStyle.baselineOffset() / 1000.0)); + if (charStyle.scaleH() != 1000) + mm.scale(charStyle.scaleH() / 1000.0, 1); + if (charStyle.scaleV() != 1000) + mm.scale(1, charStyle.scaleV() / 1000.0); + mm.rotate(embedded->rotation()); + obE.setAttribute("transform", MatrixToStr(mm)); + layerGroup.appendChild(obE); + if (groupStack.count() != 0) + { + while (embedded == groupStack.top()) + { + groupStack.pop(); + groupStack2.top().appendChild(layerGroup); + layerGroup = groupStack2.pop(); + if (groupStack.count() == 0) + break; + } + } + } + for (int em = 0; em < emG.count(); ++em) + { + PageItem* embedded = emG.at(em); + if (!embedded->isTableItem) + continue; + if ((embedded->lineColor() == CommonStrings::None) || (embedded->lineWidth() == 0.0)) + continue; + if ((embedded->TopLine) || (embedded->RightLine) || (embedded->BottomLine) || (embedded->LeftLine)) + { + QMatrix mm; + mm.translate(xpos + embedded->gXpos * (charStyle.scaleH() / 1000.0), (ypos - (embedded->gHeight * (charStyle.scaleV() / 1000.0)) + embedded->gYpos * (charStyle.scaleV() / 1000.0))); + if (charStyle.baselineOffset() != 0) + mm.translate(0, embedded->gHeight * (charStyle.baselineOffset() / 1000.0)); + if (charStyle.scaleH() != 1000) + mm.scale(charStyle.scaleH() / 1000.0, 1); + if (charStyle.scaleV() != 1000) + mm.scale(1, charStyle.scaleV() / 1000.0); + mm.rotate(embedded->rotation()); + QString stroke = getStrokeStyle(embedded); + QDomElement obL = docu.createElement("path"); + obL.setAttribute("transform", MatrixToStr(mm)); + obL.setAttribute("style", "fill:none; " + stroke); + QString pathAttr = ""; + if (embedded->TopLine) + pathAttr += "M 0 0 L "+FToStr(embedded->width())+" 0"; + if (embedded->RightLine) + pathAttr += " M " + FToStr(embedded->width()) + "0 L "+FToStr(embedded->width())+" "+FToStr(embedded->height()); + if (embedded->BottomLine) + pathAttr += " M 0 " + FToStr(embedded->height()) + " L "+FToStr(embedded->width())+" "+FToStr(embedded->height()); + if (embedded->LeftLine) + pathAttr += " M 0 0 L 0 "+FToStr(embedded->height()); + obL.setAttribute("d", pathAttr); + layerGroup.appendChild(obL); + } + } + return layerGroup; +} + +QString SVGExPlug::handleGlyph(uint chr, ScText *hl) +{ + if (chr == 32) + return "SPACE"; + QString glName = QString("Gl%1%2").arg(hl->font().psName().simplified().replace(QRegExp("[\\s\\/\\{\\[\\]\\}\\<\\>\\(\\)\\%]"), "_" )).arg(chr); + if (glyphNames.contains(glName)) + return glName; + uint gl = hl->font().char2CMap(chr); + FPointArray pts = hl->font().glyphOutline(gl); + QDomElement ob = docu.createElement("path"); + ob.setAttribute("d", SetClipPath(&pts, true)); + ob.setAttribute("id", glName); + globalDefs.appendChild(ob); + glyphNames.append(glName); + return glName; +} + +QDomElement SVGExPlug::processArrows(PageItem *Item, QDomElement line, QString trans) +{ + QDomElement ob, gr; + gr = docu.createElement("g"); + gr.appendChild(line); + if (Item->startArrowIndex() != 0) + { + QMatrix arrowTrans; + FPointArray arrow = m_Doc->arrowStyles.at(Item->startArrowIndex()-1).points.copy(); + if (Item->itemType() == PageItem::Line) + { + arrowTrans.translate(0, 0); + if (Item->NamedLStyle.isEmpty()) + { + if (Item->lineWidth() != 0.0) + arrowTrans.scale(Item->lineWidth(), Item->lineWidth()); + } + else + { + multiLine ml = m_Doc->MLineStyles[Item->NamedLStyle]; + if (ml[ml.size()-1].Width != 0.0) + arrowTrans.scale(ml[ml.size()-1].Width, ml[ml.size()-1].Width); + } + arrowTrans.scale(-1,1); + } + else + { + FPoint Start = Item->PoLine.point(0); + for (uint xx = 1; xx < Item->PoLine.size(); xx += 2) + { + FPoint Vector = Item->PoLine.point(xx); + if ((Start.x() != Vector.x()) || (Start.y() != Vector.y())) + { + double r = atan2(Start.y()-Vector.y(),Start.x()-Vector.x())*(180.0/M_PI); + arrowTrans.translate(Start.x(), Start.y()); + arrowTrans.rotate(r); + if (Item->NamedLStyle.isEmpty()) + { + if (Item->lineWidth() != 0.0) + arrowTrans.scale(Item->lineWidth(), Item->lineWidth()); + } + else + { + multiLine ml = m_Doc->MLineStyles[Item->NamedLStyle]; + if (ml[ml.size()-1].Width != 0.0) + arrowTrans.scale(ml[ml.size()-1].Width, ml[ml.size()-1].Width); + } + break; + } + } + } + arrow.map(arrowTrans); + if (Item->NamedLStyle.isEmpty()) + { + ob = docu.createElement("path"); + ob.setAttribute("d", SetClipPath(&arrow, true)); + ob.setAttribute("transform", trans); + QString aFill = "fill:"+SetColor(Item->lineColor(), Item->lineShade())+";"; + if (Item->lineTransparency() != 0) + aFill += " fill-opacity:"+FToStr(1.0 - Item->lineTransparency())+";"; + ob.setAttribute("style", aFill + " stroke:none;"); + gr.appendChild(ob); + } + else + { + multiLine ml = m_Doc->MLineStyles[Item->NamedLStyle]; + if (ml[0].Color != CommonStrings::None) + { + ob = docu.createElement("path"); + ob.setAttribute("d", SetClipPath(&arrow, true)); + ob.setAttribute("transform", trans); + QString aFill = "fill:"+SetColor(ml[0].Color, ml[0].Shade)+";"; + ob.setAttribute("style", aFill + " stroke:none;"); + gr.appendChild(ob); + } + for (int it = ml.size()-1; it > 0; it--) + { + if (ml[it].Color != CommonStrings::None) + { + QDomElement ob5 = docu.createElement("path"); + ob5.setAttribute("d", SetClipPath(&arrow, true)); + ob5.setAttribute("transform", trans); + QString stroke = "fill:none; stroke:"+SetColor(ml[it].Color, ml[it].Shade)+"; stroke-linecap:butt; stroke-linejoin:miter; stroke-dasharray:none;"; + if (ml[it].Width != 0.0) + stroke += " stroke-width:"+FToStr(ml[it].Width)+";"; + else + stroke += " stroke-width:1px;"; + ob5.setAttribute("style", stroke); + gr.appendChild(ob5); + } + } + } + } + if (Item->endArrowIndex() != 0) + { + QMatrix arrowTrans; + FPointArray arrow = m_Doc->arrowStyles.at(Item->endArrowIndex()-1).points.copy(); + if (Item->itemType() == PageItem::Line) + { + arrowTrans.translate(Item->width(), 0); + if (Item->NamedLStyle.isEmpty()) + { + if (Item->lineWidth() != 0.0) + arrowTrans.scale(Item->lineWidth(), Item->lineWidth()); + } + else + { + multiLine ml = m_Doc->MLineStyles[Item->NamedLStyle]; + if (ml[ml.size()-1].Width != 0.0) + arrowTrans.scale(ml[ml.size()-1].Width, ml[ml.size()-1].Width); + } + } + else + { + FPoint End = Item->PoLine.point(Item->PoLine.size()-2); + for (uint xx = Item->PoLine.size()-1; xx > 0; xx -= 2) + { + FPoint Vector = Item->PoLine.point(xx); + if ((End.x() != Vector.x()) || (End.y() != Vector.y())) + { + double r = atan2(End.y()-Vector.y(),End.x()-Vector.x())*(180.0/M_PI); + arrowTrans.translate(End.x(), End.y()); + arrowTrans.rotate(r); + if (Item->NamedLStyle.isEmpty()) + { + if (Item->lineWidth() != 0.0) + arrowTrans.scale(Item->lineWidth(), Item->lineWidth()); + } + else + { + multiLine ml = m_Doc->MLineStyles[Item->NamedLStyle]; + if (ml[ml.size()-1].Width != 0.0) + arrowTrans.scale(ml[ml.size()-1].Width, ml[ml.size()-1].Width); + } + break; + } + } + } + arrow.map(arrowTrans); + if (Item->NamedLStyle.isEmpty()) + { + ob = docu.createElement("path"); + ob.setAttribute("d", SetClipPath(&arrow, true)); + ob.setAttribute("transform", trans); + QString aFill = "fill:"+SetColor(Item->lineColor(), Item->lineShade())+";"; + if (Item->lineTransparency() != 0) + aFill += " fill-opacity:"+FToStr(1.0 - Item->lineTransparency())+";"; + ob.setAttribute("style", aFill + " stroke:none;"); + gr.appendChild(ob); + } + else + { + multiLine ml = m_Doc->MLineStyles[Item->NamedLStyle]; + if (ml[0].Color != CommonStrings::None) + { + ob = docu.createElement("path"); + ob.setAttribute("d", SetClipPath(&arrow, true)); + ob.setAttribute("transform", trans); + QString aFill = "fill:"+SetColor(ml[0].Color, ml[0].Shade)+";"; + ob.setAttribute("style", aFill + " stroke:none;"); + gr.appendChild(ob); + } + for (int it = ml.size()-1; it > 0; it--) + { + if (ml[it].Color != CommonStrings::None) + { + QDomElement ob5 = docu.createElement("path"); + ob5.setAttribute("d", SetClipPath(&arrow, true)); + ob5.setAttribute("transform", trans); + QString stroke = "fill:none; stroke:"+SetColor(ml[it].Color, ml[it].Shade)+"; stroke-linecap:butt; stroke-linejoin:miter; stroke-dasharray:none;"; + if (ml[it].Width != 0.0) + stroke += " stroke-width:"+FToStr(ml[it].Width)+";"; + else + stroke += " stroke-width:1px;"; + ob5.setAttribute("style", stroke); + gr.appendChild(ob5); + } + } + } + } + return gr; +} + +QString SVGExPlug::getFillStyle(PageItem *Item) +{ + QDomElement grad; + QString fill; + if (Item->asPathText()) + return "fill:none;"; + if ((Item->fillColor() != CommonStrings::None) || (Item->GrType != 0)) + { + fill = "fill:"+SetColor(Item->fillColor(), Item->fillShade())+";"; + if (Item->GrType != 0) + { + if (Item->GrType == 8) + { + QStack<PageItem*> groupStack; + QStack<QDomElement> groupStack2; + QString pattID = Item->pattern()+IToStr(PattCount); + PattCount++; + ScPattern pa = m_Doc->docPatterns[Item->pattern()]; + QDomElement patt = docu.createElement("pattern"); + patt.setAttribute("id", pattID); + patt.setAttribute("height", pa.height); + patt.setAttribute("width", pa.width); + patt.setAttribute("patternUnits", "userSpaceOnUse"); + double patternScaleX, patternScaleY, patternOffsetX, patternOffsetY, patternRotation; + Item->patternTransform(patternScaleX, patternScaleY, patternOffsetX, patternOffsetY, patternRotation); + QMatrix mpa; + mpa.translate(patternOffsetX, patternOffsetY); + mpa.rotate(patternRotation); + mpa.scale(pa.scaleX, pa.scaleY); + mpa.scale(patternScaleX / 100.0 , patternScaleY / 100.0); + patt.setAttribute("patternTransform", MatrixToStr(mpa)); + for (int em = 0; em < pa.items.count(); ++em) + { + PageItem* Item = pa.items.at(em); + if (Item->isGroupControl) + { + groupStack.push(Item->groupsLastItem); + groupStack2.push(patt); + patt = docu.createElement("g"); + if (Item->fillTransparency() != 0) + patt.setAttribute("opacity", FToStr(1.0 - Item->fillTransparency())); + QDomElement ob = docu.createElement("clipPath"); + ob.setAttribute("id", "Clip"+IToStr(ClipCount)); + QDomElement cl = docu.createElement("path"); + cl.setAttribute("d", SetClipPath(&Item->PoLine, true)); + QString trans = "translate("+FToStr(Item->gXpos)+", "+FToStr(Item->gYpos)+")"; + if (Item->rotation() != 0) + trans += " rotate("+FToStr(Item->rotation())+")"; + cl.setAttribute("transform", trans); + ob.appendChild(cl); + globalDefs.appendChild(ob); + patt.setAttribute("clip-path", "url(#Clip"+IToStr(ClipCount)+")"); + ClipCount++; + continue; + } + ProcessItemOnPage(Item->gXpos, Item->gYpos, Item, &patt); + if (groupStack.count() != 0) + { + while (Item == groupStack.top()) + { + groupStack.pop(); + groupStack2.top().appendChild(patt); + patt = groupStack2.pop(); + if (groupStack.count() == 0) + break; + } + } + } + for (int em = 0; em < pa.items.count(); ++em) + { + PageItem* embedded = pa.items.at(em); + QString trans = "translate("+FToStr(embedded->gXpos)+", "+FToStr(embedded->gYpos)+")"; + if (embedded->rotation() != 0) + trans += " rotate("+FToStr(embedded->rotation())+")"; + if (!embedded->isTableItem) + continue; + if ((embedded->lineColor() == CommonStrings::None) || (embedded->lineWidth() == 0.0)) + continue; + if ((embedded->TopLine) || (embedded->RightLine) || (embedded->BottomLine) || (embedded->LeftLine)) + { + QString stroke = getStrokeStyle(embedded); + QDomElement obL = docu.createElement("path"); + obL.setAttribute("transform", trans); + obL.setAttribute("style", "fill:none; " + stroke); + QString pathAttr = ""; + if (embedded->TopLine) + pathAttr += "M 0 0 L "+FToStr(embedded->width())+" 0"; + if (embedded->RightLine) + pathAttr += " M " + FToStr(embedded->width()) + "0 L "+FToStr(embedded->width())+" "+FToStr(embedded->height()); + if (embedded->BottomLine) + pathAttr += " M 0 " + FToStr(embedded->height()) + " L "+FToStr(embedded->width())+" "+FToStr(embedded->height()); + if (embedded->LeftLine) + pathAttr += " M 0 0 L 0 "+FToStr(embedded->height()); + obL.setAttribute("d", pathAttr); + patt.appendChild(obL); + } + } + globalDefs.appendChild(patt); + fill = "fill:url(#"+pattID+");"; + } + else + { + if ((Item->GrType == 5) || (Item->GrType == 7)) + grad = docu.createElement("radialGradient"); + else + grad = docu.createElement("linearGradient"); + grad.setAttribute("id", "Grad"+IToStr(GradCount)); + grad.setAttribute("gradientUnits", "userSpaceOnUse"); + switch (Item->GrType) + { + case 1: + grad.setAttribute("x1", "0"); + grad.setAttribute("y1", FToStr(Item->height() / 2.0)); + grad.setAttribute("x2", FToStr(Item->width())); + grad.setAttribute("y2", FToStr(Item->height() / 2.0)); + break; + case 2: + grad.setAttribute("x1", FToStr(Item->width()/ 2.0)); + grad.setAttribute("y1", "0"); + grad.setAttribute("x2", FToStr(Item->width()/ 2.0)); + grad.setAttribute("y2", FToStr(Item->height())); + break; + case 3: + grad.setAttribute("x1", "0"); + grad.setAttribute("y1", "0"); + grad.setAttribute("x2", FToStr(Item->width())); + grad.setAttribute("y2", FToStr(Item->height())); + break; + case 4: + grad.setAttribute("x1", "0"); + grad.setAttribute("y1", FToStr(Item->height())); + grad.setAttribute("x2", FToStr(Item->width())); + grad.setAttribute("y2", "0"); + break; + case 5: + grad.setAttribute("r", FToStr(qMax(Item->width() / 2.0, Item->height() / 2.0))); + grad.setAttribute("cx", FToStr(Item->width() / 2.0)); + grad.setAttribute("cy", FToStr(Item->height() / 2.0)); + break; + case 6: + grad.setAttribute("x1", FToStr(Item->GrStartX)); + grad.setAttribute("y1", FToStr(Item->GrStartY)); + grad.setAttribute("x2", FToStr(Item->GrEndX)); + grad.setAttribute("y2", FToStr(Item->GrEndY)); + break; + case 7: + grad.setAttribute("r", FToStr(qMax(Item->width() / 2.0, Item->height() / 2.0))); + grad.setAttribute("cx", FToStr(Item->GrStartX)); + grad.setAttribute("cy", FToStr(Item->GrStartY)); + break; + } + bool isFirst = true; + double actualStop = 0.0, lastStop = 0.0; + QList<VColorStop*> cstops = Item->fill_gradient.colorStops(); + for (uint cst = 0; cst < Item->fill_gradient.Stops(); ++cst) + { + actualStop = cstops.at(cst)->rampPoint; + if ((actualStop != lastStop) || (isFirst)) + { + QDomElement itcl = docu.createElement("stop"); + itcl.setAttribute("offset", FToStr(cstops.at(cst)->rampPoint*100)+"%"); + itcl.setAttribute("stop-opacity", FToStr(cstops.at(cst)->opacity)); + itcl.setAttribute("stop-color", SetColor(cstops.at(cst)->name, cstops.at(cst)->shade)); + grad.appendChild(itcl); + lastStop = actualStop; + isFirst = false; + } + } + globalDefs.appendChild(grad); + fill = "fill:url(#Grad"+IToStr(GradCount)+");"; + GradCount++; + } + } + if (Item->fillRule) + fill += " fill-rule:evenodd;"; + else + fill += " fill-rule:nonzero;"; + if (Item->fillTransparency() != 0) + fill += " fill-opacity:"+FToStr(1.0 - Item->fillTransparency())+";"; + } + else + fill = "fill:none;"; + return fill; +} + +QString SVGExPlug::getStrokeStyle(PageItem *Item) +{ + QString stroke = ""; + if (Item->lineColor() != CommonStrings::None) + { + stroke = "stroke:"+SetColor(Item->lineColor(), Item->lineShade())+";"; + if (Item->lineTransparency() != 0) + stroke += " stroke-opacity:"+FToStr(1.0 - Item->lineTransparency())+";"; + if (Item->lineWidth() != 0.0) + stroke += " stroke-width:"+FToStr(Item->lineWidth())+";"; + else + stroke += " stroke-width:1px;"; + stroke += " stroke-linecap:"; + switch (Item->PLineEnd) + { + case Qt::FlatCap: + stroke += "butt;"; + break; + case Qt::SquareCap: + stroke += "square;"; + break; + case Qt::RoundCap: + stroke += "round;"; + break; + default: + stroke += "butt;"; + break; + } + stroke += " stroke-linejoin:"; + switch (Item->PLineJoin) + { + case Qt::MiterJoin: + stroke += "miter;"; + break; + case Qt::BevelJoin: + stroke += "bevel;"; + break; + case Qt::RoundJoin: + stroke += "round;"; + break; + default: + stroke += "miter;"; + break; + } + stroke += " stroke-dasharray:"; + if (Item->DashValues.count() != 0) + { + QVector<double>::iterator it; + for ( it = Item->DashValues.begin(); it != Item->DashValues.end(); ++it ) + { + stroke += IToStr(static_cast<int>(*it))+" "; + } + stroke += "; stroke-dashoffset:"+IToStr(static_cast<int>(Item->DashOffset))+";"; + } + else + { + if (Item->PLineArt == Qt::SolidLine) + stroke += "none;"; + else + { + QString Da = getDashString(Item->PLineArt, Item->lineWidth()); + if (Da.isEmpty()) + stroke += "none;"; + else + stroke += Da.replace(" ", ", ")+";"; + } + } + } + else + stroke = "stroke:none;"; + return stroke; +} + +QString SVGExPlug::SetClipPath(FPointArray *ite, bool closed) +{ + QString tmp = ""; + FPoint np, np1, np2, np3; + bool nPath = true; + if (ite->size() > 3) + { + for (uint poi=0; poi<ite->size()-3; poi += 4) + { + if (ite->point(poi).x() > 900000) + { + tmp += "Z "; + nPath = true; + continue; + } + if (nPath) + { + np = ite->point(poi); + tmp += QString("M%1 %2 ").arg(np.x()).arg(np.y()); + nPath = false; + } + np = ite->point(poi); + np1 = ite->point(poi+1); + np2 = ite->point(poi+3); + np3 = ite->point(poi+2); + if ((np == np1) && (np2 == np3)) + tmp += QString("L%1 %2 ").arg(np3.x()).arg(np3.y()); + else + tmp += QString("C%1 %2 %3 %4 %5 %6 ").arg(np1.x()).arg(np1.y()).arg(np2.x()).arg(np2.y()).arg(np3.x()).arg(np3.y()); + } + } + if (closed) + tmp += "Z"; + return tmp; +} + +QString SVGExPlug::FToStr(double c) +{ + QString cc; + return cc.setNum(c); +} + +QString SVGExPlug::IToStr(int c) +{ + QString cc; + return cc.setNum(c); +} + +QString SVGExPlug::MatrixToStr(QMatrix &mat) +{ + QString cc("matrix(%1 %2 %3 %4 %5 %6)"); + return cc.arg(mat.m11()).arg(mat.m12()).arg(mat.m21()).arg(mat.m22()).arg(mat.dx()).arg(mat.dy()); +} + +QString SVGExPlug::SetColor(QString farbe, int shad) +{ + const ScColor& col = m_Doc->PageColors[farbe]; + return ScColorEngine::getShadeColorProof(col, m_Doc, shad).name(); +} + +QString SVGExPlug::GetMultiStroke(struct SingleLine *sl, PageItem *Item) +{ + QString tmp = "fill:none; "; + tmp += "stroke:"+SetColor(sl->Color, sl->Shade)+"; "; + if (Item->fillTransparency() != 0) + tmp += QString(" stroke-opacity:%1; ").arg(1.0 - Item->fillTransparency()); + tmp += QString("stroke-width:%1; ").arg(sl->Width); + tmp += "stroke-linecap:"; + switch (static_cast<Qt::PenCapStyle>(sl->LineEnd)) + { + case Qt::FlatCap: + tmp += "butt;"; + break; + case Qt::SquareCap: + tmp += "square;"; + break; + case Qt::RoundCap: + tmp += "round;"; + break; + default: + tmp += "butt;"; + break; + } + tmp += " stroke-linejoin:"; + switch (static_cast<Qt::PenJoinStyle>(sl->LineJoin)) + { + case Qt::MiterJoin: + tmp += "miter;"; + break; + case Qt::BevelJoin: + tmp += "bevel;"; + break; + case Qt::RoundJoin: + tmp += "round;"; + break; + default: + tmp += "miter;"; + break; + } + tmp += " stroke-dasharray:"; + if (static_cast<Qt::PenStyle>(sl->Dash) == Qt::SolidLine) + tmp += "none;"; + else + { + QString Da = getDashString(sl->Dash, sl->Width); + if (Da.isEmpty()) + tmp += "none;"; + else + tmp += Da.replace(" ", ", ")+";"; + } + return tmp; +} + +SVGExPlug::~SVGExPlug() +{ +} |
