5152909f814ab3a8cb0c8c63c6a1afabd339e35d
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 <gtk/gtkversion.h>
13 #include "Layout-TNG.h"
14 #include "display/nr-arena-glyphs.h"
15 #include "style.h"
16 #include "print.h"
17 #include "extension/print.h"
18 #include "livarot/Path.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, NRMatrix *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 NRMatrix 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
102 {
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 NRMatrix glyph_matrix;
113 _getGlyphTransformMatrix(glyph_index, &glyph_matrix);
114 NR::Matrix total_transform = glyph_matrix;
115 total_transform *= transform;
116 NR::Rect glyph_rect = _glyphs[glyph_index].span(this).font->BBox(_glyphs[glyph_index].glyph);
117 NR::Point bmi = glyph_rect.min(), bma = glyph_rect.max();
118 NR::Point tlp(bmi[0],bmi[1]), trp(bma[0],bmi[1]), blp(bmi[0],bma[1]), brp(bma[0],bma[1]);
119 tlp *= total_transform;
120 trp *= total_transform;
121 blp *= total_transform;
122 brp *= total_transform;
123 glyph_rect = NR::Rect(tlp,trp);
124 glyph_rect.expandTo(blp);
125 glyph_rect.expandTo(brp);
126 if ( (glyph_rect.min())[0] < bounding_box->x0 ) bounding_box->x0=(glyph_rect.min())[0];
127 if ( (glyph_rect.max())[0] > bounding_box->x1 ) bounding_box->x1=(glyph_rect.max())[0];
128 if ( (glyph_rect.min())[1] < bounding_box->y0 ) bounding_box->y0=(glyph_rect.min())[1];
129 if ( (glyph_rect.max())[1] > bounding_box->y1 ) bounding_box->y1=(glyph_rect.max())[1];
130 }
131 }
133 void Layout::print(SPPrintContext *ctx,
134 NRRect const *pbox, NRRect const *dbox, NRRect const *bbox,
135 NRMatrix const &ctm) const
136 {
137 if (_input_stream.empty()) return;
139 Direction block_progression = _blockProgression();
140 bool text_to_path = ctx->module->textToPath();
141 for (unsigned glyph_index = 0 ; glyph_index < _glyphs.size() ; ) {
142 if (_characters[_glyphs[glyph_index].in_character].in_glyph == -1) {
143 // invisible glyphs
144 unsigned same_character = _glyphs[glyph_index].in_character;
145 while (_glyphs[glyph_index].in_character == same_character)
146 glyph_index++;
147 continue;
148 }
149 NRMatrix glyph_matrix;
150 Span const &span = _spans[_characters[_glyphs[glyph_index].in_character].in_span];
151 InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_input_stream[span.in_input_stream_item]);
152 if (text_to_path || _path_fitted) {
153 NRBPath bpath;
154 bpath.path = (NArtBpath*)span.font->ArtBPath(_glyphs[glyph_index].glyph);
155 if (bpath.path) {
156 NRBPath abp;
157 _getGlyphTransformMatrix(glyph_index, &glyph_matrix);
158 abp.path = nr_artpath_affine(bpath.path, glyph_matrix);
159 if (text_source->style->fill.type != SP_PAINT_TYPE_NONE)
160 sp_print_fill(ctx, &abp, &ctm, text_source->style, pbox, dbox, bbox);
161 if (text_source->style->stroke.type != SP_PAINT_TYPE_NONE)
162 sp_print_stroke(ctx, &abp, &ctm, text_source->style, pbox, dbox, bbox);
163 g_free(abp.path);
164 }
165 glyph_index++;
166 } else {
167 NR::Point g_pos(0,0); // all strings are output at (0,0) because we do the translation using the matrix
168 glyph_matrix = NR::Matrix(NR::scale(1.0, -1.0) * NR::Matrix(NR::rotate(_glyphs[glyph_index].rotation)));
169 if (block_progression == LEFT_TO_RIGHT || block_progression == RIGHT_TO_LEFT) {
170 glyph_matrix.c[4] = span.line(this).baseline_y + span.baseline_shift;
171 // since we're outputting character codes, not glyphs, we want the character x
172 glyph_matrix.c[5] = span.chunk(this).left_x + span.x_start + _characters[_glyphs[glyph_index].in_character].x;
173 } else {
174 glyph_matrix.c[4] = span.chunk(this).left_x + span.x_start + _characters[_glyphs[glyph_index].in_character].x;
175 glyph_matrix.c[5] = span.line(this).baseline_y + span.baseline_shift;
176 }
177 Glib::ustring::const_iterator span_iter = span.input_stream_first_character;
178 unsigned char_index = _glyphs[glyph_index].in_character;
179 unsigned original_span = _characters[char_index].in_span;
180 while (char_index && _characters[char_index - 1].in_span == original_span) {
181 char_index--;
182 span_iter++;
183 }
185 // try to output as many characters as possible in one go by detecting kerning and stopping when we encounter it
186 Glib::ustring span_string;
187 double char_x = _characters[_glyphs[glyph_index].in_character].x;
188 unsigned this_span_index = _characters[_glyphs[glyph_index].in_character].in_span;
189 do {
190 span_string += *span_iter;
191 span_iter++;
193 unsigned same_character = _glyphs[glyph_index].in_character;
194 while (glyph_index < _glyphs.size() && _glyphs[glyph_index].in_character == same_character) {
195 char_x += _glyphs[glyph_index].width;
196 glyph_index++;
197 }
198 } while (glyph_index < _glyphs.size()
199 && _path_fitted == NULL
200 && _characters[_glyphs[glyph_index].in_character].in_span == this_span_index
201 && fabs(char_x - _characters[_glyphs[glyph_index].in_character].x) < 1e-5);
202 sp_print_bind(ctx, glyph_matrix, 1.0);
203 sp_print_text(ctx, span_string.c_str(), g_pos, text_source->style);
204 sp_print_release(ctx);
205 }
206 }
207 }
209 #ifdef HAVE_CAIRO_PDF
210 void Layout::showGlyphs(CairoRenderContext *ctx) const
211 {
212 if (_input_stream.empty()) return;
214 bool clip_mode = false;//(ctx->getRenderMode() == CairoRenderContext::RENDER_MODE_CLIP);
215 std::vector<CairoGlyphInfo> glyphtext;
217 for (unsigned glyph_index = 0 ; glyph_index < _glyphs.size() ; ) {
218 if (_characters[_glyphs[glyph_index].in_character].in_glyph == -1) {
219 // invisible glyphs
220 unsigned same_character = _glyphs[glyph_index].in_character;
221 while (_glyphs[glyph_index].in_character == same_character)
222 glyph_index++;
223 continue;
224 }
225 Span const &span = _spans[_characters[_glyphs[glyph_index].in_character].in_span];
226 InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_input_stream[span.in_input_stream_item]);
228 NRMatrix glyph_matrix;
229 _getGlyphTransformMatrix(glyph_index, &glyph_matrix);
230 if (clip_mode) {
231 NArtBpath *bpath = (NArtBpath*)span.font->ArtBPath(_glyphs[glyph_index].glyph);
232 if (bpath) {
233 NArtBpath *abp = nr_artpath_affine(bpath, glyph_matrix);
234 NRBPath bpath;
235 bpath.path = abp;
236 SPStyle const *style = text_source->style;
237 ctx->renderPath(&bpath, style, NULL);
238 g_free(abp);
239 }
240 glyph_index++;
241 continue;
242 }
244 NRMatrix font_matrix;
245 if (_path_fitted == NULL) {
246 font_matrix = glyph_matrix;
247 font_matrix[4] = 0;
248 font_matrix[5] = 0;
249 } else {
250 nr_matrix_set_identity(&font_matrix);
251 }
253 Glib::ustring::const_iterator span_iter = span.input_stream_first_character;
254 unsigned char_index = _glyphs[glyph_index].in_character;
255 unsigned original_span = _characters[char_index].in_span;
256 while (char_index && _characters[char_index - 1].in_span == original_span) {
257 char_index--;
258 span_iter++;
259 }
261 // try to output as many characters as possible in one go
262 Glib::ustring span_string;
263 unsigned this_span_index = _characters[_glyphs[glyph_index].in_character].in_span;
264 unsigned int first_index = glyph_index;
265 glyphtext.clear();
266 do {
267 span_string += *span_iter;
268 span_iter++;
270 unsigned same_character = _glyphs[glyph_index].in_character;
271 while (glyph_index < _glyphs.size() && _glyphs[glyph_index].in_character == same_character) {
272 if (glyph_index != first_index)
273 _getGlyphTransformMatrix(glyph_index, &glyph_matrix);
275 CairoGlyphInfo info;
276 info.index = _glyphs[glyph_index].glyph;
277 if (_path_fitted == NULL) {
278 info.x = glyph_matrix.c[4];
279 info.y = glyph_matrix.c[5];
280 } else {
281 info.x = 0;
282 info.y = 0;
283 }
284 glyphtext.push_back(info);
286 glyph_index++;
287 }
288 } while (glyph_index < _glyphs.size()
289 && _path_fitted == NULL
290 && nr_matrix_test_transform_equal(&font_matrix, &glyph_matrix, NR_EPSILON)
291 && _characters[_glyphs[glyph_index].in_character].in_span == this_span_index);
293 // remove vertical flip
294 font_matrix[3] *= -1.0;
296 SPStyle const *style = text_source->style;
297 float opacity = SP_SCALE24_TO_FLOAT(style->opacity.value);
299 if (_path_fitted) {
300 ctx->pushState();
301 ctx->transform(&glyph_matrix);
302 } else if (opacity != 1.0) {
303 ctx->pushState();
304 ctx->setStateForStyle(style);
305 ctx->pushLayer();
306 }
307 if (glyph_index - first_index > 0)
308 ctx->renderGlyphtext(span.font->pFont, &font_matrix, glyphtext, style);
309 if (_path_fitted)
310 ctx->popState();
311 else if (opacity != 1.0) {
312 ctx->popLayer();
313 ctx->popState();
314 }
315 }
316 }
317 #endif
319 // these functions are for dumpAsText() only. No need to translate
320 static char const *direction_to_text(Layout::Direction d)
321 {
322 switch (d) {
323 case Layout::LEFT_TO_RIGHT: return "ltr";
324 case Layout::RIGHT_TO_LEFT: return "rtl";
325 case Layout::TOP_TO_BOTTOM: return "ttb";
326 case Layout::BOTTOM_TO_TOP: return "btt";
327 }
328 return "???";
329 }
331 static char const *style_to_text(PangoStyle s)
332 {
333 switch (s) {
334 case PANGO_STYLE_NORMAL: return "upright";
335 case PANGO_STYLE_ITALIC: return "italic";
336 case PANGO_STYLE_OBLIQUE: return "oblique";
337 }
338 return "???";
339 }
341 static char const *weight_to_text(PangoWeight w)
342 {
343 switch (w) {
344 case PANGO_WEIGHT_ULTRALIGHT: return "ultralight";
345 case PANGO_WEIGHT_LIGHT : return "light";
346 #if GTK_CHECK_VERSION(2,6,0)
347 case PANGO_WEIGHT_SEMIBOLD : return "semibold";
348 #endif
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 "???";
355 }
357 Glib::ustring Layout::dumpAsText() const
358 {
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;
411 }
413 void Layout::fitToPathAlign(SVGLength const &startOffset, Path const &path)
414 {
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;
540 }
542 SPCurve *Layout::convertToCurves(iterator const &from_glyph, iterator const &to_glyph) const
543 {
544 GSList *cc = NULL;
546 for (int glyph_index = from_glyph._glyph_index ; glyph_index < to_glyph._glyph_index ; glyph_index++) {
547 NRMatrix 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 = sp_curve_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 = sp_curve_concat(cc);
564 } else {
565 curve = sp_curve_new();
566 }
568 while (cc) {
569 /* fixme: This is dangerous, as we are mixing art_alloc and g_new */
570 sp_curve_unref((SPCurve *) cc->data);
571 cc = g_slist_remove(cc, cc->data);
572 }
574 return curve;
575 }
577 void Layout::transform(NR::Matrix const &transform)
578 {
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 }
587 }
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 :