Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
sp-text.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * SVG <text> and <tspan> implementation
4 *
5 * Author:
6 * Lauris Kaplinski <lauris@kaplinski.com>
7 * bulia byak <buliabyak@users.sf.net>
8 * Jon A. Cruz <jon@joncruz.org>
9 * Abhishek Sharma
10 *
11 * Copyright (C) 1999-2002 Lauris Kaplinski
12 * Copyright (C) 2000-2001 Ximian, Inc.
13 *
14 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
15 */
16
17/*
18 * fixme:
19 *
20 * These subcomponents should not be items, or alternately
21 * we have to invent set of flags to mark, whether standard
22 * attributes are applicable to given item (I even like this
23 * idea somewhat - Lauris)
24 *
25 */
26
27#include "sp-text.h"
28
29#include <glibmm/i18n.h>
30#include <glibmm/regex.h>
31
32#include <2geom/affine.h>
33
36
37#include "attributes.h"
38#include "desktop-style.h"
39#include "desktop.h"
40#include "document.h"
41#include "layer-manager.h"
42#include "mod360.h"
43#include "preferences.h" // for Preferences
44#include "snap-candidate.h" // for SnapCandidatePoint
45#include "snap-enums.h" // for SnapTargetType
46#include "snap-preferences.h" // for SnapPreferences
47#include "text-editing.h"
48
49#include "sp-desc.h"
50#include "sp-flowregion.h"
51#include "sp-rect.h"
52#include "sp-shape.h"
53#include "sp-textpath.h"
54#include "sp-title.h"
55#include "sp-tref.h"
56#include "sp-tspan.h"
57
59#include "path/path-boolop.h"
60#include "svg/svg.h"
61#include "util/units.h"
62#include "xml/quote.h"
63
64
65// For SVG 2 text flow
66#include "livarot/Path.h"
67#include "livarot/Shape.h"
68#include "display/curve.h"
69
70
71/*#####################################################
72# SPTEXT
73#####################################################*/
76
78{
79 if (css) {
81 }
82};
83
85 this->readAttr(SPAttr::X);
86 this->readAttr(SPAttr::Y);
87 this->readAttr(SPAttr::DX);
88 this->readAttr(SPAttr::DY);
90
91 // textLength and friends
94 SPItem::build(doc, repr);
95 css = nullptr;
96 this->readAttr(SPAttr::SODIPODI_LINESPACING); // has to happen after the styles are read
97}
98
100{
103}
104
105void SPText::set(SPAttr key, const gchar* value) {
106 //std::cout << "SPText::set: " << sp_attribute_name( key ) << ": " << (value?value:"Null") << std::endl;
107
108 if (this->attributes.readSingleAttribute(key, value, style, &viewport)) {
109 this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
110 } else {
111 switch (key) {
113 // convert deprecated tag to css... but only if 'line-height' missing.
114 if (value && !this->style->line_height.set) {
115 this->style->line_height.set = TRUE;
116 this->style->line_height.inherit = FALSE;
117 this->style->line_height.normal = FALSE;
119 this->style->line_height.value = this->style->line_height.computed = sp_svg_read_percentage (value, 1.0);
120 }
121 // Remove deprecated attribute
122 this->removeAttribute("sodipodi:linespacing");
123
124 this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_TEXT_LAYOUT_MODIFIED_FLAG);
125 break;
126
127 default:
128 SPItem::set(key, value);
129 break;
130 }
131 }
132}
133
136
137 this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_TEXT_CONTENT_MODIFIED_FLAG | SP_TEXT_LAYOUT_MODIFIED_FLAG);
138}
139
142
143 this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG | SP_TEXT_CONTENT_MODIFIED_FLAG | SP_TEXT_LAYOUT_MODIFIED_FLAG);
144}
145
146
147void SPText::update(SPCtx *ctx, guint flags) {
148
149 unsigned childflags = (flags & SP_OBJECT_MODIFIED_CASCADE);
150 if (flags & SP_OBJECT_MODIFIED_FLAG) {
151 childflags |= SP_OBJECT_PARENT_MODIFIED_FLAG;
152 }
153
154 // Create temporary list of children
155 std::vector<SPObject *> l;
156 for (auto& child: children) {
157 sp_object_ref(&child, this);
158 l.push_back(&child);
159 }
160
161 for (auto child:l) {
162 if (childflags || (child->uflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) {
163 /* fixme: Do we need transform? */
164 child->updateDisplay(ctx, childflags);
165 }
166 sp_object_unref(child, this);
167 }
168
169 // update ourselves after updating children
170 SPItem::update(ctx, flags);
171
172 if (flags & ( SP_OBJECT_STYLE_MODIFIED_FLAG |
173 SP_OBJECT_CHILD_MODIFIED_FLAG |
174 SP_TEXT_LAYOUT_MODIFIED_FLAG ) )
175 {
176
177 SPItemCtx const *ictx = reinterpret_cast<SPItemCtx const *>(ctx);
178
179 double const w = ictx->viewport.width();
180 double const h = ictx->viewport.height();
181 double const em = style->font_size.computed;
182 double const ex = 0.5 * em; // fixme: get x height from pango or libnrtype.
183
184 attributes.update( em, ex, w, h );
185
186 // Set inline_size computed value if necessary (i.e. if unit is %).
187 if (has_inline_size()) {
189 if (is_horizontal()) {
190 style->inline_size.computed = style->inline_size.value * ictx->viewport.width();
191 } else {
192 style->inline_size.computed = style->inline_size.value * ictx->viewport.height();
193 }
194 }
195 }
196
197 /* fixme: It is not nice to have it here, but otherwise children content changes does not work */
198 /* fixme: Even now it may not work, as we are delayed */
199 /* fixme: So check modification flag everywhere immediate state is used */
200 this->rebuildLayout();
201
202 Geom::OptRect paintbox = this->geometricBounds();
203
204 for (auto &v : views) {
205 auto &sa = view_style_attachments[v.key];
206 sa.unattachAll();
207 auto g = cast<Inkscape::DrawingGroup>(v.drawingitem.get());
208 _clearFlow(g);
209 g->setStyle(style, parent->style);
210 // pass the bbox of this as paintbox (used for paintserver fills)
211 layout.show(g, sa, paintbox);
212 }
213 }
214}
215
216void SPText::modified(guint flags) {
217// SPItem::onModified(flags);
218
219 guint cflags = (flags & SP_OBJECT_MODIFIED_CASCADE);
220
221 if (flags & SP_OBJECT_MODIFIED_FLAG) {
222 cflags |= SP_OBJECT_PARENT_MODIFIED_FLAG;
223 }
224
225 // FIXME: all that we need to do here is to call setStyle, to set the changed
226 // style, but there's no easy way to access the drawing glyphs or texts corresponding to a
227 // text this. Therefore we do here the same as in _update, that is, destroy all items
228 // and create new ones. This is probably quite wasteful.
229 if (flags & ( SP_OBJECT_STYLE_MODIFIED_FLAG )) {
230 Geom::OptRect paintbox = geometricBounds();
231
232 for (auto &v : views) {
233 auto &sa = view_style_attachments[v.key];
234 sa.unattachAll();
235 auto g = cast<Inkscape::DrawingGroup>(v.drawingitem.get());
236 _clearFlow(g);
237 g->setStyle(style, parent->style);
238 layout.show(g, sa, paintbox);
239 }
240 }
241
242 // Create temporary list of children
243 std::vector<SPObject *> l;
244 for (auto& child: children) {
245 sp_object_ref(&child, this);
246 l.push_back(&child);
247 }
248
249 for (auto child:l) {
250 if (cflags || (child->mflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) {
251 child->emitModified(cflags);
252 }
253 sp_object_unref(child, this);
254 }
255}
256
258 if (flags & SP_OBJECT_WRITE_BUILD) {
259 if (!repr) {
260 repr = xml_doc->createElement("svg:text");
261 // we preserve spaces in the text objects we create
262 repr->setAttribute("xml:space", "preserve");
263 }
264
265 std::vector<Inkscape::XML::Node *> l;
266
267 for (auto& child: children) {
268 if (is<SPTitle>(&child) || is<SPDesc>(&child)) {
269 continue;
270 }
271
272 Inkscape::XML::Node *crepr = nullptr;
273
274 if (is<SPString>(&child)) {
275 crepr = xml_doc->createTextNode(cast<SPString>(&child)->string.c_str());
276 } else {
277 crepr = child.updateRepr(xml_doc, nullptr, flags);
278 }
279
280 if (crepr) {
281 l.push_back(crepr);
282 }
283 }
284
285 for (auto i=l.rbegin();i!=l.rend();++i) {
286 repr->addChild(*i, nullptr);
288 }
289 } else {
290 for (auto& child: children) {
291 if (is<SPTitle>(&child) || is<SPDesc>(&child)) {
292 continue;
293 }
294
295 if (is<SPString>(&child)) {
296 child.getRepr()->setContent(cast<SPString>(&child)->string.c_str());
297 } else {
298 child.updateRepr(flags);
299 }
300 }
301 }
302
303 this->attributes.writeTo(repr);
304
305 SPItem::write(xml_doc, repr, flags);
306
307 return repr;
308}
309
310
312 return this->layout.bounds(transform, type == SPItem::VISUAL_BBOX);
313}
314
315Inkscape::DrawingItem* SPText::show(Inkscape::Drawing &drawing, unsigned key, unsigned /*flags*/) {
316 Inkscape::DrawingGroup *flowed = new Inkscape::DrawingGroup(drawing);
317 flowed->setPickChildren(false);
318 flowed->setStyle(this->style, this->parent->style);
319
320 // pass the bbox of the text object as paintbox (used for paintserver fills)
322
323 return flowed;
324}
325
326
327void SPText::hide(unsigned key)
328{
330 for (auto &v : views) {
331 if (v.key == key) {
332 auto g = cast<Inkscape::DrawingGroup>(v.drawingitem.get());
333 _clearFlow(g);
334 }
335 }
336}
337
338const char* SPText::typeName() const {
340 return "text-flow";
341 return "text";
342}
343
344const char* SPText::displayName() const {
345 if (has_inline_size()) {
346 return _("Auto-wrapped text");
347 } else if (has_shape_inside()) {
348 return _("Text in-a-shape");
349 } else {
350 return _("Text");
351 }
352}
353
354gchar* SPText::description() const {
355
356 SPStyle *style = this->style;
357
358 char *n = xml_quote_strdup(style->font_family.value());
359
361 int unit = prefs->getInt("/options/font/unitType", SP_CSS_UNIT_PT);
363 q.quantity *= this->i2doc_affine().descrim();
364 Glib::ustring xs = q.string(sp_style_get_css_unit_string(unit));
365
366 char const *trunc = "";
368
369 if (layout && layout->inputTruncated()) {
370 trunc = _(" [truncated]");
371 }
372
373 char *ret = ( SP_IS_TEXT_TEXTPATH(this)
374 ? g_strdup_printf(_("on path%s (%s, %s)"), trunc, n, xs.c_str())
375 : g_strdup_printf(_("%s (%s, %s)"), trunc, n, xs.c_str()) );
376 return ret;
377}
378
379void SPText::snappoints(std::vector<Inkscape::SnapCandidatePoint> &p, Inkscape::SnapPreferences const *snapprefs) const {
381 // Choose a point on the baseline for snapping from or to, with the horizontal position
382 // of this point depending on the text alignment (left vs. right)
384
385 if (layout != nullptr && layout->outputExists()) {
386 std::optional<Geom::Point> pt = layout->baselineAnchorPoint();
387
388 if (pt) {
390 }
391 }
392 }
393}
394
396{
397 auto text = this;
398 SPStyle *item_style = this->style;
399 if (item_style && text && item_style->shape_inside.set) {
400 SPCSSAttr *css_unset = sp_css_attr_from_style(item_style, SP_STYLE_FLAG_IFSET);
402 sp_repr_css_unset_property(css_unset, "shape-inside");
403 sp_repr_css_attr_unref(css_unset);
404 this->changeCSS(css_unset, "style");
405 } else {
406 css = nullptr;
407 }
408}
409
411{
412 if (css) {
413 this->changeCSS(css, "style");
414 }
415}
416
418 // See if 'shape-inside' has rectangle
420 if (prefs->getBool("/tools/text/use_svg2", true)) {
421 if (style->shape_inside.set) {
422 return xform;
423 }
424 }
425 // we cannot optimize textpath because changing its fontsize will break its match to the path
426
427 if (SP_IS_TEXT_TEXTPATH (this)) {
428 if (!this->_optimizeTextpathText) {
429 return xform;
430 } else {
431 this->_optimizeTextpathText = false;
432 }
433 }
434
435 // we cannot optimize text with textLength because it may show different size than specified
436 if (this->attributes.getTextLength()->_set)
437 return xform;
438
439 if (this->style && this->style->inline_size.set)
440 return xform;
441
442 /* This function takes care of scaling & translation only, we return whatever parts we can't
443 handle. */
444
445// TODO: pjrm tried to use fontsize_expansion(xform) here and it works for text in that font size
446// is scaled more intuitively when scaling non-uniformly; however this necessitated using
447// fontsize_expansion instead of expansion in other places too, where it was not appropriate
448// (e.g. it broke stroke width on copy/pasting of style from horizontally stretched to vertically
449// stretched shape). Using fontsize_expansion only here broke setting the style via font
450// dialog. This needs to be investigated further.
451 double const ex = xform.descrim();
452 if (ex == 0) {
453 return xform;
454 }
455
456 Geom::Affine ret(Geom::Affine(xform).withoutTranslation());
457 ret[0] /= ex;
458 ret[1] /= ex;
459 ret[2] /= ex;
460 ret[3] /= ex;
461
462 // Adjust x/y, dx/dy
463 this->_adjustCoordsRecursive (this, xform * ret.inverse(), ex);
464
465 // Adjust font size
466 this->_adjustFontsizeRecursive (this, ex);
467
468 // Adjust stroke width
470
471 // Adjust pattern fill
472 this->adjust_pattern(xform * ret.inverse());
473
474 // Adjust gradient fill
475 this->adjust_gradient(xform * ret.inverse());
476
477 return ret;
478}
479
481 Geom::OptRect pbox, bbox, dbox;
482 pbox = this->geometricBounds();
483 bbox = this->desktopVisualBounds();
485
486 Geom::Affine const ctm (this->i2dt_affine());
487
488 this->layout.print(ctx,pbox,dbox,bbox,ctm);
489}
490
491/*
492 * Member functions
493 */
494
496{
497
499 layout.wrap_mode = Inkscape::Text::Layout::WRAP_NONE; // Default to SVG 1.1
500
501 if (style) {
502
503 // Strut
504 auto font = FontFactory::get().FaceFromStyle(style);
505 if (font) {
507 }
508 layout.strut *= style->font_size.computed;
509 if (style->line_height.normal ) {
511 } else if (style->line_height.unit == SP_CSS_UNIT_NONE) {
513 } else {
514 if( style->font_size.computed > 0.0 ) {
516 }
517 }
518
519
520 // To do: follow SPItem clip_ref/mask_ref code
521 if (style->shape_inside.set ) {
523 for (auto &&wrap_shape : makeEffectiveShapes()) {
524 layout.appendWrapShape(std::move(wrap_shape));
525 }
526 } else if (has_inline_size()) {
527
529
530 // If both shape_inside and inline_size are set, shape_inside wins out.
531
532 // We construct a rectangle with one dimension set by the computed value of 'inline-size'
533 // and the other dimension set to infinity. Text is laid out starting at the 'x' and 'y'
534 // attribute values. This is handled elsewhere.
535
536 Geom::OptRect opt_frame = get_frame();
537 Geom::Rect frame = *opt_frame;
538
539 Shape shape;
540 int v0 = shape.AddPoint(frame.corner(0));
541 int v1 = shape.AddPoint(frame.corner(1));
542 int v2 = shape.AddPoint(frame.corner(2));
543 int v3 = shape.AddPoint(frame.corner(3));
544 shape.AddEdge(v0, v1);
545 shape.AddEdge(v1, v2);
546 shape.AddEdge(v2, v3);
547 shape.AddEdge(v3, v0);
548
549 auto uncross = std::make_unique<Shape>();
550 uncross->ConvertToShape(&shape);
551 layout.appendWrapShape(std::move(uncross));
552 } else if (style->white_space.value == SP_CSS_WHITE_SPACE_PRE ||
556 }
557
558 } // if (style)
559}
560
561unsigned SPText::_buildLayoutInput(SPObject *object, Inkscape::Text::Layout::OptionalTextTagAttrs const &parent_optional_attrs, unsigned parent_attrs_offset, bool in_textpath)
562{
563 unsigned length = 0;
564 unsigned child_attrs_offset = 0;
566
567 // Per SVG spec, an object with 'display:none' doesn't contribute to text layout.
568 if (object->style->display.computed == SP_CSS_DISPLAY_NONE) {
569 return 0;
570 }
571
572 auto text_object = cast<SPText>(object);
573 auto tspan_object = cast<SPTSpan>(object);
574 auto tref_object = cast<SPTRef>(object);
575 auto textpath_object = cast<SPTextPath>(object);
576
577 if (text_object) {
578
579 bool use_xy = true;
580 bool use_dxdyrotate = true;
581
582 // SVG 2 Text wrapping.
585 use_xy = false;
586 use_dxdyrotate = false;
587 }
588
589 text_object->attributes.mergeInto(&optional_attrs, parent_optional_attrs, parent_attrs_offset, use_xy, use_dxdyrotate);
590
591 // SVG 2 Text wrapping
593
594 // For horizontal text:
595 // 'x' is used to calculate the left/right edges of the rectangle but is not
596 // needed later. If not deleted here, it will cause an incorrect positioning
597 // of the first line.
598 // 'y' is used to determine where the first line box is located and is needed
599 // during the output stage.
600 // For vertical text:
601 // Follow above but exchange 'x' and 'y'.
602 // The SVG 2 spec currently says use the 'x' and 'y' from the <text> element,
603 // if not defined in the <text> element, use the 'x' and 'y' from the first child.
604 // We only look at the <text> element. (Doing otherwise means tracking if
605 // we've found 'x' and 'y' and then creating the Shape at the end.)
606 if (is_horizontal()) {
607 // Horizontal text
609 if (y) {
610 optional_attrs.y.push_back(*y);
611 } else {
612 std::cerr << "SPText::_buildLayoutInput: No 'y' attribute value with horizontal 'inline-size'!" << std::endl;
613 }
614 } else {
615 // Vertical text
617 if (x) {
618 optional_attrs.x.push_back(*x);
619 } else {
620 std::cerr << "SPText::_buildLayoutInput: No 'x' attribute value with vertical 'inline-size'!" << std::endl;
621 }
622 }
623 }
624
625 // set textLength on the entire layout, see note in TNG-Layout.h
626 if (text_object->attributes.getTextLength()->_set) {
627 layout.textLength._set = true;
628 layout.textLength.value = text_object->attributes.getTextLength()->value;
629 layout.textLength.computed = text_object->attributes.getTextLength()->computed;
630 layout.textLength.unit = text_object->attributes.getTextLength()->unit;
631 layout.lengthAdjust = (Inkscape::Text::Layout::LengthAdjust) text_object->attributes.getLengthAdjust();
632 }
633 }
634
635 else if (tspan_object) {
636
637 // x, y attributes are stripped from some tspans marked with role="line" as we do our own line layout.
638 // This should be checked carefully, as it can undo line layout in imported SVG files.
639 bool use_xy = !in_textpath &&
640 (tspan_object->role == SP_TSPAN_ROLE_UNSPECIFIED || !tspan_object->attributes.singleXYCoordinates());
641 bool use_dxdyrotate = true;
642
643 // SVG 2 Text wrapping: see comment above.
646 use_xy = false;
647 use_dxdyrotate = false;
648 }
649
650 tspan_object->attributes.mergeInto(&optional_attrs, parent_optional_attrs, parent_attrs_offset, use_xy, use_dxdyrotate);
651
652 if (tspan_object->role != SP_TSPAN_ROLE_UNSPECIFIED) {
653 // We are doing line wrapping using sodipodi:role="line". New lines have been stripped.
654
655 // Insert paragraph break before text if not first tspan.
656 SPObject *prev_object = object->getPrev();
657 if (prev_object && cast<SPTSpan>(prev_object)) {
658 if (!layout.inputExists()) {
659 // Add an object to store style, needed even if there is no text. When does this happen?
660 layout.appendText("", prev_object->style, prev_object, &optional_attrs);
661 }
663 }
664
665 // Create empty span to store info (any non-empty tspan with sodipodi:role="line" has a child).
666 if (!object->hasChildren()) {
667 layout.appendText("", object->style, object, &optional_attrs);
668 }
669
670 length++; // interpreting line breaks as a character for the purposes of x/y/etc attributes
671 // is a liberal interpretation of the svg spec, but a strict reading would mean
672 // that if the first line is empty the second line would take its place at the
673 // start position. Very confusing.
674 // SVG 2 clarifies, attributes are matched to unicode input characters so line
675 // breaks do match to an x/y/etc attribute.
676 child_attrs_offset--;
677 }
678 }
679
680 else if (tref_object) {
681 tref_object->attributes.mergeInto(&optional_attrs, parent_optional_attrs, parent_attrs_offset, true, true);
682 }
683
684 else if (textpath_object) {
685 in_textpath = true; // This should be made local so we can mix normal text with textpath per SVG 2.
686 textpath_object->attributes.mergeInto(&optional_attrs, parent_optional_attrs, parent_attrs_offset, false, true);
687 optional_attrs.x.clear(); // Hmm, you can use x with horizontal text. So this is probably wrong.
688 optional_attrs.y.clear();
689 }
690
691 else {
692 optional_attrs = parent_optional_attrs;
693 child_attrs_offset = parent_attrs_offset;
694 }
695
696 // Recurse
697 for (auto& child: object->children) {
698 auto str = cast<SPString>(&child);
699 if (str) {
700 Glib::ustring const &string = str->string;
701 // std::cout << " Appending: >" << string << "<" << std::endl;
702 layout.appendText(string, object->style, &child, &optional_attrs, child_attrs_offset + length);
703 length += string.length();
704 } else if (!sp_repr_is_meta_element(child.getRepr())) {
705 /* ^^^^ XML Tree being directly used here while it shouldn't be.*/
706 length += _buildLayoutInput(&child, optional_attrs, child_attrs_offset + length, in_textpath);
707 }
708 }
709
710 return length;
711}
712
713std::unique_ptr<Shape> SPText::getExclusionShape() const
714{
715 auto result = std::make_unique<Shape>(); // Union of all exclusion shapes
716
717 for (auto *href : style->shape_subtract.hrefs) {
718 auto shape = href->getObject();
719 if (!shape) {
720 continue;
721 }
722 if (!shape->curve()) {
723 shape->set_shape();
724 }
725 SPCurve const *curve = shape->curve();
726 if (!curve) {
727 continue;
728 }
729
730 auto temp = std::make_unique<Path>();
731 temp->LoadPathVector(curve->get_pathvector(), shape->getRelativeTransform(this), true);
732
733 auto margin = std::make_unique<Path>();
734 if (shape->style->shape_margin.set) {
735 temp->OutsideOutline(margin.get(), -shape->style->shape_margin.computed, join_round, butt_straight, 20.0);
736 } else {
737 margin = std::move(temp);
738 }
739
740 margin->Convert(0.25); // Convert to polyline
741 auto livarot_shape = std::make_unique<Shape>();
742 margin->Fill(livarot_shape.get(), 0);
743
744 auto uncrossed = std::make_unique<Shape>();
745 uncrossed->ConvertToShape(livarot_shape.get());
746
747 if (result->hasEdges()) {
748 auto shape_temp = std::make_unique<Shape>();
749 shape_temp->Booleen(result.get(), uncrossed.get(), bool_op_union);
750 std::swap(result, shape_temp);
751 } else {
752 result->Copy(uncrossed.get());
753 }
754
755 }
756 return result;
757}
758
759std::unique_ptr<Shape> SPText::getInclusionShape(SPShape *shape) const
760{
761 if (!shape) {
762 return {};
763 }
764 if (!shape->curve()) {
765 shape->set_shape();
766 }
767 auto curve = shape->curve();
768 if (!curve) {
769 return {};
770 }
771
772 bool padding = style->shape_padding.set;
773 double padding_amount = 0.0;
774 if (padding) {
775 padding_amount = std::abs(style->shape_padding.computed);
776 if (padding_amount < 1e-12) {
777 padding = false;
778 }
779 }
780
781 auto pathvector = curve->get_pathvector();
782 flatten(pathvector, fill_nonZero);
783
784 auto temp_path = std::make_unique<Path>();
785 temp_path->LoadPathVector(pathvector, shape->transform, true);
786
787 auto const make_nice_shape = [](std::unique_ptr<Path> const &contour) -> std::unique_ptr<Shape> {
788 auto temp = std::make_unique<Shape>();
789 contour->ConvertWithBackData(1.0);
790 contour->Fill(temp.get(), 0);
791 auto result = std::make_unique<Shape>();
792 result->ConvertToShape(temp.get());
793 return result;
794 };
795
796 if (padding) {
797 auto outline = std::make_unique<Path>();
798 temp_path->Outline(outline.get(), style->shape_padding.computed, join_round, butt_straight, 20.0);
799
800 auto inclusion_shape = make_nice_shape(temp_path);
801 auto thickened_border = make_nice_shape(outline);
802
803 auto result = std::make_unique<Shape>();
804 result->Booleen(inclusion_shape.get(), thickened_border.get(), bool_op_diff);
805 return result;
806 }
807 return make_nice_shape(temp_path);
808}
809
810std::vector<std::unique_ptr<Shape>> SPText::makeEffectiveShapes() const
811{
812 // Find union of all exclusion shapes
813 std::unique_ptr<Shape> exclusion_shape;
814 if (style->shape_subtract.set) {
815 exclusion_shape = getExclusionShape();
816 }
817 bool const has_exclusion = exclusion_shape && exclusion_shape->hasEdges();
818
819 std::vector<std::unique_ptr<Shape>> result;
820 // Find inside shape curves
821 for (auto *href : style->shape_inside.hrefs) {
822 auto obj = href->getObject();
823 if (auto textarea_shape = getInclusionShape(obj)) {
824 if (has_exclusion) {
825 // Subtract exclusion shape
826 auto copy = std::make_unique<Shape>();
827 copy->Booleen(textarea_shape.get(), exclusion_shape.get(), bool_op_diff);
828 textarea_shape = std::move(copy);
829 }
830 result.push_back(std::move(textarea_shape));
831 } else {
832 std::cerr << __FUNCTION__ << ": Failed to get curve." << std::endl;
833 }
834 }
835 return result;
836}
837
838
839// SVG requires one to use the first x/y value found on a child element if x/y not given on text
840// element. TODO: Recurse.
843{
845
846 if (!x) {
847 for (auto& child: children) {
848 if (is<SPTSpan>(&child)) {
849 auto tspan = cast<SPTSpan>(&child);
850 x = tspan->attributes.getFirstXLength();
851 break;
852 }
853 }
854 }
855
856 return x;
857}
858
859
862{
864
865 if (!y) {
866 for (auto& child: children) {
867 if (is<SPTSpan>(&child)) {
868 auto tspan = cast<SPTSpan>(&child);
869 y = tspan->attributes.getFirstYLength();
870 break;
871 }
872 }
873 }
874
875 return y;
876}
877
882
884{
885 layout.clear();
887
889 _buildLayoutInput(this, optional_attrs, 0, false);
890
892
893 for (auto& child: children) {
894 if (is<SPTextPath>(&child)) {
895 SPTextPath const *textpath = cast<SPTextPath>(&child);
896 if (textpath->originalPath != nullptr) {
897#if DEBUG_TEXTLAYOUT_DUMPASTEXT
898 g_print("%s", layout.dumpAsText().c_str());
899#endif
900 layout.fitToPathAlign(textpath->startOffset, *textpath->originalPath);
901 }
902 }
903 }
904#if DEBUG_TEXTLAYOUT_DUMPASTEXT
905 g_print("%s", layout.dumpAsText().c_str());
906#endif
907
908 // set the x,y attributes on role:line spans
909 for (auto& child: children) {
910 if (is<SPTSpan>(&child)) {
911 auto tspan = cast<SPTSpan>(&child);
912 if ((tspan->role != SP_TSPAN_ROLE_UNSPECIFIED)
913 && tspan->attributes.singleXYCoordinates() ) {
915 Geom::Point anchor_point = layout.chunkAnchorPoint(iter);
916 tspan->attributes.setFirstXY(anchor_point);
917 // repr needs to be updated but if we do it here we get a loop.
918 }
919 }
920 }
921}
922
923
924void SPText::_adjustFontsizeRecursive(SPItem *item, double ex, bool is_root)
925{
927
928 if (style && !Geom::are_near(ex, 1.0)) {
929 if (!style->font_size.set && is_root) {
930 style->font_size.set = true;
931 }
933 style->font_size.computed *= ex;
934 style->letter_spacing.computed *= ex;
935 style->word_spacing.computed *= ex;
936 if (style->line_height.unit != SP_CSS_UNIT_NONE &&
940 // No unit on 'line-height' property has special behavior.
941 style->line_height.computed *= ex;
942 }
943 item->updateRepr();
944 }
945
946 for(auto& o: item->children) {
947 if (is<SPItem>(&o))
948 _adjustFontsizeRecursive(cast<SPItem>(&o), ex, false);
949 }
950}
951
955std::optional<Geom::Point> SPText::getBaselinePoint() const
956{
957 if (layout.outputExists()) {
959 }
960 return std::optional<Geom::Point>();
961}
962
963void
965{
966 // Replace '\n' by space.
967 auto string = cast<SPString>(object);
968 if (string) {
969 static Glib::RefPtr<Glib::Regex> r = Glib::Regex::create("\n+");
970 string->string = r->replace(string->string, 0, " ", (Glib::Regex::MatchFlags)0);
971 string->getRepr()->setContent(string->string.c_str());
972 }
973
974 for (auto child : object->childList(false)) {
976 }
977
978 // Add space at end of a line if line is created by sodipodi:role="line".
979 auto tspan = cast<SPTSpan>(object);
980 if (tspan &&
981 tspan->role == SP_TSPAN_ROLE_LINE &&
982 tspan->getNext() != nullptr && // Don't add space at end of last line.
983 !is_svg2) { // SVG2 uses newlines, should not have sodipodi:role.
984
985 std::vector<SPObject *> children = tspan->childList(false);
986
987 // Find last string (could be more than one if there is tspan in the middle of a tspan).
988 for (auto it = children.rbegin(); it != children.rend(); ++it) {
989 auto string = cast<SPString>(*it);
990 if (string) {
991 string->string += ' ';
992 string->getRepr()->setContent(string->string.c_str());
993 break;
994 }
995 }
996 }
997}
998
999// Prepare multi-line text for putting on path.
1000void
1008
1009void SPText::_adjustCoordsRecursive(SPItem *item, Geom::Affine const &m, double ex, bool is_root)
1010{
1011 if (is<SPTSpan>(item))
1012 cast<SPTSpan>(item)->attributes.transform(m, ex, ex, is_root);
1013 // it doesn't matter if we change the x,y for role=line spans because we'll just overwrite them anyway
1014 else if (is<SPText>(item))
1015 cast<SPText>(item)->attributes.transform(m, ex, ex, is_root);
1016 else if (is<SPTextPath>(item))
1017 cast<SPTextPath>(item)->attributes.transform(m, ex, ex, is_root);
1018 else if (is<SPTRef>(item)) {
1019 cast<SPTRef>(item)->attributes.transform(m, ex, ex, is_root);
1020 } else {
1021 g_warning("element is not text");
1022 return;
1023 }
1024
1025 for(auto& o: item->children) {
1026 if (is<SPItem>(&o))
1027 _adjustCoordsRecursive(cast<SPItem>(&o), m, ex, false);
1028 }
1029}
1030
1031
1033{
1034 in_arena->clearChildren();
1035}
1036
1037
1042 for (auto& child: children) {
1043 child.removeAttribute("x");
1044 child.removeAttribute("y");
1045 }
1046}
1047
1052
1053 // New lines can come anywhere, we must search character-by-character.
1054 auto it = layout.begin();
1055 while (it != layout.end()) {
1056 if (layout.characterAt(it) == '\n') {
1057
1058 // Delete newline ('\n').
1059 iterator_pair pair;
1060 auto it_end = it;
1061 it_end.nextCharacter();
1062 sp_te_delete (this, it, it_end, pair);
1063 it = pair.first;
1064
1065 // Insert newline (sodipodi:role="line").
1066 it = sp_te_insert_line(this, it);
1067 }
1068
1069 it.nextCharacter();
1071 }
1072}
1073
1078
1079 // tspans with sodipodi:role="line" are only direct children of a <text> element.
1080 for (auto child : childList(false)) {
1081 auto tspan = cast<SPTSpan>(child); // Could have <desc> or <title>.
1082 if (tspan && tspan->role == SP_TSPAN_ROLE_LINE) {
1083
1084 // Remove sodipodi:role attribute.
1085 tspan->removeAttribute("sodipodi:role");
1086 tspan->updateRepr();
1087
1088 // Insert '/n' if not last line.
1089 // This may screw up dx, dy, rotate attribute counting but... SVG 2 text cannot have these values.
1090 if (tspan != lastChild()) {
1091 tspan->style->white_space.computed = SP_CSS_WHITE_SPACE_PRE; // Set so '\n' is not immediately stripped out before CSS recascaded!
1092 auto last_child = tspan->lastChild();
1093 auto last_string = cast<SPString>(last_child);
1094 if (last_string) {
1095 // Add '\n' to string.
1096 last_string->string += "\n";
1097 last_string->updateRepr();
1098 } else {
1099 // Insert new string with '\n'.
1100 auto tspan_node = tspan->getRepr();
1101 auto xml_doc = tspan_node->document();
1102 tspan_node->appendChild(xml_doc->createTextNode("\n"));
1103 }
1104 }
1105 }
1106 }
1107}
1108
1110{
1111 unsigned mode = style->writing_mode.computed;
1113}
1114
1116{
1117 // If inline size is '0' it is as if it is not set.
1118 return (style->inline_size.set && style->inline_size.value != 0);
1119}
1120
1122{
1123 return (style->shape_inside.set);
1124}
1125
1126// Gets rectangle defined by <text> x, y and inline-size ("infinite" in one direction).
1128{
1129 Geom::OptRect opt_frame;
1130 Geom::Rect frame;
1131
1132 if (has_inline_size()) {
1133 double inline_size = style->inline_size.computed;
1134 //unsigned mode = style->writing_mode.computed;
1135 unsigned anchor = style->text_anchor.computed;
1136 unsigned direction = style->direction.computed;
1137
1138 if (is_horizontal()) {
1139 // horizontal
1140 frame = Geom::Rect::from_xywh(attributes.firstXY()[Geom::X], -100000, inline_size, 200000);
1141 if (anchor == SP_CSS_TEXT_ANCHOR_MIDDLE) {
1142 frame *= Geom::Translate (-inline_size/2.0, 0 );
1143 } else if ( (direction == SP_CSS_DIRECTION_LTR && anchor == SP_CSS_TEXT_ANCHOR_END ) ||
1144 (direction == SP_CSS_DIRECTION_RTL && anchor == SP_CSS_TEXT_ANCHOR_START) ) {
1145 frame *= Geom::Translate (-inline_size, 0);
1146 }
1147 } else {
1148 // vertical
1149 frame = Geom::Rect::from_xywh(-100000, attributes.firstXY()[Geom::Y], 200000, inline_size);
1150 if (anchor == SP_CSS_TEXT_ANCHOR_MIDDLE) {
1151 frame *= Geom::Translate (0, -inline_size/2.0);
1152 } else if (anchor == SP_CSS_TEXT_ANCHOR_END) {
1153 frame *= Geom::Translate (0, -inline_size);
1154 }
1155 }
1156
1157 opt_frame = frame;
1158
1159 } else {
1160 // See if 'shape-inside' has rectangle
1162
1163 if (rectangle) {
1164 double x = rectangle->getAttributeDouble("x", 0.0);
1165 double y = rectangle->getAttributeDouble("y", 0.0);
1166 double width = rectangle->getAttributeDouble("width", 0.0);
1167 double height = rectangle->getAttributeDouble("height", 0.0);
1168 frame = Geom::Rect::from_xywh(x, y, width, height);
1169 opt_frame = frame;
1170 }
1171 }
1172
1173 return opt_frame;
1174}
1175
1176// Find the node of the first rectangle (if it exists) in 'shape-inside'.
1178{
1179 if (style->shape_inside.set) {
1180
1181 for (auto *href : style->shape_inside.hrefs) {
1182 auto *shape = href->getObject();
1183 if (is<SPRect>(shape)) {
1184 auto *item = shape->getRepr();
1185 g_return_val_if_fail(item, nullptr);
1186 assert(strncmp("svg:rect", item->name(), 8) == 0);
1187 return item;
1188 }
1189 }
1190 }
1191
1192 return nullptr;
1193}
1194
1195void SPText::getLinked(std::vector<SPObject *> &objects, LinkedObjectNature direction) const
1196{
1197 if (direction == LinkedObjectNature::ANY || direction == LinkedObjectNature::DEPENDENCY) {
1198 for (auto item : get_all_shape_dependencies()) {
1199 objects.push_back(item);
1200 }
1201 }
1202 SPObject::getLinked(objects, direction);
1203}
1204
1211{
1212 for (auto item : get_all_shape_dependencies()) {
1213 return item;
1214 }
1215 return nullptr;
1216}
1217
1218const std::vector<SPItem *> SPText::get_all_shape_dependencies() const
1219{
1220 std::vector<SPItem *> ret;
1221 if (style->shape_inside.set) {
1222 for (auto *href : style->shape_inside.hrefs) {
1223 ret.push_back(href->getObject());
1224 }
1225 } else if (auto textpath = cast<SPTextPath>(firstChild())) {
1226 ret.push_back(sp_textpath_get_path_item(textpath));
1227 }
1228 return ret;
1229}
1230
1232{
1233 SPDocument *doc = desktop->getDocument();
1234
1235 Inkscape::XML::Document *xml_doc = doc->getReprDoc();
1236 Inkscape::XML::Node *text_repr = xml_doc->createElement("svg:text");
1237 text_repr->setAttribute("xml:space", "preserve"); // we preserve spaces in the text objects we create
1238
1239 auto layer = desktop->layerManager().currentLayer();
1240 g_assert(layer != nullptr);
1241
1242 auto text_object = cast<SPText>(layer->appendChildRepr(text_repr));
1243 g_assert(text_object != nullptr);
1244
1245 // Invert coordinate system?
1246 p0 *= desktop->dt2doc();
1247 p1 *= desktop->dt2doc();
1248
1249 // Pixels to user units
1250 p0 *= layer->i2doc_affine().inverse();
1251 p1 *= layer->i2doc_affine().inverse();
1252
1253 text_repr->setAttributeSvgDouble("x", p0[Geom::X]);
1254 text_repr->setAttributeSvgDouble("y", p0[Geom::Y]);
1255
1256 double inline_size = p1[Geom::X] - p0[Geom::X];
1257
1258 text_object->style->inline_size.setDouble( inline_size );
1259 text_object->style->inline_size.set = true;
1260
1261 Inkscape::XML::Node *text_node = xml_doc->createTextNode("");
1262 text_repr->appendChild(text_node);
1263
1264 //text_object->transform = layer->i2doc_affine().inverse();
1265 text_object->updateRepr();
1266
1267 Inkscape::GC::release(text_repr);
1268 Inkscape::GC::release(text_node);
1269
1270 return text_object;
1271}
1272
1274{
1275 SPDocument *doc = desktop->getDocument();
1276 auto const parent = desktop->layerManager().currentLayer();
1277 assert(parent);
1278
1279 Inkscape::XML::Document *xml_doc = doc->getReprDoc();
1280 Inkscape::XML::Node *text_repr = xml_doc->createElement("svg:text");
1281 text_repr->setAttribute("xml:space", "preserve"); // we preserve spaces in the text objects we create
1282 text_repr->setAttributeOrRemoveIfEmpty("transform", sp_svg_transform_write(parent->i2doc_affine().inverse()));
1283
1284 auto text_object = cast<SPText>(parent->appendChildRepr(text_repr));
1285 g_assert(text_object != nullptr);
1286
1287 // Invert coordinate system?
1288 p0 *= desktop->dt2doc();
1289 p1 *= desktop->dt2doc();
1290 auto const rect = Geom::Rect(p0, p1);
1291
1292 // Create rectangle
1293 Inkscape::XML::Node *rect_repr = xml_doc->createElement("svg:rect");
1294 rect_repr->setAttributeSvgDouble("x", rect.left());
1295 rect_repr->setAttributeSvgDouble("y", rect.top());
1296 rect_repr->setAttributeSvgDouble("width", rect.width());
1297 rect_repr->setAttributeSvgDouble("height", rect.height());
1298
1299 // Find defs, if does not exist, create.
1300 Inkscape::XML::Node *defs_repr = sp_repr_lookup_name (xml_doc->root(), "svg:defs");
1301 if (defs_repr == nullptr) {
1302 defs_repr = xml_doc->createElement("svg:defs");
1303 xml_doc->root()->addChild(defs_repr, nullptr);
1304 }
1305 else Inkscape::GC::anchor(defs_repr);
1306
1307 // Add rectangle to defs.
1308 defs_repr->addChild(rect_repr, nullptr);
1309
1310 // Apply desktop style (do before adding "shape-inside").
1311 sp_desktop_apply_style_tool(desktop, text_repr, "/tools/text", true);
1312 SPCSSAttr *css = sp_repr_css_attr(text_repr, "style" );
1313 sp_repr_css_set_property (css, "white-space", "pre"); // Respect new lines.
1314
1315 // Link rectangle to text
1316 std::string value("url(#");
1317 value += rect_repr->attribute("id");
1318 value += ")";
1319 sp_repr_css_set_property (css, "shape-inside", value.c_str());
1320 sp_repr_css_set(text_repr, css, "style");
1321
1323
1324 /* Create <tspan> */
1325 Inkscape::XML::Node *rtspan = xml_doc->createElement("svg:tspan");
1326 rtspan->setAttribute("sodipodi:role", "line"); // otherwise, why bother creating the tspan?
1327 Inkscape::XML::Node *text_node = xml_doc->createTextNode("");
1328 rtspan->appendChild(text_node);
1329 text_repr->appendChild(rtspan);
1330
1331 Inkscape::GC::release(rtspan);
1332 Inkscape::GC::release(text_repr);
1333 Inkscape::GC::release(text_node);
1334 Inkscape::GC::release(defs_repr);
1335 Inkscape::GC::release(rect_repr);
1336
1337 return text_object;
1338}
1339
1340/*
1341 * TextTagAttributes implementation
1342 */
1343
1344// Not used.
1345// void TextTagAttributes::readFrom(Inkscape::XML::Node const *node)
1346// {
1347// readSingleAttribute(SPAttr::X, node->attribute("x"));
1348// readSingleAttribute(SPAttr::Y, node->attribute("y"));
1349// readSingleAttribute(SPAttr::DX, node->attribute("dx"));
1350// readSingleAttribute(SPAttr::DY, node->attribute("dy"));
1351// readSingleAttribute(SPAttr::ROTATE, node->attribute("rotate"));
1352// readSingleAttribute(SPAttr::TEXTLENGTH, node->attribute("textLength"));
1353// readSingleAttribute(SPAttr::LENGTHADJUST, node->attribute("lengthAdjust"));
1354// }
1355
1356bool TextTagAttributes::readSingleAttribute(SPAttr key, gchar const *value, SPStyle const *style, Geom::Rect const *viewport)
1357{
1358 // std::cout << "TextTagAttributes::readSingleAttribute: key: " << key
1359 // << " value: " << (value?value:"Null") << std::endl;
1360 std::vector<SVGLength> *attr_vector;
1361 bool update_x = false;
1362 bool update_y = false;
1363 switch (key) {
1364 case SPAttr::X: attr_vector = &attributes.x; update_x = true; break;
1365 case SPAttr::Y: attr_vector = &attributes.y; update_y = true; break;
1366 case SPAttr::DX: attr_vector = &attributes.dx; update_x = true; break;
1367 case SPAttr::DY: attr_vector = &attributes.dy; update_y = true; break;
1368 case SPAttr::ROTATE: attr_vector = &attributes.rotate; break;
1369 case SPAttr::TEXTLENGTH:
1371 return true;
1372 break;
1374 attributes.lengthAdjust = (value && !strcmp(value, "spacingAndGlyphs")?
1376 Inkscape::Text::Layout::LENGTHADJUST_SPACING); // default is "spacing"
1377 return true;
1378 break;
1379 default: return false;
1380 }
1381
1382 // FIXME: sp_svg_length_list_read() amalgamates repeated separators. This prevents unset values.
1383 *attr_vector = sp_svg_length_list_read(value);
1384
1385 if( (update_x || update_y) && style != nullptr && viewport != nullptr ) {
1386 double const w = viewport->width();
1387 double const h = viewport->height();
1388 double const em = style->font_size.computed;
1389 double const ex = em * 0.5;
1390 for(auto & it : *attr_vector) {
1391 if( update_x )
1392 it.update( em, ex, w );
1393 if( update_y )
1394 it.update( em, ex, h );
1395 }
1396 }
1397 return true;
1398}
1399
1418
1419void TextTagAttributes::update( double em, double ex, double w, double h )
1420{
1421 for(auto & it : attributes.x) {
1422 it.update( em, ex, w );
1423 }
1424 for(auto & it : attributes.y) {
1425 it.update( em, ex, h );
1426 }
1427 for(auto & it : attributes.dx) {
1428 it.update( em, ex, w );
1429 }
1430 for(auto & it : attributes.dy) {
1431 it.update( em, ex, h );
1432 }
1433}
1434
1436{
1437 if (length._set) {
1438 node->setAttribute(key, length.write());
1439 } else
1441}
1442
1443void TextTagAttributes::writeSingleAttributeVector(Inkscape::XML::Node *node, gchar const *key, std::vector<SVGLength> const &attr_vector)
1444{
1445 if (attr_vector.empty())
1447 else {
1448 Glib::ustring string;
1449
1450 // FIXME: this has no concept of unset values because sp_svg_length_list_read() can't read them back in
1451 for (auto it : attr_vector) {
1452 if (!string.empty()) string += ' ';
1453 string += it.write();
1454 }
1456 }
1457}
1458
1460{
1461 return attributes.x.size() <= 1 && attributes.y.size() <= 1;
1462}
1463
1465{
1466 return !attributes.x.empty() || !attributes.y.empty() || !attributes.dx.empty() || !attributes.dy.empty() || !attributes.rotate.empty();
1467}
1468
1470{
1471 Geom::Point point;
1472 if (attributes.x.empty()) point[Geom::X] = 0.0;
1473 else point[Geom::X] = attributes.x[0].computed;
1474 if (attributes.y.empty()) point[Geom::Y] = 0.0;
1475 else point[Geom::Y] = attributes.y[0].computed;
1476 return point;
1477}
1478
1480{
1481 SVGLength zero_length;
1482 zero_length = 0.0;
1483
1484 if (attributes.x.empty())
1485 attributes.x.resize(1, zero_length);
1486 if (attributes.y.empty())
1487 attributes.y.resize(1, zero_length);
1488 attributes.x[0] = point[Geom::X];
1489 attributes.y[0] = point[Geom::Y];
1490}
1491
1493{
1494 if (!attributes.x.empty()) {
1495 return &attributes.x[0];
1496 } else {
1497 return nullptr;
1498 }
1499}
1500
1502{
1503 if (!attributes.y.empty()) {
1504 return &attributes.y[0];
1505 } else {
1506 return nullptr;
1507 }
1508}
1509
1510// Instance of TextTagAttributes contains attributes as defined by text/tspan element.
1511// output: What will be sent to the rendering engine.
1512// parent_attrs: Attributes collected from all ancestors.
1513// parent_attrs_offset: Where this element fits into the parent_attrs.
1514// copy_xy: Should this elements x, y attributes contribute to output (can preserve set values but not use them... kind of strange).
1515// copy_dxdxrotate: Should this elements dx, dy, rotate attributes contribute to output.
1516void TextTagAttributes::mergeInto(Inkscape::Text::Layout::OptionalTextTagAttrs *output, Inkscape::Text::Layout::OptionalTextTagAttrs const &parent_attrs, unsigned parent_attrs_offset, bool copy_xy, bool copy_dxdyrotate) const
1517{
1518 mergeSingleAttribute(&output->x, parent_attrs.x, parent_attrs_offset, copy_xy ? &attributes.x : nullptr);
1519 mergeSingleAttribute(&output->y, parent_attrs.y, parent_attrs_offset, copy_xy ? &attributes.y : nullptr);
1520 mergeSingleAttribute(&output->dx, parent_attrs.dx, parent_attrs_offset, copy_dxdyrotate ? &attributes.dx : nullptr);
1521 mergeSingleAttribute(&output->dy, parent_attrs.dy, parent_attrs_offset, copy_dxdyrotate ? &attributes.dy : nullptr);
1522 mergeSingleAttribute(&output->rotate, parent_attrs.rotate, parent_attrs_offset, copy_dxdyrotate ? &attributes.rotate : nullptr);
1523 if (attributes.textLength._set) { // only from current node, this is not inherited from parent
1529 }
1530}
1531
1532void TextTagAttributes::mergeSingleAttribute(std::vector<SVGLength> *output_list, std::vector<SVGLength> const &parent_list, unsigned parent_offset, std::vector<SVGLength> const *overlay_list)
1533{
1534 output_list->clear();
1535 if (overlay_list == nullptr) {
1536 if (parent_list.size() > parent_offset)
1537 {
1538 output_list->reserve(parent_list.size() - parent_offset);
1539 std::copy(parent_list.begin() + parent_offset, parent_list.end(), std::back_inserter(*output_list));
1540 }
1541 } else {
1542 output_list->reserve(std::max((int)parent_list.size() - (int)parent_offset, (int)overlay_list->size()));
1543 unsigned overlay_offset = 0;
1544 while (parent_offset < parent_list.size() || overlay_offset < overlay_list->size()) {
1545 SVGLength const *this_item;
1546 if (overlay_offset < overlay_list->size()) {
1547 this_item = &(*overlay_list)[overlay_offset];
1548 overlay_offset++;
1549 parent_offset++;
1550 } else {
1551 this_item = &parent_list[parent_offset];
1552 parent_offset++;
1553 }
1554 output_list->push_back(*this_item);
1555 }
1556 }
1557}
1558
1559void TextTagAttributes::erase(unsigned start_index, unsigned n)
1560{
1561 if (n == 0) return;
1562 if (!singleXYCoordinates()) {
1563 eraseSingleAttribute(&attributes.x, start_index, n);
1564 eraseSingleAttribute(&attributes.y, start_index, n);
1565 }
1566 eraseSingleAttribute(&attributes.dx, start_index, n);
1567 eraseSingleAttribute(&attributes.dy, start_index, n);
1568 eraseSingleAttribute(&attributes.rotate, start_index, n);
1569}
1570
1571void TextTagAttributes::eraseSingleAttribute(std::vector<SVGLength> *attr_vector, unsigned start_index, unsigned n)
1572{
1573 if (attr_vector->size() <= start_index) return;
1574 if (attr_vector->size() <= start_index + n)
1575 attr_vector->erase(attr_vector->begin() + start_index, attr_vector->end());
1576 else
1577 attr_vector->erase(attr_vector->begin() + start_index, attr_vector->begin() + start_index + n);
1578}
1579
1580void TextTagAttributes::insert(unsigned start_index, unsigned n)
1581{
1582 if (n == 0) return;
1583 if (!singleXYCoordinates()) {
1584 insertSingleAttribute(&attributes.x, start_index, n, true);
1585 insertSingleAttribute(&attributes.y, start_index, n, true);
1586 }
1587 insertSingleAttribute(&attributes.dx, start_index, n, false);
1588 insertSingleAttribute(&attributes.dy, start_index, n, false);
1589 insertSingleAttribute(&attributes.rotate, start_index, n, false);
1590}
1591
1592void TextTagAttributes::insertSingleAttribute(std::vector<SVGLength> *attr_vector, unsigned start_index, unsigned n, bool is_xy)
1593{
1594 if (attr_vector->size() <= start_index) return;
1595 SVGLength zero_length;
1596 zero_length = 0.0;
1597 attr_vector->insert(attr_vector->begin() + start_index, n, zero_length);
1598 if (is_xy) {
1599 double begin = start_index == 0 ? (*attr_vector)[start_index + n].computed : (*attr_vector)[start_index - 1].computed;
1600 double diff = ((*attr_vector)[start_index + n].computed - begin) / n; // n tested for nonzero in insert()
1601 for (unsigned i = 0 ; i < n ; i++)
1602 (*attr_vector)[start_index + i] = begin + diff * i;
1603 }
1604}
1605
1607{
1608 if (!singleXYCoordinates()) {
1609 splitSingleAttribute(&attributes.x, index, &second->attributes.x, false);
1610 splitSingleAttribute(&attributes.y, index, &second->attributes.y, false);
1611 }
1615}
1616
1617void TextTagAttributes::splitSingleAttribute(std::vector<SVGLength> *first_vector, unsigned index, std::vector<SVGLength> *second_vector, bool trimZeros)
1618{
1619 second_vector->clear();
1620 if (first_vector->size() <= index) return;
1621 second_vector->resize(first_vector->size() - index);
1622 std::copy(first_vector->begin() + index, first_vector->end(), second_vector->begin());
1623 first_vector->resize(index);
1624 if (trimZeros)
1625 while (!first_vector->empty() && (!first_vector->back()._set || first_vector->back().value == 0.0))
1626 first_vector->resize(first_vector->size() - 1);
1627}
1628
1629void TextTagAttributes::join(TextTagAttributes const &first, TextTagAttributes const &second, unsigned second_index)
1630{
1631 if (second.singleXYCoordinates()) {
1632 attributes.x = first.attributes.x;
1633 attributes.y = first.attributes.y;
1634 } else {
1635 joinSingleAttribute(&attributes.x, first.attributes.x, second.attributes.x, second_index);
1636 joinSingleAttribute(&attributes.y, first.attributes.y, second.attributes.y, second_index);
1637 }
1638 joinSingleAttribute(&attributes.dx, first.attributes.dx, second.attributes.dx, second_index);
1639 joinSingleAttribute(&attributes.dy, first.attributes.dy, second.attributes.dy, second_index);
1641}
1642
1643void TextTagAttributes::joinSingleAttribute(std::vector<SVGLength> *dest_vector, std::vector<SVGLength> const &first_vector, std::vector<SVGLength> const &second_vector, unsigned second_index)
1644{
1645 if (second_vector.empty())
1646 *dest_vector = first_vector;
1647 else {
1648 dest_vector->resize(second_index + second_vector.size());
1649 if (first_vector.size() < second_index) {
1650 std::copy(first_vector.begin(), first_vector.end(), dest_vector->begin());
1651 SVGLength zero_length;
1652 zero_length = 0.0;
1653 std::fill(dest_vector->begin() + first_vector.size(), dest_vector->begin() + second_index, zero_length);
1654 } else
1655 std::copy(first_vector.begin(), first_vector.begin() + second_index, dest_vector->begin());
1656 std::copy(second_vector.begin(), second_vector.end(), dest_vector->begin() + second_index);
1657 }
1658}
1659
1660void TextTagAttributes::transform(Geom::Affine const &matrix, double scale_x, double scale_y, bool extend_zero_length)
1661{
1662 SVGLength zero_length;
1663 zero_length = 0.0;
1664
1665 /* edge testcases for this code:
1666 1) moving text elements whose position is done entirely with transform="...", no x,y attributes
1667 2) unflowing multi-line flowtext then moving it (it has x but not y)
1668 */
1669 unsigned points_count = std::max(attributes.x.size(), attributes.y.size());
1670 if (extend_zero_length && points_count < 1)
1671 points_count = 1;
1672 for (unsigned i = 0 ; i < points_count ; i++) {
1673 Geom::Point point;
1674 if (i < attributes.x.size()) point[Geom::X] = attributes.x[i].computed;
1675 else point[Geom::X] = 0.0;
1676 if (i < attributes.y.size()) point[Geom::Y] = attributes.y[i].computed;
1677 else point[Geom::Y] = 0.0;
1678 point *= matrix;
1679 if (i < attributes.x.size())
1680 attributes.x[i] = point[Geom::X];
1681 else if (point[Geom::X] != 0.0 && extend_zero_length) {
1682 attributes.x.resize(i + 1, zero_length);
1683 attributes.x[i] = point[Geom::X];
1684 }
1685 if (i < attributes.y.size())
1686 attributes.y[i] = point[Geom::Y];
1687 else if (point[Geom::Y] != 0.0 && extend_zero_length) {
1688 attributes.y.resize(i + 1, zero_length);
1689 attributes.y[i] = point[Geom::Y];
1690 }
1691 }
1692 for (auto & it : attributes.dx)
1693 it = it.computed * scale_x;
1694 for (auto & it : attributes.dy)
1695 it = it.computed * scale_y;
1696}
1697
1699{
1700 if( attributes.dx.empty()) {
1701 return 0.0;
1702 }
1703 if( index < attributes.dx.size() ) {
1704 return attributes.dx[index].computed;
1705 } else {
1706 return 0.0; // attributes.dx.back().computed;
1707 }
1708}
1709
1710
1712{
1713 if( attributes.dy.empty() ) {
1714 return 0.0;
1715 }
1716 if( index < attributes.dy.size() ) {
1717 return attributes.dy[index].computed;
1718 } else {
1719 return 0.0; // attributes.dy.back().computed;
1720 }
1721}
1722
1723
1725{
1726 SVGLength zero_length;
1727 zero_length = 0.0;
1728
1729 if (attributes.dx.size() < index + 1) attributes.dx.resize(index + 1, zero_length);
1730 attributes.dx[index] = attributes.dx[index].computed + delta;
1731}
1732
1734{
1735 SVGLength zero_length;
1736 zero_length = 0.0;
1737
1738 if (attributes.dy.size() < index + 1) attributes.dy.resize(index + 1, zero_length);
1739 attributes.dy[index] = attributes.dy[index].computed + delta;
1740}
1741
1743{
1744 SVGLength zero_length;
1745 zero_length = 0.0;
1746
1747 if (adjust[Geom::X] != 0.0) {
1748 if (attributes.dx.size() < index + 1) attributes.dx.resize(index + 1, zero_length);
1749 attributes.dx[index] = attributes.dx[index].computed + adjust[Geom::X];
1750 }
1751 if (adjust[Geom::Y] != 0.0) {
1752 if (attributes.dy.size() < index + 1) attributes.dy.resize(index + 1, zero_length);
1753 attributes.dy[index] = attributes.dy[index].computed + adjust[Geom::Y];
1754 }
1755}
1756
1758{
1759 if( attributes.rotate.empty() ) {
1760 return 0.0;
1761 }
1762 if( index < attributes.rotate.size() ) {
1763 return attributes.rotate[index].computed;
1764 } else {
1765 return attributes.rotate.back().computed;
1766 }
1767}
1768
1769
1771{
1772 SVGLength zero_length;
1773 zero_length = 0.0;
1774
1775 if (attributes.rotate.size() < index + 2) {
1776 if (attributes.rotate.empty())
1777 attributes.rotate.resize(index + 2, zero_length);
1778 else
1779 attributes.rotate.resize(index + 2, attributes.rotate.back());
1780 }
1782}
1783
1784
1785void TextTagAttributes::setRotate(unsigned index, double angle)
1786{
1787 SVGLength zero_length;
1788 zero_length = 0.0;
1789
1790 if (attributes.rotate.size() < index + 2) {
1791 if (attributes.rotate.empty())
1792 attributes.rotate.resize(index + 2, zero_length);
1793 else
1794 attributes.rotate.resize(index + 2, attributes.rotate.back());
1795 }
1796 attributes.rotate[index] = mod360(angle);
1797}
1798
1799
1800/*
1801 Local Variables:
1802 mode:c++
1803 c-file-style:"stroustrup"
1804 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1805 indent-tabs-mode:nil
1806 fill-column:99
1807 End:
1808*/
1809// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
@ butt_straight
Definition LivarotDefs.h:52
@ fill_nonZero
Definition LivarotDefs.h:69
@ bool_op_diff
Definition LivarotDefs.h:79
@ bool_op_union
Definition LivarotDefs.h:77
@ join_round
Definition LivarotDefs.h:62
TODO: insert short description here.
TODO: insert short description here.
3x3 affine transformation matrix.
Lookup dictionary for attributes/properties.
SPAttr
Definition attributes.h:27
@ LENGTHADJUST
@ TEXTLENGTH
@ SODIPODI_LINESPACING
int margin
Definition canvas.cpp:166
std::shared_ptr< FontInstance > FaceFromStyle(SPStyle const *style)
Retrieve a FontInstance from a style object, first trying to use the font-specification,...
3x3 matrix representing an affine transformation.
Definition affine.h:70
Coord descrim() const
Calculate the descriminant.
Definition affine.cpp:434
Affine inverse() const
Compute the inverse matrix.
Definition affine.cpp:388
static CRect from_xywh(Coord x, Coord y, Coord w, Coord h)
Create rectangle from origin and dimensions.
C height() const
Get the vertical extent of the rectangle.
C width() const
Get the horizontal extent of the rectangle.
CPoint corner(unsigned i) const
Return the n-th corner of the rectangle.
Axis-aligned rectangle that can be empty.
Definition rect.h:203
Two-dimensional point that doubles as a vector.
Definition point.h:66
Axis aligned, non-empty rectangle.
Definition rect.h:92
Translation by a vector.
Definition transforms.h:115
void setPickChildren(bool)
Set whether the group returns children from pick calls.
SVG drawing item for display.
virtual void setStyle(SPStyle const *style, SPStyle const *context_style=nullptr)
Process information related to the new style.
SPGroup * currentLayer() const
Returns current top layer.
Preference storage class.
Definition preferences.h:66
bool getBool(Glib::ustring const &pref_path, bool def=false)
Retrieve a Boolean value.
static Preferences * get()
Access the singleton Preferences object.
int getInt(Glib::ustring const &pref_path, int def=0)
Retrieve an integer.
Storing of snapping preferences.
bool isTargetSnappable(Inkscape::SnapTargetType const target) const
void computeEffective(const double &line_height)
Calculate the effective ascent and descent including half "leading".
Holds a position within the glyph output of Layout.
Definition Layout-TNG.h:973
Generates the layout for either wrapped or non-wrapped text and stores the result.
Definition Layout-TNG.h:144
void clear()
Empties everything stored in this class and resets it to its original state, like when it was created...
LengthAdjust lengthAdjust
How do we meet textLength if specified: by letterspacing or by scaling horizontally.
Definition Layout-TNG.h:312
Geom::OptRect bounds(Geom::Affine const &transform, bool with_stroke=false, int start=-1, int length=-1) const
Calculates the smallest rectangle completely enclosing all the glyphs.
bool inputTruncated() const
Definition Layout-TNG.h:231
void validateIterator(iterator *it) const
Checks the validity of the given iterator over the current layout.
static const double LINE_HEIGHT_NORMAL
The CSS spec allows line-height:normal to be whatever the user agent thinks will look good.
Definition Layout-TNG.h:209
void appendControlCode(TextControlCode code, SPObject *source, double width=0.0, double ascent=0.0, double descent=0.0)
Control codes are metadata in the text stream to signify items that occupy real space (unlike style c...
iterator sourceToIterator(SPObject *source) const
Returns an iterator pointing to the first character in the output which was created from the given so...
bool inputExists() const
Queries whether any calls have been made to appendText() or appendControlCode() since the object was ...
Definition Layout-TNG.h:227
Glib::ustring dumpAsText() const
debug and unit test method.
std::optional< Geom::Point > baselineAnchorPoint() const
For left aligned text, the leftmost end of the baseline For rightmost text, the rightmost....
SPCurve convertToCurves(iterator const &from_glyph, iterator const &to_glyph) const
Convert the specified range of characters into their bezier outlines.
void show(DrawingGroup *in_arena, StyleAttachments &style_attachments, Geom::OptRect const &paintbox) const
Adds all the output glyphs to in_arena using the given paintbox.
void fitToPathAlign(SVGLength const &startOffset, Path const &path)
Moves all the glyphs in the structure so that the baseline of all the characters sits neatly along th...
gunichar characterAt(iterator const &it) const
Returns character pointed to by it.
Geom::Point chunkAnchorPoint(iterator const &it) const
This is that value to apply to the x,y attributes of tspan role=line elements, and hence it takes ali...
iterator end() const
Returns an iterator pointing just past the end of the last glyph, which is also just past the end of ...
void appendWrapShape(std::unique_ptr< Shape > shape, DisplayAlign display_align=DISPLAY_ALIGN_BEFORE)
Stores another shape inside which to flow the text.
enum Inkscape::Text::Layout::WrapMode wrap_mode
bool outputExists() const
Returns true if there are some glyphs in this object, ie whether computeFlow() has been called on a n...
Definition Layout-TNG.h:362
void appendText(Glib::ustring const &text, SPStyle *style, SPObject *source, OptionalTextTagAttrs const *optional_attributes, unsigned optional_attributes_offset, Glib::ustring::const_iterator text_begin, Glib::ustring::const_iterator text_end)
adds a new piece of text to the end of the current list of text to be processed.
FontMetrics strut
The strut is the minimum value used in calculating line height.
Definition Layout-TNG.h:669
SVGLength textLength
Gives the length target of this layout, as given by textLength attribute.
Definition Layout-TNG.h:309
void print(SPPrintContext *ctx, Geom::OptRect const &pbox, Geom::OptRect const &dbox, Geom::OptRect const &bbox, Geom::Affine const &ctm) const
Sends all the glyphs to the given print context.
bool calculateFlow()
Takes all the stuff you set with the members above here and creates a load of glyphs for use with the...
LengthAdjust
lengthAdjust values
Definition Layout-TNG.h:169
iterator begin() const
Returns an iterator pointing at the first glyph of the flowed output.
static FontFactory & get(Args &&... args)
Definition statics.h:153
Glib::ustring string(Unit const *u) const
Return a printable string of the value in the specified unit.
Definition units.cpp:515
Interface for refcounted XML nodes.
Definition node.h:80
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.
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 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
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.
Wrapper around a Geom::PathVector object.
Definition curve.h:26
To do: update description of desktop.
Definition desktop.h:149
SPDocument * getDocument() const
Definition desktop.h:189
Geom::Affine const & dt2doc() const
Definition desktop.cpp:1343
Inkscape::LayerManager & layerManager()
Definition desktop.h:287
Typed SVG document implementation.
Definition document.h:103
Geom::Point getDimensions() const
Definition document.cpp:973
Inkscape::XML::Document * getReprDoc()
Our Inkscape::XML::Document.
Definition document.h:213
Base class for visual SVG elements.
Definition sp-item.h:109
Geom::Affine i2dt_affine() const
Returns the transformation from item to desktop coords.
Definition sp-item.cpp:1821
void update(SPCtx *ctx, unsigned int flags) override
Definition sp-item.cpp:780
Inkscape::XML::Node * write(Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, unsigned int flags) override
Definition sp-item.cpp:851
void set(SPAttr key, char const *value) override
Definition sp-item.cpp:559
void release() override
Definition sp-item.cpp:535
void adjust_gradient(Geom::Affine const &postmul, bool set=false)
Definition sp-item.cpp:1426
void adjust_stroke_width_recursive(double ex)
Recursively scale stroke width in item and its children by expansion.
Definition sp-item.cpp:1515
Geom::Affine transform
Definition sp-item.h:138
Geom::Rect viewport
Definition sp-item.h:140
Geom::OptRect desktopVisualBounds() const
Get item's visual bbox in desktop coordinate system.
Definition sp-item.cpp:1049
void adjust_pattern(Geom::Affine const &postmul, bool set=false, PaintServerTransform=TRANSFORM_BOTH)
Definition sp-item.cpp:1380
@ VISUAL_BBOX
Definition sp-item.h:118
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:920
Geom::Affine i2doc_affine() const
Returns the accumulated transformation of the item and all its ancestors, including root's viewport.
Definition sp-item.cpp:1816
void build(SPDocument *document, Inkscape::XML::Node *repr) override
Definition sp-item.cpp:512
std::vector< SPItemView > views
Definition sp-item.h:163
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition sp-object.h:160
Inkscape::XML::Node * repr
Definition sp-object.h:193
void removeAttribute(char const *key)
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,...
SPObject * lastChild()
Definition sp-object.h:318
SPDocument * document
Definition sp-object.h:188
virtual void remove_child(Inkscape::XML::Node *child)
SPObject * firstChild()
Definition sp-object.h:315
void changeCSS(SPCSSAttr *css, char const *attr)
SPStyle * style
Represents the style properties, whether from presentation attributes, the style attribute,...
Definition sp-object.h:248
SPObject * getPrev()
Returns previous object in sibling list or NULL.
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 readAttr(char const *key)
Read value of key attribute from XML node into object.
bool hasChildren() const
Definition sp-object.h:313
Inkscape::XML::Node * getRepr()
Returns the XML representation of tree.
virtual void child_added(Inkscape::XML::Node *child, Inkscape::XML::Node *ref)
virtual void getLinked(std::vector< SPObject * > &objects, LinkedObjectNature direction=LinkedObjectNature::ANY) const
Get objects which are linked to this object as either a source or a target.
LinkedObjectNature
Definition sp-object.h:168
ChildrenList children
Definition sp-object.h:907
void requestDisplayUpdate(unsigned int flags)
Queues an deferred update of this object's display.
Base class for shapes, including <path> element.
Definition sp-shape.h:38
SPCurve const * curve() const
Return a borrowed pointer to the curve (if any exists) or NULL if there is no curve.
Definition sp-shape.cpp:970
virtual void set_shape()
Definition sp-shape.cpp:908
An SVG style object.
Definition style.h:45
T< SPAttr::SHAPE_PADDING, SPILength > shape_padding
Definition style.h:181
T< SPAttr::LINE_HEIGHT, SPILengthOrNormal > line_height
Line height (css2 10.8.1)
Definition style.h:118
T< SPAttr::LETTER_SPACING, SPILengthOrNormal > letter_spacing
letter spacing (css2 16.4)
Definition style.h:153
T< SPAttr::FONT_FAMILY, SPIString > font_family
Font family.
Definition style.h:120
T< SPAttr::DIRECTION, SPIEnum< SPCSSDirection > > direction
text direction (svg1.1)
Definition style.h:161
T< SPAttr::WHITE_SPACE, SPIEnum< SPWhiteSpace > > white_space
white space (svg2)
Definition style.h:176
T< SPAttr::WORD_SPACING, SPILengthOrNormal > word_spacing
word spacing (also css2 16.4)
Definition style.h:155
T< SPAttr::INLINE_SIZE, SPILength > inline_size
Definition style.h:183
T< SPAttr::TEXT_ANCHOR, SPIEnum< SPTextAnchor > > text_anchor
Anchor of the text (svg1.1 10.9.1)
Definition style.h:173
T< SPAttr::DISPLAY, SPIEnum< SPCSSDisplay > > display
display
Definition style.h:207
T< SPAttr::SHAPE_INSIDE, SPIShapes > shape_inside
SVG2 Text Wrapping.
Definition style.h:179
T< SPAttr::WRITING_MODE, SPIEnum< SPCSSWritingMode > > writing_mode
Writing mode (svg1.1 10.7.2, CSS Writing Modes 3)
Definition style.h:163
T< SPAttr::SHAPE_SUBTRACT, SPIShapes > shape_subtract
Definition style.h:180
T< SPAttr::FONT_SIZE, SPIFontSize > font_size
Size of the font.
Definition style.h:116
SVGLength startOffset
Definition sp-textpath.h:32
Path * originalPath
Definition sp-textpath.h:35
void getLinked(std::vector< SPObject * > &objects, LinkedObjectNature direction=LinkedObjectNature::ANY) const override
Get objects which are linked to this object as either a source or a target.
Definition sp-text.cpp:1195
bool has_inline_size() const
Definition sp-text.cpp:1115
void child_added(Inkscape::XML::Node *child, Inkscape::XML::Node *ref) override
Definition sp-text.cpp:134
std::unique_ptr< Shape > getInclusionShape(SPShape *shape) const
Add a single inclusion shape with padding.
Definition sp-text.cpp:759
~SPText() override
Definition sp-text.cpp:77
void newline_to_sodipodi()
Convert new lines in 'inline-size' text to tspans with sodipodi:role="tspan".
Definition sp-text.cpp:1051
void sodipodi_to_newline()
Convert tspans with sodipodi:role="tspans" to ' '.
Definition sp-text.cpp:1077
void print(SPPrintContext *ctx) override
Definition sp-text.cpp:480
SVGLength * _getFirstYLength()
Definition sp-text.cpp:861
SPItem * get_first_shape_dependency()
Get the first shape reference which affects the position and layout of this text item.
Definition sp-text.cpp:1210
void release() override
Definition sp-text.cpp:99
void build(SPDocument *doc, Inkscape::XML::Node *repr) override
Definition sp-text.cpp:84
Inkscape::XML::Node * write(Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, unsigned int flags) override
Definition sp-text.cpp:257
std::unique_ptr< Shape > getExclusionShape() const
Union all exclusion shapes.
Definition sp-text.cpp:713
static void _adjustCoordsRecursive(SPItem *item, Geom::Affine const &m, double ex, bool is_root=true)
when the object is transformed it's nicer to change the font size and coordinates when we can,...
Definition sp-text.cpp:1009
void _buildLayoutInit()
Initializes layout from <text> (i.e.
Definition sp-text.cpp:495
std::optional< Geom::Point > getBaselinePoint() const
Get the position of the baseline point for this text object.
Definition sp-text.cpp:955
SPCurve getNormalizedBpath() const
Converts the text object to its component curves.
Definition sp-text.cpp:878
SPText()
Definition sp-text.cpp:74
void _clearFlow(Inkscape::DrawingGroup *in_arena)
discards the drawing objects representing this text.
Definition sp-text.cpp:1032
void set(SPAttr key, const char *value) override
Definition sp-text.cpp:105
bool has_shape_inside() const
Definition sp-text.cpp:1121
std::unordered_map< unsigned, Inkscape::Text::StyleAttachments > view_style_attachments
Definition sp-text.h:53
void hide_shape_inside()
Definition sp-text.cpp:395
SPCSSAttr * css
Definition sp-text.h:104
void update(SPCtx *ctx, unsigned int flags) override
Definition sp-text.cpp:147
std::vector< std::unique_ptr< Shape > > makeEffectiveShapes() const
Compute the final effective shapes: All inclusion shapes shrunk by the padding, from which we subtrac...
Definition sp-text.cpp:810
static void _adjustFontsizeRecursive(SPItem *item, double ex, bool is_root=true)
Definition sp-text.cpp:924
Inkscape::DrawingItem * show(Inkscape::Drawing &drawing, unsigned int key, unsigned int flags) override
Definition sp-text.cpp:315
void remove_child(Inkscape::XML::Node *child) override
Definition sp-text.cpp:140
void show_shape_inside()
This two functions are useful because layout calculations need text visible for example Calculating a...
Definition sp-text.cpp:410
Geom::Affine set_transform(Geom::Affine const &transform) override
Definition sp-text.cpp:417
Geom::OptRect get_frame()
Definition sp-text.cpp:1127
const char * displayName() const override
The item's type name as a translated human string.
Definition sp-text.cpp:344
Geom::OptRect bbox(Geom::Affine const &transform, SPItem::BBoxType type) const override
Definition sp-text.cpp:311
void rebuildLayout()
Completely recalculates the layout.
Definition sp-text.cpp:883
SVGLength * _getFirstXLength()
Find first x/y values which may be in a descendent element.
Definition sp-text.cpp:842
void remove_newlines()
Definition sp-text.cpp:1001
Inkscape::Text::Layout layout
Definition sp-text.h:52
bool is_horizontal() const
Definition sp-text.cpp:1109
void modified(unsigned int flags) override
Definition sp-text.cpp:216
void snappoints(std::vector< Inkscape::SnapCandidatePoint > &p, Inkscape::SnapPreferences const *snapprefs) const override
Definition sp-text.cpp:379
const char * typeName() const override
The item's type name, not node tag name.
Definition sp-text.cpp:338
TextTagAttributes attributes
Definition sp-text.h:51
bool _optimizeTextpathText
Definition sp-text.h:73
Inkscape::XML::Node * get_first_rectangle()
Definition sp-text.cpp:1177
void hide(unsigned int key) override
Definition sp-text.cpp:327
void remove_svg11_fallback()
Remove 'x' and 'y' values on children (lines) or they will be interpreted as absolute positions when ...
Definition sp-text.cpp:1041
unsigned _buildLayoutInput(SPObject *object, Inkscape::Text::Layout::OptionalTextTagAttrs const &parent_optional_attrs, unsigned parent_attrs_offset, bool in_textpath)
Recursively walks the xml tree adding tags and their contents.
Definition sp-text.cpp:561
const std::vector< SPItem * > get_all_shape_dependencies() const
Definition sp-text.cpp:1218
char * description() const override
Definition sp-text.cpp:354
SVG length type.
Definition svg-length.h:22
float value
Definition svg-length.h:47
void readOrUnset(char const *str, Unit u=NONE, float v=0, float c=0)
bool _set
Definition svg-length.h:41
Unit unit
Definition svg-length.h:44
float computed
Definition svg-length.h:50
A class to store/manipulate directed graphs.
Definition Shape.h:65
int AddEdge(int st, int en)
Definition Shape.cpp:1052
int AddPoint(const Geom::Point x)
Definition Shape.cpp:266
void split(unsigned index, TextTagAttributes *second)
Divides the stored attributes into two, at the given index.
Definition sp-text.cpp:1606
static void insertSingleAttribute(std::vector< SVGLength > *attr_vector, unsigned start_index, unsigned n, bool is_xy)
Does the work for insert().
Definition sp-text.cpp:1592
static void writeSingleAttributeLength(Inkscape::XML::Node *node, gchar const *key, const SVGLength &length)
Writes a single length value to node.
Definition sp-text.cpp:1435
static void splitSingleAttribute(std::vector< SVGLength > *first_vector, unsigned index, std::vector< SVGLength > *second_vector, bool trimZeros)
Does the work for split().
Definition sp-text.cpp:1617
Geom::Point firstXY() const
Returns the first coordinates in the x and y vectors.
Definition sp-text.cpp:1469
void mergeInto(Inkscape::Text::Layout::OptionalTextTagAttrs *output, Inkscape::Text::Layout::OptionalTextTagAttrs const &parent_attrs, unsigned parent_attrs_offset, bool copy_xy, bool copy_dxdyrotate) const
Implements the rules for overlaying the contents of the class (treated as the child object) on top of...
Definition sp-text.cpp:1516
SVGLength * getTextLength()
SVGLength * getFirstXLength()
Gets first value in the x vector as an SVGLength.
Definition sp-text.cpp:1492
void setRotate(unsigned index, double angle)
Sets rotate vector at the given index.
Definition sp-text.cpp:1785
bool readSingleAttribute(SPAttr key, gchar const *value, SPStyle const *style, Geom::Rect const *viewport)
Process the parameters from the set() function of SPObject.
Definition sp-text.cpp:1356
bool singleXYCoordinates() const
For tspan role=line elements we should not use the set x,y coordinates since that would overrule the ...
Definition sp-text.cpp:1459
double getDx(unsigned index)
Gets current value of dx vector at index.
Definition sp-text.cpp:1698
SVGLength * getFirstYLength()
Gets first value in the y vector as an SVGLength.
Definition sp-text.cpp:1501
void addToDy(unsigned index, double delta)
Adds the given value to the dy vector at the given index.
Definition sp-text.cpp:1733
void addToDxDy(unsigned index, Geom::Point const &adjust)
Adds the given values to the dx and dy vectors at the given index.
Definition sp-text.cpp:1742
void addToRotate(unsigned index, double delta)
Adds the given value to the rotate vector at the given index.
Definition sp-text.cpp:1770
void setFirstXY(Geom::Point &point)
Sets the first coordinates in the x and y vectors.
Definition sp-text.cpp:1479
void transform(Geom::Affine const &matrix, double scale_x, double scale_y, bool extend_zero_length=false)
Applies the given transformation to the stored coordinates.
Definition sp-text.cpp:1660
void erase(unsigned start_index, unsigned n)
Deletes all the values from all the vectors beginning at start_index and extending for n fields.
Definition sp-text.cpp:1559
static void mergeSingleAttribute(std::vector< SVGLength > *output_list, std::vector< SVGLength > const &parent_list, unsigned parent_offset, std::vector< SVGLength > const *overlay_list=nullptr)
Does mergeInto() for one member of attributes.
Definition sp-text.cpp:1532
bool anyAttributesSet() const
Returns false if all of the vectors are zero length.
Definition sp-text.cpp:1464
static void writeSingleAttributeVector(Inkscape::XML::Node *node, gchar const *key, std::vector< SVGLength > const &attr_vector)
Does the reverse of readSingleAttribute(), converting a vector<> to its SVG string representation and...
Definition sp-text.cpp:1443
void writeTo(Inkscape::XML::Node *node) const
Write out all the contents of attributes to the given node.
Definition sp-text.cpp:1400
Inkscape::Text::Layout::OptionalTextTagAttrs attributes
This holds the actual values.
void insert(unsigned start_index, unsigned n)
Inserts n new values in all the stored vectors at start_index.
Definition sp-text.cpp:1580
void addToDx(unsigned index, double delta)
Adds the given value to the dx vector at the given index.
Definition sp-text.cpp:1724
double getRotate(unsigned index)
Gets current value of rotate vector at index.
Definition sp-text.cpp:1757
void update(double em, double ex, double w, double h)
Update relative values.
Definition sp-text.cpp:1419
static void joinSingleAttribute(std::vector< SVGLength > *dest_vector, std::vector< SVGLength > const &first_vector, std::vector< SVGLength > const &second_vector, unsigned second_index)
Does the work for join().
Definition sp-text.cpp:1643
double getDy(unsigned index)
Gets current value of dy vector at index.
Definition sp-text.cpp:1711
static void eraseSingleAttribute(std::vector< SVGLength > *attr_vector, unsigned start_index, unsigned n)
Does the work for erase().
Definition sp-text.cpp:1571
void join(TextTagAttributes const &first, TextTagAttributes const &second, unsigned second_index)
Overwrites all the attributes contained in this object with the given parameters by putting first at ...
Definition sp-text.cpp:1629
const double w
Definition conic-4.cpp:19
std::shared_ptr< Css const > css
Css & result
void sp_desktop_apply_style_tool(SPDesktop *desktop, Inkscape::XML::Node *repr, Glib::ustring const &tool_path, bool with_text)
Apply the desktop's current style or the tool style to repr.
Editable view implementation.
static char const *const parent
Definition dir-util.cpp:70
Group belonging to an SVG drawing element.
TODO: insert short description here.
The data describing a single loaded font.
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
SPItem * item
Inkscape::XML::Node * node
double mod360(double const x)
Returns x wrapped around to between 0 and less than 360, or 0 if x isn't finite.
Definition mod360.cpp:19
TODO: insert short description here.
bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON)
static R & anchor(R &r)
Increments the reference count of a anchored object.
Definition gc-anchored.h:92
static R & release(R &r)
Decrements the reference count of a anchored object.
@ SNAPSOURCE_TEXT_ANCHOR
Definition snap-enums.h:55
@ SNAPTARGET_TEXT_ANCHOR
Definition snap-enums.h:118
@ SNAPTARGET_TEXT_BASELINE
Definition snap-enums.h:119
static cairo_user_data_key_t key
int mode
void flatten(Geom::PathVector &pathv, FillRule fill_rule)
Boolean operations.
int size
Singleton class to access the preferences file in a convenient way.
Ocnode * child[8]
Definition quantize.cpp:33
Ocnode ** ref
Definition quantize.cpp:32
char * xml_quote_strdup(char const *src)
Definition quote.cpp:43
TODO: insert short description here.
void uncross(std::list< Point > &loop)
Definition rdm-area.cpp:102
void sp_repr_css_set(Node *repr, SPCSSAttr *css, gchar const *attr)
Sets an attribute (e.g.
Definition repr-css.cpp:265
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
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
bool sp_repr_is_meta_element(const Inkscape::XML::Node *node)
Determine if the node is a 'title', 'desc' or 'metadata' element.
Inkscape::XML::Node const * sp_repr_lookup_name(Inkscape::XML::Node const *repr, gchar const *name, gint maxdepth)
Some utility classes to store various kinds of snap candidates.
TODO: insert short description here.
SPObject * sp_object_unref(SPObject *object, SPObject *owner)
Decrease reference count of object, with possible debugging and finalization.
SPObject * sp_object_ref(SPObject *object, SPObject *owner)
Increase reference count of object, with possible debugging.
SPItem * create_text_with_rectangle(SPDesktop *desktop, Geom::Point p0, Geom::Point p1)
Definition sp-text.cpp:1273
SPItem * create_text_with_inline_size(SPDesktop *desktop, Geom::Point p0, Geom::Point p1)
Definition sp-text.cpp:1231
void remove_newlines_recursive(SPObject *object, bool is_svg2)
Definition sp-text.cpp:964
TODO: insert short description here.
bool SP_IS_TEXT_TEXTPATH(SPObject const *obj)
Definition sp-textpath.h:47
SPItem * sp_textpath_get_path_item(SPTextPath const *tp)
Definition sp-tspan.cpp:463
SVG <tref> implementation, see sp-tref.cpp.
TODO: insert short description here.
@ SP_TSPAN_ROLE_UNSPECIFIED
Definition sp-tspan.h:21
@ SP_TSPAN_ROLE_LINE
Definition sp-tspan.h:23
The optional attributes which can be applied to a SVG text or related tag.
Definition Layout-TNG.h:184
Interface for XML documents.
Definition document.h:43
virtual Node * createTextNode(char const *content)=0
virtual Node * createElement(char const *name)=0
Unused.
Definition sp-object.h:94
Contains transformations to document/viewport and the viewport size.
Definition sp-item.h:92
Geom::Rect viewport
Viewport size.
Definition sp-item.h:97
Definition curve.h:24
@ SP_CSS_TEXT_ANCHOR_MIDDLE
@ SP_CSS_TEXT_ANCHOR_START
@ SP_CSS_TEXT_ANCHOR_END
@ SP_CSS_DISPLAY_NONE
@ SP_CSS_WRITING_MODE_LR_TB
@ SP_CSS_WRITING_MODE_RL_TB
@ SP_CSS_DIRECTION_RTL
@ SP_CSS_DIRECTION_LTR
@ SP_CSS_WHITE_SPACE_PRELINE
@ SP_CSS_WHITE_SPACE_PREWRAP
@ SP_CSS_WHITE_SPACE_PRE
static const unsigned SP_STYLE_FLAG_IFSET(1<< 0)
@ SP_FONT_SIZE_LENGTH
@ SP_CSS_UNIT_PT
@ SP_CSS_UNIT_PERCENT
@ SP_CSS_UNIT_NONE
@ SP_CSS_UNIT_EM
@ SP_CSS_UNIT_EX
SPCSSAttr * sp_css_attr_from_style(SPStyle const *const style, guint const flags)
Definition style.cpp:1409
gchar const * sp_style_get_css_unit_string(int unit)
Definition style.cpp:1305
std::string sp_svg_transform_write(Geom::Affine const &transform)
std::vector< SVGLength > sp_svg_length_list_read(gchar const *str)
double sp_svg_read_percentage(char const *str, double def)
int delta
int index
SPDesktop * desktop
double height
double width
Inkscape::Text::Layout::iterator sp_te_insert_line(SPItem *item, Inkscape::Text::Layout::iterator &position)
inserts a new line break at the given position in a text or flowtext object.
Inkscape::Text::Layout const * te_get_layout(SPItem const *item)
bool sp_te_delete(SPItem *item, Inkscape::Text::Layout::iterator const &start, Inkscape::Text::Layout::iterator const &end, iterator_pair &iter_pair)
std::pair< Inkscape::Text::Layout::iterator, Inkscape::Text::Layout::iterator > iterator_pair