~ubuntu-branches/ubuntu/karmic/moon/karmic

« back to all changes in this revision

Viewing changes to src/layout.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jo Shields
  • Date: 2009-02-14 12:01:08 UTC
  • Revision ID: james.westby@ubuntu.com-20090214120108-06539vb25vhbd8bn
Tags: upstream-1.0
ImportĀ upstreamĀ versionĀ 1.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
 
2
/*
 
3
 * layout.cpp: 
 
4
 *
 
5
 * Contact:
 
6
 *   Moonlight List (moonlight-list@lists.ximian.com)
 
7
 *
 
8
 * Copyright 2008 Novell, Inc. (http://www.novell.com)
 
9
 *
 
10
 * See the LICENSE file included with the distribution for details.
 
11
 */
 
12
 
 
13
#ifdef HAVE_CONFIG_H
 
14
#include <config.h>
 
15
#endif
 
16
 
 
17
#include <math.h>
 
18
 
 
19
#include "moon-path.h"
 
20
#include "layout.h"
 
21
#include "debug.h"
 
22
 
 
23
 
 
24
 
 
25
#define BBOX_MARGIN 1.0
 
26
#define BBOX_PADDING 2.0
 
27
 
 
28
/*
 
29
 * Silverlight does not apply any kerning on a DOT, so we exclude them
 
30
 *      U+002E FULL STOP
 
31
 *      U+06D4 ARABIC FULL STOP
 
32
 *      U+3002 IDEOGRAPHIC FULL STOP
 
33
 * Note: this is different than using the "sliding dot" algorithm from
 
34
 * http://www.freetype.org/freetype2/docs/glyphs/glyphs-4.html
 
35
 */
 
36
#define APPLY_KERNING(uc)       ((uc != 0x002E) && (uc != 0x06D4) && (uc != 3002))
 
37
 
 
38
TextRun::TextRun (const gunichar *ucs4, int len, TextDecorations deco, TextFontDescription *font, Brush **fg)
 
39
{
 
40
        text = (gunichar *) g_malloc (sizeof (gunichar) * (len + 1));
 
41
        memcpy (text, ucs4, sizeof (gunichar) * len);
 
42
        text[len] = 0;
 
43
        
 
44
        this->font = font->GetFont ();
 
45
        this->deco = deco;
 
46
        this->fg = fg;
 
47
}
 
48
 
 
49
TextRun::TextRun (const char *utf8, int len, TextDecorations deco, TextFontDescription *font, Brush **fg)
 
50
{
 
51
        register gunichar *s, *d;
 
52
        
 
53
        d = this->text = g_utf8_to_ucs4_fast (utf8, len, NULL);
 
54
        
 
55
        // convert all ascii lwsp into a SPACE, conserving only \n's
 
56
        for (s = this->text; *s; s++) {
 
57
                if (g_unichar_isspace (*s)) {
 
58
                        if (*s == '\n')
 
59
                                *d++ = *s;
 
60
                        else if (*s < 128)
 
61
                                *d++ = ' ';
 
62
                        else
 
63
                                *d++ = *s;
 
64
                } else {
 
65
                        *d++ = *s;
 
66
                }
 
67
        }
 
68
        
 
69
        *d = 0;
 
70
        
 
71
        this->font = font->GetFont ();
 
72
        this->deco = deco;
 
73
        this->fg = fg;
 
74
}
 
75
 
 
76
TextRun::TextRun (TextFontDescription *font)
 
77
{
 
78
        // This TextRun will represent a LineBreak
 
79
        this->deco = TextDecorationsNone;
 
80
        this->font = font->GetFont ();
 
81
        this->text = NULL;
 
82
        this->fg = NULL;
 
83
}
 
84
 
 
85
TextRun::~TextRun ()
 
86
{
 
87
        font->unref ();
 
88
        g_free (text);
 
89
}
 
90
 
 
91
 
 
92
 
 
93
class TextSegment : public List::Node {
 
94
 public:
 
95
        moon_path *path;
 
96
        double advance;
 
97
        double width;
 
98
        TextRun *run;
 
99
        int start;
 
100
        int end;
 
101
        
 
102
        TextSegment (TextRun *run, int start);
 
103
        ~TextSegment ();
 
104
};
 
105
 
 
106
TextSegment::TextSegment (TextRun *run, int start)
 
107
{
 
108
        this->advance = 0.0;
 
109
        this->width = 0.0;
 
110
        this->start = start;
 
111
        this->end = start;
 
112
        this->path = NULL;
 
113
        this->run = run;
 
114
}
 
115
 
 
116
TextSegment::~TextSegment ()
 
117
{
 
118
        if (path)
 
119
                moon_path_destroy (path);
 
120
}
 
121
 
 
122
 
 
123
 
 
124
 
 
125
class TextLine : public List::Node {
 
126
 public:
 
127
        List *segments;
 
128
        double descend;
 
129
        double height;
 
130
        double width;
 
131
        
 
132
        TextLine ();
 
133
        ~TextLine ();
 
134
};
 
135
 
 
136
TextLine::TextLine ()
 
137
{
 
138
        segments = new List ();
 
139
        descend = 0.0;
 
140
        height = -1.0;
 
141
        width = 0.0;
 
142
}
 
143
 
 
144
TextLine::~TextLine ()
 
145
{
 
146
        segments->Clear (true);
 
147
        delete segments;
 
148
}
 
149
 
 
150
 
 
151
 
 
152
 
 
153
TextLayout::TextLayout ()
 
154
{
 
155
        wrapping = TextWrappingNoWrap;
 
156
        max_height = -1.0;
 
157
        max_width = -1.0;
 
158
        
 
159
        runs = NULL;
 
160
        
 
161
        lines = new List ();
 
162
        
 
163
        actual_height = -1.0;
 
164
        actual_width = -1.0;
 
165
}
 
166
 
 
167
TextLayout::~TextLayout ()
 
168
{
 
169
        if (runs) {
 
170
                runs->Clear (true);
 
171
                delete runs;
 
172
        }
 
173
        
 
174
        lines->Clear (true);
 
175
        delete lines;
 
176
}
 
177
 
 
178
double
 
179
TextLayout::GetMaxWidth ()
 
180
{
 
181
        return max_width;
 
182
}
 
183
 
 
184
void
 
185
TextLayout::SetMaxWidth (double max)
 
186
{
 
187
        if (max_width == max)
 
188
                return;
 
189
        
 
190
        max_width = max;
 
191
        
 
192
        actual_height = -1.0;
 
193
        actual_width = -1.0;
 
194
}
 
195
 
 
196
double
 
197
TextLayout::GetMaxHeight ()
 
198
{
 
199
        return max_height;
 
200
}
 
201
 
 
202
void
 
203
TextLayout::SetMaxHeight (double max)
 
204
{
 
205
        if (max_height == max)
 
206
                return;
 
207
        
 
208
        max_height = max;
 
209
        
 
210
        actual_height = -1.0;
 
211
        actual_width = -1.0;
 
212
}
 
213
 
 
214
TextWrapping
 
215
TextLayout::GetWrapping ()
 
216
{
 
217
        return wrapping;
 
218
}
 
219
 
 
220
void
 
221
TextLayout::SetWrapping (TextWrapping wrapping)
 
222
{
 
223
        if (this->wrapping == wrapping)
 
224
                return;
 
225
        
 
226
        this->wrapping = wrapping;
 
227
        
 
228
        actual_height = -1.0;
 
229
        actual_width = -1.0;
 
230
}
 
231
 
 
232
List *
 
233
TextLayout::GetTextRuns ()
 
234
{
 
235
        return runs;
 
236
}
 
237
 
 
238
void
 
239
TextLayout::SetTextRuns (List *runs)
 
240
{
 
241
        if (this->runs) {
 
242
                this->runs->Clear (true);
 
243
                delete this->runs;
 
244
        }
 
245
        
 
246
        this->runs = runs;
 
247
        
 
248
        actual_height = -1.0;
 
249
        actual_width = -1.0;
 
250
}
 
251
 
 
252
/**
 
253
 * TextLayout::GetActualExtents:
 
254
 * @width:
 
255
 * @height:
 
256
 *
 
257
 * Gets the actual width and height extents required for rendering the
 
258
 * full text.
 
259
 **/
 
260
void
 
261
TextLayout::GetActualExtents (double *width, double *height)
 
262
{
 
263
        *height = actual_height;
 
264
        *width = actual_width;
 
265
}
 
266
 
 
267
#if 0
 
268
/**
 
269
 * TextLayout::GetLayoutExtents:
 
270
 * @width:
 
271
 * @height:
 
272
 *
 
273
 * Gets the width and height extents suitable for rendering the text
 
274
 * w/ the current wrapping model.
 
275
 **/
 
276
void
 
277
TextLayout::GetLayoutExtents (double *width, double *height)
 
278
{
 
279
        *height = bbox_height;
 
280
        *width = bbox_width;
 
281
}
 
282
#endif
 
283
 
 
284
 
 
285
#if DEBUG
 
286
static void
 
287
print_run_text (const char *msg, gunichar *start, gunichar *end)
 
288
{
 
289
        GString *str = g_string_new ("");
 
290
        
 
291
        while (*start && (end ? start < end : true)) {
 
292
                g_string_append_unichar (str, *start == 0xA0 ? '_' : *start);
 
293
                start++;
 
294
        }
 
295
        
 
296
        printf ("%s = \"%s\"\n", msg, str->str);
 
297
        
 
298
        g_string_free (str, true);
 
299
}
 
300
 
 
301
static const char *unicode_break_types[] = {
 
302
        "G_UNICODE_BREAK_MANDATORY",
 
303
        "G_UNICODE_BREAK_CARRIAGE_RETURN",
 
304
        "G_UNICODE_BREAK_LINE_FEED",
 
305
        "G_UNICODE_BREAK_COMBINING_MARK",
 
306
        "G_UNICODE_BREAK_SURROGATE",
 
307
        "G_UNICODE_BREAK_ZERO_WIDTH_SPACE",
 
308
        "G_UNICODE_BREAK_INSEPARABLE",
 
309
        "G_UNICODE_BREAK_NON_BREAKING_GLUE",
 
310
        "G_UNICODE_BREAK_CONTINGENT",
 
311
        "G_UNICODE_BREAK_SPACE",
 
312
        "G_UNICODE_BREAK_AFTER",
 
313
        "G_UNICODE_BREAK_BEFORE",
 
314
        "G_UNICODE_BREAK_BEFORE_AND_AFTER",
 
315
        "G_UNICODE_BREAK_HYPHEN",
 
316
        "G_UNICODE_BREAK_NON_STARTER",
 
317
        "G_UNICODE_BREAK_OPEN_PUNCTUATION",
 
318
        "G_UNICODE_BREAK_CLOSE_PUNCTUATION",
 
319
        "G_UNICODE_BREAK_QUOTATION",
 
320
        "G_UNICODE_BREAK_EXCLAMATION",
 
321
        "G_UNICODE_BREAK_IDEOGRAPHIC",
 
322
        "G_UNICODE_BREAK_NUMERIC",
 
323
        "G_UNICODE_BREAK_INFIX_SEPARATOR",
 
324
        "G_UNICODE_BREAK_SYMBOL",
 
325
        "G_UNICODE_BREAK_ALPHABETIC",
 
326
        "G_UNICODE_BREAK_PREFIX",
 
327
        "G_UNICODE_BREAK_POSTFIX",
 
328
        "G_UNICODE_BREAK_COMPLEX_CONTEXT",
 
329
        "G_UNICODE_BREAK_AMBIGUOUS",
 
330
        "G_UNICODE_BREAK_UNKNOWN",
 
331
        "G_UNICODE_BREAK_NEXT_LINE",
 
332
        "G_UNICODE_BREAK_WORD_JOINER",
 
333
        "G_UNICODE_BREAK_HANGUL_L_JAMO",
 
334
        "G_UNICODE_BREAK_HANGUL_V_JAMO",
 
335
        "G_UNICODE_BREAK_HANGUL_T_JAMO",
 
336
        "G_UNICODE_BREAK_HANGUL_LV_SYLLABLE",
 
337
        "G_UNICODE_BREAK_HANGUL_LVT_SYLLABLE"
 
338
};
 
339
 
 
340
static void
 
341
print_break_info (gunichar *text)
 
342
{
 
343
        register gunichar *inptr = text;
 
344
        GUnicodeBreakType btype;
 
345
        char c[7];
 
346
        int i;
 
347
        
 
348
        printf ("Unicode break info:\n");
 
349
        
 
350
        while (*inptr) {
 
351
                btype = g_unichar_break_type (*inptr);
 
352
                i = g_unichar_to_utf8 (*inptr, c);
 
353
                c[i] = '\0';
 
354
                
 
355
                printf ("\t%u %s: break type = %s\n", *inptr, c,
 
356
                        unicode_break_types[btype]);
 
357
                
 
358
                inptr++;
 
359
        }
 
360
}
 
361
#endif
 
362
 
 
363
 
 
364
#define BreakSpace(btype) (btype == G_UNICODE_BREAK_SPACE || btype == G_UNICODE_BREAK_ZERO_WIDTH_SPACE)
 
365
#define BreakAfter(btype) (btype == G_UNICODE_BREAK_AFTER || btype == G_UNICODE_BREAK_NEXT_LINE)
 
366
#define BreakBefore(btype) (btype == G_UNICODE_BREAK_BEFORE || btype == G_UNICODE_BREAK_PREFIX)
 
367
 
 
368
void
 
369
TextLayout::LayoutWrapWithOverflow (TextLayoutHints *hints)
 
370
{
 
371
        double x0 = 0.0, x1 = 0.0, wx = 0.0, dy = 0.0;
 
372
        register gunichar *start, *word, *inptr;
 
373
        GUnicodeBreakType btype;
 
374
        bool underlined = false;
 
375
        TextSegment *segment;
 
376
        double descend = 0.0;
 
377
        double height = 0.0;
 
378
        double width = 0.0;
 
379
        bool blank = true;
 
380
        GlyphInfo *glyph;
 
381
        TextLine *line;
 
382
        double advance;
 
383
        guint32 prev;
 
384
        TextRun *run;
 
385
        
 
386
        if (hints->OverrideLineHeight ())
 
387
                height = hints->GetLineHeight ();
 
388
        
 
389
        line = new TextLine ();
 
390
        for (run = (TextRun *) runs->First (); run; run = (TextRun *) run->next) {
 
391
                if (run->text == NULL) {
 
392
                        // LineBreak
 
393
                        if (blank && !hints->OverrideLineHeight ()) {
 
394
                                descend = run->font->Descender ();
 
395
                                height = run->font->Height ();
 
396
                        }
 
397
                        
 
398
                        line->descend = descend;
 
399
                        line->height = height;
 
400
                        line->width = width;
 
401
                        dy += height;
 
402
                        
 
403
                        lines->Append (line);
 
404
                        
 
405
                        if (run->next) {
 
406
                                line = new TextLine ();
 
407
                        } else {
 
408
                                dy += height;
 
409
                                line = NULL;
 
410
                        }
 
411
                        
 
412
                        actual_height = dy;
 
413
                        underlined = false;
 
414
                        blank = true;
 
415
                        
 
416
                        if (!hints->OverrideLineHeight ()) {
 
417
                                descend = 0.0;
 
418
                                height = 0.0;
 
419
                        }
 
420
                        
 
421
                        width = 0.0;
 
422
                        x0 = 0.0;
 
423
                        x1 = 0.0;
 
424
                        
 
425
                        continue;
 
426
                }
 
427
                
 
428
                if (!underlined)
 
429
                        underlined = run->IsUnderlined ();
 
430
                
 
431
                if (!hints->OverrideLineHeight ()) {
 
432
                        descend = MIN (descend, run->font->Descender ());
 
433
                        height = MAX (height, run->font->Height ());
 
434
                }
 
435
                
 
436
                segment = new TextSegment (run, 0);
 
437
                inptr = start = run->text;
 
438
                prev = 0;
 
439
                x1 = x0;
 
440
                
 
441
                do {
 
442
                        // always include the lwsp, it is allowed to go past max_width
 
443
                        btype = g_unichar_break_type (*inptr);
 
444
                        while (BreakSpace (btype)) {
 
445
                                if ((glyph = run->font->GetGlyphInfo (*inptr))) {
 
446
                                        if ((advance = glyph->metrics.horiAdvance) > 0.0) {
 
447
                                                if ((prev != 0) && APPLY_KERNING (*inptr))
 
448
                                                        advance += run->font->Kerning (prev, glyph->index);
 
449
                                                else if (glyph->metrics.horiBearingX < 0)
 
450
                                                        advance -= glyph->metrics.horiBearingX;
 
451
                                        }
 
452
                                        
 
453
                                        prev = glyph->index;
 
454
                                        x1 += advance;
 
455
                                }
 
456
                                
 
457
                                inptr++;
 
458
                                
 
459
                                btype = g_unichar_break_type (*inptr);
 
460
                        }
 
461
                        
 
462
                        // trailing lwsp only counts toward 'ActualWidth' extents if underlined
 
463
                        if (run->IsUnderlined ()) {
 
464
                                actual_width = MAX (actual_width, x1);
 
465
                                segment->width = x1 - x0;
 
466
                                width = x1;
 
467
                        }
 
468
                        
 
469
                        if (*inptr == 0)
 
470
                                break;
 
471
                        
 
472
                        segment->advance = x1 - x0;
 
473
                        word = inptr;
 
474
                        wx = x1;
 
475
                        
 
476
                        if (max_width > 0.0 && x1 >= max_width) {
 
477
                        linebreak:
 
478
                                if (segment->start < (word - run->text)) {
 
479
                                        line->segments->Append (segment);
 
480
                                        
 
481
                                        segment = new TextSegment (run, word - run->text);
 
482
                                        start = word;
 
483
                                } else {
 
484
                                        // reuse the segment
 
485
                                }
 
486
                                
 
487
                                line->descend = descend;
 
488
                                line->height = height;
 
489
                                line->width = width;
 
490
                                dy += height;
 
491
                                
 
492
                                lines->Append (line);
 
493
                                actual_height = dy;
 
494
                                
 
495
                                line = new TextLine ();
 
496
                                blank = true;
 
497
                                
 
498
                                underlined = run->IsUnderlined ();
 
499
                                
 
500
                                if (!hints->OverrideLineHeight ()) {
 
501
                                        descend = run->font->Descender ();
 
502
                                        height = run->font->Height ();
 
503
                                }
 
504
                                
 
505
                                width = 0.0;
 
506
                                prev = 0;
 
507
                                x0 = 0.0;
 
508
                                x1 = 0.0;
 
509
                                wx = 0.0;
 
510
                        }
 
511
                        
 
512
                        // append this word onto the line
 
513
                        inptr = word;
 
514
                        btype = g_unichar_break_type (*inptr);
 
515
                        while (*inptr && !BreakSpace (btype)) {
 
516
                                if ((glyph = run->font->GetGlyphInfo (*inptr))) {
 
517
                                        if ((advance = glyph->metrics.horiAdvance) > 0.0) {
 
518
                                                if ((prev != 0) && APPLY_KERNING (*inptr))
 
519
                                                        advance += run->font->Kerning (prev, glyph->index);
 
520
                                                else if (glyph->metrics.horiBearingX < 0)
 
521
                                                        advance -= glyph->metrics.horiBearingX;
 
522
                                        }
 
523
                                        
 
524
                                        prev = glyph->index;
 
525
                                        x1 += advance;
 
526
                                        width = x1;
 
527
                                        
 
528
                                        if (max_width > 0.0 && x1 >= max_width && wx > 0.0)
 
529
                                                goto linebreak;
 
530
                                }
 
531
                                
 
532
                                inptr++;
 
533
                                
 
534
                                btype = g_unichar_break_type (*inptr);
 
535
                        }
 
536
                        
 
537
                        actual_width = MAX (actual_width, x1);
 
538
                        segment->end = inptr - run->text;
 
539
                        segment->width = x1 - x0;
 
540
                        blank = false;
 
541
                } while (*inptr);
 
542
                
 
543
                segment->advance = x1 - x0;
 
544
                line->segments->Append (segment);
 
545
                
 
546
                x0 = x1;
 
547
        }
 
548
        
 
549
        if (line) {
 
550
                line->descend = descend;
 
551
                line->height = height;
 
552
                line->width = width;
 
553
                dy += height;
 
554
                
 
555
                lines->Append (line);
 
556
                actual_height = dy;
 
557
        }
 
558
}
 
559
 
 
560
void
 
561
TextLayout::LayoutNoWrap (TextLayoutHints *hints)
 
562
{
 
563
        double x0 = 0.0, x1 = 0.0, dy = 0.0;
 
564
        register gunichar *inptr;
 
565
        GUnicodeBreakType btype;
 
566
        bool underlined = false;
 
567
        bool clipped = false;
 
568
        TextSegment *segment;
 
569
        double descend = 0.0;
 
570
        double height = 0.0;
 
571
        double width = 0.0;
 
572
        bool blank = true;
 
573
        GlyphInfo *glyph;
 
574
        TextLine *line;
 
575
        double advance;
 
576
        guint32 prev;
 
577
        TextRun *run;
 
578
        
 
579
        if (hints->OverrideLineHeight ())
 
580
                height = hints->GetLineHeight ();
 
581
        
 
582
        line = new TextLine ();
 
583
        for (run = (TextRun *) runs->First (); run; run = (TextRun *) run->next) {
 
584
                if (run->text == NULL) {
 
585
                        // LineBreak
 
586
                        if (blank && !hints->OverrideLineHeight ()) {
 
587
                                descend = run->font->Descender ();
 
588
                                height = run->font->Height ();
 
589
                        }
 
590
                        
 
591
                        line->descend = descend;
 
592
                        line->height = height;
 
593
                        line->width = width;
 
594
                        dy += height;
 
595
                        
 
596
                        lines->Append (line);
 
597
                        
 
598
                        if (run->next) {
 
599
                                line = new TextLine ();
 
600
                        } else {
 
601
                                dy += height;
 
602
                                line = NULL;
 
603
                        }
 
604
                        
 
605
                        actual_height = dy;
 
606
                        underlined = false;
 
607
                        clipped = false;
 
608
                        blank = true;
 
609
                        
 
610
                        if (!hints->OverrideLineHeight ()) {
 
611
                                descend = 0.0;
 
612
                                height = 0.0;
 
613
                        }
 
614
                        
 
615
                        width = 0.0;
 
616
                        x0 = 0.0;
 
617
                        x1 = 0.0;
 
618
                        
 
619
                        continue;
 
620
                } else if (clipped) {
 
621
                        // once we've clipped, we cannot append anymore text to the line
 
622
                        // FIXME: Silverlight 2.0 doesn't seem to clip
 
623
                        continue;
 
624
                }
 
625
                
 
626
                if (!underlined)
 
627
                        underlined = run->IsUnderlined ();
 
628
                
 
629
                if (!hints->OverrideLineHeight ()) {
 
630
                        descend = MIN (descend, run->font->Descender ());
 
631
                        height = MAX (height, run->font->Height ());
 
632
                }
 
633
                
 
634
                segment = new TextSegment (run, 0);
 
635
                inptr = run->text;
 
636
                prev = 0;
 
637
                x1 = x0;
 
638
                
 
639
                do {
 
640
                        // always include the lwsp, it is allowed to go past max_width
 
641
                        btype = g_unichar_break_type (*inptr);
 
642
                        while (BreakSpace (btype)) {
 
643
                                if ((glyph = run->font->GetGlyphInfo (*inptr))) {
 
644
                                        if ((advance = glyph->metrics.horiAdvance) > 0.0) {
 
645
                                                if ((prev != 0) && APPLY_KERNING (*inptr))
 
646
                                                        advance += run->font->Kerning (prev, glyph->index);
 
647
                                                else if (glyph->metrics.horiBearingX < 0)
 
648
                                                        advance -= glyph->metrics.horiBearingX;
 
649
                                        }
 
650
                                        
 
651
                                        prev = glyph->index;
 
652
                                        x1 += advance;
 
653
                                }
 
654
                                
 
655
                                inptr++;
 
656
                                
 
657
                                btype = g_unichar_break_type (*inptr);
 
658
                        }
 
659
                        
 
660
                        // trailing lwsp only counts toward 'ActualWidth' extents if underlined
 
661
                        if (run->IsUnderlined ()) {
 
662
                                actual_width = MAX (actual_width, x1);
 
663
                                segment->width = x1 - x0;
 
664
                                width = x1;
 
665
                        }
 
666
                        
 
667
                        if (*inptr == 0)
 
668
                                break;
 
669
                        
 
670
                        // append this word onto the line
 
671
                        btype = g_unichar_break_type (*inptr);
 
672
                        while (*inptr && !BreakSpace (btype)) {
 
673
                                if ((glyph = run->font->GetGlyphInfo (*inptr))) {
 
674
                                        if ((advance = glyph->metrics.horiAdvance) > 0.0) {
 
675
                                                if ((prev != 0) && APPLY_KERNING (*inptr))
 
676
                                                        advance += run->font->Kerning (prev, glyph->index);
 
677
                                                else if (glyph->metrics.horiBearingX < 0)
 
678
                                                        advance -= glyph->metrics.horiBearingX;
 
679
                                        }
 
680
                                        
 
681
                                        prev = glyph->index;
 
682
                                        x1 += advance;
 
683
                                        width = x1;
 
684
                                }
 
685
                                
 
686
                                inptr++;
 
687
                                
 
688
                                btype = g_unichar_break_type (*inptr);
 
689
                        }
 
690
                        
 
691
                        actual_width = MAX (actual_width, x1);
 
692
                        segment->end = inptr - run->text;
 
693
                        segment->width = x1 - x0;
 
694
                        blank = false;
 
695
                        
 
696
                        if (max_width > 0.0 && x1 >= max_width) {
 
697
                                // cut the remainder of the run unless it is underlined
 
698
                                // (in which case we need to underline trailing lwsp).
 
699
                                if (!run->IsUnderlined ()) {
 
700
                                        clipped = true;
 
701
                                        break;
 
702
                                }
 
703
                        }
 
704
                } while (*inptr);
 
705
                
 
706
                segment->advance = x1 - x0;
 
707
                line->segments->Append (segment);
 
708
                
 
709
                x0 = x1;
 
710
        }
 
711
        
 
712
        if (line) {
 
713
                line->descend = descend;
 
714
                line->height = height;
 
715
                line->width = width;
 
716
                dy += height;
 
717
                
 
718
                lines->Append (line);
 
719
                actual_height = dy;
 
720
        }
 
721
}
 
722
 
 
723
static bool
 
724
isLastWord (TextRun *run, gunichar *word, bool *include_lwsp)
 
725
{
 
726
        register gunichar *inptr = word;
 
727
        
 
728
        // skip to the end of this word
 
729
        while (*inptr && *inptr != ' ')
 
730
                inptr++;
 
731
        
 
732
        // skip over trailing lwsp
 
733
        while (*inptr == ' ')
 
734
                inptr++;
 
735
        
 
736
        if (*inptr != 0)
 
737
                return false;
 
738
        
 
739
        // now we need to check following Runs
 
740
        while (run->next) {
 
741
                run = (TextRun *) run->next;
 
742
                
 
743
                if (!run->text)
 
744
                        return true;
 
745
                
 
746
                inptr = run->text;
 
747
                
 
748
                // skip over lwsp
 
749
                while (*inptr == ' ')
 
750
                        inptr++;
 
751
                
 
752
                if (*inptr != 0)
 
753
                        return false;
 
754
        }
 
755
        
 
756
        *include_lwsp = true;
 
757
        
 
758
        return true;
 
759
}
 
760
 
 
761
 
 
762
struct WordChar {
 
763
        GUnicodeBreakType btype;
 
764
        gunichar *c;
 
765
        double x1;
 
766
};
 
767
 
 
768
 
 
769
/**
 
770
 * Notes: The last 'word' of any line must not be broken (bug in
 
771
 * Silverlight's text layout)
 
772
 **/
 
773
void
 
774
TextLayout::LayoutWrap (TextLayoutHints *hints)
 
775
{
 
776
        double x0 = 0.0, x1 = 0.0, wx = 0.0, dy = 0.0;
 
777
        register gunichar *start, *word, *inptr;
 
778
        GUnicodeBreakType btype;
 
779
        bool include_lwsp = false;
 
780
        bool underlined = false;
 
781
        bool last_word = false;
 
782
        bool in_word = false;
 
783
        TextSegment *segment;
 
784
        double word_end = 0.0;
 
785
        double descend = 0.0;
 
786
        double height = 0.0;
 
787
        double width = 0.0;
 
788
        bool blank = true;
 
789
        GlyphInfo *glyph;
 
790
        TextLine *line;
 
791
        double advance;
 
792
        GArray *array;
 
793
        guint32 prev;
 
794
        TextRun *run;
 
795
        WordChar wc;
 
796
        bool after;
 
797
        guint i;
 
798
        
 
799
        array = g_array_new (false, false, sizeof (WordChar));
 
800
        
 
801
        if (hints->OverrideLineHeight ())
 
802
                height = hints->GetLineHeight ();
 
803
        
 
804
        line = new TextLine ();
 
805
        for (run = (TextRun *) runs->First (); run; run = (TextRun *) run->next) {
 
806
                if (run->text == NULL) {
 
807
                        // LineBreak
 
808
                        if (blank && !hints->OverrideLineHeight ()) {
 
809
                                descend = run->font->Descender ();
 
810
                                height = run->font->Height ();
 
811
                        }
 
812
                        
 
813
                        line->descend = descend;
 
814
                        line->height = height;
 
815
                        line->width = width;
 
816
                        dy += height;
 
817
                        
 
818
                        lines->Append (line);
 
819
                        
 
820
                        if (run->next) {
 
821
                                line = new TextLine ();
 
822
                        } else {
 
823
                                dy += height;
 
824
                                line = NULL;
 
825
                        }
 
826
                        
 
827
                        actual_height = dy;
 
828
                        underlined = false;
 
829
                        last_word = false;
 
830
                        in_word = false;
 
831
                        blank = true;
 
832
                        
 
833
                        if (!hints->OverrideLineHeight ()) {
 
834
                                descend = 0.0;
 
835
                                height = 0.0;
 
836
                        }
 
837
                        
 
838
                        word_end = 0.0;
 
839
                        width = 0.0;
 
840
                        x0 = 0.0;
 
841
                        x1 = 0.0;
 
842
                        
 
843
                        continue;
 
844
                }
 
845
                
 
846
                if (!underlined)
 
847
                        underlined = run->IsUnderlined ();
 
848
                
 
849
                if (!hints->OverrideLineHeight ()) {
 
850
                        descend = MIN (descend, run->font->Descender ());
 
851
                        height = MAX (height, run->font->Height ());
 
852
                }
 
853
                
 
854
                segment = new TextSegment (run, 0);
 
855
#if DEBUG
 
856
                if (debug_flags & RUNTIME_DEBUG_LAYOUT) {
 
857
                        print_run_text ("Laying out Run.Text", run->text, NULL);
 
858
                        print_break_info (run->text);
 
859
                }
 
860
#endif
 
861
                inptr = run->text;
 
862
                prev = 0;
 
863
                x1 = x0;
 
864
                
 
865
                double bearing_adj = 0.0;
 
866
                do {
 
867
                        // always include the lwsp, it is allowed to go past max_width
 
868
                        start = inptr;
 
869
                        btype = g_unichar_break_type (*inptr);
 
870
                        if (in_word && BreakSpace (btype)) {
 
871
                                in_word = false;
 
872
                                word_end = x1;
 
873
                        }
 
874
                        
 
875
                        while (BreakSpace (btype)) {
 
876
                                if ((glyph = run->font->GetGlyphInfo (*inptr))) {
 
877
                                        advance = glyph->metrics.horiAdvance;
 
878
                                        if ((prev != 0) && APPLY_KERNING (*inptr))
 
879
                                                advance += run->font->Kerning (prev, glyph->index);
 
880
                                        else if (glyph->metrics.horiBearingX < 0) {
 
881
                                                bearing_adj = glyph->metrics.horiBearingX;
 
882
                                                advance += bearing_adj;
 
883
                                        }
 
884
                                        
 
885
                                        prev = glyph->index;
 
886
                                        x1 += advance - bearing_adj;
 
887
                                        bearing_adj = 0.0;
 
888
                                }
 
889
                                
 
890
                                inptr++;
 
891
                                
 
892
                                btype = g_unichar_break_type (*inptr);
 
893
                        }
 
894
                        
 
895
                        // trailing lwsp only counts toward 'ActualWidth' extents if underlined
 
896
                        if (run->IsUnderlined () || include_lwsp) {
 
897
                                actual_width = MAX (actual_width, x1);
 
898
                                segment->width = x1 - x0;
 
899
                                word_end = x1;
 
900
                                width = x1;
 
901
                        }
 
902
                        
 
903
                        if (*inptr == 0)
 
904
                                break;
 
905
                        
 
906
                        segment->advance = x1 - x0;
 
907
                        word = inptr;
 
908
                        wx = x1;
 
909
                        
 
910
                        // check to see if this is the last word of the line
 
911
                        last_word = isLastWord (run, word, &include_lwsp);
 
912
                        
 
913
                        if (max_width > 0.0 && x1 >= max_width) {
 
914
                        linebreak:
 
915
                                if (segment->advance > 0.0) {
 
916
                                        line->segments->Append (segment);
 
917
                                        
 
918
                                        segment = new TextSegment (run, word - run->text);
 
919
                                        start = word;
 
920
                                } else {
 
921
                                        // reuse the segment
 
922
                                }
 
923
                                
 
924
                                line->descend = descend;
 
925
                                line->height = height;
 
926
                                line->width = width;
 
927
                                dy += height;
 
928
                                
 
929
                                lines->Append (line);
 
930
                                actual_height = dy;
 
931
                                
 
932
                                line = new TextLine ();
 
933
                                blank = true;
 
934
                                
 
935
                                underlined = run->IsUnderlined ();
 
936
                                
 
937
                                if (!hints->OverrideLineHeight ()) {
 
938
                                        descend = run->font->Descender ();
 
939
                                        height = run->font->Height ();
 
940
                                }
 
941
                                
 
942
                                in_word = false;
 
943
                                word_end = 0.0;
 
944
                                
 
945
                                width = 0.0;
 
946
                                prev = 0;
 
947
                                x0 = 0.0;
 
948
                                x1 = 0.0;
 
949
                                wx = 0.0;
 
950
                        }
 
951
                        
 
952
                        // append this word onto the line
 
953
                        inptr = word;
 
954
                        g_array_set_size (array, 0);
 
955
                        btype = g_unichar_break_type (*inptr);
 
956
                        while (*inptr && btype != G_UNICODE_BREAK_SPACE) {
 
957
                                if (!(glyph = run->font->GetGlyphInfo (*inptr)))
 
958
                                        goto next;
 
959
                                
 
960
                                advance = glyph->metrics.horiAdvance;
 
961
                                if ((prev != 0) && APPLY_KERNING (*inptr))
 
962
                                        advance += run->font->Kerning (prev, glyph->index);
 
963
                                else if (glyph->metrics.horiBearingX < 0) {
 
964
                                        bearing_adj = glyph->metrics.horiBearingX;
 
965
                                        advance += bearing_adj;
 
966
                                }
 
967
                                
 
968
                                if (max_width > 0.0 && (x1 + advance) > max_width) {
 
969
                                        if (wx == 0.0 && inptr > word && !last_word) {
 
970
                                                // break in the middle of a word
 
971
                                                // FIXME: need to respect unicode breaking
 
972
                                                actual_width = MAX (actual_width, x1);
 
973
                                                segment->end = inptr - run->text;
 
974
                                                segment->advance = x1 - x0;
 
975
                                                segment->width = x1 - x0;
 
976
                                                blank = false;
 
977
                                                word = inptr;
 
978
                                                goto linebreak;
 
979
                                        } else if (wx > 0.0) {
 
980
                                                // scan backwards for a char to break after
 
981
                                                i = array->len;
 
982
                                                after = false;
 
983
                                                
 
984
                                                while (i > 0 && !after) {
 
985
                                                        wc = g_array_index (array, WordChar, i - 1);
 
986
                                                        
 
987
                                                        switch (wc.btype) {
 
988
                                                        case G_UNICODE_BREAK_NEXT_LINE:
 
989
                                                        case G_UNICODE_BREAK_UNKNOWN:
 
990
                                                                after = true;
 
991
                                                                break;
 
992
                                                        case G_UNICODE_BREAK_BEFORE_AND_AFTER:
 
993
                                                        case G_UNICODE_BREAK_EXCLAMATION:
 
994
                                                                //case G_UNICODE_BREAK_AFTER:
 
995
                                                                // only break after if there are chars before
 
996
                                                                after = (i > 1);
 
997
                                                                break;
 
998
                                                        case G_UNICODE_BREAK_BEFORE:
 
999
                                                                if (i > 1) {
 
1000
                                                                        // break after the previous char
 
1001
                                                                        wc = g_array_index (array, WordChar, i - 2);
 
1002
                                                                        after = true;
 
1003
                                                                }
 
1004
                                                                break;
 
1005
                                                        case G_UNICODE_BREAK_WORD_JOINER:
 
1006
                                                                // only break if there is nothing before it
 
1007
                                                                after = (i == 1);
 
1008
                                                                break;
 
1009
                                                        default:
 
1010
                                                                // don't break here
 
1011
                                                                break;
 
1012
                                                        }
 
1013
                                                        
 
1014
                                                        i--;
 
1015
                                                }
 
1016
                                                
 
1017
                                                if (after) {
 
1018
                                                        // break after a previous char in the word
 
1019
                                                        inptr = wc.c + 1;
 
1020
                                                        width = wc.x1;
 
1021
                                                        x1 = wc.x1;
 
1022
                                                        
 
1023
                                                        actual_width = MAX (actual_width, x1);
 
1024
                                                        segment->end = inptr - run->text;
 
1025
                                                        segment->advance = x1 - x0;
 
1026
                                                        segment->width = x1 - x0;
 
1027
                                                        blank = false;
 
1028
                                                        word = inptr;
 
1029
                                                        goto linebreak;
 
1030
                                                } else {
 
1031
                                                        // break before this word
 
1032
                                                        segment->advance = wx - x0;
 
1033
                                                        segment->width = wx - x0;
 
1034
                                                        width = word_end;
 
1035
                                                        
 
1036
                                                        goto linebreak;
 
1037
                                                }
 
1038
                                        }
 
1039
                                }
 
1040
                                
 
1041
                                x1 += advance - bearing_adj;
 
1042
                                bearing_adj = 0.0;
 
1043
                                
 
1044
                        next:
 
1045
                                
 
1046
                                if (!run->font->HasGlyph (*inptr))
 
1047
                                        wc.btype = G_UNICODE_BREAK_UNKNOWN;
 
1048
                                else
 
1049
                                        wc.btype = btype;
 
1050
                                
 
1051
                                in_word = true;
 
1052
                                wc.c = inptr;
 
1053
                                wc.x1 = x1;
 
1054
                                width = x1;
 
1055
                                
 
1056
                                g_array_append_val (array, wc);
 
1057
                                
 
1058
                                inptr++;
 
1059
                                
 
1060
                                btype = g_unichar_break_type (*inptr);
 
1061
                        }
 
1062
                        
 
1063
                        actual_width = MAX (actual_width, x1);
 
1064
                        segment->end = inptr - run->text;
 
1065
                        segment->width = x1 - x0;
 
1066
                        blank = false;
 
1067
                } while (*inptr);
 
1068
                
 
1069
                segment->advance = x1 - x0;
 
1070
                line->segments->Append (segment);
 
1071
                
 
1072
                x0 = x1;
 
1073
        }
 
1074
        
 
1075
        if (line) {
 
1076
                line->descend = descend;
 
1077
                line->height = height;
 
1078
                line->width = width;
 
1079
                dy += height;
 
1080
                
 
1081
                lines->Append (line);
 
1082
                actual_height = dy;
 
1083
        }
 
1084
        
 
1085
        g_array_free (array, true);
 
1086
}
 
1087
 
 
1088
#if DEBUG
 
1089
static void
 
1090
print_lines (List *lines)
 
1091
{
 
1092
        TextSegment *segment;
 
1093
        TextLine *line;
 
1094
        GString *str;
 
1095
        int ln = 0;
 
1096
        int i;
 
1097
        
 
1098
        printf ("layout results:\n");
 
1099
        
 
1100
        str = g_string_new ("");
 
1101
        line = (TextLine *) lines->First ();
 
1102
        
 
1103
        while (line) {
 
1104
                printf ("\tline #%d: ", ln);
 
1105
                
 
1106
                segment = (TextSegment *) line->segments->First ();
 
1107
                
 
1108
                while (segment) {
 
1109
                        for (i = segment->start; i < segment->end; i++)
 
1110
                                g_string_append_unichar (str, segment->run->text[i] == 0xA0 ? '_' : segment->run->text[i]);
 
1111
                        
 
1112
                        printf ("\"%s\", ", str->str);
 
1113
                        g_string_truncate (str, 0);
 
1114
                        
 
1115
                        segment = (TextSegment *) segment->next;
 
1116
                }
 
1117
                
 
1118
                printf ("\n");
 
1119
                
 
1120
                line = (TextLine *) line->next;
 
1121
                ln++;
 
1122
        }
 
1123
}
 
1124
#endif
 
1125
 
 
1126
void
 
1127
TextLayout::Layout (TextLayoutHints *hints)
 
1128
{
 
1129
        if (actual_width != -1.0)
 
1130
                return;
 
1131
        
 
1132
        lines->Clear (true);
 
1133
        actual_height = 0.0;
 
1134
        actual_width = 0.0;
 
1135
        
 
1136
        if (!runs || runs->IsEmpty ())
 
1137
                return;
 
1138
        
 
1139
        switch (wrapping) {
 
1140
        case TextWrappingWrapWithOverflow:
 
1141
#if DEBUG
 
1142
                if (debug_flags & RUNTIME_DEBUG_LAYOUT) {
 
1143
                        if (max_width > 0.0)
 
1144
                                printf ("TextLayout::LayoutWrapWithOverflow(%f)\n", max_width);
 
1145
                        else
 
1146
                                printf ("TextLayout::LayoutWrapWithOverflow()\n");
 
1147
                }
 
1148
#endif
 
1149
                LayoutWrapWithOverflow (hints);
 
1150
                break;
 
1151
        case TextWrappingNoWrap:
 
1152
#if DEBUG
 
1153
                if (debug_flags & RUNTIME_DEBUG_LAYOUT) {
 
1154
                        if (max_width > 0.0)
 
1155
                                printf ("TextLayout::LayoutWrapNoWrap(%f)\n", max_width);
 
1156
                        else
 
1157
                                printf ("TextLayout::LayoutNoWrap()\n");
 
1158
                }
 
1159
#endif
 
1160
                LayoutNoWrap (hints);
 
1161
                break;
 
1162
        case TextWrappingWrap:
 
1163
        // Silverlight default is to wrap for invalid values
 
1164
        default:
 
1165
#if DEBUG
 
1166
                if (debug_flags & RUNTIME_DEBUG_LAYOUT) {
 
1167
                        if (max_width > 0.0)
 
1168
                                printf ("TextLayout::LayoutWrap(%f)\n", max_width);
 
1169
                        else
 
1170
                                printf ("TextLayout::LayoutWrap()\n");
 
1171
                }
 
1172
#endif
 
1173
                LayoutWrap (hints);
 
1174
                break;
 
1175
        }
 
1176
        
 
1177
#if DEBUG
 
1178
        if (debug_flags & RUNTIME_DEBUG_LAYOUT) {
 
1179
                print_lines (lines);
 
1180
                printf ("actualWidth = %f, actualHeight = %f\n", actual_width, actual_height);
 
1181
        }
 
1182
#endif
 
1183
        
 
1184
        //bbox_height = actual_height;
 
1185
        //bbox_width = actual_width;
 
1186
}
 
1187
 
 
1188
static inline void
 
1189
RenderLine (cairo_t *cr, const Point &origin, const Point &position, TextLayoutHints *hints, TextLine *line, Brush *default_fg)
 
1190
{
 
1191
        TextFont *font = NULL;
 
1192
        TextDecorations deco;
 
1193
        TextSegment *segment;
 
1194
        const gunichar *text;
 
1195
        guint32 prev = 0;
 
1196
        GlyphInfo *glyph;
 
1197
        moon_path *path;
 
1198
        double x1, y1;
 
1199
        double x0, y0;
 
1200
        Brush *fg;
 
1201
        int size;
 
1202
        int i;
 
1203
        
 
1204
        // set y0 to the line's baseline (descend is a negative value)
 
1205
        y0 = position.y + line->height + line->descend;
 
1206
        x0 = position.x;
 
1207
        
 
1208
        segment = (TextSegment *) line->segments->First ();
 
1209
        
 
1210
        while (segment) {
 
1211
                text = segment->run->text;
 
1212
                deco = segment->run->deco;
 
1213
                font = segment->run->font;
 
1214
                
 
1215
                cairo_save (cr);
 
1216
                cairo_translate (cr, x0, y0 - font->Ascender ());
 
1217
                
 
1218
                // set y1 to the baseline relative to the translation matrix
 
1219
                y1 = font->Ascender ();
 
1220
                x1 = 0.0;
 
1221
                
 
1222
                if (segment->run->fg && *segment->run->fg)
 
1223
                        fg = *segment->run->fg;
 
1224
                else
 
1225
                        fg = default_fg;
 
1226
                
 
1227
                Rect area = Rect (origin.x, origin.y, segment->advance, font->Height ());
 
1228
                fg->SetupBrush (cr, area);
 
1229
                
 
1230
                if (!segment->path) {
 
1231
                        if (font->IsScalable () && segment->start < segment->end) {
 
1232
                                // count how many path data items we'll need to allocate
 
1233
                                for (size = 0, i = segment->start; i < segment->end; i++) {
 
1234
                                        if (!(glyph = font->GetGlyphInfo (text[i])))
 
1235
                                                continue;
 
1236
                                        
 
1237
                                        size += glyph->path->cairo.num_data + 1;
 
1238
                                }
 
1239
                                
 
1240
                                path = moon_path_new (size);
 
1241
                                cairo_new_path (cr);
 
1242
                        } else {
 
1243
                                path = NULL;
 
1244
                        }
 
1245
                        
 
1246
                        for (i = segment->start, prev = 0; i < segment->end; i++) {
 
1247
                                gunichar uc = text[i];
 
1248
                                if (!(glyph = font->GetGlyphInfo (uc)))
 
1249
                                        continue;
 
1250
                                
 
1251
                                if ((prev != 0) && APPLY_KERNING (uc))
 
1252
                                        x1 += font->Kerning (prev, glyph->index);
 
1253
                                else if (glyph->metrics.horiBearingX < 0)
 
1254
                                        x1 += glyph->metrics.horiBearingX;
 
1255
                                
 
1256
                                prev = glyph->index;
 
1257
                                
 
1258
                                if (font->IsScalable ()) {
 
1259
                                        if (path)
 
1260
                                                font->AppendPath (path, glyph, x1, y1);
 
1261
                                        
 
1262
                                        font->Path (cr, glyph, x1, y1);
 
1263
                                } else {
 
1264
                                        font->Render (cr, glyph, x1, y1);
 
1265
                                }
 
1266
                                
 
1267
                                x1 += glyph->metrics.horiAdvance;
 
1268
                        }
 
1269
                        
 
1270
                        if (font->IsScalable () && segment->start < segment->end) {
 
1271
                                moon_close_path (path);
 
1272
                                cairo_close_path (cr);
 
1273
                                segment->path = path;
 
1274
                                cairo_fill (cr);
 
1275
                        }
 
1276
                } else {
 
1277
                        // it is an error to append a path with no data
 
1278
                        if (segment->path->cairo.data)
 
1279
                                cairo_append_path (cr, &segment->path->cairo);
 
1280
                        
 
1281
                        cairo_fill (cr);
 
1282
                }
 
1283
                
 
1284
                if ((deco & TextDecorationsUnderline) && segment->width > 0.0) {
 
1285
                        cairo_antialias_t aa = cairo_get_antialias (cr);
 
1286
                        double thickness = font->UnderlineThickness ();
 
1287
                        double pos = y1 + font->UnderlinePosition ();
 
1288
                        
 
1289
                        cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
 
1290
                        cairo_set_line_width (cr, thickness);
 
1291
                        x1 = segment->width;
 
1292
                        
 
1293
                        cairo_new_path (cr);
 
1294
                        cairo_move_to (cr, 0.0, pos);
 
1295
                        cairo_line_to (cr, x1, pos);
 
1296
                        cairo_stroke (cr);
 
1297
                        
 
1298
                        cairo_set_antialias (cr, aa);
 
1299
                }
 
1300
                
 
1301
                x0 += segment->advance;
 
1302
                
 
1303
                segment = (TextSegment *) segment->next;
 
1304
                cairo_restore (cr);
 
1305
        }
 
1306
}
 
1307
 
 
1308
void
 
1309
TextLayout::Render (cairo_t *cr, const Point &origin, const Point &offset, TextLayoutHints *hints, Brush *default_fg, TextSelection *selection, int caret)
 
1310
{
 
1311
        TextLine *line;
 
1312
        Point position;
 
1313
        double deltax;
 
1314
        
 
1315
        position.y = offset.y;
 
1316
        
 
1317
        Layout (hints);
 
1318
        
 
1319
        line = (TextLine *) lines->First ();
 
1320
        
 
1321
        while (line) {
 
1322
                switch (hints->GetTextAlignment ()) {
 
1323
                case TextAlignmentCenter:
 
1324
                        if (line->width < max_width)
 
1325
                                deltax = (max_width - line->width) / 2.0;
 
1326
                        else
 
1327
                                deltax = 0.0;
 
1328
                        break;
 
1329
                case TextAlignmentRight:
 
1330
                        if (line->width < max_width)
 
1331
                                deltax = max_width - line->width;
 
1332
                        else
 
1333
                                deltax = 0.0;
 
1334
                        break;
 
1335
                default:
 
1336
                        deltax = 0.0;
 
1337
                        break;
 
1338
                }
 
1339
                
 
1340
                position.x = offset.x + deltax;
 
1341
                RenderLine (cr, origin, position, hints, line, default_fg);
 
1342
                position.y += (double) line->height;
 
1343
                
 
1344
                line = (TextLine *) line->next;
 
1345
        }
 
1346
}