Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
actions-svg-processing.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Gio::Actions for pre-processing svg, used in extensions
4 *
5 * Copyright (C) 2002-2023 Authors
6 *
7 * The contents of this file may be used under the GNU General Public License Version 2 or later.
8 *
9 */
10
12
13#include <iostream>
14
15#include <giomm.h>
16#include <glibmm/i18n.h>
17
18#include "actions-helper.h"
19
20
21#include "document.h"
23#include "inkscape-version.h"
24#include "style.h"
25
26#include "object/object-set.h"
27#include "object/sp-defs.h"
28#include "object/sp-image.h"
29#include "object/sp-root.h"
30#include "object/sp-text.h"
31#include "path-chemistry.h"
32#include "path/path-outline.h"
33#include "svg/svg-box.h"
34#include "svg/svg.h"
36#include "xml/node.h"
37
38/*
39 * Removes all sodipodi and inkscape elements and attributes from an xml tree.
40 * used to make plain svg output.
41 */
43{
44 if (repr) {
46 std::vector<gchar const*> attrsRemoved;
47 for ( const auto & it : repr->attributeList()) {
48 const gchar* attrName = g_quark_to_string(it.key);
49 if ((strncmp("inkscape:", attrName, 9) == 0) || (strncmp("sodipodi:", attrName, 9) == 0)) {
50 attrsRemoved.push_back(attrName);
51 }
52 }
53 // Can't change the set we're iterating over while we are iterating.
54 for (auto & it : attrsRemoved) {
55 repr->removeAttribute(it);
56 }
57 }
58
59 std::vector<Inkscape::XML::Node *> nodesRemoved;
60 for (auto child = repr->firstChild(); child; child = child->next()) {
61 if((strncmp("inkscape:", child->name(), 9) == 0) || strncmp("sodipodi:", child->name(), 9) == 0) {
62 nodesRemoved.push_back(child);
63 } else {
65 }
66 }
67 for (auto & it : nodesRemoved) {
68 repr->removeChild(it);
69 }
70 }
71}
72
73/*
74 * Similar to the above prune, but used on all documents to remove problematic elements
75 * for example Adobe's i:pgf tag; only removes known garbage tags.
76 */
78{
79 if (repr) {
80 std::vector<Inkscape::XML::Node *> nodesRemoved;
81 for (auto child = repr->firstChild(); child; child = child->next() ) {
82 if((strncmp("i:pgf", child->name(), 5) == 0)) {
83 nodesRemoved.push_back(child);
84 g_warning( "An Adobe proprietary tag was found which is known to cause issues. It was removed before saving.");
85 } else {
87 }
88 }
89 for (auto & it : nodesRemoved) {
90 repr->removeChild(it);
91 }
92 }
93}
94
108 Glib::ustring const &property)
109{
110 for (auto child = repr->firstChild(); child; child = child->next() ) {
112 }
113
114 SPCSSAttr* css = sp_repr_css_attr (repr, "style");
115 Glib::ustring value = sp_repr_css_property (css, property.c_str(), "");
116
117 if (value.empty())
118 return;
119
120 // Find reference <marker>
121 static Glib::RefPtr<Glib::Regex> regex = Glib::Regex::create("url\\(#([^\\)]*)\\)");
122 Glib::MatchInfo matchInfo;
123 regex->match(value, matchInfo);
124
125 if (matchInfo.matches()) {
126
127 auto marker_name = matchInfo.fetch(1).raw();
128 Inkscape::XML::Node *marker = sp_repr_lookup_child (defs, "id", marker_name.c_str());
129 if (marker) {
130
131 // Does marker use "auto-start-reverse"?
132 if (strncmp(marker->attribute("orient"), "auto-start-reverse", 17)==0) {
133
134 // See if a reversed marker already exists.
135 auto marker_name_reversed = marker_name + "_reversed";
136 Inkscape::XML::Node *marker_reversed =
137 sp_repr_lookup_child (defs, "id", marker_name_reversed.c_str());
138
139 if (!marker_reversed) {
140
141 // No reversed marker, need to create!
142 marker_reversed = repr->document()->createElement("svg:marker");
143
144 // Copy attributes
145 for (const auto & iter : marker->attributeList()) {
146 marker_reversed->setAttribute(g_quark_to_string(iter.key), iter.value);
147 }
148
149 // Override attributes
150 marker_reversed->setAttribute("id", marker_name_reversed);
151 marker_reversed->setAttribute("orient", "auto");
152
153 // Find transform
154 const char* refX = marker_reversed->attribute("refX");
155 const char* refY = marker_reversed->attribute("refY");
156 std::string transform = "rotate(180";
157 if (refX) {
158 transform += ",";
159 transform += refX;
160
161 if (refY) {
162 if (refX) {
163 transform += ",";
164 transform += refY;
165 } else {
166 transform += ",0,";
167 transform += refY;
168 }
169 }
170 }
171 transform += ")";
172
173 // We can't set a transform on a marker... must create group first.
174 Inkscape::XML::Node *group = repr->document()->createElement("svg:g");
175 group->setAttribute("transform", transform);
176 marker_reversed->addChild(group, nullptr);
177
178 // Copy all marker content to group.
179 for (auto child = marker->firstChild() ; child != nullptr ; child = child->next() ) {
180 auto new_child = child->duplicate(repr->document());
181 group->addChild(new_child, nullptr);
182 new_child->release();
183 }
184
185 // Add new marker to <defs>.
186 defs->addChild(marker_reversed, marker);
187 marker_reversed->release();
188 }
189
190 // Change url to reference reversed marker.
191 std::string marker_url("url(#" + marker_name_reversed + ")");
192 sp_repr_css_set_property(css, "marker-start", marker_url.c_str());
193
194 // Also fix up if property is marker shorthand.
195 if (property == "marker") {
196 std::string marker_old_url("url(#" + marker_name + ")");
198 sp_repr_css_set_property(css, "marker-mid", marker_old_url.c_str());
199 sp_repr_css_set_property(css, "marker-end", marker_old_url.c_str());
200 }
201
202 sp_repr_css_set(repr, css, "style");
203
204 } // Uses auto-start-reverse
205 }
206 }
207}
208
209// Called by remove_marker_context_paint() for each property value ("marker", "marker-start", ...).
210void remove_marker_context_paint (Inkscape::XML::Node *repr, Inkscape::XML::Node *defs, Glib::ustring property)
211{
212 // Value of 'marker', 'marker-start', ... property.
213 std::string value("url(#");
214 value += repr->attribute("id");
215 value += ")";
216
217 // Generate a list of elements that reference this marker.
218 std::vector<Inkscape::XML::Node *> to_fix_fill_stroke =
219 sp_repr_lookup_property_many(repr->root(), property, value);
220
221 for (auto it: to_fix_fill_stroke) {
222
223 // Figure out value of fill... could be inherited.
225 Glib::ustring fill = sp_repr_css_property (css, "fill", "");
226 Glib::ustring stroke = sp_repr_css_property (css, "stroke", "");
227
228 // Name of new marker./
229 Glib::ustring marker_fixed_id = repr->attribute("id");
230 if (!fill.empty()) {
231 marker_fixed_id += "_F" + fill;
232 }
233 if (!stroke.empty()) {
234 marker_fixed_id += "_S" + stroke;
235 }
236
237 {
238 // Replace characters from color value that are invalid in ids
239 gchar *normalized_id = g_strdup(marker_fixed_id.c_str());
240 g_strdelimit(normalized_id, "#%", '-');
241 g_strdelimit(normalized_id, "(), \n\t\r", '.');
242 marker_fixed_id = normalized_id;
243 g_free(normalized_id);
244 }
245
246 // See if a fixed marker already exists.
247 // Could be more robust, assumes markers are direct children of <defs>.
248 Inkscape::XML::Node* marker_fixed = sp_repr_lookup_child(defs, "id", marker_fixed_id.c_str());
249
250 if (!marker_fixed) {
251
252 // Need to create new marker.
253
254 marker_fixed = repr->duplicate(repr->document());
255 marker_fixed->setAttribute("id", marker_fixed_id);
256
257 // This needs to be turned into a function that fixes all descendents.
258 for (auto child = marker_fixed->firstChild() ; child != nullptr ; child = child->next()) {
259 // Find style.
260 SPCSSAttr* css = sp_repr_css_attr ( child, "style" );
261
262 Glib::ustring fill2 = sp_repr_css_property (css, "fill", "");
263 if (fill2 == "context-fill" ) {
264 sp_repr_css_set_property (css, "fill", fill.c_str());
265 }
266 if (fill2 == "context-stroke" ) {
267 sp_repr_css_set_property (css, "fill", stroke.c_str());
268 }
269
270 Glib::ustring stroke2 = sp_repr_css_property (css, "stroke", "");
271 if (stroke2 == "context-fill" ) {
272 sp_repr_css_set_property (css, "stroke", fill.c_str());
273 }
274 if (stroke2 == "context-stroke" ) {
275 sp_repr_css_set_property (css, "stroke", stroke.c_str());
276 }
277
278 sp_repr_css_set(child, css, "style");
280 }
281
282 defs->addChild(marker_fixed, repr);
283 marker_fixed->release();
284 }
285
286 Glib::ustring marker_value = "url(#" + marker_fixed_id + ")";
287 sp_repr_css_set_property (css, property.c_str(), marker_value.c_str());
288 sp_repr_css_set (it, css, "style");
290 }
291}
292
294{
295 for (auto child = repr->firstChild(); child; child = child->next() ) {
297 }
298
299 if (strncmp("svg:marker", repr->name(), 10) == 0) {
300
301 if (!repr->attribute("id")) {
302
303 std::cerr << "remove_marker_context_paint: <marker> without 'id'!" << std::endl;
304
305 } else {
306
307 // First see if we need to do anything.
308 bool need_to_fix = false;
309
310 // This needs to be turned into a function that searches all descendents.
311 for (auto child = repr->firstChild() ; child != nullptr ; child = child->next()) {
312
313 // Find style.
314 SPCSSAttr* css = sp_repr_css_attr ( child, "style" );
315 Glib::ustring fill = sp_repr_css_property (css, "fill", "");
316 Glib::ustring stroke = sp_repr_css_property (css, "stroke", "");
317 if (fill == "context-fill" ||
318 fill == "context-stroke" ||
319 stroke == "context-fill" ||
320 stroke == "context-stroke" ) {
321 need_to_fix = true;
322 break;
323 }
325 }
326
327 if (need_to_fix) {
328
329 // Now we need to search document for all elements that use this marker.
330 remove_marker_context_paint (repr, defs, "marker");
331 remove_marker_context_paint (repr, defs, "marker-start");
332 remove_marker_context_paint (repr, defs, "marker-mid");
333 remove_marker_context_paint (repr, defs, "marker-end");
334 }
335 }
336 }
337}
338
339/*
340 * Recursively insert SVG 1.1 fallback for SVG 2 text (ignored by SVG 2 renderers including ours).
341 * Notes:
342 * Text must have been layed out. Access via old document.
343 */
345{
346 if (repr) {
347
348 if (strncmp("svg:text", repr->name(), 8) == 0) {
349
350 auto id = repr->attribute("id");
351 // std::cout << "insert_text_fallback: found text! id: " << (id?id:"null") << std::endl;
352
353 // We need to get original SPText object to access layout.
354 SPText* text = static_cast<SPText *>(original_doc->getObjectById( id ));
355 if (text == nullptr) {
356 std::cerr << "insert_text_fallback: bad cast" << std::endl;
357 return;
358 }
359
360 if (!text->has_inline_size() &&
361 !text->has_shape_inside()) {
362 // No SVG 2 text, nothing to do.
363 return;
364 }
365
366 // We will keep this text node but replace all children.
367 // Text object must be visible for the text calculatons to work
368 bool was_hidden = text->isHidden();
369 text->setHidden(false);
370 text->rebuildLayout();
371
372 // For text in a shape, We need to unset 'text-anchor' or SVG 1.1 fallback won't work.
373 // Note 'text' here refers to original document while 'repr' refers to new document copy.
374 if (text->has_shape_inside()) {
375 SPCSSAttr *css = sp_repr_css_attr(repr, "style" );
376 sp_repr_css_unset_property(css, "text-anchor");
377 sp_repr_css_set(repr, css, "style");
379 }
380
381 // We need to put trailing white space into its own tspan for inline size so
382 // it is excluded during calculation of line position in SVG 1.1 renderers.
383 bool trim = text->has_inline_size() &&
384 !(text->style->text_anchor.computed == SP_CSS_TEXT_ANCHOR_START);
385
386 // Make a list of children to delete at end:
387 std::vector<Inkscape::XML::Node *> old_children;
388 for (auto child = repr->firstChild(); child; child = child->next()) {
389 old_children.push_back(child);
390 }
391
392 // For round-tripping, xml:space (or 'white-space:pre') must be set.
393 repr->setAttribute("xml:space", "preserve");
394
395 double text_x = repr->getAttributeDouble("x", 0.0);
396 double text_y = repr->getAttributeDouble("y", 0.0);
397 // std::cout << "text_x: " << text_x << " text_y: " << text_y << std::endl;
398
399 // Loop over all lines in layout.
400 for (auto it = text->layout.begin() ; it != text->layout.end() ; ) {
401
402 // Create a <tspan> with 'x' and 'y' for each line.
403 Inkscape::XML::Node *line_tspan = repr->document()->createElement("svg:tspan");
404
405 // This could be useful if one wants to edit in an old version of Inkscape but we
406 // need to check if it breaks anything:
407 // line_tspan->setAttribute("sodipodi:role", "line");
408
409 // Hide overflow tspan (one line of text).
410 if (text->layout.isHidden(it)) {
411 line_tspan->setAttribute("style", "visibility:hidden");
412 }
413
414 Geom::Point line_anchor_point = text->layout.characterAnchorPoint(it);
415 double line_x = line_anchor_point[Geom::X];
416 double line_y = line_anchor_point[Geom::Y];
417
418 // std::cout << " line_anchor_point: " << line_anchor_point << std::endl;
419 if (line_tspan->childCount() == 0) {
420 if (text->is_horizontal()) {
421 // std::cout << " horizontal: " << text_x << " " << line_anchor_point[Geom::Y] << std::endl;
422 if (text->has_inline_size()) {
423 // We use text_x as this is the reference for 'text-anchor'
424 // (line_x is the start of the line which gives wrong position when 'text-anchor' not start).
425 line_tspan->setAttributeSvgDouble("x", text_x);
426 } else {
427 // shape-inside (we don't have to worry about 'text-anchor').
428 line_tspan->setAttributeSvgDouble("x", line_x);
429 }
430 line_tspan->setAttributeSvgDouble("y", line_y); // FIXME: this will pick up the wrong end of counter-directional runs
431 } else {
432 // std::cout << " vertical: " << line_anchor_point[Geom::X] << " " << text_y << std::endl;
433 line_tspan->setAttributeSvgDouble("x", line_x); // FIXME: this will pick up the wrong end of counter-directional runs
434 if (text->has_inline_size()) {
435 line_tspan->setAttributeSvgDouble("y", text_y);
436 } else {
437 line_tspan->setAttributeSvgDouble("y", line_y);
438 }
439 }
440 }
441
442 // Inside line <tspan>, create <tspan>s for each change of style or shift. (No shifts in SVG 2 flowed text.)
443 // For simple lines, this creates an unneeded <tspan> but so be it.
444 Inkscape::Text::Layout::iterator it_line_end = it;
445 it_line_end.nextStartOfLine();
446
447 // Find last span in line so we can put trailing whitespace in its own tspan for SVG 1.1 fallback.
448 Inkscape::Text::Layout::iterator it_last_span = it;
449 it_last_span.nextStartOfLine();
450 it_last_span.prevStartOfSpan();
451
452 Glib::ustring trailing_whitespace;
453
454 // Loop over chunks in line
455 while (it != it_line_end) {
456
457 Inkscape::XML::Node *span_tspan = repr->document()->createElement("svg:tspan");
458
459 // use kerning to simulate justification and whatnot
460 Inkscape::Text::Layout::iterator it_span_end = it;
461 it_span_end.nextStartOfSpan();
463 text->layout.simulateLayoutUsingKerning(it, it_span_end, &attrs);
464
465 // 'dx' and 'dy' attributes are used to simulated justified text.
466 if (!text->is_horizontal()) {
467 std::swap(attrs.dx, attrs.dy);
468 }
469 TextTagAttributes(attrs).writeTo(span_tspan);
470 SPObject *source_obj = nullptr;
471 Glib::ustring::iterator span_text_start_iter;
472 text->layout.getSourceOfCharacter(it, &source_obj, &span_text_start_iter);
473
474 // Set tspan style
475 Glib::ustring style_text = (is<SPString>(source_obj) ? source_obj->parent : source_obj)
476 ->style->writeIfDiff(text->style);
477 if (!style_text.empty()) {
478 span_tspan->setAttributeOrRemoveIfEmpty("style", style_text);
479 }
480
481 // If this tspan has no attributes, discard it and add content directly to parent element.
482 if (span_tspan->attributeList().empty()) {
483 Inkscape::GC::release(span_tspan);
484 span_tspan = line_tspan;
485 } else {
486 line_tspan->appendChild(span_tspan);
487 Inkscape::GC::release(span_tspan);
488 }
489
490 // Add text node
491 auto str = cast<SPString>(source_obj);
492 if (str) {
493 Glib::ustring *string = &(str->string); // TODO fixme: dangerous, unsafe premature-optimization
494 SPObject *span_end_obj = nullptr;
495 Glib::ustring::iterator span_text_end_iter;
496 text->layout.getSourceOfCharacter(it_span_end, &span_end_obj, &span_text_end_iter);
497 if (span_end_obj != source_obj) {
498 if (it_span_end == text->layout.end()) {
499 span_text_end_iter = span_text_start_iter;
500 for (int i = text->layout.iteratorToCharIndex(it_span_end) - text->layout.iteratorToCharIndex(it) ; i ; --i)
501 ++span_text_end_iter;
502 } else
503 span_text_end_iter = string->end(); // spans will never straddle a source boundary
504 }
505
506 if (span_text_start_iter != span_text_end_iter) {
507 Glib::ustring new_string;
508 while (span_text_start_iter != span_text_end_iter)
509 new_string += *span_text_start_iter++; // grr. no substr() with iterators
510
511 if (it == it_last_span && trim) {
512 // Found last span in line
513 const auto s = new_string.find_last_not_of(" \t"); // Any other white space characters needed?
514 trailing_whitespace = new_string.substr(s+1, new_string.length());
515 new_string.erase(s+1);
516 }
517
518 Inkscape::XML::Node *new_text = repr->document()->createTextNode(new_string.c_str());
519 span_tspan->appendChild(new_text);
520 Inkscape::GC::release(new_text);
521 // std::cout << " new_string: |" << new_string << "|" << std::endl;
522 }
523 }
524 it = it_span_end;
525 }
526
527 // Add line tspan to document
528 repr->appendChild(line_tspan);
529 Inkscape::GC::release(line_tspan);
530
531 // For center and end justified text, we need to remove any spaces and put them
532 // into a separate tspan (alignment is done by "text chunk" and spaces at ends of
533 // line will mess this up).
534 if (trim && trailing_whitespace.length() != 0) {
535
536 Inkscape::XML::Node *space_tspan = repr->document()->createElement("svg:tspan");
537 // Set either 'x' or 'y' to force a new text chunk. To do: this really should
538 // be positioned at the end of the line (overhanging).
539 if (text->is_horizontal()) {
540 space_tspan->setAttributeSvgDouble("y", line_y);
541 } else {
542 space_tspan->setAttributeSvgDouble("x", line_x);
543 }
544 Inkscape::XML::Node *space = repr->document()->createTextNode(trailing_whitespace.c_str());
545 space_tspan->appendChild(space);
547 line_tspan->appendChild(space_tspan);
548 Inkscape::GC::release(space_tspan);
549 }
550
551 }
552
553 for (auto i: old_children) {
554 repr->removeChild (i);
555 }
556
557 text->setHidden(was_hidden);
558 return; // No need to look at children of <text>
559 }
560
561 for (auto child = repr->firstChild(); child; child = child->next() ) {
562 insert_text_fallback (child, original_doc, defs);
563 }
564 }
565}
566
568{
569 if (repr) {
570
571 Inkscape::XML::Node *defs = sp_repr_lookup_name (repr, "svg:defs");
572
573 if (defs == nullptr) {
574 // We always put meshes in <defs>, no defs -> no mesh.
575 return;
576 }
577
578 bool has_mesh = false;
579 for (auto child = defs->firstChild(); child; child = child->next()) {
580 if (strncmp("svg:meshgradient", child->name(), 16) == 0) {
581 has_mesh = true;
582 break;
583 }
584 }
585
586 Inkscape::XML::Node *script = sp_repr_lookup_child (repr, "id", "mesh_polyfill");
587
588 if (has_mesh && script == nullptr) {
589
590 script = repr->document()->createElement("svg:script");
591 script->setAttribute ("id", "mesh_polyfill");
592 script->setAttribute ("type", "text/javascript");
593 repr->root()->appendChild(script); // Must be last
594
595 // Insert JavaScript via raw string literal.
596 Glib::ustring js =
597#include "extension/internal/polyfill/mesh_compressed.include"
598;
599
600 Inkscape::XML::Node *script_text = repr->document()->createTextNode(js.c_str());
601 script->appendChild(script_text);
602 }
603 }
604}
606{
607 if (repr) {
608
609 Inkscape::XML::Node *defs = sp_repr_lookup_name (repr, "svg:defs");
610
611 if (defs == nullptr) {
612 // We always put meshes in <defs>, no defs -> no mesh.
613 return;
614 }
615
616 bool has_hatch = false;
617 for (auto child = defs->firstChild(); child; child = child->next()) {
618 if (strncmp("svg:hatch", child->name(), 16) == 0) {
619 has_hatch = true;
620 break;
621 }
622 }
623
624 Inkscape::XML::Node *script = sp_repr_lookup_child (repr, "id", "hatch_polyfill");
625
626 if (has_hatch && script == nullptr) {
627
628 script = repr->document()->createElement("svg:script");
629 script->setAttribute ("id", "hatch_polyfill");
630 script->setAttribute ("type", "text/javascript");
631 repr->root()->appendChild(script); // Must be last
632
633 // Insert JavaScript via raw string literal.
634 Glib::ustring js =
635#include "extension/internal/polyfill/hatch_compressed.include"
636;
637
638 Inkscape::XML::Node *script_text = repr->document()->createTextNode(js.c_str());
639 script->appendChild(script_text);
640 }
641 }
642}
643
650{
651 for (auto& child: item->childList(false)) {
652 if (auto child_item = cast<SPItem>(child)) {
653 insert_bounding_boxes(child_item);
654 }
655 }
656 auto const scale = item->document->getDocumentScale().inverse();
657 auto const vbox = item->visualBounds(item->i2doc_affine() * scale);
658 auto const gbox = item->geometricBounds(item->i2doc_affine() * scale);
659 item->setAttributeOrRemoveIfEmpty("inkscape:visualbox", SVGBox(vbox).write());
660 if (gbox != vbox) {
661 item->setAttributeOrRemoveIfEmpty("inkscape:geometricbox", SVGBox(gbox).write());
662 }
663}
664
669{
673 item->setAttribute("inkscape:d", sp_svg_write_path(fill));
674 } else {
675 for (auto& child: item->childList(false)) {
676 if (auto child_item = cast<SPItem>(child)) {
677 insert_path_data(child_item);
678 }
679 }
680 }
681}
682
687{
688 if (auto attr = node->attribute("d")) {
690 }
691 for (auto child = node->firstChild(); child; child = child->next() ) {
693 }
694}
695
696const Glib::ustring SECTION = NC_("Action Section", "Processing");
697
698std::vector<std::vector<Glib::ustring>> doc_svg_processing_actions =
699{
700 // clang-format off
701 {"doc.set-svg-version-1", N_("Set SVG Version to 1.1"), SECTION, N_("Set the document's SVG version to 1.1") },
702 {"doc.set-svg-version-2", N_("Set SVG Version to 2.0"), SECTION, N_("Set the document's SVG version to 2.0") },
703 {"doc.set-inkscape-version", N_("Set Inkscape Version"), SECTION, N_("Add the Inkscape version to the document") },
704 {"doc.prune-inkscape-namespaces", N_("Prune Inkscape Namespaces"), SECTION, N_("Remove any Inkscape-specific SVG data") },
705 {"doc.prune-proprietary-namespaces", N_("Prune Proprietary Namespaces"), SECTION, N_("Remove any known proprietary SVG data") },
706
707 {"doc.reverse-auto-start-markers", N_("Reverse Auto Start Markers"), SECTION, N_("Remove auto start positions from markers") },
708 {"doc.remove-all-transforms", N_("Try to Remove All Transforms"), SECTION, N_("Attempt to remove all transforms from all shapes") },
709 {"doc.remove-marker-context-paint", N_("Remove Marker Context Paint"), SECTION, N_("Remove context paints from markers") },
710
711 {"doc.insert-text-fallback", N_("Insert Text Fallback"), SECTION, N_("Replace SVG2 text with SVG1.1 text") },
712 {"doc.insert-mesh-polyfill", N_("Insert Mesh Polyfill"), SECTION, N_("Insert JavaScript for rendering meshes") },
713 {"doc.insert-hatch-polyfill", N_("Insert Hatch Polyfill"), SECTION, N_("Insert JavaScript for rendering hatches") },
714
715 {"doc.all-clones-to-objects", N_("Unlink All Clones"), SECTION, N_("Recursively unlink all clones and symbols") },
716 {"doc.all-objects-to-paths", N_("All Objects to Paths"), SECTION, N_("Turn all shapes recursively into path elements") },
717 {"doc.add-strokes-to-paths", N_("All Strokes to Paths"), SECTION, N_("Turn all strokes recursively into fill-only paths") },
718 {"doc.normalize-all-paths", N_("Normalize Path Data"), SECTION, N_("Make all paths absolute and predictable") },
719
720 {"doc.insert-bounding-boxes", N_("Annotate all Bounding Boxes"), SECTION, N_("Annotate every shape and group with its current bounding box (not kept up to date)") },
721 {"doc.insert-path-data", N_("Annotate all Shape Paths"), SECTION, N_("Annotate every non-path shape with their equivalent path string (not kept up to date)") },
722
723 {"doc.vacuum-defs", N_("Clean up Document"), SECTION, N_("Remove unused definitions (gradients, etc.)") },
724 // clang-format on
725};
726
728{
729
730 auto group = doc->getActionGroup();
731 // clang-format off
732 group->add_action("set-svg-version-2", [doc]() {
733 auto rdoc = doc->getReprDoc();
734 rdoc->setAttribute("standalone", "no");
735 rdoc->setAttribute("version", "2.0");
736 });
737 group->add_action("set-svg-version-1", [doc]() {
738 auto rdoc = doc->getReprDoc();
739 rdoc->setAttribute("version", "1.1");
740 });
741 group->add_action("set-inkscape-version", [doc]() {
742 auto rdoc = doc->getReprDoc();
743 rdoc->setAttribute("inkscape:version", Inkscape::version_string);
744 });
745 group->add_action("prune-inkscape-namespaces", [doc]() { prune_inkscape_from_node(doc->getReprRoot()); });
746 group->add_action("prune-proprietary-namespaces", [doc]() { prune_proprietary_from_node(doc->getReprRoot()); });
747 group->add_action("reverse-auto-start-markers", [doc]() {
748 // Do marker start for efficiency reasons
749 remove_marker_auto_start_reverse(doc->getReprRoot(), doc->getDefs()->getRepr(), "marker-start");
750 remove_marker_auto_start_reverse(doc->getReprRoot(), doc->getDefs()->getRepr(), "marker");
751 });
752 group->add_action("remove-all-transforms", [doc]() {
754 });
755
756 group->add_action("remove-marker-context-paint", [doc]() { remove_marker_context_paint(doc->getReprRoot(), doc->getDefs()->getRepr()); });
757 group->add_action("insert-text-fallback", [doc]() { insert_text_fallback(doc->getReprRoot(), doc->getOriginalDocument()); });
758 group->add_action("insert-mesh-polyfill", [doc]() { insert_mesh_polyfill(doc->getReprRoot()); });
759 group->add_action("insert-hatch-polyfill", [doc]() { insert_hatch_polyfill(doc->getReprRoot()); });
760 group->add_action("all-clones-to-objects", [doc]() {
761 auto selection = Inkscape::ObjectSet(doc);
762 selection.set(doc->getRoot());
763 selection.unlinkRecursive(true, false, true);
764 });
765 group->add_action("all-objects-to-paths", [doc]() {
766 std::vector<SPItem*> selected;
767 std::vector<Inkscape::XML::Node*> to_select;
768 sp_item_list_to_curves({doc->getRoot()}, selected, to_select, false);
769 });
770 group->add_action("add-strokes-to-paths", [doc]() {
771 item_to_paths(doc->getRoot());
772 });
773 group->add_action("normalize-all-paths", [doc]() { normalize_all_paths(doc->getReprRoot()); });
774 group->add_action("insert-bounding-boxes", [doc]() { insert_bounding_boxes(doc->getRoot()); });
775 group->add_action("insert-path-data", [doc]() { insert_path_data(doc->getRoot()); });
776 group->add_action("vacuum-defs", [doc]() { doc->vacuumDocument(); });
777 // clang-format on
778
779 // Note: This will only work for the first ux to load, possible problem.
781 if (!app) { // i.e. Inkview
782 return;
783 }
784 app->get_action_extra_data().add_data(doc_svg_processing_actions);
785}
786
787/*
788 Local Variables:
789 mode:c++
790 c-file-style:"stroustrup"
791 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
792 indent-tabs-mode:nil
793 fill-column:99
794 End:
795*/
796// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :
double scale
Definition aa.cpp:228
void insert_hatch_polyfill(Inkscape::XML::Node *repr)
void insert_bounding_boxes(SPItem *item)
Appends a visual box, and an optional geometric box to each SPItem recursively.
const Glib::ustring SECTION
void prune_inkscape_from_node(Inkscape::XML::Node *repr)
void normalize_all_paths(Inkscape::XML::Node *node)
Makes paths more predictable for better processing.
void insert_text_fallback(Inkscape::XML::Node *repr, const SPDocument *original_doc, Inkscape::XML::Node *defs)
void insert_mesh_polyfill(Inkscape::XML::Node *repr)
void add_actions_processing(SPDocument *doc)
static void remove_marker_auto_start_reverse(Inkscape::XML::Node *repr, Inkscape::XML::Node *defs, Glib::ustring const &property)
Create new markers where necessary to simulate the SVG 2 marker attribute 'orient' value 'auto-start-...
static void prune_proprietary_from_node(Inkscape::XML::Node *repr)
std::vector< std::vector< Glib::ustring > > doc_svg_processing_actions
void insert_path_data(SPItem *item)
Appends the shape path, if available, to any SPShape recursively.
void remove_marker_context_paint(Inkscape::XML::Node *repr, Inkscape::XML::Node *defs, Glib::ustring property)
TODO: insert short description here.
Sequence of subpaths.
Definition pathvector.h:122
Two-dimensional point that doubles as a vector.
Definition point.h:66
Scale inverse() const
Definition transforms.h:172
static InkscapeApplication * instance()
Singleton instance.
Holds a position within the glyph output of Layout.
Definition Layout-TNG.h:981
void simulateLayoutUsingKerning(iterator const &from, iterator const &to, OptionalTextTagAttrs *result) const
Returns kerning information which could cause the current output to be exactly reproduced if the lett...
bool isHidden(iterator const &it) const
Returns true if the text at it is hidden (i.e.
Geom::Point characterAnchorPoint(iterator const &it) const
For latin text, the left side of the character, on the baseline.
int iteratorToCharIndex(iterator const &it) const
Returns the character index from the start of the flow represented by the given iterator.
iterator end() const
Returns an iterator pointing just past the end of the last glyph, which is also just past the end of ...
void getSourceOfCharacter(iterator const &it, SPObject **source, Glib::ustring::iterator *text_iterator=nullptr) const
Discovers where the character pointed to by it came from, by retrieving the object that was passed to...
iterator begin() const
Returns an iterator pointing at the first glyph of the flowed output.
Interface for refcounted XML nodes.
Definition node.h:80
virtual Node * next()=0
Get the next sibling of this node.
virtual void addChild(Node *child, Node *after)=0
Insert another node as a child of this node.
virtual void appendChild(Node *child)=0
Append a node as the last child of this node.
virtual char const * name() const =0
Get the name of the element node.
virtual const AttributeVector & attributeList() const =0
Get a list of the node's attributes.
double getAttributeDouble(Util::const_char_ptr key, double default_value=0.0) const
Definition node.cpp:76
void setAttributeOrRemoveIfEmpty(Inkscape::Util::const_char_ptr key, Inkscape::Util::const_char_ptr value)
Change an attribute of this node.
Definition node.cpp:167
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 Node * firstChild()=0
Get the first child of this node.
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 Document * document()=0
Get the node's associated document.
virtual void removeChild(Node *child)=0
Remove a child of this node.
virtual unsigned childCount() const =0
Get the number of children of this node.
virtual NodeType type() const =0
Get the type of the node.
bool setAttributeSvgDouble(Util::const_char_ptr key, double val)
For attributes where an exponent is allowed.
Definition node.cpp:111
virtual Node * root()=0
Get the root node of this node's document.
Typed SVG document implementation.
Definition document.h:101
Glib::RefPtr< Gio::SimpleActionGroup > getActionGroup()
Definition document.h:371
SPRoot * getRoot()
Returns our SPRoot.
Definition document.h:200
SPObject * getObjectById(std::string const &id) const
Inkscape::XML::Node * getReprRoot()
Definition document.h:206
unsigned int vacuumDocument()
Remove unused definitions etc.
const SPDocument * getOriginalDocument() const
Definition document.h:136
SPDefs * getDefs()
Return the main defs object for the document.
Definition document.cpp:247
Inkscape::XML::Document * getReprDoc()
Our Inkscape::XML::Document.
Definition document.h:211
Geom::Scale getDocumentScale(bool computed=true) const
Returns document scale as defined by width/height (in pixels) and viewBox (real world to user-units).
Definition document.cpp:764
void removeTransformsRecursively(SPObject const *root) override
Base class for visual SVG elements.
Definition sp-item.h:109
bool isHidden() const
Definition sp-item.cpp:242
void setHidden(bool hidden)
Definition sp-item.cpp:248
Geom::OptRect geometricBounds(Geom::Affine const &transform=Geom::identity()) const
Get item's geometric bounding box in this item's coordinate system.
Definition sp-item.cpp:919
Geom::Affine i2doc_affine() const
Returns the accumulated transformation of the item and all its ancestors, including root's viewport.
Definition sp-item.cpp:1824
Geom::OptRect visualBounds(Geom::Affine const &transform=Geom::identity(), bool wfilter=true, bool wclip=true, bool wmask=true) const
Get item's visual bounding box in this item's coordinate system.
Definition sp-item.cpp:924
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition sp-object.h:160
void setAttributeOrRemoveIfEmpty(Inkscape::Util::const_char_ptr key, Inkscape::Util::const_char_ptr value)
Definition sp-object.h:785
void setAttribute(Inkscape::Util::const_char_ptr key, Inkscape::Util::const_char_ptr value)
std::vector< SPObject * > childList(bool add_ref, Action action=ActionGeneral)
Retrieves the children as a std vector object, optionally ref'ing the children in the process,...
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
Inkscape::XML::Node * getRepr()
Returns the XML representation of tree.
T< SPAttr::TEXT_ANCHOR, SPIEnum< SPTextAnchor > > text_anchor
Anchor of the text (svg1.1 10.9.1)
Definition style.h:173
bool has_inline_size() const
Definition sp-text.cpp:1105
bool has_shape_inside() const
Definition sp-text.cpp:1111
void rebuildLayout()
Completely recalculates the layout.
Definition sp-text.cpp:873
Inkscape::Text::Layout layout
Definition sp-text.h:43
bool is_horizontal() const
Definition sp-text.cpp:1099
void writeTo(Inkscape::XML::Node *node) const
Write out all the contents of attributes to the given node.
Definition sp-text.cpp:1393
std::shared_ptr< Css const > css
Colors::Color fill
Colors::Color stroke
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
SPItem * item
Inkscape::XML::Node * node
Mini static library that contains the version of Inkscape.
static R & release(R &r)
Decrements the reference count of a anchored object.
@ ELEMENT_NODE
Regular element node, e.g. <group />.
char const * version_string
full version string
bool sp_item_list_to_curves(const std::vector< SPItem * > &items, std::vector< SPItem * > &selected, std::vector< Inkscape::XML::Node * > &to_select, bool skip_all_lpeitems)
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.
Inkscape::XML::Node * item_to_paths(SPItem *item, bool legacy, SPItem *context)
Replace item by path objects (a.k.a.
Two related object to path operations:
Ocnode * child[8]
Definition quantize.cpp:33
SPCSSAttr * sp_repr_css_attr_inherited(Node const *repr, gchar const *attr)
Creates a new SPCSSAttr with one attribute whose value is determined by cascading.
Definition repr-css.cpp:116
void sp_repr_css_set(Node *repr, SPCSSAttr *css, gchar const *attr)
Sets an attribute (e.g.
Definition repr-css.cpp:264
void sp_repr_css_attr_unref(SPCSSAttr *css)
Unreferences an SPCSSAttr (will be garbage collected if no references remain).
Definition repr-css.cpp:76
SPCSSAttr * sp_repr_css_attr(Node const *repr, gchar const *attr)
Creates a new SPCSSAttr with one attribute (i.e.
Definition repr-css.cpp:88
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:201
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:190
Inkscape::XML::Node * sp_repr_lookup_child(Inkscape::XML::Node *repr, gchar const *key, gchar const *value)
Find an element node using an unique attribute.
Inkscape::XML::Node const * sp_repr_lookup_name(Inkscape::XML::Node const *repr, gchar const *name, gint maxdepth)
std::vector< Inkscape::XML::Node * > sp_repr_lookup_property_many(Inkscape::XML::Node *repr, Glib::ustring const &property, Glib::ustring const &value, int maxdepth)
SVG <image> implementation.
SPRoot: SVG <svg> implementation.
The optional attributes which can be applied to a SVG text or related tag.
Definition Layout-TNG.h:184
virtual Node * createTextNode(char const *content)=0
virtual Node * createElement(char const *name)=0
@ SP_CSS_TEXT_ANCHOR_START
SPStyle - a style object for SPItem objects.
Geom::PathVector sp_svg_read_pathv(char const *str)
Definition svg-path.cpp:37
static void sp_svg_write_path(Inkscape::SVG::PathString &str, Geom::Path const &p, bool normalize=false)
Definition svg-path.cpp:109
Interface for XML nodes.