Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
sp-tspan.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-tspan.h"
28
29#include <cstring>
30
31#include <glibmm/i18n.h>
32#include <glibmm/regex.h>
33
34#include "attributes.h"
35#include "text-editing.h"
36
37#include "sp-textpath.h"
38#include "sp-tref.h"
39#include "sp-use-reference.h"
40#include "style.h"
41
42#include "display/curve.h"
43
44#include "livarot/Path.h"
45
46#include "svg/stringstream.h"
48
49
50/*#####################################################
51# SPTSPAN
52#####################################################*/
56
57SPTSpan::~SPTSpan() = default;
58
60 this->readAttr(SPAttr::X);
61 this->readAttr(SPAttr::Y);
62 this->readAttr(SPAttr::DX);
63 this->readAttr(SPAttr::DY);
65
66 // Strip sodipodi:role from SVG 2 flowed text.
67 // this->role = SP_TSPAN_ROLE_UNSPECIFIED;
68 auto text = cast<SPText>(parent);
69 if (text && !(text->has_shape_inside()|| text->has_inline_size())) {
71 }
72
73 // We'll intercept "style" to strip "visibility" property (SVG 1.1 fallback for SVG 2 text) then pass it on.
75
76 SPItem::build(doc, repr);
77}
78
82
83void SPTSpan::set(SPAttr key, const gchar* value) {
84 if (this->attributes.readSingleAttribute(key, value, style, &viewport)) {
85 this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
86 } else {
87 switch (key) {
89 if (value && (!strcmp(value, "line") || !strcmp(value, "paragraph"))) {
91 } else {
93 }
94 break;
95
96 case SPAttr::STYLE:
97 if (value) {
98 Glib::ustring style(value);
99 Glib::RefPtr<Glib::Regex> regex = Glib::Regex::create("visibility\\s*:\\s*hidden;*");
100 Glib::ustring stripped = regex->replace_literal(style, 0, "", static_cast<Glib::Regex::MatchFlags >(0));
102 repr->setAttributeOrRemoveIfEmpty("style", stripped);
103 }
104 // Fall through
105 default:
106 SPItem::set(key, value);
107 break;
108 }
109 }
110}
111
112void SPTSpan::update(SPCtx *ctx, guint flags) {
113 unsigned childflags = flags;
114 if (flags & SP_OBJECT_MODIFIED_FLAG) {
115 childflags |= SP_OBJECT_PARENT_MODIFIED_FLAG;
116 }
117 childflags &= SP_OBJECT_MODIFIED_CASCADE;
118
119 for (auto& ochild: children) {
120 if ( flags || ( ochild.uflags & SP_OBJECT_MODIFIED_FLAG )) {
121 ochild.updateDisplay(ctx, childflags);
122 }
123 }
124
125 SPItem::update(ctx, flags);
126
127 if (flags & ( SP_OBJECT_STYLE_MODIFIED_FLAG |
128 SP_OBJECT_CHILD_MODIFIED_FLAG |
129 SP_TEXT_LAYOUT_MODIFIED_FLAG ) )
130 {
131 SPItemCtx const *ictx = reinterpret_cast<SPItemCtx const *>(ctx);
132
133 double const w = ictx->viewport.width();
134 double const h = ictx->viewport.height();
135 double const em = style->font_size.computed;
136 double const ex = 0.5 * em; // fixme: get x height from pango or libnrtype.
137
138 attributes.update( em, ex, w, h );
139 }
140}
141
142void SPTSpan::modified(unsigned int flags) {
143// SPItem::onModified(flags);
144
145 if (flags & SP_OBJECT_MODIFIED_FLAG) {
146 flags |= SP_OBJECT_PARENT_MODIFIED_FLAG;
147 }
148
149 flags &= SP_OBJECT_MODIFIED_CASCADE;
150
151 for (auto& ochild: children) {
152 if (flags || (ochild.mflags & SP_OBJECT_MODIFIED_FLAG)) {
153 ochild.emitModified(flags);
154 }
155 }
156}
157
160 // find out the ancestor text which holds our layout
161 SPObject const *parent_text = this;
162
163 while (parent_text && !is<SPText>(parent_text)) {
164 parent_text = parent_text->parent;
165 }
166
167 if (parent_text == nullptr) {
168 return bbox;
169 }
170
171 // get the bbox of our portion of the layout
172 return cast<SPText>(parent_text)->layout.bounds(transform,
173 type == SPItem::VISUAL_BBOX,
174 sp_text_get_length_upto(parent_text, this),
175 sp_text_get_length_upto(this, nullptr) - 1);
176}
177
179 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
180 repr = xml_doc->createElement("svg:tspan");
181 }
182
183 this->attributes.writeTo(repr);
184
185 if ( flags&SP_OBJECT_WRITE_BUILD ) {
186 std::vector<Inkscape::XML::Node *> l;
187
188 for (auto& child: children) {
189 Inkscape::XML::Node* c_repr=nullptr;
190
191 if ( is<SPTSpan>(&child) || is<SPTRef>(&child) ) {
192 c_repr = child.updateRepr(xml_doc, nullptr, flags);
193 } else if ( is<SPTextPath>(&child) ) {
194 //c_repr = child.updateRepr(xml_doc, NULL, flags); // shouldn't happen
195 } else if ( is<SPString>(&child) ) {
196 c_repr = xml_doc->createTextNode(cast<SPString>(&child)->string.c_str());
197 }
198
199 if ( c_repr ) {
200 l.push_back(c_repr);
201 }
202 }
203
204 for (auto i = l.rbegin(); i!= l.rend(); ++i) {
205 repr->addChild((*i), nullptr);
207 }
208 } else {
209 for (auto& child: children) {
210 if ( is<SPTSpan>(&child) || is<SPTRef>(&child) ) {
211 child.updateRepr(flags);
212 } else if ( is<SPTextPath>(&child) ) {
213 //c_repr = child->updateRepr(xml_doc, NULL, flags); // shouldn't happen
214 } else if ( is<SPString>(&child) ) {
215 child.getRepr()->setContent(cast<SPString>(&child)->string.c_str());
216 }
217 }
218 }
219
220 SPItem::write(xml_doc, repr, flags);
221
222 return repr;
223}
224
225const char* SPTSpan::typeName() const {
226 return "text-data";
227}
228
229const char* SPTSpan::displayName() const {
230 return _("Text Span");
231}
232
233
234/*#####################################################
235# SPTEXTPATH
236#####################################################*/
238
240 this->startOffset._set = false;
242 this->originalPath = nullptr;
243 this->isUpdating=false;
244
245 // set up the uri reference
246 this->sourcePath = new SPUsePath(this);
248}
249
251 delete this->sourcePath;
252}
253
255 this->readAttr(SPAttr::X);
256 this->readAttr(SPAttr::Y);
257 this->readAttr(SPAttr::DX);
258 this->readAttr(SPAttr::DY);
261 this->readAttr(SPAttr::SIDE);
263
264 this->readAttr(SPAttr::STYLE);
265
266 SPItem::build(doc, repr);
267}
268
270 //this->attributes.~TextTagAttributes();
271
272 if (this->originalPath) {
273 delete this->originalPath;
274 }
275
276 this->originalPath = nullptr;
277
279}
280
281void SPTextPath::set(SPAttr key, const gchar* value) {
282
283 if (this->attributes.readSingleAttribute(key, value, style, &viewport)) {
284 this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
285 } else {
286 switch (key) {
288 this->sourcePath->link((char*)value);
289 break;
290 case SPAttr::SIDE:
291 if (!value) {
292 return;
293 }
294
295 if (strncmp(value, "left", 4) == 0)
297 else if (strncmp(value, "right", 5) == 0)
299 else {
300 std::cerr << "SPTextPath: Bad side value: " << (value?value:"null") << std::endl;
302 }
303 break;
305 this->startOffset.readOrUnset(value);
306 this->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
307 break;
308 default:
309 SPItem::set(key, value);
310 break;
311 }
312 }
313}
314
315void SPTextPath::update(SPCtx *ctx, guint flags) {
316 this->isUpdating = true;
317
318 if ( this->sourcePath->sourceDirty ) {
320 }
321
322 this->isUpdating = false;
323
324 unsigned childflags = (flags & SP_OBJECT_MODIFIED_CASCADE);
325 if (flags & SP_OBJECT_MODIFIED_FLAG) {
326 childflags |= SP_OBJECT_PARENT_MODIFIED_FLAG;
327 }
328
329 for (auto& ochild: children) {
330 if (childflags || (ochild.uflags & (SP_OBJECT_MODIFIED_FLAG | SP_OBJECT_CHILD_MODIFIED_FLAG))) {
331 ochild.updateDisplay(ctx, childflags);
332 }
333 }
334
335 SPItem::update(ctx, flags);
336
337 if (flags & ( SP_OBJECT_STYLE_MODIFIED_FLAG |
338 SP_OBJECT_CHILD_MODIFIED_FLAG |
339 SP_TEXT_LAYOUT_MODIFIED_FLAG ) )
340 {
341 SPItemCtx const *ictx = reinterpret_cast<SPItemCtx const *>(ctx);
342
343 double const w = ictx->viewport.width();
344 double const h = ictx->viewport.height();
345 double const em = style->font_size.computed;
346 double const ex = 0.5 * em; // fixme: get x height from pango or libnrtype.
347
348 attributes.update( em, ex, w, h );
349 }
350}
351
352
354{
355 if ( tp == nullptr ) {
356 return;
357 }
358
360 tp->sourcePath->sourceDirty=false;
361
362 if ( tp->sourcePath->originalPath ) {
363 if (tp->originalPath) {
364 delete tp->originalPath;
365 }
366
367 auto curve_copy = *tp->sourcePath->originalPath;
368 if (tp->side == SP_TEXT_PATH_SIDE_RIGHT) {
369 curve_copy.reverse();
370 }
371
372 auto item = cast<SPItem>(tp->sourcePath->sourceObject);
373 tp->originalPath = new Path;
374 tp->originalPath->LoadPathVector(curve_copy, item->transform, true);
376 }
377}
378
379void SPTextPath::modified(unsigned int flags) {
380// SPItem::onModified(flags);
381
382 if (flags & SP_OBJECT_MODIFIED_FLAG) {
383 flags |= SP_OBJECT_PARENT_MODIFIED_FLAG;
384 }
385
386 flags &= SP_OBJECT_MODIFIED_CASCADE;
387
388 for (auto& ochild: children) {
389 if (flags || (ochild.mflags & SP_OBJECT_MODIFIED_FLAG)) {
390 ochild.emitModified(flags);
391 }
392 }
393}
394
396 if ((flags & SP_OBJECT_WRITE_BUILD) && !repr) {
397 repr = xml_doc->createElement("svg:textPath");
398 }
399
400 this->attributes.writeTo(repr);
401
402 if (this->side == SP_TEXT_PATH_SIDE_RIGHT) {
403 this->setAttribute("side", "right");
404 }
405
406 if (this->startOffset._set) {
407 if (this->startOffset.unit == SVGLength::PERCENT) {
409 os << (this->startOffset.computed * 100.0) << "%";
410 this->setAttribute("startOffset", os.str());
411 } else {
412 /* FIXME: This logic looks rather undesirable if e.g. startOffset is to be
413 in ems. */
414 repr->setAttributeSvgDouble("startOffset", this->startOffset.computed);
415 }
416 }
417
418 if ( this->sourcePath->sourceHref ) {
420 }
421
422 if ( flags & SP_OBJECT_WRITE_BUILD ) {
423 std::vector<Inkscape::XML::Node *> l;
424
425 for (auto& child: children) {
426 Inkscape::XML::Node* c_repr=nullptr;
427
428 if ( is<SPTSpan>(&child) || is<SPTRef>(&child) ) {
429 c_repr = child.updateRepr(xml_doc, nullptr, flags);
430 } else if ( is<SPTextPath>(&child) ) {
431 //c_repr = child->updateRepr(xml_doc, NULL, flags); // shouldn't happen
432 } else if ( is<SPString>(&child) ) {
433 c_repr = xml_doc->createTextNode(cast<SPString>(&child)->string.c_str());
434 }
435
436 if ( c_repr ) {
437 l.push_back(c_repr);
438 }
439 }
440
441 for( auto i = l.rbegin(); i != l.rend(); ++i ) {
442 repr->addChild(*i, nullptr);
444 }
445 } else {
446 for (auto& child: children) {
447 if ( is<SPTSpan>(&child) || is<SPTRef>(&child) ) {
448 child.updateRepr(flags);
449 } else if ( is<SPTextPath>(&child) ) {
450 //c_repr = child.updateRepr(xml_doc, NULL, flags); // shouldn't happen
451 } else if ( is<SPString>(&child) ) {
452 child.getRepr()->setContent(cast<SPString>(&child)->string.c_str());
453 }
454 }
455 }
456
457 SPItem::write(xml_doc, repr, flags);
458
459 return repr;
460}
461
462
464{
465 if (tp && tp->sourcePath) {
466 return tp->sourcePath->getObject();
467 }
468 return nullptr;
469}
470
472{
473 SPObject *text = tp->parent;
474
475 // make a list of textpath children
476 std::vector<Inkscape::XML::Node *> tp_reprs;
477
478 for (auto& o: tp->children) {
479 tp_reprs.push_back(o.getRepr());
480 }
481
482 for (auto i = tp_reprs.rbegin(); i != tp_reprs.rend(); ++i) {
483 // make a copy of each textpath child
484 Inkscape::XML::Node *copy = (*i)->duplicate(text->getRepr()->document());
485 // remove the old repr from under textpath
486 tp->getRepr()->removeChild(*i);
487 // put its copy under text
488 text->getRepr()->addChild(copy, nullptr); // fixme: copy id
489 }
490
491 // set x/y on text (to be near where it was when on path)
492 // Copied from Layout::fitToPathAlign
493 // SVG 2 allows the path to be set by the "path" attribute within <textPath>, so
494 // path may not exist (we don't support this yet).
495 Geom::Point midpoint;
496 if (auto path = cast<SPTextPath>(tp)->originalPath) {
497 SVGLength const startOffset = cast<SPTextPath>(tp)->startOffset;
498 double offset = 0.0;
499 if (startOffset._set) {
500 if (startOffset.unit == SVGLength::PERCENT)
501 offset = startOffset.computed * path->Length();
502 else
503 offset = startOffset.computed;
504 }
505 int unused = 0;
506 Path::cut_position *cut_pos = path->CurvilignToPosition(1, &offset, unused);
507 Geom::Point tangent;
508 path->PointAndTangentAt(cut_pos[0].piece, cut_pos[0].t, midpoint, tangent);
509 } else {
510 std::cerr << "sp_textpath_to_text: no path" << std::endl;
511 }
512 text->getRepr()->setAttributeSvgDouble("x", midpoint[Geom::X]);
513 text->getRepr()->setAttributeSvgDouble("y", midpoint[Geom::Y]);
514
515 //remove textpath
516 tp->deleteObject();
517}
518
519
520/*
521 Local Variables:
522 mode:c++
523 c-file-style:"stroustrup"
524 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
525 indent-tabs-mode:nil
526 fill-column:99
527 End:
528*/
529// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
TODO: insert short description here.
Lookup dictionary for attributes/properties.
SPAttr
Definition attributes.h:27
@ STARTOFFSET
@ XLINK_HREF
@ SODIPODI_ROLE
3x3 matrix representing an affine transformation.
Definition affine.h:70
C height() const
Get the vertical extent of the rectangle.
C width() const
Get the horizontal extent 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
std::string str() const
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.
void setAttributeOrRemoveIfEmpty(Inkscape::Util::const_char_ptr key, Inkscape::Util::const_char_ptr value)
Change an attribute of this node.
Definition node.cpp:167
virtual Document * document()=0
Get the node's associated document.
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
Path and its polyline approximation.
Definition Path.h:93
void ConvertWithBackData(double threshhold, bool relative=false)
Creates a polyline approximation of the path.
void LoadPathVector(Geom::PathVector const &pv, Geom::Affine const &tr, bool doTransformation)
Load a lib2geom Geom::PathVector in this path object.
Typed SVG document implementation.
Definition document.h:101
Base class for visual SVG elements.
Definition sp-item.h:109
void update(SPCtx *ctx, unsigned int flags) override
Definition sp-item.cpp:787
Inkscape::XML::Node * write(Inkscape::XML::Document *xml_doc, Inkscape::XML::Node *repr, unsigned int flags) override
Definition sp-item.cpp:858
void set(SPAttr key, char const *value) override
Definition sp-item.cpp:566
void release() override
Definition sp-item.cpp:542
Geom::Affine transform
Definition sp-item.h:138
Geom::Rect viewport
Definition sp-item.h:140
@ VISUAL_BBOX
Definition sp-item.h:118
void build(SPDocument *document, Inkscape::XML::Node *repr) override
Definition sp-item.cpp:519
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 setAttribute(Inkscape::Util::const_char_ptr key, Inkscape::Util::const_char_ptr value)
SPStyle * style
Represents the style properties, whether from presentation attributes, the style attribute,...
Definition sp-object.h:248
SPObject * parent
Definition sp-object.h:189
void readAttr(char const *key)
Read value of key attribute from XML node into object.
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.
ChildrenList children
Definition sp-object.h:907
void requestDisplayUpdate(unsigned int flags)
Queues an deferred update of this object's display.
T< SPAttr::FONT_SIZE, SPIFontSize > font_size
Size of the font.
Definition style.h:116
void modified(unsigned int flags) override
Definition sp-tspan.cpp:142
void build(SPDocument *doc, Inkscape::XML::Node *repr) override
Definition sp-tspan.cpp:59
~SPTSpan() override
void release() override
Definition sp-tspan.cpp:79
unsigned int role
Definition sp-tspan.h:32
TextTagAttributes attributes
Definition sp-tspan.h:33
void set(SPAttr key, const char *value) override
Definition sp-tspan.cpp:83
Geom::OptRect bbox(Geom::Affine const &transform, SPItem::BBoxType type) const override
Definition sp-tspan.cpp:158
const char * displayName() const override
The item's type name as a translated human string.
Definition sp-tspan.cpp:229
const char * typeName() const override
The item's type name, not node tag name.
Definition sp-tspan.cpp:225
Inkscape::XML::Node * write(Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, unsigned int flags) override
Definition sp-tspan.cpp:178
void update(SPCtx *ctx, unsigned int flags) override
Definition sp-tspan.cpp:112
void build(SPDocument *doc, Inkscape::XML::Node *repr) override
Definition sp-tspan.cpp:254
SVGLength startOffset
Definition sp-textpath.h:32
void modified(unsigned int flags) override
Definition sp-tspan.cpp:379
Inkscape::XML::Node * write(Inkscape::XML::Document *doc, Inkscape::XML::Node *repr, unsigned int flags) override
Definition sp-tspan.cpp:395
SPUsePath * sourcePath
Definition sp-textpath.h:37
~SPTextPath() override
Definition sp-tspan.cpp:250
Path * originalPath
Definition sp-textpath.h:35
void set(SPAttr key, const char *value) override
Definition sp-tspan.cpp:281
bool isUpdating
Definition sp-textpath.h:36
void update(SPCtx *ctx, unsigned int flags) override
Definition sp-tspan.cpp:315
TextTagAttributes attributes
Definition sp-textpath.h:31
void release() override
Definition sp-tspan.cpp:269
TextPathSide side
Definition sp-textpath.h:33
char * sourceHref
void(* user_unlink)(SPObject *user)
void link(char *to)
SPObject * sourceObject
std::optional< Geom::PathVector > originalPath
SPItem * getObject() const
SVG length type.
Definition svg-length.h:22
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
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:1359
void writeTo(Inkscape::XML::Node *node) const
Write out all the contents of attributes to the given node.
Definition sp-text.cpp:1403
void update(double em, double ex, double w, double h)
Update relative values.
Definition sp-text.cpp:1422
const double w
Definition conic-4.cpp:19
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
SPItem * item
double offset
static R & release(R &r)
Decrements the reference count of a anchored object.
void setHrefAttribute(XML::Node &node, Util::const_char_ptr value)
If the 'href' attribute already exists for the given node, then set a new value for it.
static cairo_user_data_key_t key
Ocnode * child[8]
Definition quantize.cpp:33
TODO: insert short description here.
void sp_textpath_to_text(SPObject *tp)
Definition sp-tspan.cpp:471
@ SP_TEXT_PATH_SIDE_RIGHT
Definition sp-textpath.h:22
@ SP_TEXT_PATH_SIDE_LEFT
Definition sp-textpath.h:21
SVG <tref> implementation, see sp-tref.cpp.
void sp_textpath_to_text(SPObject *tp)
Definition sp-tspan.cpp:471
SPItem * sp_textpath_get_path_item(SPTextPath const *tp)
Definition sp-tspan.cpp:463
void refresh_textpath_source(SPTextPath *offset)
Definition sp-tspan.cpp:353
TODO: insert short description here.
@ SP_TSPAN_ROLE_UNSPECIFIED
Definition sp-tspan.h:21
@ SP_TSPAN_ROLE_LINE
Definition sp-tspan.h:23
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
SPStyle - a style object for SPItem objects.
TODO: insert short description here.
unsigned sp_text_get_length_upto(SPObject const *item, SPObject const *upto)
Recursively gets the length of all the SPStrings at or below the given item, before and not including...
std::vector< Texture > unused