summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOwen Taylor <otaylor@redhat.com>2004-07-14 22:17:36 +0000
committerOwen Taylor <otaylor@src.gnome.org>2004-07-14 22:17:36 +0000
commite8451d0463303bbaa3ba3d840d7985f9120ba58a (patch)
tree9ce3e64b7191ea3dc352e101ccd552231e9bbc59
parent731bd56653de86e2298cd8b04c320fca82bb2f9f (diff)
downloadpango-e8451d0463303bbaa3ba3d840d7985f9120ba58a.tar.gz
Add PangoEllipsizeMode, pango_layout_set_ellipsize(), implement. (#59071)
Wed Jul 14 17:47:38 2004 Owen Taylor <otaylor@redhat.com> * pango/pango-layout.[ch] pango/ellipsize.c pango/Makefile.am: Add PangoEllipsizeMode, pango_layout_set_ellipsize(), implement. (#59071) * pango/pango-layout-private.h pango/pango-layout.c: Move PangoLayout structure into a separate header file. * pango/pango-glyph-item.[ch]: Add pango_glyph_item_free(). * pango/pango-glyph-item-private.h pango/pango-glyph-item.c: Internally export the PangoGlyphItemIter functionality. * examples/renderdemo.[ch]: Add --ellipsize option.
-rw-r--r--ChangeLog16
-rw-r--r--ChangeLog.pre-1-1016
-rw-r--r--ChangeLog.pre-1-616
-rw-r--r--ChangeLog.pre-1-816
-rw-r--r--docs/Makefile.am2
-rw-r--r--docs/pango-sections.txt8
-rw-r--r--docs/tmpl/glyphs.sgml15
-rw-r--r--docs/tmpl/layout.sgml35
-rw-r--r--docs/tmpl/main.sgml9
-rw-r--r--docs/tmpl/opentype.sgml9
-rw-r--r--examples/renderdemo.c24
-rw-r--r--examples/renderdemo.h1
-rw-r--r--pango/Makefile.am3
-rw-r--r--pango/ellipsize.c750
-rw-r--r--pango/pango-glyph-item-private.h64
-rw-r--r--pango/pango-glyph-item.c215
-rw-r--r--pango/pango-glyph-item.h1
-rw-r--r--pango/pango-layout-private.h71
-rw-r--r--pango/pango-layout.c159
-rw-r--r--pango/pango-layout.h24
20 files changed, 1350 insertions, 104 deletions
diff --git a/ChangeLog b/ChangeLog
index 53530042..61ad0acc 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,19 @@
+Wed Jul 14 17:47:38 2004 Owen Taylor <otaylor@redhat.com>
+
+ * pango/pango-layout.[ch] pango/ellipsize.c pango/Makefile.am:
+ Add PangoEllipsizeMode, pango_layout_set_ellipsize(), implement.
+ (#59071)
+
+ * pango/pango-layout-private.h pango/pango-layout.c:
+ Move PangoLayout structure into a separate header file.
+
+ * pango/pango-glyph-item.[ch]: Add pango_glyph_item_free().
+
+ * pango/pango-glyph-item-private.h pango/pango-glyph-item.c:
+ Internally export the PangoGlyphItemIter functionality.
+
+ * examples/renderdemo.[ch]: Add --ellipsize option.
+
Wed Jul 14 17:42:49 2004 Owen Taylor <otaylor@redhat.com>
* pango/pango-script.c (pango_language_includes_script):
diff --git a/ChangeLog.pre-1-10 b/ChangeLog.pre-1-10
index 53530042..61ad0acc 100644
--- a/ChangeLog.pre-1-10
+++ b/ChangeLog.pre-1-10
@@ -1,3 +1,19 @@
+Wed Jul 14 17:47:38 2004 Owen Taylor <otaylor@redhat.com>
+
+ * pango/pango-layout.[ch] pango/ellipsize.c pango/Makefile.am:
+ Add PangoEllipsizeMode, pango_layout_set_ellipsize(), implement.
+ (#59071)
+
+ * pango/pango-layout-private.h pango/pango-layout.c:
+ Move PangoLayout structure into a separate header file.
+
+ * pango/pango-glyph-item.[ch]: Add pango_glyph_item_free().
+
+ * pango/pango-glyph-item-private.h pango/pango-glyph-item.c:
+ Internally export the PangoGlyphItemIter functionality.
+
+ * examples/renderdemo.[ch]: Add --ellipsize option.
+
Wed Jul 14 17:42:49 2004 Owen Taylor <otaylor@redhat.com>
* pango/pango-script.c (pango_language_includes_script):
diff --git a/ChangeLog.pre-1-6 b/ChangeLog.pre-1-6
index 53530042..61ad0acc 100644
--- a/ChangeLog.pre-1-6
+++ b/ChangeLog.pre-1-6
@@ -1,3 +1,19 @@
+Wed Jul 14 17:47:38 2004 Owen Taylor <otaylor@redhat.com>
+
+ * pango/pango-layout.[ch] pango/ellipsize.c pango/Makefile.am:
+ Add PangoEllipsizeMode, pango_layout_set_ellipsize(), implement.
+ (#59071)
+
+ * pango/pango-layout-private.h pango/pango-layout.c:
+ Move PangoLayout structure into a separate header file.
+
+ * pango/pango-glyph-item.[ch]: Add pango_glyph_item_free().
+
+ * pango/pango-glyph-item-private.h pango/pango-glyph-item.c:
+ Internally export the PangoGlyphItemIter functionality.
+
+ * examples/renderdemo.[ch]: Add --ellipsize option.
+
Wed Jul 14 17:42:49 2004 Owen Taylor <otaylor@redhat.com>
* pango/pango-script.c (pango_language_includes_script):
diff --git a/ChangeLog.pre-1-8 b/ChangeLog.pre-1-8
index 53530042..61ad0acc 100644
--- a/ChangeLog.pre-1-8
+++ b/ChangeLog.pre-1-8
@@ -1,3 +1,19 @@
+Wed Jul 14 17:47:38 2004 Owen Taylor <otaylor@redhat.com>
+
+ * pango/pango-layout.[ch] pango/ellipsize.c pango/Makefile.am:
+ Add PangoEllipsizeMode, pango_layout_set_ellipsize(), implement.
+ (#59071)
+
+ * pango/pango-layout-private.h pango/pango-layout.c:
+ Move PangoLayout structure into a separate header file.
+
+ * pango/pango-glyph-item.[ch]: Add pango_glyph_item_free().
+
+ * pango/pango-glyph-item-private.h pango/pango-glyph-item.c:
+ Internally export the PangoGlyphItemIter functionality.
+
+ * examples/renderdemo.[ch]: Add --ellipsize option.
+
Wed Jul 14 17:42:49 2004 Owen Taylor <otaylor@redhat.com>
* pango/pango-script.c (pango_language_includes_script):
diff --git a/docs/Makefile.am b/docs/Makefile.am
index aaa468f3..aefc843f 100644
--- a/docs/Makefile.am
+++ b/docs/Makefile.am
@@ -27,6 +27,8 @@ IGNORE_HFILES= \
modules.h \
pango-engine-private.h \
pango-impl-utils.h \
+ pango-glyph-item-private.h \
+ pango-layout-private.h \
pango-script-table.h \
pango-utils.h \
pangofc-private.h \
diff --git a/docs/pango-sections.txt b/docs/pango-sections.txt
index 5a55d943..cae925bd 100644
--- a/docs/pango-sections.txt
+++ b/docs/pango-sections.txt
@@ -18,6 +18,7 @@ pango_reorder_items
<SUBSECTION>
pango_context_new
pango_context_set_font_map
+pango_context_get_font_map
pango_context_get_font_description
pango_context_set_font_description
pango_context_get_language
@@ -95,9 +96,11 @@ pango_glyph_string_get_logical_widths
pango_glyph_item_split
pango_glyph_item_apply_attrs
pango_glyph_item_letter_space
+pango_glyph_item_free
<SUBSECTION Private>
pango_glyph_string_get_type
+pango_matrix_get_type
</SECTION>
<SECTION>
@@ -402,6 +405,10 @@ pango_layout_set_wrap
pango_layout_get_wrap
PangoWrapMode
PANGO_TYPE_WRAP_MODE
+pango_layout_set_ellipsize
+pango_layout_get_ellipsize
+PangoEllipsizeMode
+PANGO_TYPE_ELLIPSIZE_MODE
pango_layout_set_indent
pango_layout_get_indent
pango_layout_get_spacing
@@ -479,6 +486,7 @@ pango_layout_get_type
pango_layout_iter_get_type
pango_alignment_get_type
pango_wrap_mode_get_type
+pango_ellipsize_mode_get_type
</SECTION>
<SECTION>
diff --git a/docs/tmpl/glyphs.sgml b/docs/tmpl/glyphs.sgml
index 435d939d..35b4fbe8 100644
--- a/docs/tmpl/glyphs.sgml
+++ b/docs/tmpl/glyphs.sgml
@@ -110,6 +110,13 @@ horizontal origin.
@x0:
@y0:
+<!-- ##### MACRO PANGO_TYPE_MATRIX ##### -->
+<para>
+
+</para>
+
+
+
<!-- ##### MACRO PANGO_MATRIX_INIT ##### -->
<para>
@@ -404,3 +411,11 @@ The GObject type for #PangoGlyphString.
@letter_spacing:
+<!-- ##### FUNCTION pango_glyph_item_free ##### -->
+<para>
+
+</para>
+
+@glyph_item:
+
+
diff --git a/docs/tmpl/layout.sgml b/docs/tmpl/layout.sgml
index 90b228e5..2f9c0398 100644
--- a/docs/tmpl/layout.sgml
+++ b/docs/tmpl/layout.sgml
@@ -215,6 +215,41 @@ The GObject type for #PangoWrapMode.
+<!-- ##### FUNCTION pango_layout_set_ellipsize ##### -->
+<para>
+
+</para>
+
+@layout:
+@ellipsize:
+
+
+<!-- ##### FUNCTION pango_layout_get_ellipsize ##### -->
+<para>
+
+</para>
+
+@layout:
+@Returns:
+
+
+<!-- ##### ENUM PangoEllipsizeMode ##### -->
+<para>
+
+</para>
+
+@PANGO_ELLIPSIZE_NONE:
+@PANGO_ELLIPSIZE_START:
+@PANGO_ELLIPSIZE_MIDDLE:
+@PANGO_ELLIPSIZE_END:
+
+<!-- ##### MACRO PANGO_TYPE_ELLIPSIZE_MODE ##### -->
+<para>
+The GObject type for #PangoEllipsizeMode.
+</para>
+
+
+
<!-- ##### FUNCTION pango_layout_set_indent ##### -->
<para>
diff --git a/docs/tmpl/main.sgml b/docs/tmpl/main.sgml
index 904853e1..669003ec 100644
--- a/docs/tmpl/main.sgml
+++ b/docs/tmpl/main.sgml
@@ -160,6 +160,15 @@ The GObject type for #PangoDirection.
@font_map:
+<!-- ##### FUNCTION pango_context_get_font_map ##### -->
+<para>
+
+</para>
+
+@context:
+@Returns:
+
+
<!-- ##### FUNCTION pango_context_get_font_description ##### -->
<para>
diff --git a/docs/tmpl/opentype.sgml b/docs/tmpl/opentype.sgml
index f77eb3b4..9de20ae6 100644
--- a/docs/tmpl/opentype.sgml
+++ b/docs/tmpl/opentype.sgml
@@ -195,6 +195,15 @@ identify the various OpenType tables in the
@cluster:
+<!-- ##### FUNCTION pango_ot_buffer_set_rtl ##### -->
+<para>
+
+</para>
+
+@buffer:
+@rtl:
+
+
<!-- ##### FUNCTION pango_ot_buffer_set_zero_width_marks ##### -->
<para>
diff --git a/examples/renderdemo.c b/examples/renderdemo.c
index cc18b725..78aa4037 100644
--- a/examples/renderdemo.c
+++ b/examples/renderdemo.c
@@ -54,6 +54,7 @@ char *opt_text = NULL;
gboolean opt_waterfall = FALSE;
int opt_width = -1;
int opt_indent = 0;
+PangoEllipsizeMode opt_ellipsize = PANGO_ELLIPSIZE_NONE;
/* Text (or markup) to render */
char *text;
@@ -101,7 +102,8 @@ make_layout(PangoContext *context,
pango_layout_set_text (layout, text, -1);
pango_layout_set_auto_dir (layout, opt_auto_dir);
-
+ pango_layout_set_ellipsize (layout, opt_ellipsize);
+
font_description = get_font_description ();
if (size > 0)
pango_font_description_set_size (font_description, size * PANGO_SCALE);
@@ -298,6 +300,24 @@ show_help (ArgContext *context,
}
void
+parse_ellipsis (ArgContext *arg_context,
+ const char *name,
+ const char *arg,
+ gpointer data)
+{
+ static GEnumClass *class = NULL;
+
+ if (!class)
+ class = g_type_class_ref (PANGO_TYPE_ELLIPSIZE_MODE);
+
+ GEnumValue *value = g_enum_get_value_by_nick (class, arg);
+ if (!value)
+ fail ("--ellipsize option must be one of none/start/middle/end");
+
+ opt_ellipsize = value->value;
+}
+
+void
parse_options (int argc, char *argv[])
{
static const ArgDesc args[] = {
@@ -307,6 +327,8 @@ parse_options (int argc, char *argv[])
ARG_BOOL, &opt_display },
{ "dpi", "Set the dpi'",
ARG_INT, &opt_dpi },
+ { "ellipsize", "Ellipsization mode [=none/start/middle/end]",
+ ARG_CALLBACK, NULL, parse_ellipsis },
{ "font", "Set the font name",
ARG_STRING, &opt_font },
{ "header", "Display the options in the output",
diff --git a/examples/renderdemo.h b/examples/renderdemo.h
index 191efe2c..94a27f49 100644
--- a/examples/renderdemo.h
+++ b/examples/renderdemo.h
@@ -53,3 +53,4 @@ extern char *opt_text;
extern gboolean opt_waterfall;
extern int opt_width;
extern int opt_indent;
+extern PangoEllipsizeMode opt_ellipsize;
diff --git a/pango/Makefile.am b/pango/Makefile.am
index 66081e8a..cc5fdb02 100644
--- a/pango/Makefile.am
+++ b/pango/Makefile.am
@@ -53,6 +53,7 @@ pango-win32res.lo: pango.rc
libpango_1_0_la_SOURCES = \
break.c \
+ ellipsize.c \
fonts.c \
glyphstring.c \
mapping.c \
@@ -66,9 +67,11 @@ libpango_1_0_la_SOURCES = \
pango-fontmap.c \
pango-fontset.c \
pango-glyph-item.c \
+ pango-glyph-item-private.h \
pango-impl-utils.h \
pango-item.c \
pango-layout.c \
+ pango-layout-private.h \
pango-markup.c \
pango-script.c \
pango-script-lang-table.h \
diff --git a/pango/ellipsize.c b/pango/ellipsize.c
new file mode 100644
index 00000000..8a7164d1
--- /dev/null
+++ b/pango/ellipsize.c
@@ -0,0 +1,750 @@
+/* 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 <string.h>
+
+#include "pango-glyph-item-private.h"
+#include "pango-layout-private.h"
+#include "pango-engine-private.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 grap 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, j;
+ 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 = g_utf8_strlen (line->layout->text,
+ line->start_index);
+
+ start_offset = 0;
+ state->total_width = 0;
+ for (l = line->runs, i = 0; l; l = l->next, i++)
+ {
+ PangoGlyphItem *run = l->data;
+ int width = 0;
+
+ for (j = 0; j < run->glyphs->num_glyphs; j++)
+ width += run->glyphs->glyphs[j].geometry.width;
+
+ 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->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[run_iter->start_glyph].geometry.width;
+ }
+ else /* RTL */
+ {
+ for (i = run_iter->start_glyph; i > run_iter->end_glyph; i--)
+ width += glyphs->glyphs[run_iter->start_glyph].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_new (PangoGlyphItem, 1);
+ 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 (!_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 = FALSE;
+
+ 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;
+
+ while (TRUE)
+ {
+ pango_attr_iterator_range (iter, &start, &end);
+ if (end > new_index)
+ break;
+
+ 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;
+
+ /* 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;
+ new_gap_start_x -= get_cluster_width (&new_gap_start_iter);
+ }
+ while (!starts_at_ellipsization_boundary (state, &new_gap_start_iter));
+
+ 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;
+ new_gap_end_x += get_cluster_width (&new_gap_end_iter);
+ }
+ while (!ends_at_ellipsization_boundary (state, &new_gap_end_iter));
+
+ 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 = 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 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.
+ **/
+void
+_pango_layout_line_ellipsize (PangoLayoutLine *line,
+ PangoAttrList *attrs)
+{
+ EllipsizeState state;
+
+ if (line->layout->ellipsize == PANGO_ELLIPSIZE_NONE ||
+ line->layout->width < 0)
+ return;
+
+ init_state (&state, line, attrs);
+
+ if (state.total_width <= state.layout->width)
+ goto out;
+
+ find_initial_span (&state);
+
+ while (current_width (&state) > state.layout->width)
+ {
+ if (!remove_one_span (&state))
+ break;
+ }
+
+ fixup_ellipsis_run (&state);
+
+ g_slist_free (line->runs);
+ line->runs = get_run_list (&state);
+
+ out:
+ free_state (&state);
+}
diff --git a/pango/pango-glyph-item-private.h b/pango/pango-glyph-item-private.h
new file mode 100644
index 00000000..245706ad
--- /dev/null
+++ b/pango/pango-glyph-item-private.h
@@ -0,0 +1,64 @@
+/* -*- mode: C; c-file-style: "gnu" -*- */
+/* Pango
+ * pango-glyph-item-private.h: Pair of PangoItem and a glyph string; private
+ * functionality
+ *
+ * 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.
+ */
+
+#ifndef __PANGO_GLYPH_ITEM_PRIVATE_H__
+#define __PANGO_GLYPH_ITEM_PRIVATE_H__
+
+#include <pango-glyph-item.h>
+
+G_BEGIN_DECLS
+
+/* Structure holding state when we're iterating over a GlyphItem.
+ * start_index/cluster_end (and range_start/range_end in
+ * apply_attrs()) are offsets into the text, so note the difference
+ * of glyph_item->item->offset between them and clusters in the
+ * log_clusters[] array.
+ */
+typedef struct _PangoGlyphItemIter PangoGlyphItemIter;
+
+struct _PangoGlyphItemIter
+{
+ PangoGlyphItem *glyph_item;
+ const gchar *text;
+
+ int start_glyph;
+ int start_index;
+ int start_char;
+
+ int end_glyph;
+ int end_index;
+ int end_char;
+};
+
+gboolean _pango_glyph_item_iter_init_start (PangoGlyphItemIter *iter,
+ PangoGlyphItem *glyph_item,
+ const char *text);
+gboolean _pango_glyph_item_iter_init_end (PangoGlyphItemIter *iter,
+ PangoGlyphItem *glyph_item,
+ const char *text);
+gboolean _pango_glyph_item_iter_next_cluster (PangoGlyphItemIter *iter);
+gboolean _pango_glyph_item_iter_prev_cluster (PangoGlyphItemIter *iter);
+
+G_END_DECLS
+
+#endif /* __PANGO_GLYPH_ITEM_PRIVATE_H__ */
diff --git a/pango/pango-glyph-item.c b/pango/pango-glyph-item.c
index af242b1e..1245ca8c 100644
--- a/pango/pango-glyph-item.c
+++ b/pango/pango-glyph-item.c
@@ -23,6 +23,7 @@
#include <string.h>
#include "pango-glyph-item.h"
+#include "pango-glyph-item-private.h"
#define LTR(glyph_item) (((glyph_item)->item->analysis.level % 2) == 0)
@@ -126,45 +127,56 @@ pango_glyph_item_split (PangoGlyphItem *orig,
return new;
}
-/* Structure holding state when we're iterating over a GlyphItem.
- start_index/cluster_end (and range_start/range_end in
- apply_attrs()) are offsets into the text, so note the difference
- of glyph_item->item->offset between them and clusters in the
- log_clusters[] array.
- */
-typedef struct
+/**
+ * pango_glyph_item_free:
+ * @glyph_item: a #PangoGlyphItem
+ *
+ * Frees a #PangoGlyphItem and memory to which it points.
+ **/
+void
+pango_glyph_item_free (PangoGlyphItem *glyph_item)
{
- PangoGlyphItem *glyph_item;
- const gchar *text;
-
- int start_glyph;
- int start_index;
- int start_char;
+ if (glyph_item->item)
+ pango_item_free (glyph_item->item);
+ if (glyph_item->glyphs)
+ pango_glyph_string_free (glyph_item->glyphs);
- int end_glyph;
- int end_index;
- int end_char;
-} GlyphItemIter;
+ g_free (glyph_item);
+}
-/* Advance to the next cluster, returns FALSE if
- * we were already on the last cluster
- */
-static gboolean
-glyph_item_iter_next_cluster (GlyphItemIter *iter)
+/**
+ * _pango_glyph_item_iter_next_cluster:
+ * @iter: a #PangoGlyphItemIter
+ *
+ * Advances the iterator to the next cluster in the glyph item.
+ *
+ * Return value: %TRUE if the iterator was advanced, %FALSE if we were already on the
+ * last cluster.
+ **/
+gboolean
+_pango_glyph_item_iter_next_cluster (PangoGlyphItemIter *iter)
{
int glyph_index = iter->end_glyph;
PangoGlyphString *glyphs = iter->glyph_item->glyphs;
PangoItem *item = iter->glyph_item->item;
-
+
+ if (LTR (iter->glyph_item))
+ {
+ if (glyph_index == glyphs->num_glyphs)
+ return FALSE;
+ }
+ else
+ {
+ if (glyph_index < 0)
+ return FALSE;
+ }
+
iter->start_glyph = iter->end_glyph;
iter->start_index = iter->end_index;
iter->start_char = iter->end_char;
if (LTR (iter->glyph_item))
{
- if (glyph_index == glyphs->num_glyphs)
- return FALSE;
-
while (TRUE)
{
glyph_index++;
@@ -187,9 +199,6 @@ glyph_item_iter_next_cluster (GlyphItemIter *iter)
}
else /* RTL */
{
- if (glyph_index < 0)
- return FALSE;
-
while (TRUE)
{
glyph_index--;
@@ -215,11 +224,103 @@ glyph_item_iter_next_cluster (GlyphItemIter *iter)
return TRUE;
}
-/* Returns FALSE if there are no clusters in the glyph item */
-static gboolean
-glyph_item_iter_init (GlyphItemIter *iter,
- PangoGlyphItem *glyph_item,
- const char *text)
+/**
+ * _pango_glyph_item_iter_prev_cluster:
+ * @iter: a #PangoGlyphItemIter
+ *
+ * Moves the iterator to the preceding cluster in the glyph item.
+ *
+ * Return value: %TRUE if the iterator was movedn, %FALSE if we were already on the
+ * first cluster.
+ **/
+gboolean
+_pango_glyph_item_iter_prev_cluster (PangoGlyphItemIter *iter)
+{
+ int glyph_index = iter->start_glyph;
+ PangoGlyphString *glyphs = iter->glyph_item->glyphs;
+ PangoItem *item = iter->glyph_item->item;
+
+ if (LTR (iter->glyph_item))
+ {
+ if (glyph_index == 0)
+ return FALSE;
+ }
+ else
+ {
+ if (glyph_index == glyphs->num_glyphs - 1)
+ return FALSE;
+
+ }
+
+ iter->end_glyph = iter->start_glyph;
+ iter->end_index = iter->start_index;
+ iter->end_char = iter->start_char;
+
+ if (LTR (iter->glyph_item))
+ {
+ while (TRUE)
+ {
+ glyph_index--;
+
+ if (glyph_index == 0)
+ {
+ iter->start_index = item->offset;
+ iter->start_char = 0;
+ break;
+ }
+
+ if (item->offset + glyphs->log_clusters[glyph_index] < iter->end_index)
+ {
+ iter->start_index = item->offset + glyphs->log_clusters[glyph_index];
+ iter->start_char -= g_utf8_strlen (iter->text + iter->start_index,
+ iter->end_index - iter->start_index);
+ break;
+ }
+ }
+ }
+ else /* RTL */
+ {
+ while (TRUE)
+ {
+ glyph_index++;
+
+ if (glyph_index == glyphs->num_glyphs - 1)
+ {
+ iter->start_index = item->offset;
+ iter->start_char = 0;
+ break;
+ }
+
+ if (item->offset + glyphs->log_clusters[glyph_index] < iter->start_index)
+ {
+ iter->start_index = item->offset + glyphs->log_clusters[glyph_index];
+ iter->start_char -= g_utf8_strlen (iter->text + iter->start_index,
+ iter->end_index - iter->start_index);
+ break;
+ }
+ }
+ }
+
+ iter->start_glyph = glyph_index;
+ return TRUE;
+}
+
+/**
+ * _pango_glyph_item_iter_init_start:
+ * @iter: pointer to a #PangoGlyphItemIter structure
+ * @glyph_item: the glyph item that the iter points into
+ * @text: text corresponding to the glyph item
+ *
+ * Initializes a #PangoGlyphItemIter structure to point to the
+ * first cluster in a glyph item.
+ *
+ * Return value: %FALSE if there are no clusters in the glyph item;
+ * in this case, the state of the iter is undefined.
+ **/
+gboolean
+_pango_glyph_item_iter_init_start (PangoGlyphItemIter *iter,
+ PangoGlyphItem *glyph_item,
+ const char *text)
{
iter->glyph_item = glyph_item;
iter->text = text;
@@ -233,12 +334,44 @@ glyph_item_iter_init (GlyphItemIter *iter,
iter->end_char = 0;
/* Advance onto the first cluster of the glyph item */
- return glyph_item_iter_next_cluster (iter);
+ return _pango_glyph_item_iter_next_cluster (iter);
+}
+
+/**
+ * _pango_glyph_item_iter_init_start:
+ * @iter: pointer to a #PangoGlyphItemIter structure
+ * @glyph_item: the glyph item that the iter points into
+ * @text: text corresponding to the glyph item
+ *
+ * Initializes a #PangoGlyphItemIter structure to point to the
+ * last cluster in a glyph item.
+ *
+ * Return value: %FALSE if there are no clusters in the glyph item;
+ * in this case, the state of the iter is undefined.
+ **/
+gboolean
+_pango_glyph_item_iter_init_end (PangoGlyphItemIter *iter,
+ PangoGlyphItem *glyph_item,
+ const char *text)
+{
+ iter->glyph_item = glyph_item;
+ iter->text = text;
+
+ if (LTR (glyph_item))
+ iter->start_glyph = glyph_item->glyphs->num_glyphs;
+ else
+ iter->start_glyph = -1;
+
+ iter->start_index = glyph_item->item->offset + glyph_item->item->length;
+ iter->start_char = glyph_item->item->num_chars;
+
+ /* Advance onto the first cluster of the glyph item */
+ return _pango_glyph_item_iter_prev_cluster (iter);
}
typedef struct
{
- GlyphItemIter iter;
+ PangoGlyphItemIter iter;
GSList *segment_attrs;
} ApplyAttrsState;
@@ -368,9 +501,9 @@ pango_glyph_item_apply_attrs (PangoGlyphItem *glyph_item,
range_end >= glyph_item->item->offset + glyph_item->item->length)
goto out;
- for (have_cluster = glyph_item_iter_init (&state.iter, glyph_item, text);
+ for (have_cluster = _pango_glyph_item_iter_init_start (&state.iter, glyph_item, text);
have_cluster;
- have_cluster = glyph_item_iter_next_cluster (&state.iter))
+ have_cluster = _pango_glyph_item_iter_next_cluster (&state.iter))
{
/* [range_start,range_end] is the first range that intersects
@@ -471,12 +604,12 @@ pango_glyph_item_letter_space (PangoGlyphItem *glyph_item,
PangoLogAttr *log_attrs,
int letter_spacing)
{
- GlyphItemIter iter;
+ PangoGlyphItemIter iter;
gboolean have_cluster;
- for (have_cluster = glyph_item_iter_init (&iter, glyph_item, text);
+ for (have_cluster = _pango_glyph_item_iter_init_start (&iter, glyph_item, text);
have_cluster;
- have_cluster = glyph_item_iter_next_cluster (&iter))
+ have_cluster = _pango_glyph_item_iter_next_cluster (&iter))
{
if (iter.start_char > 0 &&
log_attrs[iter.start_char].is_cursor_position)
diff --git a/pango/pango-glyph-item.h b/pango/pango-glyph-item.h
index 01a57489..3be2b9e6 100644
--- a/pango/pango-glyph-item.h
+++ b/pango/pango-glyph-item.h
@@ -41,6 +41,7 @@ struct _PangoGlyphItem
PangoGlyphItem *pango_glyph_item_split (PangoGlyphItem *orig,
const char *text,
int split_index);
+void pango_glyph_item_free (PangoGlyphItem *glyph_item);
GSList * pango_glyph_item_apply_attrs (PangoGlyphItem *glyph_item,
const char *text,
PangoAttrList *list);
diff --git a/pango/pango-layout-private.h b/pango/pango-layout-private.h
new file mode 100644
index 00000000..fa4f7b5d
--- /dev/null
+++ b/pango/pango-layout-private.h
@@ -0,0 +1,71 @@
+/* Pango
+ * pango-layout-private.h: Internal structures of PangoLayout
+ *
+ * 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.
+ */
+
+#ifndef __PANGO_LAYOUT_PRIVATE_H__
+#define __PANGO_LAYOUT_PRIVATE_H__
+
+#include <pango/pango-layout.h>
+
+G_BEGIN_DECLS
+
+struct _PangoLayout
+{
+ GObject parent_instance;
+
+ /* If you add fields to PangoLayout, be sure to update both
+ * the _copy function
+ */
+
+ PangoContext *context;
+ PangoAttrList *attrs;
+ PangoFontDescription *font_desc;
+
+ gchar *text;
+ int length; /* length of text in bytes */
+ int width; /* wrap width, in device units */
+ int indent; /* amount by which first line should be shorter */
+ int spacing; /* spacing between lines */
+
+ guint justify : 1;
+ guint alignment : 2;
+
+ guint single_paragraph : 1;
+ guint auto_dir : 1;
+
+ guint wrap : 2; /* PangoWrapMode */
+ guint ellipsize : 2; /* PangoEllipsizeMode */
+
+ gint n_chars; /* Total number of characters in layout */
+ PangoLogAttr *log_attrs; /* Logical attributes for layout's text */
+
+ int tab_width; /* Cached width of a tab. -1 == not yet calculated */
+
+ PangoTabArray *tabs;
+
+ GSList *lines;
+};
+
+G_END_DECLS
+
+void _pango_layout_line_ellipsize (PangoLayoutLine *line,
+ PangoAttrList *attrs);
+
+#endif /* __PANGO_LAYOUT_PRIVATE_H__ */
diff --git a/pango/pango-layout.c b/pango/pango-layout.c
index 533cf833..fe8ef25d 100644
--- a/pango/pango-layout.c
+++ b/pango/pango-layout.c
@@ -20,16 +20,18 @@
*/
#include <pango/pango-glyph.h> /* For pango_shape() */
-#include <pango/pango-layout.h>
#include <pango/pango-break.h>
#include <pango/pango-item.h>
#include <pango/pango-engine.h>
#include <string.h>
+#include "pango-layout-private.h"
+
#define LINE_IS_VALID(line) ((line)->layout != NULL)
typedef struct _Extents Extents;
typedef struct _ItemProperties ItemProperties;
+typedef struct _ParaBreakState ParaBreakState;
struct _Extents
{
@@ -103,42 +105,6 @@ struct _PangoLayoutLinePrivate
guint ref_count;
};
-struct _PangoLayout
-{
- GObject parent_instance;
-
- /* If you add fields to PangoLayout, be sure to update both
- * the _copy function
- */
-
- PangoContext *context;
- PangoAttrList *attrs;
- PangoFontDescription *font_desc;
-
- gchar *text;
- int length; /* length of text in bytes */
- int width; /* wrap width, in device units */
- int indent; /* amount by which first line should be shorter */
- int spacing; /* spacing between lines */
-
- guint justify : 1;
- guint alignment : 2;
-
- guint single_paragraph : 1;
- guint auto_dir : 1;
-
- gint n_chars; /* Total number of characters in layout */
- PangoLogAttr *log_attrs; /* Logical attributes for layout's text */
-
- int tab_width; /* Cached width of a tab. -1 == not yet calculated */
-
- PangoTabArray *tabs;
-
- GSList *lines;
-
- PangoWrapMode wrap;
-};
-
struct _PangoLayoutClass
{
GObjectClass parent_class;
@@ -152,7 +118,8 @@ static void pango_layout_check_lines (PangoLayout *layout);
static PangoAttrList *pango_layout_get_effective_attributes (PangoLayout *layout);
static PangoLayoutLine * pango_layout_line_new (PangoLayout *layout);
-static void pango_layout_line_postprocess (PangoLayoutLine *line);
+static void pango_layout_line_postprocess (PangoLayoutLine *line,
+ ParaBreakState *state);
static int *pango_layout_line_get_log2vis_map (PangoLayoutLine *line,
gboolean strong);
@@ -186,6 +153,7 @@ pango_layout_init (PangoLayout *layout)
layout->tab_width = -1;
layout->wrap = PANGO_WRAP_WORD;
+ layout->ellipsize = PANGO_ELLIPSIZE_NONE;
}
static void
@@ -285,6 +253,7 @@ pango_layout_copy (PangoLayout *src)
if (src->tabs)
layout->tabs = pango_tab_array_copy (src->tabs);
layout->wrap = src->wrap;
+ layout->ellipsize = src->ellipsize;
/* log_attrs, lines fields are updated by check_lines */
@@ -715,11 +684,17 @@ pango_layout_set_single_paragraph_mode (PangoLayout *layout,
gboolean setting)
{
g_return_if_fail (PANGO_IS_LAYOUT (layout));
-
- layout->single_paragraph = setting;
- pango_layout_clear_lines (layout);
+ setting = setting != FALSE;
+
+ if (layout->single_paragraph != setting)
+ {
+ layout->single_paragraph = setting;
+
+ pango_layout_clear_lines (layout);
+ }
}
+
/**
* pango_layout_get_single_paragraph_mode:
* @layout: a #PangoLayout
@@ -729,7 +704,6 @@ pango_layout_set_single_paragraph_mode (PangoLayout *layout,
* Return value: %TRUE if the layout does not break paragraphs at
* paragraph separator characters
**/
-
gboolean
pango_layout_get_single_paragraph_mode (PangoLayout *layout)
{
@@ -739,6 +713,53 @@ pango_layout_get_single_paragraph_mode (PangoLayout *layout)
}
/**
+ * pango_layout_get_ellipsize:
+ * @layout: a #PangoLayout
+ * @ellipsize: the new ellipsization mode for @layout
+ *
+ * Sets the type of ellipsization being performed for @layout.
+ * Depending on the ellipsization mode @ellipsize text is
+ * removed from the start, middle, or end of lines so they
+ * fit within the width of layout set with pango_layout_set_width ().
+ *
+ * If the layout contains characters such as newlines that
+ * force it to be layed out in multiple lines, then each line
+ * is ellipsized separately.
+ *
+ * Return value: Gets the current ellipsization mode for the layout.
+ **/
+void
+pango_layout_set_ellipsize (PangoLayout *layout,
+ PangoEllipsizeMode ellipsize)
+{
+ g_return_if_fail (PANGO_IS_LAYOUT (layout));
+
+ if (ellipsize != layout->ellipsize)
+ {
+ layout->ellipsize = ellipsize;
+
+ pango_layout_clear_lines (layout);
+ }
+}
+
+/**
+ * pango_layout_get_ellipsize:
+ * @layout: a #PangoLayout
+ *
+ * Gets the type of ellipsization being performed for @layout.
+ * See pango_layout_get_ellipsize()
+ *
+ * Return value: the current ellipsization mode for @layout
+ **/
+PangoEllipsizeMode
+pango_layout_get_ellipsize (PangoLayout *layout)
+{
+ g_return_val_if_fail (PANGO_IS_LAYOUT (layout), PANGO_ELLIPSIZE_NONE);
+
+ return layout->ellipsize;
+}
+
+/**
* pango_layout_set_text:
* @layout: a #PangoLayout
* @text: a UTF8-string
@@ -2473,28 +2494,35 @@ get_tab_pos (PangoLayout *layout, int index)
}
}
-static void
-shape_tab (PangoLayoutLine *line,
- PangoGlyphString *glyphs)
+static int
+line_width (PangoLayoutLine *line)
{
+ GSList *l;
int i;
- GSList *tmp_list;
-
- int current_width = 0;
-
+ int width = 0;
+
/* Compute the width of the line currently - inefficient, but easier
* than keeping the current width of the line up to date everywhere
*/
- tmp_list = line->runs;
- while (tmp_list)
+ for (l = line->runs; l; l = l->next)
{
- PangoLayoutRun *run = tmp_list->data;
- for (i=0; i < run->glyphs->num_glyphs; i++)
- current_width += run->glyphs->glyphs[i].geometry.width;
+ PangoLayoutRun *run = l->data;
- tmp_list = tmp_list->next;
+ for (i=0; i < run->glyphs->num_glyphs; i++)
+ width += run->glyphs->glyphs[i].geometry.width;
}
-
+
+ return width;
+}
+
+static void
+shape_tab (PangoLayoutLine *line,
+ PangoGlyphString *glyphs)
+{
+ int i;
+
+ int current_width = line_width (line);
+
pango_glyph_string_set_size (glyphs, 1);
glyphs->glyphs[0].glyph = 0;
@@ -2567,10 +2595,9 @@ typedef enum
BREAK_LINE_SEPARATOR
} BreakResult;
-typedef struct _ParaBreakState ParaBreakState;
-
struct _ParaBreakState
{
+ PangoAttrList *attrs; /* Attributes being used for itemization */
GList *items; /* This paragraph turned into items */
PangoDirection base_dir; /* Current resolved base direction */
gboolean first_line; /* TRUE if this is the first line of the paragraph */
@@ -2853,13 +2880,15 @@ process_line (PangoLayout *layout,
int break_remaining_width = 0; /* Remaining width before adding run with break */
int break_start_offset = 0; /* Start width before adding run with break */
GSList *break_link = NULL; /* Link holding run before break */
-
+
line = pango_layout_line_new (layout);
line->start_index = state->line_start_index;
line->is_paragraph_start = state->first_line;
line->resolved_dir = state->base_dir;
- if (state->first_line)
+ if (layout->ellipsize != PANGO_ELLIPSIZE_NONE)
+ state->remaining_width = -1;
+ else if (state->first_line)
state->remaining_width = (layout->indent >= 0) ? layout->width - layout->indent : layout->width;
else
state->remaining_width = (layout->indent >= 0) ? layout->width : layout->width + layout->indent;
@@ -2928,7 +2957,7 @@ process_line (PangoLayout *layout,
}
done:
- pango_layout_line_postprocess (line);
+ pango_layout_line_postprocess (line, state);
layout->lines = g_slist_prepend (layout->lines, line);
state->first_line = FALSE;
state->line_start_index += line->length;
@@ -3149,6 +3178,7 @@ pango_layout_check_lines (PangoLayout *layout)
g_assert (delim_len < 4); /* PS is 3 bytes */
g_assert (delim_len >= 0);
+ state.attrs = attrs;
state.items = pango_itemize_with_base_dir (layout->context,
base_dir,
layout->text,
@@ -4116,7 +4146,8 @@ adjust_line_letter_spacing (PangoLayoutLine *line)
}
static void
-pango_layout_line_postprocess (PangoLayoutLine *line)
+pango_layout_line_postprocess (PangoLayoutLine *line,
+ ParaBreakState *state)
{
/* NB: the runs are in reverse order at this point, since we prepended them to the list
*/
@@ -4125,6 +4156,10 @@ pango_layout_line_postprocess (PangoLayoutLine *line)
*/
line->runs = g_slist_reverse (line->runs);
+ /* Ellipsize the line if necessary
+ */
+ _pango_layout_line_ellipsize (line, state->attrs);
+
/* Now convert logical to visual order
*/
pango_layout_line_reorder (line);
diff --git a/pango/pango-layout.h b/pango/pango-layout.h
index d9447fca..ac501fe1 100644
--- a/pango/pango-layout.h
+++ b/pango/pango-layout.h
@@ -48,6 +48,26 @@ typedef enum {
PANGO_WRAP_WORD_CHAR
} PangoWrapMode;
+/**
+ * PangoEllipsizeMode
+ * @PANGO_ELLIPSIZE_NONE: No ellipsization
+ * @PANGO_ELLIPSIZE_START: Omit characters at the start of the text
+ * @PANGO_ELLIPSIZE_MIDDLE: Omit characters in the middle of the text
+ * @PANGO_ELLIPSIZE_END: Omit characters at the end of the text
+ *
+ * The #PangoEllipsizeMode type describes what sort of (if any)
+ * ellipsization should be applied to a line of text. In
+ * the ellipsization process characters are removed from the
+ * text in order to make it fit to a given width and replaced
+ * with an ellipsis.
+ */
+typedef enum {
+ PANGO_ELLIPSIZE_NONE,
+ PANGO_ELLIPSIZE_START,
+ PANGO_ELLIPSIZE_MIDDLE,
+ PANGO_ELLIPSIZE_END
+} PangoEllipsizeMode;
+
struct _PangoLayoutLine
{
PangoLayout *layout;
@@ -127,6 +147,10 @@ void pango_layout_set_single_paragraph_mode (PangoLayout
gboolean setting);
gboolean pango_layout_get_single_paragraph_mode (PangoLayout *layout);
+void pango_layout_set_ellipsize (PangoLayout *layout,
+ PangoEllipsizeMode ellipsize);
+PangoEllipsizeMode pango_layout_get_ellipsize (PangoLayout *layout);
+
void pango_layout_context_changed (PangoLayout *layout);
void pango_layout_get_log_attrs (PangoLayout *layout,