Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
path-outline.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2
19#include "path-outline.h"
20
21#include <vector>
22
23#include "document.h"
24#include "path-chemistry.h" // Should be moved to path directory
25#include "message-stack.h" // Should be removed.
26#include "selection.h"
27#include "style.h"
28
29#include "display/curve.h" // Should be moved to path directory
30
31#include "helper/geom.h" // pathv_to_linear_and_cubic()
32
33#include "livarot/LivarotDefs.h"
34#include "livarot/Path.h"
35#include "livarot/Shape.h"
36
37#include "object/object-set.h"
38#include "object/box3d.h"
39#include "object/sp-item.h"
40#include "object/sp-marker.h"
41#include "object/sp-shape.h"
42#include "object/sp-text.h"
43#include "object/sp-flowtext.h"
44
45#include "svg/svg.h"
46
53bool
54item_find_paths(const SPItem *item, Geom::PathVector& fill, Geom::PathVector& stroke, bool bbox_only)
55{
56 auto shape = cast<SPShape>(item);
57 auto text = cast<SPText>(item);
58
59 if (!shape && !text) {
60 return false;
61 }
62
63 std::optional<SPCurve> curve;
64 if (shape) {
65 curve = SPCurve::ptr_to_opt(shape->curve());
66 } else if (text) {
67 curve = text->getNormalizedBpath();
68 } else {
69 std::cerr << "item_find_paths: item not shape or text!" << std::endl;
70 return false;
71 }
72
73 if (!curve) {
74 std::cerr << "item_find_paths: no curve!" << std::endl;
75 return false;
76 }
77
78 if (curve->get_pathvector().empty()) {
79 std::cerr << "item_find_paths: curve empty!" << std::endl;
80 return false;
81 }
82
83 fill = curve->get_pathvector();
84
85 if (!item->style) {
86 // Should never happen
87 std::cerr << "item_find_paths: item with no style!" << std::endl;
88 return false;
89 }
90
91 if (item->style->stroke.isNone() || item->style->stroke_width.computed <= Geom::EPSILON) {
92 // No stroke, no chocolate!
93 return true;
94 }
95
96 // Now that we have a valid curve with stroke, do offset. We use Livarot for this as
97 // lib2geom does not yet handle offsets correctly.
98
99 // Livarot's outline of arcs is broken. So convert the path to linear and cubics only, for
100 // which the outline is created correctly.
102
103 SPStyle *style = item->style;
104
105 double stroke_width = style->stroke_width.computed;
106 double miter = style->stroke_miterlimit.value * stroke_width;
107
108 JoinType join;
109 switch (style->stroke_linejoin.computed) {
111 join = join_pointy;
112 break;
114 join = join_round;
115 break;
116 default:
117 join = join_straight;
118 break;
119 }
120
121 ButtType butt;
122 switch (style->stroke_linecap.computed) {
124 butt = butt_square;
125 break;
127 butt = butt_round;
128 break;
129 default:
130 butt = butt_straight;
131 break;
132 }
133
134 Path *origin = new Path; // Fill
135 Path *offset = new Path;
136
137 Geom::Affine const transform(item->transform);
138 double const scale = transform.descrim();
139
140 origin->LoadPathVector(pathv);
141 offset->SetBackData(false);
142
143 if (!style->stroke_dasharray.values.empty() && style->stroke_dasharray.is_valid()) {
144 // We have dashes!
145 origin->ConvertWithBackData(0.005); // Approximate by polyline
146 origin->DashPolylineFromStyle(style, scale, 0);
147 auto bounds = Geom::bounds_fast(pathv);
148 if (bounds) {
149 double size = Geom::L2(bounds->dimensions());
150 origin->Simplify(size * 0.000005); // Polylines to Beziers
151 }
152 }
153
154 // Finally do offset!
155 origin->Outline(offset, 0.5 * stroke_width, join, butt, 0.5 * miter);
156
157 if (bbox_only) {
158 stroke = offset->MakePathVector();
159 } else {
160 // Clean-up shape
161
162 offset->ConvertWithBackData(1.0); // Approximate by polyline
163
164 Shape *theShape = new Shape;
165 offset->Fill(theShape, 0); // Convert polyline to shape, step 1.
166
167 Shape *theOffset = new Shape;
168 theOffset->ConvertToShape(theShape, fill_positive); // Create an intersection free polygon (theOffset), step2.
169 theOffset->ConvertToForme(origin, 1, &offset); // Turn shape into contour (stored in origin).
170
171 stroke = origin->MakePathVector(); // Note origin was replaced above by stroke!
172 }
173
174 delete origin;
175 delete offset;
176
177 // std::cout << " fill: " << sp_svg_write_path(fill) << " count: " << fill.curveCount() << std::endl;
178 // std::cout << " stroke: " << sp_svg_write_path(stroke) << " count: " << stroke.curveCount() << std::endl;
179 return true;
180}
181
182
183// ======================== Item to Outline ===================== //
184
185static
187{
188 Geom::Affine tr(marker_transform);
189 tr = item->transform * tr;
190
191 // note: a marker child item can be an item group!
192 if (is<SPGroup>(item)) {
193 // recurse through all childs:
194 for (auto& o: item->children) {
195 if (auto childitem = cast<SPItem>(&o)) {
196 item_to_outline_add_marker_child(childitem, tr, pathv_in);
197 }
198 }
199 } else {
200 Geom::PathVector* marker_pathv = item_to_outline(item);
201
202 if (marker_pathv) {
203 for (const auto & j : *marker_pathv) {
204 pathv_in->push_back(j * tr);
205 }
206 delete marker_pathv;
207 }
208 }
209}
210
218Geom::PathVector* item_to_outline(SPItem const *item, bool exclude_markers)
219{
220 Geom::PathVector fill; // Used for locating markers.
221 Geom::PathVector stroke; // Used for creating outline (and finding bbox).
222 item_find_paths(item, fill, stroke, true); // Skip cleaning up stroke shape.
223
224 Geom::PathVector *ret_pathv = nullptr;
225
226 if (fill.curveCount() == 0) {
227 std::cerr << "item_to_outline: fill path has no segments!" << std::endl;
228 return ret_pathv;
229 }
230
231 if (stroke.size() > 0) {
232 ret_pathv = new Geom::PathVector(stroke);
233 } else {
234 // No stroke, use fill path.
235 ret_pathv = new Geom::PathVector(fill);
236 }
237
238 if (exclude_markers) {
239 return ret_pathv;
240 }
241
242 auto shape = cast<SPShape>(item);
243 if (shape && shape->hasMarkers()) {
244 for (auto const &[_, marker, tr] : shape->get_markers()) {
245 if (auto const marker_item = sp_item_first_item_child(marker)) {
246 item_to_outline_add_marker_child(marker_item, marker->c2p * tr, ret_pathv);
247 }
248 }
249 }
250
251 return ret_pathv;
252}
253
254// ========================= Stroke to Path ====================== //
255static void item_to_paths_add_marker(SPItem *context, SPMarker const *marker, Geom::Affine const &marker_transform,
256 Inkscape::XML::Node *g_repr, bool legacy)
257{
258 auto doc = context->document;
259 for (auto &obj : marker->children) {
260 if (auto item = cast<SPItem>(&obj)) {
261 // NOTE: The SVG spec says that a <marker> cannot have a transform attribute, even if it's set, it should be ignored.
262 // The SPMarker in Inkscape inherits from SPGroup so it does allow a transform, even though it shouldn't.
263 auto const tr = item->transform * marker_transform;
264
265 Inkscape::XML::Node *m_repr = obj.getRepr()->duplicate(doc->getReprDoc());
266 g_repr->appendChild(m_repr);
267
268 if (auto m_item = cast<SPItem>(doc->getObjectByRepr(m_repr))) {
269 m_item->doWriteTransform(tr);
270 if (!legacy) {
271 item_to_paths(m_item, legacy, context);
272 }
273 }
274 }
275 }
276}
277
278/*
279 * Find an outline that represents an item.
280 * If legacy, text will not be handled as it is not a shape.
281 * If a new item is created it is returned.
282 * If the input item is a group and that group contains a changed item, the group node is returned
283 * (marking a change).
284 *
285 * The return value is used externally to update a selection. It is nullptr if no change is made.
286 */
288item_to_paths(SPItem *item, bool legacy, SPItem *context)
289{
290 char const *id = item->getAttribute("id");
291 SPDocument *doc = item->document;
292 bool flatten = false;
293 // flatten all paths effects
294 auto lpeitem = cast<SPLPEItem>(item);
295 if (lpeitem && lpeitem->hasPathEffect()) {
296 lpeitem->removeAllPathEffects(true);
297 SPObject *elemref = doc->getObjectById(id);
298 if (elemref && elemref != item) {
299 // If the LPE item is a shape, it is converted to a path
300 // so we need to reupdate the item
301 item = cast<SPItem>(elemref);
302 }
303 auto flat_item = cast<SPLPEItem>(elemref);
304 if (!flat_item || !flat_item->hasPathEffect()) {
305 flatten = true;
306 }
307 }
308 // convert text/3dbox to path
309 if (is<SPText>(item) || is<SPFlowtext>(item) || is<SPBox3D>(item)) {
310 if (legacy) {
311 return nullptr;
312 }
313
314 Inkscape::ObjectSet original_objects {doc}; // doc or desktop shouldn't be necessary
315 original_objects.add(item);
316 original_objects.toCurves(true);
317 SPItem * new_item = original_objects.singleItem();
318 if (new_item && new_item != item) {
319 flatten = true;
320 item = new_item;
321 } else {
322 g_warning("item_to_paths: flattening text or 3D box failed.");
323 return nullptr;
324 }
325 }
326 // if group, recurse
327 auto group = cast<SPGroup>(item);
328 if (group) {
329 if (legacy) {
330 return nullptr;
331 }
332 std::vector<SPItem*> const item_list = group->item_list();
333 bool did = false;
334 for (auto subitem : item_list) {
335 if (item_to_paths(subitem, legacy)) {
336 did = true;
337 }
338 }
339 if (did || flatten) {
340 // This indicates that at least one thing was changed inside the group.
341 return group->getRepr();
342 } else {
343 return nullptr;
344 }
345 }
346
347 auto shape = cast<SPShape>(item);
348 if (!shape) {
349 return nullptr;
350 }
351
352 Geom::PathVector fill_path;
353 Geom::PathVector stroke_path;
354 bool status = item_find_paths(item, fill_path, stroke_path);
355
356 if (!status) {
357 // Was not a well structured shape (or text).
358 return nullptr;
359 }
360
361 // The styles ------------------------
362
363 // Copying stroke style to fill will fail for properties not defined by style attribute
364 // (i.e., properties defined in style sheet or by attributes).
365 SPStyle *style = item->style;
368
369 if (context) {
371
372 // TODO: browsers have different behaviours with context on markers
373 // we need to revisit in the future for best matching
374 // also dont know if opacity is or should be included in context
375 gchar const *s_val = sp_repr_css_property(ctxt_style, "stroke", nullptr);
376 gchar const *f_val = sp_repr_css_property(ctxt_style, "fill", nullptr);
377 if (style->fill.paintOrigin == SP_CSS_PAINT_ORIGIN_CONTEXT_STROKE ||
378 style->fill.paintOrigin == SP_CSS_PAINT_ORIGIN_CONTEXT_FILL)
379 {
380 gchar const *fill_value = (style->fill.paintOrigin == SP_CSS_PAINT_ORIGIN_CONTEXT_STROKE) ? s_val : f_val;
381 sp_repr_css_set_property(ncss, "fill", fill_value);
382 sp_repr_css_set_property(ncsf, "fill", fill_value);
383 }
384 if (style->stroke.paintOrigin == SP_CSS_PAINT_ORIGIN_CONTEXT_STROKE ||
385 style->stroke.paintOrigin == SP_CSS_PAINT_ORIGIN_CONTEXT_FILL)
386 {
387 gchar const *stroke_value = (style->stroke.paintOrigin == SP_CSS_PAINT_ORIGIN_CONTEXT_FILL) ? f_val : s_val;
388 sp_repr_css_set_property(ncss, "stroke", stroke_value);
389 sp_repr_css_set_property(ncsf, "stroke", stroke_value);
390 }
391 }
392 // Stroke
393
394 gchar const *s_val = sp_repr_css_property(ncss, "stroke", nullptr);
395 gchar const *s_opac = sp_repr_css_property(ncss, "stroke-opacity", nullptr);
396 gchar const *f_val = sp_repr_css_property(ncss, "fill", nullptr);
397 gchar const *opacity = sp_repr_css_property(ncss, "opacity", nullptr); // Also for markers
398 gchar const *filter = sp_repr_css_property(ncss, "filter", nullptr); // Also for markers
399
400 sp_repr_css_set_property(ncss, "stroke", "none");
401 sp_repr_css_set_property(ncss, "stroke-width", nullptr);
402 sp_repr_css_set_property(ncss, "stroke-opacity", "1.0");
403 sp_repr_css_set_property(ncss, "filter", nullptr);
404 sp_repr_css_set_property(ncss, "opacity", nullptr);
405 sp_repr_css_unset_property(ncss, "marker-start");
406 sp_repr_css_unset_property(ncss, "marker-mid");
407 sp_repr_css_unset_property(ncss, "marker-end");
408
409 // we change the stroke to fill on ncss to create the filled stroke
410 sp_repr_css_set_property(ncss, "fill", s_val);
411 if ( s_opac ) {
412 sp_repr_css_set_property(ncss, "fill-opacity", s_opac);
413 } else {
414 sp_repr_css_set_property(ncss, "fill-opacity", "1.0");
415 }
416
417 sp_repr_css_set_property(ncsf, "stroke", "none");
418 sp_repr_css_set_property(ncsf, "stroke-width", nullptr);
419 sp_repr_css_set_property(ncsf, "stroke-opacity", "1.0");
420 sp_repr_css_set_property(ncsf, "filter", nullptr);
421 sp_repr_css_set_property(ncsf, "opacity", nullptr);
422 sp_repr_css_unset_property(ncsf, "marker-start");
423 sp_repr_css_unset_property(ncsf, "marker-mid");
424 sp_repr_css_unset_property(ncsf, "marker-end");
425
426 // The object tree -------------------
427
428 // Remember the position of the item
429 gint pos = item->getRepr()->position();
430
431 // Remember parent
433
434 Inkscape::XML::Document *xml_doc = doc->getReprDoc();
435
436 // Create a group to put everything in.
437 Inkscape::XML::Node *g_repr = xml_doc->createElement("svg:g");
438
440 // drop copied style, children will be re-styled (stroke becomes fill)
441 g_repr->removeAttribute("style");
442
443 // Add the group to the parent, move to the saved position
444 parent->addChildAtPos(g_repr, pos);
445
446 // The stroke ------------------------
447 Inkscape::XML::Node *stroke = nullptr;
448 if (s_val && g_strcmp0(s_val,"none") != 0 && stroke_path.size() > 0) {
449 auto stroke_style = std::make_unique<SPStyle>(doc);
450 stroke_style->mergeCSS(ncss);
451
452 stroke = xml_doc->createElement("svg:path");
453 stroke->setAttribute("style", stroke_style->writeIfDiff(item->parent->style));
454 stroke->setAttribute("d", sp_svg_write_path(stroke_path));
455 }
457
458 // The fill --------------------------
459 Inkscape::XML::Node *fill = nullptr;
460 if (f_val && g_strcmp0(f_val,"none") != 0 && !legacy) {
461 auto fill_style = std::make_unique<SPStyle>(doc);
462 fill_style->mergeCSS(ncsf);
463
464 fill = xml_doc->createElement("svg:path");
465 fill->setAttribute("style", fill_style->writeIfDiff(item->parent->style));
466 fill->setAttribute("d", sp_svg_write_path(fill_path));
467 }
469
470 // The markers -----------------------
471 Inkscape::XML::Node *markers = nullptr;
472
473 if (shape->hasMarkers()) {
474 if (!legacy) {
475 markers = xml_doc->createElement("svg:g");
476 g_repr->addChildAtPos(markers, pos);
477 } else {
478 markers = g_repr;
479 }
480
481 for (auto const &[_, marker, tr] : shape->get_markers()) {
482 item_to_paths_add_marker(item, marker, marker->c2p * tr, markers, legacy);
483 }
484 }
485
486 gchar const *paint_order = sp_repr_css_property(ncss, "paint-order", nullptr);
487 SPIPaintOrder temp;
488 temp.read( paint_order );
489 bool unique = false;
490 if ((!fill && !markers) || (!fill && !stroke) || (!markers && !stroke)) {
491 unique = true;
492 }
493 if (temp.layer[0] != SP_CSS_PAINT_ORDER_NORMAL && !legacy && !unique) {
494
495 if (temp.layer[0] == SP_CSS_PAINT_ORDER_FILL) {
496 if (temp.layer[1] == SP_CSS_PAINT_ORDER_STROKE) {
497 if ( fill ) {
498 g_repr->appendChild(fill);
499 }
500 if ( stroke ) {
501 g_repr->appendChild(stroke);
502 }
503 if ( markers ) {
504 markers->setPosition(2);
505 }
506 } else {
507 if ( fill ) {
508 g_repr->appendChild(fill);
509 }
510 if ( markers ) {
511 markers->setPosition(1);
512 }
513 if ( stroke ) {
514 g_repr->appendChild(stroke);
515 }
516 }
517 } else if (temp.layer[0] == SP_CSS_PAINT_ORDER_STROKE) {
518 if (temp.layer[1] == SP_CSS_PAINT_ORDER_FILL) {
519 if ( stroke ) {
520 g_repr->appendChild(stroke);
521 }
522 if ( fill ) {
523 g_repr->appendChild(fill);
524 }
525 if ( markers ) {
526 markers->setPosition(2);
527 }
528 } else {
529 if ( stroke ) {
530 g_repr->appendChild(stroke);
531 }
532 if ( markers ) {
533 markers->setPosition(1);
534 }
535 if ( fill ) {
536 g_repr->appendChild(fill);
537 }
538 }
539 } else {
540 if (temp.layer[1] == SP_CSS_PAINT_ORDER_STROKE) {
541 if ( markers ) {
542 markers->setPosition(0);
543 }
544 if ( stroke ) {
545 g_repr->appendChild(stroke);
546 }
547 if ( fill ) {
548 g_repr->appendChild(fill);
549 }
550 } else {
551 if ( markers ) {
552 markers->setPosition(0);
553 }
554 if ( fill ) {
555 g_repr->appendChild(fill);
556 }
557 if ( stroke ) {
558 g_repr->appendChild(stroke);
559 }
560 }
561 }
562
563 } else if (!unique) {
564 if ( fill ) {
565 g_repr->appendChild(fill);
566 }
567 if ( stroke ) {
568 g_repr->appendChild(stroke);
569 }
570 if ( markers ) {
571 markers->setPosition(2);
572 }
573 }
574
575 bool did = false;
576 // only consider it a change if more than a fill is created.
577 if (stroke || markers) {
578 did = true;
579 }
580
581 Inkscape::XML::Node *out = nullptr;
582
583 if (!fill && !markers && did) {
584 out = stroke;
585 } else if (!fill && !stroke && did) {
586 out = markers;
587 } else if(did) {
588 out = g_repr;
589 } else {
590 parent->removeChild(g_repr);
591 Inkscape::GC::release(g_repr);
592 if (fill) {
593 // Copy the style, to preserve context-fill cascade
594 if (context) {
595 item->setAttribute("style", fill->attribute("style"));
596 }
598 }
599 return (flatten ? item->getRepr() : nullptr);
600 }
601
602 SPCSSAttr *r_style = sp_repr_css_attr_new();
603 sp_repr_css_set_property(r_style, "opacity", opacity);
604 sp_repr_css_set_property(r_style, "filter", filter);
605 sp_repr_css_change(out, r_style, "style");
606
607 sp_repr_css_attr_unref(r_style);
608 if (unique && out != markers) { // markers are already a child of g_repr
609 g_assert(out != g_repr);
610 parent->addChild(out, g_repr);
611 parent->removeChild(g_repr);
612 Inkscape::GC::release(g_repr);
613 }
614 out->setAttribute("transform", item->getRepr()->attribute("transform"));
615
616 // We're replacing item, delete it.
617 item->deleteObject(false);
618
619 out->setAttribute("id",id);
621
622 return out;
623}
624
625/*
626 Local Variables:
627 mode:c++
628 c-file-style:"stroustrup"
629 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
630 indent-tabs-mode:nil
631 fill-column:99
632 End:
633*/
634// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
TODO: insert short description here.
ButtType
Definition LivarotDefs.h:51
@ butt_round
Definition LivarotDefs.h:54
@ butt_straight
Definition LivarotDefs.h:52
@ butt_square
Definition LivarotDefs.h:53
@ fill_positive
Definition LivarotDefs.h:70
JoinType
Definition LivarotDefs.h:60
@ join_round
Definition LivarotDefs.h:62
@ join_pointy
Definition LivarotDefs.h:63
@ join_straight
Definition LivarotDefs.h:61
TODO: insert short description here.
TODO: insert short description here.
double scale
Definition aa.cpp:228
Point origin
Definition aa.cpp:227
Geom::IntRect bounds
Definition canvas.cpp:182
3x3 matrix representing an affine transformation.
Definition affine.h:70
Coord descrim() const
Calculate the descriminant.
Definition affine.cpp:434
CPoint dimensions() const
Get rectangle's width and height as a point.
Sequence of subpaths.
Definition pathvector.h:122
size_type size() const
Get the number of paths in the vector.
Definition pathvector.h:147
void push_back(Path const &path)
Append a path at the end.
Definition pathvector.h:172
size_type curveCount() const
Get the total number of curves in the vector.
Interface for refcounted XML nodes.
Definition node.h:80
virtual Node * parent()=0
Get the parent of this node.
virtual void setPosition(int pos)=0
Set the position of this node in parent's child order.
virtual void appendChild(Node *child)=0
Append a node as the last child of this node.
void addChildAtPos(Node *child, unsigned pos)
Insert another node as a child of this node.
Definition node.h:430
void setAttribute(Util::const_char_ptr key, Util::const_char_ptr value)
Change an attribute of this node.
Definition node.cpp:25
virtual Node * duplicate(Document *doc) const =0
Create a duplicate of this node.
virtual unsigned position() const =0
Get the index of this node in parent's child order.
virtual char const * attribute(char const *key) const =0
Get the string representation of a node's attribute.
void removeAttribute(Inkscape::Util::const_char_ptr key)
Remove an attribute of this node.
Definition node.h:280
virtual void removeChild(Node *child)=0
Remove a child of this node.
Path and its polyline approximation.
Definition Path.h:93
static std::optional< SPCurve > ptr_to_opt(T const &p)
Definition curve.h:83
Typed SVG document implementation.
Definition document.h:103
SPObject * getObjectById(std::string const &id) const
Inkscape::XML::Document * getReprDoc()
Our Inkscape::XML::Document.
Definition document.h:213
Paint order type internal to SPStyle.
void read(gchar const *str) override
SPPaintOrderLayer layer[PAINT_ORDER_LAYERS]
Base class for visual SVG elements.
Definition sp-item.h:109
Geom::Affine transform
Definition sp-item.h:138
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition sp-object.h:160
void setAttribute(Inkscape::Util::const_char_ptr key, Inkscape::Util::const_char_ptr value)
SPDocument * document
Definition sp-object.h:188
SPStyle * style
Represents the style properties, whether from presentation attributes, the style attribute,...
Definition sp-object.h:248
SPObject * parent
Definition sp-object.h:189
void deleteObject(bool propagate, bool propagate_descendants)
Deletes an object, unparenting it from its parent.
Inkscape::XML::Node * getRepr()
Returns the XML representation of tree.
char const * getAttribute(char const *name) const
ChildrenList children
Definition sp-object.h:907
An SVG style object.
Definition style.h:45
T< SPAttr::FILL, SPIPaint > fill
fill
Definition style.h:240
T< SPAttr::STROKE_DASHARRAY, SPIDashArray > stroke_dasharray
stroke-dasharray
Definition style.h:257
T< SPAttr::STROKE, SPIPaint > stroke
stroke
Definition style.h:247
T< SPAttr::STROKE_WIDTH, SPILength > stroke_width
stroke-width
Definition style.h:249
T< SPAttr::STROKE_LINEJOIN, SPIEnum< SPStrokeJoinType > > stroke_linejoin
stroke-linejoin
Definition style.h:253
T< SPAttr::STROKE_MITERLIMIT, SPIFloat > stroke_miterlimit
stroke-miterlimit
Definition style.h:255
T< SPAttr::STROKE_LINECAP, SPIEnum< SPStrokeCapType > > stroke_linecap
stroke-linecap
Definition style.h:251
A class to store/manipulate directed graphs.
Definition Shape.h:65
void ConvertToForme(Path *dest)
Extract contours from a directed graph.
Definition ShapeMisc.cpp:42
int ConvertToShape(Shape *a, FillRule directed=fill_nonZero, bool invert=false)
Using a given fill rule, find all intersections in the shape given, create a new intersection free sh...
static char const *const parent
Definition dir-util.cpp:70
constexpr Coord EPSILON
Default "acceptably small" value.
Definition coord.h:84
Geom::PathVector pathv_to_linear_and_cubic_beziers(Geom::PathVector const &pathv)
Definition geom.cpp:588
Specific geometry functions for Inkscape, not provided my lib2geom.
SPItem * item
double offset
Raw stack of active status messages.
SBasis L2(D2< SBasis > const &a, unsigned k)
Definition d2-sbasis.cpp:42
OptInterval bounds_fast(Bezier const &b)
Definition bezier.cpp:305
static R & release(R &r)
Decrements the reference count of a anchored object.
void copy_object_properties(XML::Node *dest, XML::Node const *src)
Copy generic object properties, like:
void flatten(Geom::PathVector &pathv, FillRule fill_rule)
bool item_find_paths(const SPItem *item, Geom::PathVector &fill, Geom::PathVector &stroke, bool bbox_only)
Given an item, find a path representing the fill and a path representing the stroke.
static void item_to_outline_add_marker_child(SPItem const *item, Geom::Affine marker_transform, Geom::PathVector *pathv_in)
Inkscape::XML::Node * item_to_paths(SPItem *item, bool legacy, SPItem *context)
Replace item by path objects (a.k.a.
Geom::PathVector * item_to_outline(SPItem const *item, bool exclude_markers)
Returns a pathvector that is the outline of the stroked item, with markers.
static void item_to_paths_add_marker(SPItem *context, SPMarker const *marker, Geom::Affine const &marker_transform, Inkscape::XML::Node *g_repr, bool legacy)
Two related object to path operations:
int size
SPCSSAttr * sp_repr_css_attr_new()
Creates an empty SPCSSAttr (a class for manipulating CSS style properties).
Definition repr-css.cpp:67
void sp_repr_css_change(Node *repr, SPCSSAttr *css, gchar const *attr)
Creates a new SPCSAttr with the values filled from a repr, merges in properties from the given SPCSAt...
Definition repr-css.cpp:358
void sp_repr_css_attr_unref(SPCSSAttr *css)
Unreferences an SPCSSAttr (will be garbage collected if no references remain).
Definition repr-css.cpp:76
char const * sp_repr_css_property(SPCSSAttr *css, gchar const *name, gchar const *defval)
Returns a character string of the value of a given style property or a default value if the attribute...
Definition repr-css.cpp:147
void sp_repr_css_unset_property(SPCSSAttr *css, gchar const *name)
Set a style property to "inkscape:unset".
Definition repr-css.cpp:202
void sp_repr_css_set_property(SPCSSAttr *css, gchar const *name, gchar const *value)
Set a style property to a new value (e.g.
Definition repr-css.cpp:191
TODO: insert short description here.
SPItem const * sp_item_first_item_child(SPObject const *obj)
Definition sp-item.cpp:1869
Some things pertinent to all visible shapes: SPItem, SPItemView, SPItemCtx.
Interface for XML documents.
Definition document.h:43
virtual Node * createElement(char const *name)=0
Definition curve.h:24
@ SP_STROKE_LINEJOIN_MITER
Definition style-enums.h:34
@ SP_STROKE_LINEJOIN_ROUND
Definition style-enums.h:35
@ SP_STROKE_LINECAP_SQUARE
Definition style-enums.h:43
@ SP_STROKE_LINECAP_ROUND
Definition style-enums.h:42
@ SP_CSS_PAINT_ORIGIN_CONTEXT_STROKE
@ SP_CSS_PAINT_ORIGIN_CONTEXT_FILL
@ SP_CSS_PAINT_ORDER_STROKE
@ SP_CSS_PAINT_ORDER_FILL
@ SP_CSS_PAINT_ORDER_NORMAL
static const unsigned SP_STYLE_FLAG_ALWAYS(1<< 2)
SPCSSAttr * sp_css_attr_from_style(SPStyle const *const style, guint const flags)
Definition style.cpp:1409
SPStyle - a style object for SPItem objects.
static void sp_svg_write_path(Inkscape::SVG::PathString &str, Geom::Path const &p, bool normalize=false)
Definition svg-path.cpp:109