summaryrefslogtreecommitdiff
path: root/pango
diff options
context:
space:
mode:
Diffstat (limited to 'pango')
-rw-r--r--pango/Makefile.am1
-rw-r--r--pango/glyphstring.c73
-rw-r--r--pango/itemize.c13
-rw-r--r--pango/mapping.c2
-rw-r--r--pango/pango-context.c8
-rw-r--r--pango/pango-glyph.h6
-rw-r--r--pango/pango-layout.c873
-rw-r--r--pango/pango-layout.h71
-rw-r--r--pango/pangox.c106
-rw-r--r--pango/pangox.h45
-rw-r--r--pango/reorder-items.c10
11 files changed, 1103 insertions, 105 deletions
diff --git a/pango/Makefile.am b/pango/Makefile.am
index 3a7803f0..930526cd 100644
--- a/pango/Makefile.am
+++ b/pango/Makefile.am
@@ -15,6 +15,7 @@ libpango_la_SOURCES = \
pango-attributes.c \
pango-context.c \
pango-coverage.c \
+ pango-layout.c \
reorder-items.c \
shape.c \
utils.c \
diff --git a/pango/glyphstring.c b/pango/glyphstring.c
index b29efe09..d3030e39 100644
--- a/pango/glyphstring.c
+++ b/pango/glyphstring.c
@@ -22,6 +22,7 @@
#include <glib.h>
#include <pango-glyph.h>
#include <pango-font.h>
+#include <unicode.h>
/**
* pango_glyph_string_new:
@@ -151,12 +152,12 @@ pango_glyph_string_extents (PangoGlyphString *glyphs,
{
new_pos = MIN (ink_rect->x, x_pos + glyph_ink.x + geometry->x_offset);
ink_rect->width = MAX (ink_rect->x + ink_rect->width,
- x_pos + glyph_ink.x + glyph_ink.width + geometry->x_offset) - ink_rect->x;
+ x_pos + glyph_ink.x + glyph_ink.width + geometry->x_offset) - new_pos;
ink_rect->x = new_pos;
new_pos = MIN (ink_rect->y, glyph_ink.y + geometry->y_offset);
ink_rect->height = MAX (ink_rect->y + ink_rect->height,
- glyph_ink.y + glyph_ink.height + geometry->y_offset) - ink_rect->y;
+ glyph_ink.y + glyph_ink.height + geometry->y_offset) - new_pos;
ink_rect->y = new_pos;
}
@@ -164,12 +165,12 @@ pango_glyph_string_extents (PangoGlyphString *glyphs,
{
new_pos = MIN (logical_rect->x, x_pos + glyph_logical.x + geometry->x_offset);
logical_rect->width = MAX (logical_rect->x + logical_rect->width,
- x_pos + glyph_logical.x + glyph_logical.width + geometry->x_offset) - logical_rect->x;
+ x_pos + glyph_logical.x + glyph_logical.width + geometry->x_offset) - new_pos;
logical_rect->x = new_pos;
new_pos = MIN (logical_rect->y, glyph_logical.y + geometry->y_offset);
logical_rect->height = MAX (logical_rect->y + logical_rect->height,
- glyph_logical.y + glyph_logical.height + geometry->y_offset) - logical_rect->y;
+ glyph_logical.y + glyph_logical.height + geometry->y_offset) - new_pos;
logical_rect->y = new_pos;
}
}
@@ -178,3 +179,67 @@ pango_glyph_string_extents (PangoGlyphString *glyphs,
}
}
+/**
+ * pango_glyph_string_get_logical_widths:
+ * @glyphs: a #PangoGlyphString
+ * @text: the text corresponding to the glyphs
+ * @length: the length of @text, in bytes
+ * @embedding_level: the embedding level of the string
+ * @logical_widths: an array whose length is unicode_strlen (text, length)
+ * to be filled in with the resulting character widths.
+ *
+ * Given a #PangoGlyphString resulting from pango_shape() and the corresponding
+ * text, determine the screen width corresponding to each character. When
+ * multiple characters compose a single cluster, the width of the entire
+ * cluster is divided equally among the characters.
+ **/
+void
+pango_glyph_string_get_logical_widths (PangoGlyphString *glyphs,
+ char *text,
+ int length,
+ int embedding_level,
+ int *logical_widths)
+{
+ int i, j;
+ int last_cluster = 0;
+ int width = 0;
+ int last_cluster_width = 0;
+ char *p = text;
+
+ for (i=0; i<=glyphs->num_glyphs; i++)
+ {
+ int glyph_index = (embedding_level % 2 == 0) ? i : glyphs->num_glyphs - i - 1;
+
+ if (i == glyphs->num_glyphs || p != text + glyphs->log_clusters[glyph_index])
+ {
+ int next_cluster = last_cluster;
+
+ if (glyph_index < glyphs->num_glyphs)
+ {
+ while (p < text + glyphs->log_clusters[glyph_index])
+ {
+ next_cluster++;
+ p = unicode_next_utf8 (p);
+ }
+ }
+ else
+ {
+ while (p < text + length)
+ {
+ next_cluster++;
+ p = unicode_next_utf8 (p);
+ }
+ }
+
+ for (j = last_cluster; j < next_cluster; j++)
+ logical_widths[j] = (width - last_cluster_width) / (next_cluster - last_cluster);
+
+ last_cluster = next_cluster;
+ last_cluster_width = width;
+ }
+
+ if (i < glyphs->num_glyphs)
+ width += glyphs->glyphs[i].geometry.width;
+ }
+}
+
diff --git a/pango/itemize.c b/pango/itemize.c
index bc7c9bd8..0f552915 100644
--- a/pango/itemize.c
+++ b/pango/itemize.c
@@ -52,10 +52,10 @@ static void add_engines (PangoContext *context,
*/
GList *
pango_itemize (PangoContext *context,
- gchar *text,
- gint length,
- PangoLangRange *lang_info,
- gint n_langs)
+ gchar *text,
+ gint length,
+ PangoLangRange *lang_info,
+ gint n_langs)
{
guint16 *text_ucs2;
gint n_chars;
@@ -70,9 +70,10 @@ pango_itemize (PangoContext *context,
PangoEngineInfo **lang_engines;
g_return_val_if_fail (context != NULL, NULL);
- g_return_val_if_fail (text != NULL, NULL);
- g_return_val_if_fail (length >= 0, NULL);
+ g_return_val_if_fail (length == 0 || text != NULL, NULL);
+ if (length == 0)
+ return NULL;
if (context->direction == PANGO_DIRECTION_RTL)
base_dir = FRIBIDI_TYPE_RTL;
diff --git a/pango/mapping.c b/pango/mapping.c
index d075b9f8..3ad923ee 100644
--- a/pango/mapping.c
+++ b/pango/mapping.c
@@ -140,10 +140,10 @@ pango_glyph_string_index_to_x (PangoGlyphString *glyphs,
p = text + start_index;
while (p < text + end_index)
{
- p = unicode_next_utf8 (p);
if (p < text + index)
cluster_offset++;
cluster_chars++;
+ p = unicode_next_utf8 (p);
}
/* Now interpolate the result. For South Asian languages
diff --git a/pango/pango-context.c b/pango/pango-context.c
index 30902c47..a6b7cccc 100644
--- a/pango/pango-context.c
+++ b/pango/pango-context.c
@@ -468,8 +468,8 @@ pango_context_get_base_dir (PangoContext *context)
*/
GList *
pango_itemize (PangoContext *context,
- gchar *text,
- gint length,
+ char *text,
+ int length,
PangoAttrList *attrs)
{
guint16 *text_ucs2;
@@ -486,9 +486,11 @@ pango_itemize (PangoContext *context,
PangoFont **fonts;
g_return_val_if_fail (context != NULL, NULL);
- g_return_val_if_fail (text != NULL, NULL);
g_return_val_if_fail (length >= 0, NULL);
+ g_return_val_if_fail (length == 0 || text != NULL, NULL);
+ if (length == 0)
+ return NULL;
if (context->base_dir == PANGO_DIRECTION_RTL)
base_dir = FRIBIDI_TYPE_RTL;
diff --git a/pango/pango-glyph.h b/pango/pango-glyph.h
index cd9b84f9..433e71f2 100644
--- a/pango/pango-glyph.h
+++ b/pango/pango-glyph.h
@@ -88,6 +88,12 @@ void pango_glyph_string_extents (PangoGlyphString *glyphs,
PangoRectangle *ink_rect,
PangoRectangle *logical_rect);
+void pango_glyph_string_get_logical_widths (PangoGlyphString *glyphs,
+ char *text,
+ int length,
+ int embedding_level,
+ int *logical_widths);
+
void pango_glyph_string_index_to_x (PangoGlyphString *glyphs,
char *text,
int length,
diff --git a/pango/pango-layout.c b/pango/pango-layout.c
new file mode 100644
index 00000000..04b440df
--- /dev/null
+++ b/pango/pango-layout.c
@@ -0,0 +1,873 @@
+/* Pango
+ * pango-layout.c: Highlevel layout driver
+ *
+ * Copyright (C) 2000 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 <pango-layout.h>
+#include <pango.h> /* For pango_shape() */
+#include <unicode.h>
+
+#define LINE_IS_VALID(line) ((line)->layout != NULL)
+
+typedef struct _PangoLayoutLinePrivate PangoLayoutLinePrivate;
+
+struct _PangoLayout
+{
+ guint ref_count;
+
+ PangoContext *context;
+ PangoAttrList *attrs;
+ gchar *text;
+ int length; /* length of text in bytes */
+ int width; /* wrap width, in device units */
+ int first_line_width; /* wrap width for first line, in device units */
+
+ GSList *lines;
+};
+
+struct _PangoLayoutLinePrivate
+{
+ PangoLayoutLine line;
+ guint ref_count;
+};
+
+static void pango_layout_clear_lines (PangoLayout *layout);
+static void pango_layout_check_lines (PangoLayout *layout);
+
+static PangoLayoutLine * pango_layout_line_new (PangoLayout *layout);
+static void pango_layout_line_reorder (PangoLayoutLine *line);
+
+/**
+ * pango_layout_new:
+ * @context: a #PangoContext
+ *
+ * Create a new #PangoLayout object with attributes initialized to
+ * default values for a particular #PangoContext.
+ *
+ * Return value: a new #PangoLayout, with a reference count of one.
+ **/
+PangoLayout *
+pango_layout_new (PangoContext *context)
+{
+ PangoLayout *layout;
+
+ g_return_val_if_fail (context != NULL, NULL);
+
+ layout = g_new (PangoLayout, 1);
+
+ layout->ref_count = 1;
+
+ layout->context = context;
+ pango_context_ref (context);
+
+ layout->attrs = NULL;
+ layout->text = NULL;
+ layout->length = 0;
+ layout->width = -1;
+ layout->first_line_width = -1;
+
+ layout->lines = NULL;
+
+ return layout;
+}
+
+/**
+ * pango_layout_ref:
+ * @layout: a #PangoLayout
+ *
+ * Increase the reference count of the #PangoLayout by one.
+ **/
+void
+pango_layout_ref (PangoLayout *layout)
+{
+ g_return_if_fail (layout != NULL);
+
+ layout->ref_count++;
+}
+
+/**
+ * pango_layout_unref:
+ * @layout:
+ *
+ * Decrease the reference count of the #PangoLayout by one. If the
+ * result is zero, free the #PangoLayout and all associated memory.
+ **/
+void
+pango_layout_unref (PangoLayout *layout)
+{
+ g_return_if_fail (layout != NULL);
+ g_return_if_fail (layout->ref_count > 0);
+
+ layout->ref_count--;
+ if (layout->ref_count == 0)
+ {
+ pango_layout_clear_lines (layout);
+
+ if (layout->context)
+ pango_context_unref (layout->context);
+
+ if (layout->attrs)
+ pango_attr_list_unref (layout->attrs);
+ if (layout->text)
+ g_free (layout->text);
+ }
+}
+
+/**
+ * pango_layout_set_width:
+ * @layout: a #PangoLayout.
+ * @width: the desired width, or -1 to indicate that no wrapping should be
+ * performed.
+ *
+ * Set the width to which the lines of the #PangoLayout should be wrapped.
+ **/
+void
+pango_layout_set_width (PangoLayout *layout,
+ int width)
+{
+ g_return_if_fail (layout != NULL);
+
+ layout->width = width;
+ pango_layout_clear_lines (layout);
+}
+
+/**
+ * pango_layout_set_first_width:
+ * @layout: a #PangoLayout.
+ * @width: the desired width, or -1 to indicate that it should be the same
+ * as the the width set by pango_layout_set_width().
+ *
+ * Set the width to which the first line of the #PangoLayout should be
+ * wrapped.
+ **/
+void
+pango_layout_set_first_line_width (PangoLayout *layout,
+ int width)
+{
+ g_return_if_fail (layout != NULL);
+
+ layout->first_line_width = width;
+ pango_layout_clear_lines (layout);
+}
+
+/**
+ * pango_layout_set_attributes:
+ * @layout: a #PangoLayout
+ * @attrs: a #PangoAttrList
+ *
+ * Set the text attributes for a layout object
+ **/
+void
+pango_layout_set_attributes (PangoLayout *layout,
+ PangoAttrList *attrs)
+{
+ if (layout->attrs)
+ pango_attr_list_unref (layout->attrs);
+
+ layout->attrs = attrs;
+ pango_attr_list_ref (layout->attrs);
+ pango_layout_clear_lines (layout);
+}
+
+/**
+ * pango_layout_set_text:
+ * @layout: a #PangoLayout
+ * @text: a UTF8-string
+ * @length: the length of @text, in bytes.
+ *
+ * Set the text of the layout.
+ **/
+void
+pango_layout_set_text (PangoLayout *layout,
+ char *text,
+ int length)
+{
+ g_return_if_fail (layout != NULL);
+ g_return_if_fail (length == 0 || text != NULL);
+
+ if (layout->text)
+ g_free (layout->text);
+
+ layout->length = length;
+
+ if (length > 0)
+ {
+ layout->text = g_malloc (length);
+ memcpy (layout->text, text, length);
+ }
+ else
+ layout->text = NULL;
+
+ pango_layout_clear_lines (layout);
+}
+
+/**
+ * pango_layout_get_line_count:
+ * @layout: #PangoLayout
+ *
+ * Retrieve the count of lines for the #PangoLayout
+ *
+ * Return value: the line count
+ **/
+int
+pango_layout_get_line_count (PangoLayout *layout)
+{
+ g_return_val_if_fail (layout != NULL, 0);
+
+ pango_layout_check_lines (layout);
+
+ return g_slist_length (layout->lines);
+}
+
+/**
+ * pango_layout_get_lines:
+ * @layout: a #PangoLayout
+ *
+ * Return the lines of the layout as a list
+ *
+ * Return value: a #GSList containing the lines in the layout. This
+ * points to internal data of the #PangoLayout and must be used with
+ * care. It will become invalid on any change to the layout's
+ * text or properties.
+ **/
+GSList *
+pango_layout_get_lines (PangoLayout *layout)
+{
+ pango_layout_check_lines (layout);
+ return layout->lines;
+}
+
+/**
+ * pango_layout_get_line:
+ * @layout: a #PangoLayout
+ * @line: the index of a line, which must be between 0 and
+ * pango_layout_get_line_count(layout) - 1, inclusive.
+ *
+ * Retrieve a particular line from a #PangoLayout
+ *
+ * Return value: the requested #PangoLayoutLine, or %NULL if the
+ * index is out of range. This layout line can
+ * be ref'ed and retained, but will become invalid
+ * if changes are made to the #PangoLayout.
+ **/
+PangoLayoutLine *
+pango_layout_get_line (PangoLayout *layout,
+ int line)
+{
+ g_return_val_if_fail (layout != NULL, NULL);
+ g_return_val_if_fail (line >= 0, NULL);
+
+ if (line < 0)
+ return NULL;
+
+ pango_layout_check_lines (layout);
+ return g_slist_nth (layout->lines, line)->data;
+}
+
+/**
+ * pango_layout_line_index_to_x:
+ * @line: a #PangoLayoutLine
+ * @index: byte offset within the Layout's text
+ * @trailing: integer indicating where in the cluster the user clicked.
+ * If the script allows positioning within the cluster, it is either
+ * 0 or 1; otherwise it is either 0 or the number
+ * of characters in the cluster. In either case
+ * 0 represents the trailing edge of the cluster.
+ * @x_pos: location to store the x_offset (in thousandths of a device unit)
+ *
+ * Convert index within a line to X pos
+ *
+ *
+ **/
+void
+pango_layout_line_index_to_x (PangoLayoutLine *line,
+ int index,
+ gboolean trailing,
+ int *x_pos)
+{
+ GSList *run_list = line->runs;
+ int width = 0;
+
+ while (run_list)
+ {
+ PangoRectangle logical_rect;
+ PangoLayoutRun *run = run_list->data;
+
+ if (run->item->offset <= index && run->item->offset + run->item->length > index)
+ {
+ pango_glyph_string_index_to_x (run->glyphs,
+ line->layout->text + run->item->offset,
+ run->item->length,
+ &run->item->analysis,
+ index - run->item->offset, trailing, x_pos);
+
+ if (x_pos)
+ *x_pos += width;
+
+ return;
+ }
+
+ pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
+ NULL, &logical_rect);
+ width += logical_rect.width;
+
+ run_list = run_list->next;
+ }
+}
+
+/**
+ * pango_layout_index_to_line_x:
+ * @layout: a #PangoLayout
+ * @index: the byte index of the character to find
+ * @trailing: whether we should compute the result for the beginning
+ * or end of the character (or cluster - the decision
+ * for which may be script dependent).
+ * @line: location to store resulting line index. (which will
+ * between 0 and pango_layout_get_line_count(layout) - 1)
+ * @x_pos: location to store resulting position within line
+ * (in thousandths of a device unit)
+ *
+ * Converts from byte index within the layout to line and X position.
+ * (X position is measured from the left edge of the line)
+ */
+void
+pango_layout_index_to_line_x (PangoLayout *layout,
+ int index,
+ gboolean trailing,
+ int *line,
+ int *x_pos)
+{
+ GSList *tmp_list;
+ int tmp_line = 0;
+ int bytes_seen = 0;
+
+ g_return_if_fail (line != NULL);
+
+ pango_layout_check_lines (layout);
+
+ tmp_list = layout->lines;
+ while (tmp_list)
+ {
+ PangoLayoutLine *layout_line = tmp_list->data;
+
+ if (bytes_seen + layout_line->length > index)
+ {
+ if (line)
+ *line = tmp_line;
+
+ pango_layout_line_index_to_x (layout_line, index, trailing, x_pos);
+ return;
+ }
+
+ tmp_list = tmp_list->next;
+ bytes_seen += layout_line->length;
+ }
+
+ if (line)
+ *line = -1;
+ if (x_pos)
+ *x_pos = -1;
+}
+
+static void
+pango_layout_clear_lines (PangoLayout *layout)
+{
+ if (layout->lines)
+ {
+ GSList *tmp_list = layout->lines;
+ while (tmp_list)
+ {
+ PangoLayoutLine *line = tmp_list->data;
+ tmp_list = tmp_list->next;
+
+ line->layout = NULL;
+ pango_layout_line_unref (line);
+ }
+
+ g_slist_free (layout->lines);
+ layout->lines = NULL;
+ }
+}
+
+/*****************
+ * Line Breaking *
+ *****************/
+
+static void
+insert_run (PangoLayoutLine *line, PangoItem *item, PangoGlyphString *glyphs)
+{
+ PangoLayoutRun *run = g_new (PangoLayoutRun, 1);
+
+ run->item = item;
+ run->glyphs = glyphs;
+
+ line->runs = g_slist_prepend (line->runs, run);
+ line->length += item->length;
+}
+
+static gboolean
+process_item (PangoLayoutLine *line, PangoItem *item, char *text, int *remaining_width)
+{
+ PangoGlyphString *glyphs = pango_glyph_string_new ();
+ PangoRectangle logical_rect;
+ int width;
+
+ if (*remaining_width == 0)
+ return FALSE;
+
+ pango_shape (text + item->offset, item->length, &item->analysis, glyphs);
+ pango_glyph_string_extents (glyphs, item->analysis.font, NULL, &logical_rect);
+ width = logical_rect.width;
+
+ if (logical_rect.width < *remaining_width)
+ {
+ *remaining_width -= width;
+ insert_run (line, item, glyphs);
+
+ return TRUE;
+ }
+ else
+ {
+ int length;
+ int num_chars = item->num_chars;
+ int new_width;
+
+ PangoLogAttr *log_attrs = g_new (PangoLogAttr, item->num_chars);
+ PangoGlyphUnit *log_widths = g_new (PangoGlyphUnit, item->num_chars);
+
+ pango_break (text + item->offset, item->length, &item->analysis, log_attrs);
+ pango_glyph_string_get_logical_widths (glyphs, text + item->offset, item->length, item->analysis.level, log_widths);
+
+ new_width = 0;
+ while (--num_chars > 0)
+ {
+ /* Shorten the item by one line break
+ */
+ width -= log_widths[num_chars];
+ if (log_attrs[num_chars].is_break && width <= *remaining_width)
+ break;
+ }
+
+ g_free (log_attrs);
+ g_free (log_widths);
+
+ if (num_chars != 0) /* Succesfully broke the item */
+ {
+ char *p;
+ gint n;
+
+ PangoItem *new_item = g_new (PangoItem, 1);
+
+ /* Determine utf8 length corresponding to num_chars. Slow?
+ */
+ n = num_chars;
+ p = text + item->offset;
+ while (n-- > 0)
+ p = unicode_next_utf8 (p);
+
+ length = p - (text + item->offset);
+
+ new_item->offset = item->offset;
+ new_item->length = length;
+ new_item->num_chars = num_chars;
+
+ new_item->analysis = item->analysis;
+ pango_font_ref (new_item->analysis.font);
+
+ item->offset += length;
+ item->length -= length;
+ item->num_chars -= num_chars;
+
+ pango_shape (text + new_item->offset, new_item->length, &new_item->analysis, glyphs);
+
+ *remaining_width -= width;
+ insert_run (line, new_item, glyphs);
+
+ return FALSE;
+ }
+ else
+ {
+ if (!line->runs) /* Only item, insert it anyways */
+ {
+ *remaining_width = 0;
+ insert_run (line, item, glyphs);
+
+ return TRUE;
+ }
+ else
+ {
+ pango_glyph_string_free (glyphs);
+ return FALSE;
+ }
+ }
+ }
+}
+
+static void
+pango_layout_check_lines (PangoLayout *layout)
+{
+ GList *items, *tmp_list;
+
+ PangoLayoutLine *line;
+ int remaining_width;
+
+ if (layout->lines)
+ return;
+
+ line = pango_layout_line_new (layout);
+ remaining_width = layout->first_line_width;
+
+ /* FIXME, should we force people to set the attrs? */
+ if (layout->attrs)
+ items = pango_itemize (layout->context, layout->text, layout->length, layout->attrs);
+ else
+ {
+ PangoAttrList *attrs = pango_attr_list_new ();
+ items = pango_itemize (layout->context, layout->text, layout->length, attrs);
+ pango_attr_list_unref (attrs);
+ }
+
+ tmp_list = items;
+
+ while (tmp_list)
+ {
+ PangoItem *item = tmp_list->data;
+ gboolean fits;
+
+ fits = process_item (line, item, layout->text, &remaining_width);
+
+ if (fits)
+ tmp_list = tmp_list->next;
+
+ if (!fits)
+ {
+ /* Complete line
+ */
+ line->runs = g_slist_reverse (line->runs);
+ pango_layout_line_reorder (line);
+
+ layout->lines = g_slist_prepend (layout->lines, line);
+
+ line = pango_layout_line_new (layout);
+ remaining_width = layout->width;
+ }
+ }
+
+ line->runs = g_slist_reverse (line->runs);
+ pango_layout_line_reorder (line);
+
+ layout->lines = g_slist_prepend (layout->lines, line);
+
+ g_list_free (tmp_list);
+ layout->lines = g_slist_reverse (layout->lines);
+}
+
+/**
+ * pango_layout_line_ref:
+ * @line: a #PangoLayoutLine
+ *
+ * Increase the reference count of a #PangoLayoutLine by one.
+ **/
+void
+pango_layout_line_ref (PangoLayoutLine *line)
+{
+ PangoLayoutLinePrivate *private = (PangoLayoutLinePrivate *)line;
+
+ g_return_if_fail (line != NULL);
+
+ private->ref_count++;
+}
+
+/**
+ * pango_layout_line_unref:
+ * @line:
+ *
+ * Decrease the reference count of a #PangoLayoutLine by one.
+ * if the result is zero, the line and all associated memory
+ * will be freed.
+ **/
+void
+pango_layout_line_unref (PangoLayoutLine *line)
+{
+ PangoLayoutLinePrivate *private = (PangoLayoutLinePrivate *)line;
+
+ g_return_if_fail (line != NULL);
+ g_return_if_fail (private->ref_count > 0);
+
+ private->ref_count--;
+ if (private->ref_count == 0)
+ {
+ GSList *tmp_list = line->runs;
+
+ while (tmp_list)
+ {
+ PangoLayoutRun *run = tmp_list->data;
+
+ pango_font_unref (run->item->analysis.font);
+ g_free (run->item);
+
+ pango_glyph_string_free (run->glyphs);
+
+ tmp_list = tmp_list->next;
+ }
+
+ g_slist_free (line->runs);
+ g_free (line);
+ }
+}
+
+/**
+ * pango_layout_line_x_to_index:
+ * @line: a #PangoLayoutLine
+ * @x_pos: the x offset (in thousands of a device unit)
+ * from the left edge of the line.
+ * @index: location to store calculated byte offset.
+ * @trailing: location to store a integer indicating where
+ * in the cluster the user clicked. If the script
+ * allows positioning within the cluster, it is either
+ * 0 or 1; otherwise it is either 0 or the number
+ * of characters in the cluster. In either case
+ * 0 represents the trailing edge of the cluster.
+ *
+ * Convert from x offset to the byte index of the corresponding
+ * character within the text of the layout.
+ *
+ * Return value: %TRUE if the x index was within the line
+ */
+gboolean
+pango_layout_line_x_to_index (PangoLayoutLine *line,
+ int x_pos,
+ int *index,
+ int *trailing)
+{
+ GSList *tmp_list;
+ gint start_pos = 0;
+
+ g_return_val_if_fail (LINE_IS_VALID (line), FALSE);
+
+ if (!LINE_IS_VALID (line))
+ return FALSE;
+
+ tmp_list = line->runs;
+ while (tmp_list)
+ {
+ PangoRectangle logical_rect;
+ PangoLayoutRun *run = tmp_list->data;
+
+ pango_glyph_string_extents (run->glyphs, run->item->analysis.font, NULL, &logical_rect);
+
+ if (x_pos >= start_pos && x_pos < start_pos + logical_rect.width)
+ {
+ int pos;
+
+ pango_glyph_string_x_to_index (run->glyphs,
+ line->layout->text + run->item->offset, run->item->length,
+ &run->item->analysis,
+ x_pos - start_pos,
+ &pos, trailing);
+
+ if (index)
+ *index = pos + run->item->offset;
+
+ return TRUE;
+ }
+
+ start_pos += logical_rect.width;
+ tmp_list = tmp_list->next;
+ }
+
+ return FALSE;
+}
+
+/**
+ * pango_layout_line_get_extents:
+ * @line: a #PangoLayoutLine
+ * @ink_rect: rectangle used to store the extents of the glyph string as drawn
+ * or %NULL to indicate that the result is not needed.
+ * @logical_rect: rectangle used to store the logical extents of the glyph string
+ * or %NULL to indicate that the result is not needed.
+ *
+ * Compute the logical and ink extents of a layout line. See the documentation
+ * for pango_font_get_glyph_extents() for details about the interpretation
+ * of the rectangles.
+ */
+void
+pango_layout_line_get_extents (PangoLayoutLine *line,
+ PangoRectangle *ink_rect,
+ PangoRectangle *logical_rect)
+{
+ GSList *tmp_list;
+ int x_pos = 0;
+
+ g_return_if_fail (LINE_IS_VALID (line));
+
+ if (!LINE_IS_VALID (line))
+ return;
+
+ if (ink_rect)
+ {
+ ink_rect->x = 0;
+ ink_rect->y = 0;
+ ink_rect->width = 0;
+ ink_rect->height = 0;
+ }
+
+ if (logical_rect)
+ {
+ logical_rect->x = 0;
+ logical_rect->y = 0;
+ logical_rect->width = 0;
+ logical_rect->height = 0;
+ }
+
+ tmp_list = line->runs;
+ while (tmp_list)
+ {
+ PangoLayoutRun *run = tmp_list->data;
+ int new_pos;
+
+ PangoRectangle run_ink;
+ PangoRectangle run_logical;
+
+ pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
+ ink_rect ? &run_ink : NULL,
+ &run_logical);
+
+ if (ink_rect)
+ {
+ new_pos = MIN (ink_rect->x, x_pos + run_ink.x);
+ ink_rect->width = MAX (ink_rect->x + ink_rect->width,
+ x_pos + run_ink.x + run_ink.width) - new_pos;
+ ink_rect->x = new_pos;
+
+ new_pos = MIN (ink_rect->y, run_ink.y);
+ ink_rect->height = MAX (ink_rect->y + ink_rect->height,
+ run_ink.y + run_ink.height) - new_pos;
+ ink_rect->y = new_pos;
+ }
+
+ if (logical_rect)
+ {
+ new_pos = MIN (logical_rect->x, x_pos + run_logical.x);
+ logical_rect->width = MAX (logical_rect->x + logical_rect->width,
+ x_pos + run_logical.x + run_logical.width) - new_pos;
+ logical_rect->x = new_pos;
+
+ new_pos = MIN (logical_rect->y, run_logical.y);
+ logical_rect->height = MAX (logical_rect->y + logical_rect->height,
+ run_logical.y + run_logical.height) - new_pos;
+ logical_rect->y = new_pos;
+ }
+
+ x_pos += run_logical.width;
+ tmp_list = tmp_list->next;
+ }
+}
+
+static PangoLayoutLine *
+pango_layout_line_new (PangoLayout *layout)
+{
+ PangoLayoutLinePrivate *private = g_new (PangoLayoutLinePrivate, 1);
+
+ private->ref_count = 1;
+ private->line.layout = layout;
+ private->line.runs = 0;
+ private->line.length = 0;
+
+ return (PangoLayoutLine *) private;
+}
+
+
+/*
+ * NB: The contents of the file implement the exact same algorithm
+ * reorder-items.c:pango_reorder_items().
+ */
+
+static GSList *
+reorder_runs_recurse (GSList *items, int n_items)
+{
+ GSList *tmp_list, *level_start_node;
+ int i, level_start_i;
+ int min_level = G_MAXINT;
+ GSList *result = NULL;
+
+ if (n_items == 0)
+ return NULL;
+
+ tmp_list = items;
+ for (i=0; i<n_items; i++)
+ {
+ PangoLayoutRun *run = tmp_list->data;
+
+ min_level = MIN (min_level, run->item->analysis.level);
+
+ tmp_list = tmp_list->next;
+ }
+
+ level_start_i = 0;
+ level_start_node = items;
+ tmp_list = items;
+ for (i=0; i<n_items; i++)
+ {
+ PangoLayoutRun *run = tmp_list->data;
+
+ if (run->item->analysis.level == min_level)
+ {
+ if (min_level % 2)
+ {
+ if (i > level_start_i)
+ result = g_slist_concat (reorder_runs_recurse (level_start_node, i - level_start_i), result);
+ result = g_slist_prepend (result, run);
+ }
+ else
+ {
+ if (i > level_start_i)
+ result = g_slist_concat (result, reorder_runs_recurse (level_start_node, i - level_start_i));
+ result = g_slist_append (result, run);
+ }
+
+ level_start_i = i + 1;
+ level_start_node = tmp_list->next;
+ }
+
+ tmp_list = tmp_list->next;
+ }
+
+ if (min_level % 2)
+ {
+ if (i > level_start_i)
+ result = g_slist_concat (reorder_runs_recurse (level_start_node, i - level_start_i), result);
+ }
+ else
+ {
+ if (i > level_start_i)
+ result = g_slist_concat (result, reorder_runs_recurse (level_start_node, i - level_start_i));
+ }
+
+ return result;
+}
+
+static void
+pango_layout_line_reorder (PangoLayoutLine *line)
+{
+ GSList *logical_runs = line->runs;
+ line->runs = reorder_runs_recurse (logical_runs, g_slist_length (logical_runs));
+ g_slist_free (logical_runs);
+}
+
diff --git a/pango/pango-layout.h b/pango/pango-layout.h
index b9c1ea6f..75e82bad 100644
--- a/pango/pango-layout.h
+++ b/pango/pango-layout.h
@@ -22,6 +22,10 @@
#ifndef __PANGO_LAYOUT_H__
#define __PANGO_LAYOUT_H__
+#include <pango-attributes.h>
+#include <pango-context.h>
+#include <pango-glyph.h>
+
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
@@ -33,7 +37,6 @@ typedef struct _PangoLayoutRun PangoLayoutRun;
struct _PangoLayoutLine
{
PangoLayout *layout;
- gint n_chars; /* length of line in characters */
gint length; /* length of line in bytes*/
GSList *runs;
};
@@ -44,37 +47,43 @@ struct _PangoLayoutRun
PangoGlyphString *glyphs;
};
-PangoLayout * pango_layout_new (void);
-void pango_layout_ref (PangoLayout *layout);
-void pango_layout_unref (PangoLayout *layout);
-void pango_layout_set_width (PangoLayout *layout,
- int width);
-void pango_layout_set_justify (PangoLayout *layout,
- gboolean justify);
-void pango_layout_set_first_line_width (PangoLayout *layout,
- int width);
-void pango_layout_set_attributes (PangoLayout *layout,
- PangoAttrList *attrs);
-void pango_layout_set_text (char *text,
- int length);
-int pango_layout_get_line_count (PangoLayout *layout);
-PangoLayoutLine *pango_layout_get_line (PangoLayout *layout,
- int line);
-void pango_layout_cp_to_line_x (PangoLayout *layout,
- gint char_pos,
- gboolean trailing,
- gint *line,
- gint *x_pos);
+PangoLayout * pango_layout_new (PangoContext *context);
+void pango_layout_ref (PangoLayout *layout);
+void pango_layout_unref (PangoLayout *layout);
+void pango_layout_set_width (PangoLayout *layout,
+ int width);
+void pango_layout_set_justify (PangoLayout *layout,
+ gboolean justify);
+void pango_layout_set_first_line_width (PangoLayout *layout,
+ int width);
+void pango_layout_set_attributes (PangoLayout *layout,
+ PangoAttrList *attrs);
+void pango_layout_set_text (PangoLayout *layout,
+ char *text,
+ int length);
+int pango_layout_get_line_count (PangoLayout *layout);
+PangoLayoutLine * pango_layout_get_line (PangoLayout *layout,
+ int line);
+GSList * pango_layout_get_lines (PangoLayout *layout);
+void pango_layout_index_to_line_x (PangoLayout *layout,
+ int index,
+ gboolean trailing,
+ int *line,
+ int *x_pos);
-void pango_layout_line_ref (PangoLayoutLine *line);
-void pango_layout_line_unref (PangoLayoutLine *line);
-void pango_layout_line_x_to_cp (PangoLayoutLine *line,
- gint x_pos,
- gint *char_pos,
- gint *trailing);
-void pango_layout_line_get_extents (PangoLayoutLine *line,
- PangoRectangle *ink_rect,
- PangoRectangle *logical_rect);
+void pango_layout_line_ref (PangoLayoutLine *line);
+void pango_layout_line_unref (PangoLayoutLine *line);
+gboolean pango_layout_line_x_to_index (PangoLayoutLine *line,
+ int x_pos,
+ int *index,
+ int *trailing);
+void pango_layout_line_index_to_x (PangoLayoutLine *line,
+ int index,
+ gboolean trailing,
+ int *x_pos);
+void pango_layout_line_get_extents (PangoLayoutLine *line,
+ PangoRectangle *ink_rect,
+ PangoRectangle *logical_rect);
#ifdef __cplusplus
}
diff --git a/pango/pangox.c b/pango/pangox.c
index f6a7d142..423722e5 100644
--- a/pango/pangox.c
+++ b/pango/pangox.c
@@ -1316,8 +1316,8 @@ pango_x_render (Display *display,
GC gc,
PangoFont *font,
PangoGlyphString *glyphs,
- int x,
- int y)
+ int x,
+ int y)
{
/* Slow initial implementation. For speed, it should really
* collect the characters into runs, and draw multiple
@@ -1540,43 +1540,43 @@ pango_x_make_matching_xlfd (PangoXFontMap *xfontmap, char *xlfd, const char *cha
if (closest_match)
{
- char *prefix_end, *p;
- char *size_end;
- int n_dashes = 0;
-
- /* OK, we have a match; let's modify it to fit this size and charset */
-
- p = closest_match;
- while (n_dashes < 6)
- {
- if (*p == '-')
- n_dashes++;
- p++;
- }
-
- prefix_end = p - 1;
-
- while (n_dashes < 9)
+ if (match_scaleable)
{
- if (*p == '-')
- n_dashes++;
- p++;
- }
+ char *prefix_end, *p;
+ char *size_end;
+ int n_dashes = 0;
+ int target_size;
+ char *prefix;
+
+ /* OK, we have a match; let's modify it to fit this size and charset */
- size_end = p - 1;
+ p = closest_match;
+ while (n_dashes < 6)
+ {
+ if (*p == '-')
+ n_dashes++;
+ p++;
+ }
+
+ prefix_end = p - 1;
+
+ while (n_dashes < 9)
+ {
+ if (*p == '-')
+ n_dashes++;
+ p++;
+ }
+
+ size_end = p - 1;
- if (match_scaleable)
- {
- int target_size = (int)((double)size / xfontmap->resolution + 0.5);
- char *prefix = g_strndup (closest_match, prefix_end - closest_match);
+ target_size = (int)((double)size / xfontmap->resolution + 0.5);
+ prefix = g_strndup (closest_match, prefix_end - closest_match);
result = g_strdup_printf ("%s--%d-*-*-*-*-*-%s", prefix, target_size, charset);
g_free (prefix);
}
else
{
- char *prefix = g_strndup (closest_match, size_end - closest_match);
- result = g_strconcat (prefix, "-*-*-*-*-", charset, NULL);
- g_free (prefix);
+ result = g_strdup (closest_match);
}
}
@@ -2002,3 +2002,47 @@ pango_x_get_unknown_glyph (PangoFont *font)
return 0;
}
+/**
+ * pango_x_render_layout_line:
+ * @display: the X display
+ * @d: the drawable on which to draw string
+ * @gc: the graphics context
+ * @line: a #PangoLayoutLine
+ * @glyphs: the glyph string to draw
+ * @x: the x position of start of string (in pixels)
+ * @y: the y position of baseline (in pixels)
+ *
+ * Render a #PangoLayoutLine onto an X drawable
+ */
+void
+pango_x_render_layout_line (Display *display,
+ Drawable d,
+ GC gc,
+ PangoLayoutLine *line,
+ int x,
+ int y)
+{
+ GSList *tmp_list = line->runs;
+ PangoRectangle logical_rect;
+
+ int x_off = 0;
+
+ pango_layout_line_get_extents (line,NULL, &logical_rect);
+ y += PANGO_ASCENT (logical_rect) / 1000;
+
+ while (tmp_list)
+ {
+ PangoLayoutRun *run = tmp_list->data;
+ tmp_list = tmp_list->next;
+
+ pango_x_render (display, d, gc, run->item->analysis.font, run->glyphs,
+ x + x_off / 1000, y);
+
+ if (tmp_list)
+ {
+ pango_glyph_string_extents (run->glyphs, run->item->analysis.font,
+ NULL, &logical_rect);
+ x_off += logical_rect.width;
+ }
+ }
+}
diff --git a/pango/pangox.h b/pango/pangox.h
index e24c6d0d..2e92347d 100644
--- a/pango/pangox.h
+++ b/pango/pangox.h
@@ -35,35 +35,22 @@ extern "C" {
/* Calls for applications
*/
-PangoContext *pango_x_get_context (Display *display);
-
-PangoFont *pango_x_load_font (Display *display,
- gchar *spec);
-void pango_x_render (Display *display,
- Drawable d,
- GC gc,
- PangoFont *font,
- PangoGlyphString *glyphs,
- gint x,
- gint y);
-void pango_x_extents (PangoFont *font,
- PangoGlyphString *glyphs,
- gint *lbearing,
- gint *rbearing,
- gint *width,
- gint *ascent,
- gint *descent,
- gint *logical_ascent,
- gint *logical_descent);
-void pango_x_glyph_extents (PangoFont *font,
- PangoGlyph glyph,
- gint *lbearing,
- gint *rbearing,
- gint *width,
- gint *ascent,
- gint *descent,
- gint *logical_ascent,
- gint *logical_descent);
+PangoContext * pango_x_get_context (Display *display);
+PangoFont * pango_x_load_font (Display *display,
+ gchar *spec);
+void pango_x_render (Display *display,
+ Drawable d,
+ GC gc,
+ PangoFont *font,
+ PangoGlyphString *glyphs,
+ gint x,
+ gint y);
+void pango_x_render_layout_line (Display *display,
+ Drawable d,
+ GC gc,
+ PangoLayoutLine *line,
+ int x,
+ int y);
/* API for rendering modules
*/
diff --git a/pango/reorder-items.c b/pango/reorder-items.c
index bbe415b7..18d68ab6 100644
--- a/pango/reorder-items.c
+++ b/pango/reorder-items.c
@@ -21,6 +21,11 @@
#include <pango.h>
+/*
+ * NB: The contents of the file implement the exact same algorithm
+ * pango-layout.c:pango_layout_line_reorder().
+ */
+
static GList *reorder_items_recurse (GList *items, int n_items);
/**
@@ -32,6 +37,11 @@ static GList *reorder_items_recurse (GList *items, int n_items);
* The original list is unmodified.
*
* Returns a GList of PangoItem structures in visual order.
+ *
+ * (Please mail otaylor@redhat.com if you use this function.
+ * It is not a particularly convenient interface, and the code
+ * is duplicated elsewhere in Pango for that reason.)
+ *
*/
GList *
pango_reorder_items (GList *logical_items)