Code

convert almost all libnrtype to Geom::
[inkscape.git] / src / libnrtype / Layout-TNG-OutIter.cpp
1 /*
2  * Inkscape::Text::Layout - text layout engine output functions using iterators
3  *
4  * Authors:
5  *   Richard Hughes <cyreve@users.sf.net>
6  *
7  * Copyright (C) 2005 Richard Hughes
8  *
9  * Released under GNU GPL, read the file 'COPYING' for more information
10  */
11 #include "Layout-TNG.h"
12 #include "livarot/Path.h"
13 #include "font-instance.h"
14 #include "svg/svg-length.h"
15 #include <2geom/transforms.h>
16 #include "style.h"
18 namespace Inkscape {
19 namespace Text {
21 Layout::iterator Layout::_cursorXOnLineToIterator(unsigned line_index, double local_x) const
22 {
23     unsigned char_index = _lineToCharacter(line_index);
24     int best_char_index = -1;
25     double best_x_difference = DBL_MAX;
27     if (char_index == _characters.size()) return end();
28     for ( ; char_index < _characters.size() ; char_index++) {
29         if (_characters[char_index].chunk(this).in_line != line_index) break;
30         if (_characters[char_index].char_attributes.is_mandatory_break) break;
31         if (!_characters[char_index].char_attributes.is_cursor_position) continue;
32         double this_x_difference = fabs(_characters[char_index].x + _characters[char_index].span(this).x_start + _characters[char_index].chunk(this).left_x - local_x);
33         if (this_x_difference < best_x_difference) {
34             best_char_index = char_index;
35             best_x_difference = this_x_difference;
36         }
37     }
38     // also try the very end of a para (not lines though because the space wraps)
39     if (char_index == _characters.size() || _characters[char_index].char_attributes.is_mandatory_break) {
40         double this_x_difference;
41         if (char_index == 0) this_x_difference = fabs(_spans.front().x_end + _chunks.front().left_x - local_x);
42         else this_x_difference = fabs(_characters[char_index - 1].span(this).x_end + _characters[char_index - 1].chunk(this).left_x - local_x);
43         if (this_x_difference < best_x_difference) {
44             best_char_index = char_index;
45             best_x_difference = this_x_difference;
46         }
47     }
48     if (best_char_index == -1) return iterator(this, char_index);
49     return iterator(this, best_char_index);
50 }
52 double Layout::_getChunkWidth(unsigned chunk_index) const
53 {
54     double chunk_width = 0.0;
55     unsigned span_index;
56     if (chunk_index) {
57         span_index = _lineToSpan(_chunks[chunk_index].in_line);
58         for ( ; span_index < _spans.size() && _spans[span_index].in_chunk < chunk_index ; span_index++);
59     } else
60         span_index = 0;
61     for ( ; span_index < _spans.size() && _spans[span_index].in_chunk == chunk_index ; span_index++)
62         chunk_width = std::max(chunk_width, (double)std::max(_spans[span_index].x_start, _spans[span_index].x_end));
63     return chunk_width;
64 }
66 /* getting the cursor position for a mouse click is not as simple as it might
67 seem. The two major problems are flows set up in multiple columns and large
68 dy adjustments such that text does not belong to the line it appears to. In
69 the worst case it's possible to have two characters on top of each other, in
70 which case the one we pick is arbitrary.
72 This is a 3-stage (2 pass) algorithm:
73 1) search all the spans to see if the point is contained in one, if so take
74    that. Note that this will collect all clicks from the current UI because
75    of how the hit detection of nrarena objects works.
76 2) if that fails, run through all the chunks finding a best guess of the one
77    the user wanted. This is the one whose y coordinate is nearest, or if
78    there's a tie, the x.
79 3) search in that chunk using x-coordinate only to find the position.
80 */
81 Layout::iterator Layout::getNearestCursorPositionTo(double x, double y) const
82 {
83     if (_lines.empty()) return begin();
84     double local_x = x;
85     double local_y = y;
87     if (_path_fitted) {
88         Path::cut_position position = const_cast<Path*>(_path_fitted)->PointToCurvilignPosition(Geom::Point(x, y));
89         local_x = const_cast<Path*>(_path_fitted)->PositionToLength(position.piece, position.t);
90         return _cursorXOnLineToIterator(0, local_x + _chunks.front().left_x);
91     }
93     if (_directions_are_orthogonal(_blockProgression(), TOP_TO_BOTTOM)) {
94         local_x = y;
95         local_y = x;
96     }
97     // stage 1:
98     for (unsigned span_index = 0 ; span_index < _spans.size() ; span_index++) {
99         double span_left, span_right;
100         if (_spans[span_index].x_start < _spans[span_index].x_end) {
101             span_left = _spans[span_index].x_start;
102             span_right = _spans[span_index].x_end;
103         } else {
104             span_left = _spans[span_index].x_end;
105             span_right = _spans[span_index].x_start;
106         }
107         if (   local_x >= _chunks[_spans[span_index].in_chunk].left_x + span_left
108             && local_x <= _chunks[_spans[span_index].in_chunk].left_x + span_right
109             && local_y >= _spans[span_index].line(this).baseline_y + _spans[span_index].baseline_shift - _spans[span_index].line_height.ascent
110             && local_y <= _spans[span_index].line(this).baseline_y + _spans[span_index].baseline_shift + _spans[span_index].line_height.descent) {
111             return _cursorXOnLineToIterator(_chunks[_spans[span_index].in_chunk].in_line, local_x);
112         }
113     }
114     
115     // stage 2:
116     unsigned span_index = 0;
117     unsigned chunk_index;
118     int best_chunk_index = -1;
119     double best_y_range = DBL_MAX;
120     double best_x_range = DBL_MAX;
121     for (chunk_index = 0 ; chunk_index < _chunks.size() ; chunk_index++) {
122         LineHeight line_height = {0.0, 0.0, 0.0};
123         double chunk_width = 0.0;
124         for ( ; span_index < _spans.size() && _spans[span_index].in_chunk == chunk_index ; span_index++) {
125             line_height.max(_spans[span_index].line_height);
126             chunk_width = std::max(chunk_width, (double)std::max(_spans[span_index].x_start, _spans[span_index].x_end));
127         }
128         double this_y_range;
129         if (local_y < _lines[_chunks[chunk_index].in_line].baseline_y - line_height.ascent)
130             this_y_range = _lines[_chunks[chunk_index].in_line].baseline_y - line_height.ascent - local_y;
131         else if (local_y > _lines[_chunks[chunk_index].in_line].baseline_y + line_height.descent)
132             this_y_range = local_y - (_lines[_chunks[chunk_index].in_line].baseline_y + line_height.descent);
133         else
134             this_y_range = 0.0;
135         if (this_y_range <= best_y_range) {
136             if (this_y_range < best_y_range) best_x_range = DBL_MAX;
137             double this_x_range;
138             if (local_x < _chunks[chunk_index].left_x)
139                 this_x_range = _chunks[chunk_index].left_x - local_y;
140             else if (local_x > _chunks[chunk_index].left_x + chunk_width)
141                 this_x_range = local_x - (_chunks[chunk_index].left_x + chunk_width);
142             else
143                 this_x_range = 0.0;
144             if (this_x_range < best_x_range) {
145                 best_y_range = this_y_range;
146                 best_x_range = this_x_range;
147                 best_chunk_index = chunk_index;
148             }
149         }
150     }
152     // stage 3:
153     if (best_chunk_index == -1) return begin();    // never happens
154     return _cursorXOnLineToIterator(_chunks[best_chunk_index].in_line, local_x);
157 Layout::iterator Layout::getLetterAt(double x, double y) const
159     Geom::Point point(x, y);
161     double rotation;
162     for (iterator it = begin() ; it != end() ; it.nextCharacter()) {
163         Geom::Rect box = characterBoundingBox(it, &rotation);
164         // todo: rotation
165         if (box.contains(point)) return it;
166     }
167     return end();
170 Layout::iterator Layout::sourceToIterator(void *source_cookie, Glib::ustring::const_iterator text_iterator) const
172     unsigned source_index;
173     if (_characters.empty()) return end();
174     for (source_index = 0 ; source_index < _input_stream.size() ; source_index++)
175         if (_input_stream[source_index]->source_cookie == source_cookie) break;
176     if (source_index == _input_stream.size()) return end();
178     unsigned char_index = _sourceToCharacter(source_index);
179     
180     if (_input_stream[source_index]->Type() != TEXT_SOURCE)
181         return iterator(this, char_index);
183     InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_input_stream[source_index]);
184     if (text_iterator <= text_source->text_begin) return iterator(this, char_index);
185     if (text_iterator >= text_source->text_end) {
186         if (source_index == _input_stream.size() - 1) return end();
187         return iterator(this, _sourceToCharacter(source_index + 1));
188     }
189     Glib::ustring::const_iterator iter_text = text_source->text_begin;
190     for ( ; char_index < _characters.size() ; char_index++) {
191         if (iter_text == text_iterator)
192             return iterator(this, char_index);
193         iter_text++;
194     }
195     return end(); // never happens
198 Layout::iterator Layout::sourceToIterator(void *source_cookie) const
200     return sourceToIterator(source_cookie, Glib::ustring::const_iterator(std::string::const_iterator(NULL)));
203 boost::optional<Geom::Rect> Layout::glyphBoundingBox(iterator const &it, double *rotation) const
205    if (rotation) *rotation = _glyphs[it._glyph_index].rotation;
206    return _glyphs[it._glyph_index].span(this).font->BBox(_glyphs[it._glyph_index].glyph);
209 Geom::Point Layout::characterAnchorPoint(iterator const &it) const
211     if (_characters.empty())
212         return _empty_cursor_shape.position;
213     if (it._char_index == _characters.size()) {
214         return Geom::Point(_chunks.back().left_x + _spans.back().x_end, _lines.back().baseline_y + _spans.back().baseline_shift);
215     } else {
216         return Geom::Point(_characters[it._char_index].chunk(this).left_x
217                              + _spans[_characters[it._char_index].in_span].x_start
218                              + _characters[it._char_index].x,
219                          _characters[it._char_index].line(this).baseline_y
220                              + _characters[it._char_index].span(this).baseline_shift);
221     }
224 Geom::Point Layout::chunkAnchorPoint(iterator const &it) const
226     unsigned chunk_index;
228     if (_chunks.empty())
229         return Geom::Point(0.0, 0.0);
231     if (_characters.empty())
232         chunk_index = 0;
233     else if (it._char_index == _characters.size())
234         chunk_index = _chunks.size() - 1;
235     else chunk_index = _characters[it._char_index].span(this).in_chunk;
237     Alignment alignment = _paragraphs[_lines[_chunks[chunk_index].in_line].in_paragraph].alignment;
238     if (alignment == LEFT || alignment == FULL)
239         return Geom::Point(_chunks[chunk_index].left_x, _lines[chunk_index].baseline_y);
241     double chunk_width = _getChunkWidth(chunk_index);
242     if (alignment == RIGHT)
243         return Geom::Point(_chunks[chunk_index].left_x + chunk_width, _lines[chunk_index].baseline_y);
244     //centre
245     return Geom::Point(_chunks[chunk_index].left_x + chunk_width * 0.5, _lines[chunk_index].baseline_y);
248 Geom::Rect Layout::characterBoundingBox(iterator const &it, double *rotation) const
250     Geom::Point top_left, bottom_right;
251     unsigned char_index = it._char_index;
253     if (_path_fitted) {
254         double cluster_half_width = 0.0;
255         for (int glyph_index = _characters[char_index].in_glyph ; _glyphs[glyph_index].in_character == char_index ; glyph_index++)
256             cluster_half_width += _glyphs[glyph_index].width;
257         cluster_half_width *= 0.5;
259         double midpoint_offset = _characters[char_index].span(this).x_start + _characters[char_index].x + cluster_half_width;
260         int unused = 0;
261         Path::cut_position *midpoint_otp = const_cast<Path*>(_path_fitted)->CurvilignToPosition(1, &midpoint_offset, unused);
262         if (midpoint_offset >= 0.0 && midpoint_otp != NULL && midpoint_otp[0].piece >= 0) {
263             NR::Point midpoint;
264             NR::Point tangent;
265             Span const &span = _characters[char_index].span(this);
267             const_cast<Path*>(_path_fitted)->PointAndTangentAt(midpoint_otp[0].piece, midpoint_otp[0].t, midpoint, tangent);
268             top_left[Geom::X] = midpoint[Geom::X] - cluster_half_width;
269             top_left[Geom::Y] = midpoint[Geom::Y] - span.line_height.ascent;
270             bottom_right[Geom::X] = midpoint[Geom::X] + cluster_half_width;
271             bottom_right[Geom::Y] = midpoint[Geom::Y] + span.line_height.descent;
272             Geom::Point normal = tangent.cw();
273             top_left += span.baseline_shift * normal;
274             bottom_right += span.baseline_shift * normal;
275             if (rotation)
276                 *rotation = atan2(tangent[1], tangent[0]);
277         }
278         g_free(midpoint_otp);
279     } else {
280         if (it._char_index == _characters.size()) {
281             top_left[Geom::X] = bottom_right[Geom::X] = _chunks.back().left_x + _spans.back().x_end;
282             char_index--;
283         } else {
284             double span_x = _spans[_characters[it._char_index].in_span].x_start + _characters[it._char_index].chunk(this).left_x;
285             top_left[Geom::X] = span_x + _characters[it._char_index].x;
286             if (it._char_index + 1 == _characters.size() || _characters[it._char_index + 1].in_span != _characters[it._char_index].in_span)
287                 bottom_right[Geom::X] = _spans[_characters[it._char_index].in_span].x_end + _characters[it._char_index].chunk(this).left_x;
288             else
289                 bottom_right[Geom::X] = span_x + _characters[it._char_index + 1].x;
290         }
292         double baseline_y = _characters[char_index].line(this).baseline_y + _characters[char_index].span(this).baseline_shift;
293         if (_directions_are_orthogonal(_blockProgression(), TOP_TO_BOTTOM)) {
294             double span_height = _spans[_characters[char_index].in_span].line_height.ascent + _spans[_characters[char_index].in_span].line_height.descent;
295             top_left[Geom::Y] = top_left[Geom::X];
296             top_left[Geom::X] = baseline_y - span_height * 0.5;
297             bottom_right[Geom::Y] = bottom_right[Geom::X];
298             bottom_right[Geom::X] = baseline_y + span_height * 0.5;
299         } else {
300             top_left[Geom::Y] = baseline_y - _spans[_characters[char_index].in_span].line_height.ascent;
301             bottom_right[Geom::Y] = baseline_y + _spans[_characters[char_index].in_span].line_height.descent;
302         }
304         if (rotation) {
305             if (it._glyph_index == -1)
306                 *rotation = 0.0;
307             else if (it._glyph_index == (int)_glyphs.size())
308                 *rotation = _glyphs.back().rotation;
309             else
310                 *rotation = _glyphs[it._glyph_index].rotation;
311         }
312     }
314     return Geom::Rect(top_left, bottom_right);
317 std::vector<Geom::Point> Layout::createSelectionShape(iterator const &it_start, iterator const &it_end, Geom::Matrix const &transform) const
319     std::vector<Geom::Point> quads;
320     unsigned char_index;
321     unsigned end_char_index;
322     
323     if (it_start._char_index < it_end._char_index) {
324         char_index = it_start._char_index;
325         end_char_index = it_end._char_index;
326     } else {
327         char_index = it_end._char_index;
328         end_char_index = it_start._char_index;
329     }
330     for ( ; char_index < end_char_index ; ) {
331         if (_characters[char_index].in_glyph == -1) {
332             char_index++;
333             continue;
334         }
335         double char_rotation = _glyphs[_characters[char_index].in_glyph].rotation;
336         unsigned span_index = _characters[char_index].in_span;
338         Geom::Point top_left, bottom_right;
339         if (_path_fitted || char_rotation != 0.0) {
340             Geom::Rect box = characterBoundingBox(iterator(this, char_index), &char_rotation);
341             top_left = box.min();
342             bottom_right = box.max();
343             char_index++;
344         } else {   // for straight text we can be faster by combining all the character boxes in a span into one box
345             double span_x = _spans[span_index].x_start + _spans[span_index].chunk(this).left_x;
346             top_left[Geom::X] = span_x + _characters[char_index].x;
347             while (char_index < end_char_index && _characters[char_index].in_span == span_index)
348                 char_index++;
349             if (char_index == _characters.size() || _characters[char_index].in_span != span_index)
350                 bottom_right[Geom::X] = _spans[span_index].x_end + _spans[span_index].chunk(this).left_x;
351             else
352                 bottom_right[Geom::X] = span_x + _characters[char_index].x;
354             double baseline_y = _spans[span_index].line(this).baseline_y + _spans[span_index].baseline_shift;
355             if (_directions_are_orthogonal(_blockProgression(), TOP_TO_BOTTOM)) {
356                 double span_height = _spans[span_index].line_height.ascent + _spans[span_index].line_height.descent;
357                 top_left[Geom::Y] = top_left[Geom::X];
358                 top_left[Geom::X] = baseline_y - span_height * 0.5;
359                 bottom_right[Geom::Y] = bottom_right[Geom::X];
360                 bottom_right[Geom::X] = baseline_y + span_height * 0.5;
361             } else {
362                 top_left[Geom::Y] = baseline_y - _spans[span_index].line_height.ascent;
363                 bottom_right[Geom::Y] = baseline_y + _spans[span_index].line_height.descent;
364             }
365         }
367         Geom::Rect char_box(top_left, bottom_right);
368         if (char_box.dimensions()[Geom::X] == 0.0 || char_box.dimensions()[Geom::Y] == 0.0)
369             continue;
370         Geom::Point center_of_rotation((top_left[Geom::X] + bottom_right[Geom::X]) * 0.5,
371                                      top_left[Geom::Y] + _spans[span_index].line_height.ascent);
372         Geom::Matrix total_transform = Geom::Translate(-center_of_rotation) * Geom::Rotate(char_rotation) * Geom::Translate(center_of_rotation) * transform;
373         for(int i = 0; i < 4; i ++)
374             quads.push_back(char_box.corner(i) * total_transform);
375     }
376     return quads;
379 void Layout::queryCursorShape(iterator const &it, Geom::Point &position, double &height, double &rotation) const
381     if (_characters.empty()) {
382         position = _empty_cursor_shape.position;
383         height = _empty_cursor_shape.height;
384         rotation = _empty_cursor_shape.rotation;
385     } else {
386         // we want to cursor to be positioned where the left edge of a character that is about to be typed will be.
387         // this means x & rotation are the current values but y & height belong to the previous character.
388         // this isn't quite right because dx attributes will be moved along, but it's good enough
389         Span const *span;
390         if (_path_fitted) {
391             // text on a path
392             double x;
393             if (it._char_index >= _characters.size()) {
394                 span = &_spans.back();
395                 x = span->x_end + _chunks.back().left_x - _chunks[0].left_x;
396             } else {
397                 span = &_spans[_characters[it._char_index].in_span];
398                 x = _chunks[span->in_chunk].left_x + span->x_start + _characters[it._char_index].x - _chunks[0].left_x;
399                 if (it._char_index != 0)
400                     span = &_spans[_characters[it._char_index - 1].in_span];
401             }
402             double path_length = const_cast<Path*>(_path_fitted)->Length();
403             double x_on_path = x;
404             if (x_on_path < 0.0) x_on_path = 0.0;
406             int unused = 0;
407                 // as far as I know these functions are const, they're just not marked as such
408             Path::cut_position *path_parameter_list = const_cast<Path*>(_path_fitted)->CurvilignToPosition(1, &x_on_path, unused);
409             Path::cut_position path_parameter;
410             if (path_parameter_list != NULL && path_parameter_list[0].piece >= 0)
411                 path_parameter = path_parameter_list[0];
412             else {
413                 path_parameter.piece = _path_fitted->descr_cmd.size() - 1;
414                 path_parameter.t = 0.9999;   // 1.0 will get the wrong tangent
415             }
416             g_free(path_parameter_list);
418             NR::Point point;
419             NR::Point tangent;
420             const_cast<Path*>(_path_fitted)->PointAndTangentAt(path_parameter.piece, path_parameter.t, point, tangent);
421             if (x < 0.0)
422                 point += x * tangent;
423             if (x > path_length )
424                 point += (x - path_length) * tangent;
425             rotation = atan2(tangent);
426             position[Geom::X] = point[Geom::X] - tangent[Geom::Y] * span->baseline_shift;
427             position[Geom::Y] = point[Geom::Y] + tangent[Geom::X] * span->baseline_shift;
428         } else {
429             // text is not on a path
430             if (it._char_index >= _characters.size()) {
431                 span = &_spans.back();
432                 position[Geom::X] = _chunks[span->in_chunk].left_x + span->x_end;
433                 rotation = _glyphs.empty() ? 0.0 : _glyphs.back().rotation;
434             } else {
435                 span = &_spans[_characters[it._char_index].in_span];
436                 position[Geom::X] = _chunks[span->in_chunk].left_x + span->x_start + _characters[it._char_index].x;
437                 if (it._glyph_index == -1) {
438                     rotation = 0.0;
439                 } else if(it._glyph_index == 0) {
440                     rotation = _glyphs[0].rotation;
441                 } else{
442                     rotation = _glyphs[it._glyph_index - 1].rotation;
443                 }
444                 // 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
445                 if (it._char_index != 0 && _characters[it._char_index - 1].chunk(this).in_line == _chunks[span->in_chunk].in_line)
446                     span = &_spans[_characters[it._char_index - 1].in_span];
447             }
448             position[Geom::Y] = span->line(this).baseline_y + span->baseline_shift;
449         }
450         // up to now *position is the baseline point, not the final point which will be the bottom of the descent
451         if (_directions_are_orthogonal(_blockProgression(), TOP_TO_BOTTOM)) {
452             height = span->line_height.ascent + span->line_height.descent;
453             rotation += M_PI / 2;
454             std::swap(position[Geom::X], position[Geom::Y]);
455             position[Geom::X] -= sin(rotation) * height * 0.5;
456             position[Geom::Y] += cos(rotation) * height * 0.5;
457         } else {
458             double caret_slope_run = 0.0, caret_slope_rise = 1.0;
459             if (span->font)
460                 const_cast<font_instance*>(span->font)->FontSlope(caret_slope_run, caret_slope_rise);
461             double caret_slope = atan2(caret_slope_run, caret_slope_rise);
462             height = (span->line_height.ascent + span->line_height.descent) / cos(caret_slope);
463             rotation += caret_slope;
464             position[Geom::X] -= sin(rotation) * span->line_height.descent;
465             position[Geom::Y] += cos(rotation) * span->line_height.descent;
466         }
467     }
470 void Layout::getSourceOfCharacter(iterator const &it, void **source_cookie, Glib::ustring::iterator *text_iterator) const
472     if (it._char_index == _characters.size()) {
473         *source_cookie = NULL;
474         return;
475     }
476     InputStreamItem *stream_item = _input_stream[_spans[_characters[it._char_index].in_span].in_input_stream_item];
477     *source_cookie = stream_item->source_cookie;
478     if (text_iterator && stream_item->Type() == TEXT_SOURCE) {
479         InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(stream_item);
480         Glib::ustring::const_iterator text_iter_const = text_source->text_begin;
481         unsigned char_index = it._char_index;
482         unsigned original_input_source_index = _spans[_characters[char_index].in_span].in_input_stream_item;
483         // confusing algorithm because the iterator goes forwards while the index goes backwards.
484         // It's just that it's faster doing it that way
485         while (char_index && _spans[_characters[char_index - 1].in_span].in_input_stream_item == original_input_source_index) {
486             ++text_iter_const;
487             char_index--;
488         }
489         text_source->text->begin().base() + (text_iter_const.base() - text_source->text->begin().base());
490         *text_iterator = Glib::ustring::iterator(std::string::iterator(const_cast<char*>(&*text_source->text->begin().base() + (text_iter_const.base() - text_source->text->begin().base()))));
491              // the caller owns the string, so they're going to want a non-const iterator
492     }
495 void Layout::simulateLayoutUsingKerning(iterator const &from, iterator const &to, OptionalTextTagAttrs *result) const
497     SVGLength zero_length;
498     zero_length = 0.0;
500     result->x.clear();
501     result->y.clear();
502     result->dx.clear();
503     result->dy.clear();
504     result->rotate.clear();
505     if (to._char_index <= from._char_index)
506         return;
507     result->dx.reserve(to._char_index - from._char_index);
508     result->dy.reserve(to._char_index - from._char_index);
509     result->rotate.reserve(to._char_index - from._char_index);
510     for (unsigned char_index = from._char_index ; char_index < to._char_index ; char_index++) {
511         if (!_characters[char_index].char_attributes.is_char_break)
512             continue;
513         if (char_index == 0)
514             continue;
515         if (_characters[char_index].chunk(this).in_line != _characters[char_index - 1].chunk(this).in_line)
516             continue;
518         unsigned prev_cluster_char_index;
519         for (prev_cluster_char_index = char_index - 1 ;
520              prev_cluster_char_index != 0 && !_characters[prev_cluster_char_index].char_attributes.is_cursor_position ;
521              prev_cluster_char_index--);
522         if (_characters[char_index].span(this).in_chunk == _characters[char_index - 1].span(this).in_chunk) {
523             // dx is zero for the first char in a chunk
524             // this algorithm works by comparing the summed widths of the glyphs with the observed
525             // difference in x coordinates of characters, and subtracting the two to produce the x kerning.
526             double glyphs_width = 0.0;
527             if (_characters[prev_cluster_char_index].in_glyph != -1)
528                 for (int glyph_index = _characters[prev_cluster_char_index].in_glyph ; glyph_index < _characters[char_index].in_glyph ; glyph_index++)
529                     glyphs_width += _glyphs[glyph_index].width;
530             if (_characters[char_index].span(this).direction == RIGHT_TO_LEFT)
531                 glyphs_width = -glyphs_width;
533             double dx = (_characters[char_index].x + _characters[char_index].span(this).x_start
534                          - _characters[prev_cluster_char_index].x - _characters[prev_cluster_char_index].span(this).x_start)
535                         - glyphs_width;
537             
538             InputStreamItem *input_item = _input_stream[_characters[char_index].span(this).in_input_stream_item];
539             if (input_item->Type() == TEXT_SOURCE) {
540                 SPStyle const *style = static_cast<InputStreamTextSource*>(input_item)->style;
541                 if (_characters[char_index].char_attributes.is_white)
542                     dx -= style->word_spacing.computed;
543                 if (_characters[char_index].char_attributes.is_cursor_position)
544                     dx -= style->letter_spacing.computed;
545             }
547             if (fabs(dx) > 0.0001) {
548                 result->dx.resize(char_index - from._char_index + 1, zero_length);
549                 result->dx.back() = dx;
550             }
551         }
552         double dy = _characters[char_index].span(this).baseline_shift - _characters[prev_cluster_char_index].span(this).baseline_shift;
553         if (fabs(dy) > 0.0001) {
554             result->dy.resize(char_index - from._char_index + 1, zero_length);
555             result->dy.back() = dy;
556         }
557         if (_characters[char_index].in_glyph != -1 && _glyphs[_characters[char_index].in_glyph].rotation != 0.0) {
558             result->rotate.resize(char_index - from._char_index + 1, zero_length);
559             result->rotate.back() = _glyphs[_characters[char_index].in_glyph].rotation;
560         }
561     }
564 #define PREV_START_OF_ITEM(this_func)                                                    \
565     {                                                                                    \
566         _cursor_moving_vertically = false;                                               \
567         if (_char_index == 0) return false;                                              \
568         _char_index--;                                                                   \
569         return this_func();                                                              \
570     }
571 // end of macro
573 #define THIS_START_OF_ITEM(item_getter)                                                  \
574     {                                                                                    \
575         _cursor_moving_vertically = false;                                               \
576         if (_char_index == 0) return false;                                              \
577         unsigned original_item;                                                          \
578         if (_char_index == _parent_layout->_characters.size()) {                         \
579             _char_index--;                                                               \
580             original_item = item_getter;                                                 \
581         } else {                                                                         \
582             original_item = item_getter;                                                 \
583             _char_index--;                                                               \
584         }                                                                                \
585         while (item_getter == original_item) {                                           \
586             if (_char_index == 0) {                                                      \
587                 _glyph_index = _parent_layout->_characters[_char_index].in_glyph;        \
588                 return true;                                                             \
589             }                                                                            \
590             _char_index--;                                                               \
591         }                                                                                \
592         _char_index++;                                                                   \
593         _glyph_index = _parent_layout->_characters[_char_index].in_glyph;                \
594         return true;                                                                     \
595     }
596 // end of macro
598 #define NEXT_START_OF_ITEM(item_getter)                                                  \
599     {                                                                                    \
600         _cursor_moving_vertically = false;                                               \
601         if (_char_index == _parent_layout->_characters.size()) return false;             \
602         unsigned original_item = item_getter;                                            \
603         for( ; ; ) {                                                                     \
604             _char_index++;                                                               \
605             if (_char_index == _parent_layout->_characters.size()) {                     \
606                 _glyph_index = _parent_layout->_glyphs.size();                           \
607                 return false;                                                            \
608             }                                                                            \
609             if (item_getter != original_item) break;                                     \
610         }                                                                                \
611         _glyph_index = _parent_layout->_characters[_char_index].in_glyph;                \
612         return true;                                                                     \
613     }
614 // end of macro
616 bool Layout::iterator::prevStartOfSpan()
617     PREV_START_OF_ITEM(thisStartOfSpan);
619 bool Layout::iterator::thisStartOfSpan()
620     THIS_START_OF_ITEM(_parent_layout->_characters[_char_index].in_span);
622 bool Layout::iterator::nextStartOfSpan()
623     NEXT_START_OF_ITEM(_parent_layout->_characters[_char_index].in_span);
626 bool Layout::iterator::prevStartOfChunk()
627     PREV_START_OF_ITEM(thisStartOfChunk);
629 bool Layout::iterator::thisStartOfChunk()
630     THIS_START_OF_ITEM(_parent_layout->_characters[_char_index].span(_parent_layout).in_chunk);
632 bool Layout::iterator::nextStartOfChunk()
633     NEXT_START_OF_ITEM(_parent_layout->_characters[_char_index].span(_parent_layout).in_chunk);
636 bool Layout::iterator::prevStartOfLine()
637     PREV_START_OF_ITEM(thisStartOfLine);
639 bool Layout::iterator::thisStartOfLine()
640     THIS_START_OF_ITEM(_parent_layout->_characters[_char_index].chunk(_parent_layout).in_line);
642 bool Layout::iterator::nextStartOfLine()
643     NEXT_START_OF_ITEM(_parent_layout->_characters[_char_index].chunk(_parent_layout).in_line);
646 bool Layout::iterator::prevStartOfShape()
647     PREV_START_OF_ITEM(thisStartOfShape);
649 bool Layout::iterator::thisStartOfShape()
650     THIS_START_OF_ITEM(_parent_layout->_characters[_char_index].line(_parent_layout).in_shape);
652 bool Layout::iterator::nextStartOfShape()
653     NEXT_START_OF_ITEM(_parent_layout->_characters[_char_index].line(_parent_layout).in_shape);
656 bool Layout::iterator::prevStartOfParagraph()
657     PREV_START_OF_ITEM(thisStartOfParagraph);
659 bool Layout::iterator::thisStartOfParagraph()
660     THIS_START_OF_ITEM(_parent_layout->_characters[_char_index].line(_parent_layout).in_paragraph);
662 bool Layout::iterator::nextStartOfParagraph()
663     NEXT_START_OF_ITEM(_parent_layout->_characters[_char_index].line(_parent_layout).in_paragraph);
666 bool Layout::iterator::prevStartOfSource()
667     PREV_START_OF_ITEM(thisStartOfSource);
669 bool Layout::iterator::thisStartOfSource()
670     THIS_START_OF_ITEM(_parent_layout->_characters[_char_index].span(_parent_layout).in_input_stream_item);
672 bool Layout::iterator::nextStartOfSource()
673     NEXT_START_OF_ITEM(_parent_layout->_characters[_char_index].span(_parent_layout).in_input_stream_item);
676 bool Layout::iterator::thisEndOfLine()
678     if (_char_index == _parent_layout->_characters.size()) return false;
679     if (nextStartOfLine())
680     {
681         if (_char_index && _parent_layout->_characters[_char_index - 1].char_attributes.is_white)
682             return prevCursorPosition();
683         return true;
684     }
685     if (_char_index && _parent_layout->_characters[_char_index - 1].chunk(_parent_layout).in_line != _parent_layout->_lines.size() - 1)
686         return prevCursorPosition();   // for when the last paragraph is empty
687     return false;
690 void Layout::iterator::beginCursorUpDown()
692     if (_char_index == _parent_layout->_characters.size())
693         _x_coordinate = _parent_layout->_chunks.back().left_x + _parent_layout->_spans.back().x_end;
694     else
695         _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;
696     _cursor_moving_vertically = true;
699 bool Layout::iterator::nextLineCursor(int n)
701     if (!_cursor_moving_vertically)
702         beginCursorUpDown();
703     if (_char_index == _parent_layout->_characters.size())
704         return false;
705     unsigned line_index = _parent_layout->_characters[_char_index].chunk(_parent_layout).in_line;
706     if (line_index == _parent_layout->_lines.size() - 1) 
707         return false; // nowhere to go
708                 else
709         n = MIN (n, static_cast<int>(_parent_layout->_lines.size() - 1 - line_index));
710     if (_parent_layout->_lines[line_index + n].in_shape != _parent_layout->_lines[line_index].in_shape) {
711         // switching between shapes: adjust the stored x to compensate
712         _x_coordinate +=   _parent_layout->_chunks[_parent_layout->_spans[_parent_layout->_lineToSpan(line_index + n)].in_chunk].left_x
713                          - _parent_layout->_chunks[_parent_layout->_spans[_parent_layout->_lineToSpan(line_index)].in_chunk].left_x;
714     }
715     _char_index = _parent_layout->_cursorXOnLineToIterator(line_index + n, _x_coordinate)._char_index;
716     _glyph_index = _parent_layout->_characters[_char_index].in_glyph;
717     return true;
720 bool Layout::iterator::prevLineCursor(int n)
722     if (!_cursor_moving_vertically)
723         beginCursorUpDown();
724     unsigned line_index;
725     if (_char_index == _parent_layout->_characters.size())
726         line_index = _parent_layout->_lines.size() - 1;
727     else
728         line_index = _parent_layout->_characters[_char_index].chunk(_parent_layout).in_line;
729     if (line_index == 0) 
730         return false; // nowhere to go
731                 else 
732         n = MIN (n, static_cast<int>(line_index));
733     if (_parent_layout->_lines[line_index - n].in_shape != _parent_layout->_lines[line_index].in_shape) {
734         // switching between shapes: adjust the stored x to compensate
735         _x_coordinate +=   _parent_layout->_chunks[_parent_layout->_spans[_parent_layout->_lineToSpan(line_index - n)].in_chunk].left_x
736                          - _parent_layout->_chunks[_parent_layout->_spans[_parent_layout->_lineToSpan(line_index)].in_chunk].left_x;
737     }
738     _char_index = _parent_layout->_cursorXOnLineToIterator(line_index - n, _x_coordinate)._char_index;
739     _glyph_index = _parent_layout->_characters[_char_index].in_glyph;
740     return true;
743 #define NEXT_WITH_ATTRIBUTE_SET(attr)                                                            \
744     {                                                                                            \
745         _cursor_moving_vertically = false;                                                       \
746         for ( ; ; ) {                                                                            \
747             if (_char_index + 1 >= _parent_layout->_characters.size()) {                         \
748                 _char_index = _parent_layout->_characters.size();                                \
749                 _glyph_index = _parent_layout->_glyphs.size();                                   \
750                 return false;                                                                    \
751             }                                                                                    \
752             _char_index++;                                                                       \
753             if (_parent_layout->_characters[_char_index].char_attributes.attr) break;            \
754         }                                                                                        \
755         _glyph_index = _parent_layout->_characters[_char_index].in_glyph;                        \
756         return true;                                                                             \
757     }
758 // end of macro
760 #define PREV_WITH_ATTRIBUTE_SET(attr)                                                            \
761     {                                                                                            \
762         _cursor_moving_vertically = false;                                                       \
763         for ( ; ; ) {                                                                            \
764             if (_char_index == 0) {                                                              \
765                 _glyph_index = 0;                                                                \
766                 return false;                                                                    \
767             }                                                                                    \
768             _char_index--;                                                                       \
769             if (_parent_layout->_characters[_char_index].char_attributes.attr) break;            \
770         }                                                                                        \
771         _glyph_index = _parent_layout->_characters[_char_index].in_glyph;                        \
772         return true;                                                                             \
773     }
774 // end of macro
776 bool Layout::iterator::nextCursorPosition()
777     NEXT_WITH_ATTRIBUTE_SET(is_cursor_position);
779 bool Layout::iterator::prevCursorPosition()
780     PREV_WITH_ATTRIBUTE_SET(is_cursor_position);
782 bool Layout::iterator::nextStartOfWord()
783     NEXT_WITH_ATTRIBUTE_SET(is_word_start);
785 bool Layout::iterator::prevStartOfWord()
786     PREV_WITH_ATTRIBUTE_SET(is_word_start);
788 bool Layout::iterator::nextEndOfWord()
789     NEXT_WITH_ATTRIBUTE_SET(is_word_end);
791 bool Layout::iterator::prevEndOfWord()
792     PREV_WITH_ATTRIBUTE_SET(is_word_end);
794 bool Layout::iterator::nextStartOfSentence()
795     NEXT_WITH_ATTRIBUTE_SET(is_sentence_start);
797 bool Layout::iterator::prevStartOfSentence()
798     PREV_WITH_ATTRIBUTE_SET(is_sentence_start);
800 bool Layout::iterator::nextEndOfSentence()
801     NEXT_WITH_ATTRIBUTE_SET(is_sentence_end);
803 bool Layout::iterator::prevEndOfSentence()
804     PREV_WITH_ATTRIBUTE_SET(is_sentence_end);
806 bool Layout::iterator::_cursorLeftOrRightLocalX(Direction direction)
808     // the only reason this function is so complicated is to enable visual cursor
809     // movement moving in to or out of counterdirectional runs
810     if (_parent_layout->_characters.empty()) return false;
811     unsigned old_span_index;
812     Direction old_span_direction;
813     if (_char_index == _parent_layout->_characters.size())
814         old_span_index = _parent_layout->_spans.size() - 1;
815     else
816         old_span_index = _parent_layout->_characters[_char_index].in_span;
817     old_span_direction = _parent_layout->_spans[old_span_index].direction;
818     Direction para_direction = _parent_layout->_spans[old_span_index].paragraph(_parent_layout).base_direction;
820     int scan_direction;
821     unsigned old_char_index = _char_index;
822     if (old_span_direction != para_direction
823         && ((_char_index == 0 && direction == para_direction)
824             || (_char_index == _parent_layout->_characters.size() && direction != para_direction))) {
825         // the end of the text is actually in the middle because of reordering. Do cleverness
826         scan_direction = direction == para_direction ? +1 : -1;
827     } else {
828         if (direction == old_span_direction) {
829             if (!nextCursorPosition()) return false;
830         } else {
831             if (!prevCursorPosition()) return false;
832         }
834         unsigned new_span_index = _parent_layout->_characters[_char_index].in_span;
835         if (new_span_index == old_span_index) return true;
836         if (old_span_direction != _parent_layout->_spans[new_span_index].direction) {
837             // we must jump to the other end of a counterdirectional run
838             scan_direction = direction == para_direction ? +1 : -1;
839         } else if (_parent_layout->_spans[old_span_index].in_chunk != _parent_layout->_spans[new_span_index].in_chunk) {
840             // we might have to do a weird jump when we would have crossed a chunk/line break
841             if (_parent_layout->_spans[old_span_index].line(_parent_layout).in_paragraph != _parent_layout->_spans[new_span_index].line(_parent_layout).in_paragraph)
842                 return true;
843             if (old_span_direction == para_direction)
844                 return true;
845             scan_direction = direction == para_direction ? +1 : -1;
846         } else
847             return true;    // same direction, same chunk: no cleverness required
848     }
850     unsigned new_span_index = old_span_index;
851     for ( ; ; ) {
852         if (scan_direction > 0) {
853             if (new_span_index == _parent_layout->_spans.size() - 1) {
854                 if (_parent_layout->_spans[new_span_index].direction == old_span_direction) {
855                     _char_index = old_char_index;
856                     return false;    // the visual end is in the logical middle
857                 }
858                 break;
859             }
860             new_span_index++;
861         } else {
862             if (new_span_index == 0) {
863                 if (_parent_layout->_spans[new_span_index].direction == old_span_direction) {
864                     _char_index = old_char_index;
865                     return false;    // the visual end is in the logical middle
866                 }
867                 break;
868             }
869             new_span_index--;
870         }
871         if (_parent_layout->_spans[new_span_index].direction == para_direction) {
872             if (para_direction == old_span_direction)
873                 new_span_index -= scan_direction;
874             break;
875         }
876         if (_parent_layout->_spans[new_span_index].in_chunk != _parent_layout->_spans[old_span_index].in_chunk) {
877             if (_parent_layout->_spans[old_span_index].line(_parent_layout).in_paragraph == _parent_layout->_spans[new_span_index].line(_parent_layout).in_paragraph
878                 && para_direction == old_span_direction)
879                 new_span_index -= scan_direction;
880             break;
881         }
882     }
884     // found the correct span, now find the correct character
885     if (_parent_layout->_spans[old_span_index].line(_parent_layout).in_paragraph != _parent_layout->_spans[new_span_index].line(_parent_layout).in_paragraph) {
886         if (new_span_index > old_span_index)
887             _char_index = _parent_layout->_spanToCharacter(new_span_index);
888         else
889             _char_index = _parent_layout->_spanToCharacter(new_span_index + 1) - 1;
890     } else {
891         if (_parent_layout->_spans[new_span_index].direction != direction) {
892             if (new_span_index >= _parent_layout->_spans.size() - 1)
893                 _char_index = _parent_layout->_characters.size();
894             else
895                 _char_index = _parent_layout->_spanToCharacter(new_span_index + 1) - 1;
896         } else
897             _char_index = _parent_layout->_spanToCharacter(new_span_index);
898     }
899     if (_char_index == _parent_layout->_characters.size()) {
900         _glyph_index = _parent_layout->_glyphs.size();
901         return false;
902     }
903     _glyph_index = _parent_layout->_characters[_char_index].in_glyph;
904     return _char_index != 0;
907 bool Layout::iterator::_cursorLeftOrRightLocalXByWord(Direction direction)
909     bool r;
910     while ((r = _cursorLeftOrRightLocalX(direction))
911            && !_parent_layout->_characters[_char_index].char_attributes.is_word_start);
912     return r;
915 bool Layout::iterator::cursorUp(int n)
917     Direction block_progression = _parent_layout->_blockProgression();
918     if(block_progression == TOP_TO_BOTTOM)
919         return prevLineCursor(n);
920     else if(block_progression == BOTTOM_TO_TOP)
921         return nextLineCursor(n);
922     else
923         return _cursorLeftOrRightLocalX(RIGHT_TO_LEFT);
926 bool Layout::iterator::cursorDown(int n)
928     Direction block_progression = _parent_layout->_blockProgression();
929     if(block_progression == TOP_TO_BOTTOM)
930         return nextLineCursor(n);
931     else if(block_progression == BOTTOM_TO_TOP)
932         return prevLineCursor(n);
933     else
934         return _cursorLeftOrRightLocalX(LEFT_TO_RIGHT);
937 bool Layout::iterator::cursorLeft()
939     Direction block_progression = _parent_layout->_blockProgression();
940     if(block_progression == LEFT_TO_RIGHT)
941         return prevLineCursor();
942     else if(block_progression == RIGHT_TO_LEFT)
943         return nextLineCursor();
944     else
945         return _cursorLeftOrRightLocalX(RIGHT_TO_LEFT);
948 bool Layout::iterator::cursorRight()
950     Direction block_progression = _parent_layout->_blockProgression();
951     if(block_progression == LEFT_TO_RIGHT)
952         return nextLineCursor();
953     else if(block_progression == RIGHT_TO_LEFT)
954         return prevLineCursor();
955     else
956         return _cursorLeftOrRightLocalX(LEFT_TO_RIGHT);
959 bool Layout::iterator::cursorUpWithControl()
961     Direction block_progression = _parent_layout->_blockProgression();
962     if(block_progression == TOP_TO_BOTTOM)
963         return prevStartOfParagraph();
964     else if(block_progression == BOTTOM_TO_TOP)
965         return nextStartOfParagraph();
966     else
967         return _cursorLeftOrRightLocalXByWord(RIGHT_TO_LEFT);
970 bool Layout::iterator::cursorDownWithControl()
972     Direction block_progression = _parent_layout->_blockProgression();
973     if(block_progression == TOP_TO_BOTTOM)
974         return nextStartOfParagraph();
975     else if(block_progression == BOTTOM_TO_TOP)
976         return prevStartOfParagraph();
977     else
978         return _cursorLeftOrRightLocalXByWord(LEFT_TO_RIGHT);
981 bool Layout::iterator::cursorLeftWithControl()
983     Direction block_progression = _parent_layout->_blockProgression();
984     if(block_progression == LEFT_TO_RIGHT)
985         return prevStartOfParagraph();
986     else if(block_progression == RIGHT_TO_LEFT)
987         return nextStartOfParagraph();
988     else
989         return _cursorLeftOrRightLocalXByWord(RIGHT_TO_LEFT);
992 bool Layout::iterator::cursorRightWithControl()
994     Direction block_progression = _parent_layout->_blockProgression();
995     if(block_progression == LEFT_TO_RIGHT)
996         return nextStartOfParagraph();
997     else if(block_progression == RIGHT_TO_LEFT)
998         return prevStartOfParagraph();
999     else
1000         return _cursorLeftOrRightLocalXByWord(LEFT_TO_RIGHT);
1003 }//namespace Text
1004 }//namespace Inkscape