[pango/line-breaker: 18/49] Add PangoLineIter
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [pango/line-breaker: 18/49] Add PangoLineIter
- Date: Mon, 24 Jan 2022 16:03:04 +0000 (UTC)
commit 1f655e6b71096ff32a9e479bdcc702ed24131923
Author: Matthias Clasen <mclasen redhat com>
Date: Mon Jan 17 00:08:57 2022 -0500
Add PangoLineIter
pango/meson.build | 2 +
pango/pango-line-iter-private.h | 5 +
pango/pango-line-iter.c | 869 ++++++++++++++++++++++++++++++++++++++++
pango/pango-line-iter.h | 84 ++++
pango/pango-lines.c | 21 +
pango/pango-lines.h | 5 +
pango/pango-simple-layout.c | 21 +
pango/pango-simple-layout.h | 3 +
pango/pango.h | 1 +
9 files changed, 1011 insertions(+)
---
diff --git a/pango/meson.build b/pango/meson.build
index 03373418..d29df3ba 100644
--- a/pango/meson.build
+++ b/pango/meson.build
@@ -35,6 +35,7 @@ pango_sources = [
'pango-line.c',
'pango-line-breaker.c',
'pango-lines.c',
+ 'pango-line-iter.c',
'pango-simple-layout.c',
]
@@ -60,6 +61,7 @@ pango_headers = [
'pango-layout-run.h',
'pango-line.h',
'pango-line-breaker.h',
+ 'pango-line-iter.h',
'pango-lines.h',
'pango-layout.h',
'pango-matrix.h',
diff --git a/pango/pango-line-iter-private.h b/pango/pango-line-iter-private.h
new file mode 100644
index 00000000..e9ca3799
--- /dev/null
+++ b/pango/pango-line-iter-private.h
@@ -0,0 +1,5 @@
+#pragma once
+
+#include "pango-line-iter.h"
+
+PangoLineIter * pango_line_iter_new (PangoLines *lines);
diff --git a/pango/pango-line-iter.c b/pango/pango-line-iter.c
new file mode 100644
index 00000000..ca31a1a3
--- /dev/null
+++ b/pango/pango-line-iter.c
@@ -0,0 +1,869 @@
+#include "config.h"
+
+#include "pango-line-iter-private.h"
+#include "pango-lines-private.h"
+#include "pango-line-private.h"
+#include "pango-layout-run-private.h"
+
+/* {{{ PangoLineIter implementation */
+
+struct _PangoLineIter
+{
+ PangoLines *lines;
+ guint serial;
+
+ int line_no;
+ int line_x;
+ int line_y;
+ PangoLine *line;
+ GSList *run_link;
+ PangoLayoutRun *run;
+ int index;
+
+ /* run handling */
+ int run_x;
+ int run_width;
+ int end_x_offset;
+ gboolean ltr;
+
+ /* cluster handling */
+ int cluster_x;
+ int cluster_width;
+ int cluster_start;
+ int next_cluster_glyph;
+ int cluster_num_chars;
+
+ int character_position;
+};
+
+G_DEFINE_BOXED_TYPE (PangoLineIter, pango_line_iter,
+ pango_line_iter_copy, pango_line_iter_free);
+
+
+/* }}} */
+/* {{{ Utilities */
+
+#define ITER_IS_VALID(iter) ((iter)->serial == (iter)->lines->serial)
+
+static gboolean
+line_is_terminated (PangoLineIter *iter)
+{
+ if (iter->line_no + 1 < pango_lines_get_line_count (iter->lines))
+ return pango_line_ends_paragraph (iter->line);
+
+ return FALSE;
+
+}
+
+static int
+next_cluster_start (PangoGlyphString *glyphs,
+ int cluster_start)
+{
+ int i;
+
+ i = cluster_start + 1;
+ while (i < glyphs->num_glyphs)
+ {
+ if (glyphs->glyphs[i].attr.is_cluster_start)
+ return i;
+
+ i++;
+ }
+
+ return glyphs->num_glyphs;
+}
+
+static int
+cluster_width (PangoGlyphString *glyphs,
+ int cluster_start)
+{
+ int i;
+ int width;
+
+ width = glyphs->glyphs[cluster_start].geometry.width;
+ i = cluster_start + 1;
+ while (i < glyphs->num_glyphs)
+ {
+ if (glyphs->glyphs[i].attr.is_cluster_start)
+ break;
+
+ width += glyphs->glyphs[i].geometry.width;
+ i++;
+ }
+
+ return width;
+}
+
+/* Sets up the iter for the start of a new cluster. cluster_start_index
+ * is the byte index of the cluster start relative to the run.
+ */
+static void
+update_cluster (PangoLineIter *iter,
+ int cluster_start_index)
+{
+ PangoGlyphItem *glyph_item;
+ char *cluster_text;
+ int cluster_length;
+
+ glyph_item = pango_layout_run_get_glyph_item (iter->run);
+
+ iter->character_position = 0;
+
+ iter->cluster_width = cluster_width (glyph_item->glyphs, iter->cluster_start);
+ iter->next_cluster_glyph = next_cluster_start (glyph_item->glyphs, iter->cluster_start);
+
+ if (iter->ltr)
+ {
+ /* For LTR text, finding the length of the cluster is easy
+ * since logical and visual runs are in the same direction.
+ */
+ if (iter->next_cluster_glyph < glyph_item->glyphs->num_glyphs)
+ cluster_length = glyph_item->glyphs->log_clusters[iter->next_cluster_glyph] - cluster_start_index;
+ else
+ cluster_length = glyph_item->item->length - cluster_start_index;
+ }
+ else
+ {
+ /* For RTL text, we have to scan backwards to find the previous
+ * visual cluster which is the next logical cluster.
+ */
+ int i = iter->cluster_start;
+ while (i > 0 && glyph_item->glyphs->log_clusters[i - 1] == cluster_start_index)
+ i--;
+
+ if (i == 0)
+ cluster_length = glyph_item->item->length - cluster_start_index;
+ else
+ cluster_length = glyph_item->glyphs->log_clusters[i - 1] - cluster_start_index;
+ }
+
+ cluster_text = iter->line->data->text + glyph_item->item->offset + cluster_start_index;
+ iter->cluster_num_chars = g_utf8_strlen (cluster_text, cluster_length);
+
+ if (iter->ltr)
+ iter->index = cluster_text - iter->line->data->text;
+ else
+ iter->index = g_utf8_prev_char (cluster_text + cluster_length) - iter->line->data->text;
+}
+
+/* Moves to the next non-empty line. If @include_terminators
+ * is set, a line with just an explicit paragraph separator
+ * is considered non-empty.
+ */
+static gboolean
+next_nonempty_line (PangoLineIter *iter,
+ gboolean include_terminators)
+{
+ gboolean result;
+
+ while (TRUE)
+ {
+ result = pango_line_iter_next_line (iter);
+ if (!result)
+ break;
+
+ if (iter->line->runs)
+ break;
+
+ if (include_terminators && line_is_terminated (iter))
+ break;
+ }
+
+ return result;
+}
+
+/* Moves to the next non-empty run. If @include_terminators
+ * is set, the trailing run at the end of a line with an explicit
+ * paragraph separator is considered non-empty.
+ */
+static gboolean
+next_nonempty_run (PangoLineIter *iter,
+ gboolean include_terminators)
+{
+ gboolean result;
+
+ while (TRUE)
+ {
+ result = pango_line_iter_next_run (iter);
+ if (!result)
+ break;
+
+ if (iter->run)
+ break;
+
+ if (include_terminators && line_is_terminated (iter))
+ break;
+ }
+
+ return result;
+}
+
+/* Like pango_layout_next_cluster(), but if @include_terminators
+ * is set, includes the fake runs/clusters for empty lines.
+ * (But not positions introduced by line wrapping).
+ */
+static gboolean
+next_cluster_internal (PangoLineIter *iter,
+ gboolean include_terminators)
+{
+ PangoGlyphItem *glyph_item;
+
+ if (iter->run == NULL)
+ return next_nonempty_line (iter, include_terminators);
+
+ glyph_item = pango_layout_run_get_glyph_item (iter->run);
+
+ if (iter->next_cluster_glyph == glyph_item->glyphs->num_glyphs)
+ {
+ return next_nonempty_run (iter, include_terminators);
+ }
+ else
+ {
+ iter->cluster_start = iter->next_cluster_glyph;
+ iter->cluster_x += iter->cluster_width;
+ update_cluster (iter, glyph_item->glyphs->log_clusters[iter->cluster_start]);
+
+ return TRUE;
+ }
+}
+
+static void
+update_run (PangoLineIter *iter,
+ int start_index)
+{
+ PangoGlyphItem *glyph_item;
+
+ if (iter->run)
+ glyph_item = pango_layout_run_get_glyph_item (iter->run);
+
+ if (iter->run_link == iter->line->runs)
+ iter->run_x = 0;
+ else
+ {
+ iter->run_x += iter->end_x_offset + iter->run_width;
+ if (iter->run)
+ iter->run_x += glyph_item->start_x_offset;
+ }
+
+ if (iter->run)
+ {
+ iter->run_width = pango_glyph_string_get_width (glyph_item->glyphs);
+ iter->end_x_offset = glyph_item->end_x_offset;
+ }
+ else
+ {
+ /* The empty run at the end of a line */
+ iter->run_width = 0;
+ iter->end_x_offset = 0;
+ }
+
+ if (iter->run)
+ iter->ltr = (glyph_item->item->analysis.level % 2) == 0;
+ else
+ iter->ltr = TRUE;
+
+ iter->cluster_start = 0;
+ iter->cluster_x = iter->run_x;
+
+ if (iter->run)
+ {
+ update_cluster (iter, glyph_item->glyphs->log_clusters[0]);
+ }
+ else
+ {
+ iter->cluster_width = 0;
+ iter->character_position = 0;
+ iter->cluster_num_chars = 0;
+ iter->index = start_index;
+ }
+}
+
+static inline void
+offset_line (PangoLineIter *iter,
+ PangoRectangle *ink_rect,
+ PangoRectangle *logical_rect)
+{
+ if (ink_rect)
+ {
+ ink_rect->x += iter->line_x;
+ ink_rect->y += iter->line_y;
+ }
+ if (logical_rect)
+ {
+ logical_rect->x += iter->line_x;
+ logical_rect->y += iter->line_y;
+ }
+}
+
+static inline void
+offset_run (PangoLineIter *iter,
+ PangoRectangle *ink_rect,
+ PangoRectangle *logical_rect)
+{
+ if (ink_rect)
+ ink_rect->x += iter->run_x;
+ if (logical_rect)
+ logical_rect->x += iter->run_x;
+}
+
+/* }}} */
+/* {{{ Private API */
+
+PangoLineIter *
+pango_line_iter_new (PangoLines *lines)
+{
+ PangoLineIter *iter;
+ int run_start_index;
+
+ g_return_val_if_fail (PANGO_IS_LINES (lines), NULL);
+
+ iter = g_new0 (PangoLineIter, 1);
+
+ iter->lines = g_object_ref (lines);
+ iter->serial = pango_lines_get_serial (lines);
+
+ iter->line_no = 0;
+ iter->line = pango_lines_get_line (iter->lines, 0, &iter->line_x, &iter->line_y);
+ iter->run_link = pango_line_get_runs (iter->line);
+ if (iter->run_link)
+ {
+ iter->run = iter->run_link->data;
+ run_start_index = iter->run->item->offset;
+ }
+ else
+ {
+ iter->run = NULL;
+ run_start_index = 0;
+ }
+
+ update_run (iter, run_start_index);
+
+ return iter;
+}
+
+/* }}} */
+/* {{{ Public API */
+
+/**
+ * pango_line_iter_copy:
+ * @iter: (nullable): a `PangoLineIter`
+ *
+ * Copies a `PangoLineIter`.
+ *
+ * Return value: (nullable): the newly allocated `PangoLineIter`
+ */
+PangoLineIter *
+pango_line_iter_copy (PangoLineIter *iter)
+{
+ PangoLineIter *copy;
+
+ if (iter == NULL)
+ return NULL;
+
+ copy = g_new0 (PangoLineIter, 1);
+ memcpy (iter, copy, sizeof (PangoLineIter));
+ g_object_ref (copy->lines);
+
+ return copy;
+}
+
+/**
+ * pango_line_iter_free:
+ * @iter: (nullable): a `PangoLineIter`
+ *
+ * Frees an iterator that's no longer in use.
+ */
+void
+pango_line_iter_free (PangoLineIter *iter)
+{
+ if (iter == NULL)
+ return;
+
+ g_object_unref (iter->lines);
+ g_free (iter);
+}
+
+/**
+ * pango_line_iter_get_lines:
+ * @iter: a `PangoLineIter`
+ *
+ * Gets the `PangoLines` object associated with a `PangoLineIter`.
+ *
+ * Return value: (transfer none): the lines associated with @iter
+ */
+PangoLines *
+pango_line_iter_get_lines (PangoLineIter *iter)
+{
+ return iter->lines;
+}
+
+/**
+ * pango_line_iter_get_line:
+ * @iter: a `PangoLineIter`
+ *
+ * Gets the current line.
+ *
+ * Return value: (transfer none): the current line
+ */
+PangoLine *
+pango_line_iter_get_line (PangoLineIter *iter)
+{
+ g_return_val_if_fail (ITER_IS_VALID (iter), NULL);
+
+ return iter->line;
+}
+
+/**
+ * pango_line_iter_at_last_line:
+ * @iter: a `PangoLineIter`
+ *
+ * Determines whether @iter is on the last line.
+ *
+ * Return value: %TRUE if @iter is on the last line
+ */
+gboolean
+pango_line_iter_at_last_line (PangoLineIter *iter)
+{
+ g_return_val_if_fail (ITER_IS_VALID (iter), FALSE);
+
+ return iter->line_no + 1 == pango_lines_get_line_count (iter->lines);
+}
+
+/**
+ * pango_line_iter_get_run:
+ * @iter: a `PangoLineIter`
+ *
+ * Gets the current run.
+ *
+ * When iterating by run, at the end of each line, there's a position
+ * with a %NULL run, so this function can return %NULL. The %NULL run
+ * at the end of each line ensures that all lines have at least one run,
+ * even lines consisting of only a newline.
+ *
+ * Return value: (transfer none) (nullable): the current run
+ */
+PangoLayoutRun *
+pango_line_iter_get_run (PangoLineIter *iter)
+{
+ g_return_val_if_fail (ITER_IS_VALID (iter), NULL);
+
+ return iter->run;
+}
+
+/**
+ * pango_line_iter_get_index:
+ * @iter: a `PangoLineIter`
+ *
+ * Gets the current byte index.
+ *
+ * The byte index is relative to the text backing the current
+ * line.
+ *
+ * Note that iterating forward by char moves in visual order,
+ * not logical order, so indexes may not be sequential. Also,
+ * the index may be equal to the length of the text in the
+ * layout, if on the %NULL run (see [method@Pango.LineIter.get_run]).
+ *
+ * Return value: current byte index
+ */
+int
+pango_line_iter_get_index (PangoLineIter *iter)
+{
+ g_return_val_if_fail (ITER_IS_VALID (iter), 0);
+
+ return iter->index;
+}
+
+/**
+ * pango_line_iter_next_line:
+ * @iter: a `PangoLineIter`
+ *
+ * Moves @iter forward to the start of the next line.
+ *
+ * If @iter is already on the last line, returns %FALSE.
+ *
+ * Return value: whether motion was possible
+ */
+gboolean
+pango_line_iter_next_line (PangoLineIter *iter)
+{
+ g_return_val_if_fail (ITER_IS_VALID (iter), FALSE);
+
+ iter->line = pango_lines_get_line (iter->lines, iter->line_no + 1, &iter->line_x, &iter->line_y);
+ if (!iter->line)
+ return FALSE;
+
+ iter->line_no++;
+ iter->run_link = pango_line_get_runs (iter->line);
+ if (iter->run_link)
+ iter->run = iter->run_link->data;
+ else
+ iter->run = NULL;
+
+ update_run (iter, iter->line->start_index);
+
+ return TRUE;
+}
+
+/**
+ * pango_line_iter_next_run:
+ * @iter: a `PangoLineIter`
+ *
+ * Moves @iter forward to the next run in visual order.
+ *
+ * If @iter was already at the end, returns %FALSE.
+ *
+ * Return value: whether motion was possible
+ */
+gboolean
+pango_line_iter_next_run (PangoLineIter *iter)
+{
+ int run_start_index;
+
+ g_return_val_if_fail (ITER_IS_VALID (iter), FALSE);
+
+ if (iter->run == NULL)
+ return pango_line_iter_next_line (iter);
+
+ iter->run_link = iter->run_link->next;
+ if (iter->run_link == NULL)
+ {
+ run_start_index = iter->run->item->offset + iter->run->item->length;
+ iter->run = NULL;
+ }
+ else
+ {
+ iter->run = iter->run_link->data;
+ run_start_index = iter->run->item->offset;
+ }
+
+ update_run (iter, run_start_index);
+
+ return TRUE;
+}
+
+/**
+ * pango_line_iter_next_cluster:
+ * @iter: a `PangoLineIter`
+ *
+ * Moves @iter forward to the next cluster in visual order.
+ *
+ * If @iter was already at the end, returns %FALSE.
+ *
+ * Return value: whether motion was possible
+ */
+gboolean
+pango_line_iter_next_cluster (PangoLineIter *iter)
+{
+ g_return_val_if_fail (ITER_IS_VALID (iter), FALSE);
+
+ return next_cluster_internal (iter, FALSE);
+}
+
+/**
+ * pango_line_iter_next_char:
+ * @iter: a `PangoLineIter`
+ *
+ * Moves @iter forward to the next character in visual order.
+ *
+ * If @iter was already at the end, returns %FALSE.
+ *
+ * Return value: whether motion was possible
+ */
+gboolean
+pango_line_iter_next_char (PangoLineIter *iter)
+{
+ const char *text;
+
+ g_return_val_if_fail (ITER_IS_VALID (iter), FALSE);
+
+ if (iter->run == NULL)
+ {
+ /* We need to fake an iterator position in the middle of a \r\n line terminator */
+ if (line_is_terminated (iter) &&
+ strncmp (iter->line->data->text + iter->line->start_index + iter->line->length, "\r\n", 2) == 0 &&
+ iter->character_position == 0)
+ {
+ iter->character_position++;
+
+ return TRUE;
+ }
+
+ return next_nonempty_line (iter, TRUE);
+ }
+
+ iter->character_position++;
+
+ if (iter->character_position >= iter->cluster_num_chars)
+ return next_cluster_internal (iter, TRUE);
+
+ text = iter->line->data->text;
+ if (iter->ltr)
+ iter->index = g_utf8_next_char (text + iter->index) - text;
+ else
+ iter->index = g_utf8_prev_char (text + iter->index) - text;
+
+ return TRUE;
+}
+
+/**
+ * pango_line_iter_get_layout_extents:
+ * @iter: a `PangoLineIter`
+ * @ink_rect: (out) (optional): rectangle to fill with ink extents
+ * @logical_rect: (out) (optional): rectangle to fill with logical extents
+ *
+ * Obtains the extents of the `PangoLines` being iterated over.
+ */
+void
+pango_line_iter_get_layout_extents (PangoLineIter *iter,
+ PangoRectangle *ink_rect,
+ PangoRectangle *logical_rect)
+{
+ g_return_if_fail (ITER_IS_VALID (iter));
+
+ pango_lines_get_extents (iter->lines, ink_rect, logical_rect);
+}
+
+/**
+ * pango_line_iter_get_line_extents:
+ * @iter: a `PangoLineIter`
+ * @ink_rect: (out) (optional): rectangle to fill with ink extents
+ * @logical_rect: (out) (optional): rectangle to fill with logical extents
+ *
+ * Obtains the extents of the current line.
+ *
+ * Extents are in layout coordinates (origin is the top-left corner of the
+ * entire `PangoLines`). Thus the extents returned by this function will be
+ * the same width/height but not at the same x/y as the extents returned
+ * from [method Pango Line.get_extents].
+ *
+ * The logical extents returned by this function always have their leading
+ * trimmed according to paragraph boundaries: if the line starts a paragraph,
+ * it has its start leading trimmed; if it ends a paragraph, it has its end
+ * leading trimmed. If you need other trimming, use
+ * [method Pango Line.get_trimmed_extents].
+ */
+void
+pango_line_iter_get_line_extents (PangoLineIter *iter,
+ PangoRectangle *ink_rect,
+ PangoRectangle *logical_rect)
+{
+ g_return_if_fail (ITER_IS_VALID (iter));
+
+ pango_line_get_extents (iter->line, ink_rect, logical_rect);
+ offset_line (iter, ink_rect, logical_rect);
+}
+
+void
+pango_line_iter_get_trimmed_line_extents (PangoLineIter *iter,
+ PangoLeadingTrim trim,
+ PangoRectangle *logical_rect)
+{
+ g_return_if_fail (ITER_IS_VALID (iter));
+
+ pango_line_get_trimmed_extents (iter->line, trim, logical_rect);
+ offset_line (iter, NULL, logical_rect);
+}
+
+/**
+ * pango_line_iter_get_run_extents:
+ * @iter: a `PangoLineIter`
+ * @ink_rect: (out) (optional): rectangle to fill with ink extents
+ * @logical_rect: (out) (optional): rectangle to fill with logical extents
+ *
+ * Gets the extents of the current run in layout coordinates.
+ *
+ * Layout coordinates have the origin at the top left of the entire `PangoLines`.
+ *
+ * The logical extents returned by this function always have their leading
+ * trimmed off. If you need extents that include leading, use
+ * [method@Pango.LayoutRun.get_extents].
+ */
+void
+pango_line_iter_get_run_extents (PangoLineIter *iter,
+ PangoRectangle *ink_rect,
+ PangoRectangle *logical_rect)
+{
+ g_return_if_fail (ITER_IS_VALID (iter));
+
+ if (iter->run)
+ {
+ pango_layout_run_get_extents (iter->run, PANGO_LEADING_TRIM_BOTH, ink_rect, logical_rect);
+ }
+ else
+ {
+ GSList *runs = pango_line_get_runs (iter->line);
+ if (runs)
+ {
+ /* Virtual run at the end of a nonempty line */
+ PangoLayoutRun *run = g_slist_last (runs)->data;
+
+ pango_layout_run_get_extents (run, PANGO_LEADING_TRIM_BOTH, ink_rect, logical_rect);
+ if (ink_rect)
+ ink_rect->width = 0;
+ if (logical_rect)
+ logical_rect->width = 0;
+ }
+ else
+ {
+ /* Empty line */
+ PangoRectangle r;
+
+ pango_line_get_empty_extents (iter->line, PANGO_LEADING_TRIM_BOTH, &r);
+
+ if (ink_rect)
+ *ink_rect = r;
+
+ if (logical_rect)
+ *logical_rect = r;
+ }
+ }
+
+ offset_line (iter, ink_rect, logical_rect);
+ offset_run (iter, ink_rect, logical_rect);
+}
+
+/**
+ * pango_line_iter_get_cluster_extents:
+ * @iter: a `PangoLineIter`
+ * @ink_rect: (out) (optional): rectangle to fill with ink extents
+ * @logical_rect: (out) (optional): rectangle to fill with logical extents
+ *
+ * Gets the extents of the current cluster, in layout coordinates.
+ *
+ * Layout coordinates have the origin at the top left of the entire `PangoLines`.
+ */
+void
+pango_line_iter_get_cluster_extents (PangoLineIter *iter,
+ PangoRectangle *ink_rect,
+ PangoRectangle *logical_rect)
+{
+ PangoGlyphItem *glyph_item;
+
+ g_return_if_fail (ITER_IS_VALID (iter));
+
+ if (iter->run == NULL)
+ {
+ /* When on the NULL run, all extents are the same */
+ pango_line_iter_get_run_extents (iter, ink_rect, logical_rect);
+ return;
+ }
+
+ glyph_item = pango_layout_run_get_glyph_item (iter->run);
+
+ pango_glyph_string_extents_range (glyph_item->glyphs,
+ iter->cluster_start,
+ iter->next_cluster_glyph,
+ glyph_item->item->analysis.font,
+ ink_rect,
+ logical_rect);
+
+ offset_line (iter, ink_rect, logical_rect);
+ if (ink_rect)
+ {
+ ink_rect->x += iter->cluster_x + glyph_item->start_x_offset;
+ ink_rect->y -= iter->run->y_offset;
+ }
+
+ if (logical_rect)
+ {
+ g_assert (logical_rect->width == iter->cluster_width);
+ logical_rect->x += iter->cluster_x + glyph_item->start_x_offset;
+ logical_rect->y -= iter->run->y_offset;
+ }
+}
+
+/**
+ * pango_line_iter_get_char_extents:
+ * @iter: a `PangoLineIter`
+ * @logical_rect: (out caller-allocates): rectangle to fill with logical extents
+ *
+ * Gets the extents of the current character, in layout coordinates.
+ *
+ * Layout coordinates have the origin at the top left of the entire `PangoLines`.
+ *
+ * Only logical extents can sensibly be obtained for characters;
+ * ink extents make sense only down to the level of clusters.
+ */
+void
+pango_line_iter_get_char_extents (PangoLineIter *iter,
+ PangoRectangle *logical_rect)
+{
+ PangoRectangle cluster_rect;
+ int x0, x1;
+
+ g_return_if_fail (ITER_IS_VALID (iter));
+
+ if (logical_rect == NULL)
+ return;
+
+ pango_line_iter_get_cluster_extents (iter, NULL, &cluster_rect);
+
+ if (iter->run == NULL)
+ {
+ /* When on the NULL run, all extents are the same */
+ *logical_rect = cluster_rect;
+ return;
+ }
+
+ if (iter->cluster_num_chars)
+ {
+ x0 = (iter->character_position * cluster_rect.width) / iter->cluster_num_chars;
+ x1 = ((iter->character_position + 1) * cluster_rect.width) / iter->cluster_num_chars;
+ }
+ else
+ {
+ x0 = x1 = 0;
+ }
+
+ logical_rect->width = x1 - x0;
+ logical_rect->height = cluster_rect.height;
+ logical_rect->y = cluster_rect.y;
+ logical_rect->x = cluster_rect.x + x0;
+}
+
+/**
+ * pango_line_iter_get_line_baseline:
+ * @iter: a `PangoLineIter`
+ *
+ * Gets the Y position of the current line's baseline, in layout
+ * coordinates.
+ *
+ * Layout coordinates have the origin at the top left of the entire `PangoLines`.
+ *
+ * Return value: baseline of current line
+ */
+int
+pango_line_iter_get_line_baseline (PangoLineIter *iter)
+{
+ g_return_val_if_fail (ITER_IS_VALID (iter), 0);
+
+ return iter->line_y;
+}
+
+/**
+ * pango_line_iter_get_run_baseline:
+ * @iter: a `PangoLineIter`
+ *
+ * Gets the Y position of the current run's baseline, in layout
+ * coordinates.
+ *
+ * Layout coordinates have the origin at the top left of the entire `PangoLines`.
+ *
+ * The run baseline can be different from the line baseline, for
+ * example due to superscript or subscript positioning.
+ */
+int
+pango_line_iter_get_run_baseline (PangoLineIter *iter)
+{
+ g_return_val_if_fail (ITER_IS_VALID (iter), 0);
+
+ if (iter->run)
+ return pango_line_iter_get_line_baseline (iter) - pango_layout_run_get_glyph_item (iter->run)->y_offset;
+ else
+ return pango_line_iter_get_line_baseline (iter);
+}
+
+/* }}} */
+
+/* vim:set foldmethod=marker expandtab: */
diff --git a/pango/pango-line-iter.h b/pango/pango-line-iter.h
new file mode 100644
index 00000000..bfd5d48c
--- /dev/null
+++ b/pango/pango-line-iter.h
@@ -0,0 +1,84 @@
+#pragma once
+
+#include <glib-object.h>
+
+#include <pango/pango-types.h>
+#include <pango/pango-lines.h>
+#include <pango/pango-glyph-item.h>
+
+G_BEGIN_DECLS
+
+PANGO_AVAILABLE_IN_ALL
+GType pango_line_iter_get_type (void) G_GNUC_CONST;
+
+PANGO_AVAILABLE_IN_ALL
+PangoLineIter * pango_line_iter_copy (PangoLineIter *iter);
+
+PANGO_AVAILABLE_IN_ALL
+void pango_line_iter_free (PangoLineIter *iter);
+
+PANGO_AVAILABLE_IN_ALL
+PangoLines * pango_line_iter_get_lines (PangoLineIter *iter);
+
+PANGO_AVAILABLE_IN_ALL
+PangoLine * pango_line_iter_get_line (PangoLineIter *iter);
+
+PANGO_AVAILABLE_IN_ALL
+gboolean pango_line_iter_at_last_line (PangoLineIter *iter);
+
+PANGO_AVAILABLE_IN_ALL
+PangoLayoutRun * pango_line_iter_get_run (PangoLineIter *iter);
+
+PANGO_AVAILABLE_IN_ALL
+int pango_line_iter_get_index (PangoLineIter *iter);
+
+PANGO_AVAILABLE_IN_ALL
+gboolean pango_line_iter_next_line (PangoLineIter *iter);
+
+PANGO_AVAILABLE_IN_ALL
+gboolean pango_line_iter_next_run (PangoLineIter *iter);
+
+PANGO_AVAILABLE_IN_ALL
+gboolean pango_line_iter_next_cluster (PangoLineIter *iter);
+
+PANGO_AVAILABLE_IN_ALL
+gboolean pango_line_iter_next_char (PangoLineIter *iter);
+
+PANGO_AVAILABLE_IN_ALL
+void pango_line_iter_get_layout_extents (PangoLineIter *iter,
+ PangoRectangle *ink_rect,
+ PangoRectangle *logical_rect);
+
+PANGO_AVAILABLE_IN_ALL
+void pango_line_iter_get_line_extents (PangoLineIter *iter,
+ PangoRectangle *ink_rect,
+ PangoRectangle *logical_rect);
+
+PANGO_AVAILABLE_IN_ALL
+void pango_line_iter_get_trimmed_line_extents
+ (PangoLineIter *iter,
+ PangoLeadingTrim trim,
+ PangoRectangle *logical_rect);
+
+PANGO_AVAILABLE_IN_ALL
+void pango_line_iter_get_run_extents (PangoLineIter *iter,
+ PangoRectangle *ink_rect,
+ PangoRectangle *logical_rect);
+
+PANGO_AVAILABLE_IN_ALL
+void pango_line_iter_get_cluster_extents (PangoLineIter *iter,
+ PangoRectangle *ink_rect,
+ PangoRectangle *logical_rect);
+
+PANGO_AVAILABLE_IN_ALL
+void pango_line_iter_get_char_extents (PangoLineIter *iter,
+ PangoRectangle *logical_rect);
+
+PANGO_AVAILABLE_IN_ALL
+int pango_line_iter_get_line_baseline (PangoLineIter *iter);
+
+PANGO_AVAILABLE_IN_ALL
+int pango_line_iter_get_run_baseline (PangoLineIter *iter);
+
+
+G_END_DECLS
diff --git a/pango/pango-lines.c b/pango/pango-lines.c
index a1fa5724..195524bc 100644
--- a/pango/pango-lines.c
+++ b/pango/pango-lines.c
@@ -3,6 +3,7 @@
#include "pango-lines-private.h"
#include "pango-line-private.h"
#include "pango-item-private.h"
+#include "pango-line-iter-private.h"
/* {{{ PangoLines implementation */
@@ -196,6 +197,26 @@ pango_lines_add_line (PangoLines *lines,
lines->serial++;
}
+/**
+ * pango_lines_get_iter:
+ * @lines: a `PangoLines`
+ *
+ * Returns an iterator to iterate over the visual extents of the lines.
+ *
+ * The returned iterator will be invaliated when more
+ * lines are added to @lines, and can't be used anymore
+ * after that point.
+ *
+ * Note that the iter holds a reference to @lines.
+ *
+ * Return value: the new `PangoLineIter`
+ */
+PangoLineIter *
+pango_lines_get_iter (PangoLines *lines)
+{
+ return pango_line_iter_new (lines);
+}
+
/**
* pango_lines_get_line_count:
* @lines: a `PangoLines`
diff --git a/pango/pango-lines.h b/pango/pango-lines.h
index 505758b4..1ed2b86e 100644
--- a/pango/pango-lines.h
+++ b/pango/pango-lines.h
@@ -12,6 +12,8 @@ G_BEGIN_DECLS
PANGO_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (PangoLines, pango_lines, PANGO, LINES, GObject);
+typedef struct _PangoLineIter PangoLineIter;
+
PANGO_AVAILABLE_IN_ALL
PangoLines * pango_lines_new (void);
@@ -33,6 +35,9 @@ PangoLine * pango_lines_get_line (PangoLines *lines,
int *line_x,
int *line_y);
+PANGO_AVAILABLE_IN_ALL
+PangoLineIter * pango_lines_get_iter (PangoLines *lines);
+
PANGO_AVAILABLE_IN_ALL
void pango_lines_get_extents (PangoLines *lines,
PangoRectangle *ink_rect,
diff --git a/pango/pango-simple-layout.c b/pango/pango-simple-layout.c
index b4c3c71a..adddd2fb 100644
--- a/pango/pango-simple-layout.c
+++ b/pango/pango-simple-layout.c
@@ -1527,6 +1527,27 @@ pango_simple_layout_get_log_attrs (PangoSimpleLayout *layout,
return line->data->log_attrs;
}
+/**
+ * pango_simple_layout_get_iter:
+ * @layout: a `PangoSimpleLayout`
+ *
+ * Returns an iterator to iterate over the visual extents
+ * of the layout.
+ *
+ * This is a convenience wrapper for [method@Pango.Lines.get_iter].
+ *
+ * Returns: the new `PangoLineIter`
+ */
+PangoLineIter *
+pango_simple_layout_get_iter (PangoSimpleLayout *layout)
+{
+ g_return_val_if_fail (PANGO_IS_SIMPLE_LAYOUT (layout), NULL);
+
+ ensure_lines (layout);
+
+ return pango_lines_get_iter (layout->lines);
+}
+
/* }}} */
/* }}} */
diff --git a/pango/pango-simple-layout.h b/pango/pango-simple-layout.h
index 2a746be7..852b8b19 100644
--- a/pango/pango-simple-layout.h
+++ b/pango/pango-simple-layout.h
@@ -135,6 +135,9 @@ gboolean pango_simple_layout_get_auto_dir (PangoSimpleLayout *
PANGO_AVAILABLE_IN_ALL
PangoLines * pango_simple_layout_get_lines (PangoSimpleLayout *layout);
+PANGO_AVAILABLE_IN_ALL
+PangoLineIter * pango_simple_layout_get_iter (PangoSimpleLayout *layout);
+
PANGO_AVAILABLE_IN_ALL
const PangoLogAttr * pango_simple_layout_get_log_attrs (PangoSimpleLayout *layout,
int *n_attrs);
diff --git a/pango/pango.h b/pango/pango.h
index df4bf3d3..41167631 100644
--- a/pango/pango.h
+++ b/pango/pango.h
@@ -45,6 +45,7 @@
#include <pango/pango-layout-run.h>
#include <pango/pango-line.h>
#include <pango/pango-line-breaker.h>
+#include <pango/pango-line-iter.h>
#include <pango/pango-lines.h>
#include <pango/pango-matrix.h>
#include <pango/pango-markup.h>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]