static void print_pango_layout (GnomePrintContext *gpc, PangoLayout *layout) { PangoLayoutIter *iter; gnome_print_gsave (gpc); current_point_to_origin (gpc); iter = pango_layout_get_iter (layout); do { PangoRectangle logical_rect; PangoLayoutLine *line; int baseline; line = pango_layout_iter_get_line (iter); pango_layout_iter_get_line_extents (iter, NULL, &logical_rect); baseline = pango_layout_iter_get_baseline (iter); moveto (gpc, logical_rect.x, - baseline); print_pango_layout_line (gpc, line); } while (pango_layout_iter_next_line (iter)); pango_layout_iter_free (iter); gnome_print_grestore (gpc); }
static PyObject * pango_GetLayoutLinePos(PyObject *self, PyObject *args) { int i, len; double baseline, dy; void *LayoutObj; PangoLayout *layout; PangoLayoutIter *iter; PyObject *ret; if (!PyArg_ParseTuple(args, "O", &LayoutObj)) { return NULL; } layout = PyCObject_AsVoidPtr(LayoutObj); len = pango_layout_get_line_count(layout); ret = PyTuple_New(len); iter = pango_layout_get_iter(layout); dy = ((double) pango_layout_iter_get_baseline(iter)) / PANGO_SCALE; for (i = 0; i < len; i++) { baseline = -1.0 * ((double) pango_layout_iter_get_baseline(iter)) / PANGO_SCALE + dy; PyTuple_SetItem(ret, i, PyFloat_FromDouble(baseline)); pango_layout_iter_next_line(iter); } pango_layout_iter_free(iter); return ret; }
void x11_draw_layout_with_colors( Drawable drawable, GC gc, int x, int y, PangoLayout *layout, wxColour &colour ) { PangoLayoutIter *iter = pango_layout_get_iter (layout); do { PangoLayoutLine *line = pango_layout_iter_get_line (iter); PangoRectangle logical_rect; pango_layout_iter_get_line_extents (iter, NULL, &logical_rect); int baseline = pango_layout_iter_get_baseline (iter); x11_draw_layout_line_with_colors( drawable, gc, x + logical_rect.x / PANGO_SCALE, y + baseline / PANGO_SCALE, line, colour ); } while (pango_layout_iter_next_line (iter)); pango_layout_iter_free (iter); }
static void draw_page (GtkPrintOperation *operation, GtkPrintContext *context, int page_nr, PrintData *print_data) { cairo_t *cr; GList *pagebreak; int start, end, i; PangoLayoutIter *iter; double start_pos; if (page_nr == 0) start = 0; else { pagebreak = g_list_nth (print_data->page_breaks, page_nr - 1); start = GPOINTER_TO_INT (pagebreak->data); } pagebreak = g_list_nth (print_data->page_breaks, page_nr); if (pagebreak == NULL) end = pango_layout_get_line_count (print_data->layout); else end = GPOINTER_TO_INT (pagebreak->data); cr = gtk_print_context_get_cairo_context (context); cairo_set_source_rgb (cr, 0, 0, 0); i = 0; start_pos = 0; iter = pango_layout_get_iter (print_data->layout); do { PangoRectangle logical_rect; PangoLayoutLine *line; int baseline; if (i >= start) { line = pango_layout_iter_get_line (iter); pango_layout_iter_get_line_extents (iter, NULL, &logical_rect); baseline = pango_layout_iter_get_baseline (iter); if (i == start) start_pos = logical_rect.y / 1024.0; cairo_move_to (cr, logical_rect.x / 1024.0, baseline / 1024.0 - start_pos); pango_cairo_show_layout_line (cr, line); } i++; } while (i < end && pango_layout_iter_next_line (iter)); pango_layout_iter_free (iter); }
/** Get size information for the given string, font and height. * * @returns an array of offsets of the individual glyphs in the layout. */ real* dia_font_get_sizes(const char* string, DiaFont *font, real height, real *width, real *ascent, real *descent, int *n_offsets, PangoLayoutLine **layout_offsets) { PangoLayout* layout; PangoLayoutIter* iter; real top, bline, bottom; const gchar* non_empty_string; PangoRectangle ink_rect,logical_rect; real* offsets = NULL; /* avoid: 'offsets' may be used uninitialized in this function */ /* We need some reasonable ascent/descent values even for empty strings. */ if (string == NULL || string[0] == '\0') { non_empty_string = "XjgM149"; } else { non_empty_string = string; } layout = dia_font_build_layout(non_empty_string, font, height * global_zoom_factor); /* Only one line here ? */ iter = pango_layout_get_iter(layout); pango_layout_iter_get_line_extents(iter, &ink_rect, &logical_rect); top = pdu_to_dcm(logical_rect.y) / global_zoom_factor; bottom = pdu_to_dcm(logical_rect.y + logical_rect.height) / global_zoom_factor; bline = pdu_to_dcm(pango_layout_iter_get_baseline(iter)) / global_zoom_factor; get_string_offsets(iter, &offsets, n_offsets); get_layout_offsets(pango_layout_get_line(layout, 0), layout_offsets); /* FIXME: the above assumption of 'one line' is wrong. At least calculate the overall width correctly * to avoid text overflowing its box, like in bug #482585 */ while (pango_layout_iter_next_line (iter)) { PangoRectangle more_ink_rect, more_logical_rect; pango_layout_iter_get_line_extents(iter, &more_ink_rect, &more_logical_rect); if (more_logical_rect.width > logical_rect.width) logical_rect.width = more_logical_rect.width; /* also calculate for the ink rect (true space needed for drawing the glyphs) */ if (more_ink_rect.width > ink_rect.width) ink_rect.width = more_ink_rect.width; } pango_layout_iter_free(iter); g_object_unref(G_OBJECT(layout)); *ascent = bline-top; *descent = bottom-bline; if (non_empty_string != string) { *width = 0.0; } else { /* take the bigger rectangle to avoid cutting of any part of the string */ *width = pdu_to_dcm(logical_rect.width > ink_rect.width ? logical_rect.width : ink_rect.width) / global_zoom_factor; } return offsets; }
static void fo_doc_cairo_do_callbacks (cairo_t *cr, PangoLayout *layout, gint line_first, gint line_last, gint x, gint y) { PangoLayoutIter *iter; g_return_if_fail (cr != NULL); g_return_if_fail (PANGO_IS_LAYOUT (layout)); g_return_if_fail (line_first >= 0); /*g_return_if_fail (line_last >= line_first && line_last <= g_slist_length (pango_layout_get_lines (layout)) - 1);*/ iter = pango_layout_get_iter (layout); gint line_number = -1; do { PangoRectangle logical_rect; PangoLayoutLine *line; int baseline; line_number++; if (line_number < line_first) { continue; } line = pango_layout_iter_get_line (iter); pango_layout_iter_get_line_extents (iter, NULL, &logical_rect); baseline = pango_layout_iter_get_baseline (iter); fo_doc_cairo_do_line_callbacks (cr, line, x + logical_rect.x, y - baseline); if (line_number >= line_last) { break; } } while (pango_layout_iter_next_line (iter)); pango_layout_iter_free (iter); }
void pango_clutter_ensure_glyph_cache_for_layout (PangoLayout *layout) { PangoContext *context; PangoFontMap *fontmap; PangoRenderer *renderer; PangoLayoutIter *iter; g_return_if_fail (PANGO_IS_LAYOUT (layout)); context = pango_layout_get_context (layout); fontmap = pango_context_get_font_map (context); g_return_if_fail (PANGO_CLUTTER_IS_FONT_MAP (fontmap)); renderer = _pango_clutter_font_map_get_renderer (PANGO_CLUTTER_FONT_MAP (fontmap)); if ((iter = pango_layout_get_iter (layout)) == NULL) return; do { PangoLayoutLine *line; GSList *l; line = pango_layout_iter_get_line_readonly (iter); for (l = line->runs; l; l = l->next) { PangoLayoutRun *run = l->data; PangoGlyphString *glyphs = run->glyphs; int i; for (i = 0; i < glyphs->num_glyphs; i++) { PangoGlyphInfo *gi = &glyphs->glyphs[i]; if (!run->item->analysis.font) /* Font not found */ continue; pango_clutter_renderer_get_cached_glyph (renderer, run->item->analysis.font, gi->glyph); } } } while (pango_layout_iter_next_line (iter)); pango_layout_iter_free (iter); }
/** * pango_renderer_draw_layout: * @renderer: a #PangoRenderer * @layout: a #PangoLayout * @x: X position of left edge of baseline, in user space coordinates * in Pango units. * @y: Y position of left edge of baseline, in user space coordinates * in Pango units. * * Draws @layout with the specified #PangoRenderer. * * Since: 1.8 **/ void pango_renderer_draw_layout (PangoRenderer *renderer, PangoLayout *layout, int x, int y) { PangoLayoutIter *iter; g_return_if_fail (PANGO_IS_RENDERER (renderer)); g_return_if_fail (PANGO_IS_LAYOUT (layout)); /* We only change the matrix if the renderer isn't already * active. */ if (!renderer->active_count) { PangoContext *context = pango_layout_get_context (layout); pango_renderer_set_matrix (renderer, pango_context_get_matrix (context)); } pango_renderer_activate (renderer); iter = pango_layout_get_iter (layout); do { PangoRectangle logical_rect; PangoLayoutLine *line; int baseline; line = pango_layout_iter_get_line_readonly (iter); pango_layout_iter_get_line_extents (iter, NULL, &logical_rect); baseline = pango_layout_iter_get_baseline (iter); pango_renderer_draw_layout_line (renderer, line, x + logical_rect.x, y + baseline); } while (pango_layout_iter_next_line (iter)); pango_layout_iter_free (iter); pango_renderer_deactivate (renderer); }
gui2::tpoint ttext::get_cursor_position( const unsigned column, const unsigned line) const { recalculate(); // First we need to determine the byte offset, if more routines need it it // would be a good idea to make it a separate function. titor itor(layout_); // Go the wanted line. if(line != 0) { if(pango_layout_get_line_count(layout_) >= static_cast<int>(line)) { return gui2::tpoint(0, 0); } for(size_t i = 0; i < line; ++i) { pango_layout_iter_next_line(itor); } } // Go the wanted column. for(size_t i = 0; i < column; ++i) { if(!pango_layout_iter_next_char(itor)) { // It seems that the documentation is wrong and causes and off by // one error... the result should be false if already at the end of // the data when started. if(i + 1 == column) { break; } // We are beyond data. return gui2::tpoint(0, 0); } } // Get the byte offset const int offset = pango_layout_iter_get_index(itor); // Convert the byte offset in a position. PangoRectangle rect; pango_layout_get_cursor_pos(layout_, offset, &rect, nullptr); return gui2::tpoint(PANGO_PIXELS(rect.x), PANGO_PIXELS(rect.y)); }
static void dump_lines (PangoLayout *layout, GString *string) { PangoLayoutIter *iter; const gchar *text; gint index, index2; gboolean has_more; gchar *char_str; gint i; PangoLayoutLine *line; text = pango_layout_get_text (layout); iter = pango_layout_get_iter (layout); has_more = TRUE; index = pango_layout_iter_get_index (iter); i = 0; while (has_more) { line = pango_layout_iter_get_line (iter); has_more = pango_layout_iter_next_line (iter); i++; if (has_more) { index2 = pango_layout_iter_get_index (iter); char_str = g_strndup (text + index, index2 - index); } else { char_str = g_strdup (text + index); } g_string_append_printf (string, "i=%d, index=%d, paragraph-start=%d, dir=%s '%s'\n", i, index, line->is_paragraph_start, direction_name (line->resolved_dir), char_str); g_free (char_str); index = index2; } pango_layout_iter_free (iter); }
/** * gdk_pango_layout_get_clip_region: (skip) * @layout: a #PangoLayout * @x_origin: X pixel where you intend to draw the layout with this clip * @y_origin: Y pixel where you intend to draw the layout with this clip * @index_ranges: array of byte indexes into the layout, where even members of array are start indexes and odd elements are end indexes * @n_ranges: number of ranges in @index_ranges, i.e. half the size of @index_ranges * * Obtains a clip region which contains the areas where the given ranges * of text would be drawn. @x_origin and @y_origin are the top left point * to center the layout. @index_ranges should contain * ranges of bytes in the layout's text. * * Note that the regions returned correspond to logical extents of the text * ranges, not ink extents. So the drawn layout may in fact touch areas out of * the clip region. The clip region is mainly useful for highlightling parts * of text, such as when text is selected. * * Return value: a clip region containing the given ranges **/ cairo_region_t* gdk_pango_layout_get_clip_region (PangoLayout *layout, gint x_origin, gint y_origin, const gint *index_ranges, gint n_ranges) { PangoLayoutIter *iter; cairo_region_t *clip_region; g_return_val_if_fail (PANGO_IS_LAYOUT (layout), NULL); g_return_val_if_fail (index_ranges != NULL, NULL); clip_region = cairo_region_create (); iter = pango_layout_get_iter (layout); do { PangoRectangle logical_rect; cairo_region_t *line_region; gint baseline; pango_layout_iter_get_line_extents (iter, NULL, &logical_rect); baseline = pango_layout_iter_get_baseline (iter); line_region = layout_iter_get_line_clip_region(iter, x_origin + PANGO_PIXELS (logical_rect.x), y_origin + PANGO_PIXELS (baseline), index_ranges, n_ranges); cairo_region_union (clip_region, line_region); cairo_region_destroy (line_region); } while (pango_layout_iter_next_line (iter)); pango_layout_iter_free (iter); return clip_region; }
void x11_draw_layout_with_colors( Drawable drawable, GC gc, int x, int y, PangoLayout *layout, wxColour &colour ) { PangoLayoutIter *iter = pango_layout_get_iter (layout); #if defined(__INTEL_COMPILER) && 1 /* VDM auto patch */ # pragma ivdep # pragma swp # pragma unroll # pragma prefetch # if 0 # pragma simd noassert # endif #endif /* VDM auto patch */ do { PangoLayoutLine *line = pango_layout_iter_get_line (iter); PangoRectangle logical_rect; pango_layout_iter_get_line_extents (iter, NULL, &logical_rect); int baseline = pango_layout_iter_get_baseline (iter); x11_draw_layout_line_with_colors( drawable, gc, x + logical_rect.x / PANGO_SCALE, y + baseline / PANGO_SCALE, line, colour ); } while (pango_layout_iter_next_line (iter)); pango_layout_iter_free (iter); }
/** * gdk_pango_layout_line_get_clip_region: (skip) * @line: a #PangoLayoutLine * @x_origin: X pixel where you intend to draw the layout line with this clip * @y_origin: baseline pixel where you intend to draw the layout line with this clip * @index_ranges: (array): array of byte indexes into the layout, * where even members of array are start indexes and odd elements * are end indexes * @n_ranges: number of ranges in @index_ranges, i.e. half the size of @index_ranges * * Obtains a clip region which contains the areas where the given * ranges of text would be drawn. @x_origin and @y_origin are the top left * position of the layout. @index_ranges * should contain ranges of bytes in the layout's text. The clip * region will include space to the left or right of the line (to the * layout bounding box) if you have indexes above or below the indexes * contained inside the line. This is to draw the selection all the way * to the side of the layout. However, the clip region is in line coordinates, * not layout coordinates. * * Note that the regions returned correspond to logical extents of the text * ranges, not ink extents. So the drawn line may in fact touch areas out of * the clip region. The clip region is mainly useful for highlightling parts * of text, such as when text is selected. * * Return value: a clip region containing the given ranges **/ cairo_region_t* gdk_pango_layout_line_get_clip_region (PangoLayoutLine *line, gint x_origin, gint y_origin, const gint *index_ranges, gint n_ranges) { cairo_region_t *clip_region; PangoLayoutIter *iter; g_return_val_if_fail (line != NULL, NULL); g_return_val_if_fail (index_ranges != NULL, NULL); iter = pango_layout_get_iter (line->layout); while (pango_layout_iter_get_line_readonly (iter) != line) pango_layout_iter_next_line (iter); clip_region = layout_iter_get_line_clip_region(iter, x_origin, y_origin, index_ranges, n_ranges); pango_layout_iter_free (iter); return clip_region; }
static void pango_draw (Lisp_Font *f, char *string, size_t length, Window id, GC gc, Lisp_Color *fg, int x, int y) { static XftDraw *draw; XftColor xft_color; PangoLayout *layout; PangoLayoutIter *iter; if (draw == 0) draw = XftDrawCreate (dpy, id, image_visual, image_cmap); else XftDrawChange (draw, id); xft_color.pixel = fg->pixel; xft_color.color.red = fg->red; xft_color.color.green = fg->green; xft_color.color.blue = fg->blue; xft_color.color.alpha = fg->alpha; layout = pango_layout_new (pango_context); pango_layout_set_font_description(layout, f->font); pango_layout_set_text (layout, string, length); iter = pango_layout_get_iter (layout); do { PangoLayoutLine *line = pango_layout_iter_get_line (iter); PangoRectangle rect; pango_layout_iter_get_line_extents (iter, NULL, &rect); pango_draw_line (draw, id, gc, &xft_color, line, x + rect.x / PANGO_SCALE, y); } while (pango_layout_iter_next_line (iter)); g_object_unref (layout); pango_layout_iter_free (iter); }
static void schgui_cairo_drafter_draw_text(SchGUICairoDrafter *drafter, const struct _SchText *text) { if (text != NULL) { int visible; sch_text_get_visible(text, &visible); if (visible) { SchGUICairoDrafterPrivate *privat = SCHGUI_CAIRO_DRAFTER_GET_PRIVATE(drafter); if (privat->cairo != NULL) { PangoLayout *layout; SchMultiline *multiline = sch_text_get_multiline(text); int point_size = sch_text_get_size(text); float height; int alignment; cairo_font_options_t *options; PangoContext *context; int baseline; PangoLayoutIter *iter; int index; int show; SchGUIDrawingCfgColor color; int enabled; sch_text_get_color(text, &index); enabled = schgui_drawing_cfg_get_color(privat->config, index, &color); if (enabled) { if (0) /* show ink rect */ { GeomBounds bounds; int success; success = schgui_cairo_drafter_text_bounds(drafter, text, &bounds); if (success) { cairo_set_source_rgb(privat->cairo, 1.0, 0, 0); cairo_move_to(privat->cairo, bounds.min_x, bounds.min_y); cairo_line_to(privat->cairo, bounds.max_x, bounds.min_y); cairo_stroke(privat->cairo); cairo_set_source_rgb(privat->cairo, 0.75, 0, 0); cairo_move_to(privat->cairo, bounds.max_x, bounds.min_y); cairo_line_to(privat->cairo, bounds.max_x, bounds.max_y); cairo_line_to(privat->cairo, bounds.min_x, bounds.max_y); cairo_line_to(privat->cairo, bounds.min_x, bounds.min_y); //cairo_close_path(privat->cairo); cairo_stroke(privat->cairo); cairo_set_source_rgb(privat->cairo, 0, 0, 0); } cairo_set_source_rgb(privat->cairo, 0, 0, 1.0); cairo_new_sub_path(privat->cairo); cairo_arc(privat->cairo, sch_text_get_x(text), sch_text_get_y(text), 10, 0, 2 * M_PI); cairo_stroke(privat->cairo); cairo_set_source_rgb(privat->cairo, 0, 0, 0); } cairo_save(privat->cairo); height = 1000 * point_size / 72; layout = pango_cairo_create_layout(privat->cairo); pango_cairo_context_set_resolution(pango_layout_get_context(layout), 936); // context = pango_layout_get_context(layout); // options = cairo_font_options_create (); // cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_OFF); // cairo_font_options_set_hint_style (options, CAIRO_HINT_STYLE_MEDIUM); // pango_cairo_context_set_font_options (context, options); // cairo_font_options_destroy (options); cairo_set_source_rgb(privat->cairo, color.red, color.green, color.blue); pango_font_description_set_size(privat->desc, point_size * PANGO_SCALE ); pango_layout_set_spacing(layout, 40000); pango_layout_set_font_description(layout, privat->desc); sch_text_get_show(text, &show); pango_layout_set_markup(layout, sch_multiline_peek_markup(multiline, show), -1); PangoFontMetrics *metrics = pango_context_get_metrics( pango_layout_get_context(layout), privat->desc, NULL ); cairo_move_to(privat->cairo, sch_text_get_x(text), sch_text_get_y(text)); cairo_rotate(privat->cairo, M_PI * sch_text_get_angle(text) / 180); cairo_scale(privat->cairo, 1, -1); baseline = pango_layout_get_baseline(layout); alignment = sch_text_get_alignment(text); #if 1 switch (alignment) { case 2: case 5: case 8: /* upper */ //cairo_rel_move_to(privat->cairo, 0, -pango_font_metrics_get_ascent(metrics)/(privat->zoom * PANGO_SCALE)); //cairo_rel_move_to(privat->cairo, 0, height); break; case 1: case 4: case 7: /* center */ cairo_rel_move_to(privat->cairo, 0, -pango_font_metrics_get_ascent(metrics)/(privat->zoom * PANGO_SCALE)); cairo_rel_move_to(privat->cairo, 0, height); cairo_rel_move_to(privat->cairo, 0, -pango_font_metrics_get_ascent(metrics) * sch_multiline_lines(multiline)/(2 * privat->zoom * PANGO_SCALE)); cairo_rel_move_to(privat->cairo, 0, -pango_font_metrics_get_descent(metrics) * (sch_multiline_lines(multiline) - 1)/(2 * privat->zoom * PANGO_SCALE)); break; case 0: case 3: case 6: default: /* lower */ //cairo_rel_move_to(privat->cairo, 0, -pango_font_metrics_get_ascent(metrics) * sch_multiline_lines(multiline)/(privat->zoom * PANGO_SCALE)); //cairo_rel_move_to(privat->cairo, 0, -pango_font_metrics_get_descent(metrics) * (sch_multiline_lines(multiline)-1)/(privat->zoom * PANGO_SCALE)); //cairo_rel_move_to(privat->cairo, 0, -pango_layout_get_spacing(layout) * (sch_multiline_lines(multiline)-1)/ PANGO_SCALE); iter = pango_layout_get_iter(layout); while (!pango_layout_iter_at_last_line(iter)) { pango_layout_iter_next_line(iter); } cairo_rel_move_to(privat->cairo, 0, -pango_layout_iter_get_baseline(iter) / PANGO_SCALE); pango_layout_iter_free(iter); } #endif //g_debug("Ascent: %d", pango_font_metrics_get_ascent(metrics)); //g_debug("Descent: %d", pango_font_metrics_get_descent(metrics)); //g_debug("Spacing: %d", pango_layout_get_spacing(layout)); //g_debug("Font size: %d", pango_font_description_get_size(privat->desc)); //g_debug("Baseline %d", pango_layout_get_baseline(layout)); pango_font_metrics_unref(metrics); pango_cairo_show_layout(privat->cairo, layout); cairo_restore(privat->cairo); g_object_unref(layout); } } } } }
static void schgui_cairo_text_draw(SchGUICairoDrawItem *item, cairo_t *cairo) { if (cairo != NULL) { SchGUICairoTextPrivate *privat = SCHGUI_CAIRO_TEXT_GET_PRIVATE(item); if (privat != NULL) { PangoLayout *layout = pango_cairo_create_layout(cairo); if (layout != NULL) { PangoLayoutIter *iter; int width; cairo_save(cairo); cairo_set_source_rgba(cairo, privat->red, privat->green, privat->blue, privat->alpha); pango_cairo_context_set_resolution(pango_layout_get_context(layout), 936); pango_layout_set_spacing(layout, 40000); pango_layout_set_font_description(layout, privat->font_desc); pango_layout_set_markup(layout, privat->markup, -1); cairo_move_to(cairo, privat->x, privat->y); cairo_rotate(cairo, privat->angle); cairo_scale(cairo, 1, -1); switch (privat->alignment) { case 2: case 5: case 8: /* upper */ break; case 1: case 4: case 7: /* center */ iter = pango_layout_get_iter(layout); while (!pango_layout_iter_at_last_line(iter)) { pango_layout_iter_next_line(iter); } cairo_rel_move_to(cairo, 0, -pango_layout_iter_get_baseline(iter) / PANGO_SCALE / 2); pango_layout_iter_free(iter); break; case 0: case 3: case 6: default: /* lower */ iter = pango_layout_get_iter(layout); while (!pango_layout_iter_at_last_line(iter)) { pango_layout_iter_next_line(iter); } cairo_rel_move_to(cairo, 0, -pango_layout_iter_get_baseline(iter) / PANGO_SCALE); pango_layout_iter_free(iter); } switch (privat->alignment) { case 3: case 4: case 5: /* center */ pango_layout_get_size(layout, &width, NULL); cairo_rel_move_to(cairo, -width / PANGO_SCALE / 2, 0); break; case 6: case 7: case 8: pango_layout_get_size(layout, &width, NULL); cairo_rel_move_to(cairo, -width / PANGO_SCALE, 0); /* right */ break; case 0: case 1: case 2: default: /* left */ ; } pango_cairo_show_layout(cairo, layout); cairo_restore(cairo); g_object_unref(layout); } } } }
void gnm_rendered_value_remeasure (GnmRenderedValue *rv) { if (rv->rotation) { GnmRenderedRotatedValue *rrv = (GnmRenderedRotatedValue *)rv; PangoContext *context = pango_layout_get_context (rv->layout); double sin_a, abs_sin_a, cos_a; int sdx = 0; int x0 = 0, x1 = 0; PangoLayoutIter *iter; int l = 0; int lwidth; sin_a = rrv->rotmat.xy; abs_sin_a = fabs (sin_a); cos_a = rrv->rotmat.xx; pango_context_set_matrix (context, &rrv->rotmat); pango_layout_context_changed (rv->layout); rrv->linecount = pango_layout_get_line_count (rv->layout); rrv->lines = g_new (struct GnmRenderedRotatedValueInfo, rrv->linecount); pango_layout_get_size (rv->layout, &lwidth, NULL); rv->layout_natural_height = 0; iter = pango_layout_get_iter (rv->layout); do { PangoRectangle logical; int x, dx, dy, indent; int h, ytop, ybot, baseline; pango_layout_iter_get_line_extents (iter, NULL, &logical); pango_layout_iter_get_line_yrange (iter, &ytop, &ybot); baseline = pango_layout_iter_get_baseline (iter); indent = logical.x; if (sin_a < 0) indent -= lwidth; if (l == 0 && rv->noborders) sdx = (int)(baseline * sin_a - ybot / sin_a); dx = sdx + (int)(ybot / sin_a + indent * cos_a); dy = (int)((baseline - ybot) * cos_a - indent * sin_a); rrv->lines[l].dx = dx; rrv->lines[l].dy = dy; /* Left edge. */ x = dx - (int)((baseline - ytop) * sin_a); x0 = MIN (x0, x); /* Right edge. */ x = dx + (int)(logical.width * cos_a + (ybot - baseline) * sin_a); x1 = MAX (x1, x); h = logical.width * abs_sin_a + logical.height * cos_a; if (h > rv->layout_natural_height) rv->layout_natural_height = h; l++; } while (pango_layout_iter_next_line (iter)); pango_layout_iter_free (iter); rv->layout_natural_width = x1 - x0; if (sin_a < 0) { int dx = rv->layout_natural_width; for (l = 0; l < rrv->linecount; l++) rrv->lines[l].dx += dx; } for (l = 0; l < rrv->linecount; l++) rrv->lines[l].dy += rv->layout_natural_height; #if 0 g_print ("Natural size: %d x %d\n", rv->layout_natural_width, rv->layout_natural_height); #endif pango_context_set_matrix (context, NULL); pango_layout_context_changed (rv->layout); } else
static void gimp_text_tool_move_cursor (GimpTextTool *text_tool, GtkMovementStep step, gint count, gboolean extend_selection) { GtkTextBuffer *buffer = GTK_TEXT_BUFFER (text_tool->buffer); GtkTextIter cursor; GtkTextIter selection; GtkTextIter *sel_start; gboolean cancel_selection = FALSE; gint x_pos = -1; GIMP_LOG (TEXT_EDITING, "%s count = %d, select = %s", g_enum_get_value (g_type_class_ref (GTK_TYPE_MOVEMENT_STEP), step)->value_name, count, extend_selection ? "TRUE" : "FALSE"); gtk_text_buffer_get_iter_at_mark (buffer, &cursor, gtk_text_buffer_get_insert (buffer)); gtk_text_buffer_get_iter_at_mark (buffer, &selection, gtk_text_buffer_get_selection_bound (buffer)); if (extend_selection) { sel_start = &selection; } else { /* when there is a selection, moving the cursor without * extending it should move the cursor to the end of the * selection that is in moving direction */ if (count > 0) gtk_text_iter_order (&selection, &cursor); else gtk_text_iter_order (&cursor, &selection); sel_start = &cursor; /* if we actually have a selection, just move *to* the beginning/end * of the selection and not *from* there on LOGICAL_POSITIONS * and VISUAL_POSITIONS movement */ if (! gtk_text_iter_equal (&cursor, &selection)) cancel_selection = TRUE; } switch (step) { case GTK_MOVEMENT_LOGICAL_POSITIONS: if (! cancel_selection) gtk_text_iter_forward_visible_cursor_positions (&cursor, count); break; case GTK_MOVEMENT_VISUAL_POSITIONS: if (! cancel_selection) { PangoLayout *layout; const gchar *text; if (! gimp_text_tool_ensure_layout (text_tool)) break; layout = gimp_text_layout_get_pango_layout (text_tool->layout); text = pango_layout_get_text (layout); while (count != 0) { const gunichar word_joiner = 8288; /*g_utf8_get_char(WORD_JOINER);*/ gint index; gint trailing = 0; gint new_index; index = gimp_text_buffer_get_iter_index (text_tool->buffer, &cursor, TRUE); if (count > 0) { if (g_utf8_get_char (text + index) == word_joiner) pango_layout_move_cursor_visually (layout, TRUE, index, 0, 1, &new_index, &trailing); else new_index = index; pango_layout_move_cursor_visually (layout, TRUE, new_index, trailing, 1, &new_index, &trailing); count--; } else { pango_layout_move_cursor_visually (layout, TRUE, index, 0, -1, &new_index, &trailing); if (new_index != -1 && new_index != G_MAXINT && g_utf8_get_char (text + new_index) == word_joiner) { pango_layout_move_cursor_visually (layout, TRUE, new_index, trailing, -1, &new_index, &trailing); } count++; } if (new_index != G_MAXINT && new_index != -1) index = new_index; else break; gimp_text_buffer_get_iter_at_index (text_tool->buffer, &cursor, index, TRUE); gtk_text_iter_forward_chars (&cursor, trailing); } } break; case GTK_MOVEMENT_WORDS: if (count < 0) { gtk_text_iter_backward_visible_word_starts (&cursor, -count); } else if (count > 0) { if (! gtk_text_iter_forward_visible_word_ends (&cursor, count)) gtk_text_iter_forward_to_line_end (&cursor); } break; case GTK_MOVEMENT_DISPLAY_LINES: { GtkTextIter start; GtkTextIter end; gint cursor_index; PangoLayout *layout; PangoLayoutLine *layout_line; PangoLayoutIter *layout_iter; PangoRectangle logical; gint line; gint trailing; gint i; gtk_text_buffer_get_bounds (buffer, &start, &end); cursor_index = gimp_text_buffer_get_iter_index (text_tool->buffer, &cursor, TRUE); if (! gimp_text_tool_ensure_layout (text_tool)) break; layout = gimp_text_layout_get_pango_layout (text_tool->layout); pango_layout_index_to_line_x (layout, cursor_index, FALSE, &line, &x_pos); layout_iter = pango_layout_get_iter (layout); for (i = 0; i < line; i++) pango_layout_iter_next_line (layout_iter); pango_layout_iter_get_line_extents (layout_iter, NULL, &logical); x_pos += logical.x; pango_layout_iter_free (layout_iter); /* try to go to the remembered x_pos if it exists *and* we are at * the beginning or at the end of the current line */ if (text_tool->x_pos != -1 && (x_pos <= logical.x || x_pos >= logical.x + logical.width)) x_pos = text_tool->x_pos; line += count; if (line < 0) { cursor = start; break; } else if (line >= pango_layout_get_line_count (layout)) { cursor = end; break; } layout_iter = pango_layout_get_iter (layout); for (i = 0; i < line; i++) pango_layout_iter_next_line (layout_iter); layout_line = pango_layout_iter_get_line_readonly (layout_iter); pango_layout_iter_get_line_extents (layout_iter, NULL, &logical); pango_layout_iter_free (layout_iter); pango_layout_line_x_to_index (layout_line, x_pos - logical.x, &cursor_index, &trailing); gimp_text_buffer_get_iter_at_index (text_tool->buffer, &cursor, cursor_index, TRUE); while (trailing--) gtk_text_iter_forward_char (&cursor); } break; case GTK_MOVEMENT_PAGES: /* well... */ case GTK_MOVEMENT_BUFFER_ENDS: if (count < 0) { gtk_text_buffer_get_start_iter (buffer, &cursor); } else if (count > 0) { gtk_text_buffer_get_end_iter (buffer, &cursor); } break; case GTK_MOVEMENT_PARAGRAPH_ENDS: if (count < 0) { gtk_text_iter_set_line_offset (&cursor, 0); } else if (count > 0) { if (! gtk_text_iter_ends_line (&cursor)) gtk_text_iter_forward_to_line_end (&cursor); } break; case GTK_MOVEMENT_DISPLAY_LINE_ENDS: if (count < 0) { gtk_text_iter_set_line_offset (&cursor, 0); } else if (count > 0) { if (! gtk_text_iter_ends_line (&cursor)) gtk_text_iter_forward_to_line_end (&cursor); } break; default: return; } text_tool->x_pos = x_pos; gimp_draw_tool_pause (GIMP_DRAW_TOOL (text_tool)); gimp_text_tool_reset_im_context (text_tool); gtk_text_buffer_select_range (buffer, &cursor, sel_start); gimp_draw_tool_resume (GIMP_DRAW_TOOL (text_tool)); }
/* split a text buffer into chunks using the passed Pango layout */ GList * split_for_layout(PangoLayout * layout, const gchar * text, PangoAttrList * attributes, BalsaPrintSetup * psetup, gboolean is_header, GArray ** offsets) { GList *split_list = NULL; PangoLayoutIter *iter; const gchar *start; gint p_offset; gboolean add_tab; gint p_y0; gint p_y1; gint p_y_pos; gint p_height; /* set the text and its attributes, then get an iter */ pango_layout_set_text(layout, text, -1); if (attributes) pango_layout_set_attributes(layout, attributes); if (offsets) *offsets = g_array_new(FALSE, FALSE, sizeof(guint)); iter = pango_layout_get_iter(layout); /* loop over lines */ start = text; p_offset = 0; add_tab = FALSE; p_y_pos = C_TO_P(psetup->c_y_pos); p_height = C_TO_P(psetup->c_height); do { pango_layout_iter_get_line_yrange(iter, &p_y0, &p_y1); if (p_y_pos + p_y1 - p_offset > p_height) { gint index; gint tr; gchar *chunk; gboolean ends_with_nl; if (offsets) { guint offs = start - text; *offsets = g_array_append_val(*offsets, offs); } pango_layout_xy_to_index(layout, 0, p_y0, &index, &tr); ends_with_nl = text[index - 1] == '\n'; if (ends_with_nl) index--; chunk = g_strndup(start, text + index - start); if (add_tab) split_list = g_list_append(split_list, g_strconcat("\t", chunk, NULL)); else split_list = g_list_append(split_list, g_strdup(chunk)); add_tab = is_header && !ends_with_nl; g_free(chunk); start = text + index; if (ends_with_nl) start++; if (*start == '\0') p_y_pos = p_height; else { p_y_pos = 0; psetup->page_count++; } p_offset = p_y0; } } while (pango_layout_iter_next_line(iter)); pango_layout_iter_free(iter); /* append any remaining stuff */ if (*start != '\0') { p_y_pos += p_y1 - p_offset; if (offsets) { guint offs = start - text; *offsets = g_array_append_val(*offsets, offs); } if (add_tab) split_list = g_list_append(split_list, g_strconcat("\t", start, NULL)); else split_list = g_list_append(split_list, g_strdup(start)); } /* remember the new y position in cairo units */ psetup->c_y_pos = P_TO_C(p_y_pos); /* return the list */ return split_list; }
GpStatus pango_MeasureString (GpGraphics *graphics, GDIPCONST WCHAR *stringUnicode, int length, GDIPCONST GpFont *font, GDIPCONST RectF *rc, GDIPCONST GpStringFormat *format, RectF *boundingBox, int *codepointsFitted, int *linesFilled) { PangoLayout *layout; PangoLayoutLine *line; PangoRectangle logical; PangoLayoutIter *iter; int *charsRemoved = NULL; cairo_save (graphics->ct); layout = gdip_pango_setup_layout (graphics, stringUnicode, length, font, rc, boundingBox, format, &charsRemoved); if (!layout) { cairo_restore (graphics->ct); return OutOfMemory; } if (codepointsFitted) { int charsFitted; int lastIndex; int y0; int y1; double min_x; double max_x; double max_y; const char *layoutText; if (boundingBox && format && (format->formatFlags & StringFormatFlagsDirectionVertical)) { min_x = boundingBox->Y; max_x = boundingBox->Y + boundingBox->Height; max_y = boundingBox->X + boundingBox->Width; } else if (boundingBox) { min_x = boundingBox->X; max_x = boundingBox->X + boundingBox->Width; max_y = boundingBox->Y + boundingBox->Height; } else if (format && (format->formatFlags & StringFormatFlagsDirectionVertical)) { min_x = rc->Y; max_x = rc->Y + rc->Height; max_y = rc->X + rc->Width; } else { min_x = rc->X; max_x = rc->X + rc->Width; max_y = rc->Y + rc->Height; } lastIndex = 0; iter = pango_layout_get_iter (layout); do { if (iter == NULL) break; pango_layout_iter_get_line_yrange (iter, &y0, &y1); if (y0 / PANGO_SCALE >= max_y) break; if (pango_layout_iter_at_last_line (iter)) { do { pango_layout_iter_get_char_extents (iter, &logical); /* check both max and min to catch right-to-left text, also width may be negative */ if ((logical.x / PANGO_SCALE > max_x || (logical.x + logical.width) / PANGO_SCALE > max_x) || (logical.x / PANGO_SCALE < min_x || (logical.x + logical.width) / PANGO_SCALE < min_x)) break; lastIndex = pango_layout_iter_get_index (iter); } while (pango_layout_iter_next_char (iter)); break; } else { line = pango_layout_iter_get_line_readonly (iter); lastIndex = line->start_index + line->length - 1; } } while (pango_layout_iter_next_line (iter)); pango_layout_iter_free (iter); layoutText = pango_layout_get_text (layout); /* this can happen when the string ends in a newline */ if (lastIndex >= strlen (layoutText)) lastIndex = strlen (layoutText) - 1; /* Add back in any & characters removed and the final newline characters (if any) */ charsFitted = g_utf8_strlen (layoutText, lastIndex + 1) + charsRemoved [lastIndex]; //g_warning("lastIndex: %d\t\tcharsRemoved: %d", lastIndex, charsRemoved[lastIndex]); /* safe because of null termination */ switch (layoutText [lastIndex + 1]) { case '\r': charsFitted++; if (layoutText [lastIndex + 2] == '\n') charsFitted++; break; case '\n': charsFitted++; break; } *codepointsFitted = charsFitted; } GdipFree (charsRemoved); if (linesFilled) { *linesFilled = pango_layout_get_line_count (layout); // g_warning ("linesFilled %d", *linesFilled); } // else g_warning ("linesFilled %d", pango_layout_get_line_count (layout)); g_object_unref (layout); cairo_restore (graphics->ct); return Ok; }
PangoLayout* gdip_pango_setup_layout (GpGraphics *graphics, GDIPCONST WCHAR *stringUnicode, int length, GDIPCONST GpFont *font, GDIPCONST RectF *rc, RectF *box, GDIPCONST GpStringFormat *format, int **charsRemoved) { GpStringFormat *fmt; PangoLayout *layout; PangoContext *context; PangoRectangle logical; /* logical size of text (used for alignment) */ PangoRectangle ink; /* ink size of text (to pixel boundaries) */ PangoAttrList *list = NULL; GString *ftext; PangoTabArray *tabs; PangoLayoutIter *iter; int i; int FrameWidth; /* rc->Width (or rc->Height if vertical) */ int FrameHeight; /* rc->Height (or rc->Width if vertical) */ int FrameX; /* rc->X (or rc->Y if vertical) */ int FrameY; /* rc->Y (or rc->X if vertical) */ int y0; /* y0,y1,clipNN used for checking line positions vs. clip rectangle */ int y1; double clipx1; double clipx2; double clipy1; double clipy2; int trimSpace; /* whether or not to trim the space */ gchar *text = ucs2_to_utf8 (stringUnicode, length); if (!text) return NULL; length = strlen(text); if (charsRemoved) { (*charsRemoved) = GdipAlloc (sizeof (int) * length); if (!*charsRemoved) { GdipFree (text); return NULL; } memset (*charsRemoved, 0, sizeof (int) * length); } /* TODO - Digit substitution */ // g_warning ("layout >%s< (%d) [x %g, y %g, w %g, h %g] [font %s, %g points]", text, length, rc->X, rc->Y, rc->Width, FrameHeight, font->face, font->emSize); /* a NULL format is valid, it means get the generic default values (and free them later) */ if (!format) { GpStatus status = GdipStringFormatGetGenericDefault ((GpStringFormat **)&fmt); if (status != Ok) { GdipFree (text); return NULL; } } else { fmt = (GpStringFormat *)format; } layout = pango_cairo_create_layout (graphics->ct); /* context is owned by Pango (i.e. not referenced counted) do not free */ context = pango_layout_get_context (layout); pango_layout_set_font_description (layout, gdip_get_pango_font_description ((GpFont*) font)); if (fmt->formatFlags & StringFormatFlagsDirectionVertical) { FrameWidth = MAKE_SAFE_FOR_PANGO (SAFE_FLOAT_TO_UINT32 (rc->Height)); FrameHeight = MAKE_SAFE_FOR_PANGO (SAFE_FLOAT_TO_UINT32 (rc->Width)); FrameX = SAFE_FLOAT_TO_UINT32 (rc->Y); FrameY = SAFE_FLOAT_TO_UINT32 (rc->X); } else { FrameWidth = MAKE_SAFE_FOR_PANGO (SAFE_FLOAT_TO_UINT32 (rc->Width)); FrameHeight = MAKE_SAFE_FOR_PANGO (SAFE_FLOAT_TO_UINT32 (rc->Height)); FrameX = SAFE_FLOAT_TO_UINT32 (rc->X); FrameY = SAFE_FLOAT_TO_UINT32 (rc->Y); } //g_warning("FW: %d\tFH: %d", FrameWidth, FrameHeight); if ((FrameWidth <= 0) || (fmt->formatFlags & StringFormatFlagsNoWrap)) { pango_layout_set_width (layout, -1); //g_warning ("Setting width: %d", -1); } else { pango_layout_set_width (layout, FrameWidth * PANGO_SCALE); //g_warning ("Setting width: %d", FrameWidth * PANGO_SCALE); } if ((rc->Width != 0) && (rc->Height != 0) && ((fmt->formatFlags & StringFormatFlagsNoClip) == 0)) { // g_warning ("\tclip [%g %g %g %g]", rc->X, rc->Y, rc->Width, rc->Height); /* We do not call cairo_reset_clip because we want to take previous clipping into account */ /* Use rc instead of frame variables because this is pre-transform */ gdip_cairo_rectangle (graphics, rc->X, rc->Y, rc->Width, rc->Height, TRUE); cairo_clip (graphics->ct); } /* with GDI+ the API not the renderer makes the direction decision */ pango_layout_set_auto_dir (layout, FALSE); if (!(fmt->formatFlags & StringFormatFlagsDirectionRightToLeft) != !(fmt->formatFlags & StringFormatFlagsDirectionVertical)) { pango_context_set_base_dir (context, PANGO_DIRECTION_WEAK_RTL); pango_layout_context_changed (layout); /* horizontal alignment */ switch (fmt->alignment) { case StringAlignmentNear: pango_layout_set_alignment (layout, PANGO_ALIGN_RIGHT); break; case StringAlignmentCenter: pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER); break; case StringAlignmentFar: pango_layout_set_alignment (layout, PANGO_ALIGN_LEFT); break; } } else { /* pango default base dir is WEAK_LTR, which is what we want */ /* horizontal alignment */ switch (fmt->alignment) { case StringAlignmentNear: pango_layout_set_alignment (layout, PANGO_ALIGN_LEFT); break; case StringAlignmentCenter: pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER); break; case StringAlignmentFar: pango_layout_set_alignment (layout, PANGO_ALIGN_RIGHT); break; } } #ifdef PANGO_VERSION_CHECK #if PANGO_VERSION_CHECK(1,16,0) if (fmt->formatFlags & StringFormatFlagsDirectionVertical) { if (fmt->formatFlags & StringFormatFlagsDirectionRightToLeft) { cairo_rotate (graphics->ct, M_PI/2.0); cairo_translate (graphics->ct, 0, -FrameHeight); pango_cairo_update_context (graphics->ct, context); } else { cairo_rotate (graphics->ct, 3.0*M_PI/2.0); cairo_translate (graphics->ct, -FrameWidth, 0); pango_cairo_update_context (graphics->ct, context); } /* only since Pango 1.16 */ pango_context_set_base_gravity (context, PANGO_GRAVITY_AUTO); pango_context_set_gravity_hint (context, PANGO_GRAVITY_HINT_LINE); pango_layout_context_changed (layout); } #endif #endif /* TODO - StringFormatFlagsDisplayFormatControl scan and replace them ??? */ /* Trimming options seem to apply only to the end of the string - gdi+ will still wrap * with preference to word first, then character. Unfortunately, pango doesn't have * any way to differentiate wrapping behavior from trimming behavior that I could find */ pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR); switch (fmt->trimming) { case StringTrimmingNone: pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_NONE); break; case StringTrimmingCharacter: pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_NONE); break; case StringTrimmingWord: pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_NONE); break; case StringTrimmingEllipsisCharacter: pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END); if (!(fmt->formatFlags & StringFormatFlagsNoWrap)) pango_layout_set_height (layout, FrameHeight == 0 ? G_MAXINT32 : FrameHeight * PANGO_SCALE); break; case StringTrimmingEllipsisWord: pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END); if (!(fmt->formatFlags & StringFormatFlagsNoWrap)) pango_layout_set_height (layout, FrameHeight == 0 ? G_MAXINT32 : FrameHeight * PANGO_SCALE); break; case StringTrimmingEllipsisPath: pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_MIDDLE); if (!(fmt->formatFlags & StringFormatFlagsNoWrap)) pango_layout_set_height (layout, FrameHeight == 0 ? G_MAXINT32 : FrameHeight * PANGO_SCALE); break; } /* some stuff can only be done by manipulating the attributes (but we can avoid this most of the time) */ if ((fmt->formatFlags & StringFormatFlagsNoFontFallback) || (font->style & (FontStyleUnderline | FontStyleStrikeout))) { list = gdip_get_layout_attributes (layout); /* StringFormatFlagsNoFontFallback */ if (fmt->formatFlags & StringFormatFlagsNoFontFallback) { PangoAttribute *attr = pango_attr_fallback_new (FALSE); attr->start_index = 0; attr->end_index = length; pango_attr_list_insert (list, attr); } if (font->style & FontStyleUnderline) { PangoAttribute *attr = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE); attr->start_index = 0; attr->end_index = length; pango_attr_list_insert (list, attr); } if (font->style & FontStyleStrikeout) { PangoAttribute *attr = pango_attr_strikethrough_new (TRUE); attr->start_index = 0; attr->end_index = length; pango_attr_list_insert (list, attr); } } if (fmt->numtabStops > 0) { float tabPosition; tabs = pango_tab_array_new (fmt->numtabStops, FALSE); tabPosition = fmt->firstTabOffset; for (i = 0; i < fmt->numtabStops; i++) { tabPosition += fmt->tabStops[i]; pango_tab_array_set_tab (tabs, i, PANGO_TAB_LEFT, (gint)min (tabPosition, PANGO_MAX) * PANGO_SCALE); } pango_layout_set_tabs (layout, tabs); pango_tab_array_free (tabs); } //g_warning ("length before ws removal: %d", length); trimSpace = (fmt->formatFlags & StringFormatFlagsMeasureTrailingSpaces) == 0; switch (fmt->hotkeyPrefix) { case HotkeyPrefixHide: /* we need to remove any accelerator from the string */ ftext = gdip_process_string (text, length, 1, trimSpace, NULL, charsRemoved); break; case HotkeyPrefixShow: /* optimization: is seems that we never see the hotkey when using an underline font */ if (font->style & FontStyleUnderline) { /* so don't bother drawing it (and simply add the '&' character) */ ftext = gdip_process_string (text, length, 1, trimSpace, NULL, charsRemoved); } else { /* find accelerator and add attribute to the next character (unless it's the prefix too) */ if (!list) list = gdip_get_layout_attributes (layout); ftext = gdip_process_string (text, length, 1, trimSpace, list, charsRemoved); } break; default: ftext = gdip_process_string (text, length, 0, trimSpace, NULL, charsRemoved); break; } length = ftext->len; //g_warning ("length after ws removal: %d", length); if (list) { pango_layout_set_attributes (layout, list); pango_attr_list_unref (list); } // g_warning("\tftext>%s< (%d)", ftext->str, -1); pango_layout_set_text (layout, ftext->str, ftext->len); GdipFree (text); g_string_free(ftext, TRUE); /* Trim the text after the last line for ease of counting lines/characters */ /* Also prevents drawing whole lines outside the boundaries if NoClip was specified */ /* In case of pre-existing clipping, use smaller of clip rectangle or our specified height */ if (FrameHeight > 0) { cairo_clip_extents (graphics->ct, &clipx1, &clipy1, &clipx2, &clipy2); if (clipy2 > 0 && !(fmt->formatFlags & StringFormatFlagsNoClip)) clipy2 = min (clipy2, FrameHeight + FrameY); else clipy2 = FrameHeight + FrameY; iter = pango_layout_get_iter (layout); do { if (iter == NULL) break; pango_layout_iter_get_line_yrange (iter, &y0, &y1); //g_warning("yrange: %d %d clipy2: %f", y0 / PANGO_SCALE, y1 / PANGO_SCALE, clipy2); /* StringFormatFlagsLineLimit */ if (((fmt->formatFlags & StringFormatFlagsLineLimit) && y1 / PANGO_SCALE > clipy2) || (y0 / PANGO_SCALE > clipy2)) { PangoLayoutLine *line = pango_layout_iter_get_line_readonly (iter); pango_layout_set_text (layout, pango_layout_get_text (layout), line->start_index); break; } } while (pango_layout_iter_next_line (iter)); pango_layout_iter_free (iter); } pango_layout_get_pixel_extents (layout, &ink, &logical); // g_warning ("\tlogical\t[x %d, y %d, w %d, h %d][x %d, y %d, w %d, h %d]", logical.x, logical.y, logical.width, logical.height, ink.x, ink.y, ink.width, ink.height); if ((fmt->formatFlags & StringFormatFlagsNoFitBlackBox) == 0) { /* By default don't allow overhang - ink space may be larger than logical space */ if (fmt->formatFlags & StringFormatFlagsDirectionVertical) { box->X = min (ink.y, logical.y); box->Y = min (ink.x, logical.x); box->Height = max (ink.width, logical.width); box->Width = max (ink.height, logical.height); } else { box->X = min (ink.x, logical.x); box->Y = min (ink.y, logical.y); box->Height = max (ink.height, logical.height); box->Width = max (ink.width, logical.width); } } else { /* Allow overhang */ if (fmt->formatFlags & StringFormatFlagsDirectionVertical) { box->X = logical.y; box->Y = logical.x; box->Height = logical.width; box->Width = logical.height; } else { box->X = logical.x; box->Y = logical.y; box->Height = logical.height; box->Width = logical.width; } } // g_warning ("\tbox\t[x %g, y %g, w %g, h %g]", box->X, box->Y, box->Width, box->Height); /* vertical alignment*/ if (fmt->formatFlags & StringFormatFlagsDirectionVertical) { switch (fmt->lineAlignment) { case StringAlignmentNear: break; case StringAlignmentCenter: box->X += (rc->Width - box->Width) / 2; break; case StringAlignmentFar: box->X += (rc->Width - box->Width); break; } } else { switch (fmt->lineAlignment) { case StringAlignmentNear: break; case StringAlignmentCenter: box->Y += (rc->Height - box->Height) / 2; break; case StringAlignmentFar: box->Y += (rc->Height - box->Height); break; } } // g_warning ("va-box\t[x %g, y %g, w %g, h %g]", box->X, box->Y, box->Width, box->Height); pango_cairo_update_layout (graphics->ct, layout); return layout; }
static void render_para (GdkDrawable *drawable, GtkTextRenderState *render_state, GtkTextLineDisplay *line_display, /* Top-left corner of paragraph including all margins */ int x, int y, int selection_start_index, int selection_end_index, GList **widgets) { GSList *shaped_pointer = line_display->shaped_objects; PangoLayout *layout = line_display->layout; int byte_offset = 0; PangoLayoutIter *iter; PangoRectangle layout_logical; int screen_width; GdkGC *fg_gc, *bg_gc; gint state; gboolean first = TRUE; iter = pango_layout_get_iter (layout); pango_layout_iter_get_layout_extents (iter, NULL, &layout_logical); /* Adjust for margins */ layout_logical.x += line_display->x_offset * PANGO_SCALE; layout_logical.y += line_display->top_margin * PANGO_SCALE; screen_width = line_display->total_width; if (GTK_WIDGET_HAS_FOCUS (render_state->widget)) state = GTK_STATE_SELECTED; else state = GTK_STATE_ACTIVE; fg_gc = render_state->widget->style->text_gc [state]; bg_gc = render_state->widget->style->base_gc [state]; do { PangoLayoutLine *line = pango_layout_iter_get_line (iter); int selection_y, selection_height; int first_y, last_y; PangoRectangle line_rect; int baseline; pango_layout_iter_get_line_extents (iter, NULL, &line_rect); baseline = pango_layout_iter_get_baseline (iter); pango_layout_iter_get_line_yrange (iter, &first_y, &last_y); /* Adjust for margins */ line_rect.x += line_display->x_offset * PANGO_SCALE; line_rect.y += line_display->top_margin * PANGO_SCALE; baseline += line_display->top_margin * PANGO_SCALE; /* Selection is the height of the line, plus top/bottom * margin if we're the first/last line */ selection_y = y + PANGO_PIXELS (first_y) + line_display->top_margin; selection_height = PANGO_PIXELS (last_y) - PANGO_PIXELS (first_y); if (first) { selection_y -= line_display->top_margin; selection_height += line_display->top_margin; } if (pango_layout_iter_at_last_line (iter)) selection_height += line_display->bottom_margin; first = FALSE; if (selection_start_index < byte_offset && selection_end_index > line->length + byte_offset) /* All selected */ { gdk_draw_rectangle (drawable, bg_gc, TRUE, x + line_display->left_margin, selection_y, screen_width, selection_height); render_layout_line (drawable, render_state, line, &shaped_pointer, x + PANGO_PIXELS (line_rect.x), y + PANGO_PIXELS (baseline), TRUE, widgets); } else { GSList *shaped_pointer_tmp = shaped_pointer; render_layout_line (drawable, render_state, line, &shaped_pointer, x + PANGO_PIXELS (line_rect.x), y + PANGO_PIXELS (baseline), FALSE, widgets); if (selection_start_index <= byte_offset + line->length && selection_end_index > byte_offset) /* Some selected */ { GdkRegion *clip_region = get_selected_clip (render_state, layout, line, x + line_display->x_offset, selection_y, selection_height, selection_start_index, selection_end_index); gdk_gc_set_clip_region (fg_gc, clip_region); gdk_gc_set_clip_region (bg_gc, clip_region); gdk_draw_rectangle (drawable, bg_gc, TRUE, x + PANGO_PIXELS (line_rect.x), selection_y, PANGO_PIXELS (line_rect.width), selection_height); render_layout_line (drawable, render_state, line, &shaped_pointer_tmp, x + PANGO_PIXELS (line_rect.x), y + PANGO_PIXELS (baseline), TRUE, widgets); gdk_gc_set_clip_region (fg_gc, NULL); gdk_gc_set_clip_region (bg_gc, NULL); gdk_region_destroy (clip_region); /* Paint in the ends of the line */ if (line_rect.x > line_display->left_margin * PANGO_SCALE && ((line_display->direction == GTK_TEXT_DIR_LTR && selection_start_index < byte_offset) || (line_display->direction == GTK_TEXT_DIR_RTL && selection_end_index > byte_offset + line->length))) { gdk_draw_rectangle (drawable, bg_gc, TRUE, x + line_display->left_margin, selection_y, PANGO_PIXELS (line_rect.x) - line_display->left_margin, selection_height); } if (line_rect.x + line_rect.width < (screen_width + line_display->left_margin) * PANGO_SCALE && ((line_display->direction == GTK_TEXT_DIR_LTR && selection_end_index > byte_offset + line->length) || (line_display->direction == GTK_TEXT_DIR_RTL && selection_start_index < byte_offset))) { int nonlayout_width; nonlayout_width = line_display->left_margin + screen_width - PANGO_PIXELS (line_rect.x) - PANGO_PIXELS (line_rect.width); gdk_draw_rectangle (drawable, bg_gc, TRUE, x + PANGO_PIXELS (line_rect.x) + PANGO_PIXELS (line_rect.width), selection_y, nonlayout_width, selection_height); } } } byte_offset += line->length; } while (pango_layout_iter_next_line (iter)); pango_layout_iter_free (iter); }
static void create_pages (Pqueue * queue) { gchar * msg; gdouble line_height; guint i, id; glong index; PangoLayoutLine * line; PangoRectangle ink_rect, logical_rect; g_return_if_fail (queue); g_return_if_fail (queue->pos < strlen(queue->text)); while (queue->pos < strlen (queue->text)) { while (gtk_events_pending ()) gtk_main_iteration (); for (i = 0; i < queue->lines_per_page; i++) { line = pango_layout_iter_get_line (queue->iter); pango_layout_iter_next_line (queue->iter); pango_layout_iter_get_line_extents (queue->iter, &ink_rect, &logical_rect); index = pango_layout_iter_get_index (queue->iter); if (index == 0) { i = queue->lines_per_page; queue->pos = strlen (queue->text); g_message ("%s", _("Error: Pango iter index is zero.")); continue; } line_height = logical_rect.height / PANGO_SCALE; if ((queue->page_height + line_height) > (queue->height - (EDGE_MARGIN/2))) { queue->pos += index; queue->page_height = EDGE_MARGIN; gtk_progress_bar_pulse (queue->progressbar); pango_cairo_update_layout (queue->cr, queue->layout); queue->layout = make_new_page (queue->context, queue->desc, queue->height, queue->width); i = queue->lines_per_page; queue->page_count++; pango_layout_set_text (queue->layout, (queue->text+queue->pos), -1); queue->iter = pango_layout_get_iter (queue->layout); pango_cairo_show_layout_line (queue->cr, line); pango_cairo_update_layout (queue->cr, queue->layout); cairo_show_page (queue->cr); } else pango_cairo_show_layout_line (queue->cr, line); queue->page_height += line_height; cairo_move_to (queue->cr, SIDE_MARGIN / 2, queue->page_height); } } pango_layout_iter_free (queue->iter); gtk_progress_bar_set_fraction (queue->progressbar, 0.0); cairo_surface_destroy(queue->surface); pango_font_description_free (queue->desc); g_object_unref (queue->context); g_object_unref (queue->layout); cairo_destroy (queue->cr); id = gtk_statusbar_get_context_id (queue->statusbar, PACKAGE); msg = g_strdup_printf (ngettext("Saved PDF file. (%ld page)", "Saved PDF file (%ld pages).", queue->page_count), queue->page_count); gtk_statusbar_push (queue->statusbar, id, msg); g_free (msg); }
void text_wrapper::DoLayout(void) { // THE function // first some sanity checks if ( default_font == NULL ) return; if ( uni32_length <= 0 || utf8_length <= 0 ) return; // prepare the pangolayout object { //char *tc = pango_font_description_to_string(default_font->descr); //printf("layout with %s\n", tc); //free(tc); } pango_layout_set_font_description(pLayout, default_font->descr); pango_layout_set_text(pLayout, utf8_text, utf8_length); // reset the glyph string if ( glyph_text ) free(glyph_text); glyph_text = NULL; glyph_length = 0; double pango_to_ink = (1.0 / ((double)PANGO_SCALE)); // utility int max_g = 0; PangoLayoutIter *pIter = pango_layout_get_iter(pLayout); // and go! do { PangoLayoutLine *pLine = pango_layout_iter_get_line(pIter); // no need for unref int plOffset = pLine->start_index; // start of the line in the uni32_text PangoRectangle ink_r, log_r; pango_layout_iter_get_line_extents(pIter, &ink_r, &log_r); double plY = (1.0 / ((double)PANGO_SCALE)) * ((double)log_r.y); // start position of this line of the layout double plX = (1.0 / ((double)PANGO_SCALE)) * ((double)log_r.x); GSList *curR = pLine->runs; // get ready to iterate over the runs of this line while ( curR ) { PangoLayoutRun *pRun = (PangoLayoutRun*)curR->data; if ( pRun ) { int prOffset = pRun->item->offset; // start of the run in the line // a run has uniform font/directionality/etc... int o_g_l = glyph_length; // save the index of the first glyph we'll add for (int i = 0; i < pRun->glyphs->num_glyphs; i++) { // add glyph sequentially, reading them from the run // realloc the structures if ( glyph_length >= max_g ) { max_g = 2 * glyph_length + 1; one_glyph *newdata = static_cast<one_glyph*>(realloc(glyph_text, (max_g + 1) * sizeof(one_glyph))); if (newdata != NULL) { glyph_text = newdata; } else { g_warning("Failed to reallocate glyph_text"); } } // fill the glyph info glyph_text[glyph_length].font = pRun->item->analysis.font; glyph_text[glyph_length].gl = pRun->glyphs->glyphs[i].glyph; glyph_text[glyph_length].uni_st = plOffset + prOffset + pRun->glyphs->log_clusters[i]; // depending on the directionality, the last uni32 codepoint for this glyph is the first of the next char // or the first of the previous if ( pRun->item->analysis.level == 1 ) { // rtl if ( i < pRun->glyphs->num_glyphs - 1 ) { glyph_text[glyph_length + 1].uni_en = glyph_text[glyph_length].uni_st; } glyph_text[glyph_length].uni_dir = 1; glyph_text[glyph_length + 1].uni_dir = 1; // set the directionality for the next too, so that the last glyph in // the array has the correct direction } else { // ltr if ( i > 0 ) { glyph_text[glyph_length - 1].uni_en = glyph_text[glyph_length].uni_st; } glyph_text[glyph_length].uni_dir = 0; glyph_text[glyph_length + 1].uni_dir = 0; } // set the position // the layout is an infinite line glyph_text[glyph_length].x = plX + pango_to_ink * ((double)pRun->glyphs->glyphs[i].geometry.x_offset); glyph_text[glyph_length].y = plY + pango_to_ink * ((double)pRun->glyphs->glyphs[i].geometry.y_offset); // advance to the next glyph plX += pango_to_ink * ((double)pRun->glyphs->glyphs[i].geometry.width); // and set the next glyph's position, in case it's the terminating glyph glyph_text[glyph_length + 1].x = plX; glyph_text[glyph_length + 1].y = plY; glyph_length++; } // and finish filling the info // notably, the uni_en of the last char in ltr text and the uni_en of the first in rtl are still not set if ( pRun->item->analysis.level == 1 ) { // rtl if ( glyph_length > o_g_l ) glyph_text[o_g_l].uni_en = plOffset + prOffset + pRun->item->length; } else { if ( glyph_length > 0 ) glyph_text[glyph_length - 1].uni_en = plOffset + prOffset + pRun->item->length; } // the terminating glyph has glyph_id=0 because it means 'no glyph' glyph_text[glyph_length].gl = 0; // and is associated with no text (but you cannot set uni_st=uni_en=0, because the termination // is expected to be the glyph for the termination of the uni32_text) glyph_text[glyph_length].uni_st = glyph_text[glyph_length].uni_en = plOffset + prOffset + pRun->item->length; } curR = curR->next; } } while ( pango_layout_iter_next_line(pIter) ); pango_layout_iter_free(pIter); // grunt work done. now some additional info for layout: computing letters, mostly (one letter = several glyphs sometimes) PangoLogAttr *pAttrs = NULL; int nbAttr = 0; // get the layout attrs, they hold the boundaries pango computed pango_layout_get_log_attrs(pLayout, &pAttrs, &nbAttr); // feed to MakeTextBoundaries which knows what to do with these MakeTextBoundaries(pAttrs, nbAttr); // the array of boundaries is full, but out-of-order SortBoundaries(); // boundary array is ready to be used, call chunktext to fill the *_start fields of the glyphs, and compute // the boxed version of the text for sp-typeset ChunkText(); // get rid of the attributes if ( pAttrs ) g_free(pAttrs); // cleaning up for (int i = 0; i < glyph_length; i++) { glyph_text[i].uni_st = uni32_codepoint[glyph_text[i].uni_st]; glyph_text[i].uni_en = uni32_codepoint[glyph_text[i].uni_en]; glyph_text[i].x /= 512; // why is this not default_font->parent->fontsize? glyph_text[i].y /= 512; } if ( glyph_length > 0 ) { glyph_text[glyph_length].x /= 512; glyph_text[glyph_length].y /= 512; } }
static VALUE layout_iter_next_line(VALUE self) { return CBOOL2RVAL(pango_layout_iter_next_line(_SELF(self))); }