1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760
|
/* Pango
* ellipsize.c: Routine to ellipsize layout lines
*
* Copyright (C) 2004 Red Hat Software
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#include "config.h"
#include <string.h>
#include "pango-glyph-item.h"
#include "pango-layout-private.h"
#include "pango-engine-private.h"
#include "pango-impl-utils.h"
typedef struct _EllipsizeState EllipsizeState;
typedef struct _RunInfo RunInfo;
typedef struct _LineIter LineIter;
/* Overall, the way we ellipsize is we grow a "gap" out from an original
* gap center position until:
*
* line_width - gap_width + ellipsize_width <= goal_width
*
* Line: [-------------------------------------------]
* Runs: [------)[---------------)[------------------]
* Gap center: *
* Gap: [----------------------]
*
* The gap center may be at the start or end in which case the gap grows
* in only one direction.
*
* Note the line and last run are logically closed at the end; this allows
* us to use a gap position at x=line_width and still have it be part of
* of a run.
*
* We grow the gap out one "span" at a time, where a span is simply a
* consecutive run of clusters that we can't interrupt with an ellipsis.
*
* When choosing whether to grow the gap at the start or the end, we
* calculate the next span to remove in both directions and see which
* causes the smaller increase in:
*
* MAX (gap_end - gap_center, gap_start - gap_center)
*
* All computations are done using logical order; the ellipsization
* process occurs before the runs are ordered into visual order.
*/
/* Keeps information about a single run */
struct _RunInfo
{
PangoGlyphItem *run;
int start_offset; /* Character offset of run start */
int width; /* Width of run in Pango units */
};
/* Iterator to a position within the ellipsized line */
struct _LineIter
{
PangoGlyphItemIter run_iter;
int run_index;
};
/* State of ellipsization process */
struct _EllipsizeState
{
PangoLayout *layout; /* Layout being ellipsized */
PangoAttrList *attrs; /* Attributes used for itemization/shaping */
RunInfo *run_info; /* Array of information about each run */
int n_runs;
int total_width; /* Original width of line in Pango units */
int gap_center; /* Goal for center of gap */
PangoGlyphItem *ellipsis_run; /* Run created to hold ellipsis */
int ellipsis_width; /* Width of ellipsis, in Pango units */
int ellipsis_is_cjk; /* Whether the first character in the ellipsized
* is wide; this triggers us to try to use a
* mid-line ellipsis instead of a baseline
*/
PangoAttrIterator *line_start_attr; /* Cached PangoAttrIterator for the start of the run */
LineIter gap_start_iter; /* Iteratator pointig to the first cluster in gap */
int gap_start_x; /* x position of start of gap, in Pango units */
PangoAttrIterator *gap_start_attr; /* Attribute iterator pointing to a range containing
* the first character in gap */
LineIter gap_end_iter; /* Iterator pointing to last cluster in gap */
int gap_end_x; /* x position of end of gap, in Pango units */
};
/* Compute global information needed for the itemization process
*/
static void
init_state (EllipsizeState *state,
PangoLayoutLine *line,
PangoAttrList *attrs)
{
GSList *l;
int i;
int start_offset;
state->layout = line->layout;
state->attrs = attrs;
state->n_runs = g_slist_length (line->runs);
state->run_info = g_new (RunInfo, state->n_runs);
start_offset = pango_utf8_strlen (line->layout->text,
line->start_index);
state->total_width = 0;
for (l = line->runs, i = 0; l; l = l->next, i++)
{
PangoGlyphItem *run = l->data;
int width = pango_glyph_string_get_width (run->glyphs);
state->run_info[i].run = run;
state->run_info[i].width = width;
state->run_info[i].start_offset = start_offset;
state->total_width += width;
start_offset += run->item->num_chars;
}
state->ellipsis_run = NULL;
state->ellipsis_is_cjk = FALSE;
state->line_start_attr = NULL;
state->gap_start_attr = NULL;
}
/* Cleanup memory allocation
*/
static void
free_state (EllipsizeState *state)
{
if (state->line_start_attr)
pango_attr_iterator_destroy (state->line_start_attr);
if (state->gap_start_attr)
pango_attr_iterator_destroy (state->gap_start_attr);
g_free (state->run_info);
}
/* Computes the width of a single cluster
*/
static int
get_cluster_width (LineIter *iter)
{
PangoGlyphItemIter *run_iter = &iter->run_iter;
PangoGlyphString *glyphs = run_iter->glyph_item->glyphs;
int width = 0;
int i;
if (run_iter->start_glyph < run_iter->end_glyph) /* LTR */
{
for (i = run_iter->start_glyph; i < run_iter->end_glyph; i++)
width += glyphs->glyphs[i].geometry.width;
}
else /* RTL */
{
for (i = run_iter->start_glyph; i > run_iter->end_glyph; i--)
width += glyphs->glyphs[i].geometry.width;
}
return width;
}
/* Move forward one cluster. Returns %FALSE if we were already at the end
*/
static gboolean
line_iter_next_cluster (EllipsizeState *state,
LineIter *iter)
{
if (!pango_glyph_item_iter_next_cluster (&iter->run_iter))
{
if (iter->run_index == state->n_runs - 1)
return FALSE;
else
{
iter->run_index++;
pango_glyph_item_iter_init_start (&iter->run_iter,
state->run_info[iter->run_index].run,
state->layout->text);
}
}
return TRUE;
}
/* Move backward one cluster. Returns %FALSE if we were already at the end
*/
static gboolean
line_iter_prev_cluster (EllipsizeState *state,
LineIter *iter)
{
if (!pango_glyph_item_iter_prev_cluster (&iter->run_iter))
{
if (iter->run_index == 0)
return FALSE;
else
{
iter->run_index--;
pango_glyph_item_iter_init_end (&iter->run_iter,
state->run_info[iter->run_index].run,
state->layout->text);
}
}
return TRUE;
}
/*
* An ellipsization boundary is defined by two things
*
* - Starts a cluster - forced by structure of code
* - Starts a grapheme - checked here
*
* In the future we'd also like to add a check for cursive connectivity here.
* This should be an addition to #PangoGlyphVisAttr
*
*/
/* Checks if there is a ellipsization boundary before the cluster @iter points to
*/
static gboolean
starts_at_ellipsization_boundary (EllipsizeState *state,
LineIter *iter)
{
RunInfo *run_info = &state->run_info[iter->run_index];
if (iter->run_iter.start_char == 0 && iter->run_index == 0)
return TRUE;
return state->layout->log_attrs[run_info->start_offset + iter->run_iter.start_char].is_cursor_position;
}
/* Checks if there is a ellipsization boundary after the cluster @iter points to
*/
static gboolean
ends_at_ellipsization_boundary (EllipsizeState *state,
LineIter *iter)
{
RunInfo *run_info = &state->run_info[iter->run_index];
if (iter->run_iter.end_char == run_info->run->item->num_chars && iter->run_index == state->n_runs - 1)
return TRUE;
return state->layout->log_attrs[run_info->start_offset + iter->run_iter.end_char + 1].is_cursor_position;
}
/* Helper function to re-itemize a string of text
*/
static PangoItem *
itemize_text (EllipsizeState *state,
const char *text,
PangoAttrList *attrs)
{
GList *items;
PangoItem *item;
items = pango_itemize (state->layout->context, text, 0, strlen (text), attrs, NULL);
g_assert (g_list_length (items) == 1);
item = items->data;
g_list_free (items);
return item;
}
/* Shapes the ellipsis using the font and is_cjk information computed by
* update_ellipsis_shape() from the first character in the gap.
*/
static void
shape_ellipsis (EllipsizeState *state)
{
PangoAttrList *attrs = pango_attr_list_new ();
GSList *run_attrs;
PangoItem *item;
PangoGlyphString *glyphs;
GSList *l;
PangoAttribute *fallback;
const char *ellipsis_text;
int i;
/* Create/reset state->ellipsis_run
*/
if (!state->ellipsis_run)
{
state->ellipsis_run = g_slice_new (PangoGlyphItem);
state->ellipsis_run->glyphs = pango_glyph_string_new ();
state->ellipsis_run->item = NULL;
}
if (state->ellipsis_run->item)
{
pango_item_free (state->ellipsis_run->item);
state->ellipsis_run->item = NULL;
}
/* Create an attribute list
*/
run_attrs = pango_attr_iterator_get_attrs (state->gap_start_attr);
for (l = run_attrs; l; l = l->next)
{
PangoAttribute *attr = l->data;
attr->start_index = 0;
attr->end_index = G_MAXINT;
pango_attr_list_insert (attrs, attr);
}
g_slist_free (run_attrs);
fallback = pango_attr_fallback_new (FALSE);
fallback->start_index = 0;
fallback->end_index = G_MAXINT;
pango_attr_list_insert (attrs, fallback);
/* First try using a specific ellipsis character in the best matching font
*/
if (state->ellipsis_is_cjk)
ellipsis_text = "\342\213\257"; /* U+22EF: MIDLINE HORIZONTAL ELLIPSIS, used for CJK */
else
ellipsis_text = "\342\200\246"; /* U+2026: HORIZONTAL ELLIPSIS */
item = itemize_text (state, ellipsis_text, attrs);
/* If that fails we use "..." in the first matching font
*/
if (!item->analysis.font ||
!_pango_engine_shape_covers (item->analysis.shape_engine, item->analysis.font,
item->analysis.language, g_utf8_get_char (ellipsis_text)))
{
pango_item_free (item);
/* Modify the fallback iter while it is inside the PangoAttrList; Don't try this at home
*/
((PangoAttrInt *)fallback)->value = TRUE;
ellipsis_text = "...";
item = itemize_text (state, ellipsis_text, attrs);
}
pango_attr_list_unref (attrs);
state->ellipsis_run->item = item;
/* Now shape
*/
glyphs = state->ellipsis_run->glyphs;
pango_shape (ellipsis_text, strlen (ellipsis_text),
&item->analysis, glyphs);
state->ellipsis_width = 0;
for (i = 0; i < glyphs->num_glyphs; i++)
state->ellipsis_width += glyphs->glyphs[i].geometry.width;
}
/* Helper function to advance a PangoAttrIterator to a particular
* byte index.
*/
static void
advance_iterator_to (PangoAttrIterator *iter,
int new_index)
{
int start, end;
do
{
pango_attr_iterator_range (iter, &start, &end);
if (end > new_index)
break;
}
while (pango_attr_iterator_next (iter));
}
/* Updates the shaping of the ellipsis if necessary when we move the
* position of the start of the gap.
*
* The shaping of the ellipsis is determined by two things:
*
* - The font attributes applied to the first character in the gap
* - Whether the first character in the gap is wide or not. If the
* first character is wide, then we assume that we are ellipsizing
* East-Asian text, so prefer a mid-line ellipsizes to a baseline
* ellipsis, since that's typical practice for Chinese/Japanese/Korean.
*/
static void
update_ellipsis_shape (EllipsizeState *state)
{
gboolean recompute = FALSE;
gunichar start_wc;
gboolean is_cjk;
/* Unfortunately, we can only advance PangoAttrIterator forward; so each
* time we back up we need to go forward to find the new position. To make
* this not utterly slow, we cache an iterator at the start of the line
*/
if (!state->line_start_attr)
{
state->line_start_attr = pango_attr_list_get_iterator (state->attrs);
advance_iterator_to (state->line_start_attr, state->run_info[0].run->item->offset);
}
if (state->gap_start_attr)
{
/* See if the current attribute range contains the new start position
*/
int start, end;
pango_attr_iterator_range (state->gap_start_attr, &start, &end);
if (state->gap_start_iter.run_iter.start_index < start)
{
pango_attr_iterator_destroy (state->gap_start_attr);
state->gap_start_attr = NULL;
}
}
/* Check whether we need to recompute the ellipsis because of new font attributes
*/
if (!state->gap_start_attr)
{
state->gap_start_attr = pango_attr_iterator_copy (state->line_start_attr);
advance_iterator_to (state->gap_start_attr,
state->run_info[state->gap_start_iter.run_index].run->item->offset);
recompute = TRUE;
}
/* Check whether we need to recompute the ellipsis because we switch from CJK to not
* or vice-versa
*/
start_wc = g_utf8_get_char (state->layout->text + state->gap_start_iter.run_iter.start_index);
is_cjk = g_unichar_iswide (start_wc);
if (is_cjk != state->ellipsis_is_cjk)
{
state->ellipsis_is_cjk = is_cjk;
recompute = TRUE;
}
if (recompute)
shape_ellipsis (state);
}
/* Computes the position of the gap center and finds the smallest span containing it
*/
static void
find_initial_span (EllipsizeState *state)
{
PangoGlyphItem *glyph_item;
PangoGlyphItemIter *run_iter;
gboolean have_cluster;
int i;
int x;
int cluster_width;
switch (state->layout->ellipsize)
{
case PANGO_ELLIPSIZE_NONE:
default:
g_assert_not_reached ();
case PANGO_ELLIPSIZE_START:
state->gap_center = 0;
break;
case PANGO_ELLIPSIZE_MIDDLE:
state->gap_center = state->total_width / 2;
break;
case PANGO_ELLIPSIZE_END:
state->gap_center = state->total_width;
break;
}
/* Find the run containing the gap center
*/
x = 0;
for (i = 0; i < state->n_runs; i++)
{
if (x + state->run_info[i].width > state->gap_center)
break;
x += state->run_info[i].width;
}
if (i == state->n_runs) /* Last run is a closed interval, so back off one run */
{
i--;
x -= state->run_info[i].width;
}
/* Find the cluster containing the gap center
*/
state->gap_start_iter.run_index = i;
run_iter = &state->gap_start_iter.run_iter;
glyph_item = state->run_info[i].run;
cluster_width = 0; /* Quiet GCC, the line must have at least one cluster */
for (have_cluster = pango_glyph_item_iter_init_start (run_iter, glyph_item, state->layout->text);
have_cluster;
have_cluster = pango_glyph_item_iter_next_cluster (run_iter))
{
cluster_width = get_cluster_width (&state->gap_start_iter);
if (x + cluster_width > state->gap_center)
break;
x += cluster_width;
}
if (!have_cluster) /* Last cluster is a closed interval, so back off one cluster */
x -= cluster_width;
state->gap_end_iter = state->gap_start_iter;
state->gap_start_x = x;
state->gap_end_x = x + cluster_width;
/* Expand the gap to a full span
*/
while (!starts_at_ellipsization_boundary (state, &state->gap_start_iter))
{
line_iter_prev_cluster (state, &state->gap_start_iter);
state->gap_start_x -= get_cluster_width (&state->gap_start_iter);
}
while (!ends_at_ellipsization_boundary (state, &state->gap_end_iter))
{
line_iter_next_cluster (state, &state->gap_end_iter);
state->gap_end_x += get_cluster_width (&state->gap_end_iter);
}
update_ellipsis_shape (state);
}
/* Removes one run from the start or end of the gap. Returns FALSE
* if there's nothing left to remove in either direction.
*/
static gboolean
remove_one_span (EllipsizeState *state)
{
LineIter new_gap_start_iter;
LineIter new_gap_end_iter;
int new_gap_start_x;
int new_gap_end_x;
int width;
/* Find one span backwards and forward from the gap
*/
new_gap_start_iter = state->gap_start_iter;
new_gap_start_x = state->gap_start_x;
do
{
if (!line_iter_prev_cluster (state, &new_gap_start_iter))
break;
width = get_cluster_width (&new_gap_start_iter);
new_gap_start_x -= width;
}
while (!starts_at_ellipsization_boundary (state, &new_gap_start_iter) ||
width == 0);
new_gap_end_iter = state->gap_end_iter;
new_gap_end_x = state->gap_end_x;
do
{
if (!line_iter_next_cluster (state, &new_gap_end_iter))
break;
width = get_cluster_width (&new_gap_end_iter);
new_gap_end_x += width;
}
while (!ends_at_ellipsization_boundary (state, &new_gap_end_iter) ||
width == 0);
if (state->gap_end_x == new_gap_end_x && state->gap_start_x == new_gap_start_x)
return FALSE;
/* In the case where we could remove a span from either end of the
* gap, we look at which causes the smaller increase in the
* MAX (gap_end - gap_center, gap_start - gap_center)
*/
if (state->gap_end_x == new_gap_end_x ||
(state->gap_start_x != new_gap_start_x &&
state->gap_center - new_gap_start_x < new_gap_end_x - state->gap_center))
{
state->gap_start_iter = new_gap_start_iter;
state->gap_start_x = new_gap_start_x;
update_ellipsis_shape (state);
}
else
{
state->gap_end_iter = new_gap_end_iter;
state->gap_end_x = new_gap_end_x;
}
return TRUE;
}
/* Fixes up the properties of the ellipsis run once we've determined the final extents
* of the gap
*/
static void
fixup_ellipsis_run (EllipsizeState *state)
{
PangoGlyphString *glyphs = state->ellipsis_run->glyphs;
PangoItem *item = state->ellipsis_run->item;
int level;
int i;
/* Make the entire glyphstring into a single logical cluster */
for (i = 0; i < glyphs->num_glyphs; i++)
{
glyphs->log_clusters[i] = 0;
glyphs->glyphs[i].attr.is_cluster_start = FALSE;
}
glyphs->glyphs[0].attr.is_cluster_start = TRUE;
/* Fix up the item to point to the entire elided text */
item->offset = state->gap_start_iter.run_iter.start_index;
item->length = state->gap_end_iter.run_iter.end_index - item->offset;
item->num_chars = pango_utf8_strlen (state->layout->text + item->offset, item->length);
/* The level for the item is the minimum level of the elided text */
level = G_MAXINT;
for (i = state->gap_start_iter.run_index; i <= state->gap_end_iter.run_index; i++)
level = MIN (level, state->run_info[i].run->item->analysis.level);
item->analysis.level = level;
item->analysis.flags |= PANGO_ANALYSIS_FLAG_IS_ELLIPSIS;
}
/* Computes the new list of runs for the line
*/
static GSList *
get_run_list (EllipsizeState *state)
{
PangoGlyphItem *partial_start_run = NULL;
PangoGlyphItem *partial_end_run = NULL;
GSList *result = NULL;
RunInfo *run_info;
PangoGlyphItemIter *run_iter;
int i;
/* We first cut out the pieces of the starting and ending runs we want to
* preserve; we do the end first in case the end and the start are
* the same. Doing the start first would disturb the indices for the end.
*/
run_info = &state->run_info[state->gap_end_iter.run_index];
run_iter = &state->gap_end_iter.run_iter;
if (run_iter->end_char != run_info->run->item->num_chars)
{
partial_end_run = run_info->run;
run_info->run = pango_glyph_item_split (run_info->run, state->layout->text,
run_iter->end_index - run_info->run->item->offset);
}
run_info = &state->run_info[state->gap_start_iter.run_index];
run_iter = &state->gap_start_iter.run_iter;
if (run_iter->start_char != 0)
{
partial_start_run = pango_glyph_item_split (run_info->run, state->layout->text,
run_iter->start_index - run_info->run->item->offset);
}
/* Now assemble the new list of runs
*/
for (i = 0; i < state->gap_start_iter.run_index; i++)
result = g_slist_prepend (result, state->run_info[i].run);
if (partial_start_run)
result = g_slist_prepend (result, partial_start_run);
result = g_slist_prepend (result, state->ellipsis_run);
if (partial_end_run)
result = g_slist_prepend (result, partial_end_run);
for (i = state->gap_end_iter.run_index + 1; i < state->n_runs; i++)
result = g_slist_prepend (result, state->run_info[i].run);
/* And free the ones we didn't use
*/
for (i = state->gap_start_iter.run_index; i <= state->gap_end_iter.run_index; i++)
pango_glyph_item_free (state->run_info[i].run);
return g_slist_reverse (result);
}
/* Computes the width of the line as currently ellipsized
*/
static int
current_width (EllipsizeState *state)
{
return state->total_width - (state->gap_end_x - state->gap_start_x) + state->ellipsis_width;
}
/**
* _pango_layout_line_ellipsize:
* @line: a #PangoLayoutLine
* @attrs: Attributes being used for itemization/shaping
*
* Given a #PangoLayoutLine with the runs still in logical order, ellipsize
* it according the layout's policy to fit within the set width of the layout.
*
* Return value: whether the line had to be ellipsized
**/
gboolean
_pango_layout_line_ellipsize (PangoLayoutLine *line,
PangoAttrList *attrs,
int goal_width)
{
EllipsizeState state;
gboolean is_ellipsized = FALSE;
g_return_val_if_fail (line->layout->ellipsize != PANGO_ELLIPSIZE_NONE && goal_width >= 0, is_ellipsized);
init_state (&state, line, attrs);
if (state.total_width <= goal_width)
goto out;
find_initial_span (&state);
while (current_width (&state) > goal_width)
{
if (!remove_one_span (&state))
break;
}
fixup_ellipsis_run (&state);
g_slist_free (line->runs);
line->runs = get_run_list (&state);
is_ellipsized = TRUE;
out:
free_state (&state);
return is_ellipsized;
}
|