1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Inkscape::Text::Layout - text layout engine output functions using iterators
4  *
5  * Authors:
6  *   Richard Hughes <cyreve@users.sf.net>
7  *
8  * Copyright (C) 2005 Richard Hughes
9  *
10  * Released under GNU GPL v2+, read the file 'COPYING' for more information.
11  */
12 #include "Layout-TNG.h"
13 #include "livarot/Path.h"
14 #include "font-instance.h"
15 #include "svg/svg-length.h"
16 #include <2geom/transforms.h>
17 #include <2geom/line.h>
18 #include "style.h"
19 
20 namespace Inkscape {
21 namespace Text {
22 
23 // Comment 18 Sept 2019:
24 // Cursor code might be simpler if Character was turned into a proper
25 // class and kept track of its absolute postion and extent. This would
26 // make handling multi-line text (including multi-line text using
27 // 'white-space:pre') easier. This would also avoid problems where
28 // 'dx','dy' moved the character a long distance from its nominal
29 // position.
30 
_cursorXOnLineToIterator(unsigned line_index,double local_x,double local_y) const31 Layout::iterator Layout::_cursorXOnLineToIterator(unsigned line_index, double local_x, double local_y) const
32 {
33     unsigned char_index = _lineToCharacter(line_index);
34     int best_char_index = -1;
35     double best_difference = DBL_MAX;
36 
37     if (char_index == _characters.size()) return end();
38     for ( ; char_index < _characters.size() ; char_index++) {
39         if (_characters[char_index].chunk(this).in_line != line_index) break;
40         //if (_characters[char_index].char_attributes.is_mandatory_break) break;
41         if (!_characters[char_index].char_attributes.is_cursor_position) continue;
42 
43         double delta_x =
44           _characters[char_index].x +
45           _characters[char_index].span(this).x_start +
46           _characters[char_index].chunk(this).left_x -
47           local_x;
48 
49         double delta_y =
50           _characters[char_index].span(this).y_offset +
51           _characters[char_index].line(this).baseline_y -
52           local_y;
53 
54         double this_difference = std::sqrt(delta_x*delta_x + delta_y*delta_y);
55 
56         if (this_difference < best_difference) {
57           best_difference = this_difference;
58           best_char_index = char_index;
59         }
60     }
61 
62     // also try the very end of a para (not lines though because the space wraps)
63     if (char_index == _characters.size() || _characters[char_index].char_attributes.is_mandatory_break) {
64 
65         double delta_x = 0.0;
66         double delta_y = 0.0;
67 
68         if (char_index == 0) {
69             delta_x = _spans.front().x_end + _chunks.front().left_x - local_x;
70             delta_y = _spans.front().y_offset + _spans.front().line(this).baseline_y - local_y;
71         } else {
72             delta_x = _characters[char_index - 1].span(this).x_end    + _characters[char_index - 1].chunk(this).left_x - local_x;
73             delta_y = _characters[char_index - 1].span(this).y_offset + _characters[char_index - 1].line(this).baseline_y - local_y;
74         }
75 
76         double this_difference = std::sqrt(delta_x*delta_x + delta_y*delta_y);
77 
78         if (this_difference < best_difference) {
79             best_char_index = char_index;
80             best_difference = this_difference;
81         }
82     }
83 
84 
85     if (best_char_index == -1) {
86         best_char_index = char_index;
87     }
88 
89     if (best_char_index == _characters.size()) {
90         return end();
91     }
92 
93     return iterator(this, best_char_index);
94 }
95 
_getChunkWidth(unsigned chunk_index) const96 double Layout::_getChunkWidth(unsigned chunk_index) const
97 {
98     double chunk_width = 0.0;
99     unsigned span_index;
100     if (chunk_index) {
101         span_index = _lineToSpan(_chunks[chunk_index].in_line);
102         for ( ; span_index < _spans.size() && _spans[span_index].in_chunk < chunk_index ; span_index++){};
103     } else {
104         span_index = 0;
105     }
106 
107     for ( ; span_index < _spans.size() && _spans[span_index].in_chunk == chunk_index ; span_index++) {
108         chunk_width = std::max(chunk_width, (double)std::max(_spans[span_index].x_start, _spans[span_index].x_end));
109     }
110 
111     return chunk_width;
112 }
113 
114 /* getting the cursor position for a mouse click is not as simple as it might
115 seem. The two major problems are flows set up in multiple columns and large
116 dy adjustments such that text does not belong to the line it appears to. In
117 the worst case it's possible to have two characters on top of each other, in
118 which case the one we pick is arbitrary.
119 
120 This is a 3-stage (2 pass) algorithm:
121 1) search all the spans to see if the point is contained in one, if so take
122    that. Note that this will collect all clicks from the current UI because
123    of how the hit detection of nrarena objects works.
124 2) if that fails, run through all the chunks finding a best guess of the one
125    the user wanted. This is the one whose y coordinate is nearest, or if
126    there's a tie, the x.
127 3) search in that chunk using x-coordinate only to find the position.
128 */
getNearestCursorPositionTo(double x,double y) const129 Layout::iterator Layout::getNearestCursorPositionTo(double x, double y) const
130 {
131     if (_lines.empty()) return begin();
132     double local_x = x;
133     double local_y = y;
134 
135     if (_path_fitted) {
136         Path::cut_position position = const_cast<Path*>(_path_fitted)->PointToCurvilignPosition(Geom::Point(x, y));
137         local_x = const_cast<Path*>(_path_fitted)->PositionToLength(position.piece, position.t);
138         return _cursorXOnLineToIterator(0, local_x + _chunks.front().left_x);
139     }
140 
141     if (_directions_are_orthogonal(_blockProgression(), TOP_TO_BOTTOM)) {
142         local_x = y;
143         local_y = x;
144     }
145 
146     // stage 1:
147     for (const auto & _span : _spans) {
148         double span_left, span_right;
149         if (_span.x_start < _span.x_end) {
150             span_left = _span.x_start;
151             span_right = _span.x_end;
152         } else {
153             span_left = _span.x_end;
154             span_right = _span.x_start;
155         }
156 
157         double y_line = _span.line(this).baseline_y + _span.baseline_shift + _span.y_offset;
158         if (   local_x >= _chunks[_span.in_chunk].left_x + span_left
159             && local_x <= _chunks[_span.in_chunk].left_x + span_right
160             && local_y >= y_line - _span.line_height.ascent
161             && local_y <= y_line + _span.line_height.descent) {
162           return _cursorXOnLineToIterator(_chunks[_span.in_chunk].in_line, local_x, local_y);
163         }
164     }
165 
166     // stage 2:
167     unsigned span_index = 0;
168     unsigned chunk_index;
169     int best_chunk_index = -1;
170     double best_y_range = DBL_MAX;
171     double best_x_range = DBL_MAX;
172     for (chunk_index = 0 ; chunk_index < _chunks.size() ; chunk_index++) {
173         FontMetrics line_height;
174         line_height *= 0.0; // Set all metrics to zero.
175         double chunk_width = 0.0;
176         for ( ; span_index < _spans.size() && _spans[span_index].in_chunk == chunk_index ; span_index++) {
177             line_height.max(_spans[span_index].line_height);
178             chunk_width = std::max(chunk_width, (double)std::max(_spans[span_index].x_start, _spans[span_index].x_end));
179         }
180         double this_y_range;
181         if (local_y < _lines[_chunks[chunk_index].in_line].baseline_y - line_height.ascent)
182             this_y_range = _lines[_chunks[chunk_index].in_line].baseline_y - line_height.ascent - local_y;
183         else if (local_y > _lines[_chunks[chunk_index].in_line].baseline_y + line_height.descent)
184             this_y_range = local_y - (_lines[_chunks[chunk_index].in_line].baseline_y + line_height.descent);
185         else
186             this_y_range = 0.0;
187         if (this_y_range <= best_y_range) {
188             if (this_y_range < best_y_range) best_x_range = DBL_MAX;
189             double this_x_range;
190             if (local_x < _chunks[chunk_index].left_x)
191                 this_x_range = _chunks[chunk_index].left_x - local_y;
192             else if (local_x > _chunks[chunk_index].left_x + chunk_width)
193                 this_x_range = local_x - (_chunks[chunk_index].left_x + chunk_width);
194             else
195                 this_x_range = 0.0;
196             if (this_x_range < best_x_range) {
197                 best_y_range = this_y_range;
198                 best_x_range = this_x_range;
199                 best_chunk_index = chunk_index;
200             }
201         }
202     }
203 
204     // stage 3:
205     if (best_chunk_index == -1) return begin();    // never happens
206     return _cursorXOnLineToIterator(_chunks[best_chunk_index].in_line, local_x, local_y);
207 }
208 
getLetterAt(double x,double y) const209 Layout::iterator Layout::getLetterAt(double x, double y) const
210 {
211     Geom::Point point(x, y);
212 
213     double rotation;
214     for (iterator it = begin() ; it != end() ; it.nextCharacter()) {
215         Geom::Rect box = characterBoundingBox(it, &rotation);
216         // todo: rotation
217         if (box.contains(point)) return it;
218     }
219     return end();
220 }
221 
sourceToIterator(SPObject * source) const222 Layout::iterator Layout::sourceToIterator(SPObject *source /*, Glib::ustring::const_iterator text_iterator*/) const
223 {
224     unsigned source_index;
225     if (_characters.empty()) return end();
226     for (source_index = 0 ; source_index < _input_stream.size() ; source_index++)
227         if (_input_stream[source_index]->source == source) break;
228     if (source_index == _input_stream.size()) return end();
229 
230     unsigned char_index = _sourceToCharacter(source_index);
231 
232     // Fix a bug when hidding content in flow box element
233     if (char_index >= _characters.size())
234     return end();
235 
236     if (_input_stream[source_index]->Type() != TEXT_SOURCE)
237         return iterator(this, char_index);
238 
239     return iterator(this, char_index);
240     /* This code was never used, the text_iterator argument was "NULL" in all calling code
241     InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_input_stream[source_index]);
242 
243     if (text_iterator <= text_source->text_begin) return iterator(this, char_index);
244     if (text_iterator >= text_source->text_end) {
245         if (source_index == _input_stream.size() - 1) return end();
246         return iterator(this, _sourceToCharacter(source_index + 1));
247     }
248     Glib::ustring::const_iterator iter_text = text_source->text_begin;
249     for ( ; char_index < _characters.size() ; char_index++) {
250         if (iter_text == text_iterator)
251             return iterator(this, char_index);
252         iter_text++;
253     }
254     return end(); // never happens
255     */
256 }
257 
glyphBoundingBox(iterator const & it,double * rotation) const258 Geom::OptRect Layout::glyphBoundingBox(iterator const &it, double *rotation) const
259 {
260    if (rotation) *rotation = _glyphs[it._glyph_index].rotation;
261    return _glyphs[it._glyph_index].span(this).font->BBox(_glyphs[it._glyph_index].glyph);
262 }
263 
characterAnchorPoint(iterator const & it) const264 Geom::Point Layout::characterAnchorPoint(iterator const &it) const
265 {
266     if (_characters.empty())
267         return _empty_cursor_shape.position;
268     if (it._char_index == _characters.size()) {
269         return Geom::Point(_chunks.back().left_x + _spans.back().x_end, _lines.back().baseline_y + _spans.back().baseline_shift);
270     } else {
271         return Geom::Point(_characters[it._char_index].chunk(this).left_x
272                              + _spans[_characters[it._char_index].in_span].x_start
273                              + _characters[it._char_index].x,
274                          _characters[it._char_index].line(this).baseline_y
275                              + _characters[it._char_index].span(this).baseline_shift);
276     }
277 }
278 
baselineAnchorPoint() const279 std::optional<Geom::Point> Layout::baselineAnchorPoint() const
280 {
281     iterator pos = this->begin();
282     Geom::Point left_pt = this->characterAnchorPoint(pos);
283     pos.thisEndOfLine();
284     Geom::Point right_pt = this->characterAnchorPoint(pos);
285 
286     if (this->_blockProgression() == LEFT_TO_RIGHT || this->_blockProgression() == RIGHT_TO_LEFT) {
287         left_pt = Geom::Point(left_pt[Geom::Y], left_pt[Geom::X]);
288         right_pt = Geom::Point(right_pt[Geom::Y], right_pt[Geom::X]);
289     }
290 
291     switch (this->paragraphAlignment(pos)) {
292         case LEFT:
293         case FULL:
294             return left_pt;
295             break;
296         case CENTER:
297             return (left_pt + right_pt)/2; // middle point
298             break;
299         case RIGHT:
300             return right_pt;
301             break;
302         default:
303             return std::optional<Geom::Point>();
304             break;
305     }
306 }
307 
baseline() const308 Geom::Path Layout::baseline() const
309 {
310     iterator pos = this->begin();
311     Geom::Point left_pt = this->characterAnchorPoint(pos);
312     pos.thisEndOfLine();
313     Geom::Point right_pt = this->characterAnchorPoint(pos);
314 
315     if (this->_blockProgression() == LEFT_TO_RIGHT || this->_blockProgression() == RIGHT_TO_LEFT) {
316         left_pt = Geom::Point(left_pt[Geom::Y], left_pt[Geom::X]);
317         right_pt = Geom::Point(right_pt[Geom::Y], right_pt[Geom::X]);
318     }
319 
320     Geom::Path baseline;
321     baseline.start(left_pt);
322     baseline.appendNew<Geom::LineSegment>(right_pt);
323 
324     return baseline;
325 }
326 
327 
chunkAnchorPoint(iterator const & it) const328 Geom::Point Layout::chunkAnchorPoint(iterator const &it) const
329 {
330     unsigned chunk_index;
331 
332     if (_chunks.empty())
333         return Geom::Point(0.0, 0.0);
334 
335     if (_characters.empty())
336         chunk_index = 0;
337     else if (it._char_index == _characters.size())
338         chunk_index = _chunks.size() - 1;
339     else chunk_index = _characters[it._char_index].span(this).in_chunk;
340 
341     Alignment alignment = _paragraphs[_lines[_chunks[chunk_index].in_line].in_paragraph].alignment;
342     if (alignment == LEFT || alignment == FULL)
343         return Geom::Point(_chunks[chunk_index].left_x, _lines[_chunks[chunk_index].in_line].baseline_y);
344 
345     double chunk_width = _getChunkWidth(chunk_index);
346     if (alignment == RIGHT)
347         return Geom::Point(_chunks[chunk_index].left_x + chunk_width, _lines[_chunks[chunk_index].in_line].baseline_y);
348     //centre
349     return Geom::Point(_chunks[chunk_index].left_x + chunk_width * 0.5, _lines[_chunks[chunk_index].in_line].baseline_y);
350 }
351 
characterBoundingBox(iterator const & it,double * rotation) const352 Geom::Rect Layout::characterBoundingBox(iterator const &it, double *rotation) const
353 {
354     Geom::Point top_left, bottom_right;
355     unsigned char_index = it._char_index;
356 
357     if (_path_fitted) {
358         double cluster_half_width = 0.0;
359         for (int glyph_index = _characters[char_index].in_glyph ; _glyphs.size() != glyph_index ; glyph_index++) {
360             if (_glyphs[glyph_index].in_character != char_index) break;
361             cluster_half_width += _glyphs[glyph_index].advance;
362         }
363         cluster_half_width *= 0.5;
364 
365         double midpoint_offset = _characters[char_index].span(this).x_start + _characters[char_index].x + cluster_half_width;
366         int unused = 0;
367         Path::cut_position *midpoint_otp = const_cast<Path*>(_path_fitted)->CurvilignToPosition(1, &midpoint_offset, unused);
368         if (midpoint_offset >= 0.0 && midpoint_otp != nullptr && midpoint_otp[0].piece >= 0) {
369             Geom::Point midpoint;
370             Geom::Point tangent;
371             Span const &span = _characters[char_index].span(this);
372 
373             const_cast<Path*>(_path_fitted)->PointAndTangentAt(midpoint_otp[0].piece, midpoint_otp[0].t, midpoint, tangent);
374             top_left[Geom::X] = midpoint[Geom::X] - cluster_half_width;
375             top_left[Geom::Y] = midpoint[Geom::Y] - span.line_height.ascent;
376             bottom_right[Geom::X] = midpoint[Geom::X] + cluster_half_width;
377             bottom_right[Geom::Y] = midpoint[Geom::Y] + span.line_height.descent;
378             Geom::Point normal = tangent.cw();
379             top_left += span.baseline_shift * normal;
380             bottom_right += span.baseline_shift * normal;
381             if (rotation)
382                 *rotation = atan2(tangent[1], tangent[0]);
383         }
384         g_free(midpoint_otp);
385     } else {
386         if (it._char_index == _characters.size()) {
387             top_left[Geom::X] = bottom_right[Geom::X] = _chunks.back().left_x + _spans.back().x_end;
388             char_index--;
389         } else {
390             double span_x = _spans[_characters[it._char_index].in_span].x_start + _characters[it._char_index].chunk(this).left_x;
391             top_left[Geom::X] = span_x + _characters[it._char_index].x;
392             if (it._char_index + 1 == _characters.size() || _characters[it._char_index + 1].in_span != _characters[it._char_index].in_span)
393                 bottom_right[Geom::X] = _spans[_characters[it._char_index].in_span].x_end + _characters[it._char_index].chunk(this).left_x;
394             else
395                 bottom_right[Geom::X] = span_x + _characters[it._char_index + 1].x;
396         }
397 
398         double baseline_y = _characters[char_index].line(this).baseline_y + _characters[char_index].span(this).baseline_shift;
399         if (_directions_are_orthogonal(_blockProgression(), TOP_TO_BOTTOM)) {
400 	    double span_height = _spans[_characters[char_index].in_span].line_height.emSize();
401 	    top_left[Geom::Y] = top_left[Geom::X];
402             top_left[Geom::X] = baseline_y - span_height * 0.5;
403             bottom_right[Geom::Y] = bottom_right[Geom::X];
404             bottom_right[Geom::X] = baseline_y + span_height * 0.5;
405         } else {
406             top_left[Geom::Y] = baseline_y - _spans[_characters[char_index].in_span].line_height.ascent;
407             bottom_right[Geom::Y] = baseline_y + _spans[_characters[char_index].in_span].line_height.descent;
408         }
409 
410         if (rotation) {
411             if (it._glyph_index == -1)
412                 *rotation = 0.0;
413             else if (it._glyph_index == (int)_glyphs.size())
414                 *rotation = _glyphs.back().rotation;
415             else
416                 *rotation = _glyphs[it._glyph_index].rotation;
417         }
418     }
419 
420     return Geom::Rect(top_left, bottom_right);
421 }
422 
createSelectionShape(iterator const & it_start,iterator const & it_end,Geom::Affine const & transform) const423 std::vector<Geom::Point> Layout::createSelectionShape(iterator const &it_start, iterator const &it_end, Geom::Affine const &transform) const
424 {
425     std::vector<Geom::Point> quads;
426     unsigned char_index;
427     unsigned end_char_index;
428 
429     if (it_start._char_index < it_end._char_index) {
430         char_index = it_start._char_index;
431         end_char_index = it_end._char_index;
432     } else {
433         char_index = it_end._char_index;
434         end_char_index = it_start._char_index;
435     }
436     for ( ; char_index < end_char_index ; ) {
437         if (_characters[char_index].in_glyph == -1) {
438             char_index++;
439             continue;
440         }
441         double char_rotation = _glyphs[_characters[char_index].in_glyph].rotation;
442         unsigned span_index = _characters[char_index].in_span;
443 
444         Geom::Point top_left, bottom_right;
445         if (_path_fitted || char_rotation != 0.0) {
446             Geom::Rect box = characterBoundingBox(iterator(this, char_index), &char_rotation);
447             top_left = box.min();
448             bottom_right = box.max();
449             char_index++;
450         } else {   // for straight text we can be faster by combining all the character boxes in a span into one box
451             double span_x = _spans[span_index].x_start + _spans[span_index].chunk(this).left_x;
452             top_left[Geom::X] = span_x + _characters[char_index].x;
453             while (char_index < end_char_index && _characters[char_index].in_span == span_index)
454                 char_index++;
455             if (char_index == _characters.size() || _characters[char_index].in_span != span_index)
456                 bottom_right[Geom::X] = _spans[span_index].x_end + _spans[span_index].chunk(this).left_x;
457             else
458                 bottom_right[Geom::X] = span_x + _characters[char_index].x;
459 
460             double baseline_y = _spans[span_index].line(this).baseline_y + _spans[span_index].baseline_shift;
461             double vertical_scale = _glyphs.back().vertical_scale;
462             double offset_y = _spans[span_index].y_offset;
463 
464             if (_directions_are_orthogonal(_blockProgression(), TOP_TO_BOTTOM)) {
465                 double span_height = vertical_scale * _spans[span_index].line_height.emSize();
466                 top_left[Geom::Y] = top_left[Geom::X];
467                 top_left[Geom::X] = offset_y + baseline_y - span_height * 0.5;
468                 bottom_right[Geom::Y] = bottom_right[Geom::X];
469                 bottom_right[Geom::X] = offset_y + baseline_y + span_height * 0.5;
470             } else {
471                 top_left[Geom::Y] = offset_y + baseline_y - vertical_scale * _spans[span_index].line_height.ascent;
472                 bottom_right[Geom::Y] = offset_y + baseline_y + vertical_scale * _spans[span_index].line_height.descent;
473             }
474         }
475 
476         Geom::Rect char_box(top_left, bottom_right);
477         if (char_box.dimensions()[Geom::X] == 0.0 || char_box.dimensions()[Geom::Y] == 0.0)
478             continue;
479         Geom::Point center_of_rotation((top_left[Geom::X] + bottom_right[Geom::X]) * 0.5,
480                                      top_left[Geom::Y] + _spans[span_index].line_height.ascent);
481         Geom::Affine total_transform = Geom::Translate(-center_of_rotation) * Geom::Rotate(char_rotation) * Geom::Translate(center_of_rotation) * transform;
482         for(int i = 0; i < 4; i ++)
483             quads.push_back(char_box.corner(i) * total_transform);
484     }
485     return quads;
486 }
487 
queryCursorShape(iterator const & it,Geom::Point & position,double & height,double & rotation) const488 void Layout::queryCursorShape(iterator const &it, Geom::Point &position, double &height, double &rotation) const
489 {
490     if (_characters.empty()) {
491         position = _empty_cursor_shape.position;
492         height = _empty_cursor_shape.height;
493         rotation = _empty_cursor_shape.rotation;
494     } else {
495         // we want to cursor to be positioned where the left edge of a character that is about to be typed will be.
496         // this means x & rotation are the current values but y & height belong to the previous character.
497         // this isn't quite right because dx attributes will be moved along, but it's good enough
498         Span const *span;
499         if (_path_fitted) {
500             // text on a path
501             double x;
502             if (it._char_index >= _characters.size()) {
503                 span = &_spans.back();
504                 x = span->x_end + _chunks.back().left_x - _chunks[0].left_x;
505             } else {
506                 span = &_spans[_characters[it._char_index].in_span];
507                 x = _chunks[span->in_chunk].left_x + span->x_start + _characters[it._char_index].x - _chunks[0].left_x;
508                 if (_directions_are_orthogonal(_blockProgression(), TOP_TO_BOTTOM))
509                 	x -= span->line_height.descent;
510                 if (it._char_index != 0)
511                     span = &_spans[_characters[it._char_index - 1].in_span];
512             }
513             double path_length = const_cast<Path*>(_path_fitted)->Length();
514             double x_on_path = x;
515             if (x_on_path < 0.0) x_on_path = 0.0;
516 
517             int unused = 0;
518             // as far as I know these functions are const, they're just not marked as such
519             Path::cut_position *path_parameter_list = const_cast<Path*>(_path_fitted)->CurvilignToPosition(1, &x_on_path, unused);
520             Path::cut_position path_parameter;
521             if (path_parameter_list != nullptr && path_parameter_list[0].piece >= 0)
522                 path_parameter = path_parameter_list[0];
523             else {
524                 path_parameter.piece = _path_fitted->descr_cmd.size() - 1;
525                 path_parameter.t = 0.9999;   // 1.0 will get the wrong tangent
526             }
527             g_free(path_parameter_list);
528 
529             Geom::Point point;
530             Geom::Point tangent;
531             const_cast<Path*>(_path_fitted)->PointAndTangentAt(path_parameter.piece, path_parameter.t, point, tangent);
532             if (x < 0.0)
533                 point += x * tangent;
534             if (x > path_length )
535                 point += (x - path_length) * tangent;
536             if (_directions_are_orthogonal(_blockProgression(), TOP_TO_BOTTOM)) {
537                 rotation = atan2(-tangent[Geom::X], tangent[Geom::Y]);
538                 position[Geom::X] = point[Geom::Y] - tangent[Geom::X] * span->baseline_shift;
539                 position[Geom::Y] = point[Geom::X] + tangent[Geom::Y] * span->baseline_shift;
540             } else {
541                 rotation = atan2(tangent);
542                 position[Geom::X] = point[Geom::X] - tangent[Geom::Y] * span->baseline_shift;
543                 position[Geom::Y] = point[Geom::Y] + tangent[Geom::X] * span->baseline_shift;
544             }
545 
546         } else {
547             // text is not on a path
548 
549             bool last_char_is_newline = false;
550             if (it._char_index >= _characters.size()) {
551                 span = &_spans.back();
552                 position[Geom::X] = _chunks[span->in_chunk].left_x + span->x_end;
553                 rotation = _glyphs.empty() ? 0.0 : _glyphs.back().rotation;
554 
555                 // Check if last character is new line.
556                 if (_characters.back().the_char == '\n') {
557                     last_char_is_newline = true;
558                     position[Geom::X] = chunkAnchorPoint(it)[Geom::X];
559                 }
560             } else {
561                 span = &_spans[_characters[it._char_index].in_span];
562                 position[Geom::X] = _chunks[span->in_chunk].left_x + span->x_start + _characters[it._char_index].x;
563                 if (it._glyph_index == -1) {
564                     rotation = 0.0;
565                 } else if(it._glyph_index == 0) {
566                     rotation = _glyphs.empty() ? 0.0 : _glyphs[0].rotation;
567                 } else{
568                     rotation = _glyphs[it._glyph_index - 1].rotation;
569                 }
570                 // the first char in a line wants to have the y of the new line, so in that case we don't switch to the previous span
571                 if (it._char_index != 0 && _characters[it._char_index - 1].chunk(this).in_line == _chunks[span->in_chunk].in_line)
572                     span = &_spans[_characters[it._char_index - 1].in_span];
573             }
574             position[Geom::Y] = span->line(this).baseline_y + span->baseline_shift + span->y_offset;
575 
576             if (last_char_is_newline) {
577                 // Move cursor to empty new line.
578                 double vertical_scale = _glyphs.empty() ? 1.0 : _glyphs.back().vertical_scale;
579                 if (_directions_are_orthogonal(_blockProgression(), TOP_TO_BOTTOM)) {
580                     // Vertical text
581                     position[Geom::Y] -= vertical_scale * span->line_height.emSize();
582                 } else {
583                     position[Geom::Y] += vertical_scale * span->line_height.emSize();
584                 }
585             }
586         }
587 
588         // up to now *position is the baseline point, not the final point which will be the bottom of the descent
589         double vertical_scale = _glyphs.empty() ? 1.0 : _glyphs.back().vertical_scale;
590 
591         if (_directions_are_orthogonal(_blockProgression(), TOP_TO_BOTTOM)) {
592 	    // Vertical text
593 	    height = vertical_scale * span->line_height.emSize();
594             rotation += M_PI / 2;
595             std::swap(position[Geom::X], position[Geom::Y]);
596             position[Geom::X] -= vertical_scale * sin(rotation) * height * 0.5;
597             position[Geom::Y] += vertical_scale * cos(rotation) * height * 0.5;
598         } else {
599 	    // Horizontal text
600             double caret_slope_run = 0.0, caret_slope_rise = 1.0;
601             if (span->font)
602                 const_cast<font_instance*>(span->font)->FontSlope(caret_slope_run, caret_slope_rise);
603             double caret_slope = atan2(caret_slope_run, caret_slope_rise);
604             height = vertical_scale * (span->line_height.emSize()) / cos(caret_slope);
605             rotation += caret_slope;
606             position[Geom::X] -= sin(rotation) * vertical_scale * span->line_height.descent;
607             position[Geom::Y] += cos(rotation) * vertical_scale * span->line_height.descent;
608         }
609     }
610 }
611 
isHidden(iterator const & it) const612 bool Layout::isHidden(iterator const &it) const
613 {
614   return _characters[it._char_index].line(this).hidden;
615 }
616 
617 
getSourceOfCharacter(iterator const & it,SPObject ** source,Glib::ustring::iterator * text_iterator) const618 void Layout::getSourceOfCharacter(iterator const &it, SPObject **source, Glib::ustring::iterator *text_iterator) const
619 {
620     if (it._char_index >= _characters.size()) {
621         *source = nullptr;
622         return;
623     }
624     InputStreamItem *stream_item = _input_stream[_spans[_characters[it._char_index].in_span].in_input_stream_item];
625     *source = stream_item->source;
626     if (text_iterator && stream_item->Type() == TEXT_SOURCE) {
627         InputStreamTextSource *text_source = dynamic_cast<InputStreamTextSource *>(stream_item);
628 
629         // In order to return a non-const iterator in text_iterator, do the const_cast here.
630         // Note that, although ugly, it is safe because we do not write to *iterator anywhere.
631         Glib::ustring::iterator text_iter = const_cast<Glib::ustring *>(text_source->text)->begin();
632 
633         unsigned char_index = it._char_index;
634         unsigned original_input_source_index = _spans[_characters[char_index].in_span].in_input_stream_item;
635         // confusing algorithm because the iterator goes forwards while the index goes backwards.
636         // It's just that it's faster doing it that way
637         while (char_index && _spans[_characters[char_index - 1].in_span].in_input_stream_item == original_input_source_index) {
638             ++text_iter;
639             char_index--;
640         }
641 
642         if (text_iterator) {
643             *text_iterator = text_iter;
644         }
645     }
646 }
647 
simulateLayoutUsingKerning(iterator const & from,iterator const & to,OptionalTextTagAttrs * result) const648 void Layout::simulateLayoutUsingKerning(iterator const &from, iterator const &to, OptionalTextTagAttrs *result) const
649 {
650     SVGLength zero_length;
651     zero_length = 0.0;
652 
653     result->x.clear();
654     result->y.clear();
655     result->dx.clear();
656     result->dy.clear();
657     result->rotate.clear();
658     if (to._char_index <= from._char_index)
659         return;
660     result->dx.reserve(to._char_index - from._char_index);
661     result->dy.reserve(to._char_index - from._char_index);
662     result->rotate.reserve(to._char_index - from._char_index);
663     for (unsigned char_index = from._char_index ; char_index < to._char_index ; char_index++) {
664         if (!_characters[char_index].char_attributes.is_char_break)
665             continue;
666         if (char_index == 0)
667             continue;
668         if (_characters[char_index].chunk(this).in_line != _characters[char_index - 1].chunk(this).in_line)
669             continue;
670 
671         unsigned prev_cluster_char_index;
672         for (prev_cluster_char_index = char_index - 1 ;
673              prev_cluster_char_index != 0 && !_characters[prev_cluster_char_index].char_attributes.is_cursor_position ;
674              prev_cluster_char_index--){};
675         if (_characters[char_index].span(this).in_chunk == _characters[char_index - 1].span(this).in_chunk) {
676             // dx is zero for the first char in a chunk
677             // this algorithm works by comparing the summed widths of the glyphs with the observed
678             // difference in x coordinates of characters, and subtracting the two to produce the x kerning.
679             double glyphs_width = 0.0;
680             if (_characters[prev_cluster_char_index].in_glyph != -1)
681                 for (int glyph_index = _characters[prev_cluster_char_index].in_glyph ; glyph_index < _characters[char_index].in_glyph ; glyph_index++)
682                     glyphs_width += _glyphs[glyph_index].advance;
683             if (_characters[char_index].span(this).direction == RIGHT_TO_LEFT)
684                 glyphs_width = -glyphs_width;
685 
686             double dx = (_characters[char_index].x + _characters[char_index].span(this).x_start
687                          - _characters[prev_cluster_char_index].x - _characters[prev_cluster_char_index].span(this).x_start)
688                         - glyphs_width;
689 
690 
691             InputStreamItem *input_item = _input_stream[_characters[char_index].span(this).in_input_stream_item];
692             if (input_item->Type() == TEXT_SOURCE) {
693                 SPStyle const *style = static_cast<InputStreamTextSource*>(input_item)->style;
694                 if (_characters[char_index].char_attributes.is_white)
695                     dx -= style->word_spacing.computed * getTextLengthMultiplierDue();
696                 if (_characters[char_index].char_attributes.is_cursor_position)
697                     dx -= style->letter_spacing.computed * getTextLengthMultiplierDue();
698                 dx -= getTextLengthIncrementDue();
699             }
700 
701             if (fabs(dx) > 0.0001) {
702                 result->dx.resize(char_index - from._char_index + 1, zero_length);
703                 result->dx.back() = dx;
704             }
705         }
706         double dy = _characters[char_index].span(this).baseline_shift - _characters[prev_cluster_char_index].span(this).baseline_shift;
707         if (fabs(dy) > 0.0001) {
708             result->dy.resize(char_index - from._char_index + 1, zero_length);
709             result->dy.back() = dy;
710         }
711         if (_characters[char_index].in_glyph != -1 && _glyphs[_characters[char_index].in_glyph].rotation != 0.0) {
712             result->rotate.resize(char_index - from._char_index + 1, zero_length);
713             result->rotate.back() = _glyphs[_characters[char_index].in_glyph].rotation;
714         }
715     }
716 }
717 
718 #define PREV_START_OF_ITEM(this_func)                                                    \
719     {                                                                                    \
720         _cursor_moving_vertically = false;                                               \
721         if (_char_index == 0) return false;                                              \
722         _char_index--;                                                                   \
723         return this_func();                                                              \
724     }
725 // end of macro
726 
727 #define THIS_START_OF_ITEM(item_getter)                                                  \
728     {                                                                                    \
729         _cursor_moving_vertically = false;                                               \
730         if (_char_index == 0) return false;                                              \
731         unsigned original_item;                                                          \
732         if (_char_index == _parent_layout->_characters.size()) {                         \
733             _char_index--;                                                               \
734             original_item = item_getter;                                                 \
735         } else {                                                                         \
736             original_item = item_getter;                                                 \
737             _char_index--;                                                               \
738         }                                                                                \
739         while (item_getter == original_item) {                                           \
740             if (_char_index == 0) {                                                      \
741                 _glyph_index = _parent_layout->_characters[_char_index].in_glyph;        \
742                 return true;                                                             \
743             }                                                                            \
744             _char_index--;                                                               \
745         }                                                                                \
746         _char_index++;                                                                   \
747         _glyph_index = _parent_layout->_characters[_char_index].in_glyph;                \
748         return true;                                                                     \
749     }
750 // end of macro
751 
752 #define NEXT_START_OF_ITEM(item_getter)                                                  \
753     {                                                                                    \
754         _cursor_moving_vertically = false;                                               \
755         if (_char_index == _parent_layout->_characters.size()) return false;             \
756         unsigned original_item = item_getter;                                            \
757         for( ; ; ) {                                                                     \
758             _char_index++;                                                               \
759             if (_char_index == _parent_layout->_characters.size()) {                     \
760                 _glyph_index = _parent_layout->_glyphs.size();                           \
761                 return false;                                                            \
762             }                                                                            \
763             if (item_getter != original_item) break;                                     \
764         }                                                                                \
765         _glyph_index = _parent_layout->_characters[_char_index].in_glyph;                \
766         return true;                                                                     \
767     }
768 // end of macro
769 
770 bool Layout::iterator::prevStartOfSpan()
771     PREV_START_OF_ITEM(thisStartOfSpan);
772 
773 bool Layout::iterator::thisStartOfSpan()
774     THIS_START_OF_ITEM(_parent_layout->_characters[_char_index].in_span);
775 
776 bool Layout::iterator::nextStartOfSpan()
777     NEXT_START_OF_ITEM(_parent_layout->_characters[_char_index].in_span);
778 
779 
780 bool Layout::iterator::prevStartOfChunk()
781     PREV_START_OF_ITEM(thisStartOfChunk);
782 
783 bool Layout::iterator::thisStartOfChunk()
784     THIS_START_OF_ITEM(_parent_layout->_characters[_char_index].span(_parent_layout).in_chunk);
785 
786 bool Layout::iterator::nextStartOfChunk()
787     NEXT_START_OF_ITEM(_parent_layout->_characters[_char_index].span(_parent_layout).in_chunk);
788 
789 
790 bool Layout::iterator::prevStartOfLine()
791     PREV_START_OF_ITEM(thisStartOfLine);
792 
793 bool Layout::iterator::thisStartOfLine()
794     THIS_START_OF_ITEM(_parent_layout->_characters[_char_index].chunk(_parent_layout).in_line);
795 
796 bool Layout::iterator::nextStartOfLine()
797     NEXT_START_OF_ITEM(_parent_layout->_characters[_char_index].chunk(_parent_layout).in_line);
798 
799 
800 bool Layout::iterator::prevStartOfShape()
801     PREV_START_OF_ITEM(thisStartOfShape);
802 
803 bool Layout::iterator::thisStartOfShape()
804     THIS_START_OF_ITEM(_parent_layout->_characters[_char_index].line(_parent_layout).in_shape);
805 
806 bool Layout::iterator::nextStartOfShape()
807     NEXT_START_OF_ITEM(_parent_layout->_characters[_char_index].line(_parent_layout).in_shape);
808 
809 
810 bool Layout::iterator::prevStartOfParagraph()
811     PREV_START_OF_ITEM(thisStartOfParagraph);
812 
813 bool Layout::iterator::thisStartOfParagraph()
814     THIS_START_OF_ITEM(_parent_layout->_characters[_char_index].line(_parent_layout).in_paragraph);
815 
816 bool Layout::iterator::nextStartOfParagraph()
817     NEXT_START_OF_ITEM(_parent_layout->_characters[_char_index].line(_parent_layout).in_paragraph);
818 
819 
820 bool Layout::iterator::prevStartOfSource()
821     PREV_START_OF_ITEM(thisStartOfSource);
822 
823 bool Layout::iterator::thisStartOfSource()
824     THIS_START_OF_ITEM(_parent_layout->_characters[_char_index].span(_parent_layout).in_input_stream_item);
825 
826 bool Layout::iterator::nextStartOfSource()
827     NEXT_START_OF_ITEM(_parent_layout->_characters[_char_index].span(_parent_layout).in_input_stream_item);
828 
829 
thisEndOfLine()830 bool Layout::iterator::thisEndOfLine()
831 {
832     if (_char_index == _parent_layout->_characters.size()) return false;
833     if (nextStartOfLine())
834     {
835         if (_char_index && _parent_layout->_characters[_char_index - 1].char_attributes.is_white)
836             return prevCursorPosition();
837         return true;
838     }
839     if (_char_index && _parent_layout->_characters[_char_index - 1].chunk(_parent_layout).in_line != _parent_layout->_lines.size() - 1)
840         return prevCursorPosition();   // for when the last paragraph is empty
841     return false;
842 }
843 
beginCursorUpDown()844 void Layout::iterator::beginCursorUpDown()
845 {
846     if (_char_index == _parent_layout->_characters.size())
847         _x_coordinate = _parent_layout->_chunks.back().left_x + _parent_layout->_spans.back().x_end;
848     else
849         _x_coordinate = _parent_layout->_characters[_char_index].x + _parent_layout->_characters[_char_index].span(_parent_layout).x_start + _parent_layout->_characters[_char_index].chunk(_parent_layout).left_x;
850     _cursor_moving_vertically = true;
851 }
852 
nextLineCursor(int n)853 bool Layout::iterator::nextLineCursor(int n)
854 {
855     if (!_cursor_moving_vertically)
856         beginCursorUpDown();
857     if (_char_index == _parent_layout->_characters.size())
858         return false;
859     unsigned line_index = _parent_layout->_characters[_char_index].chunk(_parent_layout).in_line;
860     if (line_index == _parent_layout->_lines.size() - 1)
861         return false; // nowhere to go
862     else
863         n = MIN (n, static_cast<int>(_parent_layout->_lines.size() - 1 - line_index));
864     if (_parent_layout->_lines[line_index + n].in_shape != _parent_layout->_lines[line_index].in_shape) {
865         // switching between shapes: adjust the stored x to compensate
866         _x_coordinate +=   _parent_layout->_chunks[_parent_layout->_spans[_parent_layout->_lineToSpan(line_index + n)].in_chunk].left_x
867                          - _parent_layout->_chunks[_parent_layout->_spans[_parent_layout->_lineToSpan(line_index)].in_chunk].left_x;
868     }
869     _char_index = _parent_layout->_cursorXOnLineToIterator(line_index + n, _x_coordinate)._char_index;
870     if (_char_index == _parent_layout->_characters.size())
871         _glyph_index = _parent_layout->_glyphs.size();
872     else
873         _glyph_index = _parent_layout->_characters[_char_index].in_glyph;
874     return true;
875 }
876 
prevLineCursor(int n)877 bool Layout::iterator::prevLineCursor(int n)
878 {
879     if (!_cursor_moving_vertically)
880         beginCursorUpDown();
881     int line_index;
882     if (_char_index == _parent_layout->_characters.size())
883         line_index = _parent_layout->_lines.size() - 1;
884     else
885         line_index = _parent_layout->_characters[_char_index].chunk(_parent_layout).in_line;
886     if (line_index <= 0)
887         return false; // nowhere to go
888     else
889         n = MIN (n, static_cast<int>(line_index));
890     if (_parent_layout->_lines[line_index - n].in_shape != _parent_layout->_lines[line_index].in_shape) {
891         // switching between shapes: adjust the stored x to compensate
892         _x_coordinate +=   _parent_layout->_chunks[_parent_layout->_spans[_parent_layout->_lineToSpan(line_index - n)].in_chunk].left_x
893                          - _parent_layout->_chunks[_parent_layout->_spans[_parent_layout->_lineToSpan(line_index)].in_chunk].left_x;
894     }
895     _char_index = _parent_layout->_cursorXOnLineToIterator(line_index - n, _x_coordinate)._char_index;
896     _glyph_index = _parent_layout->_characters[_char_index].in_glyph;
897     return true;
898 }
899 
900 #define NEXT_WITH_ATTRIBUTE_SET(attr)                                                            \
901     {                                                                                            \
902         _cursor_moving_vertically = false;                                                       \
903         for ( ; ; ) {                                                                            \
904             if (_char_index + 1 >= _parent_layout->_characters.size()) {                         \
905                 _char_index = _parent_layout->_characters.size();                                \
906                 _glyph_index = _parent_layout->_glyphs.size();                                   \
907                 return false;                                                                    \
908             }                                                                                    \
909             _char_index++;                                                                       \
910             if (_parent_layout->_characters[_char_index].char_attributes.attr) break;            \
911         }                                                                                        \
912         _glyph_index = _parent_layout->_characters[_char_index].in_glyph;                        \
913         return true;                                                                             \
914     }
915 // end of macro
916 
917 #define PREV_WITH_ATTRIBUTE_SET(attr)                                                            \
918     {                                                                                            \
919         _cursor_moving_vertically = false;                                                       \
920         for ( ; ; ) {                                                                            \
921             if (_char_index == 0) {                                                              \
922                 _glyph_index = 0;                                                                \
923                 return false;                                                                    \
924             }                                                                                    \
925             _char_index--;                                                                       \
926             if (_parent_layout->_characters[_char_index].char_attributes.attr) break;            \
927         }                                                                                        \
928         _glyph_index = _parent_layout->_characters[_char_index].in_glyph;                        \
929         return true;                                                                             \
930     }
931 // end of macro
932 
933 bool Layout::iterator::nextCursorPosition()
934     NEXT_WITH_ATTRIBUTE_SET(is_cursor_position);
935 
936 bool Layout::iterator::prevCursorPosition()
937     PREV_WITH_ATTRIBUTE_SET(is_cursor_position);
938 
939 bool Layout::iterator::nextStartOfWord()
940     NEXT_WITH_ATTRIBUTE_SET(is_word_start);
941 
942 bool Layout::iterator::prevStartOfWord()
943     PREV_WITH_ATTRIBUTE_SET(is_word_start);
944 
945 bool Layout::iterator::nextEndOfWord()
946     NEXT_WITH_ATTRIBUTE_SET(is_word_end);
947 
948 bool Layout::iterator::prevEndOfWord()
949     PREV_WITH_ATTRIBUTE_SET(is_word_end);
950 
951 bool Layout::iterator::nextStartOfSentence()
952     NEXT_WITH_ATTRIBUTE_SET(is_sentence_start);
953 
954 bool Layout::iterator::prevStartOfSentence()
955     PREV_WITH_ATTRIBUTE_SET(is_sentence_start);
956 
957 bool Layout::iterator::nextEndOfSentence()
958     NEXT_WITH_ATTRIBUTE_SET(is_sentence_end);
959 
960 bool Layout::iterator::prevEndOfSentence()
961     PREV_WITH_ATTRIBUTE_SET(is_sentence_end);
962 
_cursorLeftOrRightLocalX(Direction direction)963 bool Layout::iterator::_cursorLeftOrRightLocalX(Direction direction)
964 {
965     // the only reason this function is so complicated is to enable visual cursor
966     // movement moving in to or out of counterdirectional runs
967     if (_parent_layout->_characters.empty()) return false;
968     unsigned old_span_index;
969     Direction old_span_direction;
970     if (_char_index == _parent_layout->_characters.size())
971         old_span_index = _parent_layout->_spans.size() - 1;
972     else
973         old_span_index = _parent_layout->_characters[_char_index].in_span;
974     old_span_direction = _parent_layout->_spans[old_span_index].direction;
975     Direction para_direction = _parent_layout->_spans[old_span_index].paragraph(_parent_layout).base_direction;
976 
977     int scan_direction;
978     unsigned old_char_index = _char_index;
979     if (old_span_direction != para_direction
980         && ((_char_index == 0 && direction == para_direction)
981             || (_char_index == _parent_layout->_characters.size() && direction != para_direction))) {
982         // the end of the text is actually in the middle because of reordering. Do cleverness
983         scan_direction = direction == para_direction ? +1 : -1;
984     } else {
985         if (direction == old_span_direction) {
986             if (!nextCursorPosition()) return false;
987         } else {
988             if (!prevCursorPosition()) return false;
989         }
990 
991         unsigned new_span_index = _parent_layout->_characters[_char_index].in_span;
992         if (new_span_index == old_span_index) return true;
993         if (old_span_direction != _parent_layout->_spans[new_span_index].direction) {
994             // we must jump to the other end of a counterdirectional run
995             scan_direction = direction == para_direction ? +1 : -1;
996         } else if (_parent_layout->_spans[old_span_index].in_chunk != _parent_layout->_spans[new_span_index].in_chunk) {
997             // we might have to do a weird jump when we would have crossed a chunk/line break
998             if (_parent_layout->_spans[old_span_index].line(_parent_layout).in_paragraph != _parent_layout->_spans[new_span_index].line(_parent_layout).in_paragraph)
999                 return true;
1000             if (old_span_direction == para_direction)
1001                 return true;
1002             scan_direction = direction == para_direction ? +1 : -1;
1003         } else
1004             return true;    // same direction, same chunk: no cleverness required
1005     }
1006 
1007     unsigned new_span_index = old_span_index;
1008     for ( ; ; ) {
1009         if (scan_direction > 0) {
1010             if (new_span_index == _parent_layout->_spans.size() - 1) {
1011                 if (_parent_layout->_spans[new_span_index].direction == old_span_direction) {
1012                     _char_index = old_char_index;
1013                     return false;    // the visual end is in the logical middle
1014                 }
1015                 break;
1016             }
1017             new_span_index++;
1018         } else {
1019             if (new_span_index == 0) {
1020                 if (_parent_layout->_spans[new_span_index].direction == old_span_direction) {
1021                     _char_index = old_char_index;
1022                     return false;    // the visual end is in the logical middle
1023                 }
1024                 break;
1025             }
1026             new_span_index--;
1027         }
1028         if (_parent_layout->_spans[new_span_index].direction == para_direction) {
1029             if (para_direction == old_span_direction)
1030                 new_span_index -= scan_direction;
1031             break;
1032         }
1033         if (_parent_layout->_spans[new_span_index].in_chunk != _parent_layout->_spans[old_span_index].in_chunk) {
1034             if (_parent_layout->_spans[old_span_index].line(_parent_layout).in_paragraph == _parent_layout->_spans[new_span_index].line(_parent_layout).in_paragraph
1035                 && para_direction == old_span_direction)
1036                 new_span_index -= scan_direction;
1037             break;
1038         }
1039     }
1040 
1041     // found the correct span, now find the correct character
1042     if (_parent_layout->_spans[old_span_index].line(_parent_layout).in_paragraph != _parent_layout->_spans[new_span_index].line(_parent_layout).in_paragraph) {
1043         if (new_span_index > old_span_index)
1044             _char_index = _parent_layout->_spanToCharacter(new_span_index);
1045         else
1046             _char_index = _parent_layout->_spanToCharacter(new_span_index + 1) - 1;
1047     } else {
1048         if (_parent_layout->_spans[new_span_index].direction != direction) {
1049             if (new_span_index >= _parent_layout->_spans.size() - 1)
1050                 _char_index = _parent_layout->_characters.size();
1051             else
1052                 _char_index = _parent_layout->_spanToCharacter(new_span_index + 1) - 1;
1053         } else
1054             _char_index = _parent_layout->_spanToCharacter(new_span_index);
1055     }
1056     if (_char_index == _parent_layout->_characters.size()) {
1057         _glyph_index = _parent_layout->_glyphs.size();
1058         return false;
1059     }
1060     _glyph_index = _parent_layout->_characters[_char_index].in_glyph;
1061     return _char_index != 0;
1062 }
1063 
_cursorLeftOrRightLocalXByWord(Direction direction)1064 bool Layout::iterator::_cursorLeftOrRightLocalXByWord(Direction direction)
1065 {
1066     bool r;
1067     while ((r = _cursorLeftOrRightLocalX(direction))
1068            && !_parent_layout->_characters[_char_index].char_attributes.is_word_start){};
1069     return r;
1070 }
1071 
cursorUp(int n)1072 bool Layout::iterator::cursorUp(int n)
1073 {
1074     Direction block_progression = _parent_layout->_blockProgression();
1075     if(block_progression == TOP_TO_BOTTOM)
1076         return prevLineCursor(n);
1077     else if(block_progression == BOTTOM_TO_TOP)
1078         return nextLineCursor(n);
1079     else
1080         return _cursorLeftOrRightLocalX(RIGHT_TO_LEFT);
1081 }
1082 
cursorDown(int n)1083 bool Layout::iterator::cursorDown(int n)
1084 {
1085     Direction block_progression = _parent_layout->_blockProgression();
1086     if(block_progression == TOP_TO_BOTTOM)
1087         return nextLineCursor(n);
1088     else if(block_progression == BOTTOM_TO_TOP)
1089         return prevLineCursor(n);
1090     else
1091         return _cursorLeftOrRightLocalX(LEFT_TO_RIGHT);
1092 }
1093 
cursorLeft()1094 bool Layout::iterator::cursorLeft()
1095 {
1096     Direction block_progression = _parent_layout->_blockProgression();
1097     if(block_progression == LEFT_TO_RIGHT)
1098         return prevLineCursor();
1099     else if(block_progression == RIGHT_TO_LEFT)
1100         return nextLineCursor();
1101     else
1102         return _cursorLeftOrRightLocalX(RIGHT_TO_LEFT);
1103 }
1104 
cursorRight()1105 bool Layout::iterator::cursorRight()
1106 {
1107     Direction block_progression = _parent_layout->_blockProgression();
1108     if(block_progression == LEFT_TO_RIGHT)
1109         return nextLineCursor();
1110     else if(block_progression == RIGHT_TO_LEFT)
1111         return prevLineCursor();
1112     else
1113         return _cursorLeftOrRightLocalX(LEFT_TO_RIGHT);
1114 }
1115 
cursorUpWithControl()1116 bool Layout::iterator::cursorUpWithControl()
1117 {
1118     Direction block_progression = _parent_layout->_blockProgression();
1119     if(block_progression == TOP_TO_BOTTOM)
1120         return prevStartOfParagraph();
1121     else if(block_progression == BOTTOM_TO_TOP)
1122         return nextStartOfParagraph();
1123     else
1124         return _cursorLeftOrRightLocalXByWord(RIGHT_TO_LEFT);
1125 }
1126 
cursorDownWithControl()1127 bool Layout::iterator::cursorDownWithControl()
1128 {
1129     Direction block_progression = _parent_layout->_blockProgression();
1130     if(block_progression == TOP_TO_BOTTOM)
1131         return nextStartOfParagraph();
1132     else if(block_progression == BOTTOM_TO_TOP)
1133         return prevStartOfParagraph();
1134     else
1135         return _cursorLeftOrRightLocalXByWord(LEFT_TO_RIGHT);
1136 }
1137 
cursorLeftWithControl()1138 bool Layout::iterator::cursorLeftWithControl()
1139 {
1140     Direction block_progression = _parent_layout->_blockProgression();
1141     if(block_progression == LEFT_TO_RIGHT)
1142         return prevStartOfParagraph();
1143     else if(block_progression == RIGHT_TO_LEFT)
1144         return nextStartOfParagraph();
1145     else
1146         return _cursorLeftOrRightLocalXByWord(RIGHT_TO_LEFT);
1147 }
1148 
cursorRightWithControl()1149 bool Layout::iterator::cursorRightWithControl()
1150 {
1151     Direction block_progression = _parent_layout->_blockProgression();
1152     if(block_progression == LEFT_TO_RIGHT)
1153         return nextStartOfParagraph();
1154     else if(block_progression == RIGHT_TO_LEFT)
1155         return prevStartOfParagraph();
1156     else
1157         return _cursorLeftOrRightLocalXByWord(LEFT_TO_RIGHT);
1158 }
1159 
1160 }//namespace Text
1161 }//namespace Inkscape
1162 
1163 /*
1164   Local Variables:
1165   mode:c++
1166   c-file-style:"stroustrup"
1167   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
1168   indent-tabs-mode:nil
1169   fill-column:99
1170   End:
1171 */
1172 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
1173