diff options
-rw-r--r-- | pango/pango-layout.c | 33 | ||||
-rw-r--r-- | tests/testmisc.c | 72 | ||||
-rw-r--r-- | utils/viewer-pangocairo.c | 358 |
3 files changed, 320 insertions, 143 deletions
diff --git a/pango/pango-layout.c b/pango/pango-layout.c index ee58243b..669410ee 100644 --- a/pango/pango-layout.c +++ b/pango/pango-layout.c @@ -2374,7 +2374,8 @@ pango_layout_index_to_pos (PangoLayout *layout, int index, PangoRectangle *pos) { - PangoRectangle logical_rect; + PangoRectangle line_logical_rect; + PangoRectangle run_logical_rect; PangoLayoutIter iter; PangoLayoutLine *layout_line = NULL; int x_pos; @@ -2393,10 +2394,10 @@ pango_layout_index_to_pos (PangoLayout *layout, if (tmp_line->start_index > index) { - /* index is in the paragraph delim&iters, move to + /* index is in the paragraph delimiters, move to * end of previous line * - * This shouldn’t occur in the first loop &iteration as the first + * This shouldn’t occur in the first loop iteration as the first * line’s start_index should always be 0. */ g_assert (layout_line != NULL); @@ -2404,24 +2405,28 @@ pango_layout_index_to_pos (PangoLayout *layout, break; } + pango_layout_iter_get_line_extents (&iter, NULL, &line_logical_rect); + layout_line = tmp_line; - if (layout_line->start_index + layout_line->length > index) + if (layout_line->start_index + layout_line->length >= index) { - while (TRUE) + do { PangoLayoutRun *run = _pango_layout_iter_get_run (&iter); - pango_layout_iter_get_run_extents (&iter, NULL, &logical_rect); + pango_layout_iter_get_run_extents (&iter, NULL, &run_logical_rect); - if (run->item->offset <= index && index < run->item->offset + run->item->length) + if (!run) break; - if (!pango_layout_iter_next_run (&iter)) + if (run->item->offset <= index && index < run->item->offset + run->item->length) break; - } + } + while (pango_layout_iter_next_run (&iter)); - break; + if (layout_line->start_index + layout_line->length > index) + break; } if (!pango_layout_iter_next_line (&iter)) @@ -2431,16 +2436,16 @@ pango_layout_index_to_pos (PangoLayout *layout, } } - pos->y = logical_rect.y; - pos->height = logical_rect.height; + pos->y = run_logical_rect.y; + pos->height = run_logical_rect.height; pango_layout_line_index_to_x (layout_line, index, 0, &x_pos); - pos->x = logical_rect.x + x_pos; + pos->x = line_logical_rect.x + x_pos; if (index < layout_line->start_index + layout_line->length) { pango_layout_line_index_to_x (layout_line, index, 1, &x_pos); - pos->width = (logical_rect.x + x_pos) - pos->x; + pos->width = (line_logical_rect.x + x_pos) - pos->x; } else pos->width = 0; diff --git a/tests/testmisc.c b/tests/testmisc.c index 8ebfec80..da24388b 100644 --- a/tests/testmisc.c +++ b/tests/testmisc.c @@ -418,6 +418,77 @@ test_index_to_x (void) g_object_unref (context); } +static gboolean +pango_rectangle_contains (const PangoRectangle *r1, + const PangoRectangle *r2) +{ + return r2->x >= r1->x && + r2->y >= r1->y && + r2->x + r2->width <= r1->x + r1->width && + r2->y + r2->height <= r1->y + r1->height; +} + +static void +test_extents (void) +{ + PangoContext *context; + const char *tests[] = { + "Some long text that has multiple lines that are wrapped by Pango." + }; + + context = pango_font_map_create_context (pango_cairo_font_map_get_default ()); + + for (int i = 0; i < G_N_ELEMENTS (tests); i++) + { + PangoLayout *layout; + PangoLayoutIter *iter; + PangoRectangle layout_extents; + PangoRectangle line_extents; + PangoRectangle run_extents; + PangoRectangle cluster_extents; + PangoRectangle char_extents; + PangoRectangle pos; + + layout = pango_layout_new (context); + pango_layout_set_text (layout, tests[i], -1); + pango_layout_set_width (layout, 60 * PANGO_SCALE); + + pango_layout_get_extents (layout, NULL, &layout_extents); + + iter = pango_layout_get_iter (layout); + + do + { + pango_layout_iter_get_line_extents (iter, NULL, &line_extents); + pango_layout_iter_get_run_extents (iter, NULL, &run_extents); + pango_layout_iter_get_cluster_extents (iter, NULL, &cluster_extents); + pango_layout_iter_get_char_extents (iter, &char_extents); + + pango_layout_index_to_pos (layout, + pango_layout_iter_get_index (iter), + &pos); + if (pos.width < 0) + { + pos.x += pos.width; + pos.width = - pos.width; + } + + g_assert_true (pango_rectangle_contains (&layout_extents, &line_extents)); + g_assert_true (pango_rectangle_contains (&line_extents, &run_extents)); + g_assert_true (pango_rectangle_contains (&run_extents, &cluster_extents)); + g_assert_true (pango_rectangle_contains (&cluster_extents, &char_extents)); + + g_assert_true (pango_rectangle_contains (&line_extents, &pos)); + } + while (pango_layout_iter_next_char (iter)); + + pango_layout_iter_free (iter); + g_object_unref (layout); + } + + g_object_unref (context); +} + int main (int argc, char *argv[]) { @@ -442,6 +513,7 @@ main (int argc, char *argv[]) g_test_add_func ("/bidi/get-cursor-crash", test_get_cursor_crash); g_test_add_func ("/bidi/get-cursor", test_get_cursor); g_test_add_func ("/layout/index-to-x", test_index_to_x); + g_test_add_func ("/layout/extents", test_extents); return g_test_run (); } diff --git a/utils/viewer-pangocairo.c b/utils/viewer-pangocairo.c index ac3b49c3..77201666 100644 --- a/utils/viewer-pangocairo.c +++ b/utils/viewer-pangocairo.c @@ -148,20 +148,32 @@ pangocairo_view_destroy_surface (gpointer instance, g_slice_free (CairoSurface, surface); } +enum { + ANNOTATE_GRAVITY_ROOF = 1, + ANNOTATE_BLOCK_PROGRESSION = 2, + ANNOTATE_BASELINES = 4, + ANNOTATE_LAYOUT_EXTENTS = 8, + ANNOTATE_LINE_EXTENTS = 16, + ANNOTATE_RUN_EXTENTS = 32, + ANNOTATE_CLUSTER_EXTENTS = 64, + ANNOTATE_CHAR_EXTENTS = 128, + ANNOTATE_LAST = 256, +}; + static void render_callback (PangoLayout *layout, - int x, - int y, - gpointer context, - gpointer state) + int x, + int y, + gpointer context, + gpointer state) { cairo_t *cr = (cairo_t *) context; - int annotate = (GPOINTER_TO_INT (state) + opt_annotate) % 4; + int annotate = (GPOINTER_TO_INT (state) + opt_annotate) % ANNOTATE_LAST; cairo_save (cr); cairo_translate (cr, x, y); - if (annotate) + if (annotate != 0) { cairo_pattern_t *pattern; PangoRectangle ink, logical; @@ -170,127 +182,135 @@ render_callback (PangoLayout *layout, pango_layout_get_extents (layout, &ink, &logical); - if (annotate >= 2) + if (annotate & ANNOTATE_GRAVITY_ROOF) + { + /* draw resolved gravity "roof" in blue */ + cairo_save (cr); + cairo_translate (cr, + (double)logical.x / PANGO_SCALE, + (double)logical.y / PANGO_SCALE); + cairo_scale (cr, + (double)logical.width / PANGO_SCALE * 0.5, + (double)logical.height / PANGO_SCALE * 0.5); + cairo_translate (cr, 1.0, 1.0); + cairo_rotate (cr, + pango_gravity_to_rotation ( + pango_context_get_gravity ( + pango_layout_get_context (layout)))); + cairo_move_to (cr, -1.0, -1.0); + cairo_rel_line_to (cr, +1.0, -0.2); /* / */ + cairo_rel_line_to (cr, +1.0, +0.2); /* \ */ + cairo_close_path (cr); /* - */ + pattern = cairo_pattern_create_linear (0, -1.0, 0, -1.2); + cairo_pattern_add_color_stop_rgba (pattern, 0.0, 0.0, 0.0, 1.0, 0.0); + cairo_pattern_add_color_stop_rgba (pattern, 1.0, 0.0, 0.0, 1.0, 0.15); + cairo_set_source (cr, pattern); + cairo_fill (cr); + /* once more, without close_path this time */ + cairo_move_to (cr, -1.0, -1.0); + cairo_rel_line_to (cr, +1.0, -0.2); /* / */ + cairo_rel_line_to (cr, +1.0, +0.2); /* \ */ + /* silly line_width is not locked :(. get rid of scale. */ + cairo_restore (cr); + cairo_save (cr); + cairo_set_source_rgba (cr, 0.0, 0.0, 0.7, 0.2); + cairo_stroke (cr); + cairo_restore (cr); + } + + if (annotate & ANNOTATE_BLOCK_PROGRESSION) + { + /* draw block progression arrow in green */ + cairo_save (cr); + cairo_translate (cr, + (double)logical.x / PANGO_SCALE, + (double)logical.y / PANGO_SCALE); + cairo_scale (cr, + (double)logical.width / PANGO_SCALE * 0.5, + (double)logical.height / PANGO_SCALE * 0.5); + cairo_translate (cr, 1.0, 1.0); + cairo_move_to (cr, -0.4, -0.7); + cairo_rel_line_to (cr, +0.8, 0.0); /* -- */ + cairo_rel_line_to (cr, 0.0, +0.9); /* | */ + cairo_rel_line_to (cr, +0.4, 0.0); /* - */ + cairo_rel_line_to (cr, -0.8, +0.5); /* / */ + cairo_rel_line_to (cr, -0.8, -0.5); /* \ */ + cairo_rel_line_to (cr, +0.4, 0.0); /* - */ + cairo_close_path (cr); /* | */ + pattern = cairo_pattern_create_linear (0, -0.7, 0, 0.7); + cairo_pattern_add_color_stop_rgba (pattern, 0.0, 0.0, 1.0, 0.0, 0.0); + cairo_pattern_add_color_stop_rgba (pattern, 1.0, 0.0, 1.0, 0.0, 0.15); + cairo_set_source (cr, pattern); + cairo_fill_preserve (cr); + /* silly line_width is not locked :(. get rid of scale. */ + cairo_restore (cr); + cairo_save (cr); + cairo_set_source_rgba (cr, 0.0, 0.7, 0.0, 0.2); + cairo_stroke (cr); + cairo_restore (cr); + } + + if (annotate & ANNOTATE_BASELINES) + { + /* draw baselines with line direction arrow in orange */ + cairo_save (cr); + cairo_set_source_rgba (cr, 1.0, 0.5, 0.0, 0.5); + iter = pango_layout_get_iter (layout); + do + { + PangoLayoutLine *line = pango_layout_iter_get_line (iter); + double width = (double)logical.width / PANGO_SCALE; + + y = pango_layout_iter_get_baseline (iter); + cairo_save (cr); + cairo_translate (cr, + (double)logical.x / PANGO_SCALE + width * 0.5, + (double)y / PANGO_SCALE); + if (line->resolved_dir) + cairo_scale (cr, -1, 1); + cairo_move_to (cr, -width * .5, -lw*0.2); + cairo_rel_line_to (cr, +width * .9, -lw*0.3); + cairo_rel_line_to (cr, 0, -lw); + cairo_rel_line_to (cr, +width * .1, +lw*1.5); + cairo_rel_line_to (cr, -width * .1, +lw*1.5); + cairo_rel_line_to (cr, 0, -lw); + cairo_rel_line_to (cr, -width * .9, -lw*0.3); + cairo_close_path (cr); + cairo_fill (cr); + cairo_restore (cr); + } + while (pango_layout_iter_next_line (iter)); + pango_layout_iter_free (iter); + cairo_restore (cr); + } + + if (annotate & ANNOTATE_LAYOUT_EXTENTS) { - /* draw resolved gravity "roof" in blue */ - cairo_save (cr); - cairo_translate (cr, - (double)logical.x / PANGO_SCALE, - (double)logical.y / PANGO_SCALE); - cairo_scale (cr, - (double)logical.width / PANGO_SCALE * 0.5, - (double)logical.height / PANGO_SCALE * 0.5); - cairo_translate (cr, 1.0, 1.0); - cairo_rotate (cr, - pango_gravity_to_rotation ( - pango_context_get_gravity ( - pango_layout_get_context (layout)))); - cairo_move_to (cr, -1.0, -1.0); - cairo_rel_line_to (cr, +1.0, -0.2); /* / */ - cairo_rel_line_to (cr, +1.0, +0.2); /* \ */ - cairo_close_path (cr); /* - */ - pattern = cairo_pattern_create_linear (0, -1.0, 0, -1.2); - cairo_pattern_add_color_stop_rgba (pattern, 0.0, 0.0, 0.0, 1.0, 0.0); - cairo_pattern_add_color_stop_rgba (pattern, 1.0, 0.0, 0.0, 1.0, 0.15); - cairo_set_source (cr, pattern); - cairo_fill (cr); - /* once more, without close_path this time */ - cairo_move_to (cr, -1.0, -1.0); - cairo_rel_line_to (cr, +1.0, -0.2); /* / */ - cairo_rel_line_to (cr, +1.0, +0.2); /* \ */ - /* silly line_width is not locked :(. get rid of scale. */ - cairo_restore (cr); - cairo_save (cr); - cairo_set_source_rgba (cr, 0.0, 0.0, 0.7, 0.2); - cairo_stroke (cr); - cairo_restore (cr); - - - /* draw block progression arrow in green */ - cairo_save (cr); - cairo_translate (cr, - (double)logical.x / PANGO_SCALE, - (double)logical.y / PANGO_SCALE); - cairo_scale (cr, - (double)logical.width / PANGO_SCALE * 0.5, - (double)logical.height / PANGO_SCALE * 0.5); - cairo_translate (cr, 1.0, 1.0); - cairo_move_to (cr, -0.4, -0.7); - cairo_rel_line_to (cr, +0.8, 0.0); /* -- */ - cairo_rel_line_to (cr, 0.0, +0.9); /* | */ - cairo_rel_line_to (cr, +0.4, 0.0); /* - */ - cairo_rel_line_to (cr, -0.8, +0.5); /* / */ - cairo_rel_line_to (cr, -0.8, -0.5); /* \ */ - cairo_rel_line_to (cr, +0.4, 0.0); /* - */ - cairo_close_path (cr); /* | */ - pattern = cairo_pattern_create_linear (0, -0.7, 0, 0.7); - cairo_pattern_add_color_stop_rgba (pattern, 0.0, 0.0, 1.0, 0.0, 0.0); - cairo_pattern_add_color_stop_rgba (pattern, 1.0, 0.0, 1.0, 0.0, 0.15); - cairo_set_source (cr, pattern); - cairo_fill_preserve (cr); - /* silly line_width is not locked :(. get rid of scale. */ - cairo_restore (cr); - cairo_save (cr); - cairo_set_source_rgba (cr, 0.0, 0.7, 0.0, 0.2); - cairo_stroke (cr); - cairo_restore (cr); - } - - /* draw baselines with line direction arrow in orange */ - cairo_save (cr); - cairo_set_source_rgba (cr, 1.0, 0.5, 0.0, 0.5); - iter = pango_layout_get_iter (layout); - do - { - PangoLayoutLine *line = pango_layout_iter_get_line (iter); - double width = (double)logical.width / PANGO_SCALE; - - y = pango_layout_iter_get_baseline (iter); - cairo_save (cr); - cairo_translate (cr, - (double)logical.x / PANGO_SCALE + width * 0.5, - (double)y / PANGO_SCALE); - if (line->resolved_dir) - cairo_scale (cr, -1, 1); - cairo_move_to (cr, -width * .5, -lw*0.2); - cairo_rel_line_to (cr, +width * .9, -lw*0.3); - cairo_rel_line_to (cr, 0, -lw); - cairo_rel_line_to (cr, +width * .1, +lw*1.5); - cairo_rel_line_to (cr, -width * .1, +lw*1.5); - cairo_rel_line_to (cr, 0, -lw); - cairo_rel_line_to (cr, -width * .9, -lw*0.3); - cairo_close_path (cr); - cairo_fill (cr); - cairo_restore (cr); - } - while (pango_layout_iter_next_line (iter)); - pango_layout_iter_free (iter); - cairo_restore (cr); - - /* draw the logical rect in red */ - cairo_save (cr); - cairo_set_source_rgba (cr, 1.0, 0.0, 0.0, 0.5); - - cairo_rectangle (cr, - (double)logical.x / PANGO_SCALE - lw / 2, - (double)logical.y / PANGO_SCALE - lw / 2, - (double)logical.width / PANGO_SCALE + lw, - (double)logical.height / PANGO_SCALE + lw); - cairo_stroke (cr); - cairo_restore (cr); - - /* draw the ink rect in green */ - cairo_save (cr); - cairo_set_source_rgba (cr, 0.0, 1.0, 0.0, 0.5); - cairo_rectangle (cr, - (double)ink.x / PANGO_SCALE - lw / 2, - (double)ink.y / PANGO_SCALE - lw / 2, - (double)ink.width / PANGO_SCALE + lw, - (double)ink.height / PANGO_SCALE + lw); - cairo_stroke (cr); - cairo_restore (cr); - - if (opt_annotate >= 3) + /* draw the logical rect in red */ + cairo_save (cr); + cairo_set_source_rgba (cr, 1.0, 0.0, 0.0, 0.5); + + cairo_rectangle (cr, + (double)logical.x / PANGO_SCALE - lw / 2, + (double)logical.y / PANGO_SCALE - lw / 2, + (double)logical.width / PANGO_SCALE + lw, + (double)logical.height / PANGO_SCALE + lw); + cairo_stroke (cr); + cairo_restore (cr); + + /* draw the ink rect in green */ + cairo_save (cr); + cairo_set_source_rgba (cr, 0.0, 1.0, 0.0, 0.5); + cairo_rectangle (cr, + (double)ink.x / PANGO_SCALE - lw / 2, + (double)ink.y / PANGO_SCALE - lw / 2, + (double)ink.width / PANGO_SCALE + lw, + (double)ink.height / PANGO_SCALE + lw); + cairo_stroke (cr); + cairo_restore (cr); + } + + if (annotate & ANNOTATE_LINE_EXTENTS) { /* draw the logical rects for lines in red */ cairo_save (cr); @@ -298,7 +318,7 @@ render_callback (PangoLayout *layout, iter = pango_layout_get_iter (layout); do - { + { PangoRectangle rect; pango_layout_iter_get_line_extents (iter, NULL, &rect); @@ -313,6 +333,78 @@ render_callback (PangoLayout *layout, pango_layout_iter_free (iter); cairo_restore (cr); } + + if (annotate & ANNOTATE_RUN_EXTENTS) + { + /* draw the logical rects for runs in blue */ + cairo_save (cr); + cairo_set_source_rgba (cr, 0.0, 0.0, 1.0, 0.5); + + iter = pango_layout_get_iter (layout); + do + { + PangoRectangle rect; + + pango_layout_iter_get_run_extents (iter, NULL, &rect); + cairo_rectangle (cr, + (double)rect.x / PANGO_SCALE - lw / 2, + (double)rect.y / PANGO_SCALE - lw / 2, + (double)rect.width / PANGO_SCALE + lw, + (double)rect.height / PANGO_SCALE + lw); + cairo_stroke (cr); + } + while (pango_layout_iter_next_run (iter)); + pango_layout_iter_free (iter); + cairo_restore (cr); + } + + if (annotate & ANNOTATE_CLUSTER_EXTENTS) + { + /* draw the logical rects for clusters in purple */ + cairo_save (cr); + cairo_set_source_rgba (cr, 1.0, 0.0, 1.0, 0.5); + + iter = pango_layout_get_iter (layout); + do + { + PangoRectangle rect; + + pango_layout_iter_get_cluster_extents (iter, NULL, &rect); + cairo_rectangle (cr, + (double)rect.x / PANGO_SCALE - lw / 2, + (double)rect.y / PANGO_SCALE - lw / 2, + (double)rect.width / PANGO_SCALE + lw, + (double)rect.height / PANGO_SCALE + lw); + cairo_stroke (cr); + } + while (pango_layout_iter_next_cluster (iter)); + pango_layout_iter_free (iter); + cairo_restore (cr); + } + + if (annotate & ANNOTATE_CHAR_EXTENTS) + { + /* draw the logical rects for chars in orange */ + cairo_save (cr); + cairo_set_source_rgba (cr, 1.0, 0.5, 0.0, 0.5); + + iter = pango_layout_get_iter (layout); + do + { + PangoRectangle rect; + + pango_layout_iter_get_cluster_extents (iter, NULL, &rect); + cairo_rectangle (cr, + (double)rect.x / PANGO_SCALE - lw / 2, + (double)rect.y / PANGO_SCALE - lw / 2, + (double)rect.width / PANGO_SCALE + lw, + (double)rect.height / PANGO_SCALE + lw); + cairo_stroke (cr); + } + while (pango_layout_iter_next_cluster (iter)); + pango_layout_iter_free (iter); + cairo_restore (cr); + } } cairo_move_to (cr, 0, 0); @@ -458,8 +550,16 @@ pangocairo_view_get_option_group (const PangoViewer *klass G_GNUC_UNUSED) { GOptionEntry entries[] = { - {"annotate", 0, 0, G_OPTION_ARG_INT, &opt_annotate, - "Annotate the output", "1, 2 or 3"}, + {"annotate", 0, 0, G_OPTION_ARG_INT, &opt_annotate, + "Annotate the output\n" + "\t\t\t\t\t\t\t 1 - gravity\n" + "\t\t\t\t\t\t\t 2 - block progression\n" + "\t\t\t\t\t\t\t 4 - baselines\n" + "\t\t\t\t\t\t\t 8 - layout extents\n" + "\t\t\t\t\t\t\t 16 - line extents\n" + "\t\t\t\t\t\t\t 32 - run extents\n" + "\t\t\t\t\t\t\t 64 - cluster extents\n" + "\t\t\t\t\t\t\t 128 - char extents", "FLAGS"}, {NULL} }; GOptionGroup *group; |