1018
1020
* Note that tabs and justification conflict with each other:
1019
1021
* Justification will move content away from its tab-aligned
1022
* positions. The same is true for alignments other than
1023
* %PANGO_ALIGN_LEFT.
1023
1026
pango_layout_set_tabs (PangoLayout *layout,
3369
/* For now we only need the tab position, we assume
3370
* all tabs are left-aligned.
3373
get_tab_pos (PangoLayout *layout,
3375
gboolean *is_default)
3374
get_tab_pos (PangoLayoutLine *line,
3377
PangoTabAlign *alignment,
3379
gboolean *is_default)
3381
PangoLayout *layout = line->layout;
3378
3383
gboolean in_pixels;
3386
if (layout->alignment != PANGO_ALIGN_CENTER)
3388
if (line->is_paragraph_start && layout->indent >= 0)
3389
offset = layout->indent;
3390
else if (!line->is_paragraph_start && layout->indent < 0)
3391
offset = - layout->indent;
3380
3394
if (layout->tabs)
3382
3396
n_tabs = pango_tab_array_get_size (layout->tabs);
3383
3397
in_pixels = pango_tab_array_get_positions_in_pixels (layout->tabs);
3385
*is_default = FALSE;
3398
*is_default = FALSE;
3390
3403
in_pixels = FALSE;
3395
3407
if (index < n_tabs)
3399
pango_tab_array_get_tab (layout->tabs, index, NULL, &pos);
3409
pango_tab_array_get_tab (layout->tabs, index, alignment, tab_pos);
3402
return pos * PANGO_SCALE;
3412
*tab_pos *= PANGO_SCALE;
3414
*decimal = pango_tab_array_get_decimal_point (layout->tabs, index);
3416
else if (n_tabs > 0)
3409
/* Extrapolate tab position, repeating the last tab gap to
3418
/* Extrapolate tab position, repeating the last tab gap to infinity. */
3412
3419
int last_pos = 0;
3413
3420
int next_to_last_pos = 0;
3416
pango_tab_array_get_tab (layout->tabs, n_tabs - 1, NULL, &last_pos);
3423
pango_tab_array_get_tab (layout->tabs, n_tabs - 1, alignment, &last_pos);
3424
*decimal = pango_tab_array_get_decimal_point (layout->tabs, n_tabs - 1);
3418
3426
if (n_tabs > 1)
3419
3427
pango_tab_array_get_tab (layout->tabs, n_tabs - 2, NULL, &next_to_last_pos);
3429
3437
if (last_pos > next_to_last_pos)
3431
tab_width = last_pos - next_to_last_pos;
3438
tab_width = last_pos - next_to_last_pos;
3435
tab_width = layout->tab_width;
3440
tab_width = layout->tab_width;
3438
return last_pos + tab_width * (index - n_tabs + 1);
3442
*tab_pos = last_pos + tab_width * (index - n_tabs + 1);
3442
/* No tab array set, so use default tab width
3444
return layout->tab_width * index;
3446
/* No tab array set, so use default tab width */
3447
*tab_pos = layout->tab_width * index;
3448
*alignment = PANGO_TAB_LEFT;
3493
static void break_state_set_last_tab (ParaBreakState *state,
3494
PangoGlyphString *glyphs,
3497
PangoTabAlign tab_align,
3498
gunichar tab_decimal);
3501
ensure_decimal (PangoLayout *layout)
3503
if (layout->decimal == 0)
3504
layout->decimal = g_utf8_get_char (localeconv ()->decimal_point);
3487
3508
shape_tab (PangoLayoutLine *line,
3509
ParaBreakState *state,
3488
3510
PangoItem *item,
3489
3511
PangoGlyphString *glyphs)
3491
3513
int i, space_width;
3516
PangoTabAlign tab_align;
3517
gunichar tab_decimal;
3493
int current_width = line_width (line);
3519
current_width = line_width (line);
3495
3521
pango_glyph_string_set_size (glyphs, 1);
3498
3524
glyphs->glyphs[0].glyph = PANGO_GET_UNKNOWN_GLYPH ('\t');
3500
3526
glyphs->glyphs[0].glyph = PANGO_GLYPH_EMPTY;
3501
3528
glyphs->glyphs[0].geometry.x_offset = 0;
3502
3529
glyphs->glyphs[0].geometry.y_offset = 0;
3503
3530
glyphs->glyphs[0].attr.is_cluster_start = 1;
3508
3535
ensure_tab_width (line->layout);
3509
3536
space_width = line->layout->tab_width / 8;
3513
3540
gboolean is_default;
3514
int tab_pos = get_tab_pos (line->layout, i, &is_default);
3542
get_tab_pos (line, i, &tab_pos, &tab_align, &tab_decimal, &is_default);
3515
3544
/* Make sure there is at least a space-width of space between
3516
* tab-aligned text and the text before it. However, only do
3545
* tab-aligned text and the text before it. However, only do
3517
3546
* this if no tab array is set on the layout, ie. using default
3518
* tab positions. If use has set tab positions, respect it to
3547
* tab positions. If the user has set tab positions, respect it
3521
3550
if (tab_pos >= current_width + (is_default ? space_width : 1))
3557
if (tab_decimal == 0)
3559
ensure_decimal (line->layout);
3560
tab_decimal = line->layout->decimal;
3563
break_state_set_last_tab (state, glyphs, current_width, tab_pos, tab_align, tab_decimal);
3529
3566
static inline gboolean
3607
3644
int hyphen_width; /* How much space a hyphen will take */
3609
3646
GList *baseline_shifts;
3648
PangoGlyphString *last_tab;
3651
PangoTabAlign last_tab_align;
3652
gunichar last_tab_decimal;
3656
break_state_set_last_tab (ParaBreakState *state,
3657
PangoGlyphString *glyphs,
3660
PangoTabAlign tab_align,
3661
gunichar tab_decimal)
3664
state->last_tab = glyphs;
3665
state->last_tab_width = width;
3666
state->last_tab_pos = tab_pos;
3667
state->last_tab_align = tab_align;
3668
state->last_tab_decimal = tab_decimal;
3612
3671
static gboolean
3613
3672
should_ellipsize_current_line (PangoLayout *layout,
3614
3673
ParaBreakState *state);
3676
get_decimal_prefix_width (PangoItem *item,
3677
PangoGlyphString *glyphs,
3683
PangoGlyphItem glyph_item = { item, glyphs, 0, 0, 0 };
3688
log_widths = g_new (int, item->num_chars);
3690
pango_glyph_item_get_logical_widths (&glyph_item, text, log_widths);
3695
for (i = 0, p = text + item->offset; i < item->num_chars; i++, p = g_utf8_next_char (p))
3697
if (g_utf8_get_char (p) == decimal)
3699
*width += log_widths[i] / 2;
3704
*width += log_widths[i];
3707
g_free (log_widths);
3616
3710
static PangoGlyphString *
3617
3711
shape_run (PangoLayoutLine *line,
3618
3712
ParaBreakState *state,
3622
3716
PangoGlyphString *glyphs = pango_glyph_string_new ();
3624
3718
if (layout->text[item->offset] == '\t')
3625
shape_tab (line, item, glyphs);
3719
shape_tab (line, state, item, glyphs);
3628
3722
PangoShapeFlags shape_flags = PANGO_SHAPE_NONE;
3660
3754
glyphs->glyphs[0].geometry.x_offset += space_left;
3661
3755
glyphs->glyphs[glyphs->num_glyphs - 1].geometry.width += space_right;
3758
if (state->last_tab != NULL)
3762
g_assert (state->last_tab->num_glyphs == 1);
3764
/* Update the width of the current tab to position this run properly */
3766
w = state->last_tab_pos - state->last_tab_width;
3768
if (state->last_tab_align == PANGO_TAB_RIGHT)
3769
w -= pango_glyph_string_get_width (glyphs);
3770
else if (state->last_tab_align == PANGO_TAB_CENTER)
3771
w -= pango_glyph_string_get_width (glyphs) / 2;
3772
else if (state->last_tab_align == PANGO_TAB_DECIMAL)
3777
get_decimal_prefix_width (item, glyphs, layout->text, state->last_tab_decimal, &width, &found);
3782
state->last_tab->glyphs[0].geometry.width = MAX (w, 0);
3680
3801
run->glyphs = glyphs;
3681
3802
else if (last_run && state->log_widths_offset == 0 &&
3682
3803
!(run_item->analysis.flags & PANGO_ANALYSIS_FLAG_NEED_HYPHEN))
3683
run->glyphs = state->glyphs;
3805
run->glyphs = state->glyphs;
3806
state->glyphs = NULL;
3685
3809
run->glyphs = shape_run (line, state, run_item);
3811
if (last_run && state->glyphs)
3689
if (state->log_widths_offset > 0)
3690
pango_glyph_string_free (state->glyphs);
3813
pango_glyph_string_free (state->glyphs);
3691
3814
state->glyphs = NULL;
3694
3817
line->runs = g_slist_prepend (line->runs, run);
3695
3818
line->length += run_item->length;
3820
if (state->last_tab && run->glyphs != state->last_tab)
3822
/* Adjust the tab position so placing further runs will continue to
3823
* maintain the tab placement. In the case of decimal tabs, we are
3824
* done once we've placed the run with the decimal point.
3827
if (state->last_tab_align == PANGO_TAB_RIGHT)
3828
state->last_tab_width += pango_glyph_string_get_width (run->glyphs);
3829
else if (state->last_tab_align == PANGO_TAB_CENTER)
3830
state->last_tab_width += pango_glyph_string_get_width (run->glyphs) / 2;
3831
else if (state->last_tab_align == PANGO_TAB_DECIMAL)
3836
get_decimal_prefix_width (run->item, run->glyphs, line->layout->text, state->last_tab_decimal, &width, &found);
3838
state->last_tab_width += width;
3840
state->last_tab = NULL;
3698
3845
static gboolean
3800
3947
pango_glyph_item_get_logical_widths (&glyph_item, layout->text, state->log_widths);
3950
/* If last_tab is set, we've added a tab and remaining_width has been updated to
3951
* account for its origin width, which is last_tab_pos - last_tab_width. shape_run
3952
* updates the tab width, so we need to consider the delta when comparing
3953
* against remaining_width.
3956
tab_width_change (ParaBreakState *state)
3958
if (state->last_tab)
3959
return state->last_tab->glyphs[0].geometry.width - (state->last_tab_pos - state->last_tab_width);
3803
3964
/* Tries to insert as much as possible of the item at the head of
3804
3965
* state->items onto @line. Five results are possible:
3915
4076
if (state->remaining_width < 0 && !no_break_at_end) /* Wrapping off */
3917
4078
insert_run (line, state, item, NULL, TRUE);
3919
4079
DEBUG1 ("no wrapping, all-fit");
3920
4080
return BREAK_ALL_FIT;
3931
4091
width += state->log_widths[state->log_widths_offset + i];
4094
if (layout->text[item->offset] == '\t')
4096
insert_run (line, state, item, NULL, TRUE);
4097
state->remaining_width -= width;
4098
state->remaining_width = MAX (state->remaining_width, 0);
4100
DEBUG1 ("tab run, all-fit");
4101
return BREAK_ALL_FIT;
3934
4104
if (!no_break_at_end &&
3935
4105
can_break_at (layout, state->start_offset + item->num_chars, wrap))
3946
4116
extra_width = 0;
3948
if ((width + extra_width <= state->remaining_width || (item->num_chars == 1 && !line->runs)) &&
4118
if ((width + extra_width <= state->remaining_width || (item->num_chars == 1 && !line->runs) ||
4119
(state->last_tab && state->last_tab_align != PANGO_TAB_LEFT)) &&
3949
4120
!no_break_at_end)
4122
PangoGlyphString *glyphs;
3951
4124
DEBUG1 ("%d + %d <= %d", width, extra_width, state->remaining_width);
3952
insert_run (line, state, item, NULL, FALSE);
4125
glyphs = shape_run (line, state, item);
3954
width = pango_glyph_string_get_width (((PangoGlyphItem *)(line->runs->data))->glyphs);
4127
width = pango_glyph_string_get_width (glyphs) + tab_width_change (state);
3956
4129
if (width + extra_width <= state->remaining_width || (item->num_chars == 1 && !line->runs))
4131
insert_run (line, state, item, glyphs, TRUE);
3958
4133
state->remaining_width -= width;
3959
4134
state->remaining_width = MAX (state->remaining_width, 0);
3961
/* We passed last_run == FALSE to insert_run, so it did not do this */
3962
pango_glyph_string_free (state->glyphs);
3963
state->glyphs = NULL;
3965
4136
DEBUG1 ("early accept '%.*s', all-fit, remaining %d",
3966
4137
item->length, layout->text + item->offset,
3967
4138
state->remaining_width);
3968
4139
return BREAK_ALL_FIT;
3971
/* if it doesn't fit after shaping, revert and proceed to break the item */
3972
uninsert_run (line);
4142
/* if it doesn't fit after shaping, discard and proceed to break the item */
4143
pango_glyph_string_free (glyphs);
3975
4146
/*** From here on, we look for a way to break item ***/
4055
4226
glyphs = shape_run (line, state, new_item);
4057
new_break_width = pango_glyph_string_get_width (glyphs);
4228
new_break_width = pango_glyph_string_get_width (glyphs) + tab_width_change (state);
4059
4230
if (num_chars > 0 &&
4060
4231
layout->log_attrs[state->start_offset + num_chars - 1].is_white)
4315
4486
state->remaining_width = -1;
4317
4488
state->remaining_width = state->line_width;
4490
state->last_tab = NULL;
4491
state->last_tab_align = PANGO_TAB_LEFT;
4318
4493
DEBUG ("starting to fill line", line, state);
4320
4495
while (state->items)