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