Code

5fa20c81c41a8591a5c95085e00b0f6002448f6c
[inkscape.git] / src / libnrtype / Layout-TNG-Output.cpp
1 /*
2  * Inkscape::Text::Layout - text layout engine output functions
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 <glib/gmem.h>
12 #include "Layout-TNG.h"
13 #include "display/nr-arena-glyphs.h"
14 #include "style.h"
15 #include "print.h"
16 #include "extension/print.h"
17 #include "livarot/Path.h"
18 #include "libnr/nr-matrix-fns.h"
19 #include "libnr/nr-scale-matrix-ops.h"
20 #include "font-instance.h"
21 #include "svg/svg-length.h"
22 #include "extension/internal/cairo-render-context.h"
24 namespace Inkscape {
25     namespace Extension {
26         namespace Internal {
27             class CairoRenderContext;
28             class CairoGlyphInfo;
29         }
30     }
31 }
33 using Inkscape::Extension::Internal::CairoRenderContext;
34 using Inkscape::Extension::Internal::CairoGlyphInfo;
36 namespace Inkscape {
37 namespace Text {
39 void Layout::_clearOutputObjects()
40 {
41     _paragraphs.clear();
42     _lines.clear();
43     _chunks.clear();
44     for (std::vector<Span>::iterator it_span = _spans.begin() ; it_span != _spans.end() ; it_span++)
45         if (it_span->font) it_span->font->Unref();
46     _spans.clear();
47     _characters.clear();
48     _glyphs.clear();
49     _path_fitted = NULL;
50 }
52 void Layout::LineHeight::max(LineHeight const &other)
53 {
54     if (other.ascent > ascent)  ascent  = other.ascent;
55     if (other.descent > descent) descent = other.descent;
56     if (other.leading > leading) leading = other.leading;
57 }
59 void Layout::_getGlyphTransformMatrix(int glyph_index, NR::Matrix *matrix) const
60 {
61     Span const &span = _glyphs[glyph_index].span(this);
62     double sin_rotation = sin(_glyphs[glyph_index].rotation);
63     double cos_rotation = cos(_glyphs[glyph_index].rotation);
64     (*matrix)[0] = span.font_size * cos_rotation;
65     (*matrix)[1] = span.font_size * sin_rotation;
66     (*matrix)[2] = span.font_size * sin_rotation;
67     (*matrix)[3] = -span.font_size * cos_rotation;
68     if (span.block_progression == LEFT_TO_RIGHT || span.block_progression == RIGHT_TO_LEFT) {
69         (*matrix)[4] = _lines[_chunks[span.in_chunk].in_line].baseline_y + _glyphs[glyph_index].y;
70         (*matrix)[5] = _chunks[span.in_chunk].left_x + _glyphs[glyph_index].x;
71     } else {
72         (*matrix)[4] = _chunks[span.in_chunk].left_x + _glyphs[glyph_index].x;
73         (*matrix)[5] = _lines[_chunks[span.in_chunk].in_line].baseline_y + _glyphs[glyph_index].y;
74     }
75 }
77 void Layout::show(NRArenaGroup *in_arena, NRRect const *paintbox) const
78 {
79     int glyph_index = 0;
80     for (unsigned span_index = 0 ; span_index < _spans.size() ; span_index++) {
81         if (_input_stream[_spans[span_index].in_input_stream_item]->Type() != TEXT_SOURCE) continue;
82         InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_input_stream[_spans[span_index].in_input_stream_item]);
83         NRArenaGlyphsGroup *nr_group = NRArenaGlyphsGroup::create(in_arena->arena);
84         nr_arena_item_add_child(in_arena, nr_group, NULL);
85         nr_arena_item_unref(nr_group);
87         nr_arena_glyphs_group_set_style(nr_group, text_source->style);
88         while (glyph_index < (int)_glyphs.size() && _characters[_glyphs[glyph_index].in_character].in_span == span_index) {
89             if (_characters[_glyphs[glyph_index].in_character].in_glyph != -1) {
90                 NR::Matrix glyph_matrix;
91                 _getGlyphTransformMatrix(glyph_index, &glyph_matrix);
92                 nr_arena_glyphs_group_add_component(nr_group, _spans[span_index].font, _glyphs[glyph_index].glyph, &glyph_matrix);
93             }
94             glyph_index++;
95         }
96         nr_arena_glyphs_group_set_paintbox(NR_ARENA_GLYPHS_GROUP(nr_group), paintbox);
97     }
98     nr_arena_item_request_update(NR_ARENA_ITEM(in_arena), NR_ARENA_ITEM_STATE_ALL, FALSE);
99 }
101 void Layout::getBoundingBox(NRRect *bounding_box, NR::Matrix const &transform, int start, int length) const
103     for (unsigned glyph_index = 0 ; glyph_index < _glyphs.size() ; glyph_index++) {
104         if (_characters[_glyphs[glyph_index].in_character].in_glyph == -1) continue;
105         if (start != -1 && (int) _glyphs[glyph_index].in_character < start) continue;
106         if (length != -1) {
107             if (start == -1)
108                 start = 0;
109             if ((int) _glyphs[glyph_index].in_character > start + length) continue;
110         }
111         // this could be faster
112         NR::Matrix glyph_matrix;
113         _getGlyphTransformMatrix(glyph_index, &glyph_matrix);
114         NR::Matrix total_transform = glyph_matrix;
115         total_transform *= transform;
116         NR::Maybe<NR::Rect> glyph_rect = _glyphs[glyph_index].span(this).font->BBox(_glyphs[glyph_index].glyph);
117         if (glyph_rect) {
118             NR::Point bmi = glyph_rect->min(), bma = glyph_rect->max();
119             NR::Point tlp(bmi[0],bmi[1]), trp(bma[0],bmi[1]), blp(bmi[0],bma[1]), brp(bma[0],bma[1]);
120             tlp *= total_transform;
121             trp *= total_transform;
122             blp *= total_transform;
123             brp *= total_transform;
124             *glyph_rect = NR::Rect(tlp,trp);
125             glyph_rect->expandTo(blp);
126             glyph_rect->expandTo(brp);
127             if ( (glyph_rect->min())[0] < bounding_box->x0 ) bounding_box->x0=(glyph_rect->min())[0];
128             if ( (glyph_rect->max())[0] > bounding_box->x1 ) bounding_box->x1=(glyph_rect->max())[0];
129             if ( (glyph_rect->min())[1] < bounding_box->y0 ) bounding_box->y0=(glyph_rect->min())[1];
130             if ( (glyph_rect->max())[1] > bounding_box->y1 ) bounding_box->y1=(glyph_rect->max())[1];
131         }
132     }
135 void Layout::print(SPPrintContext *ctx,
136                    NRRect const *pbox, NRRect const *dbox, NRRect const *bbox,
137                    NR::Matrix const &ctm) const
139     if (_input_stream.empty()) return;
141     Direction block_progression = _blockProgression();
142     bool text_to_path = ctx->module->textToPath();
143     for (unsigned glyph_index = 0 ; glyph_index < _glyphs.size() ; ) {
144         if (_characters[_glyphs[glyph_index].in_character].in_glyph == -1) {
145             // invisible glyphs
146             unsigned same_character = _glyphs[glyph_index].in_character;
147             while (_glyphs[glyph_index].in_character == same_character)
148                 glyph_index++;
149             continue;
150         }
151         NR::Matrix glyph_matrix;
152         Span const &span = _spans[_characters[_glyphs[glyph_index].in_character].in_span];
153         InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_input_stream[span.in_input_stream_item]);
154         if (text_to_path || _path_fitted) {
155             NRBPath bpath;
156             bpath.path = (NArtBpath*)span.font->ArtBPath(_glyphs[glyph_index].glyph);
157             if (bpath.path) {
158                 NRBPath abp;
159                 _getGlyphTransformMatrix(glyph_index, &glyph_matrix);
160                 abp.path = nr_artpath_affine(bpath.path, glyph_matrix);
161                 if (!text_source->style->fill.isNone())
162                     sp_print_fill(ctx, &abp, &ctm, text_source->style, pbox, dbox, bbox);
163                 if (!text_source->style->stroke.isNone())
164                     sp_print_stroke(ctx, &abp, &ctm, text_source->style, pbox, dbox, bbox);
165                 g_free(abp.path);
166             }
167             glyph_index++;
168         } else {
169             NR::Point g_pos(0,0);    // all strings are output at (0,0) because we do the translation using the matrix
170             glyph_matrix = NR::Matrix(NR::scale(1.0, -1.0) * NR::Matrix(NR::rotate(_glyphs[glyph_index].rotation)));
171             if (block_progression == LEFT_TO_RIGHT || block_progression == RIGHT_TO_LEFT) {
172                 glyph_matrix[4] = span.line(this).baseline_y + span.baseline_shift;
173                 // since we're outputting character codes, not glyphs, we want the character x
174                 glyph_matrix[5] = span.chunk(this).left_x + span.x_start + _characters[_glyphs[glyph_index].in_character].x;
175             } else {
176                 glyph_matrix[4] = span.chunk(this).left_x + span.x_start + _characters[_glyphs[glyph_index].in_character].x;
177                 glyph_matrix[5] = span.line(this).baseline_y + span.baseline_shift;
178             }
179             Glib::ustring::const_iterator span_iter = span.input_stream_first_character;
180             unsigned char_index = _glyphs[glyph_index].in_character;
181             unsigned original_span = _characters[char_index].in_span;
182             while (char_index && _characters[char_index - 1].in_span == original_span) {
183                 char_index--;
184                 span_iter++;
185             }
187             // try to output as many characters as possible in one go by detecting kerning and stopping when we encounter it
188             Glib::ustring span_string;
189             double char_x = _characters[_glyphs[glyph_index].in_character].x;
190             unsigned this_span_index = _characters[_glyphs[glyph_index].in_character].in_span;
191             do {
192                 span_string += *span_iter;
193                 span_iter++;
195                 unsigned same_character = _glyphs[glyph_index].in_character;
196                 while (glyph_index < _glyphs.size() && _glyphs[glyph_index].in_character == same_character) {
197                     char_x += _glyphs[glyph_index].width;
198                     glyph_index++;
199                 }
200             } while (glyph_index < _glyphs.size()
201                      && _path_fitted == NULL
202                      && _characters[_glyphs[glyph_index].in_character].in_span == this_span_index
203                      && fabs(char_x - _characters[_glyphs[glyph_index].in_character].x) < 1e-5);
204             sp_print_bind(ctx, glyph_matrix, 1.0);
205             sp_print_text(ctx, span_string.c_str(), g_pos, text_source->style);
206             sp_print_release(ctx);
207         }
208     }
211 #ifdef HAVE_CAIRO_PDF
212 void Layout::showGlyphs(CairoRenderContext *ctx) const
214     if (_input_stream.empty()) return;
215     
216     bool clip_mode = false;//(ctx->getRenderMode() == CairoRenderContext::RENDER_MODE_CLIP);
217     std::vector<CairoGlyphInfo> glyphtext;
219     for (unsigned glyph_index = 0 ; glyph_index < _glyphs.size() ; ) {
220         if (_characters[_glyphs[glyph_index].in_character].in_glyph == -1) {
221             // invisible glyphs
222             unsigned same_character = _glyphs[glyph_index].in_character;
223             while (_glyphs[glyph_index].in_character == same_character)
224                 glyph_index++;
225             continue;
226         }
227         Span const &span = _spans[_characters[_glyphs[glyph_index].in_character].in_span];
228         InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_input_stream[span.in_input_stream_item]);
230         NR::Matrix glyph_matrix;
231         _getGlyphTransformMatrix(glyph_index, &glyph_matrix);
232         if (clip_mode) {
233             NArtBpath *bpath = (NArtBpath*)span.font->ArtBPath(_glyphs[glyph_index].glyph);
234             if (bpath) {
235                 NArtBpath *abp = nr_artpath_affine(bpath, glyph_matrix);
236                 NRBPath bpath;
237                 bpath.path = abp;
238                 SPStyle const *style = text_source->style;
239                 ctx->renderPath(&bpath, style, NULL);
240                 g_free(abp);
241             }
242             glyph_index++;
243             continue;
244         }
246         NR::Matrix font_matrix;
247         if (_path_fitted == NULL) {
248             font_matrix = glyph_matrix;
249             font_matrix[4] = 0;
250             font_matrix[5] = 0;
251         } else {
252             font_matrix.set_identity();
253         }
255         Glib::ustring::const_iterator span_iter = span.input_stream_first_character;
256         unsigned char_index = _glyphs[glyph_index].in_character;
257         unsigned original_span = _characters[char_index].in_span;
258         while (char_index && _characters[char_index - 1].in_span == original_span) {
259             char_index--;
260             span_iter++;
261         }
263         // try to output as many characters as possible in one go
264         Glib::ustring span_string;
265         unsigned this_span_index = _characters[_glyphs[glyph_index].in_character].in_span;
266         unsigned int first_index = glyph_index;
267         glyphtext.clear();
268         do {
269             span_string += *span_iter;
270             span_iter++;
272             unsigned same_character = _glyphs[glyph_index].in_character;
273             while (glyph_index < _glyphs.size() && _glyphs[glyph_index].in_character == same_character) {
274                 if (glyph_index != first_index)
275                     _getGlyphTransformMatrix(glyph_index, &glyph_matrix);
277                 CairoGlyphInfo info;
278                 info.index = _glyphs[glyph_index].glyph;
279                 if (_path_fitted == NULL) {
280                     info.x = glyph_matrix[4];
281                     info.y = glyph_matrix[5];
282                 } else {
283                     info.x = 0;
284                     info.y = 0;                    
285                 }
286                 glyphtext.push_back(info);
288                 glyph_index++;
289             }
290         } while (glyph_index < _glyphs.size()
291                  && _path_fitted == NULL
292                  && NR::transform_equalp(font_matrix, glyph_matrix, NR_EPSILON)
293                  && _characters[_glyphs[glyph_index].in_character].in_span == this_span_index);
294          
295         // remove vertical flip
296         font_matrix[3] *= -1.0;
298         SPStyle const *style = text_source->style;
299         float opacity = SP_SCALE24_TO_FLOAT(style->opacity.value);
300         
301         if (_path_fitted) {
302             ctx->pushState();
303             ctx->transform(&glyph_matrix);
304         } else if (opacity != 1.0) {
305             ctx->pushState();
306             ctx->setStateForStyle(style);
307             ctx->pushLayer();
308         }
309         if (glyph_index - first_index > 0)
310             ctx->renderGlyphtext(span.font->pFont, &font_matrix, glyphtext, style);
311         if (_path_fitted)
312             ctx->popState();
313         else if (opacity != 1.0) {
314             ctx->popLayer();
315             ctx->popState();
316         }
317     }
319 #endif
321 // these functions are for dumpAsText() only. No need to translate
322 static char const *direction_to_text(Layout::Direction d)
324     switch (d) {
325         case Layout::LEFT_TO_RIGHT: return "ltr";
326         case Layout::RIGHT_TO_LEFT: return "rtl";
327         case Layout::TOP_TO_BOTTOM: return "ttb";
328         case Layout::BOTTOM_TO_TOP: return "btt";
329     }
330     return "???";
333 static char const *style_to_text(PangoStyle s)
335     switch (s) {
336         case PANGO_STYLE_NORMAL: return "upright";
337         case PANGO_STYLE_ITALIC: return "italic";
338         case PANGO_STYLE_OBLIQUE: return "oblique";
339     }
340     return "???";
343 static char const *weight_to_text(PangoWeight w)
345     switch (w) {
346         case PANGO_WEIGHT_ULTRALIGHT: return "ultralight";
347         case PANGO_WEIGHT_LIGHT     : return "light";
348         case PANGO_WEIGHT_SEMIBOLD  : return "semibold";
349         case PANGO_WEIGHT_NORMAL    : return "normalweight";
350         case PANGO_WEIGHT_BOLD      : return "bold";
351         case PANGO_WEIGHT_ULTRABOLD : return "ultrabold";
352         case PANGO_WEIGHT_HEAVY     : return "heavy";
353     }
354     return "???";
357 Glib::ustring Layout::dumpAsText() const
359     Glib::ustring result;
361     for (unsigned span_index = 0 ; span_index < _spans.size() ; span_index++) {
362         char line[256];
363         snprintf(line, sizeof(line), "==== span %d\n", span_index);
364         result += line;
365         snprintf(line, sizeof(line), "  in para %d (direction=%s)\n", _lines[_chunks[_spans[span_index].in_chunk].in_line].in_paragraph,
366                  direction_to_text(_paragraphs[_lines[_chunks[_spans[span_index].in_chunk].in_line].in_paragraph].base_direction));
367         result += line;
368         snprintf(line, sizeof(line), "  in source %d (type=%d, cookie=%p)\n", _spans[span_index].in_input_stream_item,
369                  _input_stream[_spans[span_index].in_input_stream_item]->Type(),
370                  _input_stream[_spans[span_index].in_input_stream_item]->source_cookie);
371         result += line;
372         snprintf(line, sizeof(line), "  in line %d (baseline=%f, shape=%d)\n", _chunks[_spans[span_index].in_chunk].in_line,
373                  _lines[_chunks[_spans[span_index].in_chunk].in_line].baseline_y,
374                  _lines[_chunks[_spans[span_index].in_chunk].in_line].in_shape);
375         result += line;
376         snprintf(line, sizeof(line), "  in chunk %d (x=%f, baselineshift=%f)\n", _spans[span_index].in_chunk, _chunks[_spans[span_index].in_chunk].left_x, _spans[span_index].baseline_shift);
377         result += line;
378         if (_spans[span_index].font) {
379             snprintf(line, sizeof(line), "    font '%s' %f %s %s\n", pango_font_description_get_family(_spans[span_index].font->descr), _spans[span_index].font_size, style_to_text(pango_font_description_get_style(_spans[span_index].font->descr)), weight_to_text(pango_font_description_get_weight(_spans[span_index].font->descr)));
380             result += line;
381         }
382         snprintf(line, sizeof(line), "    x_start = %f, x_end = %f\n", _spans[span_index].x_start, _spans[span_index].x_end);
383         result += line;
384         snprintf(line, sizeof(line), "    line height: ascent %f, descent %f leading %f\n", _spans[span_index].line_height.ascent, _spans[span_index].line_height.descent, _spans[span_index].line_height.leading);
385         result += line;
386         snprintf(line, sizeof(line), "    direction %s, block-progression %s\n", direction_to_text(_spans[span_index].direction), direction_to_text(_spans[span_index].block_progression));
387         result += line;
388         result += "    ** characters:\n";
389         Glib::ustring::const_iterator iter_char = _spans[span_index].input_stream_first_character;
390         // very inefficent code. what the hell, it's only debug stuff.
391         for (unsigned char_index = 0 ; char_index < _characters.size() ; char_index++) {
392             if (_characters[char_index].in_span != span_index) continue;
393             if (_input_stream[_spans[span_index].in_input_stream_item]->Type() != TEXT_SOURCE) {
394                 snprintf(line, sizeof(line), "      %d: control x=%f flags=%03x glyph=%d\n", char_index, _characters[char_index].x, *(unsigned*)&_characters[char_index].char_attributes, _characters[char_index].in_glyph);
395             } else {
396                 snprintf(line, sizeof(line), "      %d: '%c' x=%f flags=%03x glyph=%d\n", char_index, *iter_char, _characters[char_index].x, *(unsigned*)&_characters[char_index].char_attributes, _characters[char_index].in_glyph);
397                 iter_char++;
398             }
399             result += line;
400         }
401         result += "    ** glyphs:\n";
402         for (unsigned glyph_index = 0 ; glyph_index < _glyphs.size() ; glyph_index++) {
403             if (_characters[_glyphs[glyph_index].in_character].in_span != span_index) continue;
404             snprintf(line, sizeof(line), "      %d: %d (%f,%f) rot=%f cx=%f char=%d\n", glyph_index, _glyphs[glyph_index].glyph, _glyphs[glyph_index].x, _glyphs[glyph_index].y, _glyphs[glyph_index].rotation, _glyphs[glyph_index].width, _glyphs[glyph_index].in_character);
405             result += line;
406         }
407         result += "\n";
408     }
409     result += "EOT\n";
410     return result;
413 void Layout::fitToPathAlign(SVGLength const &startOffset, Path const &path)
415     double offset = 0.0;
417     if (startOffset._set) {
418         if (startOffset.unit == SVGLength::PERCENT)
419             offset = startOffset.computed * const_cast<Path&>(path).Length();
420         else
421             offset = startOffset.computed;
422     }
424     switch (_paragraphs.front().alignment) {
425         case CENTER:
426             offset -= _getChunkWidth(0) * 0.5;
427             break;
428         case RIGHT:
429             offset -= _getChunkWidth(0);
430             break;
431         default:
432             break;
433     }
435     if (_characters.empty()) {
436         int unused = 0;
437         Path::cut_position *point_otp = const_cast<Path&>(path).CurvilignToPosition(1, &offset, unused);
438         if (offset >= 0.0 && point_otp != NULL && point_otp[0].piece >= 0) {
439             NR::Point point;
440             NR::Point tangent;
441             const_cast<Path&>(path).PointAndTangentAt(point_otp[0].piece, point_otp[0].t, point, tangent);
442             _empty_cursor_shape.position = point;
443             _empty_cursor_shape.rotation = atan2(tangent[NR::Y], tangent[NR::X]);
444         }
445     }
447     for (unsigned char_index = 0 ; char_index < _characters.size() ; ) {
448         int next_cluster_glyph_index;
449         unsigned next_cluster_char_index;
450         double character_advance;
451         Span const &span = _characters[char_index].span(this);
453         for (next_cluster_char_index = char_index + 1 ;
454              next_cluster_char_index < _characters.size() && !_characters[next_cluster_char_index].char_attributes.is_cursor_position;
455              next_cluster_char_index++);
457         if (next_cluster_char_index == _characters.size()) {
458             next_cluster_glyph_index = _glyphs.size();
459             character_advance = 0.0;   // arbitrary because we're not going to advance
460         } else {
461             next_cluster_glyph_index = _characters[next_cluster_char_index].in_glyph;
462             character_advance =   (_glyphs[next_cluster_glyph_index].x + _glyphs[next_cluster_glyph_index].chunk(this).left_x)
463                 - (_glyphs[_characters[char_index].in_glyph].x + span.chunk(this).left_x);
464         }
466         double start_offset = offset + span.x_start + _characters[char_index].x;
467         double cluster_width = 0.0;
468         for (int glyph_index = _characters[char_index].in_glyph ; glyph_index < next_cluster_glyph_index ; glyph_index++)
469             cluster_width += _glyphs[glyph_index].width;
470         if (span.direction == RIGHT_TO_LEFT)
471             start_offset -= cluster_width;
472         double end_offset = start_offset + cluster_width;
474         int unused = 0;
475         double midpoint_offset = (start_offset + end_offset) * 0.5;
476         // as far as I know these functions are const, they're just not marked as such
477         Path::cut_position *midpoint_otp = const_cast<Path&>(path).CurvilignToPosition(1, &midpoint_offset, unused);
478         if (midpoint_offset >= 0.0 && midpoint_otp != NULL && midpoint_otp[0].piece >= 0) {
479             NR::Point midpoint;
480             NR::Point tangent;
482             const_cast<Path&>(path).PointAndTangentAt(midpoint_otp[0].piece, midpoint_otp[0].t, midpoint, tangent);
484             if (start_offset >= 0.0 && end_offset >= 0.0) {
485                 Path::cut_position *start_otp = const_cast<Path&>(path).CurvilignToPosition(1, &start_offset, unused);
486                 if (start_otp != NULL && start_otp[0].piece >= 0) {
487                     Path::cut_position *end_otp = const_cast<Path&>(path).CurvilignToPosition(1, &end_offset, unused);
488                     if (end_otp != NULL && end_otp[0].piece >= 0) {
489                         bool on_same_subpath = true;
490                         for (size_t i = 0 ; i < path.pts.size() ; i++) {
491                             if (path.pts[i].piece <= start_otp[0].piece) continue;
492                             if (path.pts[i].piece >= end_otp[0].piece) break;
493                             if (path.pts[i].isMoveTo == polyline_moveto) {
494                                 on_same_subpath = false;
495                                 break;
496                             }
497                         }
498                         if (on_same_subpath) {
499                             // both points were on the same subpath (without this test the angle is very weird)
500                             NR::Point startpoint, endpoint;
501                             const_cast<Path&>(path).PointAt(start_otp[0].piece, start_otp[0].t, startpoint);
502                             const_cast<Path&>(path).PointAt(end_otp[0].piece, end_otp[0].t, endpoint);
503                             if (endpoint != startpoint) {
504                                 tangent = endpoint - startpoint;
505                                 tangent.normalize();
506                             } else {
507                                 tangent = NR::Point (0,0);
508                             }
509                         }
510                         g_free(end_otp);
511                     }
512                     g_free(start_otp);
513                 }
514             }
516             double rotation = atan2(tangent[1], tangent[0]);
517             for (int glyph_index = _characters[char_index].in_glyph ; glyph_index < next_cluster_glyph_index ; glyph_index++) {
518                 double tangent_shift = -cluster_width * 0.5 + _glyphs[glyph_index].x - (_characters[char_index].x + span.x_start);
519                 double normal_shift = _glyphs[glyph_index].y;
520                 if (span.direction == RIGHT_TO_LEFT)
521                     tangent_shift += cluster_width;
522                 _glyphs[glyph_index].x = midpoint[0] - span.chunk(this).left_x + tangent[0] * tangent_shift - tangent[1] * normal_shift;
523                 _glyphs[glyph_index].y = midpoint[1] - _lines.front().baseline_y + tangent[1] * tangent_shift + tangent[0] * normal_shift;
524                 _glyphs[glyph_index].rotation += rotation;
525             }
526         } else {  // outside the bounds of the path: hide the glyphs
527             _characters[char_index].in_glyph = -1;
528         }
529         g_free(midpoint_otp);
531         char_index = next_cluster_char_index;
532     }
534     for (unsigned span_index = 0 ; span_index < _spans.size() ; span_index++) {
535         _spans[span_index].x_start += offset;
536         _spans[span_index].x_end += offset;
537     }
539     _path_fitted = &path;
542 SPCurve *Layout::convertToCurves(iterator const &from_glyph, iterator const &to_glyph) const
544     GSList *cc = NULL;
546     for (int glyph_index = from_glyph._glyph_index ; glyph_index < to_glyph._glyph_index ; glyph_index++) {
547         NR::Matrix glyph_matrix;
548         Span const &span = _glyphs[glyph_index].span(this);
549         _getGlyphTransformMatrix(glyph_index, &glyph_matrix);
551         NRBPath bpath;
552         bpath.path = (NArtBpath*)span.font->ArtBPath(_glyphs[glyph_index].glyph);
553         if (bpath.path) {
554             NArtBpath *abp = nr_artpath_affine(bpath.path, glyph_matrix);
555             SPCurve *c = SPCurve::new_from_bpath(abp);
556             if (c) cc = g_slist_prepend(cc, c);
557         }
558     }
559     cc = g_slist_reverse(cc);
561     SPCurve *curve;
562     if ( cc ) {
563         curve = SPCurve::concat(cc);
564     } else {
565         curve = new SPCurve();
566     }
568     while (cc) {
569         /* fixme: This is dangerous, as we are mixing art_alloc and g_new */
570         reinterpret_cast<SPCurve *>(cc->data)->unref();
571         cc = g_slist_remove(cc, cc->data);
572     }
574     return curve;
577 void Layout::transform(NR::Matrix const &transform)
579     // this is all massively oversimplified
580     // I can't actually think of anybody who'll want to use it at the moment, so it'll stay simple
581     for (unsigned glyph_index = 0 ; glyph_index < _glyphs.size() ; glyph_index++) {
582         NR::Point point(_glyphs[glyph_index].x, _glyphs[glyph_index].y);
583         point *= transform;
584         _glyphs[glyph_index].x = point[0];
585         _glyphs[glyph_index].y = point[1];
586     }
589 }//namespace Text
590 }//namespace Inkscape
593 /*
594   Local Variables:
595   mode:c++
596   c-file-style:"stroustrup"
597   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
598   indent-tabs-mode:nil
599   fill-column:99
600   End:
601 */
602 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :