Code

replace #ifdef PANGO_WEIGHT_SEMIBOLD with #if GTK_CHECK_VERSION(2,6,0), fixes compile...
[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 <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"
23 namespace Inkscape {
24 namespace Text {
26 void Layout::_clearOutputObjects()
27 {
28     _paragraphs.clear();
29     _lines.clear();
30     _chunks.clear();
31     for (std::vector<Span>::iterator it_span = _spans.begin() ; it_span != _spans.end() ; it_span++)
32         if (it_span->font) it_span->font->Unref();
33     _spans.clear();
34     _characters.clear();
35     _glyphs.clear();
36     _path_fitted = NULL;
37 }
39 void Layout::LineHeight::max(LineHeight const &other)
40 {
41     if (other.ascent > ascent)  ascent  = other.ascent;
42     if (other.descent > descent) descent = other.descent;
43     if (other.leading > leading) leading = other.leading;
44 }
46 void Layout::_getGlyphTransformMatrix(int glyph_index, NRMatrix *matrix) const
47 {
48     Span const &span = _glyphs[glyph_index].span(this);
49     double sin_rotation = sin(_glyphs[glyph_index].rotation);
50     double cos_rotation = cos(_glyphs[glyph_index].rotation);
51     (*matrix)[0] = span.font_size * cos_rotation;
52     (*matrix)[1] = span.font_size * sin_rotation;
53     (*matrix)[2] = span.font_size * sin_rotation;
54     (*matrix)[3] = -span.font_size * cos_rotation;
55     if (span.block_progression == LEFT_TO_RIGHT || span.block_progression == RIGHT_TO_LEFT) {
56         (*matrix)[4] = _lines[_chunks[span.in_chunk].in_line].baseline_y + _glyphs[glyph_index].y;
57         (*matrix)[5] = _chunks[span.in_chunk].left_x + _glyphs[glyph_index].x;
58     } else {
59         (*matrix)[4] = _chunks[span.in_chunk].left_x + _glyphs[glyph_index].x;
60         (*matrix)[5] = _lines[_chunks[span.in_chunk].in_line].baseline_y + _glyphs[glyph_index].y;
61     }
62 }
64 void Layout::show(NRArenaGroup *in_arena, NRRect const *paintbox) const
65 {
66     int glyph_index = 0;
67     for (unsigned span_index = 0 ; span_index < _spans.size() ; span_index++) {
68         if (_input_stream[_spans[span_index].in_input_stream_item]->Type() != TEXT_SOURCE) continue;
69         InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_input_stream[_spans[span_index].in_input_stream_item]);
70         NRArenaGlyphsGroup *nr_group = NRArenaGlyphsGroup::create(in_arena->arena);
71         nr_arena_item_add_child(in_arena, nr_group, NULL);
72         nr_arena_item_unref(nr_group);
74         nr_arena_glyphs_group_set_style(nr_group, text_source->style);
75         while (glyph_index < (int)_glyphs.size() && _characters[_glyphs[glyph_index].in_character].in_span == span_index) {
76             if (_characters[_glyphs[glyph_index].in_character].in_glyph != -1) {
77                 NRMatrix glyph_matrix;
78                 _getGlyphTransformMatrix(glyph_index, &glyph_matrix);
79                 nr_arena_glyphs_group_add_component(nr_group, _spans[span_index].font, _glyphs[glyph_index].glyph, &glyph_matrix);
80             }
81             glyph_index++;
82         }
83         nr_arena_glyphs_group_set_paintbox(NR_ARENA_GLYPHS_GROUP(nr_group), paintbox);
84     }
85     nr_arena_item_request_update(NR_ARENA_ITEM(in_arena), NR_ARENA_ITEM_STATE_ALL, FALSE);
86 }
88 void Layout::getBoundingBox(NRRect *bounding_box, NR::Matrix const &transform, int start, int length) const
89 {
90     for (unsigned glyph_index = 0 ; glyph_index < _glyphs.size() ; glyph_index++) {
91         if (_characters[_glyphs[glyph_index].in_character].in_glyph == -1) continue;
92         if (start != -1 && (int) _glyphs[glyph_index].in_character < start) continue;
93         if (length != -1) {
94             if (start == -1)
95                 start = 0;
96             if ((int) _glyphs[glyph_index].in_character > start + length) continue;
97         }
98         // this could be faster
99         NRMatrix glyph_matrix;
100         _getGlyphTransformMatrix(glyph_index, &glyph_matrix);
101         NR::Matrix total_transform = glyph_matrix;
102         total_transform *= transform;
103         NR::Rect glyph_rect = _glyphs[glyph_index].span(this).font->BBox(_glyphs[glyph_index].glyph);
104         NR::Point bmi = glyph_rect.min(), bma = glyph_rect.max();
105         NR::Point tlp(bmi[0],bmi[1]), trp(bma[0],bmi[1]), blp(bmi[0],bma[1]), brp(bma[0],bma[1]);
106         tlp *= total_transform;
107         trp *= total_transform;
108         blp *= total_transform;
109         brp *= total_transform;
110         glyph_rect = NR::Rect(tlp,trp);
111         glyph_rect.expandTo(blp);
112         glyph_rect.expandTo(brp);
113         if ( (glyph_rect.min())[0] < bounding_box->x0 ) bounding_box->x0=(glyph_rect.min())[0];
114         if ( (glyph_rect.max())[0] > bounding_box->x1 ) bounding_box->x1=(glyph_rect.max())[0];
115         if ( (glyph_rect.min())[1] < bounding_box->y0 ) bounding_box->y0=(glyph_rect.min())[1];
116         if ( (glyph_rect.max())[1] > bounding_box->y1 ) bounding_box->y1=(glyph_rect.max())[1];
117     }
120 void Layout::print(SPPrintContext *ctx,
121                    NRRect const *pbox, NRRect const *dbox, NRRect const *bbox,
122                    NRMatrix const &ctm) const
124     if (_input_stream.empty()) return;
126     Direction block_progression = _blockProgression();
127     bool text_to_path = ctx->module->textToPath();
128     for (unsigned glyph_index = 0 ; glyph_index < _glyphs.size() ; ) {
129         if (_characters[_glyphs[glyph_index].in_character].in_glyph == -1) {
130             // invisible glyphs
131             unsigned same_character = _glyphs[glyph_index].in_character;
132             while (_glyphs[glyph_index].in_character == same_character)
133                 glyph_index++;
134             continue;
135         }
136         NRMatrix glyph_matrix;
137         Span const &span = _spans[_characters[_glyphs[glyph_index].in_character].in_span];
138         InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_input_stream[span.in_input_stream_item]);
139         if (text_to_path || _path_fitted) {
140             NRBPath bpath;
141             bpath.path = (NArtBpath*)span.font->ArtBPath(_glyphs[glyph_index].glyph);
142             if (bpath.path) {
143                 NRBPath abp;
144                 _getGlyphTransformMatrix(glyph_index, &glyph_matrix);
145                 abp.path = nr_artpath_affine(bpath.path, glyph_matrix);
146                 if (text_source->style->fill.type != SP_PAINT_TYPE_NONE)
147                     sp_print_fill(ctx, &abp, &ctm, text_source->style, pbox, dbox, bbox);
148                 if (text_source->style->stroke.type != SP_PAINT_TYPE_NONE)
149                     sp_print_stroke(ctx, &abp, &ctm, text_source->style, pbox, dbox, bbox);
150                 g_free(abp.path);
151             }
152             glyph_index++;
153         } else {
154             NR::Point g_pos(0,0);    // all strings are output at (0,0) because we do the translation using the matrix
155             glyph_matrix = NR::Matrix(NR::scale(1.0, -1.0) * NR::Matrix(NR::rotate(_glyphs[glyph_index].rotation)));
156             if (block_progression == LEFT_TO_RIGHT || block_progression == RIGHT_TO_LEFT) {
157                 glyph_matrix.c[4] = span.line(this).baseline_y + span.baseline_shift;
158                 // since we're outputting character codes, not glyphs, we want the character x
159                 glyph_matrix.c[5] = span.chunk(this).left_x + span.x_start + _characters[_glyphs[glyph_index].in_character].x;
160             } else {
161                 glyph_matrix.c[4] = span.chunk(this).left_x + span.x_start + _characters[_glyphs[glyph_index].in_character].x;
162                 glyph_matrix.c[5] = span.line(this).baseline_y + span.baseline_shift;
163             }
164             Glib::ustring::const_iterator span_iter = span.input_stream_first_character;
165             unsigned char_index = _glyphs[glyph_index].in_character;
166             unsigned original_span = _characters[char_index].in_span;
167             while (char_index && _characters[char_index - 1].in_span == original_span) {
168                 char_index--;
169                 span_iter++;
170             }
172             // try to output as many characters as possible in one go by detecting kerning and stopping when we encounter it
173             Glib::ustring span_string;
174             double char_x = _characters[_glyphs[glyph_index].in_character].x;
175             unsigned this_span_index = _characters[_glyphs[glyph_index].in_character].in_span;
176             do {
177                 span_string += *span_iter;
178                 span_iter++;
180                 unsigned same_character = _glyphs[glyph_index].in_character;
181                 while (glyph_index < _glyphs.size() && _glyphs[glyph_index].in_character == same_character) {
182                     char_x += _glyphs[glyph_index].width;
183                     glyph_index++;
184                 }
185             } while (glyph_index < _glyphs.size()
186                      && _path_fitted == NULL
187                      && _characters[_glyphs[glyph_index].in_character].in_span == this_span_index
188                      && fabs(char_x - _characters[_glyphs[glyph_index].in_character].x) < 1e-5);
189             sp_print_bind(ctx, glyph_matrix, 1.0);
190             sp_print_text(ctx, span_string.c_str(), g_pos, text_source->style);
191             sp_print_release(ctx);
192         }
193     }
196 // these functions are for dumpAsText() only. No need to translate
197 static char const *direction_to_text(Layout::Direction d)
199     switch (d) {
200         case Layout::LEFT_TO_RIGHT: return "ltr";
201         case Layout::RIGHT_TO_LEFT: return "rtl";
202         case Layout::TOP_TO_BOTTOM: return "ttb";
203         case Layout::BOTTOM_TO_TOP: return "btt";
204     }
205     return "???";
208 static char const *style_to_text(PangoStyle s)
210     switch (s) {
211         case PANGO_STYLE_NORMAL: return "upright";
212         case PANGO_STYLE_ITALIC: return "italic";
213         case PANGO_STYLE_OBLIQUE: return "oblique";
214     }
215     return "???";
218 static char const *weight_to_text(PangoWeight w)
220     switch (w) {
221         case PANGO_WEIGHT_ULTRALIGHT: return "ultralight";
222         case PANGO_WEIGHT_LIGHT     : return "light";
223 #if GTK_CHECK_VERSION(2,6,0)                                      
224         case PANGO_WEIGHT_SEMIBOLD  : return "semibold";
225 #endif                                      
226         case PANGO_WEIGHT_NORMAL    : return "normalweight";
227         case PANGO_WEIGHT_BOLD      : return "bold";
228         case PANGO_WEIGHT_ULTRABOLD : return "ultrabold";
229         case PANGO_WEIGHT_HEAVY     : return "heavy";
230     }
231     return "???";
234 Glib::ustring Layout::dumpAsText() const
236     Glib::ustring result;
238     for (unsigned span_index = 0 ; span_index < _spans.size() ; span_index++) {
239         char line[256];
240         snprintf(line, sizeof(line), "==== span %d\n", span_index);
241         result += line;
242         snprintf(line, sizeof(line), "  in para %d (direction=%s)\n", _lines[_chunks[_spans[span_index].in_chunk].in_line].in_paragraph,
243                  direction_to_text(_paragraphs[_lines[_chunks[_spans[span_index].in_chunk].in_line].in_paragraph].base_direction));
244         result += line;
245         snprintf(line, sizeof(line), "  in source %d (type=%d, cookie=%p)\n", _spans[span_index].in_input_stream_item,
246                  _input_stream[_spans[span_index].in_input_stream_item]->Type(),
247                  _input_stream[_spans[span_index].in_input_stream_item]->source_cookie);
248         result += line;
249         snprintf(line, sizeof(line), "  in line %d (baseline=%f, shape=%d)\n", _chunks[_spans[span_index].in_chunk].in_line,
250                  _lines[_chunks[_spans[span_index].in_chunk].in_line].baseline_y,
251                  _lines[_chunks[_spans[span_index].in_chunk].in_line].in_shape);
252         result += line;
253         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);
254         result += line;
255         if (_spans[span_index].font) {
256             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)));
257             result += line;
258         }
259         snprintf(line, sizeof(line), "    x_start = %f, x_end = %f\n", _spans[span_index].x_start, _spans[span_index].x_end);
260         result += line;
261         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);
262         result += line;
263         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));
264         result += line;
265         result += "    ** characters:\n";
266         Glib::ustring::const_iterator iter_char = _spans[span_index].input_stream_first_character;
267         // very inefficent code. what the hell, it's only debug stuff.
268         for (unsigned char_index = 0 ; char_index < _characters.size() ; char_index++) {
269             if (_characters[char_index].in_span != span_index) continue;
270             if (_input_stream[_spans[span_index].in_input_stream_item]->Type() != TEXT_SOURCE) {
271                 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);
272             } else {
273                 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);
274                 iter_char++;
275             }
276             result += line;
277         }
278         result += "    ** glyphs:\n";
279         for (unsigned glyph_index = 0 ; glyph_index < _glyphs.size() ; glyph_index++) {
280             if (_characters[_glyphs[glyph_index].in_character].in_span != span_index) continue;
281             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);
282             result += line;
283         }
284         result += "\n";
285     }
286     result += "EOT\n";
287     return result;
290 void Layout::fitToPathAlign(SVGLength const &startOffset, Path const &path)
292     double offset = 0.0;
294     if (startOffset._set) {
295         if (startOffset.unit == SVGLength::PERCENT)
296             offset = startOffset.computed * const_cast<Path&>(path).Length();
297         else
298             offset = startOffset.computed;
299     }
301     switch (_paragraphs.front().alignment) {
302         case CENTER:
303             offset -= _getChunkWidth(0) * 0.5;
304             break;
305         case RIGHT:
306             offset -= _getChunkWidth(0);
307             break;
308         default:
309             break;
310     }
312     if (_characters.empty()) {
313         int unused = 0;
314         Path::cut_position *point_otp = const_cast<Path&>(path).CurvilignToPosition(1, &offset, unused);
315         if (offset >= 0.0 && point_otp != NULL && point_otp[0].piece >= 0) {
316             NR::Point point;
317             NR::Point tangent;
318             const_cast<Path&>(path).PointAndTangentAt(point_otp[0].piece, point_otp[0].t, point, tangent);
319             _empty_cursor_shape.position = point;
320             _empty_cursor_shape.rotation = atan2(tangent[NR::Y], tangent[NR::X]);
321         }
322     }
324     for (unsigned char_index = 0 ; char_index < _characters.size() ; ) {
325         int next_cluster_glyph_index;
326         unsigned next_cluster_char_index;
327         double character_advance;
328         Span const &span = _characters[char_index].span(this);
330         for (next_cluster_char_index = char_index + 1 ;
331              next_cluster_char_index < _characters.size() && !_characters[next_cluster_char_index].char_attributes.is_cursor_position;
332              next_cluster_char_index++);
334         if (next_cluster_char_index == _characters.size()) {
335             next_cluster_glyph_index = _glyphs.size();
336             character_advance = 0.0;   // arbitrary because we're not going to advance
337         } else {
338             next_cluster_glyph_index = _characters[next_cluster_char_index].in_glyph;
339             character_advance =   (_glyphs[next_cluster_glyph_index].x + _glyphs[next_cluster_glyph_index].chunk(this).left_x)
340                 - (_glyphs[_characters[char_index].in_glyph].x + span.chunk(this).left_x);
341         }
343         double start_offset = offset + span.x_start + _characters[char_index].x;
344         double cluster_width = 0.0;
345         for (int glyph_index = _characters[char_index].in_glyph ; glyph_index < next_cluster_glyph_index ; glyph_index++)
346             cluster_width += _glyphs[glyph_index].width;
347         if (span.direction == RIGHT_TO_LEFT)
348             start_offset -= cluster_width;
349         double end_offset = start_offset + cluster_width;
351         int unused = 0;
352         double midpoint_offset = (start_offset + end_offset) * 0.5;
353         // as far as I know these functions are const, they're just not marked as such
354         Path::cut_position *midpoint_otp = const_cast<Path&>(path).CurvilignToPosition(1, &midpoint_offset, unused);
355         if (midpoint_offset >= 0.0 && midpoint_otp != NULL && midpoint_otp[0].piece >= 0) {
356             NR::Point midpoint;
357             NR::Point tangent;
359             const_cast<Path&>(path).PointAndTangentAt(midpoint_otp[0].piece, midpoint_otp[0].t, midpoint, tangent);
361             if (start_offset >= 0.0 && end_offset >= 0.0) {
362                 Path::cut_position *start_otp = const_cast<Path&>(path).CurvilignToPosition(1, &start_offset, unused);
363                 if (start_otp != NULL && start_otp[0].piece >= 0) {
364                     Path::cut_position *end_otp = const_cast<Path&>(path).CurvilignToPosition(1, &end_offset, unused);
365                     if (end_otp != NULL && end_otp[0].piece >= 0) {
366                         bool on_same_subpath = true;
367                         for (size_t i = 0 ; i < path.pts.size() ; i++) {
368                             if (path.pts[i].piece <= start_otp[0].piece) continue;
369                             if (path.pts[i].piece >= end_otp[0].piece) break;
370                             if (path.pts[i].isMoveTo == polyline_moveto) {
371                                 on_same_subpath = false;
372                                 break;
373                             }
374                         }
375                         if (on_same_subpath) {
376                             // both points were on the same subpath (without this test the angle is very weird)
377                             NR::Point startpoint, endpoint;
378                             const_cast<Path&>(path).PointAt(start_otp[0].piece, start_otp[0].t, startpoint);
379                             const_cast<Path&>(path).PointAt(end_otp[0].piece, end_otp[0].t, endpoint);
380                             if (endpoint != startpoint) {
381                                 tangent = endpoint - startpoint;
382                                 tangent.normalize();
383                             } else {
384                                 tangent = NR::Point (0,0);
385                             }
386                         }
387                         g_free(end_otp);
388                     }
389                     g_free(start_otp);
390                 }
391             }
393             double rotation = atan2(tangent[1], tangent[0]);
394             for (int glyph_index = _characters[char_index].in_glyph ; glyph_index < next_cluster_glyph_index ; glyph_index++) {
395                 double tangent_shift = -cluster_width * 0.5 + _glyphs[glyph_index].x - (_characters[char_index].x + span.x_start);
396                 double normal_shift = _glyphs[glyph_index].y;
397                 if (span.direction == RIGHT_TO_LEFT)
398                     tangent_shift += cluster_width;
399                 _glyphs[glyph_index].x = midpoint[0] - span.chunk(this).left_x + tangent[0] * tangent_shift - tangent[1] * normal_shift;
400                 _glyphs[glyph_index].y = midpoint[1] - _lines.front().baseline_y + tangent[1] * tangent_shift + tangent[0] * normal_shift;
401                 _glyphs[glyph_index].rotation += rotation;
402             }
403         } else {  // outside the bounds of the path: hide the glyphs
404             _characters[char_index].in_glyph = -1;
405         }
406         g_free(midpoint_otp);
408         char_index = next_cluster_char_index;
409     }
411     for (unsigned span_index = 0 ; span_index < _spans.size() ; span_index++) {
412         _spans[span_index].x_start += offset;
413         _spans[span_index].x_end += offset;
414     }
416     _path_fitted = &path;
419 SPCurve *Layout::convertToCurves(iterator const &from_glyph, iterator const &to_glyph) const
421     GSList *cc = NULL;
423     for (int glyph_index = from_glyph._glyph_index ; glyph_index < to_glyph._glyph_index ; glyph_index++) {
424         NRMatrix glyph_matrix;
425         Span const &span = _glyphs[glyph_index].span(this);
426         _getGlyphTransformMatrix(glyph_index, &glyph_matrix);
428         NRBPath bpath;
429         bpath.path = (NArtBpath*)span.font->ArtBPath(_glyphs[glyph_index].glyph);
430         if (bpath.path) {
431             NArtBpath *abp = nr_artpath_affine(bpath.path, glyph_matrix);
432             SPCurve *c = sp_curve_new_from_bpath(abp);
433             if (c) cc = g_slist_prepend(cc, c);
434         }
435     }
436     cc = g_slist_reverse(cc);
438     SPCurve *curve;
439     if ( cc ) {
440         curve = sp_curve_concat(cc);
441     } else {
442         curve = sp_curve_new();
443     }
445     while (cc) {
446         /* fixme: This is dangerous, as we are mixing art_alloc and g_new */
447         sp_curve_unref((SPCurve *) cc->data);
448         cc = g_slist_remove(cc, cc->data);
449     }
451     return curve;
454 void Layout::transform(NR::Matrix const &transform)
456     // this is all massively oversimplified
457     // I can't actually think of anybody who'll want to use it at the moment, so it'll stay simple
458     for (unsigned glyph_index = 0 ; glyph_index < _glyphs.size() ; glyph_index++) {
459         NR::Point point(_glyphs[glyph_index].x, _glyphs[glyph_index].y);
460         point *= transform;
461         _glyphs[glyph_index].x = point[0];
462         _glyphs[glyph_index].y = point[1];
463     }
466 }//namespace Text
467 }//namespace Inkscape
470 /*
471   Local Variables:
472   mode:c++
473   c-file-style:"stroustrup"
474   c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
475   indent-tabs-mode:nil
476   fill-column:99
477   End:
478 */
479 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :