Code

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