📄 ellipsize.c
字号:
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 voidupdate_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 voidfind_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 gbooleanremove_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 voidfixup_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 = g_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;}/* 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 intcurrent_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){ EllipsizeState state; int goal_width; gboolean is_ellipsized = FALSE; if (line->layout->ellipsize == PANGO_ELLIPSIZE_NONE || line->layout->width < 0) goto ret; init_state (&state, line, attrs); goal_width = state.layout->width; if (state.layout->indent > 0 && state.layout->alignment != PANGO_ALIGN_CENTER) goal_width -= state.layout->indent; 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); ret: return is_ellipsized;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -