1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /** @file
3  * TODO: insert short description here
4  *//*
5  * Authors: see git history
6  *
7  * Copyright (C) 2018 Authors
8  * Released under GNU GPL v2+, read the file 'COPYING' for more information.
9  */
10 /*
11  */
12 
13 #include <glibmm/i18n.h>
14 
15 #include <xml/repr.h>
16 #include "display/curve.h"
17 #include "sp-shape.h"
18 #include "sp-text.h"
19 #include "sp-use.h"
20 #include "style.h"
21 #include "document.h"
22 #include "sp-title.h"
23 #include "sp-desc.h"
24 
25 #include "sp-flowregion.h"
26 
27 #include "livarot/Path.h"
28 #include "livarot/Shape.h"
29 
30 
31 static void         GetDest(SPObject* child,Shape **computed);
32 
33 
SPFlowregion()34 SPFlowregion::SPFlowregion() : SPItem() {
35 }
36 
~SPFlowregion()37 SPFlowregion::~SPFlowregion() {
38 	for (auto & it : this->computed) {
39         delete it;
40 	}
41 }
42 
child_added(Inkscape::XML::Node * child,Inkscape::XML::Node * ref)43 void SPFlowregion::child_added(Inkscape::XML::Node *child, Inkscape::XML::Node *ref) {
44 	SPItem::child_added(child, ref);
45 
46 	this->requestModified(SP_OBJECT_MODIFIED_FLAG);
47 }
48 
49 /* fixme: hide (Lauris) */
50 
remove_child(Inkscape::XML::Node * child)51 void SPFlowregion::remove_child(Inkscape::XML::Node * child) {
52 	SPItem::remove_child(child);
53 
54 	this->requestModified(SP_OBJECT_MODIFIED_FLAG);
55 }
56 
57 
update(SPCtx * ctx,unsigned int flags)58 void SPFlowregion::update(SPCtx *ctx, unsigned int flags) {
59     SPItemCtx *ictx = reinterpret_cast<SPItemCtx *>(ctx);
60     SPItemCtx cctx = *ictx;
61 
62     unsigned childflags = flags;
63     if (flags & SP_OBJECT_MODIFIED_FLAG) {
64         childflags |= SP_OBJECT_PARENT_MODIFIED_FLAG;
65     }
66     childflags &= SP_OBJECT_MODIFIED_CASCADE;
67 
68     std::vector<SPObject*>l;
69 
70     for (auto& child: children) {
71         sp_object_ref(&child);
72         l.push_back(&child);
73     }
74 
75     for (auto child:l) {
76         g_assert(child != nullptr);
77         SPItem *item = dynamic_cast<SPItem *>(child);
78 
79         if (childflags || (child->uflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) {
80             if (item) {
81                 SPItem const &chi = *item;
82                 cctx.i2doc = chi.transform * ictx->i2doc;
83                 cctx.i2vp = chi.transform * ictx->i2vp;
84                 child->updateDisplay((SPCtx *)&cctx, childflags);
85             } else {
86                 child->updateDisplay(ctx, childflags);
87             }
88         }
89 
90         sp_object_unref(child);
91     }
92 
93     SPItem::update(ctx, flags);
94 
95     this->UpdateComputed();
96 }
97 
UpdateComputed()98 void SPFlowregion::UpdateComputed()
99 {
100     for (auto & it : computed) {
101         delete it;
102     }
103     computed.clear();
104 
105     for (auto& child: children) {
106         Shape *shape = nullptr;
107         GetDest(&child, &shape);
108         computed.push_back(shape);
109     }
110 }
111 
modified(guint flags)112 void SPFlowregion::modified(guint flags) {
113     if (flags & SP_OBJECT_MODIFIED_FLAG) {
114         flags |= SP_OBJECT_PARENT_MODIFIED_FLAG;
115     }
116 
117     flags &= SP_OBJECT_MODIFIED_CASCADE;
118 
119     std::vector<SPObject *>l;
120 
121     for (auto& child: children) {
122         sp_object_ref(&child);
123         l.push_back(&child);
124     }
125 
126     for (auto child:l) {
127         g_assert(child != nullptr);
128 
129         if (flags || (child->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) {
130             child->emitModified(flags);
131         }
132 
133         sp_object_unref(child);
134     }
135 }
136 
write(Inkscape::XML::Document * xml_doc,Inkscape::XML::Node * repr,guint flags)137 Inkscape::XML::Node *SPFlowregion::write(Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags) {
138     if (flags & SP_OBJECT_WRITE_BUILD) {
139         if ( repr == nullptr ) {
140             repr = xml_doc->createElement("svg:flowRegion");
141         }
142 
143         std::vector<Inkscape::XML::Node *> l;
144         for (auto& child: children) {
145             if ( !dynamic_cast<SPTitle *>(&child) && !dynamic_cast<SPDesc *>(&child) ) {
146                 Inkscape::XML::Node *crepr = child.updateRepr(xml_doc, nullptr, flags);
147 
148                 if (crepr) {
149                     l.push_back(crepr);
150                 }
151             }
152         }
153 
154         for (auto i = l.rbegin(); i != l.rend(); ++i) {
155             repr->addChild(*i, nullptr);
156             Inkscape::GC::release(*i);
157         }
158 
159         for (auto& child: children) {
160             if ( !dynamic_cast<SPTitle *>(&child) && !dynamic_cast<SPDesc *>(&child) ) {
161                 child.updateRepr(flags);
162             }
163         }
164     }
165 
166     SPItem::write(xml_doc, repr, flags);
167 
168     this->UpdateComputed();  // copied from update(), see LP Bug 1339305
169 
170     return repr;
171 }
172 
displayName() const173 const char* SPFlowregion::displayName() const {
174 	// TRANSLATORS: "Flow region" is an area where text is allowed to flow
175 	return _("Flow Region");
176 }
177 
SPFlowregionExclude()178 SPFlowregionExclude::SPFlowregionExclude() : SPItem() {
179 	this->computed = nullptr;
180 }
181 
~SPFlowregionExclude()182 SPFlowregionExclude::~SPFlowregionExclude() {
183     if (this->computed) {
184         delete this->computed;
185         this->computed = nullptr;
186     }
187 }
188 
child_added(Inkscape::XML::Node * child,Inkscape::XML::Node * ref)189 void SPFlowregionExclude::child_added(Inkscape::XML::Node *child, Inkscape::XML::Node *ref) {
190 	SPItem::child_added(child, ref);
191 
192 	this->requestModified(SP_OBJECT_MODIFIED_FLAG);
193 }
194 
195 /* fixme: hide (Lauris) */
196 
remove_child(Inkscape::XML::Node * child)197 void SPFlowregionExclude::remove_child(Inkscape::XML::Node * child) {
198 	SPItem::remove_child(child);
199 
200 	this->requestModified(SP_OBJECT_MODIFIED_FLAG);
201 }
202 
203 
update(SPCtx * ctx,unsigned int flags)204 void SPFlowregionExclude::update(SPCtx *ctx, unsigned int flags) {
205     SPItemCtx *ictx = reinterpret_cast<SPItemCtx *>(ctx);
206     SPItemCtx cctx = *ictx;
207 
208     SPItem::update(ctx, flags);
209 
210     if (flags & SP_OBJECT_MODIFIED_FLAG) {
211         flags |= SP_OBJECT_PARENT_MODIFIED_FLAG;
212     }
213 
214     flags &= SP_OBJECT_MODIFIED_CASCADE;
215 
216     std::vector<SPObject *> l;
217 
218     for (auto& child: children) {
219         sp_object_ref(&child);
220         l.push_back(&child);
221     }
222 
223     for(auto child:l) {
224         g_assert(child != nullptr);
225 
226         if (flags || (child->uflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) {
227             SPItem *item = dynamic_cast<SPItem *>(child);
228             if (item) {
229                 SPItem const &chi = *item;
230                 cctx.i2doc = chi.transform * ictx->i2doc;
231                 cctx.i2vp = chi.transform * ictx->i2vp;
232                 child->updateDisplay((SPCtx *)&cctx, flags);
233             } else {
234                 child->updateDisplay(ctx, flags);
235             }
236         }
237 
238         sp_object_unref(child);
239     }
240 
241     this->UpdateComputed();
242 }
243 
244 
UpdateComputed()245 void SPFlowregionExclude::UpdateComputed()
246 {
247     if (computed) {
248         delete computed;
249         computed = nullptr;
250     }
251 
252     for (auto& child: children) {
253         GetDest(&child, &computed);
254     }
255 }
256 
modified(guint flags)257 void SPFlowregionExclude::modified(guint flags) {
258     if (flags & SP_OBJECT_MODIFIED_FLAG) {
259         flags |= SP_OBJECT_PARENT_MODIFIED_FLAG;
260     }
261 
262     flags &= SP_OBJECT_MODIFIED_CASCADE;
263 
264     std::vector<SPObject*> l;
265 
266     for (auto& child: children) {
267         sp_object_ref(&child);
268         l.push_back(&child);
269     }
270 
271     for (auto child:l) {
272         g_assert(child != nullptr);
273 
274         if (flags || (child->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) {
275             child->emitModified(flags);
276         }
277 
278         sp_object_unref(child);
279     }
280 }
281 
write(Inkscape::XML::Document * xml_doc,Inkscape::XML::Node * repr,guint flags)282 Inkscape::XML::Node *SPFlowregionExclude::write(Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, guint flags) {
283     if (flags & SP_OBJECT_WRITE_BUILD) {
284         if ( repr == nullptr ) {
285             repr = xml_doc->createElement("svg:flowRegionExclude");
286         }
287 
288         std::vector<Inkscape::XML::Node *> l;
289 
290         for (auto& child: children) {
291             Inkscape::XML::Node *crepr = child.updateRepr(xml_doc, nullptr, flags);
292 
293             if (crepr) {
294                 l.push_back(crepr);
295             }
296         }
297 
298         for (auto i = l.rbegin(); i != l.rend(); ++i) {
299             repr->addChild(*i, nullptr);
300             Inkscape::GC::release(*i);
301         }
302 
303     } else {
304         for (auto& child: children) {
305             child.updateRepr(flags);
306         }
307     }
308 
309     SPItem::write(xml_doc, repr, flags);
310 
311     return repr;
312 }
313 
displayName() const314 const char* SPFlowregionExclude::displayName() const {
315 	/* TRANSLATORS: A region "cut out of" a flow region; text is not allowed to flow inside the
316 	 * flow excluded region.  flowRegionExclude in SVG 1.2: see
317 	 * http://www.w3.org/TR/2004/WD-SVG12-20041027/flow.html#flowRegion-elem and
318 	 * http://www.w3.org/TR/2004/WD-SVG12-20041027/flow.html#flowRegionExclude-elem. */
319 	return _("Flow Excluded Region");
320 }
321 
UnionShape(Shape * & base_shape,Shape const * add_shape)322 static void UnionShape(Shape *&base_shape, Shape const *add_shape)
323 {
324     if (base_shape == nullptr)
325         base_shape = new Shape;
326 
327     if (base_shape->hasEdges() == false) {
328         base_shape->Copy(const_cast<Shape *>(add_shape));
329     } else if (add_shape->hasEdges()) {
330         Shape *temp = new Shape;
331         temp->Booleen(const_cast<Shape *>(add_shape), base_shape, bool_op_union);
332         delete base_shape;
333         base_shape = temp;
334     }
335 }
336 
GetDest(SPObject * child,Shape ** computed)337 static void         GetDest(SPObject* child,Shape **computed)
338 {
339     auto item = dynamic_cast<SPItem *>(child);
340     if (item == nullptr)
341         return;
342 
343     std::unique_ptr<SPCurve> curve;
344     Geom::Affine tr_mat;
345 
346     SPObject* u_child = child;
347     SPUse *use = dynamic_cast<SPUse *>(item);
348     if ( use ) {
349         u_child = use->child;
350         tr_mat = use->getRelativeTransform(child->parent);
351     } else {
352         tr_mat = item->transform;
353     }
354     SPShape *shape = dynamic_cast<SPShape *>(u_child);
355     if ( shape ) {
356         if (!shape->curve()) {
357             shape->set_shape();
358         }
359         curve = SPCurve::copy(shape->curve());
360     } else {
361         SPText *text = dynamic_cast<SPText *>(u_child);
362         if ( text ) {
363             curve = text->getNormalizedBpath();
364         }
365     }
366 
367 	if ( curve ) {
368 		Path*   temp=new Path;
369         temp->LoadPathVector(curve->get_pathvector(), tr_mat, true);
370 		Shape*  n_shp=new Shape;
371 		temp->Convert(0.25);
372 		temp->Fill(n_shp,0);
373 		Shape*  uncross=new Shape;
374 		SPStyle* style = u_child->style;
375 		if ( style && style->fill_rule.computed == SP_WIND_RULE_EVENODD ) {
376 			uncross->ConvertToShape(n_shp,fill_oddEven);
377 		} else {
378 			uncross->ConvertToShape(n_shp,fill_nonZero);
379 		}
380 		UnionShape(*computed, uncross);
381 		delete uncross;
382 		delete n_shp;
383 		delete temp;
384 	}
385 }
386 
387 /*
388   Local Variables:
389   mode:c++
390   c-file-style:"stroustrup"
391   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
392   indent-tabs-mode:nil
393   fill-column:99
394   End:
395 */
396 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
397