~valavanisalex/ubuntu/precise/inkscape/fix-943984

« back to all changes in this revision

Viewing changes to inkscape-0.47pre1/src/libnrtype/Layout-TNG-OutIter.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Bryce Harrington
  • Date: 2009-07-02 17:09:45 UTC
  • mfrom: (1.1.9 upstream)
  • Revision ID: james.westby@ubuntu.com-20090702170945-nn6d6zswovbwju1t
Tags: 0.47~pre1-0ubuntu1
* New upstream release.
  - Don't constrain maximization on small resolution devices (pre0)
    (LP: #348842)
  - Fixes segfault on startup (pre0)
    (LP: #391149)

Show diffs side-by-side

added added

removed removed

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