Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
text-chemistry.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Text commands
4 *
5 * Authors:
6 * bulia byak
7 * Jon A. Cruz <jon@joncruz.org>
8 * Abhishek Sharma
9 *
10 * Copyright (C) 2004 authors
11 *
12 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
13 */
14
15#include <cstring>
16#include <string>
17#include <glibmm/i18n.h>
18#include <glibmm/regex.h>
19
20
21#include "desktop.h"
22#include "document-undo.h"
23#include "document.h"
24#include "inkscape.h"
25#include "message-stack.h"
26#include "preferences.h"
27#include "selection.h"
28#include "text-chemistry.h"
29#include "text-editing.h"
30
31#include "object/sp-flowdiv.h"
33#include "object/sp-flowtext.h"
34#include "object/sp-rect.h"
35#include "object/sp-textpath.h"
36#include "object/sp-tspan.h"
37#include "style.h"
38#include "ui/icon-names.h"
39
40#include "xml/repr.h"
41
43
44static SPItem *
46{
47 auto items = selection->items();
48 for(auto i=items.begin();i!=items.end();++i){
49 if (is<SPText>(*i) || is<SPFlowtext>(*i))
50 return *i;
51 }
52 return nullptr;
53}
54
55static SPItem *
57{
58 auto items = selection->items();
59 for(auto i=items.begin();i!=items.end();++i){
60 if (is<SPShape>(*i))
61 return *i;
62 }
63 return nullptr;
64}
65
66void
68{
69 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
70 if (!desktop)
71 return;
72
74
75 SPItem *text = text_or_flowtext_in_selection(selection);
76 SPItem *shape = shape_in_selection(selection);
77
79
80 if (!text || !shape || boost::distance(selection->items()) != 2) {
81 desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>a text and a path</b> to put text on path."));
82 return;
83 }
84
85 if (SP_IS_TEXT_TEXTPATH(text)) {
86 desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("This text object is <b>already put on a path</b>. Remove it from the path first. Use <b>Shift+D</b> to look up its path."));
87 return;
88 }
89
90 // if a flowed text is selected, convert it to a regular text object
91 if (is<SPFlowtext>(text)) {
92
93 if (!cast_unsafe<SPFlowtext>(text)->layout.outputExists()) {
96 _("The flowed text(s) must be <b>visible</b> in order to be put on a path."));
97 }
98
99 Inkscape::XML::Node *repr = cast<SPFlowtext>(text)->getAsText();
100
101 if (!repr) return;
102
104 parent->appendChild(repr);
105
106 SPItem *new_item = (SPItem *) desktop->getDocument()->getObjectByRepr(repr);
107 new_item->doWriteTransform(text->transform);
108 new_item->updateRepr();
109
111 text->deleteObject(); // delete the original flowtext
112
114
115 selection->clear();
116
117 text = new_item; // point to the new text
118 }
119
120 if (auto textitem = cast<SPText>(text)) {
121 // Replace any new lines (including sodipodi:role="line") by spaces.
122 textitem->remove_newlines();
123 }
124
125 Inkscape::Text::Layout const *layout = te_get_layout(text);
126 Inkscape::Text::Layout::Alignment text_alignment = layout->paragraphAlignment(layout->begin());
127
128 // remove transform from text, but recursively scale text's fontsize by the expansion
129 cast<SPText>(text)->_adjustFontsizeRecursive (text, text->transform.descrim());
130 text->removeAttribute("transform");
131
132 // make a list of text children
133 std::vector<Inkscape::XML::Node *> text_reprs;
134 for(auto& o: text->children) {
135 text_reprs.push_back(o.getRepr());
136 }
137
138 // create textPath and put it into the text
139 Inkscape::XML::Node *textpath = xml_doc->createElement("svg:textPath");
140 // reference the shape
141 gchar *href_str = g_strdup_printf("#%s", shape->getRepr()->attribute("id"));
142 textpath->setAttribute("xlink:href", href_str);
143 g_free(href_str);
144 if (text_alignment == Inkscape::Text::Layout::RIGHT) {
145 textpath->setAttribute("startOffset", "100%");
146 } else if (text_alignment == Inkscape::Text::Layout::CENTER) {
147 textpath->setAttribute("startOffset", "50%");
148 }
149 text->getRepr()->addChild(textpath, nullptr);
150
151 for (auto i=text_reprs.rbegin();i!=text_reprs.rend();++i) {
152 // Make a copy of each text child
153 Inkscape::XML::Node *copy = (*i)->duplicate(xml_doc);
154 // We cannot have multiline in textpath, so remove line attrs from tspans
155 if (!strcmp(copy->name(), "svg:tspan")) {
156 copy->removeAttribute("sodipodi:role");
157 copy->removeAttribute("x");
158 copy->removeAttribute("y");
159 }
160 // remove the old repr from under text
161 text->getRepr()->removeChild(*i);
162 // put its copy into under textPath
163 textpath->addChild(copy, nullptr); // fixme: copy id
164 }
165
166 // x/y are useless with textpath, and confuse Batik 1.5
167 text->removeAttribute("x");
168 text->removeAttribute("y");
169
170 DocumentUndo::done(desktop->getDocument(), _("Put text on path"), INKSCAPE_ICON("draw-text"));
171}
172
173void
175{
176 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
177
179
180 if (selection->isEmpty()) {
181 desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>a text on path</b> to remove it from path."));
182 return;
183 }
184
185 bool did = false;
186 auto items = selection->items();
187 for(auto i=items.begin();i!=items.end();++i){
188 SPObject *obj = *i;
189
190 if (SP_IS_TEXT_TEXTPATH(obj)) {
191 SPObject *tp = obj->firstChild();
192
193 did = true;
194
196 }
197 }
198
199 if (!did) {
200 desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("<b>No texts-on-paths</b> in the selection."));
201 } else {
202 DocumentUndo::done(desktop->getDocument(), _("Remove text from path"), INKSCAPE_ICON("draw-text"));
203 std::vector<SPItem *> vec(selection->items().begin(), selection->items().end());
204 selection->setList(vec); // reselect to update statusbar description
205 }
206}
207
208static void
210{
211 o->removeAttribute("dx");
212 o->removeAttribute("dy");
213 o->removeAttribute("rotate");
214
215 // if x contains a list, leave only the first value
216 gchar const *x = o->getRepr()->attribute("x");
217 if (x) {
218 gchar **xa_space = g_strsplit(x, " ", 0);
219 gchar **xa_comma = g_strsplit(x, ",", 0);
220 if (xa_space && *xa_space && *(xa_space + 1)) {
221 o->setAttribute("x", *xa_space);
222 } else if (xa_comma && *xa_comma && *(xa_comma + 1)) {
223 o->setAttribute("x", *xa_comma);
224 }
225 g_strfreev(xa_space);
226 g_strfreev(xa_comma);
227 }
228
229 for (auto& i: o->children) {
231 i.requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_TEXT_LAYOUT_MODIFIED_FLAG);
232 }
233}
234
235//FIXME: must work with text selection
236void
238{
239 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
240
242
243 if (selection->isEmpty()) {
244 desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>text(s)</b> to remove kerns from."));
245 return;
246 }
247
248 bool did = false;
249
250 auto items = selection->items();
251 for(auto i=items.begin();i!=items.end();++i){
252 SPObject *obj = *i;
253
254 if (!is<SPText>(obj) && !is<SPTSpan>(obj) && !is<SPFlowtext>(obj)) {
255 continue;
256 }
257
259 obj->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_TEXT_LAYOUT_MODIFIED_FLAG);
260 did = true;
261 }
262
263 if (!did) {
264 desktop->messageStack()->flash(Inkscape::ERROR_MESSAGE, _("Select <b>text(s)</b> to remove kerns from."));
265 } else {
266 DocumentUndo::done(desktop->getDocument(), _("Remove manual kerns"), INKSCAPE_ICON("draw-text"));
267 }
268}
269
270void
272{
273 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
274 if (!desktop)
275 return;
276
279 SPItem *text = text_or_flowtext_in_selection(selection);
280
281 if (is<SPText>(text)) {
282 // Make list of all shapes.
283 Glib::ustring shapes;
284 auto items = selection->items();
285 for (auto item : items) {
286 if (is<SPShape>(item)) {
287 if (!shapes.empty()) shapes += " ";
288 shapes += item->getUrl();
289 }
290 }
291
292 // Set 'shape-subtract' property.
293 text->style->shape_subtract.read(shapes.c_str());
294 text->updateRepr();
295
296 DocumentUndo::done(doc, _("Flow text subtract shape"), INKSCAPE_ICON("draw-text"));
297 } else {
298 // SVG 1.2 Flowed Text
299 desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Subtraction not available for SVG 1.2 Flowed text."));
300 }
301}
302
303void
305{
306 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
307 if (!desktop)
308 return;
309
311 Inkscape::XML::Document *xml_doc = doc->getReprDoc();
312
314
315 SPItem *text = text_or_flowtext_in_selection(selection);
316 SPItem *shape = shape_in_selection(selection);
317
318 if (!text || !shape || boost::distance(selection->items()) < 2) {
319 desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>a text</b> and one or more <b>paths or shapes</b> to flow text."));
320 return;
321 }
322
323 auto prefs = Inkscape::Preferences::get();
324 if (prefs->getBool("/tools/text/use_svg2", true)) {
325 // SVG 2 Text
326
327 if (is<SPText>(text)) {
328 // Make list of all shapes.
329 Glib::ustring shapes;
330 auto items = selection->items();
331 for (auto item : items) {
332 if (is<SPShape>(item)) {
333 if (!shapes.empty()) {
334 shapes += " ";
335 } else {
336 // can only take one shape into account for transform compensation
337 // compensate transform
338 auto const new_transform = i2i_affine(item->parent, text->parent);
339 auto const ex = text->transform.descrim() / new_transform.descrim();
340 static_cast<SPText *>(text)->_adjustFontsizeRecursive(text, ex);
341 text->transform = new_transform;
342 }
343
344 shapes += item->getUrl();
345 }
346 }
347
348 // Set 'shape-inside' property.
349 text->style->shape_inside.read(shapes.c_str());
350 text->style->white_space.read("pre-wrap"); // Respect new lines.
351 text->updateRepr();
352
353 DocumentUndo::done(doc, _("Flow text into shape"), INKSCAPE_ICON("draw-text"));
354 }
355 } else {
356 if (is<SPText>(text) || is<SPFlowtext>(text)) {
357 // remove transform from text, but recursively scale text's fontsize by the expansion
358 auto ex = i2i_affine(text, shape->parent).descrim();
359 cast<SPText>(text)->_adjustFontsizeRecursive(text, ex);
360 text->removeAttribute("transform");
361 }
362
363 Inkscape::XML::Node *root_repr = xml_doc->createElement("svg:flowRoot");
364 root_repr->setAttribute("xml:space", "preserve"); // we preserve spaces in the text objects we create
365 root_repr->setAttribute("style", text->getRepr()->attribute("style")); // fixme: transfer style attrs too
366 shape->parent->getRepr()->appendChild(root_repr);
367 SPObject *root_object = doc->getObjectByRepr(root_repr);
368 g_return_if_fail(is<SPFlowtext>(root_object));
369
370 Inkscape::XML::Node *region_repr = xml_doc->createElement("svg:flowRegion");
371 root_repr->appendChild(region_repr);
372 SPObject *object = doc->getObjectByRepr(region_repr);
373 g_return_if_fail(is<SPFlowregion>(object));
374
375 /* Add clones */
376 auto items = selection->items();
377 for(auto i=items.begin();i!=items.end();++i){
378 SPItem *item = *i;
379 if (is<SPShape>(item)){
380 Inkscape::XML::Node *clone = xml_doc->createElement("svg:use");
381 clone->setAttribute("x", "0");
382 clone->setAttribute("y", "0");
383 gchar *href_str = g_strdup_printf("#%s", item->getRepr()->attribute("id"));
384 clone->setAttribute("xlink:href", href_str);
385 g_free(href_str);
386
387 // add the new clone to the region
388 region_repr->appendChild(clone);
389 }
390 }
391
392 if (is<SPText>(text)) { // flow from text, as string
393 Inkscape::XML::Node *para_repr = xml_doc->createElement("svg:flowPara");
394 root_repr->appendChild(para_repr);
395 object = doc->getObjectByRepr(para_repr);
396 g_return_if_fail(is<SPFlowpara>(object));
397
398 Inkscape::Text::Layout const *layout = te_get_layout(text);
399 Glib::ustring text_ustring = sp_te_get_string_multiline(text, layout->begin(), layout->end());
400
401 Inkscape::XML::Node *text_repr = xml_doc->createTextNode(text_ustring.c_str()); // FIXME: transfer all formatting! and convert newlines into flowParas!
402 para_repr->appendChild(text_repr);
403
404 Inkscape::GC::release(para_repr);
405 Inkscape::GC::release(text_repr);
406
407 } else { // reflow an already flowed text, preserving paras
408 for(auto& o: text->children) {
409 if (is<SPFlowpara>(&o)) {
410 Inkscape::XML::Node *para_repr = o.getRepr()->duplicate(xml_doc);
411 root_repr->appendChild(para_repr);
412 object = doc->getObjectByRepr(para_repr);
413 g_return_if_fail(is<SPFlowpara>(object));
414 Inkscape::GC::release(para_repr);
415 }
416 }
417 }
418
419 text->deleteObject(true);
420
421 DocumentUndo::done(doc, _("Flow text into shape"), INKSCAPE_ICON("draw-text"));
422
423 desktop->getSelection()->set(cast<SPItem>(root_object));
424
425 Inkscape::GC::release(root_repr);
426 Inkscape::GC::release(region_repr);
427 }
428}
429
430void
432{
433 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
434 if (!desktop)
435 return;
436
438 Inkscape::XML::Document *xml_doc = doc->getReprDoc();
439
441
442 if (!text_or_flowtext_in_selection(selection) || boost::distance(selection->items()) < 1) {
443 desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("Select <b>a flowed text</b> to unflow it."));
444 return;
445 }
446
447 std::vector<SPItem*> new_objs;
448 std::vector<SPItem *> old_objs;
449
450 auto items = selection->items();
451 for (auto i : items) {
452
453 auto flowtext = cast<SPFlowtext>(i);
454 auto text = cast<SPText>(i);
455
456 if (flowtext) {
457
458 // we discard transform when unflowing, but we must preserve expansion which is visible as
459 // font size multiplier
460 double ex = (flowtext->transform).descrim();
461
462 Glib::ustring text_string = sp_te_get_string_multiline(flowtext);
463 if (text_string.empty()) { // flowtext is empty
464 continue;
465 }
466
467 /* Create <text> */
468 Inkscape::XML::Node *rtext = xml_doc->createElement("svg:text");
469 rtext->setAttribute("xml:space", "preserve"); // we preserve spaces in the text objects we create
470
471 /* Set style */
472 rtext->setAttribute("style", flowtext->getRepr()->attribute("style")); // fixme: transfer style attrs too;
473 // and from descendants
474
475 Geom::OptRect bbox = flowtext->geometricBounds(flowtext->i2doc_affine());
476 if (bbox) {
477 Geom::Point xy = bbox->min();
478 rtext->setAttributeSvgDouble("x", xy[Geom::X]);
479 rtext->setAttributeSvgDouble("y", xy[Geom::Y]);
480 }
481
482 /* Create <tspan> */
483 Inkscape::XML::Node *rtspan = xml_doc->createElement("svg:tspan");
484 rtspan->setAttribute("sodipodi:role", "line"); // otherwise, why bother creating the tspan?
485 rtext->addChild(rtspan, nullptr);
486
487 Inkscape::XML::Node *text_repr = xml_doc->createTextNode(text_string.c_str()); // FIXME: transfer all formatting!!!
488 rtspan->appendChild(text_repr);
489
490 flowtext->parent->getRepr()->appendChild(rtext);
491 SPObject *text_object = doc->getObjectByRepr(rtext);
492
493 // restore the font size multiplier from the flowtext's transform
494 auto text = cast<SPText>(text_object);
495 text->_adjustFontsizeRecursive(text, ex);
496
497 new_objs.push_back((SPItem *)text_object);
498 old_objs.push_back(flowtext);
499
501 Inkscape::GC::release(rtspan);
502 Inkscape::GC::release(text_repr);
503
504 } else if (text) {
505 if (text->has_shape_inside()) {
506
507 auto old_point = text->getBaselinePoint();
508 Inkscape::XML::Node *rtext = text->getRepr();
509
510 // Position unflowed text near shape.
511 Geom::OptRect bbox = text->geometricBounds(text->i2doc_affine());
512 if (bbox) {
513 Geom::Point xy = bbox->min();
514 rtext->setAttributeSvgDouble("x", xy[Geom::X]);
515 rtext->setAttributeSvgDouble("y", xy[Geom::Y]);
516 }
517
518 // Remove 'shape-inside' property.
519 SPCSSAttr *css = sp_repr_css_attr(rtext, "style");
520 sp_repr_css_unset_property(css, "shape-inside");
521 sp_repr_css_unset_property(css, "shape-padding");
522 sp_repr_css_change(rtext, css, "style");
524
525 // We'll leave tspans alone other than stripping 'x' and 'y' (this will preserve
526 // styling).
527 // We'll also remove temporarily 'sodipodi:role' (which shouldn't be
528 // necessary later).
529 for (auto j : text->childList(false)) {
530 auto tspan = cast<SPTSpan>(j);
531 if (tspan) {
532 tspan->getRepr()->removeAttribute("x");
533 tspan->getRepr()->removeAttribute("y");
534 tspan->getRepr()->removeAttribute("sodipodi:role");
535 }
536 }
537 // Reposition the text so the baselines don't change.
538 text->rebuildLayout();
539 auto new_point = text->getBaselinePoint();
540 if (old_point && new_point) {
541 auto move = Geom::Translate(*old_point - *new_point) * text->transform;
542 text->doWriteTransform(move, &move, false);
543 }
544 }
545 }
546 }
547
548 // For flowtext objects.
549 if (new_objs.size() != 0) {
550
551 // Update selection
552 selection->clear();
553 reverse(new_objs.begin(), new_objs.end());
554 selection->setList(new_objs);
555
556 // Delete old objects
557 for (auto i : old_objs) {
558 i->deleteObject(true);
559 }
560 }
561
562 DocumentUndo::done(doc, _("Unflow flowed text"), INKSCAPE_ICON("draw-text"));
563}
564
565void
567{
568 auto desktop = SP_ACTIVE_DESKTOP;
569 auto selection = desktop->getSelection();
570 std::vector<SPText*> results;
571 std::vector<SPText*> to_delete;
572
573 auto doc = desktop->getDocument();
574 auto xml_doc = doc->getReprDoc();
575
576 for(auto item : selection->items()) {
577 auto text = cast<SPText>(item);
578 if (!text)
579 continue;
580
581 auto parent = text->parent->getRepr();
582 auto sibling = text->getRepr();
583
584 auto const &layout = text->layout;
585 auto iter = layout.end();
586 while (iter != layout.begin()) {
587
588 // Glyph index may not be zero leading to an infinite loop
589 // if we don't test this here... (see issue #4767).
590 if (!iter.prevCharacter()) {
591 break;
592 }
593
594 if (layout.isWhitespace(iter))
595 continue;
596
597 auto str = Glib::ustring(1, layout.characterAt(iter));
598 auto point = layout.characterAnchorPoint(iter);
599
600 SPObject *tspan = nullptr;
601 layout.getSourceOfCharacter(iter, &tspan);
602 if (!tspan)
603 break;
604
605 // Create new text object to hold the single glyph
606 Inkscape::XML::Node *new_node = xml_doc->createElement("svg:text");
607
608 // Write the effective style and transform back into the new text node
609 SPStyle *result_style = new SPStyle(doc);
610 for (auto sp = tspan->parent; sp && sp != text; sp = sp->parent) {
611 result_style->merge(sp->style);
612 }
613 result_style->merge(text->style);
614 result_style->text_anchor.read("start");
615 Glib::ustring glyph_style = result_style->writeIfDiff(text->parent->style);
616 delete result_style;
617
618 new_node->setAttributeOrRemoveIfEmpty("style", glyph_style);
619 new_node->setAttributeOrRemoveIfEmpty("transform", text->getAttribute("transform"));
620 new_node->setAttributeSvgDouble("x", point[Geom::X]);
621 new_node->setAttributeSvgDouble("y", point[Geom::Y]);
622 new_node->appendChild(xml_doc->createTextNode(str.c_str()));
623
624 // Store the new object for the selection and prepare for the next glyph
625 parent->addChild(new_node, sibling);
626 auto new_text = cast<SPText>(doc->getObjectByRepr(new_node));
627 results.push_back(new_text);
628 Inkscape::GC::release(new_node);
629 }
630 to_delete.push_back(text);
631 }
632
633 selection->clear();
634 for (auto item : to_delete) {
636 }
637
638 if (results.empty()) {
640 _("Select <b>text(s)</b> to convert to glyphs."));
641 } else {
642 DocumentUndo::done(doc, _("Convert text to glyphs"), INKSCAPE_ICON("text-convert-to-regular"));
643 selection->setList(results);
644 }
645}
646
647void
649{
650 SPDesktop *desktop = SP_ACTIVE_DESKTOP;
651
653
654 if (selection->isEmpty()) {
656 _("Select <b>flowed text(s)</b> to convert."));
657 return;
658 }
659
660 bool did = false;
661 bool ignored = false;
662
663 std::vector<Inkscape::XML::Node*> reprs;
664 std::vector<SPItem*> items(selection->items().begin(), selection->items().end());
665 for(auto item : items){
666
667 if (!is<SPFlowtext>(item))
668 continue;
669
670 if (!cast_unsafe<SPFlowtext>(item)->layout.outputExists()) {
671 ignored = true;
672 continue;
673 }
674
675 Inkscape::XML::Node *repr = cast<SPFlowtext>(item)->getAsText();
676
677 if (!repr) break;
678
679 did = true;
680
682 parent->addChild(repr, item->getRepr());
683
684 SPItem *new_item = reinterpret_cast<SPItem *>(desktop->getDocument()->getObjectByRepr(repr));
685 new_item->doWriteTransform(item->transform);
686 new_item->updateRepr();
687
690
691 reprs.push_back(repr);
692 }
693
694 if (did) {
695 DocumentUndo::done(desktop->getDocument(), _("Convert flowed text to text"), INKSCAPE_ICON("text-convert-to-regular"));
696 selection->setReprList(reprs);
697 } else if (ignored) {
698 // no message for (did && ignored) because it is immediately overwritten
701 _("Flowed text(s) must be <b>visible</b> in order to be converted."));
702
703 } else {
706 _("<b>No flowed text(s)</b> to convert in the selection."));
707 }
708
709}
710
711
712Glib::ustring text_relink_shapes_str(gchar const *prop, std::map<Glib::ustring, Glib::ustring> const &old_to_new) {
713 std::vector<Glib::ustring> shapes_url = Glib::Regex::split_simple(" ", prop);
714 Glib::ustring res;
715 for (auto shape_url : shapes_url) {
716 if (shape_url.compare(0, 5, "url(#") != 0 || shape_url.compare(shape_url.size() - 1, 1, ")") != 0) {
717 std::cerr << "text_relink_shapes_str: Invalid shape value: " << shape_url.raw() << std::endl;
718 } else {
719 auto old_id = shape_url.substr(5, shape_url.size() - 6);
720 auto find_it = old_to_new.find(old_id);
721 if (find_it != old_to_new.end()) {
722 res.append("url(#").append(find_it->second).append(") ");
723 } else {
724 std::cerr << "Failed to replace reference " << old_id.raw() << std::endl;
725 }
726 }
727 }
728 // remove trailing space
729 if (!res.empty()) {
730 assert(res.raw().back() == ' ');
731 res.resize(res.size() - 1);
732 }
733 return res;
734}
735
736/*
737 Local Variables:
738 mode:c++
739 c-file-style:"stroustrup"
740 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
741 indent-tabs-mode:nil
742 fill-column:99
743 End:
744*/
745// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
Coord descrim() const
Calculate the descriminant.
Definition affine.cpp:434
Axis-aligned rectangle that can be empty.
Definition rect.h:203
Two-dimensional point that doubles as a vector.
Definition point.h:66
Translation by a vector.
Definition transforms.h:115
static void done(SPDocument *document, Glib::ustring const &event_description, Glib::ustring const &undo_icon, unsigned int object_modified_tag=0)
MessageId flash(MessageType type, char const *message)
Temporarily pushes a message onto the stack.
SPItemRange items()
Returns a range of selected SPItems.
Definition object-set.h:255
boost::enable_if< boost::is_base_of< SPObject, T >, void >::type setList(const std::vector< T * > &objs)
Selects exactly the specified objects.
Definition object-set.h:299
void setReprList(std::vector< XML::Node * > const &list)
Selects the objects with the same IDs as those in list.
void clear()
Unselects all selected objects.
bool isEmpty()
Returns true if no items are selected.
static Preferences * get()
Access the singleton Preferences object.
The set of selected SPObjects for a given document and layer model.
Definition selection.h:80
void set(XML::Node *repr)
Set the selection to an XML node's SPObject.
Definition selection.h:118
Generates the layout for either wrapped or non-wrapped text and stores the result.
Definition Layout-TNG.h:144
Alignment
For expressing paragraph alignment.
Definition Layout-TNG.h:205
iterator end() const
Returns an iterator pointing just past the end of the last glyph, which is also just past the end of ...
Alignment paragraphAlignment(iterator const &it) const
Returns the actual alignment used for the paragraph containing the character pointed to by it.
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 * parent()=0
Get the parent 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.
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 char const * attribute(char const *key) const =0
Get the string representation of a node's attribute.
virtual void removeChild(Node *child)=0
Remove a child of this node.
bool setAttributeSvgDouble(Util::const_char_ptr key, double val)
For attributes where an exponent is allowed.
Definition node.cpp:111
To do: update description of desktop.
Definition desktop.h:149
SPDocument * getDocument() const
Definition desktop.h:189
Inkscape::MessageStack * messageStack() const
Definition desktop.h:160
Inkscape::Selection * getSelection() const
Definition desktop.h:188
SPDocument * doc() const
Definition desktop.h:159
Typed SVG document implementation.
Definition document.h:103
Inkscape::XML::Document * getReprDoc()
Our Inkscape::XML::Document.
Definition document.h:213
int ensureUpToDate(unsigned int object_modified_tag=0)
Repeatedly works on getting the document updated, since sometimes it takes more than one pass to get ...
SPObject * getObjectByRepr(Inkscape::XML::Node *repr) const
Base class for visual SVG elements.
Definition sp-item.h:109
Geom::Affine transform
Definition sp-item.h:138
void doWriteTransform(Geom::Affine const &transform, Geom::Affine const *adv=nullptr, bool compensate=true)
Set a new transform on an object.
Definition sp-item.cpp:1658
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)
void removeAttribute(char const *key)
SPObject * firstChild()
Definition sp-object.h:315
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 * updateRepr(unsigned int flags=SP_OBJECT_WRITE_EXT)
Updates the object's repr based on the object's state.
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.
std::string getUrl() const
Get the id in a URL format.
ChildrenList children
Definition sp-object.h:907
void requestDisplayUpdate(unsigned int flags)
Queues an deferred update of this object's display.
An SVG style object.
Definition style.h:45
void merge(SPStyle const *parent)
Combine style and parent style specifications into a single style specification that preserves (as mu...
Definition style.cpp:843
Glib::ustring writeIfDiff(SPStyle const *base) const
Get CSS string for set properties which are different from the given base style.
Definition style.cpp:799
T< SPAttr::WHITE_SPACE, SPIEnum< SPWhiteSpace > > white_space
white space (svg2)
Definition style.h:176
T< SPAttr::TEXT_ANCHOR, SPIEnum< SPTextAnchor > > text_anchor
Anchor of the text (svg1.1 10.9.1)
Definition style.h:173
T< SPAttr::SHAPE_INSIDE, SPIShapes > shape_inside
SVG2 Text Wrapping.
Definition style.h:179
T< SPAttr::SHAPE_SUBTRACT, SPIShapes > shape_subtract
Definition style.h:180
std::shared_ptr< Css const > css
Editable view implementation.
static char const *const parent
Definition dir-util.cpp:70
TODO: insert short description here.
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
Macro for icon names used in Inkscape.
SPItem * item
Raw stack of active status messages.
static R & release(R &r)
Decrements the reference count of a anchored object.
@ ERROR_MESSAGE
Definition message.h:29
@ WARNING_MESSAGE
Definition message.h:28
Singleton class to access the preferences file in a convenient way.
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
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
void sp_repr_css_unset_property(SPCSSAttr *css, gchar const *name)
Set a style property to "inkscape:unset".
Definition repr-css.cpp:202
C facade to Inkscape::XML::Node.
GList * items
TODO: insert short description here.
TODO: insert short description here.
TODO: insert short description here.
Geom::Affine i2i_affine(SPObject const *src, SPObject const *dest)
Definition sp-item.cpp:1806
TODO: insert short description here.
void sp_textpath_to_text(SPObject *tp)
Definition sp-tspan.cpp:471
bool SP_IS_TEXT_TEXTPATH(SPObject const *obj)
Definition sp-textpath.h:47
TODO: insert short description here.
Interface for XML documents.
Definition document.h:43
virtual Node * createTextNode(char const *content)=0
virtual Node * createElement(char const *name)=0
SPStyle - a style object for SPItem objects.
SPDesktop * desktop
void text_remove_from_path()
void text_flow_shape_subtract()
Glib::ustring text_relink_shapes_str(gchar const *prop, std::map< Glib::ustring, Glib::ustring > const &old_to_new)
void text_flow_into_shape()
void flowtext_to_text()
static void text_remove_all_kerns_recursively(SPObject *o)
void text_remove_all_kerns()
void text_to_glyphs()
static SPItem * text_or_flowtext_in_selection(Inkscape::Selection *selection)
void text_put_on_path()
void text_unflow()
static SPItem * shape_in_selection(Inkscape::Selection *selection)
Glib::ustring sp_te_get_string_multiline(SPItem const *text)
Gets a text-only representation of the given text or flowroot object, replacing line break elements w...
Inkscape::Text::Layout const * te_get_layout(SPItem const *item)