Code

Mnemonics in "Input devices", and LPE dialogs (Bug 170765)
[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 Geom::OptRect 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 boost::optional<Geom::Point> Layout::baselineAnchorPoint() const
226     iterator pos = this->begin();
227     Geom::Point left_pt = this->characterAnchorPoint(pos);
228     pos.thisEndOfLine();
229     Geom::Point right_pt = this->characterAnchorPoint(pos);
231     if (this->_blockProgression() == LEFT_TO_RIGHT || this->_blockProgression() == RIGHT_TO_LEFT) {
232         left_pt = Geom::Point(left_pt[Geom::Y], left_pt[Geom::X]);
233         right_pt = Geom::Point(right_pt[Geom::Y], right_pt[Geom::X]);
234     }
236     switch (this->paragraphAlignment(pos)) {
237         case LEFT:
238         case FULL:
239             return left_pt;
240             break;
241         case CENTER:
242             return (left_pt + right_pt)/2; // middle point
243             break;
244         case RIGHT:
245             return right_pt;
246             break;
247         default:
248             return boost::optional<Geom::Point>();
249             break;
250     }
253 Geom::Point Layout::chunkAnchorPoint(iterator const &it) const
255     unsigned chunk_index;
257     if (_chunks.empty())
258         return Geom::Point(0.0, 0.0);
260     if (_characters.empty())
261         chunk_index = 0;
262     else if (it._char_index == _characters.size())
263         chunk_index = _chunks.size() - 1;
264     else chunk_index = _characters[it._char_index].span(this).in_chunk;
266     Alignment alignment = _paragraphs[_lines[_chunks[chunk_index].in_line].in_paragraph].alignment;
267     if (alignment == LEFT || alignment == FULL)
268         return Geom::Point(_chunks[chunk_index].left_x, _lines[chunk_index].baseline_y);
270     double chunk_width = _getChunkWidth(chunk_index);
271     if (alignment == RIGHT)
272         return Geom::Point(_chunks[chunk_index].left_x + chunk_width, _lines[chunk_index].baseline_y);
273     //centre
274     return Geom::Point(_chunks[chunk_index].left_x + chunk_width * 0.5, _lines[chunk_index].baseline_y);
277 Geom::Rect Layout::characterBoundingBox(iterator const &it, double *rotation) const
279     Geom::Point top_left, bottom_right;
280     unsigned char_index = it._char_index;
282     if (_path_fitted) {
283         double cluster_half_width = 0.0;
284         for (int glyph_index = _characters[char_index].in_glyph ; _glyphs[glyph_index].in_character == char_index ; glyph_index++)
285             cluster_half_width += _glyphs[glyph_index].width;
286         cluster_half_width *= 0.5;
288         double midpoint_offset = _characters[char_index].span(this).x_start + _characters[char_index].x + cluster_half_width;
289         int unused = 0;
290         Path::cut_position *midpoint_otp = const_cast<Path*>(_path_fitted)->CurvilignToPosition(1, &midpoint_offset, unused);
291         if (midpoint_offset >= 0.0 && midpoint_otp != NULL && midpoint_otp[0].piece >= 0) {
292             Geom::Point midpoint;
293             Geom::Point tangent;
294             Span const &span = _characters[char_index].span(this);
296             const_cast<Path*>(_path_fitted)->PointAndTangentAt(midpoint_otp[0].piece, midpoint_otp[0].t, midpoint, tangent);
297             top_left[Geom::X] = midpoint[Geom::X] - cluster_half_width;
298             top_left[Geom::Y] = midpoint[Geom::Y] - span.line_height.ascent;
299             bottom_right[Geom::X] = midpoint[Geom::X] + cluster_half_width;
300             bottom_right[Geom::Y] = midpoint[Geom::Y] + span.line_height.descent;
301             Geom::Point normal = tangent.cw();
302             top_left += span.baseline_shift * normal;
303             bottom_right += span.baseline_shift * normal;
304             if (rotation)
305                 *rotation = atan2(tangent[1], tangent[0]);
306         }
307         g_free(midpoint_otp);
308     } else {
309         if (it._char_index == _characters.size()) {
310             top_left[Geom::X] = bottom_right[Geom::X] = _chunks.back().left_x + _spans.back().x_end;
311             char_index--;
312         } else {
313             double span_x = _spans[_characters[it._char_index].in_span].x_start + _characters[it._char_index].chunk(this).left_x;
314             top_left[Geom::X] = span_x + _characters[it._char_index].x;
315             if (it._char_index + 1 == _characters.size() || _characters[it._char_index + 1].in_span != _characters[it._char_index].in_span)
316                 bottom_right[Geom::X] = _spans[_characters[it._char_index].in_span].x_end + _characters[it._char_index].chunk(this).left_x;
317             else
318                 bottom_right[Geom::X] = span_x + _characters[it._char_index + 1].x;
319         }
321         double baseline_y = _characters[char_index].line(this).baseline_y + _characters[char_index].span(this).baseline_shift;
322         if (_directions_are_orthogonal(_blockProgression(), TOP_TO_BOTTOM)) {
323             double span_height = _spans[_characters[char_index].in_span].line_height.ascent + _spans[_characters[char_index].in_span].line_height.descent;
324             top_left[Geom::Y] = top_left[Geom::X];
325             top_left[Geom::X] = baseline_y - span_height * 0.5;
326             bottom_right[Geom::Y] = bottom_right[Geom::X];
327             bottom_right[Geom::X] = baseline_y + span_height * 0.5;
328         } else {
329             top_left[Geom::Y] = baseline_y - _spans[_characters[char_index].in_span].line_height.ascent;
330             bottom_right[Geom::Y] = baseline_y + _spans[_characters[char_index].in_span].line_height.descent;
331         }
333         if (rotation) {
334             if (it._glyph_index == -1)
335                 *rotation = 0.0;
336             else if (it._glyph_index == (int)_glyphs.size())
337                 *rotation = _glyphs.back().rotation;
338             else
339                 *rotation = _glyphs[it._glyph_index].rotation;
340         }
341     }
343     return Geom::Rect(top_left, bottom_right);
346 std::vector<Geom::Point> Layout::createSelectionShape(iterator const &it_start, iterator const &it_end, Geom::Matrix const &transform) const
348     std::vector<Geom::Point> quads;
349     unsigned char_index;
350     unsigned end_char_index;
351     
352     if (it_start._char_index < it_end._char_index) {
353         char_index = it_start._char_index;
354         end_char_index = it_end._char_index;
355     } else {
356         char_index = it_end._char_index;
357         end_char_index = it_start._char_index;
358     }
359     for ( ; char_index < end_char_index ; ) {
360         if (_characters[char_index].in_glyph == -1) {
361             char_index++;
362             continue;
363         }
364         double char_rotation = _glyphs[_characters[char_index].in_glyph].rotation;
365         unsigned span_index = _characters[char_index].in_span;
367         Geom::Point top_left, bottom_right;
368         if (_path_fitted || char_rotation != 0.0) {
369             Geom::Rect box = characterBoundingBox(iterator(this, char_index), &char_rotation);
370             top_left = box.min();
371             bottom_right = box.max();
372             char_index++;
373         } else {   // for straight text we can be faster by combining all the character boxes in a span into one box
374             double span_x = _spans[span_index].x_start + _spans[span_index].chunk(this).left_x;
375             top_left[Geom::X] = span_x + _characters[char_index].x;
376             while (char_index < end_char_index && _characters[char_index].in_span == span_index)
377                 char_index++;
378             if (char_index == _characters.size() || _characters[char_index].in_span != span_index)
379                 bottom_right[Geom::X] = _spans[span_index].x_end + _spans[span_index].chunk(this).left_x;
380             else
381                 bottom_right[Geom::X] = span_x + _characters[char_index].x;
383             double baseline_y = _spans[span_index].line(this).baseline_y + _spans[span_index].baseline_shift;
384             if (_directions_are_orthogonal(_blockProgression(), TOP_TO_BOTTOM)) {
385                 double span_height = _spans[span_index].line_height.ascent + _spans[span_index].line_height.descent;
386                 top_left[Geom::Y] = top_left[Geom::X];
387                 top_left[Geom::X] = baseline_y - span_height * 0.5;
388                 bottom_right[Geom::Y] = bottom_right[Geom::X];
389                 bottom_right[Geom::X] = baseline_y + span_height * 0.5;
390             } else {
391                 top_left[Geom::Y] = baseline_y - _spans[span_index].line_height.ascent;
392                 bottom_right[Geom::Y] = baseline_y + _spans[span_index].line_height.descent;
393             }
394         }
396         Geom::Rect char_box(top_left, bottom_right);
397         if (char_box.dimensions()[Geom::X] == 0.0 || char_box.dimensions()[Geom::Y] == 0.0)
398             continue;
399         Geom::Point center_of_rotation((top_left[Geom::X] + bottom_right[Geom::X]) * 0.5,
400                                      top_left[Geom::Y] + _spans[span_index].line_height.ascent);
401         Geom::Matrix total_transform = Geom::Translate(-center_of_rotation) * Geom::Rotate(char_rotation) * Geom::Translate(center_of_rotation) * transform;
402         for(int i = 0; i < 4; i ++)
403             quads.push_back(char_box.corner(i) * total_transform);
404     }
405     return quads;
408 void Layout::queryCursorShape(iterator const &it, Geom::Point &position, double &height, double &rotation) const
410     if (_characters.empty()) {
411         position = _empty_cursor_shape.position;
412         height = _empty_cursor_shape.height;
413         rotation = _empty_cursor_shape.rotation;
414     } else {
415         // we want to cursor to be positioned where the left edge of a character that is about to be typed will be.
416         // this means x & rotation are the current values but y & height belong to the previous character.
417         // this isn't quite right because dx attributes will be moved along, but it's good enough
418         Span const *span;
419         if (_path_fitted) {
420             // text on a path
421             double x;
422             if (it._char_index >= _characters.size()) {
423                 span = &_spans.back();
424                 x = span->x_end + _chunks.back().left_x - _chunks[0].left_x;
425             } else {
426                 span = &_spans[_characters[it._char_index].in_span];
427                 x = _chunks[span->in_chunk].left_x + span->x_start + _characters[it._char_index].x - _chunks[0].left_x;
428                 if (it._char_index != 0)
429                     span = &_spans[_characters[it._char_index - 1].in_span];
430             }
431             double path_length = const_cast<Path*>(_path_fitted)->Length();
432             double x_on_path = x;
433             if (x_on_path < 0.0) x_on_path = 0.0;
435             int unused = 0;
436                 // as far as I know these functions are const, they're just not marked as such
437             Path::cut_position *path_parameter_list = const_cast<Path*>(_path_fitted)->CurvilignToPosition(1, &x_on_path, unused);
438             Path::cut_position path_parameter;
439             if (path_parameter_list != NULL && path_parameter_list[0].piece >= 0)
440                 path_parameter = path_parameter_list[0];
441             else {
442                 path_parameter.piece = _path_fitted->descr_cmd.size() - 1;
443                 path_parameter.t = 0.9999;   // 1.0 will get the wrong tangent
444             }
445             g_free(path_parameter_list);
447             Geom::Point point;
448             Geom::Point tangent;
449             const_cast<Path*>(_path_fitted)->PointAndTangentAt(path_parameter.piece, path_parameter.t, point, tangent);
450             if (x < 0.0)
451                 point += x * tangent;
452             if (x > path_length )
453                 point += (x - path_length) * tangent;
454             rotation = atan2(tangent);
455             position[Geom::X] = point[Geom::X] - tangent[Geom::Y] * span->baseline_shift;
456             position[Geom::Y] = point[Geom::Y] + tangent[Geom::X] * span->baseline_shift;
457         } else {
458             // text is not on a path
459             if (it._char_index >= _characters.size()) {
460                 span = &_spans.back();
461                 position[Geom::X] = _chunks[span->in_chunk].left_x + span->x_end;
462                 rotation = _glyphs.empty() ? 0.0 : _glyphs.back().rotation;
463             } else {
464                 span = &_spans[_characters[it._char_index].in_span];
465                 position[Geom::X] = _chunks[span->in_chunk].left_x + span->x_start + _characters[it._char_index].x;
466                 if (it._glyph_index == -1) {
467                     rotation = 0.0;
468                 } else if(it._glyph_index == 0) {
469                     rotation = _glyphs[0].rotation;
470                 } else{
471                     rotation = _glyphs[it._glyph_index - 1].rotation;
472                 }
473                 // 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
474                 if (it._char_index != 0 && _characters[it._char_index - 1].chunk(this).in_line == _chunks[span->in_chunk].in_line)
475                     span = &_spans[_characters[it._char_index - 1].in_span];
476             }
477             position[Geom::Y] = span->line(this).baseline_y + span->baseline_shift;
478         }
479         // up to now *position is the baseline point, not the final point which will be the bottom of the descent
480         if (_directions_are_orthogonal(_blockProgression(), TOP_TO_BOTTOM)) {
481             height = span->line_height.ascent + span->line_height.descent;
482             rotation += M_PI / 2;
483             std::swap(position[Geom::X], position[Geom::Y]);
484             position[Geom::X] -= sin(rotation) * height * 0.5;
485             position[Geom::Y] += cos(rotation) * height * 0.5;
486         } else {
487             double caret_slope_run = 0.0, caret_slope_rise = 1.0;
488             if (span->font)
489                 const_cast<font_instance*>(span->font)->FontSlope(caret_slope_run, caret_slope_rise);
490             double caret_slope = atan2(caret_slope_run, caret_slope_rise);
491             height = (span->line_height.ascent + span->line_height.descent) / cos(caret_slope);
492             rotation += caret_slope;
493             position[Geom::X] -= sin(rotation) * span->line_height.descent;
494             position[Geom::Y] += cos(rotation) * span->line_height.descent;
495         }
496     }
499 void Layout::getSourceOfCharacter(iterator const &it, void **source_cookie, Glib::ustring::iterator *text_iterator) const
501     if (it._char_index == _characters.size()) {
502         *source_cookie = NULL;
503         return;
504     }
505     InputStreamItem *stream_item = _input_stream[_spans[_characters[it._char_index].in_span].in_input_stream_item];
506     *source_cookie = stream_item->source_cookie;
507     if (text_iterator && stream_item->Type() == TEXT_SOURCE) {
508         InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(stream_item);
509         Glib::ustring::const_iterator text_iter_const = text_source->text_begin;
510         unsigned char_index = it._char_index;
511         unsigned original_input_source_index = _spans[_characters[char_index].in_span].in_input_stream_item;
512         // confusing algorithm because the iterator goes forwards while the index goes backwards.
513         // It's just that it's faster doing it that way
514         while (char_index && _spans[_characters[char_index - 1].in_span].in_input_stream_item == original_input_source_index) {
515             ++text_iter_const;
516             char_index--;
517         }
518         text_source->text->begin().base() + (text_iter_const.base() - text_source->text->begin().base());
519         *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()))));
520              // the caller owns the string, so they're going to want a non-const iterator
521     }
524 void Layout::simulateLayoutUsingKerning(iterator const &from, iterator const &to, OptionalTextTagAttrs *result) const
526     SVGLength zero_length;
527     zero_length = 0.0;
529     result->x.clear();
530     result->y.clear();
531     result->dx.clear();
532     result->dy.clear();
533     result->rotate.clear();
534     if (to._char_index <= from._char_index)
535         return;
536     result->dx.reserve(to._char_index - from._char_index);
537     result->dy.reserve(to._char_index - from._char_index);
538     result->rotate.reserve(to._char_index - from._char_index);
539     for (unsigned char_index = from._char_index ; char_index < to._char_index ; char_index++) {
540         if (!_characters[char_index].char_attributes.is_char_break)
541             continue;
542         if (char_index == 0)
543             continue;
544         if (_characters[char_index].chunk(this).in_line != _characters[char_index - 1].chunk(this).in_line)
545             continue;
547         unsigned prev_cluster_char_index;
548         for (prev_cluster_char_index = char_index - 1 ;
549              prev_cluster_char_index != 0 && !_characters[prev_cluster_char_index].char_attributes.is_cursor_position ;
550              prev_cluster_char_index--){};
551         if (_characters[char_index].span(this).in_chunk == _characters[char_index - 1].span(this).in_chunk) {
552             // dx is zero for the first char in a chunk
553             // this algorithm works by comparing the summed widths of the glyphs with the observed
554             // difference in x coordinates of characters, and subtracting the two to produce the x kerning.
555             double glyphs_width = 0.0;
556             if (_characters[prev_cluster_char_index].in_glyph != -1)
557                 for (int glyph_index = _characters[prev_cluster_char_index].in_glyph ; glyph_index < _characters[char_index].in_glyph ; glyph_index++)
558                     glyphs_width += _glyphs[glyph_index].width;
559             if (_characters[char_index].span(this).direction == RIGHT_TO_LEFT)
560                 glyphs_width = -glyphs_width;
562             double dx = (_characters[char_index].x + _characters[char_index].span(this).x_start
563                          - _characters[prev_cluster_char_index].x - _characters[prev_cluster_char_index].span(this).x_start)
564                         - glyphs_width;
566             
567             InputStreamItem *input_item = _input_stream[_characters[char_index].span(this).in_input_stream_item];
568             if (input_item->Type() == TEXT_SOURCE) {
569                 SPStyle const *style = static_cast<InputStreamTextSource*>(input_item)->style;
570                 if (_characters[char_index].char_attributes.is_white)
571                     dx -= style->word_spacing.computed;
572                 if (_characters[char_index].char_attributes.is_cursor_position)
573                     dx -= style->letter_spacing.computed;
574             }
576             if (fabs(dx) > 0.0001) {
577                 result->dx.resize(char_index - from._char_index + 1, zero_length);
578                 result->dx.back() = dx;
579             }
580         }
581         double dy = _characters[char_index].span(this).baseline_shift - _characters[prev_cluster_char_index].span(this).baseline_shift;
582         if (fabs(dy) > 0.0001) {
583             result->dy.resize(char_index - from._char_index + 1, zero_length);
584             result->dy.back() = dy;
585         }
586         if (_characters[char_index].in_glyph != -1 && _glyphs[_characters[char_index].in_glyph].rotation != 0.0) {
587             result->rotate.resize(char_index - from._char_index + 1, zero_length);
588             result->rotate.back() = _glyphs[_characters[char_index].in_glyph].rotation;
589         }
590     }
593 #define PREV_START_OF_ITEM(this_func)                                                    \
594     {                                                                                    \
595         _cursor_moving_vertically = false;                                               \
596         if (_char_index == 0) return false;                                              \
597         _char_index--;                                                                   \
598         return this_func();                                                              \
599     }
600 // end of macro
602 #define THIS_START_OF_ITEM(item_getter)                                                  \
603     {                                                                                    \
604         _cursor_moving_vertically = false;                                               \
605         if (_char_index == 0) return false;                                              \
606         unsigned original_item;                                                          \
607         if (_char_index == _parent_layout->_characters.size()) {                         \
608             _char_index--;                                                               \
609             original_item = item_getter;                                                 \
610         } else {                                                                         \
611             original_item = item_getter;                                                 \
612             _char_index--;                                                               \
613         }                                                                                \
614         while (item_getter == original_item) {                                           \
615             if (_char_index == 0) {                                                      \
616                 _glyph_index = _parent_layout->_characters[_char_index].in_glyph;        \
617                 return true;                                                             \
618             }                                                                            \
619             _char_index--;                                                               \
620         }                                                                                \
621         _char_index++;                                                                   \
622         _glyph_index = _parent_layout->_characters[_char_index].in_glyph;                \
623         return true;                                                                     \
624     }
625 // end of macro
627 #define NEXT_START_OF_ITEM(item_getter)                                                  \
628     {                                                                                    \
629         _cursor_moving_vertically = false;                                               \
630         if (_char_index == _parent_layout->_characters.size()) return false;             \
631         unsigned original_item = item_getter;                                            \
632         for( ; ; ) {                                                                     \
633             _char_index++;                                                               \
634             if (_char_index == _parent_layout->_characters.size()) {                     \
635                 _glyph_index = _parent_layout->_glyphs.size();                           \
636                 return false;                                                            \
637             }                                                                            \
638             if (item_getter != original_item) break;                                     \
639         }                                                                                \
640         _glyph_index = _parent_layout->_characters[_char_index].in_glyph;                \
641         return true;                                                                     \
642     }
643 // end of macro
645 bool Layout::iterator::prevStartOfSpan()
646     PREV_START_OF_ITEM(thisStartOfSpan);
648 bool Layout::iterator::thisStartOfSpan()
649     THIS_START_OF_ITEM(_parent_layout->_characters[_char_index].in_span);
651 bool Layout::iterator::nextStartOfSpan()
652     NEXT_START_OF_ITEM(_parent_layout->_characters[_char_index].in_span);
655 bool Layout::iterator::prevStartOfChunk()
656     PREV_START_OF_ITEM(thisStartOfChunk);
658 bool Layout::iterator::thisStartOfChunk()
659     THIS_START_OF_ITEM(_parent_layout->_characters[_char_index].span(_parent_layout).in_chunk);
661 bool Layout::iterator::nextStartOfChunk()
662     NEXT_START_OF_ITEM(_parent_layout->_characters[_char_index].span(_parent_layout).in_chunk);
665 bool Layout::iterator::prevStartOfLine()
666     PREV_START_OF_ITEM(thisStartOfLine);
668 bool Layout::iterator::thisStartOfLine()
669     THIS_START_OF_ITEM(_parent_layout->_characters[_char_index].chunk(_parent_layout).in_line);
671 bool Layout::iterator::nextStartOfLine()
672     NEXT_START_OF_ITEM(_parent_layout->_characters[_char_index].chunk(_parent_layout).in_line);
675 bool Layout::iterator::prevStartOfShape()
676     PREV_START_OF_ITEM(thisStartOfShape);
678 bool Layout::iterator::thisStartOfShape()
679     THIS_START_OF_ITEM(_parent_layout->_characters[_char_index].line(_parent_layout).in_shape);
681 bool Layout::iterator::nextStartOfShape()
682     NEXT_START_OF_ITEM(_parent_layout->_characters[_char_index].line(_parent_layout).in_shape);
685 bool Layout::iterator::prevStartOfParagraph()
686     PREV_START_OF_ITEM(thisStartOfParagraph);
688 bool Layout::iterator::thisStartOfParagraph()
689     THIS_START_OF_ITEM(_parent_layout->_characters[_char_index].line(_parent_layout).in_paragraph);
691 bool Layout::iterator::nextStartOfParagraph()
692     NEXT_START_OF_ITEM(_parent_layout->_characters[_char_index].line(_parent_layout).in_paragraph);
695 bool Layout::iterator::prevStartOfSource()
696     PREV_START_OF_ITEM(thisStartOfSource);
698 bool Layout::iterator::thisStartOfSource()
699     THIS_START_OF_ITEM(_parent_layout->_characters[_char_index].span(_parent_layout).in_input_stream_item);
701 bool Layout::iterator::nextStartOfSource()
702     NEXT_START_OF_ITEM(_parent_layout->_characters[_char_index].span(_parent_layout).in_input_stream_item);
705 bool Layout::iterator::thisEndOfLine()
707     if (_char_index == _parent_layout->_characters.size()) return false;
708     if (nextStartOfLine())
709     {
710         if (_char_index && _parent_layout->_characters[_char_index - 1].char_attributes.is_white)
711             return prevCursorPosition();
712         return true;
713     }
714     if (_char_index && _parent_layout->_characters[_char_index - 1].chunk(_parent_layout).in_line != _parent_layout->_lines.size() - 1)
715         return prevCursorPosition();   // for when the last paragraph is empty
716     return false;
719 void Layout::iterator::beginCursorUpDown()
721     if (_char_index == _parent_layout->_characters.size())
722         _x_coordinate = _parent_layout->_chunks.back().left_x + _parent_layout->_spans.back().x_end;
723     else
724         _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;
725     _cursor_moving_vertically = true;
728 bool Layout::iterator::nextLineCursor(int n)
730     if (!_cursor_moving_vertically)
731         beginCursorUpDown();
732     if (_char_index == _parent_layout->_characters.size())
733         return false;
734     unsigned line_index = _parent_layout->_characters[_char_index].chunk(_parent_layout).in_line;
735     if (line_index == _parent_layout->_lines.size() - 1) 
736         return false; // nowhere to go
737     else
738         n = MIN (n, static_cast<int>(_parent_layout->_lines.size() - 1 - line_index));
739     if (_parent_layout->_lines[line_index + n].in_shape != _parent_layout->_lines[line_index].in_shape) {
740         // switching between shapes: adjust the stored x to compensate
741         _x_coordinate +=   _parent_layout->_chunks[_parent_layout->_spans[_parent_layout->_lineToSpan(line_index + n)].in_chunk].left_x
742                          - _parent_layout->_chunks[_parent_layout->_spans[_parent_layout->_lineToSpan(line_index)].in_chunk].left_x;
743     }
744     _char_index = _parent_layout->_cursorXOnLineToIterator(line_index + n, _x_coordinate)._char_index;
745     _glyph_index = _parent_layout->_characters[_char_index].in_glyph;
746     return true;
749 bool Layout::iterator::prevLineCursor(int n)
751     if (!_cursor_moving_vertically)
752         beginCursorUpDown();
753     unsigned line_index;
754     if (_char_index == _parent_layout->_characters.size())
755         line_index = _parent_layout->_lines.size() - 1;
756     else
757         line_index = _parent_layout->_characters[_char_index].chunk(_parent_layout).in_line;
758     if (line_index == 0) 
759         return false; // nowhere to go
760     else
761         n = MIN (n, static_cast<int>(line_index));
762     if (_parent_layout->_lines[line_index - n].in_shape != _parent_layout->_lines[line_index].in_shape) {
763         // switching between shapes: adjust the stored x to compensate
764         _x_coordinate +=   _parent_layout->_chunks[_parent_layout->_spans[_parent_layout->_lineToSpan(line_index - n)].in_chunk].left_x
765                          - _parent_layout->_chunks[_parent_layout->_spans[_parent_layout->_lineToSpan(line_index)].in_chunk].left_x;
766     }
767     _char_index = _parent_layout->_cursorXOnLineToIterator(line_index - n, _x_coordinate)._char_index;
768     _glyph_index = _parent_layout->_characters[_char_index].in_glyph;
769     return true;
772 #define NEXT_WITH_ATTRIBUTE_SET(attr)                                                            \
773     {                                                                                            \
774         _cursor_moving_vertically = false;                                                       \
775         for ( ; ; ) {                                                                            \
776             if (_char_index + 1 >= _parent_layout->_characters.size()) {                         \
777                 _char_index = _parent_layout->_characters.size();                                \
778                 _glyph_index = _parent_layout->_glyphs.size();                                   \
779                 return false;                                                                    \
780             }                                                                                    \
781             _char_index++;                                                                       \
782             if (_parent_layout->_characters[_char_index].char_attributes.attr) break;            \
783         }                                                                                        \
784         _glyph_index = _parent_layout->_characters[_char_index].in_glyph;                        \
785         return true;                                                                             \
786     }
787 // end of macro
789 #define PREV_WITH_ATTRIBUTE_SET(attr)                                                            \
790     {                                                                                            \
791         _cursor_moving_vertically = false;                                                       \
792         for ( ; ; ) {                                                                            \
793             if (_char_index == 0) {                                                              \
794                 _glyph_index = 0;                                                                \
795                 return false;                                                                    \
796             }                                                                                    \
797             _char_index--;                                                                       \
798             if (_parent_layout->_characters[_char_index].char_attributes.attr) break;            \
799         }                                                                                        \
800         _glyph_index = _parent_layout->_characters[_char_index].in_glyph;                        \
801         return true;                                                                             \
802     }
803 // end of macro
805 bool Layout::iterator::nextCursorPosition()
806     NEXT_WITH_ATTRIBUTE_SET(is_cursor_position);
808 bool Layout::iterator::prevCursorPosition()
809     PREV_WITH_ATTRIBUTE_SET(is_cursor_position);
811 bool Layout::iterator::nextStartOfWord()
812     NEXT_WITH_ATTRIBUTE_SET(is_word_start);
814 bool Layout::iterator::prevStartOfWord()
815     PREV_WITH_ATTRIBUTE_SET(is_word_start);
817 bool Layout::iterator::nextEndOfWord()
818     NEXT_WITH_ATTRIBUTE_SET(is_word_end);
820 bool Layout::iterator::prevEndOfWord()
821     PREV_WITH_ATTRIBUTE_SET(is_word_end);
823 bool Layout::iterator::nextStartOfSentence()
824     NEXT_WITH_ATTRIBUTE_SET(is_sentence_start);
826 bool Layout::iterator::prevStartOfSentence()
827     PREV_WITH_ATTRIBUTE_SET(is_sentence_start);
829 bool Layout::iterator::nextEndOfSentence()
830     NEXT_WITH_ATTRIBUTE_SET(is_sentence_end);
832 bool Layout::iterator::prevEndOfSentence()
833     PREV_WITH_ATTRIBUTE_SET(is_sentence_end);
835 bool Layout::iterator::_cursorLeftOrRightLocalX(Direction direction)
837     // the only reason this function is so complicated is to enable visual cursor
838     // movement moving in to or out of counterdirectional runs
839     if (_parent_layout->_characters.empty()) return false;
840     unsigned old_span_index;
841     Direction old_span_direction;
842     if (_char_index == _parent_layout->_characters.size())
843         old_span_index = _parent_layout->_spans.size() - 1;
844     else
845         old_span_index = _parent_layout->_characters[_char_index].in_span;
846     old_span_direction = _parent_layout->_spans[old_span_index].direction;
847     Direction para_direction = _parent_layout->_spans[old_span_index].paragraph(_parent_layout).base_direction;
849     int scan_direction;
850     unsigned old_char_index = _char_index;
851     if (old_span_direction != para_direction
852         && ((_char_index == 0 && direction == para_direction)
853             || (_char_index == _parent_layout->_characters.size() && direction != para_direction))) {
854         // the end of the text is actually in the middle because of reordering. Do cleverness
855         scan_direction = direction == para_direction ? +1 : -1;
856     } else {
857         if (direction == old_span_direction) {
858             if (!nextCursorPosition()) return false;
859         } else {
860             if (!prevCursorPosition()) return false;
861         }
863         unsigned new_span_index = _parent_layout->_characters[_char_index].in_span;
864         if (new_span_index == old_span_index) return true;
865         if (old_span_direction != _parent_layout->_spans[new_span_index].direction) {
866             // we must jump to the other end of a counterdirectional run
867             scan_direction = direction == para_direction ? +1 : -1;
868         } else if (_parent_layout->_spans[old_span_index].in_chunk != _parent_layout->_spans[new_span_index].in_chunk) {
869             // we might have to do a weird jump when we would have crossed a chunk/line break
870             if (_parent_layout->_spans[old_span_index].line(_parent_layout).in_paragraph != _parent_layout->_spans[new_span_index].line(_parent_layout).in_paragraph)
871                 return true;
872             if (old_span_direction == para_direction)
873                 return true;
874             scan_direction = direction == para_direction ? +1 : -1;
875         } else
876             return true;    // same direction, same chunk: no cleverness required
877     }
879     unsigned new_span_index = old_span_index;
880     for ( ; ; ) {
881         if (scan_direction > 0) {
882             if (new_span_index == _parent_layout->_spans.size() - 1) {
883                 if (_parent_layout->_spans[new_span_index].direction == old_span_direction) {
884                     _char_index = old_char_index;
885                     return false;    // the visual end is in the logical middle
886                 }
887                 break;
888             }
889             new_span_index++;
890         } else {
891             if (new_span_index == 0) {
892                 if (_parent_layout->_spans[new_span_index].direction == old_span_direction) {
893                     _char_index = old_char_index;
894                     return false;    // the visual end is in the logical middle
895                 }
896                 break;
897             }
898             new_span_index--;
899         }
900         if (_parent_layout->_spans[new_span_index].direction == para_direction) {
901             if (para_direction == old_span_direction)
902                 new_span_index -= scan_direction;
903             break;
904         }
905         if (_parent_layout->_spans[new_span_index].in_chunk != _parent_layout->_spans[old_span_index].in_chunk) {
906             if (_parent_layout->_spans[old_span_index].line(_parent_layout).in_paragraph == _parent_layout->_spans[new_span_index].line(_parent_layout).in_paragraph
907                 && para_direction == old_span_direction)
908                 new_span_index -= scan_direction;
909             break;
910         }
911     }
913     // found the correct span, now find the correct character
914     if (_parent_layout->_spans[old_span_index].line(_parent_layout).in_paragraph != _parent_layout->_spans[new_span_index].line(_parent_layout).in_paragraph) {
915         if (new_span_index > old_span_index)
916             _char_index = _parent_layout->_spanToCharacter(new_span_index);
917         else
918             _char_index = _parent_layout->_spanToCharacter(new_span_index + 1) - 1;
919     } else {
920         if (_parent_layout->_spans[new_span_index].direction != direction) {
921             if (new_span_index >= _parent_layout->_spans.size() - 1)
922                 _char_index = _parent_layout->_characters.size();
923             else
924                 _char_index = _parent_layout->_spanToCharacter(new_span_index + 1) - 1;
925         } else
926             _char_index = _parent_layout->_spanToCharacter(new_span_index);
927     }
928     if (_char_index == _parent_layout->_characters.size()) {
929         _glyph_index = _parent_layout->_glyphs.size();
930         return false;
931     }
932     _glyph_index = _parent_layout->_characters[_char_index].in_glyph;
933     return _char_index != 0;
936 bool Layout::iterator::_cursorLeftOrRightLocalXByWord(Direction direction)
938     bool r;
939     while ((r = _cursorLeftOrRightLocalX(direction))
940            && !_parent_layout->_characters[_char_index].char_attributes.is_word_start){};
941     return r;
944 bool Layout::iterator::cursorUp(int n)
946     Direction block_progression = _parent_layout->_blockProgression();
947     if(block_progression == TOP_TO_BOTTOM)
948         return prevLineCursor(n);
949     else if(block_progression == BOTTOM_TO_TOP)
950         return nextLineCursor(n);
951     else
952         return _cursorLeftOrRightLocalX(RIGHT_TO_LEFT);
955 bool Layout::iterator::cursorDown(int n)
957     Direction block_progression = _parent_layout->_blockProgression();
958     if(block_progression == TOP_TO_BOTTOM)
959         return nextLineCursor(n);
960     else if(block_progression == BOTTOM_TO_TOP)
961         return prevLineCursor(n);
962     else
963         return _cursorLeftOrRightLocalX(LEFT_TO_RIGHT);
966 bool Layout::iterator::cursorLeft()
968     Direction block_progression = _parent_layout->_blockProgression();
969     if(block_progression == LEFT_TO_RIGHT)
970         return prevLineCursor();
971     else if(block_progression == RIGHT_TO_LEFT)
972         return nextLineCursor();
973     else
974         return _cursorLeftOrRightLocalX(RIGHT_TO_LEFT);
977 bool Layout::iterator::cursorRight()
979     Direction block_progression = _parent_layout->_blockProgression();
980     if(block_progression == LEFT_TO_RIGHT)
981         return nextLineCursor();
982     else if(block_progression == RIGHT_TO_LEFT)
983         return prevLineCursor();
984     else
985         return _cursorLeftOrRightLocalX(LEFT_TO_RIGHT);
988 bool Layout::iterator::cursorUpWithControl()
990     Direction block_progression = _parent_layout->_blockProgression();
991     if(block_progression == TOP_TO_BOTTOM)
992         return prevStartOfParagraph();
993     else if(block_progression == BOTTOM_TO_TOP)
994         return nextStartOfParagraph();
995     else
996         return _cursorLeftOrRightLocalXByWord(RIGHT_TO_LEFT);
999 bool Layout::iterator::cursorDownWithControl()
1001     Direction block_progression = _parent_layout->_blockProgression();
1002     if(block_progression == TOP_TO_BOTTOM)
1003         return nextStartOfParagraph();
1004     else if(block_progression == BOTTOM_TO_TOP)
1005         return prevStartOfParagraph();
1006     else
1007         return _cursorLeftOrRightLocalXByWord(LEFT_TO_RIGHT);
1010 bool Layout::iterator::cursorLeftWithControl()
1012     Direction block_progression = _parent_layout->_blockProgression();
1013     if(block_progression == LEFT_TO_RIGHT)
1014         return prevStartOfParagraph();
1015     else if(block_progression == RIGHT_TO_LEFT)
1016         return nextStartOfParagraph();
1017     else
1018         return _cursorLeftOrRightLocalXByWord(RIGHT_TO_LEFT);
1021 bool Layout::iterator::cursorRightWithControl()
1023     Direction block_progression = _parent_layout->_blockProgression();
1024     if(block_progression == LEFT_TO_RIGHT)
1025         return nextStartOfParagraph();
1026     else if(block_progression == RIGHT_TO_LEFT)
1027         return prevStartOfParagraph();
1028     else
1029         return _cursorLeftOrRightLocalXByWord(LEFT_TO_RIGHT);
1032 }//namespace Text
1033 }//namespace Inkscape