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 *   Copyright (C) 2007 by Franz Schmid                                     *
9 *   franz.schmid@altmuehlnet.de                                            *
10 *                                                                          *
11 *   This program is free software; you can redistribute it and/or modify   *
12 *   it under the terms of the GNU General Public License as published by   *
13 *   the Free Software Foundation; either version 2 of the License, or      *
14 *   (at your option) any later version.                                    *
15 *                                                                          *
16 *   This program is distributed in the hope that it will be useful,        *
17 *   but WITHOUT ANY WARRANTY; without even the implied warranty of         *
18 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          *
19 *   GNU General Public License for more details.                           *
20 *                                                                          *
21 *   You should have received a copy of the GNU General Public License      *
22 *   along with this program; if not, write to the                          *
23 *   Free Software Foundation, Inc.,                                        *
24 *   51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.              *
25 ****************************************************************************/
26 
27 #if defined(_MSC_VER) && !defined(_USE_MATH_DEFINES)
28 #define _USE_MATH_DEFINES
29 #endif
30 #include <cmath>
31 #include <QPainterPathStroker>
32 
33 #include "pathstroker.h"
34 
35 #include "appmodes.h"
36 #include "commonstrings.h"
37 #include "pageitem_polygon.h"
38 #include "scribuscore.h"
39 #include "scribusdoc.h"
40 #include "selection.h"
41 #include "util.h"
42 
43 
44 
pathstroker_getPluginAPIVersion()45 int pathstroker_getPluginAPIVersion()
46 {
47 	return PLUGIN_API_VERSION;
48 }
49 
pathstroker_getPlugin()50 ScPlugin* pathstroker_getPlugin()
51 {
52 	PathStrokerPlugin* plug = new PathStrokerPlugin();
53 	Q_CHECK_PTR(plug);
54 	return plug;
55 }
56 
pathstroker_freePlugin(ScPlugin * plugin)57 void pathstroker_freePlugin(ScPlugin* plugin)
58 {
59 	PathStrokerPlugin* plug = qobject_cast<PathStrokerPlugin*>(plugin);
60 	Q_ASSERT(plug);
61 	delete plug;
62 }
63 
PathStrokerPlugin()64 PathStrokerPlugin::PathStrokerPlugin()
65 {
66 	// Set action info in languageChange, so we only have to do
67 	// it in one place.
68 	languageChange();
69 }
70 
~PathStrokerPlugin()71 PathStrokerPlugin::~PathStrokerPlugin() {};
72 
languageChange()73 void PathStrokerPlugin::languageChange()
74 {
75 	// Note that we leave the unused members unset. They'll be initialised
76 	// with their default ctors during construction.
77 	// Action name
78 	m_actionInfo.name = "PathStroker";
79 	// Action text for menu, including accel
80 	m_actionInfo.text = tr("Create Path from Stroke");
81 	// Menu
82 	m_actionInfo.menu = "ItemPathOps";
83 	m_actionInfo.parentMenu = "Item";
84 	m_actionInfo.subMenuName = tr("Path Tools");
85 	m_actionInfo.enabledOnStartup = false;
86 	m_actionInfo.notSuitableFor.append(PageItem::Line);
87 	m_actionInfo.notSuitableFor.append(PageItem::TextFrame);
88 	m_actionInfo.notSuitableFor.append(PageItem::ImageFrame);
89 	m_actionInfo.notSuitableFor.append(PageItem::PathText);
90 	m_actionInfo.notSuitableFor.append(PageItem::LatexFrame);
91 	m_actionInfo.notSuitableFor.append(PageItem::Symbol);
92 	m_actionInfo.notSuitableFor.append(PageItem::RegularPolygon);
93 	m_actionInfo.notSuitableFor.append(PageItem::Arc);
94 	m_actionInfo.notSuitableFor.append(PageItem::Spiral);
95 	m_actionInfo.forAppMode.append(modeNormal);
96 	m_actionInfo.needsNumObjects = 1;
97 }
98 
fullTrName() const99 QString PathStrokerPlugin::fullTrName() const
100 {
101 	return QObject::tr("PathStroker");
102 }
103 
getAboutData() const104 const ScActionPlugin::AboutData* PathStrokerPlugin::getAboutData() const
105 {
106 	AboutData* about = new AboutData;
107 	Q_CHECK_PTR(about);
108 	about->authors = QString::fromUtf8("Franz Schmid <Franz.Schmid@altmuehlnet.de>");
109 	about->shortDescription = tr("Create Path from Stroke");
110 	about->description = tr("Converts the stroke of a Path to a filled Path.");
111 	// about->version
112 	// about->releaseDate
113 	// about->copyright
114 	about->license = "GPL";
115 	return about;
116 }
117 
deleteAboutData(const AboutData * about) const118 void PathStrokerPlugin::deleteAboutData(const AboutData* about) const
119 {
120 	Q_ASSERT(about);
121 	delete about;
122 }
123 
run(ScribusDoc * doc,const QString &)124 bool PathStrokerPlugin::run(ScribusDoc* doc, const QString&)
125 {
126 	ScribusDoc* currDoc = doc;
127 	if (currDoc == nullptr)
128 		currDoc = ScCore->primaryMainWindow()->doc;
129 	if (currDoc->m_Selection->count() > 0)
130 	{
131 		QVector<double> m_array;
132 		PageItem *currItem = currDoc->m_Selection->itemAt(0);
133 		FPointArray path = currItem->PoLine;
134 		QPainterPath pp;
135 		if (currItem->itemType() == PageItem::PolyLine)
136 			pp = path.toQPainterPath(false);
137 		else
138 			pp = path.toQPainterPath(true);
139 		if (currItem->NamedLStyle.isEmpty())
140 		{
141 			QPainterPathStroker stroke;
142 			stroke.setCapStyle(currItem->lineEnd());
143 			stroke.setJoinStyle(currItem->lineJoin());
144 			if (currItem->lineStyle() == Qt::SolidLine)
145 				stroke.setDashPattern(currItem->lineStyle());
146 			else
147 			{
148 				getDashArray(currItem->lineStyle(), 1, m_array);
149 				stroke.setDashPattern(m_array);
150 			}
151 			stroke.setWidth(currItem->lineWidth());
152 			QPainterPath result = stroke.createStroke(pp).simplified();
153 			if (currItem->startArrowIndex() != 0)
154 			{
155 				FPoint Start = currItem->PoLine.point(0);
156 				for (int xx = 1; xx < currItem->PoLine.size(); xx += 2)
157 				{
158 					FPoint Vector = currItem->PoLine.point(xx);
159 					if ((Start.x() != Vector.x()) || (Start.y() != Vector.y()))
160 					{
161 						double r = atan2(Start.y()-Vector.y(),Start.x()-Vector.x())*(180.0/M_PI);
162 						QTransform arrowTrans;
163 						FPointArray arrow = currDoc->arrowStyles().at(currItem->startArrowIndex()-1).points.copy();
164 						arrowTrans.translate(Start.x(), Start.y());
165 						arrowTrans.rotate(r);
166 						arrowTrans.scale(currItem->startArrowScale() / 100.0, currItem->startArrowScale() / 100.0);
167 						arrowTrans.scale(currItem->lineWidth(), currItem->lineWidth());
168 						arrow.map(arrowTrans);
169 						result.addPath(arrow.toQPainterPath(true));
170 						break;
171 					}
172 				}
173 			}
174 			if (currItem->endArrowIndex() != 0)
175 			{
176 				FPoint End = currItem->PoLine.point(currItem->PoLine.size()-2);
177 				for (uint xx = currItem->PoLine.size()-1; xx > 0; xx -= 2)
178 				{
179 					FPoint Vector = currItem->PoLine.point(xx);
180 					if ((End.x() != Vector.x()) || (End.y() != Vector.y()))
181 					{
182 						double r = atan2(End.y()-Vector.y(),End.x()-Vector.x())*(180.0/M_PI);
183 						QTransform arrowTrans;
184 						FPointArray arrow = currDoc->arrowStyles().at(currItem->endArrowIndex()-1).points.copy();
185 						arrowTrans.translate(End.x(), End.y());
186 						arrowTrans.rotate(r);
187 						arrowTrans.scale(currItem->endArrowScale() / 100.0, currItem->endArrowScale() / 100.0);
188 						arrowTrans.scale(currItem->lineWidth(), currItem->lineWidth());
189 						arrow.map(arrowTrans);
190 						result.addPath(arrow.toQPainterPath(true));
191 						break;
192 					}
193 				}
194 			}
195 			currDoc->m_Selection->clear();
196 			PageItem* newItem = currDoc->convertItemTo(currItem, PageItem::Polygon);
197 			newItem->setLineWidth(0);
198 			newItem->setLineStyle(Qt::SolidLine);
199 			newItem->setFillColor(newItem->lineColor());
200 			newItem->setFillShade(newItem->lineShade());
201 			newItem->setFillTransparency(newItem->lineTransparency());
202 			newItem->setFillBlendmode(newItem->lineBlendmode());
203 			FPointArray points;
204 			points.fromQPainterPath(result);
205 			newItem->PoLine = points;
206 			newItem->ClipEdited = true;
207 			newItem->FrameType = 3;
208 			currDoc->adjustItemSize(newItem);
209 			newItem->OldB2 = newItem->width();
210 			newItem->OldH2 = newItem->height();
211 			newItem->updateClip();
212 			newItem->ContourLine = newItem->PoLine.copy();
213 			newItem->setFillEvenOdd(false);
214 			currDoc->m_Selection->addItem(newItem);
215 		}
216 		else
217 		{
218 			currDoc->m_Selection->clear();
219 			multiLine ml = currDoc->docLineStyles[currItem->NamedLStyle];
220 			bool first = true;
221 			for (int it = ml.size()-1; it > -1; it--)
222 			{
223 				if ((ml[it].Color != CommonStrings::None) && (ml[it].Width != 0))
224 				{
225 					QPainterPathStroker stroke;
226 					stroke.setCapStyle(static_cast<Qt::PenCapStyle>(ml[it].LineEnd));
227 					stroke.setJoinStyle(static_cast<Qt::PenJoinStyle>(ml[it].LineJoin));
228 					if (static_cast<Qt::PenStyle>(ml[it].Dash) == Qt::SolidLine)
229 						stroke.setDashPattern(static_cast<Qt::PenStyle>(ml[it].Dash));
230 					else
231 					{
232 						getDashArray(static_cast<Qt::PenStyle>(ml[it].Dash), 1, m_array);
233 						stroke.setDashPattern(m_array);
234 					}
235 					stroke.setWidth(ml[it].Width);
236 					QPainterPath result = stroke.createStroke(pp).simplified();
237 					PageItem* newItem;
238 					if (first)
239 					{
240 						newItem = currDoc->convertItemTo(currItem, PageItem::Polygon);
241 					}
242 					else
243 					{
244 						newItem = new PageItem_Polygon(*currItem);
245 						newItem->convertTo(PageItem::Polygon);
246 						currDoc->Items->append(newItem);
247 					}
248 					first = false;
249 					newItem->setLineStyle(Qt::SolidLine);
250 					newItem->setFillColor(ml[it].Color);
251 					newItem->setFillShade(ml[it].Shade);
252 					newItem->setFillTransparency(newItem->lineTransparency());
253 					newItem->setFillBlendmode(newItem->lineBlendmode());
254 					newItem->setLineColor(CommonStrings::None);
255 					newItem->setCustomLineStyle("");
256 					FPointArray points;
257 					points.fromQPainterPath(result);
258 					newItem->PoLine = points;
259 					newItem->ClipEdited = true;
260 					newItem->FrameType = 3;
261 					currDoc->adjustItemSize(newItem);
262 					newItem->OldB2 = newItem->width();
263 					newItem->OldH2 = newItem->height();
264 					newItem->updateClip();
265 					newItem->ContourLine = newItem->PoLine.copy();
266 					newItem->setFillEvenOdd(false);
267 					currDoc->m_Selection->addItem(newItem);
268 				}
269 			}
270 			if (currItem->startArrowIndex() != 0)
271 			{
272 				FPoint Start = currItem->PoLine.point(0);
273 				for (int xx = 1; xx < currItem->PoLine.size(); xx += 2)
274 				{
275 					FPoint Vector = currItem->PoLine.point(xx);
276 					if ((Start.x() != Vector.x()) || (Start.y() != Vector.y()))
277 					{
278 						double r = atan2(Start.y()-Vector.y(),Start.x()-Vector.x())*(180.0/M_PI);
279 						QTransform arrowTrans;
280 						FPointArray arrow = currDoc->arrowStyles().at(currItem->startArrowIndex()-1).points.copy();
281 						arrowTrans.translate(Start.x(), Start.y());
282 						arrowTrans.rotate(r);
283 						arrowTrans.scale(currItem->startArrowScale() / 100.0, currItem->startArrowScale() / 100.0);
284 						arrowTrans.scale(currItem->lineWidth(), currItem->lineWidth());
285 						arrow.map(arrowTrans);
286 						PageItem* newItem = new PageItem_Polygon(*currItem);
287 						currDoc->Items->append(newItem);
288 						newItem->setLineWidth(0);
289 						newItem->setLineStyle(Qt::SolidLine);
290 						newItem->setCustomLineStyle("");
291 						newItem->setFillColor(newItem->lineColor());
292 						newItem->setFillShade(newItem->lineShade());
293 						newItem->setFillTransparency(newItem->lineTransparency());
294 						newItem->setFillBlendmode(newItem->lineBlendmode());
295 						newItem->PoLine = arrow;
296 						newItem->ClipEdited = true;
297 						newItem->FrameType = 3;
298 						currDoc->adjustItemSize(newItem);
299 						newItem->OldB2 = newItem->width();
300 						newItem->OldH2 = newItem->height();
301 						newItem->updateClip();
302 						newItem->ContourLine = newItem->PoLine.copy();
303 						newItem->setFillEvenOdd(true);
304 						currDoc->m_Selection->addItem(newItem);
305 						break;
306 					}
307 				}
308 			}
309 			if (currItem->endArrowIndex() != 0)
310 			{
311 				FPoint End = currItem->PoLine.point(currItem->PoLine.size()-2);
312 				for (uint xx = currItem->PoLine.size()-1; xx > 0; xx -= 2)
313 				{
314 					FPoint Vector = currItem->PoLine.point(xx);
315 					if ((End.x() != Vector.x()) || (End.y() != Vector.y()))
316 					{
317 						double r = atan2(End.y()-Vector.y(),End.x()-Vector.x())*(180.0/M_PI);
318 						QTransform arrowTrans;
319 						FPointArray arrow = currDoc->arrowStyles().at(currItem->endArrowIndex()-1).points.copy();
320 						arrowTrans.translate(End.x(), End.y());
321 						arrowTrans.rotate(r);
322 						arrowTrans.scale(currItem->endArrowScale() / 100.0, currItem->endArrowScale() / 100.0);
323 						arrowTrans.scale(currItem->lineWidth(), currItem->lineWidth());
324 						arrow.map(arrowTrans);
325 						PageItem* newItem = new PageItem_Polygon(*currItem);
326 						currDoc->Items->append(newItem);
327 						newItem->setLineWidth(0);
328 						newItem->setLineStyle(Qt::SolidLine);
329 						newItem->setCustomLineStyle("");
330 						newItem->setFillColor(newItem->lineColor());
331 						newItem->setFillShade(newItem->lineShade());
332 						newItem->setFillTransparency(newItem->lineTransparency());
333 						newItem->setFillBlendmode(newItem->lineBlendmode());
334 						newItem->PoLine = arrow;
335 						newItem->ClipEdited = true;
336 						newItem->FrameType = 3;
337 						currDoc->adjustItemSize(newItem);
338 						newItem->OldB2 = newItem->width();
339 						newItem->OldH2 = newItem->height();
340 						newItem->updateClip();
341 						newItem->ContourLine = newItem->PoLine.copy();
342 						newItem->setFillEvenOdd(true);
343 						currDoc->m_Selection->addItem(newItem);
344 						break;
345 					}
346 				}
347 			}
348 			if (currDoc->m_Selection->count() > 1)
349 				currDoc->itemSelection_GroupObjects(false, false);
350 			currDoc->m_Selection->itemAt(0)->emitAllToGUI();
351 		}
352 		currDoc->changed();
353 	}
354 	return true;
355 }
356