1 /*
2 For general Scribus (>=1.3.2) copyright and licensing information please refer
3 to the COPYING file provided with the program. Following this notice may exist
4 a copyright and/or license notice that predates the release of Scribus 1.3.2
5 for which a new license (GPL+exception) is in place.
6 */
7 /***************************************************************************
8 svgexplugin.cpp - description
9 -------------------
10 begin : Sun Aug 3 08:00:00 CEST 2002
11 copyright : (C) 2002 by Franz Schmid
12 email : Franz.Schmid@altmuehlnet.de
13 ***************************************************************************/
14
15 /***************************************************************************
16 * *
17 * This program is free software; you can redistribute it and/or modify *
18 * it under the terms of the GNU General Public License as published by *
19 * the Free Software Foundation; either version 2 of the License, or *
20 * (at your option) any later version. *
21 * *
22 ***************************************************************************/
23
24 #include <QBuffer>
25 #include <QByteArray>
26 #include <QCheckBox>
27 #include <QDataStream>
28 #include <QFile>
29 #include <QList>
30 #include <QMessageBox>
31 #include <QScopedPointer>
32 #include <QTextStream>
33
34 #include "svgexplugin.h"
35
36 #include "scconfig.h"
37 #include "canvas.h"
38 #include "cmsettings.h"
39 #include "commonstrings.h"
40 #include "pageitem_table.h"
41 #include "prefsmanager.h"
42 #include "prefsfile.h"
43 #include "prefscontext.h"
44 #include "qtiocompressor.h"
45 #include "scpage.h"
46 #include "scpattern.h"
47 #include "scribuscore.h"
48 #include "sctextstruct.h"
49 #include "tableutils.h"
50 #include "util.h"
51 #include "ui/customfdialog.h"
52 #include "ui/guidemanager.h"
53 #include "ui/scmessagebox.h"
54 #include "sccolorengine.h"
55 #include "util_formats.h"
56 #include "util_math.h"
57 #include "text/textlayout.h"
58 #include "text/textlayoutpainter.h"
59 #include "text/boxes.h"
60
svgexplugin_getPluginAPIVersion()61 int svgexplugin_getPluginAPIVersion()
62 {
63 return PLUGIN_API_VERSION;
64 }
65
svgexplugin_getPlugin()66 ScPlugin* svgexplugin_getPlugin()
67 {
68 SVGExportPlugin* plug = new SVGExportPlugin();
69 Q_CHECK_PTR(plug);
70 return plug;
71 }
72
svgexplugin_freePlugin(ScPlugin * plugin)73 void svgexplugin_freePlugin(ScPlugin* plugin)
74 {
75 SVGExportPlugin* plug = qobject_cast<SVGExportPlugin*>(plugin);
76 Q_ASSERT(plug);
77 delete plug;
78 }
79
80 using namespace TableUtils;
81
SVGExportPlugin()82 SVGExportPlugin::SVGExportPlugin()
83 {
84 // Set action info in languageChange, so we only have to do
85 // it in one place.
86 languageChange();
87 }
88
~SVGExportPlugin()89 SVGExportPlugin::~SVGExportPlugin() {};
90
languageChange()91 void SVGExportPlugin::languageChange()
92 {
93 // Note that we leave the unused members unset. They'll be initialised
94 // with their default ctors during construction.
95 // Action name
96 m_actionInfo.name = "ExportAsSVG";
97 // Action text for menu, including accel
98 m_actionInfo.text = tr("Save as &SVG...");
99 // Menu
100 m_actionInfo.menu = "FileExport";
101 m_actionInfo.enabledOnStartup = false;
102 m_actionInfo.needsNumObjects = -1;
103 }
104
fullTrName() const105 QString SVGExportPlugin::fullTrName() const
106 {
107 return QObject::tr("SVG Export");
108 }
109
getAboutData() const110 const ScActionPlugin::AboutData* SVGExportPlugin::getAboutData() const
111 {
112 AboutData* about = new AboutData;
113 about->authors = "Franz Schmid <franz@scribus.info>";
114 about->shortDescription = tr("Exports SVG Files");
115 about->description = tr("Exports the current page into an SVG file.");
116 about->license = "GPL";
117 Q_CHECK_PTR(about);
118 return about;
119 }
120
deleteAboutData(const AboutData * about) const121 void SVGExportPlugin::deleteAboutData(const AboutData* about) const
122 {
123 Q_ASSERT(about);
124 delete about;
125 }
126
run(ScribusDoc * doc,const QString & filename)127 bool SVGExportPlugin::run(ScribusDoc* doc, const QString& filename)
128 {
129 Q_ASSERT(filename.isEmpty());
130 QString fileName;
131 if (doc!=nullptr)
132 {
133 PrefsContext* prefs = PrefsManager::instance().prefsFile->getPluginContext("svgex");
134 QString wdir = prefs->get("wdir", ".");
135 QScopedPointer<CustomFDialog> openDia( new CustomFDialog(doc->scMW(), wdir, QObject::tr("Save as"), QObject::tr("%1;;All Files (*)").arg(FormatsManager::instance()->extensionsForFormat(FormatsManager::SVG)), fdHidePreviewCheckBox) );
136 openDia->setSelection(getFileNameByPage(doc, doc->currentPage()->pageNr(), "svg"));
137 openDia->setExtension("svg");
138 openDia->setZipExtension("svgz");
139 QCheckBox* compress = new QCheckBox(openDia.data());
140 compress->setText( tr("Compress File"));
141 compress->setChecked(false);
142 openDia->addWidgets(compress);
143 QCheckBox* inlineImages = new QCheckBox(openDia.data());
144 inlineImages->setText( tr("Save Images inline"));
145 inlineImages->setToolTip( tr("Adds all Images on the Page inline to the SVG.\nCaution: this will increase the file size!"));
146 inlineImages->setChecked(true);
147 openDia->addWidgets(inlineImages);
148 QCheckBox* exportBack = new QCheckBox(openDia.data());
149 exportBack->setText( tr("Export Page background"));
150 exportBack->setToolTip( tr("Adds the Page itself as background to the SVG"));
151 exportBack->setChecked(false);
152 openDia->addWidgets(exportBack);
153
154 if (!openDia->exec())
155 return true;
156 fileName = openDia->selectedFile();
157 QFileInfo fi(fileName);
158 QString m_baseDir = fi.absolutePath();
159 if (compress->isChecked())
160 fileName = m_baseDir + "/" + fi.baseName() + ".svgz";
161 else
162 fileName = m_baseDir + "/" + fi.baseName() + ".svg";
163
164 SVGOptions Options;
165 Options.inlineImages = inlineImages->isChecked();
166 Options.exportPageBackground = exportBack->isChecked();
167 Options.compressFile = compress->isChecked();
168
169 if (fileName.isEmpty())
170 return true;
171 prefs->set("wdir", fileName.left(fileName.lastIndexOf("/")));
172 QFile f(fileName);
173 if (f.exists())
174 {
175 int exit = ScMessageBox::warning(doc->scMW(), CommonStrings::trWarning,
176 QObject::tr("Do you really want to overwrite the file:\n%1 ?").arg(fileName),
177 QMessageBox::Yes | QMessageBox::No,
178 QMessageBox::NoButton, // GUI default
179 QMessageBox::Yes); // batch default
180 if (exit == QMessageBox::No)
181 return true;
182 }
183 SVGExPlug *dia = new SVGExPlug(doc);
184 dia->doExport(fileName, Options);
185 delete dia;
186 }
187 return true;
188 }
189
SVGExPlug(ScribusDoc * doc)190 SVGExPlug::SVGExPlug( ScribusDoc* doc )
191 {
192 m_Doc = doc;
193 Options.inlineImages = true;
194 Options.exportPageBackground = false;
195 Options.compressFile = false;
196 m_glyphNames.clear();
197 }
198
doExport(const QString & fName,SVGOptions & Opts)199 bool SVGExPlug::doExport( const QString& fName, SVGOptions &Opts )
200 {
201 Options = Opts;
202 QFileInfo fiBase(fName);
203
204 m_baseDir = fiBase.absolutePath();
205 m_gradCount = 0;
206 m_clipCount = 0;
207 m_pattCount = 0;
208 m_maskCount = 0;
209 m_filterCount = 0;
210
211 m_domDoc = QDomDocument("svgdoc");
212 QString vo = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
213 QString st = "<svg></svg>";
214 m_domDoc.setContent(st);
215
216 ScPage *page = m_Doc->currentPage();
217 double pageWidth = page->width();
218 double pageHeight = page->height();
219 m_domElem = m_domDoc.documentElement();
220 m_domElem.setAttribute("width", FToStr(pageWidth) + "pt");
221 m_domElem.setAttribute("height", FToStr(pageHeight) + "pt");
222 m_domElem.setAttribute("viewBox", QString("0 0 %1 %2").arg(pageWidth).arg(pageHeight));
223 m_domElem.setAttribute("xmlns", "http://www.w3.org/2000/svg");
224 m_domElem.setAttribute("xmlns:inkscape","http://www.inkscape.org/namespaces/inkscape");
225 m_domElem.setAttribute("xmlns:xlink","http://www.w3.org/1999/xlink");
226 m_domElem.setAttribute("version","1.1");
227 if (!m_Doc->documentInfo().title().isEmpty())
228 {
229 QDomText title = m_domDoc.createTextNode(m_Doc->documentInfo().title());
230 QDomElement titleElem = m_domDoc.createElement("title");
231 titleElem.appendChild(title);
232 m_domElem.appendChild(titleElem);
233 }
234 if (!m_Doc->documentInfo().comments().isEmpty())
235 {
236 QDomText desc = m_domDoc.createTextNode(m_Doc->documentInfo().comments());
237 QDomElement descElem = m_domDoc.createElement("desc");
238 descElem.appendChild(desc);
239 m_domElem.appendChild(descElem);
240 }
241 m_globalDefs = m_domDoc.createElement("defs");
242 writeBasePatterns();
243 writeBaseSymbols();
244 m_domElem.appendChild(m_globalDefs);
245 if (Options.exportPageBackground)
246 {
247 QDomElement backG = m_domDoc.createElement("rect");
248 backG.setAttribute("x", "0");
249 backG.setAttribute("y", "0");
250 backG.setAttribute("width", FToStr(pageWidth));
251 backG.setAttribute("height", FToStr(pageHeight));
252 backG.setAttribute("style", "fill:" + m_Doc->paperColor().name() + ";" + "stroke:none;");
253 m_domElem.appendChild(backG);
254 }
255 ScLayer ll;
256 ll.isPrintable = false;
257 for (int la = 0; la < m_Doc->Layers.count(); la++)
258 {
259 m_Doc->Layers.levelToLayer(ll, la);
260 if (ll.isPrintable)
261 {
262 page = m_Doc->MasterPages.at(m_Doc->MasterNames[m_Doc->currentPage()->masterPageName()]);
263 processPageLayer(page, ll);
264 page = m_Doc->currentPage();
265 processPageLayer(page, ll);
266 }
267 }
268 if(Options.compressFile)
269 {
270 // zipped saving
271 QString wr = vo;
272 wr += m_domDoc.toString();
273 QByteArray utf8wr = wr.toUtf8();
274 QFile file(fName);
275 QtIOCompressor compressor(&file);
276 compressor.setStreamFormat(QtIOCompressor::GzipFormat);
277 compressor.open(QIODevice::WriteOnly);
278 compressor.write(utf8wr);
279 compressor.close();
280 }
281 else
282 {
283 QFile f(fName);
284 if(!f.open(QIODevice::WriteOnly))
285 return false;
286 QDataStream s(&f);
287 QString wr = vo;
288 wr += m_domDoc.toString();
289 QByteArray utf8wr = wr.toUtf8();
290 s.writeRawData(utf8wr.data(), utf8wr.length());
291 f.close();
292 }
293 return true;
294 }
295
processPageLayer(ScPage * page,ScLayer & layer)296 void SVGExPlug::processPageLayer(ScPage *page, ScLayer& layer)
297 {
298 QDomElement layerGroup;
299 PageItem *item;
300 QList<PageItem*> items;
301 ScPage* SavedAct = m_Doc->currentPage();
302 if (page->pageNameEmpty())
303 items = m_Doc->DocItems;
304 else
305 items = m_Doc->MasterItems;
306 if (items.count() == 0)
307 return;
308 if (!layer.isPrintable)
309 return;
310 m_Doc->setCurrentPage(page);
311
312 layerGroup = m_domDoc.createElement("g");
313 layerGroup.setAttribute("id", layer.Name);
314 layerGroup.setAttribute("inkscape:label", layer.Name);
315 layerGroup.setAttribute("inkscape:groupmode", "layer");
316 if (layer.transparency != 1.0)
317 layerGroup.setAttribute("opacity", FToStr(layer.transparency));
318 for (int j = 0; j < items.count(); ++j)
319 {
320 item = items.at(j);
321 if (item->m_layerID != layer.ID)
322 continue;
323 if (!item->printEnabled())
324 continue;
325 double x = page->xOffset();
326 double y = page->yOffset();
327 double w = page->width();
328 double h = page->height();
329 double x2 = item->BoundingX;
330 double y2 = item->BoundingY;
331 double w2 = item->BoundingW;
332 double h2 = item->BoundingH;
333 if (!( qMax( x, x2 ) <= qMin( x+w, x2+w2 ) && qMax( y, y2 ) <= qMin( y+h, y2+h2 )))
334 continue;
335 if ((!page->pageNameEmpty()) && (item->OwnPage != static_cast<int>(page->pageNr())) && (item->OwnPage != -1))
336 continue;
337 processItemOnPage(item->xPos()-page->xOffset(), item->yPos()-page->yOffset(), item, &layerGroup);
338 }
339 m_domElem.appendChild(layerGroup);
340
341 m_Doc->setCurrentPage(SavedAct);
342 }
343
processItemOnPage(double xOffset,double yOffset,PageItem * item,QDomElement * parentElem)344 void SVGExPlug::processItemOnPage(double xOffset, double yOffset, PageItem *item, QDomElement *parentElem)
345 {
346 QDomElement ob;
347 QString trans = "translate(" + FToStr(xOffset) + ", " + FToStr(yOffset) + ")";
348 if (item->rotation() != 0)
349 trans += " rotate(" + FToStr(item->rotation()) + ")";
350 QString fill = getFillStyle(item);
351 fill += processDropShadow(item);
352 QString stroke = "stroke:none";
353 stroke = getStrokeStyle(item);
354 switch (item->itemType())
355 {
356 case PageItem::Arc:
357 case PageItem::Polygon:
358 case PageItem::PolyLine:
359 case PageItem::RegularPolygon:
360 case PageItem::Spiral:
361 ob = processPolyItem(item, trans, fill, stroke);
362 if ((item->lineColor() != CommonStrings::None) && ((item->startArrowIndex() != 0) || (item->endArrowIndex() != 0)))
363 ob = processArrows(item, ob, trans);
364 break;
365 case PageItem::Line:
366 ob = processLineItem(item, trans, stroke);
367 if ((item->lineColor() != CommonStrings::None) && ((item->startArrowIndex() != 0) || (item->endArrowIndex() != 0)))
368 ob = processArrows(item, ob, trans);
369 break;
370 case PageItem::ImageFrame:
371 case PageItem::LatexFrame:
372 ob = processImageItem(item, trans, fill, stroke);
373 break;
374 case PageItem::TextFrame:
375 case PageItem::PathText:
376 ob = processTextItem(item, trans, fill, stroke);
377 break;
378 case PageItem::Symbol:
379 ob = processSymbolItem(item, trans);
380 break;
381 case PageItem::Group:
382 if (item->groupItemList.count() > 0)
383 {
384 ob = m_domDoc.createElement("g");
385 if (!item->AutoName)
386 ob.setAttribute("id", item->itemName());
387 if (item->GrMask > 0)
388 ob.setAttribute("mask", handleMask(item, xOffset, yOffset));
389 else
390 {
391 if (item->fillTransparency() != 0)
392 ob.setAttribute("opacity", FToStr(1.0 - item->fillTransparency()));
393 }
394 QString tr = trans;
395 if (item->imageFlippedH())
396 {
397 tr += QString(" translate(%1, 0.0)").arg(item->width());
398 tr += QString(" scale(-1.0, 1.0)");
399 }
400 if (item->imageFlippedV())
401 {
402 tr += QString(" translate(0.0, %1)").arg(item->height());
403 tr += QString(" scale(1.0, -1.0)");
404 }
405 tr += QString(" scale(%1, %2)").arg(item->width() / item->groupWidth).arg(item->height() / item->groupHeight);
406 ob.setAttribute("transform", tr);
407 ob.setAttribute("style", "fill:none; stroke:none");
408 if (item->groupClipping())
409 {
410 FPointArray clipPath = item->PoLine;
411 QTransform transform;
412 transform.scale(item->width() / item->groupWidth, item->height() / item->groupHeight);
413 transform = transform.inverted();
414 clipPath.map(transform);
415 QDomElement obc = createClipPathElement(&clipPath);
416 if (!obc.isNull())
417 ob.setAttribute("clip-path", "url(#" + obc.attribute("id") + ")");
418 if (item->fillRule)
419 ob.setAttribute("clip-rule", "evenodd");
420 else
421 ob.setAttribute("clip-rule", "nonzero");
422 }
423 for (int em = 0; em < item->groupItemList.count(); ++em)
424 {
425 PageItem* embed = item->groupItemList.at(em);
426 processItemOnPage(embed->gXpos, embed->gYpos, embed, &ob);
427 }
428 }
429 break;
430 case PageItem::Table:
431 ob = m_domDoc.createElement("g");
432 ob.setAttribute("transform", trans + QString("translate(%1, %2)").arg(item->asTable()->gridOffset().x()).arg(item->asTable()->gridOffset().y()));
433 // Paint table fill.
434 if (item->asTable()->fillColor() != CommonStrings::None)
435 {
436 int lastCol = item->asTable()->columns() - 1;
437 int lastRow = item->asTable()->rows() - 1;
438 double x = item->asTable()->columnPosition(0);
439 double y = item->asTable()->rowPosition(0);
440 double width = item->asTable()->columnPosition(lastCol) + item->asTable()->columnWidth(lastCol) - x;
441 double height = item->asTable()->rowPosition(lastRow) + item->asTable()->rowHeight(lastRow) - y;
442 QDomElement cl = m_domDoc.createElement("rect");
443 cl.setAttribute("fill", setColor(item->asTable()->fillColor(), item->asTable()->fillShade()));
444 cl.setAttribute("x", "0");
445 cl.setAttribute("y", "0");
446 cl.setAttribute("width", FToStr(width));
447 cl.setAttribute("height", FToStr(height));
448 ob.appendChild(cl);
449 }
450 // Pass 1: Paint cell fills.
451 for (int row = 0; row < item->asTable()->rows(); ++row)
452 {
453 int colSpan = 0;
454 for (int col = 0; col < item->asTable()->columns(); col += colSpan)
455 {
456 TableCell cell = item->asTable()->cellAt(row, col);
457 if (row == cell.row())
458 {
459 QString colorName = cell.fillColor();
460 if (colorName != CommonStrings::None)
461 {
462 int row = cell.row();
463 int col = cell.column();
464 int lastRow = row + cell.rowSpan() - 1;
465 int lastCol = col + cell.columnSpan() - 1;
466 double x = item->asTable()->columnPosition(col);
467 double y = item->asTable()->rowPosition(row);
468 double width = item->asTable()->columnPosition(lastCol) + item->asTable()->columnWidth(lastCol) - x;
469 double height = item->asTable()->rowPosition(lastRow) + item->asTable()->rowHeight(lastRow) - y;
470 QDomElement cl = m_domDoc.createElement("rect");
471 cl.setAttribute("fill", setColor(colorName, cell.fillShade()));
472 cl.setAttribute("x", FToStr(x));
473 cl.setAttribute("y", FToStr(y));
474 cl.setAttribute("width", FToStr(width));
475 cl.setAttribute("height", FToStr(height));
476 ob.appendChild(cl);
477 }
478 }
479 colSpan = cell.columnSpan();
480 }
481 }
482 // Pass 2: Paint vertical borders.
483 for (int row = 0; row < item->asTable()->rows(); ++row)
484 {
485 int colSpan = 0;
486 for (int col = 0; col < item->asTable()->columns(); col += colSpan)
487 {
488 TableCell cell = item->asTable()->cellAt(row, col);
489 if (row == cell.row())
490 {
491 const int lastRow = cell.row() + cell.rowSpan() - 1;
492 const int lastCol = cell.column() + cell.columnSpan() - 1;
493 const double borderX = item->asTable()->columnPosition(lastCol) + item->asTable()->columnWidth(lastCol);
494 QPointF start(borderX, 0.0);
495 QPointF end(borderX, 0.0);
496 QPointF startOffsetFactors, endOffsetFactors;
497 int startRow, endRow;
498 for (int row = cell.row(); row <= lastRow; row += endRow - startRow + 1)
499 {
500 TableCell rightCell = item->asTable()->cellAt(row, lastCol + 1);
501 startRow = qMax(cell.row(), rightCell.row());
502 endRow = qMin(lastRow, rightCell.isValid() ? rightCell.row() + rightCell.rowSpan() - 1 : lastRow);
503 TableCell topLeftCell = item->asTable()->cellAt(startRow - 1, lastCol);
504 TableCell topRightCell = item->asTable()->cellAt(startRow - 1, lastCol + 1);
505 TableCell bottomRightCell = item->asTable()->cellAt(endRow + 1, lastCol + 1);
506 TableCell bottomLeftCell = item->asTable()->cellAt(endRow + 1, lastCol);
507 TableBorder topLeft, top, topRight, border, bottomLeft, bottom, bottomRight;
508 resolveBordersVertical(topLeftCell, topRightCell, cell, rightCell, bottomLeftCell, bottomRightCell,
509 &topLeft, &top, &topRight, &border, &bottomLeft, &bottom, &bottomRight, item->asTable());
510 if (border.isNull())
511 continue; // Quit early if the border to paint is null.
512 start.setY(item->asTable()->rowPosition(startRow));
513 end.setY((item->asTable()->rowPosition(endRow) + item->asTable()->rowHeight(endRow)));
514 joinVertical(border, topLeft, top, topRight, bottomLeft, bottom, bottomRight, &start, &end, &startOffsetFactors, &endOffsetFactors);
515 paintBorder(border, start, end, startOffsetFactors, endOffsetFactors, ob);
516 }
517 if (col == 0)
518 {
519 const int lastRow = cell.row() + cell.rowSpan() - 1;
520 const int firstCol = cell.column();
521 const double borderX = item->asTable()->columnPosition(firstCol);
522 QPointF start(borderX, 0.0);
523 QPointF end(borderX, 0.0);
524 QPointF startOffsetFactors, endOffsetFactors;
525 int startRow, endRow;
526 for (int row = cell.row(); row <= lastRow; row += endRow - startRow + 1)
527 {
528 TableCell leftCell = item->asTable()->cellAt(row, firstCol - 1);
529 startRow = qMax(cell.row(), leftCell.row());
530 endRow = qMin(lastRow, leftCell.isValid() ? leftCell.row() + leftCell.rowSpan() - 1 : lastRow);
531 TableCell topLeftCell = item->asTable()->cellAt(startRow - 1, firstCol - 1);
532 TableCell topRightCell = item->asTable()->cellAt(startRow - 1, firstCol);
533 TableCell bottomRightCell = item->asTable()->cellAt(lastRow + 1, firstCol);
534 TableCell bottomLeftCell = item->asTable()->cellAt(lastRow + 1, firstCol - 1);
535 TableBorder topLeft, top, topRight, border, bottomLeft, bottom, bottomRight;
536 resolveBordersVertical(topLeftCell, topRightCell, leftCell, cell, bottomLeftCell, bottomRightCell,
537 &topLeft, &top, &topRight, &border, &bottomLeft, &bottom, &bottomRight, item->asTable());
538 if (border.isNull())
539 continue; // Quit early if the border to paint is null.
540 start.setY(item->asTable()->rowPosition(startRow));
541 end.setY((item->asTable()->rowPosition(endRow) + item->asTable()->rowHeight(endRow)));
542 joinVertical(border, topLeft, top, topRight, bottomLeft, bottom, bottomRight, &start, &end, &startOffsetFactors, &endOffsetFactors);
543 paintBorder(border, start, end, startOffsetFactors, endOffsetFactors, ob);
544 }
545 }
546 }
547 colSpan = cell.columnSpan();
548 }
549 }
550 // Pass 3: Paint horizontal borders.
551 for (int row = 0; row < item->asTable()->rows(); ++row)
552 {
553 int colSpan = 0;
554 for (int col = 0; col < item->asTable()->columns(); col += colSpan)
555 {
556 TableCell cell = item->asTable()->cellAt(row, col);
557 if (row == cell.row())
558 {
559 const int lastRow = cell.row() + cell.rowSpan() - 1;
560 const int lastCol = cell.column() + cell.columnSpan() - 1;
561 const double borderY = (item->asTable()->rowPosition(lastRow) + item->asTable()->rowHeight(lastRow));
562 QPointF start(0.0, borderY);
563 QPointF end(0.0, borderY);
564 QPointF startOffsetFactors, endOffsetFactors;
565 int startCol, endCol;
566 for (int col = cell.column(); col <= lastCol; col += endCol - startCol + 1)
567 {
568 TableCell bottomCell = item->asTable()->cellAt(lastRow + 1, col);
569 startCol = qMax(cell.column(), bottomCell.column());
570 endCol = qMin(lastCol, bottomCell.isValid() ? bottomCell.column() + bottomCell.columnSpan() - 1 : lastCol);
571 TableCell topLeftCell = item->asTable()->cellAt(lastRow, startCol - 1);
572 TableCell topRightCell = item->asTable()->cellAt(lastRow, endCol + 1);
573 TableCell bottomRightCell = item->asTable()->cellAt(lastRow + 1, endCol + 1);
574 TableCell bottomLeftCell = item->asTable()->cellAt(lastRow + 1, startCol - 1);
575 TableBorder topLeft, left, bottomLeft, border, topRight, right, bottomRight;
576 resolveBordersHorizontal(topLeftCell, cell, topRightCell, bottomLeftCell, bottomCell,
577 bottomRightCell, &topLeft, &left, &bottomLeft, &border, &topRight, &right, &bottomRight, item->asTable());
578 if (border.isNull())
579 continue; // Quit early if the border is null.
580 start.setX(item->asTable()->columnPosition(startCol));
581 end.setX(item->asTable()->columnPosition(endCol) + item->asTable()->columnWidth(endCol));
582 joinHorizontal(border, topLeft, left, bottomLeft, topRight, right, bottomRight, &start, &end, &startOffsetFactors, &endOffsetFactors);
583 paintBorder(border, start, end, startOffsetFactors, endOffsetFactors, ob);
584 }
585 if (row == 0)
586 {
587 const int firstRow = cell.row();
588 const int lastCol = cell.column() + cell.columnSpan() - 1;
589 const double borderY = item->asTable()->rowPosition(firstRow);
590 QPointF start(0.0, borderY);
591 QPointF end(0.0, borderY);
592 QPointF startOffsetFactors, endOffsetFactors;
593 int startCol, endCol;
594 for (int col = cell.column(); col <= lastCol; col += endCol - startCol + 1)
595 {
596 TableCell topCell = item->asTable()->cellAt(firstRow - 1, col);
597 startCol = qMax(cell.column(), topCell.column());
598 endCol = qMin(lastCol, topCell.isValid() ? topCell.column() + topCell.columnSpan() - 1 : lastCol);
599 TableCell topLeftCell = item->asTable()->cellAt(firstRow - 1, startCol - 1);
600 TableCell topRightCell = item->asTable()->cellAt(firstRow - 1, endCol + 1);
601 TableCell bottomRightCell = item->asTable()->cellAt(firstRow, endCol + 1);
602 TableCell bottomLeftCell = item->asTable()->cellAt(firstRow, startCol - 1);
603 TableBorder topLeft, left, bottomLeft, border, topRight, right, bottomRight;
604 resolveBordersHorizontal(topLeftCell, topCell, topRightCell, bottomLeftCell, cell,
605 bottomRightCell, &topLeft, &left, &bottomLeft, &border, &topRight, &right, &bottomRight, item->asTable());
606 if (border.isNull())
607 continue; // Quit early if the border is null.
608 start.setX(item->asTable()->columnPosition(startCol));
609 end.setX(item->asTable()->columnPosition(endCol) + item->asTable()->columnWidth(endCol));
610 joinHorizontal(border, topLeft, left, bottomLeft, topRight, right, bottomRight, &start, &end, &startOffsetFactors, &endOffsetFactors);
611 paintBorder(border, start, end, startOffsetFactors, endOffsetFactors, ob);
612 }
613 }
614 }
615 colSpan = cell.columnSpan();
616 }
617 }
618 // Pass 4: Paint cell content.
619 for (int row = 0; row < item->asTable()->rows(); ++row)
620 {
621 for (int col = 0; col < item->asTable()->columns(); col ++)
622 {
623 TableCell cell = item->asTable()->cellAt(row, col);
624 if (cell.row() == row && cell.column() == col)
625 {
626 PageItem* textFrame = cell.textFrame();
627 processItemOnPage(cell.contentRect().x(), cell.contentRect().y(), textFrame, &ob);
628 }
629 }
630 }
631 break;
632 default:
633 break;
634 }
635 if (item->GrMask > 0)
636 ob.setAttribute("mask", handleMask(item, xOffset, yOffset));
637 if (!item->AutoName)
638 ob.setAttribute("id", item->itemName());
639 parentElem->appendChild(ob);
640 }
641
paintBorder(const TableBorder & border,const QPointF & start,const QPointF & end,const QPointF & startOffsetFactors,const QPointF & endOffsetFactors,QDomElement & ob)642 void SVGExPlug::paintBorder(const TableBorder& border, const QPointF& start, const QPointF& end, const QPointF& startOffsetFactors, const QPointF& endOffsetFactors, QDomElement &ob)
643 {
644 QPointF lineStart, lineEnd;
645 for (const TableBorderLine& line : border.borderLines())
646 {
647 lineStart.setX(start.x() + line.width() * startOffsetFactors.x());
648 lineStart.setY(start.y() + line.width() * startOffsetFactors.y());
649 lineEnd.setX(end.x() + line.width() * endOffsetFactors.x());
650 lineEnd.setY(end.y() + line.width() * endOffsetFactors.y());
651 QDomElement cl = m_domDoc.createElement("path");
652 cl.setAttribute("d", "M " + FToStr(lineStart.x()) + " " + FToStr(lineStart.y()) + " L " + FToStr(lineEnd.x()) + " " + FToStr(lineEnd.y()));
653 QString stroke = "";
654 if (line.color() != CommonStrings::None)
655 cl.setAttribute("stroke", setColor(line.color(), line.shade()));
656 if (line.width() != 0.0)
657 stroke = "stroke-width:" + FToStr(line.width()) + ";";
658 else
659 stroke = "stroke-width:1px;";
660 stroke += " stroke-linecap:butt;";
661 stroke += " stroke-linejoin:miter;";
662 stroke += " stroke-dasharray:";
663 if (line.style() == Qt::SolidLine)
664 stroke += "none;";
665 else
666 {
667 QString Da = getDashString(line.style(), qMax(line.width(), 1.0));
668 if (Da.isEmpty())
669 stroke += "none;";
670 else
671 stroke += Da.replace(" ", ", ") + ";";
672 }
673 cl.setAttribute("style", stroke);
674 ob.appendChild(cl);
675 }
676 }
677
processDropShadow(PageItem * item)678 QString SVGExPlug::processDropShadow(PageItem *item)
679 {
680 if (!item->hasSoftShadow())
681 return "";
682 QString ID = "Filter" + IToStr(m_filterCount);
683 QDomElement filter = m_domDoc.createElement("filter");
684 filter.setAttribute("id", ID);
685 filter.setAttribute("inkscape:label", "Drop shadow");
686 QDomElement ob = m_domDoc.createElement("feGaussianBlur");
687 ob.setAttribute("id", "feGaussianBlur" + IToStr(m_filterCount));
688 ob.setAttribute("in", "SourceAlpha");
689 ob.setAttribute("stdDeviation", FToStr(item->softShadowBlurRadius()));
690 ob.setAttribute("result", "blur");
691 filter.appendChild(ob);
692 QDomElement ob2 = m_domDoc.createElement("feColorMatrix");
693 ob2.setAttribute("id", "feColorMatrix" + IToStr(m_filterCount));
694 const ScColor& col = m_Doc->PageColors[item->softShadowColor()];
695 QColor color = ScColorEngine::getShadeColorProof(col, m_Doc, item->softShadowShade());
696 ob2.setAttribute("type", "matrix");
697 ob2.setAttribute("values", QString("1 0 0 %1 0 0 1 0 %2 0 0 0 1 %3 0 0 0 0 %4 0").arg(color.redF()).arg(color.greenF()).arg(color.blueF()).arg(1.0 - item->softShadowOpacity()));
698 ob2.setAttribute("result", "bluralpha");
699 filter.appendChild(ob2);
700 QDomElement ob3 = m_domDoc.createElement("feOffset");
701 ob3.setAttribute("id", "feOffset" + IToStr(m_filterCount));
702 ob3.setAttribute("in", "bluralpha");
703 ob3.setAttribute("dx", FToStr(item->softShadowXOffset()));
704 ob3.setAttribute("dy", FToStr(item->softShadowYOffset()));
705 ob3.setAttribute("result", "offsetBlur");
706 filter.appendChild(ob3);
707 QDomElement ob4 = m_domDoc.createElement("feMerge");
708 ob4.setAttribute("id", "feMerge" + IToStr(m_filterCount));
709 QDomElement ob5 = m_domDoc.createElement("feMergeNode");
710 ob5.setAttribute("id", "feMergeNode1" + IToStr(m_filterCount));
711 ob5.setAttribute("in", "offsetBlur");
712 ob4.appendChild(ob5);
713 QDomElement ob6 = m_domDoc.createElement("feMergeNode");
714 ob6.setAttribute("id", "feMergeNode2" + IToStr(m_filterCount));
715 ob6.setAttribute("in", "SourceGraphic");
716 ob4.appendChild(ob6);
717 filter.appendChild(ob4);
718 m_globalDefs.appendChild(filter);
719 m_filterCount++;
720 return "filter:url(#" + ID + ");";
721 }
722
processHatchFill(PageItem * item,const QString & transl)723 QDomElement SVGExPlug::processHatchFill(PageItem *item, const QString& transl)
724 {
725 QDomElement ob;
726 ob = m_domDoc.createElement("g");
727 if (!transl.isEmpty())
728 ob.setAttribute("transform", transl);
729 QDomElement obc = createClipPathElement(&item->PoLine);
730 if (!obc.isNull())
731 ob.setAttribute("clip-path", "url(#" + obc.attribute("id") + ")");
732 if (item->fillRule)
733 ob.setAttribute("clip-rule", "evenodd");
734 else
735 ob.setAttribute("clip-rule", "nonzero");
736 if (item->hatchUseBackground)
737 {
738 QDomElement ob2 = m_domDoc.createElement("path");
739 ob2.setAttribute("d", setClipPath(&item->PoLine, true));
740 ob2.setAttribute("fill", setColor(item->hatchBackground, 100));
741 ob.appendChild(ob2);
742 }
743 QString stroke = "";
744 stroke += "stroke-width:1;";
745 stroke += " stroke-linecap:butt;";
746 stroke += " stroke-linejoin:miter;";
747 double lineLen = sqrt((item->width() / 2.0) * (item->width() / 2.0) + (item->height() / 2.0) * (item->height() / 2.0));
748 double dist = 0.0;
749 while (dist < lineLen)
750 {
751 QTransform mpx;
752 mpx.translate(item->width() / 2.0, item->height() / 2.0);
753 if (item->hatchAngle != 0.0)
754 mpx.rotate(-item->hatchAngle);
755 QDomElement ob3 = m_domDoc.createElement("path");
756 ob3.setAttribute("transform", matrixToStr(mpx));
757 ob3.setAttribute("d", QString("M %1, %2 L %3, %4").arg(-lineLen).arg(dist).arg(lineLen).arg(dist));
758 ob3.setAttribute("stroke", setColor(item->hatchForeground, 100));
759 ob3.setAttribute("style", stroke);
760 ob.appendChild(ob3);
761 if (dist > 0)
762 {
763 QDomElement ob4 = m_domDoc.createElement("path");
764 ob4.setAttribute("transform", matrixToStr(mpx));
765 ob4.setAttribute("d", QString("M %1, %2 L %3, %4").arg(-lineLen).arg(-dist).arg(lineLen).arg(-dist));
766 ob4.setAttribute("stroke", setColor(item->hatchForeground, 100));
767 ob4.setAttribute("style", stroke);
768 ob.appendChild(ob4);
769 }
770 dist += item->hatchDistance;
771 }
772 if ((item->hatchType == 1) || (item->hatchType == 2))
773 {
774 dist = 0.0;
775 while (dist < lineLen)
776 {
777 QTransform mpx;
778 mpx.translate(item->width() / 2.0, item->height() / 2.0);
779 if (item->hatchAngle != 0.0)
780 mpx.rotate(-item->hatchAngle + 90);
781 QDomElement ob3 = m_domDoc.createElement("path");
782 ob3.setAttribute("transform", matrixToStr(mpx));
783 ob3.setAttribute("d", QString("M %1, %2 L %3, %4").arg(-lineLen).arg(dist).arg(lineLen).arg(dist));
784 ob3.setAttribute("stroke", setColor(item->hatchForeground, 100));
785 ob3.setAttribute("style", stroke);
786 ob.appendChild(ob3);
787 if (dist > 0)
788 {
789 QDomElement ob4 = m_domDoc.createElement("path");
790 ob4.setAttribute("transform", matrixToStr(mpx));
791 ob4.setAttribute("d", QString("M %1, %2 L %3, %4").arg(-lineLen).arg(-dist).arg(lineLen).arg(-dist));
792 ob4.setAttribute("stroke", setColor(item->hatchForeground, 100));
793 ob4.setAttribute("style", stroke);
794 ob.appendChild(ob4);
795 }
796 dist += item->hatchDistance;
797 }
798 }
799 if (item->hatchType == 2)
800 {
801 dist = 0.0;
802 while (dist < lineLen)
803 {
804 double dDist = dist * sqrt(2.0);
805 QTransform mpx;
806 mpx.translate(item->width() / 2.0, item->height() / 2.0);
807 if (item->hatchAngle != 0.0)
808 mpx.rotate(-item->hatchAngle + 45);
809 QDomElement ob3 = m_domDoc.createElement("path");
810 ob3.setAttribute("transform", matrixToStr(mpx));
811 ob3.setAttribute("d", QString("M %1, %2 L %3, %4").arg(-lineLen).arg(dDist).arg(lineLen).arg(dDist));
812 ob3.setAttribute("stroke", setColor(item->hatchForeground, 100));
813 ob3.setAttribute("style", stroke);
814 ob.appendChild(ob3);
815 if (dist > 0)
816 {
817 QDomElement ob4 = m_domDoc.createElement("path");
818 ob4.setAttribute("transform", matrixToStr(mpx));
819 ob4.setAttribute("d", QString("M %1, %2 L %3, %4").arg(-lineLen).arg(-dDist).arg(lineLen).arg(-dDist));
820 ob4.setAttribute("stroke", setColor(item->hatchForeground, 100));
821 ob4.setAttribute("style", stroke);
822 ob.appendChild(ob4);
823 }
824 dist += item->hatchDistance;
825 }
826 }
827 return ob;
828 }
829
processSymbolStroke(PageItem * item,const QString & trans)830 QDomElement SVGExPlug::processSymbolStroke(PageItem *item, const QString& trans)
831 {
832 QDomElement ob;
833 ob = m_domDoc.createElement("g");
834 ob.setAttribute("transform", trans);
835 QPainterPath path = item->PoLine.toQPainterPath(false);
836 ScPattern pat = m_Doc->docPatterns[item->strokePattern()];
837 double pLen = path.length() - ((pat.width / 2.0) * (item->patternStrokeScaleX / 100.0));
838 double adv = pat.width * item->patternStrokeScaleX / 100.0 * item->patternStrokeSpace;
839 double xpos = item->patternStrokeOffsetX * item->patternStrokeScaleX / 100.0;
840 while (xpos < pLen)
841 {
842 double currPerc = path.percentAtLength(xpos);
843 double currAngle = path.angleAtPercent(currPerc);
844 if (currAngle <= 180.0)
845 currAngle *= -1.0;
846 else
847 currAngle = 360.0 - currAngle;
848 QPointF currPoint = path.pointAtPercent(currPerc);
849 QTransform trans;
850 trans.translate(currPoint.x(), currPoint.y());
851 trans.rotate(-currAngle);
852 trans.translate(0.0, item->patternStrokeOffsetY);
853 trans.rotate(-item->patternStrokeRotation);
854 trans.shear(item->patternStrokeSkewX, -item->patternStrokeSkewY);
855 trans.scale(item->patternStrokeScaleX / 100.0, item->patternStrokeScaleY / 100.0);
856 trans.translate(-pat.width / 2.0, -pat.height / 2.0);
857 QDomElement obS;
858 obS = m_domDoc.createElement("use");
859 obS.setAttribute("transform", matrixToStr(trans));
860 if (item->patternStrokeMirrorX)
861 {
862 trans.translate(pat.width, 0);
863 trans.scale(-1, 1);
864 }
865 if (item->patternStrokeMirrorY)
866 {
867 trans.translate(0, pat.height);
868 trans.scale(1, -1);
869 }
870 obS.setAttribute("x", "0");
871 obS.setAttribute("y", "0");
872 obS.setAttribute("width", FToStr(pat.width));
873 obS.setAttribute("height", FToStr(pat.height));
874 obS.setAttribute("xlink:href", "#S" + item->strokePattern());
875 ob.appendChild(obS);
876 xpos += adv;
877 }
878 return ob;
879 }
880
processSymbolItem(PageItem * item,const QString & trans)881 QDomElement SVGExPlug::processSymbolItem(PageItem *item, const QString& trans)
882 {
883 QDomElement ob;
884 ScPattern pat = m_Doc->docPatterns[item->pattern()];
885 ob = m_domDoc.createElement("use");
886 ob.setAttribute("x", "0");
887 ob.setAttribute("y", "0");
888 ob.setAttribute("width", FToStr(pat.width));
889 ob.setAttribute("height", FToStr(pat.height));
890 ob.setAttribute("xlink:href", "#S" + item->pattern());
891 QString tr = trans + QString(" scale(%1, %2)").arg(item->width() / pat.width).arg(item->height() / pat.height);
892 ob.setAttribute("transform", tr);
893 return ob;
894 }
895
processPolyItem(PageItem * item,const QString & trans,const QString & fill,const QString & stroke)896 QDomElement SVGExPlug::processPolyItem(PageItem *item, const QString& trans, const QString& fill, const QString& stroke)
897 {
898 bool closedPath;
899 QDomElement ob;
900 closedPath = (item->itemType() == PageItem::Polygon) || (item->itemType() == PageItem::RegularPolygon) || (item->itemType() == PageItem::Arc);
901 if (item->NamedLStyle.isEmpty())
902 {
903 if ((!item->strokePattern().isEmpty()) && (item->patternStrokePath))
904 {
905 ob = m_domDoc.createElement("g");
906 if (item->GrType == Gradient_Hatch)
907 {
908 QDomElement ob1 = processHatchFill(item, trans);
909 ob.appendChild(ob1);
910 }
911 QDomElement ob2 = m_domDoc.createElement("path");
912 ob2.setAttribute("d", setClipPath(&item->PoLine, closedPath));
913 ob2.setAttribute("transform", trans);
914 if (item->GrType != Gradient_Hatch)
915 ob2.setAttribute("style", fill);
916 else
917 {
918 QString drS = processDropShadow(item);
919 if (!drS.isEmpty())
920 ob2.setAttribute("style", "fill:none;" + drS);
921 }
922 ob.appendChild(ob2);
923 ob.appendChild(processSymbolStroke(item, trans));
924 }
925 else
926 {
927 if (item->GrType == Gradient_Hatch)
928 {
929 ob = m_domDoc.createElement("g");
930 ob.setAttribute("transform", trans);
931 QDomElement ob1 = processHatchFill(item);
932 ob.appendChild(ob1);
933 QDomElement ob2 = m_domDoc.createElement("path");
934 ob2.setAttribute("d", setClipPath(&item->PoLine, closedPath));
935 ob2.setAttribute("style", stroke + "fill:none;" + processDropShadow(item));
936 ob.appendChild(ob2);
937 }
938 else
939 {
940 ob = m_domDoc.createElement("path");
941 ob.setAttribute("d", setClipPath(&item->PoLine, closedPath));
942 ob.setAttribute("transform", trans);
943 ob.setAttribute("style", fill + stroke);
944 }
945 }
946 }
947 else
948 {
949 ob = m_domDoc.createElement("g");
950 ob.setAttribute("transform", trans);
951 if (item->GrType == Gradient_Hatch)
952 {
953 QDomElement ob1 = processHatchFill(item);
954 ob.appendChild(ob1);
955 }
956 QDomElement ob2 = m_domDoc.createElement("path");
957 ob2.setAttribute("d", setClipPath(&item->PoLine, closedPath));
958 if (item->GrType != Gradient_Hatch)
959 ob2.setAttribute("style", fill);
960 else
961 {
962 QString drS = processDropShadow(item);
963 if (!drS.isEmpty())
964 ob2.setAttribute("style", "fill:none;" + drS);
965 }
966 ob.appendChild(ob2);
967 multiLine ml = m_Doc->docLineStyles[item->NamedLStyle];
968 for (int it = ml.size()-1; it > -1; it--)
969 {
970 if ((ml[it].Color != CommonStrings::None) && (ml[it].Width != 0))
971 {
972 QDomElement ob3 = m_domDoc.createElement("path");
973 ob3.setAttribute("d", setClipPath(&item->PoLine, closedPath));
974 ob3.setAttribute("style", getMultiStroke(&ml[it], item));
975 ob.appendChild(ob3);
976 }
977 }
978 }
979 return ob;
980 }
981
processLineItem(PageItem * item,const QString & trans,const QString & stroke)982 QDomElement SVGExPlug::processLineItem(PageItem *item, const QString& trans, const QString& stroke)
983 {
984 QDomElement ob;
985 if (item->NamedLStyle.isEmpty())
986 {
987 ob = m_domDoc.createElement("path");
988 ob.setAttribute("d", "M 0 0 L " + FToStr(item->width()) + " 0");
989 ob.setAttribute("transform", trans);
990 ob.setAttribute("style", stroke);
991 }
992 else
993 {
994 ob = m_domDoc.createElement("g");
995 ob.setAttribute("transform", trans);
996 multiLine ml = m_Doc->docLineStyles[item->NamedLStyle];
997 for (int i = ml.size()-1; i > -1; i--)
998 {
999 if ((ml[i].Color != CommonStrings::None) && (ml[i].Width != 0))
1000 {
1001 QDomElement ob2 = m_domDoc.createElement("path");
1002 ob2.setAttribute("d", "M 0 0 L " + FToStr(item->width()) + " 0");
1003 ob2.setAttribute("style", getMultiStroke(&ml[i], item));
1004 ob.appendChild(ob2);
1005 }
1006 }
1007 }
1008 return ob;
1009 }
1010
processImageItem(PageItem * item,const QString & trans,const QString & fill,const QString & stroke)1011 QDomElement SVGExPlug::processImageItem(PageItem *item, const QString& trans, const QString& fill, const QString& stroke)
1012 {
1013 QDomElement ob;
1014 ob = m_domDoc.createElement("g");
1015 ob.setAttribute("transform", trans);
1016 if ((item->fillColor() != CommonStrings::None) || (item->GrType != 0))
1017 {
1018 if (item->GrType == Gradient_Hatch)
1019 {
1020 QDomElement ob1 = processHatchFill(item);
1021 ob.appendChild(ob1);
1022 QString drS = processDropShadow(item);
1023 if (!drS.isEmpty())
1024 ob.setAttribute("style", "fill:none;" + drS);
1025 }
1026 else
1027 {
1028 QDomElement ob1 = m_domDoc.createElement("path");
1029 ob1.setAttribute("d", setClipPath(&item->PoLine, true));
1030 ob1.setAttribute("style", fill);
1031 ob.appendChild(ob1);
1032 }
1033 }
1034 if ((item->imageIsAvailable) && (!item->Pfile.isEmpty()))
1035 {
1036 QDomElement cl, ob2;
1037 if (!item->imageClip.empty())
1038 ob2 = createClipPathElement(&item->imageClip, &cl);
1039 else
1040 ob2 = createClipPathElement(&item->PoLine, &cl);
1041 if (!ob2.isNull())
1042 {
1043 ob2.setAttribute("clipPathUnits", "userSpaceOnUse");
1044 ob2.setAttribute("clip-rule", "evenodd");
1045 QTransform mpc;
1046 if (item->imageFlippedH())
1047 {
1048 mpc.translate(item->width(), 0);
1049 mpc.scale(-1, 1);
1050 }
1051 if (item->imageFlippedV())
1052 {
1053 mpc.translate(0, item->height());
1054 mpc.scale(1, -1);
1055 }
1056 cl.setAttribute("transform", matrixToStr(mpc));
1057 }
1058 QDomElement ob6 = m_domDoc.createElement("g");
1059 if (!ob2.isNull())
1060 ob6.setAttribute("clip-path", "url(#" + ob2.attribute("id") + ")");
1061 QDomElement ob3 = m_domDoc.createElement("image");
1062 ScImage img;
1063 CMSettings cms(m_Doc, item->ImageProfile, item->ImageIntent);
1064 cms.setUseEmbeddedProfile(item->UseEmbedded);
1065 cms.allowSoftProofing(true);
1066 img.loadPicture(item->Pfile, item->pixm.imgInfo.actualPageNumber, cms, ScImage::RGBData, 72);
1067 img.applyEffect(item->effectsInUse, m_Doc->PageColors, true);
1068 if (Options.inlineImages)
1069 {
1070 QBuffer buffer;
1071 buffer.open(QIODevice::WriteOnly);
1072 img.qImage().save(&buffer, "PNG");
1073 QByteArray ba = buffer.buffer().toBase64();
1074 buffer.close();
1075 ob3.setAttribute("xlink:href", "data:image/png;base64," + QString(ba));
1076 }
1077 else
1078 {
1079 QFileInfo fi = QFileInfo(item->Pfile);
1080 QString imgFileName = m_baseDir + "/" + fi.baseName() + ".png";
1081 QFileInfo im = QFileInfo(imgFileName);
1082 if (im.exists())
1083 imgFileName = m_baseDir + "/" + fi.baseName() + "_copy.png";
1084 img.qImage().save(imgFileName, "PNG");
1085 QFileInfo fi2 = QFileInfo(imgFileName);
1086 ob3.setAttribute("xlink:href", fi2.baseName() + ".png");
1087 }
1088 ob3.setAttribute("x", FToStr(item->imageXOffset() * item->imageXScale()));
1089 ob3.setAttribute("y", FToStr(item->imageYOffset() * item->imageYScale()));
1090 ob3.setAttribute("width", FToStr(img.width() * item->imageXScale()));
1091 ob3.setAttribute("height", FToStr(img.height() * item->imageYScale()));
1092 QTransform mpa;
1093 if (item->imageFlippedH())
1094 {
1095 mpa.translate(item->width(), 0);
1096 mpa.scale(-1, 1);
1097 }
1098 if (item->imageFlippedV())
1099 {
1100 mpa.translate(0, item->height());
1101 mpa.scale(1, -1);
1102 }
1103 mpa.rotate(item->imageRotation());
1104 ob3.setAttribute("transform", matrixToStr(mpa));
1105 ob6.appendChild(ob3);
1106 ob.appendChild(ob6);
1107 }
1108 if (item->NamedLStyle.isEmpty())
1109 {
1110 if ((!item->strokePattern().isEmpty()) && (item->patternStrokePath))
1111 {
1112 QDomElement ob4 = m_domDoc.createElement("g");
1113 QDomElement ob2 = m_domDoc.createElement("path");
1114 ob2.setAttribute("d", setClipPath(&item->PoLine, true));
1115 ob2.setAttribute("transform", trans);
1116 ob2.setAttribute("style", fill);
1117 ob4.appendChild(ob2);
1118 ob4.appendChild(processSymbolStroke(item, trans));
1119 ob.appendChild(ob4);
1120 }
1121 else
1122 {
1123 QDomElement ob4 = m_domDoc.createElement("path");
1124 ob4.setAttribute("d", setClipPath(&item->PoLine, true));
1125 ob4.setAttribute("style", "fill:none; " + stroke);
1126 ob.appendChild(ob4);
1127 }
1128 }
1129 else
1130 {
1131 multiLine ml = m_Doc->docLineStyles[item->NamedLStyle];
1132 for (int it = ml.size()-1; it > -1; it--)
1133 {
1134 if ((ml[it].Color != CommonStrings::None) && (ml[it].Width != 0))
1135 {
1136 QDomElement ob5 = m_domDoc.createElement("path");
1137 ob5.setAttribute("d", setClipPath(&item->PoLine, true));
1138 ob5.setAttribute("style", "fill:none; " + getMultiStroke(&ml[it], item));
1139 ob.appendChild(ob5);
1140 }
1141 }
1142 }
1143 return ob;
1144 }
1145
1146 class SvgPainter: public TextLayoutPainter
1147 {
1148 QDomElement m_elem;
1149 SVGExPlug *m_svg;
1150 QString m_trans;
1151
1152 public:
SvgPainter(const QString & trans,SVGExPlug * svg,QDomElement & elem)1153 SvgPainter(const QString& trans, SVGExPlug *svg, QDomElement &elem)
1154 : m_elem(elem)
1155 , m_svg(svg)
1156 , m_trans(trans)
1157 {}
1158
drawGlyph(const GlyphCluster & gc)1159 void drawGlyph(const GlyphCluster& gc) override
1160 {
1161 if (gc.isControlGlyphs() || gc.isEmpty())
1162 return;
1163 double current_x = 0.0;
1164 for (const GlyphLayout& gl : gc.glyphs())
1165 {
1166 if (gl.glyph >= ScFace::CONTROL_GLYPHS)
1167 {
1168 current_x += gl.xadvance * gl.scaleH;
1169 continue;
1170 }
1171
1172 QTransform transform = matrix();
1173 transform.translate(x() + gl.xoffset + current_x, y() - (fontSize() * gc.scaleV()) + gl.yoffset);
1174 transform.scale(gc.scaleH() * fontSize() / 10.0, gc.scaleV() * fontSize() / 10.0);
1175 QDomElement glyph = m_svg->m_domDoc.createElement("use");
1176 glyph.setAttribute("xlink:href", "#" + m_svg->handleGlyph(gl.glyph, font()));
1177 glyph.setAttribute("transform", m_svg->matrixToStr(transform));
1178 QString fill = "fill:" + m_svg->setColor(fillColor().color, fillColor().shade) + ";";
1179 QString stroke = "stroke:none;";
1180 glyph.setAttribute("style", fill + stroke);
1181 m_elem.appendChild(glyph);
1182
1183 current_x += gl.xadvance * gl.scaleH;
1184 }
1185 }
1186
drawGlyphOutline(const GlyphCluster & gc,bool hasFill)1187 void drawGlyphOutline(const GlyphCluster& gc, bool hasFill) override
1188 {
1189 if (gc.isControlGlyphs() | gc.isEmpty())
1190 return;
1191
1192 double current_x = 0.0;
1193 for (const GlyphLayout& gl : gc.glyphs())
1194 {
1195 if (gl.glyph >= ScFace::CONTROL_GLYPHS)
1196 {
1197 current_x += gl.xadvance * gl.scaleH;
1198 continue;
1199 }
1200
1201 QTransform transform = matrix();
1202 transform.translate(x() + gl.xoffset + current_x, y() - (fontSize() * gc.scaleV()) + gl.yoffset);
1203 transform.scale(gc.scaleH() * fontSize() / 10.0, gc.scaleV() * fontSize() / 10.0);
1204 QDomElement glyph = m_svg->m_domDoc.createElement("use");
1205 glyph.setAttribute("xlink:href", "#" + m_svg->handleGlyph(gl.glyph, font()));
1206 glyph.setAttribute("transform", m_svg->matrixToStr(transform));
1207 QString fill = "fill:none;";
1208 if (hasFill)
1209 fill = "fill:" + m_svg->setColor(fillColor().color, fillColor().shade) + ";";
1210 QString stroke ="stroke:" + m_svg->setColor(strokeColor().color, strokeColor().shade) + ";";
1211 stroke += " stroke-width:" + m_svg->FToStr(strokeWidth() / (gc.scaleV() * fontSize() / 10.0)) + ";";
1212 glyph.setAttribute("style", fill + stroke);
1213 m_elem.appendChild(glyph);
1214
1215 current_x += gl.xadvance * gl.scaleH;
1216 }
1217 }
1218
drawLine(QPointF start,QPointF end)1219 void drawLine(QPointF start, QPointF end) override
1220 {
1221 QTransform transform = matrix();
1222 transform.translate(x(), y());
1223 QDomElement path = m_svg-> m_domDoc.createElement("path");
1224 path.setAttribute("d", QString("M %1 %2 L%3 %4").arg(start.x()).arg(start.y()).arg(end.x()).arg(end.y()));
1225 QString stroke = "stroke:none;";
1226 if (fillColor().color != CommonStrings::None)
1227 {
1228 stroke = "stroke:" + m_svg->setColor(fillColor().color, fillColor().shade) + ";";
1229 stroke += " stroke-width:" + m_svg->FToStr(strokeWidth()) + ";";
1230 }
1231 path.setAttribute("style", "fill:none;" + stroke);
1232 path.setAttribute("transform", m_svg->matrixToStr(transform));
1233 m_elem.appendChild(path);
1234 }
1235
drawRect(QRectF rect)1236 void drawRect(QRectF rect) override
1237 {
1238 QTransform transform = matrix();
1239 transform.translate(x(), y());
1240 QString paS = QString("M %1 %2 ").arg(rect.x()).arg(rect.y());
1241 paS += QString("L %1 %2 ").arg(rect.x() + rect.width()).arg(rect.y());
1242 paS += QString("L %1 %2 ").arg(rect.x() + rect.width()).arg(rect.y() + rect.height());
1243 paS += QString("L %1 %2 ").arg(rect.x()).arg(rect.y() + rect.height());
1244 paS += "Z";
1245 QDomElement path = m_svg->m_domDoc.createElement("path");
1246 path.setAttribute("d", paS);
1247 path.setAttribute("transform", m_svg->matrixToStr(transform));
1248 path.setAttribute("style", "fill:" + m_svg->setColor(fillColor().color, fillColor().shade) + ";" + "stroke:none;");
1249 m_elem.appendChild(path);
1250 }
1251
drawObject(PageItem * item)1252 void drawObject(PageItem* item) override
1253 {
1254 QTransform transform = matrix();
1255 transform.translate(x() + item->gXpos, y() + item->gYpos);
1256 transform.rotate(item->rotation());
1257 transform.scale(scaleH(), scaleV());
1258 QDomElement Group = m_svg->m_domDoc.createElement("g");
1259 QDomElement layerGroup = m_svg->processInlineItem(item, m_trans, scaleH(), scaleV());
1260 Group.appendChild(layerGroup);
1261 Group.setAttribute("transform", m_svg->matrixToStr(transform));
1262 m_elem.appendChild(Group);
1263 }
1264 };
1265
processTextItem(PageItem * item,const QString & trans,const QString & fill,const QString & stroke)1266 QDomElement SVGExPlug::processTextItem(PageItem *item, const QString& trans, const QString& fill, const QString& stroke)
1267 {
1268 QDomElement ob;
1269 ob = m_domDoc.createElement("g");
1270 ob.setAttribute("transform", trans);
1271 if ((item->fillColor() != CommonStrings::None) || (item->GrType != 0))
1272 {
1273 if (item->GrType == Gradient_Hatch)
1274 {
1275 QDomElement ob1 = processHatchFill(item);
1276 ob.appendChild(ob1);
1277 QString drS = processDropShadow(item);
1278 if (!drS.isEmpty())
1279 ob.setAttribute("style", "fill:none;" + drS);
1280 }
1281 else
1282 {
1283 QDomElement ob1 = m_domDoc.createElement("path");
1284 ob1.setAttribute("d", setClipPath(&item->PoLine, true));
1285 ob1.setAttribute("style", fill);
1286 ob.appendChild(ob1);
1287 }
1288 }
1289
1290 if (item->itemText.length() != 0)
1291 {
1292 SvgPainter p(trans, this, ob);
1293 item->textLayout.renderBackground(&p);
1294 item->textLayout.render(&p);
1295 }
1296 if (item->isTextFrame())
1297 {
1298 if (item->NamedLStyle.isEmpty())
1299 {
1300 if ((!item->strokePattern().isEmpty()) && (item->patternStrokePath))
1301 {
1302 QDomElement ob4 = m_domDoc.createElement("g");
1303 QDomElement ob2 = m_domDoc.createElement("path");
1304 ob2.setAttribute("d", setClipPath(&item->PoLine, true));
1305 ob2.setAttribute("transform", trans);
1306 ob2.setAttribute("style", fill);
1307 ob4.appendChild(ob2);
1308 ob4.appendChild(processSymbolStroke(item, trans));
1309 ob.appendChild(ob4);
1310 }
1311 else
1312 {
1313 QDomElement ob4 = m_domDoc.createElement("path");
1314 ob4.setAttribute("d", setClipPath(&item->PoLine, true));
1315 ob4.setAttribute("style", "fill:none; " + stroke);
1316 ob.appendChild(ob4);
1317 }
1318 }
1319 else
1320 {
1321 multiLine ml = m_Doc->docLineStyles[item->NamedLStyle];
1322 for (int it = ml.size()-1; it > -1; it--)
1323 {
1324 if ((ml[it].Color != CommonStrings::None) && (ml[it].Width != 0))
1325 {
1326 QDomElement ob5 = m_domDoc.createElement("path");
1327 ob5.setAttribute("d", setClipPath(&item->PoLine, true));
1328 ob5.setAttribute("style", "fill:none; " + getMultiStroke(&ml[it], item));
1329 ob.appendChild(ob5);
1330 }
1331 }
1332 }
1333 }
1334 else if (item->isPathText() && item->PoShow)
1335 {
1336 if (item->NamedLStyle.isEmpty())
1337 {
1338 if ((!item->strokePattern().isEmpty()) && (item->patternStrokePath))
1339 {
1340 QDomElement ob4 = m_domDoc.createElement("g");
1341 QDomElement ob2 = m_domDoc.createElement("path");
1342 ob2.setAttribute("d", setClipPath(&item->PoLine, false));
1343 ob2.setAttribute("transform", trans);
1344 ob2.setAttribute("style", fill);
1345 ob4.appendChild(ob2);
1346 ob4.appendChild(processSymbolStroke(item, trans));
1347 ob.appendChild(ob4);
1348 }
1349 else
1350 {
1351 QDomElement ob4 = m_domDoc.createElement("path");
1352 ob4.setAttribute("d", setClipPath(&item->PoLine, false));
1353 ob4.setAttribute("style", "fill:none; " + stroke);
1354 ob.appendChild(ob4);
1355 }
1356 }
1357 else
1358 {
1359 multiLine ml = m_Doc->docLineStyles[item->NamedLStyle];
1360 for (int it = ml.size()-1; it > -1; it--)
1361 {
1362 if ((ml[it].Color != CommonStrings::None) && (ml[it].Width != 0))
1363 {
1364 QDomElement ob5 = m_domDoc.createElement("path");
1365 ob5.setAttribute("d", setClipPath(&item->PoLine, false));
1366 ob5.setAttribute("style", "fill:none; " + getMultiStroke(&ml[it], item));
1367 ob.appendChild(ob5);
1368 }
1369 }
1370 }
1371 }
1372 return ob;
1373 }
1374
processInlineItem(PageItem * embItem,const QString & trans,double scaleH,double scaleV)1375 QDomElement SVGExPlug::processInlineItem(PageItem* embItem, const QString& trans, double scaleH, double scaleV)
1376 {
1377 QList<PageItem*> emG;
1378 if (embItem->isGroup())
1379 emG = embItem->groupItemList;
1380 else
1381 emG.append(embItem);
1382
1383 QDomElement layerGroup = m_domDoc.createElement("g");
1384 for (int em = 0; em < emG.count(); ++em)
1385 {
1386 PageItem* embedded = emG.at(em);
1387 QDomElement obE;
1388 QString fill = getFillStyle(embedded);
1389 QString stroke = "stroke:none";
1390 stroke = getStrokeStyle(embedded);
1391 QString transE = "";
1392 if (embItem->isGroup())
1393 {
1394 transE = "translate(" + FToStr(embedded->gXpos) + ", " + FToStr(embedded->gYpos) + ")";
1395 if (embedded->rotation() != 0)
1396 transE += " rotate(" + FToStr(embedded->rotation()) + ")";
1397 }
1398 switch (embedded->itemType())
1399 {
1400 case PageItem::Arc:
1401 case PageItem::Polygon:
1402 case PageItem::PolyLine:
1403 case PageItem::RegularPolygon:
1404 case PageItem::Spiral:
1405 obE = processPolyItem(embedded, transE, fill, stroke);
1406 if ((embedded->lineColor() != CommonStrings::None) && ((embedded->startArrowIndex() != 0) || (embedded->endArrowIndex() != 0)))
1407 obE = processArrows(embedded, obE, transE);
1408 break;
1409 case PageItem::Line:
1410 obE = processLineItem(embedded, transE, stroke);
1411 if ((embedded->lineColor() != CommonStrings::None) && ((embedded->startArrowIndex() != 0) || (embedded->endArrowIndex() != 0)))
1412 obE = processArrows(embedded, obE, transE);
1413 break;
1414 case PageItem::ImageFrame:
1415 case PageItem::LatexFrame:
1416 obE = processImageItem(embedded, transE, fill, stroke);
1417 break;
1418 case PageItem::TextFrame:
1419 case PageItem::PathText:
1420 obE = processTextItem(embedded, transE, fill, stroke);
1421 break;
1422 case PageItem::Symbol:
1423 obE = processSymbolItem(embedded, transE);
1424 break;
1425 case PageItem::Group:
1426 if (embedded->groupItemList.count() > 0)
1427 {
1428 obE = m_domDoc.createElement("g");
1429 if (!embedded->AutoName)
1430 obE.setAttribute("id", embedded->itemName());
1431 if (embedded->GrMask > 0)
1432 obE.setAttribute("mask", handleMask(embedded, embedded->xPos() - m_Doc->currentPage()->xOffset(), embedded->yPos() - m_Doc->currentPage()->yOffset()));
1433 else
1434 {
1435 if (embedded->fillTransparency() != 0)
1436 obE.setAttribute("opacity", FToStr(1.0 - embedded->fillTransparency()));
1437 }
1438 QString tr = trans;
1439 if (embedded->imageFlippedH())
1440 {
1441 tr += QString(" translate(%1, 0.0)").arg(embedded->width());
1442 tr += QString(" scale(-1.0, 1.0)");
1443 }
1444 if (embedded->imageFlippedV())
1445 {
1446 tr += QString(" translate(0.0, %1)").arg(embedded->height());
1447 tr += QString(" scale(1.0, -1.0)");
1448 }
1449 tr += QString(" scale(%1, %2)").arg(embedded->width() / embedded->groupWidth).arg(embedded->height() / embedded->groupHeight);
1450 obE.setAttribute("transform", tr);
1451 obE.setAttribute("style", "fill:none; stroke:none");
1452 if (embedded->groupClipping())
1453 {
1454 FPointArray clipPath = embedded->PoLine;
1455 QTransform transform;
1456 transform.scale(embedded->width() / embedded->groupWidth, embedded->height() / embedded->groupHeight);
1457 transform = transform.inverted();
1458 clipPath.map(transform);
1459 QDomElement obc = createClipPathElement(&clipPath);
1460 if (!obc.isNull())
1461 obE.setAttribute("clip-path", "url(#" + obc.attribute("id") + ")");
1462 if (embedded->fillRule)
1463 obE.setAttribute("clip-rule", "evenodd");
1464 else
1465 obE.setAttribute("clip-rule", "nonzero");
1466 }
1467 for (int em = 0; em < embedded->groupItemList.count(); ++em)
1468 {
1469 PageItem* embed = embedded->groupItemList.at(em);
1470 processItemOnPage(embed->gXpos, embed->gYpos, embed, &obE);
1471 }
1472 }
1473 break;
1474 default:
1475 break;
1476 }
1477 layerGroup.appendChild(obE);
1478 }
1479 QTransform mm;
1480 if (embItem->isGroup())
1481 mm.scale(embItem->width() / embItem->groupWidth, embItem->height() / embItem->groupHeight);
1482 layerGroup.setAttribute("transform", matrixToStr(mm));
1483 return layerGroup;
1484 }
1485
handleGlyph(uint gid,const ScFace & font)1486 QString SVGExPlug::handleGlyph(uint gid, const ScFace& font)
1487 {
1488 QString glName = QString("Gl%1%2").arg(font.psName().simplified().replace(QRegExp("[\\s\\/\\{\\[\\]\\}\\<\\>\\(\\)\\%]"), "_" )).arg(gid);
1489 if (m_glyphNames.contains(glName))
1490 return glName;
1491 FPointArray pts = font.glyphOutline(gid);
1492 QDomElement ob = m_domDoc.createElement("path");
1493 ob.setAttribute("d", setClipPath(&pts, true));
1494 ob.setAttribute("id", glName);
1495 m_globalDefs.appendChild(ob);
1496 m_glyphNames.append(glName);
1497 return glName;
1498 }
1499
processArrows(PageItem * item,const QDomElement & line,const QString & trans)1500 QDomElement SVGExPlug::processArrows(PageItem *item, const QDomElement& line, const QString& trans)
1501 {
1502 QDomElement ob, gr;
1503 gr = m_domDoc.createElement("g");
1504 gr.appendChild(line);
1505 if (item->startArrowIndex() != 0)
1506 {
1507 QTransform arrowTrans;
1508 FPointArray arrow = m_Doc->arrowStyles().at(item->startArrowIndex()-1).points.copy();
1509 if (item->itemType() == PageItem::Line)
1510 {
1511 arrowTrans.translate(0, 0);
1512 arrowTrans.scale(item->startArrowScale() / 100.0, item->startArrowScale() / 100.0);
1513 if (item->NamedLStyle.isEmpty())
1514 {
1515 if (item->lineWidth() != 0.0)
1516 arrowTrans.scale(item->lineWidth(), item->lineWidth());
1517 }
1518 else
1519 {
1520 multiLine ml = m_Doc->docLineStyles[item->NamedLStyle];
1521 if (ml[ml.size()-1].Width != 0.0)
1522 arrowTrans.scale(ml[ml.size()-1].Width, ml[ml.size()-1].Width);
1523 }
1524 arrowTrans.scale(-1,1);
1525 }
1526 else
1527 {
1528 FPoint Start = item->PoLine.point(0);
1529 for (int xx = 1; xx < item->PoLine.size(); xx += 2)
1530 {
1531 FPoint Vector = item->PoLine.point(xx);
1532 if ((Start.x() != Vector.x()) || (Start.y() != Vector.y()))
1533 {
1534 double r = atan2(Start.y()-Vector.y(),Start.x()-Vector.x())*(180.0/M_PI);
1535 arrowTrans.translate(Start.x(), Start.y());
1536 arrowTrans.rotate(r);
1537 arrowTrans.scale(item->startArrowScale() / 100.0, item->startArrowScale() / 100.0);
1538 if (item->NamedLStyle.isEmpty())
1539 {
1540 if (item->lineWidth() != 0.0)
1541 arrowTrans.scale(item->lineWidth(), item->lineWidth());
1542 }
1543 else
1544 {
1545 multiLine ml = m_Doc->docLineStyles[item->NamedLStyle];
1546 if (ml[ml.size()-1].Width != 0.0)
1547 arrowTrans.scale(ml[ml.size()-1].Width, ml[ml.size()-1].Width);
1548 }
1549 break;
1550 }
1551 }
1552 }
1553 arrow.map(arrowTrans);
1554 if (item->NamedLStyle.isEmpty())
1555 {
1556 ob = m_domDoc.createElement("path");
1557 ob.setAttribute("d", setClipPath(&arrow, true));
1558 ob.setAttribute("transform", trans);
1559 QString aFill;
1560 if (!item->strokePattern().isEmpty())
1561 {
1562 QString pattID = item->strokePattern()+IToStr(m_pattCount);
1563 m_pattCount++;
1564 ScPattern pa = m_Doc->docPatterns[item->strokePattern()];
1565 QDomElement patt = m_domDoc.createElement("pattern");
1566 patt.setAttribute("id", pattID);
1567 patt.setAttribute("height", pa.height);
1568 patt.setAttribute("width", pa.width);
1569 patt.setAttribute("patternUnits", "userSpaceOnUse");
1570 double patternScaleX, patternScaleY, patternOffsetX, patternOffsetY, patternRotation, patternSkewX, patternSkewY, patternSpace;
1571 item->strokePatternTransform(patternScaleX, patternScaleY, patternOffsetX, patternOffsetY, patternRotation, patternSkewX, patternSkewY, patternSpace);
1572 bool mirrorX, mirrorY;
1573 item->strokePatternFlip(mirrorX, mirrorY);
1574 QTransform mpa;
1575 mpa.translate(-item->lineWidth() / 2.0, -item->lineWidth() / 2.0);
1576 mpa.translate(patternOffsetX, patternOffsetY);
1577 mpa.rotate(patternRotation);
1578 mpa.shear(-patternSkewX, patternSkewY);
1579 mpa.scale(pa.scaleX, pa.scaleY);
1580 mpa.scale(patternScaleX / 100.0 , patternScaleY / 100.0);
1581 if (mirrorX)
1582 mpa.scale(-1, 1);
1583 if (mirrorY)
1584 mpa.scale(1, -1);
1585 patt.setAttribute("patternTransform", matrixToStr(mpa));
1586 patt.setAttribute("xlink:href", "#" + item->strokePattern());
1587 m_globalDefs.appendChild(patt);
1588 aFill += "fill:url(#" + pattID + ");";
1589 }
1590 else if (item->GrTypeStroke > 0)
1591 {
1592 QDomElement grad;
1593 if (item->GrTypeStroke == Gradient_Radial)
1594 {
1595 grad = m_domDoc.createElement("radialGradient");
1596 grad.setAttribute("r", FToStr(sqrt(pow(item->GrStrokeEndX - item->GrStrokeStartX, 2) + pow(item->GrStrokeEndY - item->GrStrokeStartY,2))));
1597 grad.setAttribute("cx", FToStr(item->GrStrokeStartX));
1598 grad.setAttribute("cy", FToStr(item->GrStrokeStartY));
1599 }
1600 else
1601 {
1602 grad = m_domDoc.createElement("linearGradient");
1603 grad.setAttribute("x1", FToStr(item->GrStrokeStartX));
1604 grad.setAttribute("y1", FToStr(item->GrStrokeStartY));
1605 grad.setAttribute("x2", FToStr(item->GrStrokeEndX));
1606 grad.setAttribute("y2", FToStr(item->GrStrokeEndY));
1607 }
1608 bool isFirst = true;
1609 double actualStop = 0.0, lastStop = 0.0;
1610 QList<VColorStop*> cstops = item->stroke_gradient.colorStops();
1611 for (int cst = 0; cst < item->stroke_gradient.stops(); ++cst)
1612 {
1613 actualStop = cstops.at(cst)->rampPoint;
1614 if ((actualStop != lastStop) || (isFirst))
1615 {
1616 QDomElement itcl = m_domDoc.createElement("stop");
1617 itcl.setAttribute("offset", FToStr(cstops.at(cst)->rampPoint*100) + "%");
1618 if (cstops.at(cst)->name == CommonStrings::None)
1619 itcl.setAttribute("stop-opacity", FToStr(0));
1620 else
1621 itcl.setAttribute("stop-opacity", FToStr(cstops.at(cst)->opacity));
1622 itcl.setAttribute("stop-color", setColor(cstops.at(cst)->name, cstops.at(cst)->shade));
1623 grad.appendChild(itcl);
1624 lastStop = actualStop;
1625 isFirst = false;
1626 }
1627 }
1628 grad.setAttribute("id", "Grad" + IToStr(m_gradCount));
1629 grad.setAttribute("gradientUnits", "userSpaceOnUse");
1630 m_globalDefs.appendChild(grad);
1631 aFill = " fill:url(#Grad" + IToStr(m_gradCount) + ");";
1632 m_gradCount++;
1633 }
1634 else
1635 aFill = "fill:" + setColor(item->lineColor(), item->lineShade()) + ";";
1636 if (item->lineTransparency() != 0)
1637 aFill += " fill-opacity:" + FToStr(1.0 - item->lineTransparency()) + ";";
1638 ob.setAttribute("style", aFill + " stroke:none;");
1639 gr.appendChild(ob);
1640 }
1641 else
1642 {
1643 multiLine ml = m_Doc->docLineStyles[item->NamedLStyle];
1644 if (ml[0].Color != CommonStrings::None)
1645 {
1646 ob = m_domDoc.createElement("path");
1647 ob.setAttribute("d", setClipPath(&arrow, true));
1648 ob.setAttribute("transform", trans);
1649 QString aFill = "fill:" + setColor(ml[0].Color, ml[0].Shade) + ";";
1650 ob.setAttribute("style", aFill + " stroke:none;");
1651 gr.appendChild(ob);
1652 }
1653 for (int it = ml.size()-1; it > 0; it--)
1654 {
1655 if (ml[it].Color != CommonStrings::None)
1656 {
1657 QDomElement ob5 = m_domDoc.createElement("path");
1658 ob5.setAttribute("d", setClipPath(&arrow, true));
1659 ob5.setAttribute("transform", trans);
1660 QString stroke = "fill:none; stroke:" + setColor(ml[it].Color, ml[it].Shade) + "; stroke-linecap:butt; stroke-linejoin:miter; stroke-dasharray:none;";
1661 if (ml[it].Width != 0.0)
1662 stroke += " stroke-width:" + FToStr(ml[it].Width) + ";";
1663 else
1664 stroke += " stroke-width:1px;";
1665 ob5.setAttribute("style", stroke);
1666 gr.appendChild(ob5);
1667 }
1668 }
1669 }
1670 }
1671 if (item->endArrowIndex() != 0)
1672 {
1673 QTransform arrowTrans;
1674 FPointArray arrow = m_Doc->arrowStyles().at(item->endArrowIndex()-1).points.copy();
1675 if (item->itemType() == PageItem::Line)
1676 {
1677 arrowTrans.translate(item->width(), 0);
1678 arrowTrans.scale(item->endArrowScale() / 100.0, item->endArrowScale() / 100.0);
1679 if (item->NamedLStyle.isEmpty())
1680 {
1681 if (item->lineWidth() != 0.0)
1682 arrowTrans.scale(item->lineWidth(), item->lineWidth());
1683 }
1684 else
1685 {
1686 multiLine ml = m_Doc->docLineStyles[item->NamedLStyle];
1687 if (ml[ml.size()-1].Width != 0.0)
1688 arrowTrans.scale(ml[ml.size() - 1].Width, ml[ml.size() - 1].Width);
1689 }
1690 }
1691 else
1692 {
1693 FPoint End = item->PoLine.point(item->PoLine.size() - 2);
1694 for (uint xx = item->PoLine.size() - 1; xx > 0; xx -= 2)
1695 {
1696 FPoint Vector = item->PoLine.point(xx);
1697 if ((End.x() != Vector.x()) || (End.y() != Vector.y()))
1698 {
1699 double r = atan2(End.y() - Vector.y(), End.x() - Vector.x()) * (180.0 / M_PI);
1700 arrowTrans.translate(End.x(), End.y());
1701 arrowTrans.rotate(r);
1702 arrowTrans.scale(item->endArrowScale() / 100.0, item->endArrowScale() / 100.0);
1703 if (item->NamedLStyle.isEmpty())
1704 {
1705 if (item->lineWidth() != 0.0)
1706 arrowTrans.scale(item->lineWidth(), item->lineWidth());
1707 }
1708 else
1709 {
1710 multiLine ml = m_Doc->docLineStyles[item->NamedLStyle];
1711 if (ml[ml.size() - 1].Width != 0.0)
1712 arrowTrans.scale(ml[ml.size() - 1].Width, ml[ml.size() - 1].Width);
1713 }
1714 break;
1715 }
1716 }
1717 }
1718 arrow.map(arrowTrans);
1719 if (item->NamedLStyle.isEmpty())
1720 {
1721 ob = m_domDoc.createElement("path");
1722 ob.setAttribute("d", setClipPath(&arrow, true));
1723 ob.setAttribute("transform", trans);
1724 QString aFill;
1725 if (!item->strokePattern().isEmpty())
1726 {
1727 QString pattID = item->strokePattern() + IToStr(m_pattCount);
1728 m_pattCount++;
1729 ScPattern pa = m_Doc->docPatterns[item->strokePattern()];
1730 QDomElement patt = m_domDoc.createElement("pattern");
1731 patt.setAttribute("id", pattID);
1732 patt.setAttribute("height", pa.height);
1733 patt.setAttribute("width", pa.width);
1734 patt.setAttribute("patternUnits", "userSpaceOnUse");
1735 double patternScaleX, patternScaleY, patternOffsetX, patternOffsetY, patternRotation, patternSkewX, patternSkewY, patternSpace;
1736 item->strokePatternTransform(patternScaleX, patternScaleY, patternOffsetX, patternOffsetY, patternRotation, patternSkewX, patternSkewY, patternSpace);
1737 bool mirrorX, mirrorY;
1738 item->strokePatternFlip(mirrorX, mirrorY);
1739 QTransform mpa;
1740 mpa.translate(-item->lineWidth() / 2.0, -item->lineWidth() / 2.0);
1741 mpa.translate(patternOffsetX, patternOffsetY);
1742 mpa.rotate(patternRotation);
1743 mpa.shear(-patternSkewX, patternSkewY);
1744 mpa.scale(pa.scaleX, pa.scaleY);
1745 mpa.scale(patternScaleX / 100.0 , patternScaleY / 100.0);
1746 if (mirrorX)
1747 mpa.scale(-1, 1);
1748 if (mirrorY)
1749 mpa.scale(1, -1);
1750 patt.setAttribute("patternTransform", matrixToStr(mpa));
1751 patt.setAttribute("xlink:href", "#" + item->strokePattern());
1752 m_globalDefs.appendChild(patt);
1753 aFill += "fill:url(#" + pattID + ");";
1754 }
1755 else if (item->GrTypeStroke > 0)
1756 {
1757 QDomElement grad;
1758 if (item->GrTypeStroke == Gradient_Radial)
1759 {
1760 grad = m_domDoc.createElement("radialGradient");
1761 grad.setAttribute("r", FToStr(sqrt(pow(item->GrStrokeEndX - item->GrStrokeStartX, 2) + pow(item->GrStrokeEndY - item->GrStrokeStartY,2))));
1762 grad.setAttribute("cx", FToStr(item->GrStrokeStartX));
1763 grad.setAttribute("cy", FToStr(item->GrStrokeStartY));
1764 }
1765 else
1766 {
1767 grad = m_domDoc.createElement("linearGradient");
1768 grad.setAttribute("x1", FToStr(item->GrStrokeStartX));
1769 grad.setAttribute("y1", FToStr(item->GrStrokeStartY));
1770 grad.setAttribute("x2", FToStr(item->GrStrokeEndX));
1771 grad.setAttribute("y2", FToStr(item->GrStrokeEndY));
1772 }
1773 bool isFirst = true;
1774 double actualStop = 0.0, lastStop = 0.0;
1775 QList<VColorStop*> cstops = item->stroke_gradient.colorStops();
1776 for (int cst = 0; cst < item->stroke_gradient.stops(); ++cst)
1777 {
1778 actualStop = cstops.at(cst)->rampPoint;
1779 if ((actualStop != lastStop) || (isFirst))
1780 {
1781 QDomElement itcl = m_domDoc.createElement("stop");
1782 itcl.setAttribute("offset", FToStr(cstops.at(cst)->rampPoint*100) + "%");
1783 if (cstops.at(cst)->name == CommonStrings::None)
1784 itcl.setAttribute("stop-opacity", FToStr(0));
1785 else
1786 itcl.setAttribute("stop-opacity", FToStr(cstops.at(cst)->opacity));
1787 itcl.setAttribute("stop-color", setColor(cstops.at(cst)->name, cstops.at(cst)->shade));
1788 grad.appendChild(itcl);
1789 lastStop = actualStop;
1790 isFirst = false;
1791 }
1792 }
1793 grad.setAttribute("id", "Grad" + IToStr(m_gradCount));
1794 grad.setAttribute("gradientUnits", "userSpaceOnUse");
1795 m_globalDefs.appendChild(grad);
1796 aFill = " fill:url(#Grad" + IToStr(m_gradCount) + ");";
1797 m_gradCount++;
1798 }
1799 else
1800 aFill = "fill:" + setColor(item->lineColor(), item->lineShade()) + ";";
1801 if (item->lineTransparency() != 0)
1802 aFill += " fill-opacity:" + FToStr(1.0 - item->lineTransparency()) + ";";
1803 ob.setAttribute("style", aFill + " stroke:none;");
1804 gr.appendChild(ob);
1805 }
1806 else
1807 {
1808 multiLine ml = m_Doc->docLineStyles[item->NamedLStyle];
1809 if (ml[0].Color != CommonStrings::None)
1810 {
1811 ob = m_domDoc.createElement("path");
1812 ob.setAttribute("d", setClipPath(&arrow, true));
1813 ob.setAttribute("transform", trans);
1814 QString aFill = "fill:" + setColor(ml[0].Color, ml[0].Shade) + ";";
1815 ob.setAttribute("style", aFill + " stroke:none;");
1816 gr.appendChild(ob);
1817 }
1818 for (int it = ml.size()-1; it > 0; it--)
1819 {
1820 if (ml[it].Color != CommonStrings::None)
1821 {
1822 QDomElement ob5 = m_domDoc.createElement("path");
1823 ob5.setAttribute("d", setClipPath(&arrow, true));
1824 ob5.setAttribute("transform", trans);
1825 QString stroke = "fill:none; stroke:" + setColor(ml[it].Color, ml[it].Shade) + "; stroke-linecap:butt; stroke-linejoin:miter; stroke-dasharray:none;";
1826 if (ml[it].Width != 0.0)
1827 stroke += " stroke-width:" + FToStr(ml[it].Width) + ";";
1828 else
1829 stroke += " stroke-width:1px;";
1830 ob5.setAttribute("style", stroke);
1831 gr.appendChild(ob5);
1832 }
1833 }
1834 }
1835 }
1836 return gr;
1837 }
1838
handleMask(PageItem * item,double xOffset,double yOffset)1839 QString SVGExPlug::handleMask(PageItem *item, double xOffset, double yOffset)
1840 {
1841 QDomElement grad;
1842 QString retVal = "";
1843 if (item->GrMask == 0)
1844 return retVal;
1845
1846 QString maskID = "Mask" + IToStr(m_maskCount);
1847 m_maskCount++;
1848 QDomElement mask = m_domDoc.createElement("mask");
1849 mask.setAttribute("id", maskID);
1850 QDomElement ob = m_domDoc.createElement("path");
1851 ob.setAttribute("d", "M 0 0 L " + FToStr(item->width()) + " 0 L " + FToStr(item->width()) + " " + FToStr(item->height()) + " L 0 " + FToStr(item->height()) + " Z");
1852 if (item->isGroup())
1853 {
1854 QString trans = "translate(" + FToStr(xOffset) + ", " + FToStr(yOffset) + ")";
1855 if (item->rotation() != 0)
1856 trans += " rotate(" + FToStr(item->rotation()) + ")";
1857 ob.setAttribute("transform", trans);
1858 }
1859 if ((item->GrMask == GradMask_Pattern) || (item->GrMask == GradMask_PatternLumAlpha))
1860 {
1861 QString pattID = item->patternMask() + IToStr(m_pattCount);
1862 m_pattCount++;
1863 ScPattern pa = m_Doc->docPatterns[item->patternMask()];
1864 QDomElement patt = m_domDoc.createElement("pattern");
1865 patt.setAttribute("id", pattID);
1866 patt.setAttribute("height", FToStr(pa.height));
1867 patt.setAttribute("width", FToStr(pa.width));
1868 patt.setAttribute("patternUnits", "userSpaceOnUse");
1869 double patternScaleX, patternScaleY, patternOffsetX, patternOffsetY, patternRotation, patternSkewX, patternSkewY;
1870 item->maskTransform(patternScaleX, patternScaleY, patternOffsetX, patternOffsetY, patternRotation, patternSkewX, patternSkewY);
1871 bool mirrorX, mirrorY;
1872 item->maskFlip(mirrorX, mirrorY);
1873 QTransform mpa;
1874 mpa.translate(patternOffsetX, patternOffsetY);
1875 mpa.rotate(patternRotation);
1876 mpa.shear(-patternSkewX, patternSkewY);
1877 mpa.scale(pa.scaleX, pa.scaleY);
1878 mpa.scale(patternScaleX / 100.0 , patternScaleY / 100.0);
1879 if (mirrorX)
1880 mpa.scale(-1, 1);
1881 if (mirrorY)
1882 mpa.scale(1, -1);
1883 patt.setAttribute("patternTransform", matrixToStr(mpa));
1884 patt.setAttribute("xlink:href", "#" + item->patternMask());
1885 m_globalDefs.appendChild(patt);
1886 ob.setAttribute("fill", "url(#" + pattID + ")");
1887 }
1888 else if ((item->GrMask == GradMask_Linear) || (item->GrMask == GradMask_Radial) || (item->GrMask == GradMask_LinearLumAlpha) || (item->GrMask == GradMask_RadialLumAlpha))
1889 {
1890 if ((item->GrMask == GradMask_Linear) || (item->GrMask == GradMask_LinearLumAlpha))
1891 {
1892 grad = m_domDoc.createElement("linearGradient");
1893 grad.setAttribute("x1", FToStr(item->GrMaskStartX));
1894 grad.setAttribute("y1", FToStr(item->GrMaskStartY));
1895 grad.setAttribute("x2", FToStr(item->GrMaskEndX));
1896 grad.setAttribute("y2", FToStr(item->GrMaskEndY));
1897 }
1898 else
1899 {
1900 grad = m_domDoc.createElement("radialGradient");
1901 grad.setAttribute("r", FToStr(sqrt(pow(item->GrMaskEndX - item->GrMaskStartX, 2) + pow(item->GrMaskEndY - item->GrMaskStartY,2))));
1902 grad.setAttribute("cx", FToStr(item->GrMaskStartX));
1903 grad.setAttribute("cy", FToStr(item->GrMaskStartY));
1904 grad.setAttribute("fx", FToStr(item->GrMaskFocalX));
1905 grad.setAttribute("fy", FToStr(item->GrMaskFocalY));
1906 }
1907 double gradientSkew;
1908 if (item->GrMaskSkew == 90)
1909 gradientSkew = 1;
1910 else if (item->GrMaskSkew == 180)
1911 gradientSkew = 0;
1912 else if (item->GrMaskSkew == 270)
1913 gradientSkew = -1;
1914 else if (item->GrMaskSkew == 390)
1915 gradientSkew = 0;
1916 else
1917 gradientSkew = tan(M_PI / 180.0 * item->GrMaskSkew);
1918 QTransform qmatrix;
1919 if (item->GrType == Gradient_Linear)
1920 {
1921 qmatrix.translate(item->GrMaskStartX, item->GrMaskStartY);
1922 qmatrix.shear(-gradientSkew, 0);
1923 qmatrix.translate(-item->GrMaskStartX, -item->GrMaskStartY);
1924 }
1925 else
1926 {
1927 double rotEnd = xy2Deg(item->GrMaskEndX - item->GrMaskStartX, item->GrMaskEndY - item->GrMaskStartY);
1928 qmatrix.translate(item->GrMaskStartX, item->GrMaskStartY);
1929 qmatrix.rotate(rotEnd);
1930 qmatrix.shear(gradientSkew, 0);
1931 qmatrix.translate(0, item->GrMaskStartY * (1.0 - item->GrMaskScale));
1932 qmatrix.translate(-item->GrMaskStartX, -item->GrMaskStartY);
1933 qmatrix.scale(1, item->GrMaskScale);
1934 }
1935 grad.setAttribute("gradientTransform", matrixToStr(qmatrix));
1936 grad.setAttribute("id", "Grad" + IToStr(m_gradCount));
1937 grad.setAttribute("gradientUnits", "userSpaceOnUse");
1938 QList<VColorStop*> cstops = item->mask_gradient.colorStops();
1939 for (int cst = 0; cst < item->mask_gradient.stops(); ++cst)
1940 {
1941 QDomElement itcl = m_domDoc.createElement("stop");
1942 itcl.setAttribute("offset", FToStr(cstops.at(cst)->rampPoint*100) + "%");
1943 if (cstops.at(cst)->name == CommonStrings::None)
1944 itcl.setAttribute("stop-opacity", FToStr(0));
1945 else
1946 itcl.setAttribute("stop-opacity", FToStr(cstops.at(cst)->opacity));
1947 itcl.setAttribute("stop-color", setColor(cstops.at(cst)->name, cstops.at(cst)->shade));
1948 grad.appendChild(itcl);
1949 }
1950 m_globalDefs.appendChild(grad);
1951 ob.setAttribute("fill", "url(#Grad" + IToStr(m_gradCount) + ")");
1952 m_gradCount++;
1953 }
1954 if ((item->lineColor() != CommonStrings::None) && (!item->isGroup()))
1955 {
1956 ob.setAttribute("stroke", "white");
1957 if (item->lineWidth() != 0.0)
1958 ob.setAttribute("stroke-width", FToStr(item->lineWidth()));
1959 else
1960 ob.setAttribute("stroke-width", "1px");
1961 }
1962 else
1963 ob.setAttribute("stroke", "none");
1964 mask.appendChild(ob);
1965 m_globalDefs.appendChild(mask);
1966
1967 retVal = "url(#" + maskID + ")";
1968 return retVal;
1969 }
1970
getFillStyle(PageItem * item)1971 QString SVGExPlug::getFillStyle(PageItem *item)
1972 {
1973 QDomElement grad;
1974 QString fill;
1975 if (item->asPathText())
1976 return "fill:none;";
1977 if ((item->fillColor() != CommonStrings::None) || (item->GrType != 0))
1978 {
1979 fill = "fill:" + setColor(item->fillColor(), item->fillShade()) + ";";
1980 if (item->GrType != 0)
1981 {
1982 if (item->GrType == Gradient_Pattern)
1983 {
1984 QString pattID = item->pattern() + IToStr(m_pattCount);
1985 m_pattCount++;
1986 ScPattern pa = m_Doc->docPatterns[item->pattern()];
1987 QDomElement patt = m_domDoc.createElement("pattern");
1988 patt.setAttribute("id", pattID);
1989 patt.setAttribute("height", FToStr(pa.height));
1990 patt.setAttribute("width", FToStr(pa.width));
1991 patt.setAttribute("patternUnits", "userSpaceOnUse");
1992 double patternScaleX, patternScaleY, patternOffsetX, patternOffsetY, patternRotation, patternSkewX, patternSkewY;
1993 item->patternTransform(patternScaleX, patternScaleY, patternOffsetX, patternOffsetY, patternRotation, patternSkewX, patternSkewY);
1994 bool mirrorX, mirrorY;
1995 item->patternFlip(mirrorX, mirrorY);
1996 QTransform mpa;
1997 mpa.translate(patternOffsetX, patternOffsetY);
1998 mpa.rotate(patternRotation);
1999 mpa.shear(-patternSkewX, patternSkewY);
2000 mpa.scale(pa.scaleX, pa.scaleY);
2001 mpa.scale(patternScaleX / 100.0 , patternScaleY / 100.0);
2002 if (mirrorX)
2003 mpa.scale(-1, 1);
2004 if (mirrorY)
2005 mpa.scale(1, -1);
2006 patt.setAttribute("patternTransform", matrixToStr(mpa));
2007 patt.setAttribute("xlink:href", "#" + item->pattern());
2008 m_globalDefs.appendChild(patt);
2009 fill = "fill:url(#" + pattID + ");";
2010 }
2011 else
2012 {
2013 if (item->GrType == Gradient_Linear)
2014 {
2015 grad = m_domDoc.createElement("linearGradient");
2016 grad.setAttribute("x1", FToStr(item->GrStartX));
2017 grad.setAttribute("y1", FToStr(item->GrStartY));
2018 grad.setAttribute("x2", FToStr(item->GrEndX));
2019 grad.setAttribute("y2", FToStr(item->GrEndY));
2020 }
2021 else
2022 {
2023 grad = m_domDoc.createElement("radialGradient");
2024 grad.setAttribute("r", FToStr(sqrt(pow(item->GrEndX - item->GrStartX, 2) + pow(item->GrEndY - item->GrStartY,2))));
2025 grad.setAttribute("cx", FToStr(item->GrStartX));
2026 grad.setAttribute("cy", FToStr(item->GrStartY));
2027 grad.setAttribute("fx", FToStr(item->GrFocalX));
2028 grad.setAttribute("fy", FToStr(item->GrFocalY));
2029 }
2030 double gradientSkew;
2031 if (item->GrSkew == 90)
2032 gradientSkew = 1;
2033 else if (item->GrSkew == 180)
2034 gradientSkew = 0;
2035 else if (item->GrSkew == 270)
2036 gradientSkew = -1;
2037 else if (item->GrSkew == 390)
2038 gradientSkew = 0;
2039 else
2040 gradientSkew = tan(M_PI / 180.0 * item->GrSkew);
2041 QTransform qmatrix;
2042 if (item->GrType == Gradient_Linear)
2043 {
2044 qmatrix.translate(item->GrStartX, item->GrStartY);
2045 qmatrix.shear(-gradientSkew, 0);
2046 qmatrix.translate(-item->GrStartX, -item->GrStartY);
2047 }
2048 else
2049 {
2050 double rotEnd = xy2Deg(item->GrEndX - item->GrStartX, item->GrEndY - item->GrStartY);
2051 qmatrix.translate(item->GrStartX, item->GrStartY);
2052 qmatrix.rotate(rotEnd);
2053 qmatrix.shear(gradientSkew, 0);
2054 qmatrix.translate(0, item->GrStartY * (1.0 - item->GrScale));
2055 qmatrix.translate(-item->GrStartX, -item->GrStartY);
2056 qmatrix.scale(1, item->GrScale);
2057 }
2058 grad.setAttribute("gradientTransform", matrixToStr(qmatrix));
2059 grad.setAttribute("id", "Grad" + IToStr(m_gradCount));
2060 grad.setAttribute("gradientUnits", "userSpaceOnUse");
2061 bool isFirst = true;
2062 double actualStop = 0.0, lastStop = 0.0;
2063 QList<VColorStop*> cstops = item->fill_gradient.colorStops();
2064 for (int cst = 0; cst < item->fill_gradient.stops(); ++cst)
2065 {
2066 actualStop = cstops.at(cst)->rampPoint;
2067 if ((actualStop != lastStop) || (isFirst))
2068 {
2069 QDomElement itcl = m_domDoc.createElement("stop");
2070 itcl.setAttribute("offset", FToStr(cstops.at(cst)->rampPoint * 100) + "%");
2071 if (cstops.at(cst)->name == CommonStrings::None)
2072 itcl.setAttribute("stop-opacity", FToStr(0));
2073 else
2074 itcl.setAttribute("stop-opacity", FToStr(cstops.at(cst)->opacity));
2075 itcl.setAttribute("stop-color", setColor(cstops.at(cst)->name, cstops.at(cst)->shade));
2076 grad.appendChild(itcl);
2077 lastStop = actualStop;
2078 isFirst = false;
2079 }
2080 }
2081 m_globalDefs.appendChild(grad);
2082 fill = "fill:url(#Grad" + IToStr(m_gradCount) + ");";
2083 m_gradCount++;
2084 }
2085 }
2086 if (item->fillRule)
2087 fill += " fill-rule:evenodd;";
2088 else
2089 fill += " fill-rule:nonzero;";
2090 if (item->fillTransparency() != 0)
2091 fill += " fill-opacity:" + FToStr(1.0 - item->fillTransparency()) + ";";
2092 }
2093 else
2094 fill = "fill:none;";
2095 return fill;
2096 }
2097
writeBasePatterns()2098 void SVGExPlug::writeBasePatterns()
2099 {
2100 QStringList patterns = m_Doc->getPatternDependencyList(m_Doc->getUsedPatterns());
2101 for (int c = 0; c < patterns.count(); ++c)
2102 {
2103 ScPattern pa = m_Doc->docPatterns[patterns[c]];
2104 QDomElement patt = m_domDoc.createElement("pattern");
2105 patt.setAttribute("id", patterns[c]);
2106 patt.setAttribute("height", FToStr(pa.height));
2107 patt.setAttribute("width", FToStr(pa.width));
2108 for (int em = 0; em < pa.items.count(); ++em)
2109 {
2110 PageItem* item = pa.items.at(em);
2111 processItemOnPage(item->gXpos, item->gYpos, item, &patt);
2112 }
2113 m_globalDefs.appendChild(patt);
2114 }
2115 }
2116
writeBaseSymbols()2117 void SVGExPlug::writeBaseSymbols()
2118 {
2119 QStringList patterns = m_Doc->getUsedSymbols();
2120 for (int c = 0; c < patterns.count(); ++c)
2121 {
2122 ScPattern pa = m_Doc->docPatterns[patterns[c]];
2123 QDomElement patt = m_domDoc.createElement("symbol");
2124 patt.setAttribute("id", "S" + patterns[c]);
2125 patt.setAttribute("viewbox", "0 0 " + FToStr(pa.height) + " " + FToStr(pa.width));
2126 for (int em = 0; em < pa.items.count(); ++em)
2127 {
2128 PageItem* item = pa.items.at(em);
2129 processItemOnPage(item->gXpos, item->gYpos, item, &patt);
2130 }
2131 m_globalDefs.appendChild(patt);
2132 }
2133 }
2134
getStrokeStyle(PageItem * item)2135 QString SVGExPlug::getStrokeStyle(PageItem *item)
2136 {
2137 QString stroke = "";
2138 if (item->lineTransparency() != 0)
2139 stroke = "stroke-opacity:" + FToStr(1.0 - item->lineTransparency()) + ";";
2140 if (item->lineWidth() != 0.0)
2141 stroke = "stroke-width:" + FToStr(item->lineWidth()) + ";";
2142 else
2143 stroke = "stroke-width:1px;";
2144 stroke += " stroke-linecap:";
2145 switch (item->PLineEnd)
2146 {
2147 case Qt::FlatCap:
2148 stroke += "butt;";
2149 break;
2150 case Qt::SquareCap:
2151 stroke += "square;";
2152 break;
2153 case Qt::RoundCap:
2154 stroke += "round;";
2155 break;
2156 default:
2157 stroke += "butt;";
2158 break;
2159 }
2160 stroke += " stroke-linejoin:";
2161 switch (item->PLineJoin)
2162 {
2163 case Qt::MiterJoin:
2164 stroke += "miter;";
2165 break;
2166 case Qt::BevelJoin:
2167 stroke += "bevel;";
2168 break;
2169 case Qt::RoundJoin:
2170 stroke += "round;";
2171 break;
2172 default:
2173 stroke += "miter;";
2174 break;
2175 }
2176 stroke += " stroke-dasharray:";
2177 if (item->DashValues.count() != 0)
2178 {
2179 for (auto it = item->DashValues.cbegin(); it != item->DashValues.cend(); ++it )
2180 {
2181 stroke += IToStr(static_cast<int>(*it)) + " ";
2182 }
2183 stroke += "; stroke-dashoffset:" + IToStr(static_cast<int>(item->DashOffset)) + ";";
2184 }
2185 else
2186 {
2187 if (item->PLineArt == Qt::SolidLine)
2188 stroke += "none;";
2189 else
2190 {
2191 QString Da = getDashString(item->PLineArt, item->lineWidth());
2192 if (Da.isEmpty())
2193 stroke += "none;";
2194 else
2195 stroke += Da.replace(" ", ", ") + ";";
2196 }
2197 }
2198 if ((!item->strokePattern().isEmpty()) && (!item->patternStrokePath))
2199 {
2200 QString pattID = item->strokePattern() + IToStr(m_pattCount);
2201 m_pattCount++;
2202 ScPattern pa = m_Doc->docPatterns[item->strokePattern()];
2203 QDomElement patt = m_domDoc.createElement("pattern");
2204 patt.setAttribute("id", pattID);
2205 patt.setAttribute("height", FToStr(pa.height));
2206 patt.setAttribute("width", FToStr(pa.width));
2207 patt.setAttribute("patternUnits", "userSpaceOnUse");
2208 double patternScaleX, patternScaleY, patternOffsetX, patternOffsetY, patternRotation, patternSkewX, patternSkewY, patternSpace;
2209 item->strokePatternTransform(patternScaleX, patternScaleY, patternOffsetX, patternOffsetY, patternRotation, patternSkewX, patternSkewY, patternSpace);
2210 bool mirrorX, mirrorY;
2211 item->strokePatternFlip(mirrorX, mirrorY);
2212 QTransform mpa;
2213 mpa.translate(-item->lineWidth() / 2.0, -item->lineWidth() / 2.0);
2214 mpa.translate(patternOffsetX, patternOffsetY);
2215 mpa.rotate(patternRotation);
2216 mpa.shear(-patternSkewX, patternSkewY);
2217 mpa.scale(pa.scaleX, pa.scaleY);
2218 mpa.scale(patternScaleX / 100.0 , patternScaleY / 100.0);
2219 if (mirrorX)
2220 mpa.scale(-1, 1);
2221 if (mirrorY)
2222 mpa.scale(1, -1);
2223 patt.setAttribute("patternTransform", matrixToStr(mpa));
2224 patt.setAttribute("xlink:href", "#" + item->strokePattern());
2225 m_globalDefs.appendChild(patt);
2226 stroke += " stroke:url(#" + pattID + ");";
2227 }
2228 else if (item->GrTypeStroke > 0)
2229 {
2230 QDomElement grad;
2231 if (item->GrTypeStroke == Gradient_Radial)
2232 {
2233 grad = m_domDoc.createElement("radialGradient");
2234 grad.setAttribute("r", FToStr(sqrt(pow(item->GrStrokeEndX - item->GrStrokeStartX, 2) + pow(item->GrStrokeEndY - item->GrStrokeStartY,2))));
2235 grad.setAttribute("cx", FToStr(item->GrStrokeStartX));
2236 grad.setAttribute("cy", FToStr(item->GrStrokeStartY));
2237 grad.setAttribute("fx", FToStr(item->GrStrokeFocalX));
2238 grad.setAttribute("fy", FToStr(item->GrStrokeFocalY));
2239 }
2240 else
2241 {
2242 grad = m_domDoc.createElement("linearGradient");
2243 grad.setAttribute("x1", FToStr(item->GrStrokeStartX));
2244 grad.setAttribute("y1", FToStr(item->GrStrokeStartY));
2245 grad.setAttribute("x2", FToStr(item->GrStrokeEndX));
2246 grad.setAttribute("y2", FToStr(item->GrStrokeEndY));
2247 }
2248 bool isFirst = true;
2249 double actualStop = 0.0, lastStop = 0.0;
2250 QList<VColorStop*> cstops = item->stroke_gradient.colorStops();
2251 for (int cst = 0; cst < item->stroke_gradient.stops(); ++cst)
2252 {
2253 actualStop = cstops.at(cst)->rampPoint;
2254 if ((actualStop != lastStop) || (isFirst))
2255 {
2256 QDomElement itcl = m_domDoc.createElement("stop");
2257 itcl.setAttribute("offset", FToStr(cstops.at(cst)->rampPoint*100) + "%");
2258 if (cstops.at(cst)->name == CommonStrings::None)
2259 itcl.setAttribute("stop-opacity", FToStr(0));
2260 else
2261 itcl.setAttribute("stop-opacity", FToStr(cstops.at(cst)->opacity));
2262 itcl.setAttribute("stop-color", setColor(cstops.at(cst)->name, cstops.at(cst)->shade));
2263 grad.appendChild(itcl);
2264 lastStop = actualStop;
2265 isFirst = false;
2266 }
2267 }
2268 double gradientSkew;
2269 if (item->GrStrokeSkew == 90)
2270 gradientSkew = 1;
2271 else if (item->GrStrokeSkew == 180)
2272 gradientSkew = 0;
2273 else if (item->GrStrokeSkew == 270)
2274 gradientSkew = -1;
2275 else if (item->GrStrokeSkew == 390)
2276 gradientSkew = 0;
2277 else
2278 gradientSkew = tan(M_PI / 180.0 * item->GrStrokeSkew);
2279 QTransform qmatrix;
2280 if (item->GrType == Gradient_Linear)
2281 {
2282 qmatrix.translate(item->GrStrokeStartX, item->GrStrokeStartY);
2283 qmatrix.shear(-gradientSkew, 0);
2284 qmatrix.translate(-item->GrStrokeStartX, -item->GrStrokeStartY);
2285 }
2286 else
2287 {
2288 double rotEnd = xy2Deg(item->GrStrokeEndX - item->GrStrokeStartX, item->GrStrokeEndY - item->GrStrokeStartY);
2289 qmatrix.translate(item->GrStrokeStartX, item->GrStrokeStartY);
2290 qmatrix.rotate(rotEnd);
2291 qmatrix.shear(gradientSkew, 0);
2292 qmatrix.translate(0, item->GrStrokeStartY * (1.0 - item->GrStrokeScale));
2293 qmatrix.translate(-item->GrStrokeStartX, -item->GrStrokeStartY);
2294 qmatrix.scale(1, item->GrStrokeScale);
2295 }
2296 grad.setAttribute("gradientTransform", matrixToStr(qmatrix));
2297 grad.setAttribute("id", "Grad" + IToStr(m_gradCount));
2298 grad.setAttribute("gradientUnits", "userSpaceOnUse");
2299 m_globalDefs.appendChild(grad);
2300 stroke += " stroke:url(#Grad" + IToStr(m_gradCount) + ");";
2301 m_gradCount++;
2302 }
2303 else if (item->lineColor() != CommonStrings::None)
2304 {
2305 stroke += " stroke:" + setColor(item->lineColor(), item->lineShade()) + ";";
2306 }
2307 else
2308 stroke = "stroke:none;";
2309 return stroke;
2310 }
2311
createClipPathElement(FPointArray * ite,QDomElement * pathElem)2312 QDomElement SVGExPlug::createClipPathElement(FPointArray *ite, QDomElement* pathElem)
2313 {
2314 QString clipPathStr = setClipPath(ite, true);
2315 if (clipPathStr.isEmpty())
2316 return QDomElement();
2317 QDomElement clipPathElem = m_domDoc.createElement("clipPath");
2318 clipPathElem.setAttribute("id", "Clip" + IToStr(m_clipCount));
2319 QDomElement cl = m_domDoc.createElement("path");
2320 if (pathElem)
2321 *pathElem = cl;
2322 cl.setAttribute("d", clipPathStr);
2323 clipPathElem.appendChild(cl);
2324 m_globalDefs.appendChild(clipPathElem);
2325 m_clipCount++;
2326 return clipPathElem;
2327 }
2328
setClipPath(FPointArray * ite,bool closed)2329 QString SVGExPlug::setClipPath(FPointArray *ite, bool closed)
2330 {
2331 QString tmp;
2332 FPoint np, np1, np2, np3, np4, firstP;
2333 bool nPath = true;
2334 bool first = true;
2335
2336 if (ite->size() <= 3)
2337 return tmp;
2338
2339 for (int poi=0; poi<ite->size()-3; poi += 4)
2340 {
2341 if (ite->isMarker(poi))
2342 {
2343 nPath = true;
2344 continue;
2345 }
2346 if (nPath)
2347 {
2348 np = ite->point(poi);
2349 if ((!first) && (closed) && (np4 == firstP))
2350 tmp += "Z ";
2351 tmp += QString("M%1 %2 ").arg(np.x()).arg(np.y());
2352 nPath = false;
2353 first = false;
2354 firstP = np;
2355 np4 = np;
2356 }
2357 np = ite->point(poi);
2358 np1 = ite->point(poi+1);
2359 np2 = ite->point(poi+3);
2360 np3 = ite->point(poi+2);
2361 if ((np == np1) && (np2 == np3))
2362 tmp += QString("L%1 %2 ").arg(np3.x()).arg(np3.y());
2363 else
2364 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());
2365 np4 = np3;
2366 }
2367 if (closed)
2368 tmp += "Z";
2369 return tmp;
2370 }
2371
FToStr(double c)2372 QString SVGExPlug::FToStr(double c)
2373 {
2374 QString cc;
2375 return cc.setNum(c);
2376 }
2377
IToStr(int c)2378 QString SVGExPlug::IToStr(int c)
2379 {
2380 QString cc;
2381 return cc.setNum(c);
2382 }
2383
matrixToStr(QTransform & mat)2384 QString SVGExPlug::matrixToStr(QTransform &mat)
2385 {
2386 QString cc("matrix(%1 %2 %3 %4 %5 %6)");
2387 return cc.arg(mat.m11()).arg(mat.m12()).arg(mat.m21()).arg(mat.m22()).arg(mat.dx()).arg(mat.dy());
2388 }
2389
setColor(const QString & farbe,int shad)2390 QString SVGExPlug::setColor(const QString& farbe, int shad)
2391 {
2392 if (farbe == CommonStrings::None)
2393 return "#FFFFFF";
2394 const ScColor& col = m_Doc->PageColors[farbe];
2395 return ScColorEngine::getShadeColorProof(col, m_Doc, shad).name();
2396 }
2397
getMultiStroke(struct SingleLine * sl,PageItem * item)2398 QString SVGExPlug::getMultiStroke(struct SingleLine *sl, PageItem *item)
2399 {
2400 QString tmp = "fill:none; ";
2401 tmp += "stroke:" + setColor(sl->Color, sl->Shade) + "; ";
2402 if (item->fillTransparency() != 0)
2403 tmp += QString(" stroke-opacity:%1; ").arg(1.0 - item->fillTransparency());
2404 tmp += QString("stroke-width:%1; ").arg(sl->Width);
2405 tmp += "stroke-linecap:";
2406 switch (static_cast<Qt::PenCapStyle>(sl->LineEnd))
2407 {
2408 case Qt::FlatCap:
2409 tmp += "butt;";
2410 break;
2411 case Qt::SquareCap:
2412 tmp += "square;";
2413 break;
2414 case Qt::RoundCap:
2415 tmp += "round;";
2416 break;
2417 default:
2418 tmp += "butt;";
2419 break;
2420 }
2421 tmp += " stroke-linejoin:";
2422 switch (static_cast<Qt::PenJoinStyle>(sl->LineJoin))
2423 {
2424 case Qt::MiterJoin:
2425 tmp += "miter;";
2426 break;
2427 case Qt::BevelJoin:
2428 tmp += "bevel;";
2429 break;
2430 case Qt::RoundJoin:
2431 tmp += "round;";
2432 break;
2433 default:
2434 tmp += "miter;";
2435 break;
2436 }
2437 tmp += " stroke-dasharray:";
2438 if (static_cast<Qt::PenStyle>(sl->Dash) == Qt::SolidLine)
2439 tmp += "none;";
2440 else
2441 {
2442 QString Da = getDashString(sl->Dash, sl->Width);
2443 if (Da.isEmpty())
2444 tmp += "none;";
2445 else
2446 tmp += Da.replace(" ", ", ") + ";";
2447 }
2448 return tmp;
2449 }
2450
~SVGExPlug()2451 SVGExPlug::~SVGExPlug()
2452 {
2453 }
2454