Code

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