1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
6
* Moonlight List (moonlight-list@lists.ximian.com)
8
* Copyright 2008 Novell, Inc. (http://www.novell.com)
10
* See the LICENSE file included with the distribution for details.
19
#include "moon-path.h"
25
#define BBOX_MARGIN 1.0
26
#define BBOX_PADDING 2.0
29
* Silverlight does not apply any kerning on a DOT, so we exclude them
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
36
#define APPLY_KERNING(uc) ((uc != 0x002E) && (uc != 0x06D4) && (uc != 3002))
38
TextRun::TextRun (const gunichar *ucs4, int len, TextDecorations deco, TextFontDescription *font, Brush **fg)
40
text = (gunichar *) g_malloc (sizeof (gunichar) * (len + 1));
41
memcpy (text, ucs4, sizeof (gunichar) * len);
44
this->font = font->GetFont ();
49
TextRun::TextRun (const char *utf8, int len, TextDecorations deco, TextFontDescription *font, Brush **fg)
51
register gunichar *s, *d;
53
d = this->text = g_utf8_to_ucs4_fast (utf8, len, NULL);
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)) {
71
this->font = font->GetFont ();
76
TextRun::TextRun (TextFontDescription *font)
78
// This TextRun will represent a LineBreak
79
this->deco = TextDecorationsNone;
80
this->font = font->GetFont ();
93
class TextSegment : public List::Node {
102
TextSegment (TextRun *run, int start);
106
TextSegment::TextSegment (TextRun *run, int start)
116
TextSegment::~TextSegment ()
119
moon_path_destroy (path);
125
class TextLine : public List::Node {
136
TextLine::TextLine ()
138
segments = new List ();
144
TextLine::~TextLine ()
146
segments->Clear (true);
153
TextLayout::TextLayout ()
155
wrapping = TextWrappingNoWrap;
163
actual_height = -1.0;
167
TextLayout::~TextLayout ()
179
TextLayout::GetMaxWidth ()
185
TextLayout::SetMaxWidth (double max)
187
if (max_width == max)
192
actual_height = -1.0;
197
TextLayout::GetMaxHeight ()
203
TextLayout::SetMaxHeight (double max)
205
if (max_height == max)
210
actual_height = -1.0;
215
TextLayout::GetWrapping ()
221
TextLayout::SetWrapping (TextWrapping wrapping)
223
if (this->wrapping == wrapping)
226
this->wrapping = wrapping;
228
actual_height = -1.0;
233
TextLayout::GetTextRuns ()
239
TextLayout::SetTextRuns (List *runs)
242
this->runs->Clear (true);
248
actual_height = -1.0;
253
* TextLayout::GetActualExtents:
257
* Gets the actual width and height extents required for rendering the
261
TextLayout::GetActualExtents (double *width, double *height)
263
*height = actual_height;
264
*width = actual_width;
269
* TextLayout::GetLayoutExtents:
273
* Gets the width and height extents suitable for rendering the text
274
* w/ the current wrapping model.
277
TextLayout::GetLayoutExtents (double *width, double *height)
279
*height = bbox_height;
287
print_run_text (const char *msg, gunichar *start, gunichar *end)
289
GString *str = g_string_new ("");
291
while (*start && (end ? start < end : true)) {
292
g_string_append_unichar (str, *start == 0xA0 ? '_' : *start);
296
printf ("%s = \"%s\"\n", msg, str->str);
298
g_string_free (str, true);
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"
341
print_break_info (gunichar *text)
343
register gunichar *inptr = text;
344
GUnicodeBreakType btype;
348
printf ("Unicode break info:\n");
351
btype = g_unichar_break_type (*inptr);
352
i = g_unichar_to_utf8 (*inptr, c);
355
printf ("\t%u %s: break type = %s\n", *inptr, c,
356
unicode_break_types[btype]);
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)
369
TextLayout::LayoutWrapWithOverflow (TextLayoutHints *hints)
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;
386
if (hints->OverrideLineHeight ())
387
height = hints->GetLineHeight ();
389
line = new TextLine ();
390
for (run = (TextRun *) runs->First (); run; run = (TextRun *) run->next) {
391
if (run->text == NULL) {
393
if (blank && !hints->OverrideLineHeight ()) {
394
descend = run->font->Descender ();
395
height = run->font->Height ();
398
line->descend = descend;
399
line->height = height;
403
lines->Append (line);
406
line = new TextLine ();
416
if (!hints->OverrideLineHeight ()) {
429
underlined = run->IsUnderlined ();
431
if (!hints->OverrideLineHeight ()) {
432
descend = MIN (descend, run->font->Descender ());
433
height = MAX (height, run->font->Height ());
436
segment = new TextSegment (run, 0);
437
inptr = start = run->text;
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;
459
btype = g_unichar_break_type (*inptr);
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;
472
segment->advance = x1 - x0;
476
if (max_width > 0.0 && x1 >= max_width) {
478
if (segment->start < (word - run->text)) {
479
line->segments->Append (segment);
481
segment = new TextSegment (run, word - run->text);
487
line->descend = descend;
488
line->height = height;
492
lines->Append (line);
495
line = new TextLine ();
498
underlined = run->IsUnderlined ();
500
if (!hints->OverrideLineHeight ()) {
501
descend = run->font->Descender ();
502
height = run->font->Height ();
512
// append this word onto the line
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;
528
if (max_width > 0.0 && x1 >= max_width && wx > 0.0)
534
btype = g_unichar_break_type (*inptr);
537
actual_width = MAX (actual_width, x1);
538
segment->end = inptr - run->text;
539
segment->width = x1 - x0;
543
segment->advance = x1 - x0;
544
line->segments->Append (segment);
550
line->descend = descend;
551
line->height = height;
555
lines->Append (line);
561
TextLayout::LayoutNoWrap (TextLayoutHints *hints)
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;
579
if (hints->OverrideLineHeight ())
580
height = hints->GetLineHeight ();
582
line = new TextLine ();
583
for (run = (TextRun *) runs->First (); run; run = (TextRun *) run->next) {
584
if (run->text == NULL) {
586
if (blank && !hints->OverrideLineHeight ()) {
587
descend = run->font->Descender ();
588
height = run->font->Height ();
591
line->descend = descend;
592
line->height = height;
596
lines->Append (line);
599
line = new TextLine ();
610
if (!hints->OverrideLineHeight ()) {
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
627
underlined = run->IsUnderlined ();
629
if (!hints->OverrideLineHeight ()) {
630
descend = MIN (descend, run->font->Descender ());
631
height = MAX (height, run->font->Height ());
634
segment = new TextSegment (run, 0);
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;
657
btype = g_unichar_break_type (*inptr);
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;
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;
688
btype = g_unichar_break_type (*inptr);
691
actual_width = MAX (actual_width, x1);
692
segment->end = inptr - run->text;
693
segment->width = x1 - x0;
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 ()) {
706
segment->advance = x1 - x0;
707
line->segments->Append (segment);
713
line->descend = descend;
714
line->height = height;
718
lines->Append (line);
724
isLastWord (TextRun *run, gunichar *word, bool *include_lwsp)
726
register gunichar *inptr = word;
728
// skip to the end of this word
729
while (*inptr && *inptr != ' ')
732
// skip over trailing lwsp
733
while (*inptr == ' ')
739
// now we need to check following Runs
741
run = (TextRun *) run->next;
749
while (*inptr == ' ')
756
*include_lwsp = true;
763
GUnicodeBreakType btype;
770
* Notes: The last 'word' of any line must not be broken (bug in
771
* Silverlight's text layout)
774
TextLayout::LayoutWrap (TextLayoutHints *hints)
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;
799
array = g_array_new (false, false, sizeof (WordChar));
801
if (hints->OverrideLineHeight ())
802
height = hints->GetLineHeight ();
804
line = new TextLine ();
805
for (run = (TextRun *) runs->First (); run; run = (TextRun *) run->next) {
806
if (run->text == NULL) {
808
if (blank && !hints->OverrideLineHeight ()) {
809
descend = run->font->Descender ();
810
height = run->font->Height ();
813
line->descend = descend;
814
line->height = height;
818
lines->Append (line);
821
line = new TextLine ();
833
if (!hints->OverrideLineHeight ()) {
847
underlined = run->IsUnderlined ();
849
if (!hints->OverrideLineHeight ()) {
850
descend = MIN (descend, run->font->Descender ());
851
height = MAX (height, run->font->Height ());
854
segment = new TextSegment (run, 0);
856
if (debug_flags & RUNTIME_DEBUG_LAYOUT) {
857
print_run_text ("Laying out Run.Text", run->text, NULL);
858
print_break_info (run->text);
865
double bearing_adj = 0.0;
867
// always include the lwsp, it is allowed to go past max_width
869
btype = g_unichar_break_type (*inptr);
870
if (in_word && BreakSpace (btype)) {
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;
886
x1 += advance - bearing_adj;
892
btype = g_unichar_break_type (*inptr);
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;
906
segment->advance = x1 - x0;
910
// check to see if this is the last word of the line
911
last_word = isLastWord (run, word, &include_lwsp);
913
if (max_width > 0.0 && x1 >= max_width) {
915
if (segment->advance > 0.0) {
916
line->segments->Append (segment);
918
segment = new TextSegment (run, word - run->text);
924
line->descend = descend;
925
line->height = height;
929
lines->Append (line);
932
line = new TextLine ();
935
underlined = run->IsUnderlined ();
937
if (!hints->OverrideLineHeight ()) {
938
descend = run->font->Descender ();
939
height = run->font->Height ();
952
// append this word onto the line
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)))
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;
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;
979
} else if (wx > 0.0) {
980
// scan backwards for a char to break after
984
while (i > 0 && !after) {
985
wc = g_array_index (array, WordChar, i - 1);
988
case G_UNICODE_BREAK_NEXT_LINE:
989
case G_UNICODE_BREAK_UNKNOWN:
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
998
case G_UNICODE_BREAK_BEFORE:
1000
// break after the previous char
1001
wc = g_array_index (array, WordChar, i - 2);
1005
case G_UNICODE_BREAK_WORD_JOINER:
1006
// only break if there is nothing before it
1018
// break after a previous char in the word
1023
actual_width = MAX (actual_width, x1);
1024
segment->end = inptr - run->text;
1025
segment->advance = x1 - x0;
1026
segment->width = x1 - x0;
1031
// break before this word
1032
segment->advance = wx - x0;
1033
segment->width = wx - x0;
1041
x1 += advance - bearing_adj;
1046
if (!run->font->HasGlyph (*inptr))
1047
wc.btype = G_UNICODE_BREAK_UNKNOWN;
1056
g_array_append_val (array, wc);
1060
btype = g_unichar_break_type (*inptr);
1063
actual_width = MAX (actual_width, x1);
1064
segment->end = inptr - run->text;
1065
segment->width = x1 - x0;
1069
segment->advance = x1 - x0;
1070
line->segments->Append (segment);
1076
line->descend = descend;
1077
line->height = height;
1078
line->width = width;
1081
lines->Append (line);
1085
g_array_free (array, true);
1090
print_lines (List *lines)
1092
TextSegment *segment;
1098
printf ("layout results:\n");
1100
str = g_string_new ("");
1101
line = (TextLine *) lines->First ();
1104
printf ("\tline #%d: ", ln);
1106
segment = (TextSegment *) line->segments->First ();
1109
for (i = segment->start; i < segment->end; i++)
1110
g_string_append_unichar (str, segment->run->text[i] == 0xA0 ? '_' : segment->run->text[i]);
1112
printf ("\"%s\", ", str->str);
1113
g_string_truncate (str, 0);
1115
segment = (TextSegment *) segment->next;
1120
line = (TextLine *) line->next;
1127
TextLayout::Layout (TextLayoutHints *hints)
1129
if (actual_width != -1.0)
1132
lines->Clear (true);
1133
actual_height = 0.0;
1136
if (!runs || runs->IsEmpty ())
1140
case TextWrappingWrapWithOverflow:
1142
if (debug_flags & RUNTIME_DEBUG_LAYOUT) {
1143
if (max_width > 0.0)
1144
printf ("TextLayout::LayoutWrapWithOverflow(%f)\n", max_width);
1146
printf ("TextLayout::LayoutWrapWithOverflow()\n");
1149
LayoutWrapWithOverflow (hints);
1151
case TextWrappingNoWrap:
1153
if (debug_flags & RUNTIME_DEBUG_LAYOUT) {
1154
if (max_width > 0.0)
1155
printf ("TextLayout::LayoutWrapNoWrap(%f)\n", max_width);
1157
printf ("TextLayout::LayoutNoWrap()\n");
1160
LayoutNoWrap (hints);
1162
case TextWrappingWrap:
1163
// Silverlight default is to wrap for invalid values
1166
if (debug_flags & RUNTIME_DEBUG_LAYOUT) {
1167
if (max_width > 0.0)
1168
printf ("TextLayout::LayoutWrap(%f)\n", max_width);
1170
printf ("TextLayout::LayoutWrap()\n");
1178
if (debug_flags & RUNTIME_DEBUG_LAYOUT) {
1179
print_lines (lines);
1180
printf ("actualWidth = %f, actualHeight = %f\n", actual_width, actual_height);
1184
//bbox_height = actual_height;
1185
//bbox_width = actual_width;
1189
RenderLine (cairo_t *cr, const Point &origin, const Point &position, TextLayoutHints *hints, TextLine *line, Brush *default_fg)
1191
TextFont *font = NULL;
1192
TextDecorations deco;
1193
TextSegment *segment;
1194
const gunichar *text;
1204
// set y0 to the line's baseline (descend is a negative value)
1205
y0 = position.y + line->height + line->descend;
1208
segment = (TextSegment *) line->segments->First ();
1211
text = segment->run->text;
1212
deco = segment->run->deco;
1213
font = segment->run->font;
1216
cairo_translate (cr, x0, y0 - font->Ascender ());
1218
// set y1 to the baseline relative to the translation matrix
1219
y1 = font->Ascender ();
1222
if (segment->run->fg && *segment->run->fg)
1223
fg = *segment->run->fg;
1227
Rect area = Rect (origin.x, origin.y, segment->advance, font->Height ());
1228
fg->SetupBrush (cr, area);
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])))
1237
size += glyph->path->cairo.num_data + 1;
1240
path = moon_path_new (size);
1241
cairo_new_path (cr);
1246
for (i = segment->start, prev = 0; i < segment->end; i++) {
1247
gunichar uc = text[i];
1248
if (!(glyph = font->GetGlyphInfo (uc)))
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;
1256
prev = glyph->index;
1258
if (font->IsScalable ()) {
1260
font->AppendPath (path, glyph, x1, y1);
1262
font->Path (cr, glyph, x1, y1);
1264
font->Render (cr, glyph, x1, y1);
1267
x1 += glyph->metrics.horiAdvance;
1270
if (font->IsScalable () && segment->start < segment->end) {
1271
moon_close_path (path);
1272
cairo_close_path (cr);
1273
segment->path = path;
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);
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 ();
1289
cairo_set_antialias (cr, CAIRO_ANTIALIAS_NONE);
1290
cairo_set_line_width (cr, thickness);
1291
x1 = segment->width;
1293
cairo_new_path (cr);
1294
cairo_move_to (cr, 0.0, pos);
1295
cairo_line_to (cr, x1, pos);
1298
cairo_set_antialias (cr, aa);
1301
x0 += segment->advance;
1303
segment = (TextSegment *) segment->next;
1309
TextLayout::Render (cairo_t *cr, const Point &origin, const Point &offset, TextLayoutHints *hints, Brush *default_fg, TextSelection *selection, int caret)
1315
position.y = offset.y;
1319
line = (TextLine *) lines->First ();
1322
switch (hints->GetTextAlignment ()) {
1323
case TextAlignmentCenter:
1324
if (line->width < max_width)
1325
deltax = (max_width - line->width) / 2.0;
1329
case TextAlignmentRight:
1330
if (line->width < max_width)
1331
deltax = max_width - line->width;
1340
position.x = offset.x + deltax;
1341
RenderLine (cr, origin, position, hints, line, default_fg);
1342
position.y += (double) line->height;
1344
line = (TextLine *) line->next;