2
* Inkscape::Text::Layout - text layout engine output functions
5
* Richard Hughes <cyreve@users.sf.net>
7
* Copyright (C) 2005 Richard Hughes
9
* Released under GNU GPL, read the file 'COPYING' for more information
11
#include "Layout-TNG.h"
12
#include "display/nr-arena-glyphs.h"
15
#include "extension/print.h"
16
#include "livarot/Path.h"
17
#include "libnr/nr-scale-matrix-ops.h"
18
#include "font-instance.h"
19
#include "svg/svg-length.h"
24
void Layout::_clearOutputObjects()
29
for (std::vector<Span>::iterator it_span = _spans.begin() ; it_span != _spans.end() ; it_span++)
30
if (it_span->font) it_span->font->Unref();
37
void Layout::LineHeight::max(LineHeight const &other)
39
if (other.ascent > ascent) ascent = other.ascent;
40
if (other.descent > descent) descent = other.descent;
41
if (other.leading > leading) leading = other.leading;
44
void Layout::_getGlyphTransformMatrix(int glyph_index, NRMatrix *matrix) const
46
Span const &span = _glyphs[glyph_index].span(this);
47
double sin_rotation = sin(_glyphs[glyph_index].rotation);
48
double cos_rotation = cos(_glyphs[glyph_index].rotation);
49
(*matrix)[0] = span.font_size * cos_rotation;
50
(*matrix)[1] = span.font_size * sin_rotation;
51
(*matrix)[2] = span.font_size * sin_rotation;
52
(*matrix)[3] = -span.font_size * cos_rotation;
53
if (span.block_progression == LEFT_TO_RIGHT || span.block_progression == RIGHT_TO_LEFT) {
54
(*matrix)[4] = _lines[_chunks[span.in_chunk].in_line].baseline_y + _glyphs[glyph_index].y;
55
(*matrix)[5] = _chunks[span.in_chunk].left_x + _glyphs[glyph_index].x;
57
(*matrix)[4] = _chunks[span.in_chunk].left_x + _glyphs[glyph_index].x;
58
(*matrix)[5] = _lines[_chunks[span.in_chunk].in_line].baseline_y + _glyphs[glyph_index].y;
62
void Layout::show(NRArenaGroup *in_arena, NRRect const *paintbox) const
65
for (unsigned span_index = 0 ; span_index < _spans.size() ; span_index++) {
66
if (_input_stream[_spans[span_index].in_input_stream_item]->Type() != TEXT_SOURCE) continue;
67
InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_input_stream[_spans[span_index].in_input_stream_item]);
68
NRArenaGlyphsGroup *nr_group = NRArenaGlyphsGroup::create(in_arena->arena);
69
nr_arena_item_add_child(in_arena, nr_group, NULL);
70
nr_arena_item_unref(nr_group);
72
nr_arena_glyphs_group_set_style(nr_group, text_source->style);
73
while (glyph_index < (int)_glyphs.size() && _characters[_glyphs[glyph_index].in_character].in_span == span_index) {
74
if (_characters[_glyphs[glyph_index].in_character].in_glyph != -1) {
75
NRMatrix glyph_matrix;
76
_getGlyphTransformMatrix(glyph_index, &glyph_matrix);
77
nr_arena_glyphs_group_add_component(nr_group, _spans[span_index].font, _glyphs[glyph_index].glyph, &glyph_matrix);
81
nr_arena_glyphs_group_set_paintbox(NR_ARENA_GLYPHS_GROUP(nr_group), paintbox);
83
nr_arena_item_request_update(NR_ARENA_ITEM(in_arena), NR_ARENA_ITEM_STATE_ALL, FALSE);
86
void Layout::getBoundingBox(NRRect *bounding_box, NR::Matrix const &transform) const
88
for (unsigned glyph_index = 0 ; glyph_index < _glyphs.size() ; glyph_index++) {
89
if (_characters[_glyphs[glyph_index].in_character].in_glyph == -1) continue;
90
// this could be faster
91
NRMatrix glyph_matrix;
92
_getGlyphTransformMatrix(glyph_index, &glyph_matrix);
93
NR::Matrix total_transform = glyph_matrix;
94
total_transform *= transform;
95
NR::Rect glyph_rect = _glyphs[glyph_index].span(this).font->BBox(_glyphs[glyph_index].glyph);
96
NR::Point bmi = glyph_rect.min(), bma = glyph_rect.max();
97
NR::Point tlp(bmi[0],bmi[1]), trp(bma[0],bmi[1]), blp(bmi[0],bma[1]), brp(bma[0],bma[1]);
98
tlp *= total_transform;
99
trp *= total_transform;
100
blp *= total_transform;
101
brp *= total_transform;
102
glyph_rect = NR::Rect(tlp,trp);
103
glyph_rect.expandTo(blp);
104
glyph_rect.expandTo(brp);
105
if ( (glyph_rect.min())[0] < bounding_box->x0 ) bounding_box->x0=(glyph_rect.min())[0];
106
if ( (glyph_rect.max())[0] > bounding_box->x1 ) bounding_box->x1=(glyph_rect.max())[0];
107
if ( (glyph_rect.min())[1] < bounding_box->y0 ) bounding_box->y0=(glyph_rect.min())[1];
108
if ( (glyph_rect.max())[1] > bounding_box->y1 ) bounding_box->y1=(glyph_rect.max())[1];
112
void Layout::print(SPPrintContext *ctx,
113
NRRect const *pbox, NRRect const *dbox, NRRect const *bbox,
114
NRMatrix const &ctm) const
116
if (_input_stream.empty()) return;
118
Direction block_progression = _blockProgression();
119
bool text_to_path = ctx->module->textToPath();
120
for (unsigned glyph_index = 0 ; glyph_index < _glyphs.size() ; ) {
121
if (_characters[_glyphs[glyph_index].in_character].in_glyph == -1) {
123
unsigned same_character = _glyphs[glyph_index].in_character;
124
while (_glyphs[glyph_index].in_character == same_character)
128
NRMatrix glyph_matrix;
129
Span const &span = _spans[_characters[_glyphs[glyph_index].in_character].in_span];
130
InputStreamTextSource const *text_source = static_cast<InputStreamTextSource const *>(_input_stream[span.in_input_stream_item]);
131
if (text_to_path || _path_fitted) {
133
bpath.path = (NArtBpath*)span.font->ArtBPath(_glyphs[glyph_index].glyph);
136
_getGlyphTransformMatrix(glyph_index, &glyph_matrix);
137
abp.path = nr_artpath_affine(bpath.path, glyph_matrix);
138
if (text_source->style->fill.type != SP_PAINT_TYPE_NONE)
139
sp_print_fill(ctx, &abp, &ctm, text_source->style, pbox, dbox, bbox);
140
if (text_source->style->stroke.type != SP_PAINT_TYPE_NONE)
141
sp_print_stroke(ctx, &abp, &ctm, text_source->style, pbox, dbox, bbox);
146
NR::Point g_pos(0,0); // all strings are output at (0,0) because we do the translation using the matrix
147
glyph_matrix = NR::Matrix(NR::scale(1.0, -1.0) * NR::Matrix(NR::rotate(_glyphs[glyph_index].rotation)));
148
if (block_progression == LEFT_TO_RIGHT || block_progression == RIGHT_TO_LEFT) {
149
glyph_matrix.c[4] = span.line(this).baseline_y + span.baseline_shift;
150
// since we're outputting character codes, not glyphs, we want the character x
151
glyph_matrix.c[5] = span.chunk(this).left_x + span.x_start + _characters[_glyphs[glyph_index].in_character].x;
153
glyph_matrix.c[4] = span.chunk(this).left_x + span.x_start + _characters[_glyphs[glyph_index].in_character].x;
154
glyph_matrix.c[5] = span.line(this).baseline_y + span.baseline_shift;
156
Glib::ustring::const_iterator span_iter = span.input_stream_first_character;
157
unsigned char_index = _glyphs[glyph_index].in_character;
158
unsigned original_span = _characters[char_index].in_span;
159
while (char_index && _characters[char_index - 1].in_span == original_span) {
164
// try to output as many characters as possible in one go by detecting kerning and stopping when we encounter it
165
Glib::ustring span_string;
166
double char_x = _characters[_glyphs[glyph_index].in_character].x;
167
unsigned this_span_index = _characters[_glyphs[glyph_index].in_character].in_span;
169
span_string += *span_iter;
172
unsigned same_character = _glyphs[glyph_index].in_character;
173
while (glyph_index < _glyphs.size() && _glyphs[glyph_index].in_character == same_character) {
174
char_x += _glyphs[glyph_index].width;
177
} while (glyph_index < _glyphs.size()
178
&& _path_fitted == NULL
179
&& _characters[_glyphs[glyph_index].in_character].in_span == this_span_index
180
&& fabs(char_x - _characters[_glyphs[glyph_index].in_character].x) < 1e-5);
181
sp_print_bind(ctx, glyph_matrix, 1.0);
182
sp_print_text(ctx, span_string.c_str(), g_pos, text_source->style);
183
sp_print_release(ctx);
188
// these functions are for dumpAsText() only. No need to translate
189
static char const *direction_to_text(Layout::Direction d)
192
case Layout::LEFT_TO_RIGHT: return "ltr";
193
case Layout::RIGHT_TO_LEFT: return "rtl";
194
case Layout::TOP_TO_BOTTOM: return "ttb";
195
case Layout::BOTTOM_TO_TOP: return "btt";
200
static char const *style_to_text(PangoStyle s)
203
case PANGO_STYLE_NORMAL: return "upright";
204
case PANGO_STYLE_ITALIC: return "italic";
205
case PANGO_STYLE_OBLIQUE: return "oblique";
210
static char const *weight_to_text(PangoWeight w)
213
case PANGO_WEIGHT_ULTRALIGHT: return "ultralight";
214
case PANGO_WEIGHT_LIGHT : return "light";
215
case PANGO_WEIGHT_SEMIBOLD : return "semibold";
216
case PANGO_WEIGHT_NORMAL : return "normalweight";
217
case PANGO_WEIGHT_BOLD : return "bold";
218
case PANGO_WEIGHT_ULTRABOLD : return "ultrabold";
219
case PANGO_WEIGHT_HEAVY : return "heavy";
224
Glib::ustring Layout::dumpAsText() const
226
Glib::ustring result;
228
for (unsigned span_index = 0 ; span_index < _spans.size() ; span_index++) {
230
snprintf(line, sizeof(line), "==== span %d\n", span_index);
232
snprintf(line, sizeof(line), " in para %d (direction=%s)\n", _lines[_chunks[_spans[span_index].in_chunk].in_line].in_paragraph,
233
direction_to_text(_paragraphs[_lines[_chunks[_spans[span_index].in_chunk].in_line].in_paragraph].base_direction));
235
snprintf(line, sizeof(line), " in source %d (type=%d, cookie=%p)\n", _spans[span_index].in_input_stream_item,
236
_input_stream[_spans[span_index].in_input_stream_item]->Type(),
237
_input_stream[_spans[span_index].in_input_stream_item]->source_cookie);
239
snprintf(line, sizeof(line), " in line %d (baseline=%f, shape=%d)\n", _chunks[_spans[span_index].in_chunk].in_line,
240
_lines[_chunks[_spans[span_index].in_chunk].in_line].baseline_y,
241
_lines[_chunks[_spans[span_index].in_chunk].in_line].in_shape);
243
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);
245
if (_spans[span_index].font) {
246
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)));
249
snprintf(line, sizeof(line), " x_start = %f, x_end = %f\n", _spans[span_index].x_start, _spans[span_index].x_end);
251
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);
253
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));
255
result += " ** characters:\n";
256
Glib::ustring::const_iterator iter_char = _spans[span_index].input_stream_first_character;
257
// very inefficent code. what the hell, it's only debug stuff.
258
for (unsigned char_index = 0 ; char_index < _characters.size() ; char_index++) {
259
if (_characters[char_index].in_span != span_index) continue;
260
if (_input_stream[_spans[span_index].in_input_stream_item]->Type() != TEXT_SOURCE) {
261
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);
263
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);
268
result += " ** glyphs:\n";
269
for (unsigned glyph_index = 0 ; glyph_index < _glyphs.size() ; glyph_index++) {
270
if (_characters[_glyphs[glyph_index].in_character].in_span != span_index) continue;
271
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);
280
void Layout::fitToPathAlign(SVGLength const &startOffset, Path const &path)
284
if (startOffset._set) {
285
if (startOffset.unit == SVGLength::PERCENT)
286
offset = startOffset.computed * const_cast<Path&>(path).Length();
288
offset = startOffset.computed;
291
switch (_paragraphs.front().alignment) {
293
offset -= _getChunkWidth(0) * 0.5;
296
offset -= _getChunkWidth(0);
302
if (_characters.empty()) {
304
Path::cut_position *point_otp = const_cast<Path&>(path).CurvilignToPosition(1, &offset, unused);
305
if (offset >= 0.0 && point_otp != NULL && point_otp[0].piece >= 0) {
308
const_cast<Path&>(path).PointAndTangentAt(point_otp[0].piece, point_otp[0].t, point, tangent);
309
_empty_cursor_shape.position = point;
310
_empty_cursor_shape.rotation = atan2(tangent[NR::Y], tangent[NR::X]);
314
for (unsigned char_index = 0 ; char_index < _characters.size() ; ) {
315
int next_cluster_glyph_index;
316
unsigned next_cluster_char_index;
317
double character_advance;
318
Span const &span = _characters[char_index].span(this);
320
for (next_cluster_char_index = char_index + 1 ;
321
next_cluster_char_index < _characters.size() && !_characters[next_cluster_char_index].char_attributes.is_cursor_position;
322
next_cluster_char_index++);
324
if (next_cluster_char_index == _characters.size()) {
325
next_cluster_glyph_index = _glyphs.size();
326
character_advance = 0.0; // arbitrary because we're not going to advance
328
next_cluster_glyph_index = _characters[next_cluster_char_index].in_glyph;
329
character_advance = (_glyphs[next_cluster_glyph_index].x + _glyphs[next_cluster_glyph_index].chunk(this).left_x)
330
- (_glyphs[_characters[char_index].in_glyph].x + span.chunk(this).left_x);
333
double start_offset = offset + span.x_start + _characters[char_index].x;
334
double cluster_width = 0.0;
335
for (int glyph_index = _characters[char_index].in_glyph ; glyph_index < next_cluster_glyph_index ; glyph_index++)
336
cluster_width += _glyphs[glyph_index].width;
337
if (span.direction == RIGHT_TO_LEFT)
338
start_offset -= cluster_width;
339
double end_offset = start_offset + cluster_width;
342
double midpoint_offset = (start_offset + end_offset) * 0.5;
343
// as far as I know these functions are const, they're just not marked as such
344
Path::cut_position *midpoint_otp = const_cast<Path&>(path).CurvilignToPosition(1, &midpoint_offset, unused);
345
if (midpoint_offset >= 0.0 && midpoint_otp != NULL && midpoint_otp[0].piece >= 0) {
349
const_cast<Path&>(path).PointAndTangentAt(midpoint_otp[0].piece, midpoint_otp[0].t, midpoint, tangent);
351
if (start_offset >= 0.0 && end_offset >= 0.0) {
352
Path::cut_position *start_otp = const_cast<Path&>(path).CurvilignToPosition(1, &start_offset, unused);
353
if (start_otp != NULL && start_otp[0].piece >= 0) {
354
Path::cut_position *end_otp = const_cast<Path&>(path).CurvilignToPosition(1, &end_offset, unused);
355
if (end_otp != NULL && end_otp[0].piece >= 0) {
356
bool on_same_subpath = true;
357
for (size_t i = 0 ; i < path.pts.size() ; i++) {
358
if (path.pts[i].piece <= start_otp[0].piece) continue;
359
if (path.pts[i].piece >= end_otp[0].piece) break;
360
if (path.pts[i].isMoveTo == polyline_moveto) {
361
on_same_subpath = false;
365
if (on_same_subpath) {
366
// both points were on the same subpath (without this test the angle is very weird)
367
NR::Point startpoint, endpoint;
368
const_cast<Path&>(path).PointAt(start_otp[0].piece, start_otp[0].t, startpoint);
369
const_cast<Path&>(path).PointAt(end_otp[0].piece, end_otp[0].t, endpoint);
370
if (endpoint != startpoint) {
371
tangent = endpoint - startpoint;
374
tangent = NR::Point (0,0);
383
double rotation = atan2(tangent[1], tangent[0]);
384
for (int glyph_index = _characters[char_index].in_glyph ; glyph_index < next_cluster_glyph_index ; glyph_index++) {
385
double tangent_shift = -cluster_width * 0.5 + _glyphs[glyph_index].x - (_characters[char_index].x + span.x_start);
386
double normal_shift = _glyphs[glyph_index].y;
387
if (span.direction == RIGHT_TO_LEFT)
388
tangent_shift += cluster_width;
389
_glyphs[glyph_index].x = midpoint[0] - span.chunk(this).left_x + tangent[0] * tangent_shift - tangent[1] * normal_shift;
390
_glyphs[glyph_index].y = midpoint[1] - _lines.front().baseline_y + tangent[1] * tangent_shift + tangent[0] * normal_shift;
391
_glyphs[glyph_index].rotation += rotation;
393
} else { // outside the bounds of the path: hide the glyphs
394
_characters[char_index].in_glyph = -1;
396
g_free(midpoint_otp);
398
char_index = next_cluster_char_index;
401
for (unsigned span_index = 0 ; span_index < _spans.size() ; span_index++) {
402
_spans[span_index].x_start += offset;
403
_spans[span_index].x_end += offset;
406
_path_fitted = &path;
409
SPCurve *Layout::convertToCurves(iterator const &from_glyph, iterator const &to_glyph) const
413
for (int glyph_index = from_glyph._glyph_index ; glyph_index < to_glyph._glyph_index ; glyph_index++) {
414
NRMatrix glyph_matrix;
415
Span const &span = _glyphs[glyph_index].span(this);
416
_getGlyphTransformMatrix(glyph_index, &glyph_matrix);
419
bpath.path = (NArtBpath*)span.font->ArtBPath(_glyphs[glyph_index].glyph);
421
NArtBpath *abp = nr_artpath_affine(bpath.path, glyph_matrix);
422
SPCurve *c = sp_curve_new_from_bpath(abp);
423
if (c) cc = g_slist_prepend(cc, c);
426
cc = g_slist_reverse(cc);
430
curve = sp_curve_concat(cc);
432
curve = sp_curve_new();
436
/* fixme: This is dangerous, as we are mixing art_alloc and g_new */
437
sp_curve_unref((SPCurve *) cc->data);
438
cc = g_slist_remove(cc, cc->data);
444
void Layout::transform(NR::Matrix const &transform)
446
// this is all massively oversimplified
447
// I can't actually think of anybody who'll want to use it at the moment, so it'll stay simple
448
for (unsigned glyph_index = 0 ; glyph_index < _glyphs.size() ; glyph_index++) {
449
NR::Point point(_glyphs[glyph_index].x, _glyphs[glyph_index].y);
451
_glyphs[glyph_index].x = point[0];
452
_glyphs[glyph_index].y = point[1];
457
}//namespace Inkscape
463
c-file-style:"stroustrup"
464
c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
469
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :