Bug Summary

File:ctk/ctkpango.c
Warning:line 721, column 26
Access to field 'start_index' results in a dereference of a null pointer (loaded from variable 'line')

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple x86_64-pc-linux-gnu -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name ctkpango.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 2 -fhalf-no-semantic-interposition -mframe-pointer=all -fmath-errno -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/rootdir/ctk -resource-dir /usr/lib/llvm-16/lib/clang/16 -D HAVE_CONFIG_H -I . -I .. -D G_LOG_DOMAIN="Ctk" -D G_LOG_USE_STRUCTURED=1 -D CTK_VERSION="3.25.5" -D CTK_BINARY_VERSION="3.0.0" -D CTK_COMPILATION -D CTK_PRINT_BACKEND_ENABLE_UNSUPPORTED -D CTK_LIBDIR="/usr/lib" -D CTK_LOCALEDIR="/usr/share/locale" -D CTK_DATADIR="/usr/share" -D CTK_DATA_PREFIX="/usr" -D CTK_SYSCONFDIR="/usr/etc" -D CTK_HOST="x86_64-pc-linux-gnu" -D CTK_PRINT_BACKENDS="file,cups" -D X11_DATA_PREFIX="/usr" -D ISO_CODES_PREFIX="" -I .. -I ../ctk -I .. -I ../cdk -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/sysprof-6 -D G_ENABLE_DEBUG -D G_ENABLE_CONSISTENCY_CHECKS -D GLIB_MIN_REQUIRED_VERSION=GLIB_VERSION_2_66 -D GLIB_MAX_ALLOWED_VERSION=GLIB_VERSION_2_66 -I /usr/include/pango-1.0 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/sysprof-6 -I /usr/include/harfbuzz -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/fribidi -I /usr/include/cairo -I /usr/include/pixman-1 -I /usr/include/atk-1.0 -I /usr/include/gdk-pixbuf-2.0 -I /usr/include/x86_64-linux-gnu -I /usr/include/webp -I /usr/include/at-spi2-atk/2.0 -I /usr/include/at-spi-2.0 -I /usr/include/dbus-1.0 -I /usr/lib/x86_64-linux-gnu/dbus-1.0/include -I /usr/include/gio-unix-2.0 -I /usr/include/harfbuzz -I /usr/include/freetype2 -I /usr/include/libpng16 -I /usr/include/glib-2.0 -I /usr/lib/x86_64-linux-gnu/glib-2.0/include -I /usr/include/sysprof-6 -I /usr/include/pango-1.0 -I /usr/include/libmount -I /usr/include/blkid -I /usr/include/fribidi -I /usr/include/cairo -I /usr/include/pixman-1 -D PIC -internal-isystem /usr/lib/llvm-16/lib/clang/16/include -internal-isystem /usr/local/include -internal-isystem /usr/lib/gcc/x86_64-linux-gnu/14/../../../../x86_64-linux-gnu/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -fdebug-compilation-dir=/rootdir/ctk -ferror-limit 19 -fvisibility=hidden -fgnuc-version=4.2.1 -analyzer-checker deadcode.DeadStores -analyzer-checker alpha.deadcode.UnreachableCode -analyzer-checker alpha.core.CastSize -analyzer-checker alpha.core.CastToStruct -analyzer-checker alpha.core.IdenticalExpr -analyzer-checker alpha.core.SizeofPtr -analyzer-checker alpha.security.ArrayBoundV2 -analyzer-checker alpha.security.MallocOverflow -analyzer-checker alpha.security.ReturnPtrRange -analyzer-checker alpha.unix.SimpleStream -analyzer-checker alpha.unix.cstring.BufferOverlap -analyzer-checker alpha.unix.cstring.NotNullTerminated -analyzer-checker alpha.unix.cstring.OutOfBounds -analyzer-checker alpha.core.FixedAddr -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /rootdir/html-report/2024-09-19-171836-43636-1 -x c ctkpango.c
1/* ctkpango.c - pango-related utilities
2 *
3 * Copyright (c) 2010 Red Hat, Inc.
4 *
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public
7 * License as published by the Free Software Foundation; either
8 * version 2 of the License, or (at your option) any later version.
9 *
10 * This library is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 * Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library. If not, see <http://www.gnu.org/licenses/>.Free
17 */
18/*
19 * Modified by the CTK+ Team and others 1997-2000. See the AUTHORS
20 * file for a list of people on the CTK+ Team. See the ChangeLog
21 * files for a list of changes. These files are distributed with
22 * CTK+ at ftp://ftp.ctk.org/pub/ctk/.
23 */
24
25#include "config.h"
26#include "ctkpango.h"
27#include <pango/pangocairo.h>
28#include <fribidi.h>
29#include "ctkintl.h"
30
31#define CTK_TYPE_FILL_LAYOUT_RENDERER(_ctk_fill_layout_renderer_get_type()) (_ctk_fill_layout_renderer_get_type())
32#define CTK_FILL_LAYOUT_RENDERER(object)((((CtkFillLayoutRenderer*) (void *) g_type_check_instance_cast
((GTypeInstance*) ((object)), ((_ctk_fill_layout_renderer_get_type
()))))))
(G_TYPE_CHECK_INSTANCE_CAST ((object), CTK_TYPE_FILL_LAYOUT_RENDERER, CtkFillLayoutRenderer)(((CtkFillLayoutRenderer*) (void *) g_type_check_instance_cast
((GTypeInstance*) ((object)), ((_ctk_fill_layout_renderer_get_type
())))))
)
33#define CTK_IS_FILL_LAYOUT_RENDERER(object)(((__extension__ ({ GTypeInstance *__inst = (GTypeInstance*) (
(object)); GType __t = ((_ctk_fill_layout_renderer_get_type()
)); gboolean __r; if (!__inst) __r = (0); else if (__inst->
g_class && __inst->g_class->g_type == __t) __r =
(!(0)); else __r = g_type_check_instance_is_a (__inst, __t);
__r; }))))
(G_TYPE_CHECK_INSTANCE_TYPE ((object), CTK_TYPE_FILL_LAYOUT_RENDERER)((__extension__ ({ GTypeInstance *__inst = (GTypeInstance*) (
(object)); GType __t = ((_ctk_fill_layout_renderer_get_type()
)); gboolean __r; if (!__inst) __r = (0); else if (__inst->
g_class && __inst->g_class->g_type == __t) __r =
(!(0)); else __r = g_type_check_instance_is_a (__inst, __t);
__r; })))
)
34#define CTK_FILL_LAYOUT_RENDERER_CLASS(klass)((((CtkFillLayoutRendererClass*) (void *) g_type_check_class_cast
((GTypeClass*) ((klass)), ((_ctk_fill_layout_renderer_get_type
()))))))
(G_TYPE_CHECK_CLASS_CAST ((klass), CTK_TYPE_FILL_LAYOUT_RENDERER, CtkFillLayoutRendererClass)(((CtkFillLayoutRendererClass*) (void *) g_type_check_class_cast
((GTypeClass*) ((klass)), ((_ctk_fill_layout_renderer_get_type
())))))
)
35#define CTK_IS_FILL_LAYOUT_RENDERER_CLASS(klass)(((__extension__ ({ GTypeClass *__class = (GTypeClass*) ((klass
)); GType __t = ((_ctk_fill_layout_renderer_get_type())); gboolean
__r; if (!__class) __r = (0); else if (__class->g_type ==
__t) __r = (!(0)); else __r = g_type_check_class_is_a (__class
, __t); __r; }))))
(G_TYPE_CHECK_CLASS_TYPE ((klass), CTK_TYPE_FILL_LAYOUT_RENDERER)((__extension__ ({ GTypeClass *__class = (GTypeClass*) ((klass
)); GType __t = ((_ctk_fill_layout_renderer_get_type())); gboolean
__r; if (!__class) __r = (0); else if (__class->g_type ==
__t) __r = (!(0)); else __r = g_type_check_class_is_a (__class
, __t); __r; })))
)
36#define CTK_FILL_LAYOUT_RENDERER_GET_CLASS(obj)((((CtkFillLayoutRendererClass*) (((GTypeInstance*) ((obj)))->
g_class))))
(G_TYPE_INSTANCE_GET_CLASS ((obj), CTK_TYPE_FILL_LAYOUT_RENDERER, CtkFillLayoutRendererClass)(((CtkFillLayoutRendererClass*) (((GTypeInstance*) ((obj)))->
g_class)))
)
37
38typedef struct _CtkFillLayoutRenderer CtkFillLayoutRenderer;
39typedef struct _CtkFillLayoutRendererClass CtkFillLayoutRendererClass;
40
41struct _CtkFillLayoutRenderer
42{
43 PangoRenderer parent_instance;
44
45 cairo_t *cr;
46};
47
48struct _CtkFillLayoutRendererClass
49{
50 PangoRendererClass parent_class;
51};
52
53GType _ctk_fill_layout_renderer_get_type (void);
54
55G_DEFINE_TYPE (CtkFillLayoutRenderer, _ctk_fill_layout_renderer, PANGO_TYPE_RENDERER)static void _ctk_fill_layout_renderer_init (CtkFillLayoutRenderer
*self); static void _ctk_fill_layout_renderer_class_init (CtkFillLayoutRendererClass
*klass); static GType _ctk_fill_layout_renderer_get_type_once
(void); static gpointer _ctk_fill_layout_renderer_parent_class
= ((void*)0); static gint CtkFillLayoutRenderer_private_offset
; static void _ctk_fill_layout_renderer_class_intern_init (gpointer
klass) { _ctk_fill_layout_renderer_parent_class = g_type_class_peek_parent
(klass); if (CtkFillLayoutRenderer_private_offset != 0) g_type_class_adjust_private_offset
(klass, &CtkFillLayoutRenderer_private_offset); _ctk_fill_layout_renderer_class_init
((CtkFillLayoutRendererClass*) klass); } __attribute__ ((__unused__
)) static inline gpointer _ctk_fill_layout_renderer_get_instance_private
(CtkFillLayoutRenderer *self) { return (((gpointer) ((guint8
*) (self) + (glong) (CtkFillLayoutRenderer_private_offset))))
; } GType _ctk_fill_layout_renderer_get_type (void) { static GType
static_g_define_type_id = 0; if ((__extension__ ({ _Static_assert
(sizeof *(&static_g_define_type_id) == sizeof (gpointer)
, "Expression evaluates to false"); (void) (0 ? (gpointer) * (
&static_g_define_type_id) : ((void*)0)); (!(__extension__
({ _Static_assert (sizeof *(&static_g_define_type_id) ==
sizeof (gpointer), "Expression evaluates to false"); __typeof__
(*(&static_g_define_type_id)) gapg_temp_newval; __typeof__
((&static_g_define_type_id)) gapg_temp_atomic = (&static_g_define_type_id
); __atomic_load (gapg_temp_atomic, &gapg_temp_newval, 5)
; gapg_temp_newval; })) && g_once_init_enter_pointer (
&static_g_define_type_id)); })) ) { GType g_define_type_id
= _ctk_fill_layout_renderer_get_type_once (); (__extension__
({ _Static_assert (sizeof *(&static_g_define_type_id) ==
sizeof (gpointer), "Expression evaluates to false"); 0 ? (void
) (*(&static_g_define_type_id) = (g_define_type_id)) : (void
) 0; g_once_init_leave_pointer ((&static_g_define_type_id
), (gpointer) (guintptr) (g_define_type_id)); })) ; } return static_g_define_type_id
; } __attribute__ ((__noinline__)) static GType _ctk_fill_layout_renderer_get_type_once
(void) { GType g_define_type_id = g_type_register_static_simple
((pango_renderer_get_type()), g_intern_static_string ("CtkFillLayoutRenderer"
), sizeof (CtkFillLayoutRendererClass), (GClassInitFunc)(void
(*)(void)) _ctk_fill_layout_renderer_class_intern_init, sizeof
(CtkFillLayoutRenderer), (GInstanceInitFunc)(void (*)(void))
_ctk_fill_layout_renderer_init, (GTypeFlags) 0); { {{};} } return
g_define_type_id; }
56
57static void
58ctk_fill_layout_renderer_draw_glyphs (PangoRenderer *renderer,
59 PangoFont *font,
60 PangoGlyphString *glyphs,
61 int x,
62 int y)
63{
64 CtkFillLayoutRenderer *text_renderer = CTK_FILL_LAYOUT_RENDERER (renderer)((((CtkFillLayoutRenderer*) (void *) g_type_check_instance_cast
((GTypeInstance*) ((renderer)), ((_ctk_fill_layout_renderer_get_type
()))))))
;
65
66 cairo_move_to (text_renderer->cr, (double)x / PANGO_SCALE1024, (double)y / PANGO_SCALE1024);
67 pango_cairo_show_glyph_string (text_renderer->cr, font, glyphs);
68}
69
70static void
71ctk_fill_layout_renderer_draw_glyph_item (PangoRenderer *renderer,
72 const char *text,
73 PangoGlyphItem *glyph_item,
74 int x,
75 int y)
76{
77 CtkFillLayoutRenderer *text_renderer = CTK_FILL_LAYOUT_RENDERER (renderer)((((CtkFillLayoutRenderer*) (void *) g_type_check_instance_cast
((GTypeInstance*) ((renderer)), ((_ctk_fill_layout_renderer_get_type
()))))))
;
78
79 cairo_move_to (text_renderer->cr, (double)x / PANGO_SCALE1024, (double)y / PANGO_SCALE1024);
80 pango_cairo_show_glyph_item (text_renderer->cr, text, glyph_item);
81}
82
83static void
84ctk_fill_layout_renderer_draw_rectangle (PangoRenderer *renderer,
85 PangoRenderPart part,
86 int x,
87 int y,
88 int width,
89 int height)
90{
91 CtkFillLayoutRenderer *text_renderer = CTK_FILL_LAYOUT_RENDERER (renderer)((((CtkFillLayoutRenderer*) (void *) g_type_check_instance_cast
((GTypeInstance*) ((renderer)), ((_ctk_fill_layout_renderer_get_type
()))))))
;
92
93 if (part == PANGO_RENDER_PART_BACKGROUND)
94 return;
95
96 cairo_rectangle (text_renderer->cr,
97 (double)x / PANGO_SCALE1024, (double)y / PANGO_SCALE1024,
98 (double)width / PANGO_SCALE1024, (double)height / PANGO_SCALE1024);
99 cairo_fill (text_renderer->cr);
100}
101
102static void
103ctk_fill_layout_renderer_draw_trapezoid (PangoRenderer *renderer,
104 PangoRenderPart part G_GNUC_UNUSED__attribute__ ((__unused__)),
105 double y1_,
106 double x11,
107 double x21,
108 double y2,
109 double x12,
110 double x22)
111{
112 CtkFillLayoutRenderer *text_renderer = CTK_FILL_LAYOUT_RENDERER (renderer)((((CtkFillLayoutRenderer*) (void *) g_type_check_instance_cast
((GTypeInstance*) ((renderer)), ((_ctk_fill_layout_renderer_get_type
()))))))
;
113 cairo_matrix_t matrix;
114 cairo_t *cr;
115
116 cr = text_renderer->cr;
117
118 cairo_save (cr);
119
120 /* use identity scale, but keep translation */
121 cairo_get_matrix (cr, &matrix);
122 matrix.xx = matrix.yy = 1;
123 matrix.xy = matrix.yx = 0;
124 cairo_set_matrix (cr, &matrix);
125
126 cairo_move_to (cr, x11, y1_);
127 cairo_line_to (cr, x21, y1_);
128 cairo_line_to (cr, x22, y2);
129 cairo_line_to (cr, x12, y2);
130 cairo_close_path (cr);
131
132 cairo_fill (cr);
133
134 cairo_restore (cr);
135}
136
137static void
138ctk_fill_layout_renderer_draw_error_underline (PangoRenderer *renderer,
139 int x,
140 int y,
141 int width,
142 int height)
143{
144 CtkFillLayoutRenderer *text_renderer = CTK_FILL_LAYOUT_RENDERER (renderer)((((CtkFillLayoutRenderer*) (void *) g_type_check_instance_cast
((GTypeInstance*) ((renderer)), ((_ctk_fill_layout_renderer_get_type
()))))))
;
145
146 pango_cairo_show_error_underline (text_renderer->cr,
147 (double)x / PANGO_SCALE1024, (double)y / PANGO_SCALE1024,
148 (double)width / PANGO_SCALE1024, (double)height / PANGO_SCALE1024);
149}
150
151static void
152ctk_fill_layout_renderer_draw_shape (PangoRenderer *renderer,
153 PangoAttrShape *attr,
154 int x,
155 int y)
156{
157 CtkFillLayoutRenderer *text_renderer = CTK_FILL_LAYOUT_RENDERER (renderer)((((CtkFillLayoutRenderer*) (void *) g_type_check_instance_cast
((GTypeInstance*) ((renderer)), ((_ctk_fill_layout_renderer_get_type
()))))))
;
158 cairo_t *cr = text_renderer->cr;
159 PangoLayout *layout;
160 PangoCairoShapeRendererFunc shape_renderer;
161 gpointer shape_renderer_data;
162
163 layout = pango_renderer_get_layout (renderer);
164
165 if (!layout)
166 return;
167
168 shape_renderer = pango_cairo_context_get_shape_renderer (pango_layout_get_context (layout),
169 &shape_renderer_data);
170
171 if (!shape_renderer)
172 return;
173
174 cairo_save (cr);
175
176 cairo_move_to (cr, (double)x / PANGO_SCALE1024, (double)y / PANGO_SCALE1024);
177
178 shape_renderer (cr, attr, FALSE(0), shape_renderer_data);
179
180 cairo_restore (cr);
181}
182
183static void
184ctk_fill_layout_renderer_finalize (GObject *object)
185{
186 G_OBJECT_CLASS (_ctk_fill_layout_renderer_parent_class)((((GObjectClass*) (void *) g_type_check_class_cast ((GTypeClass
*) ((_ctk_fill_layout_renderer_parent_class)), (((GType) ((20
) << (2))))))))
->finalize (object);
187}
188
189static void
190_ctk_fill_layout_renderer_init (CtkFillLayoutRenderer *renderer G_GNUC_UNUSED__attribute__ ((__unused__)))
191{
192}
193
194static void
195_ctk_fill_layout_renderer_class_init (CtkFillLayoutRendererClass *klass)
196{
197 GObjectClass *object_class = G_OBJECT_CLASS (klass)((((GObjectClass*) (void *) g_type_check_class_cast ((GTypeClass
*) ((klass)), (((GType) ((20) << (2))))))))
;
198
199 PangoRendererClass *renderer_class = PANGO_RENDERER_CLASS (klass)((((PangoRendererClass*) (void *) g_type_check_class_cast ((GTypeClass
*) ((klass)), ((pango_renderer_get_type()))))))
;
200
201 renderer_class->draw_glyphs = ctk_fill_layout_renderer_draw_glyphs;
202 renderer_class->draw_glyph_item = ctk_fill_layout_renderer_draw_glyph_item;
203 renderer_class->draw_rectangle = ctk_fill_layout_renderer_draw_rectangle;
204 renderer_class->draw_trapezoid = ctk_fill_layout_renderer_draw_trapezoid;
205 renderer_class->draw_error_underline = ctk_fill_layout_renderer_draw_error_underline;
206 renderer_class->draw_shape = ctk_fill_layout_renderer_draw_shape;
207
208 object_class->finalize = ctk_fill_layout_renderer_finalize;
209}
210
211void
212_ctk_pango_fill_layout (cairo_t *cr,
213 PangoLayout *layout)
214{
215 static CtkFillLayoutRenderer *renderer = NULL((void*)0);
216 gboolean has_current_point;
217 double current_x, current_y;
218
219 has_current_point = cairo_has_current_point (cr);
220 cairo_get_current_point (cr, &current_x, &current_y);
221
222 if (renderer == NULL((void*)0))
223 renderer = g_object_new (CTK_TYPE_FILL_LAYOUT_RENDERER(_ctk_fill_layout_renderer_get_type()), NULL((void*)0));
224
225 cairo_save (cr);
226 cairo_translate (cr, current_x, current_y);
227
228 renderer->cr = cr;
229 pango_renderer_draw_layout (PANGO_RENDERER (renderer)((((PangoRenderer*) (void *) g_type_check_instance_cast ((GTypeInstance
*) ((renderer)), ((pango_renderer_get_type()))))))
, layout, 0, 0);
230
231 cairo_restore (cr);
232
233 if (has_current_point)
234 cairo_move_to (cr, current_x, current_y);
235}
236
237static AtkAttributeSet *
238add_attribute (AtkAttributeSet *attributes,
239 AtkTextAttribute attr,
240 const gchar *value)
241{
242 AtkAttribute *at;
243
244 at = g_new (AtkAttribute, 1)((AtkAttribute *) g_malloc_n ((1), sizeof (AtkAttribute)));
245 at->name = g_strdup (atk_text_attribute_get_name (attr))g_strdup_inline (atk_text_attribute_get_name (attr));
246 at->value = g_strdup (value)g_strdup_inline (value);
247
248 return g_slist_prepend (attributes, at);
249}
250
251/*
252 * _ctk_pango_get_default_attributes:
253 * @attributes: a #AtkAttributeSet to add the attributes to
254 * @layout: the #PangoLayout from which to get attributes
255 *
256 * Adds the default text attributes from @layout to @attributes,
257 * after translating them from Pango attributes to ATK attributes.
258 *
259 * This is a convenience function that can be used to implement
260 * support for the #AtkText interface in widgets using Pango
261 * layouts.
262 *
263 * Returns: the modified @attributes
264 */
265AtkAttributeSet*
266_ctk_pango_get_default_attributes (AtkAttributeSet *attributes,
267 PangoLayout *layout)
268{
269 PangoContext *context;
270 gint i;
271 PangoWrapMode mode;
272
273 context = pango_layout_get_context (layout);
274 if (context)
275 {
276 PangoLanguage *language;
277 PangoFontDescription *font;
278
279 language = pango_context_get_language (context);
280 if (language)
281 attributes = add_attribute (attributes, ATK_TEXT_ATTR_LANGUAGE,
282 pango_language_to_string (language)((const char *)language));
283
284 font = pango_context_get_font_description (context);
285 if (font)
286 {
287 gchar buf[60];
288 attributes = add_attribute (attributes, ATK_TEXT_ATTR_STYLE,
289 atk_text_attribute_get_value (ATK_TEXT_ATTR_STYLE,
290 pango_font_description_get_style (font)));
291 attributes = add_attribute (attributes, ATK_TEXT_ATTR_VARIANT,
292 atk_text_attribute_get_value (ATK_TEXT_ATTR_VARIANT,
293 pango_font_description_get_variant (font)));
294 attributes = add_attribute (attributes, ATK_TEXT_ATTR_STRETCH,
295 atk_text_attribute_get_value (ATK_TEXT_ATTR_STRETCH,
296 pango_font_description_get_stretch (font)));
297 attributes = add_attribute (attributes, ATK_TEXT_ATTR_FAMILY_NAME,
298 pango_font_description_get_family (font));
299 g_snprintf (buf, 60, "%d", pango_font_description_get_weight (font));
300 attributes = add_attribute (attributes, ATK_TEXT_ATTR_WEIGHT, buf);
301 g_snprintf (buf, 60, "%i", pango_font_description_get_size (font) / PANGO_SCALE1024);
302 attributes = add_attribute (attributes, ATK_TEXT_ATTR_SIZE, buf);
303 }
304 }
305 if (pango_layout_get_justify (layout))
306 {
307 i = 3;
308 }
309 else
310 {
311 PangoAlignment align;
312
313 align = pango_layout_get_alignment (layout);
314 if (align == PANGO_ALIGN_LEFT)
315 i = 0;
316 else if (align == PANGO_ALIGN_CENTER)
317 i = 2;
318 else /* PANGO_ALIGN_RIGHT */
319 i = 1;
320 }
321 attributes = add_attribute (attributes, ATK_TEXT_ATTR_JUSTIFICATION,
322 atk_text_attribute_get_value (ATK_TEXT_ATTR_JUSTIFICATION, i));
323 mode = pango_layout_get_wrap (layout);
324 if (mode == PANGO_WRAP_WORD)
325 i = 2;
326 else /* PANGO_WRAP_CHAR */
327 i = 1;
328 attributes = add_attribute (attributes, ATK_TEXT_ATTR_WRAP_MODE,
329 atk_text_attribute_get_value (ATK_TEXT_ATTR_WRAP_MODE, i));
330
331 attributes = add_attribute (attributes, ATK_TEXT_ATTR_STRIKETHROUGH,
332 atk_text_attribute_get_value (ATK_TEXT_ATTR_STRIKETHROUGH, 0));
333 attributes = add_attribute (attributes, ATK_TEXT_ATTR_UNDERLINE,
334 atk_text_attribute_get_value (ATK_TEXT_ATTR_UNDERLINE, 0));
335 attributes = add_attribute (attributes, ATK_TEXT_ATTR_RISE, "0");
336 attributes = add_attribute (attributes, ATK_TEXT_ATTR_SCALE, "1");
337 attributes = add_attribute (attributes, ATK_TEXT_ATTR_BG_FULL_HEIGHT, "0");
338 attributes = add_attribute (attributes, ATK_TEXT_ATTR_PIXELS_INSIDE_WRAP, "0");
339 attributes = add_attribute (attributes, ATK_TEXT_ATTR_PIXELS_BELOW_LINES, "0");
340 attributes = add_attribute (attributes, ATK_TEXT_ATTR_PIXELS_ABOVE_LINES, "0");
341 attributes = add_attribute (attributes, ATK_TEXT_ATTR_EDITABLE,
342 atk_text_attribute_get_value (ATK_TEXT_ATTR_EDITABLE, 0));
343 attributes = add_attribute (attributes, ATK_TEXT_ATTR_INVISIBLE,
344 atk_text_attribute_get_value (ATK_TEXT_ATTR_INVISIBLE, 0));
345 attributes = add_attribute (attributes, ATK_TEXT_ATTR_INDENT, "0");
346 attributes = add_attribute (attributes, ATK_TEXT_ATTR_RIGHT_MARGIN, "0");
347 attributes = add_attribute (attributes, ATK_TEXT_ATTR_LEFT_MARGIN, "0");
348
349 return attributes;
350}
351
352/*
353 * _ctk_pango_get_run_attributes:
354 * @attributes: a #AtkAttributeSet to add attributes to
355 * @layout: the #PangoLayout to get the attributes from
356 * @offset: the offset at which the attributes are wanted
357 * @start_offset: return location for the starting offset
358 * of the current run
359 * @end_offset: return location for the ending offset of the
360 * current run
361 *
362 * Finds the “run” around index (i.e. the maximal range of characters
363 * where the set of applicable attributes remains constant) and
364 * returns the starting and ending offsets for it.
365 *
366 * The attributes for the run are added to @attributes, after
367 * translating them from Pango attributes to ATK attributes.
368 *
369 * This is a convenience function that can be used to implement
370 * support for the #AtkText interface in widgets using Pango
371 * layouts.
372 *
373 * Returns: the modified #AtkAttributeSet
374 */
375AtkAttributeSet *
376_ctk_pango_get_run_attributes (AtkAttributeSet *attributes,
377 PangoLayout *layout,
378 gint offset,
379 gint *start_offset,
380 gint *end_offset)
381{
382 PangoAttrIterator *iter;
383 PangoAttrList *attr;
384 PangoAttrString *pango_string;
385 PangoAttrInt *pango_int;
386 PangoAttrColor *pango_color;
387 PangoAttrLanguage *pango_lang;
388 PangoAttrFloat *pango_float;
389 gint index, start_index, end_index;
390 gboolean is_next;
391 glong len;
392 const gchar *text;
393 gchar *value;
394
395 text = pango_layout_get_text (layout);
396 len = g_utf8_strlen (text, -1);
397
398 /* Grab the attributes of the PangoLayout, if any */
399 attr = pango_layout_get_attributes (layout);
400
401 if (attr == NULL((void*)0))
402 {
403 *start_offset = 0;
404 *end_offset = len;
405 return attributes;
406 }
407
408 iter = pango_attr_list_get_iterator (attr);
409 /* Get invariant range offsets */
410 /* If offset out of range, set offset in range */
411 if (offset > len)
412 offset = len;
413 else if (offset < 0)
414 offset = 0;
415
416 index = g_utf8_offset_to_pointer (text, offset) - text;
417 pango_attr_iterator_range (iter, &start_index, &end_index);
418 is_next = TRUE(!(0));
419 while (is_next)
420 {
421 if (index >= start_index && index < end_index)
422 {
423 *start_offset = g_utf8_pointer_to_offset (text, text + start_index);
424 if (end_index == G_MAXINT2147483647) /* Last iterator */
425 end_index = len;
426
427 *end_offset = g_utf8_pointer_to_offset (text, text + end_index);
428 break;
429 }
430 is_next = pango_attr_iterator_next (iter);
431 pango_attr_iterator_range (iter, &start_index, &end_index);
432 }
433
434 /* Get attributes */
435 pango_string = (PangoAttrString*) pango_attr_iterator_get (iter, PANGO_ATTR_FAMILY);
436 if (pango_string != NULL((void*)0))
437 {
438 value = g_strdup_printf ("%s", pango_string->value);
439 attributes = add_attribute (attributes, ATK_TEXT_ATTR_FAMILY_NAME, value);
440 g_free (value);
441 }
442 pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_STYLE);
443 if (pango_int != NULL((void*)0))
444 {
445 attributes = add_attribute (attributes, ATK_TEXT_ATTR_STYLE,
446 atk_text_attribute_get_value (ATK_TEXT_ATTR_STYLE, pango_int->value));
447 }
448 pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_WEIGHT);
449 if (pango_int != NULL((void*)0))
450 {
451 value = g_strdup_printf ("%i", pango_int->value);
452 attributes = add_attribute (attributes, ATK_TEXT_ATTR_WEIGHT, value);
453 g_free (value);
454 }
455 pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_VARIANT);
456 if (pango_int != NULL((void*)0))
457 {
458 attributes = add_attribute (attributes, ATK_TEXT_ATTR_VARIANT,
459 atk_text_attribute_get_value (ATK_TEXT_ATTR_VARIANT, pango_int->value));
460 }
461 pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_STRETCH);
462 if (pango_int != NULL((void*)0))
463 {
464 attributes = add_attribute (attributes, ATK_TEXT_ATTR_STRETCH,
465 atk_text_attribute_get_value (ATK_TEXT_ATTR_STRETCH, pango_int->value));
466 }
467 pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_SIZE);
468 if (pango_int != NULL((void*)0))
469 {
470 value = g_strdup_printf ("%i", pango_int->value / PANGO_SCALE1024);
471 attributes = add_attribute (attributes, ATK_TEXT_ATTR_SIZE, value);
472 g_free (value);
473 }
474 pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_UNDERLINE);
475 if (pango_int != NULL((void*)0))
476 {
477 attributes = add_attribute (attributes, ATK_TEXT_ATTR_UNDERLINE,
478 atk_text_attribute_get_value (ATK_TEXT_ATTR_UNDERLINE, pango_int->value));
479 }
480 pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_STRIKETHROUGH);
481 if (pango_int != NULL((void*)0))
482 {
483 attributes = add_attribute (attributes, ATK_TEXT_ATTR_STRIKETHROUGH,
484 atk_text_attribute_get_value (ATK_TEXT_ATTR_STRIKETHROUGH, pango_int->value));
485 }
486 pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_RISE);
487 if (pango_int != NULL((void*)0))
488 {
489 value = g_strdup_printf ("%i", pango_int->value);
490 attributes = add_attribute (attributes, ATK_TEXT_ATTR_RISE, value);
491 g_free (value);
492 }
493 pango_lang = (PangoAttrLanguage*) pango_attr_iterator_get (iter, PANGO_ATTR_LANGUAGE);
494 if (pango_lang != NULL((void*)0))
495 {
496 attributes = add_attribute (attributes, ATK_TEXT_ATTR_LANGUAGE,
497 pango_language_to_string (pango_lang->value)((const char *)pango_lang->value));
498 }
499 pango_float = (PangoAttrFloat*) pango_attr_iterator_get (iter, PANGO_ATTR_SCALE);
500 if (pango_float != NULL((void*)0))
501 {
502 value = g_strdup_printf ("%g", pango_float->value);
503 attributes = add_attribute (attributes, ATK_TEXT_ATTR_SCALE, value);
504 g_free (value);
505 }
506 pango_color = (PangoAttrColor*) pango_attr_iterator_get (iter, PANGO_ATTR_FOREGROUND);
507 if (pango_color != NULL((void*)0))
508 {
509 value = g_strdup_printf ("%u,%u,%u",
510 pango_color->color.red,
511 pango_color->color.green,
512 pango_color->color.blue);
513 attributes = add_attribute (attributes, ATK_TEXT_ATTR_FG_COLOR, value);
514 g_free (value);
515 }
516 pango_color = (PangoAttrColor*) pango_attr_iterator_get (iter, PANGO_ATTR_BACKGROUND);
517 if (pango_color != NULL((void*)0))
518 {
519 value = g_strdup_printf ("%u,%u,%u",
520 pango_color->color.red,
521 pango_color->color.green,
522 pango_color->color.blue);
523 attributes = add_attribute (attributes, ATK_TEXT_ATTR_BG_COLOR, value);
524 g_free (value);
525 }
526 pango_attr_iterator_destroy (iter);
527
528 return attributes;
529}
530
531/*
532 * _ctk_pango_move_chars:
533 * @layout: a #PangoLayout
534 * @offset: a character offset in @layout
535 * @count: the number of characters to move from @offset
536 *
537 * Returns the position that is @count characters from the
538 * given @offset. @count may be positive or negative.
539 *
540 * For the purpose of this function, characters are defined
541 * by what Pango considers cursor positions.
542 *
543 * Returns: the new position
544 */
545gint
546_ctk_pango_move_chars (PangoLayout *layout,
547 gint offset,
548 gint count)
549{
550 const PangoLogAttr *attrs;
551 gint n_attrs;
552
553 attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs);
554
555 while (count > 0 && offset < n_attrs - 1)
556 {
557 do
558 offset++;
559 while (offset < n_attrs - 1 && !attrs[offset].is_cursor_position);
560
561 count--;
562 }
563 while (count < 0 && offset > 0)
564 {
565 do
566 offset--;
567 while (offset > 0 && !attrs[offset].is_cursor_position);
568
569 count++;
570 }
571
572 return offset;
573}
574
575/*
576 * _ctk_pango_move_words:
577 * @layout: a #PangoLayout
578 * @offset: a character offset in @layout
579 * @count: the number of words to move from @offset
580 *
581 * Returns the position that is @count words from the
582 * given @offset. @count may be positive or negative.
583 *
584 * If @count is positive, the returned position will
585 * be a word end, otherwise it will be a word start.
586 * See the Pango documentation for details on how
587 * word starts and ends are defined.
588 *
589 * Returns: the new position
590 */
591gint
592_ctk_pango_move_words (PangoLayout *layout,
593 gint offset,
594 gint count)
595{
596 const PangoLogAttr *attrs;
597 gint n_attrs;
598
599 attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs);
600
601 while (count > 0 && offset < n_attrs - 1)
602 {
603 do
604 offset++;
605 while (offset < n_attrs - 1 && !attrs[offset].is_word_end);
606
607 count--;
608 }
609 while (count < 0 && offset > 0)
610 {
611 do
612 offset--;
613 while (offset > 0 && !attrs[offset].is_word_start);
614
615 count++;
616 }
617
618 return offset;
619}
620
621/*
622 * _ctk_pango_move_sentences:
623 * @layout: a #PangoLayout
624 * @offset: a character offset in @layout
625 * @count: the number of sentences to move from @offset
626 *
627 * Returns the position that is @count sentences from the
628 * given @offset. @count may be positive or negative.
629 *
630 * If @count is positive, the returned position will
631 * be a sentence end, otherwise it will be a sentence start.
632 * See the Pango documentation for details on how
633 * sentence starts and ends are defined.
634 *
635 * Returns: the new position
636 */
637gint
638_ctk_pango_move_sentences (PangoLayout *layout,
639 gint offset,
640 gint count)
641{
642 const PangoLogAttr *attrs;
643 gint n_attrs;
644
645 attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs);
646
647 while (count > 0 && offset < n_attrs - 1)
648 {
649 do
650 offset++;
651 while (offset < n_attrs - 1 && !attrs[offset].is_sentence_end);
652
653 count--;
654 }
655 while (count < 0 && offset > 0)
656 {
657 do
658 offset--;
659 while (offset > 0 && !attrs[offset].is_sentence_start);
660
661 count++;
662 }
663
664 return offset;
665}
666
667/*
668 * _ctk_pango_move_lines:
669 * @layout: a #PangoLayout
670 * @offset: a character offset in @layout
671 * @count: the number of lines to move from @offset
672 *
673 * Returns the position that is @count lines from the
674 * given @offset. @count may be positive or negative.
675 *
676 * If @count is negative, the returned position will
677 * be the start of a line, else it will be the end of
678 * line.
679 *
680 * Returns: the new position
681 */
682gint
683_ctk_pango_move_lines (PangoLayout *layout,
684 gint offset,
685 gint count)
686{
687 GSList *lines, *l;
688 PangoLayoutLine *line;
689 gint num;
690 const gchar *text;
691 gint pos, line_pos;
692 gint index;
693 gint len;
694
695 text = pango_layout_get_text (layout);
696 index = g_utf8_offset_to_pointer (text, offset) - text;
697 lines = pango_layout_get_lines (layout);
698 line = NULL((void*)0);
1
Null pointer value stored to 'line'
699
700 num = 0;
701 for (l = lines; l; l = l->next)
2
Loop condition is false. Execution continues on line 709
702 {
703 line = l->data;
704 if (index < line->start_index + line->length)
705 break;
706 num++;
707 }
708
709 if (count < 0)
3
Assuming 'count' is >= 0
4
Taking false branch
710 {
711 num += count;
712 if (num < 0)
713 num = 0;
714
715 line = g_slist_nth_data (lines, num);
716
717 return g_utf8_pointer_to_offset (text, text + line->start_index);
718 }
719 else
720 {
721 line_pos = index - line->start_index;
5
Access to field 'start_index' results in a dereference of a null pointer (loaded from variable 'line')
722
723 len = g_slist_length (lines);
724 num += count;
725 if (num >= len || (count == 0 && num == len - 1))
726 return g_utf8_strlen (text, -1) - 1;
727
728 line = l->data;
729 pos = line->start_index + line_pos;
730 if (pos >= line->start_index + line->length)
731 pos = line->start_index + line->length - 1;
732
733 return g_utf8_pointer_to_offset (text, text + pos);
734 }
735}
736
737/*
738 * _ctk_pango_is_inside_word:
739 * @layout: a #PangoLayout
740 * @offset: a character offset in @layout
741 *
742 * Returns whether the given position is inside
743 * a word.
744 *
745 * Returns: %TRUE if @offset is inside a word
746 */
747gboolean
748_ctk_pango_is_inside_word (PangoLayout *layout,
749 gint offset)
750{
751 const PangoLogAttr *attrs;
752 gint n_attrs;
753
754 attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs);
755
756 while (offset >= 0 &&
757 !(attrs[offset].is_word_start || attrs[offset].is_word_end))
758 offset--;
759
760 if (offset >= 0)
761 return attrs[offset].is_word_start;
762
763 return FALSE(0);
764}
765
766/*
767 * _ctk_pango_is_inside_sentence:
768 * @layout: a #PangoLayout
769 * @offset: a character offset in @layout
770 *
771 * Returns whether the given position is inside
772 * a sentence.
773 *
774 * Returns: %TRUE if @offset is inside a sentence
775 */
776gboolean
777_ctk_pango_is_inside_sentence (PangoLayout *layout,
778 gint offset)
779{
780 const PangoLogAttr *attrs;
781 gint n_attrs;
782
783 attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs);
784
785 while (offset >= 0 &&
786 !(attrs[offset].is_sentence_start || attrs[offset].is_sentence_end))
787 offset--;
788
789 if (offset >= 0)
790 return attrs[offset].is_sentence_start;
791
792 return FALSE(0);
793}
794
795static void
796pango_layout_get_line_before (PangoLayout *layout,
797 AtkTextBoundary boundary_type,
798 gint offset,
799 gint *start_offset,
800 gint *end_offset)
801{
802 PangoLayoutIter *iter;
803 PangoLayoutLine *line, *prev_line = NULL((void*)0), *prev_prev_line = NULL((void*)0);
804 gint index, start_index, end_index;
805 const gchar *text;
806 gboolean found = FALSE(0);
807
808 text = pango_layout_get_text (layout);
809 index = g_utf8_offset_to_pointer (text, offset) - text;
810 iter = pango_layout_get_iter (layout);
811 do
812 {
813 line = pango_layout_iter_get_line (iter);
814 start_index = line->start_index;
815 end_index = start_index + line->length;
816
817 if (index >= start_index && index <= end_index)
818 {
819 /* Found line for offset */
820 if (prev_line)
821 {
822 switch (boundary_type)
823 {
824 case ATK_TEXT_BOUNDARY_LINE_START:
825 end_index = start_index;
826 start_index = prev_line->start_index;
827 break;
828 case ATK_TEXT_BOUNDARY_LINE_END:
829 if (prev_prev_line)
830 start_index = prev_prev_line->start_index + prev_prev_line->length;
831 else
832 start_index = 0;
833 end_index = prev_line->start_index + prev_line->length;
834 break;
835 default:
836 g_assert_not_reached()do { g_assertion_message_expr ("Ctk", "ctkpango.c", 836, ((const
char*) (__func__)), ((void*)0)); } while (0)
;
837 }
838 }
839 else
840 start_index = end_index = 0;
841
842 found = TRUE(!(0));
843 break;
844 }
845
846 prev_prev_line = prev_line;
847 prev_line = line;
848 }
849 while (pango_layout_iter_next_line (iter));
850
851 if (!found)
852 {
853 start_index = prev_line->start_index + prev_line->length;
854 end_index = start_index;
855 }
856 pango_layout_iter_free (iter);
857
858 *start_offset = g_utf8_pointer_to_offset (text, text + start_index);
859 *end_offset = g_utf8_pointer_to_offset (text, text + end_index);
860}
861
862static void
863pango_layout_get_line_at (PangoLayout *layout,
864 AtkTextBoundary boundary_type,
865 gint offset,
866 gint *start_offset,
867 gint *end_offset)
868{
869 PangoLayoutIter *iter;
870 PangoLayoutLine *line, *prev_line = NULL((void*)0);
871 gint index, start_index, end_index;
872 const gchar *text;
873 gboolean found = FALSE(0);
874
875 text = pango_layout_get_text (layout);
876 index = g_utf8_offset_to_pointer (text, offset) - text;
877 iter = pango_layout_get_iter (layout);
878 do
879 {
880 line = pango_layout_iter_get_line (iter);
881 start_index = line->start_index;
882 end_index = start_index + line->length;
883
884 if (index >= start_index && index <= end_index)
885 {
886 /* Found line for offset */
887 switch (boundary_type)
888 {
889 case ATK_TEXT_BOUNDARY_LINE_START:
890 if (pango_layout_iter_next_line (iter))
891 end_index = pango_layout_iter_get_line (iter)->start_index;
892 break;
893 case ATK_TEXT_BOUNDARY_LINE_END:
894 if (prev_line)
895 start_index = prev_line->start_index + prev_line->length;
896 break;
897 default:
898 g_assert_not_reached()do { g_assertion_message_expr ("Ctk", "ctkpango.c", 898, ((const
char*) (__func__)), ((void*)0)); } while (0)
;
899 }
900
901 found = TRUE(!(0));
902 break;
903 }
904
905 prev_line = line;
906 }
907 while (pango_layout_iter_next_line (iter));
908
909 if (!found)
910 {
911 start_index = prev_line->start_index + prev_line->length;
912 end_index = start_index;
913 }
914 pango_layout_iter_free (iter);
915
916 *start_offset = g_utf8_pointer_to_offset (text, text + start_index);
917 *end_offset = g_utf8_pointer_to_offset (text, text + end_index);
918}
919
920static void
921pango_layout_get_line_after (PangoLayout *layout,
922 AtkTextBoundary boundary_type,
923 gint offset,
924 gint *start_offset,
925 gint *end_offset)
926{
927 PangoLayoutIter *iter;
928 PangoLayoutLine *line, *prev_line = NULL((void*)0);
929 gint index, start_index, end_index;
930 const gchar *text;
931 gboolean found = FALSE(0);
932
933 text = pango_layout_get_text (layout);
934 index = g_utf8_offset_to_pointer (text, offset) - text;
935 iter = pango_layout_get_iter (layout);
936 do
937 {
938 line = pango_layout_iter_get_line (iter);
939 start_index = line->start_index;
940 end_index = start_index + line->length;
941
942 if (index >= start_index && index <= end_index)
943 {
944 /* Found line for offset */
945 if (pango_layout_iter_next_line (iter))
946 {
947 line = pango_layout_iter_get_line (iter);
948 switch (boundary_type)
949 {
950 case ATK_TEXT_BOUNDARY_LINE_START:
951 start_index = line->start_index;
952 if (pango_layout_iter_next_line (iter))
953 end_index = pango_layout_iter_get_line (iter)->start_index;
954 else
955 end_index = start_index + line->length;
956 break;
957 case ATK_TEXT_BOUNDARY_LINE_END:
958 start_index = end_index;
959 end_index = line->start_index + line->length;
960 break;
961 default:
962 g_assert_not_reached()do { g_assertion_message_expr ("Ctk", "ctkpango.c", 962, ((const
char*) (__func__)), ((void*)0)); } while (0)
;
963 }
964 }
965 else
966 start_index = end_index;
967
968 found = TRUE(!(0));
969 break;
970 }
971
972 prev_line = line;
973 }
974 while (pango_layout_iter_next_line (iter));
975
976 if (!found)
977 {
978 start_index = prev_line->start_index + prev_line->length;
979 end_index = start_index;
980 }
981 pango_layout_iter_free (iter);
982
983 *start_offset = g_utf8_pointer_to_offset (text, text + start_index);
984 *end_offset = g_utf8_pointer_to_offset (text, text + end_index);
985}
986
987/*
988 * _ctk_pango_get_text_before:
989 * @layout: a #PangoLayout
990 * @boundary_type: a #AtkTextBoundary
991 * @offset: a character offset in @layout
992 * @start_offset: return location for the start of the returned text
993 * @end_offset: return location for the end of the return text
994 *
995 * Gets a slice of the text from @layout before @offset.
996 *
997 * The @boundary_type determines the size of the returned slice of
998 * text. For the exact semantics of this function, see
999 * atk_text_get_text_before_offset().
1000 *
1001 * Returns: a newly allocated string containing a slice of text
1002 * from layout. Free with g_free().
1003 */
1004gchar *
1005_ctk_pango_get_text_before (PangoLayout *layout,
1006 AtkTextBoundary boundary_type,
1007 gint offset,
1008 gint *start_offset,
1009 gint *end_offset)
1010{
1011 const gchar *text;
1012 gint start, end;
1013 const PangoLogAttr *attrs;
1014 gint n_attrs;
1015
1016 text = pango_layout_get_text (layout);
1017
1018 if (text[0] == 0)
1019 {
1020 *start_offset = 0;
1021 *end_offset = 0;
1022 return g_strdup ("")g_strdup_inline ("");
1023 }
1024
1025 attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs);
1026
1027 start = offset;
1028 end = start;
1029
1030 switch (boundary_type)
1031 {
1032 case ATK_TEXT_BOUNDARY_CHAR:
1033 start = _ctk_pango_move_chars (layout, start, -1);
1034 break;
1035
1036 case ATK_TEXT_BOUNDARY_WORD_START:
1037 if (!attrs[start].is_word_start)
1038 start = _ctk_pango_move_words (layout, start, -1);
1039 end = start;
1040 start = _ctk_pango_move_words (layout, start, -1);
1041 break;
1042
1043 case ATK_TEXT_BOUNDARY_WORD_END:
1044 if (_ctk_pango_is_inside_word (layout, start) &&
1045 !attrs[start].is_word_start)
1046 start = _ctk_pango_move_words (layout, start, -1);
1047 while (!attrs[start].is_word_end && start > 0)
1048 start = _ctk_pango_move_chars (layout, start, -1);
1049 end = start;
1050 start = _ctk_pango_move_words (layout, start, -1);
1051 while (!attrs[start].is_word_end && start > 0)
1052 start = _ctk_pango_move_chars (layout, start, -1);
1053 break;
1054
1055 case ATK_TEXT_BOUNDARY_SENTENCE_START:
1056 if (!attrs[start].is_sentence_start)
1057 start = _ctk_pango_move_sentences (layout, start, -1);
1058 end = start;
1059 start = _ctk_pango_move_sentences (layout, start, -1);
1060 break;
1061
1062 case ATK_TEXT_BOUNDARY_SENTENCE_END:
1063 if (_ctk_pango_is_inside_sentence (layout, start) &&
1064 !attrs[start].is_sentence_start)
1065 start = _ctk_pango_move_sentences (layout, start, -1);
1066 while (!attrs[start].is_sentence_end && start > 0)
1067 start = _ctk_pango_move_chars (layout, start, -1);
1068 end = start;
1069 start = _ctk_pango_move_sentences (layout, start, -1);
1070 while (!attrs[start].is_sentence_end && start > 0)
1071 start = _ctk_pango_move_chars (layout, start, -1);
1072 break;
1073
1074 case ATK_TEXT_BOUNDARY_LINE_START:
1075 case ATK_TEXT_BOUNDARY_LINE_END:
1076 pango_layout_get_line_before (layout, boundary_type, offset, &start, &end);
1077 break;
1078 }
1079
1080 *start_offset = start;
1081 *end_offset = end;
1082
1083 g_assert (start <= end)do { if (start <= end) ; else g_assertion_message_expr ("Ctk"
, "ctkpango.c", 1083, ((const char*) (__func__)), "start <= end"
); } while (0)
;
1084
1085 return g_utf8_substring (text, start, end);
1086}
1087
1088/*
1089 * _ctk_pango_get_text_after:
1090 * @layout: a #PangoLayout
1091 * @boundary_type: a #AtkTextBoundary
1092 * @offset: a character offset in @layout
1093 * @start_offset: return location for the start of the returned text
1094 * @end_offset: return location for the end of the return text
1095 *
1096 * Gets a slice of the text from @layout after @offset.
1097 *
1098 * The @boundary_type determines the size of the returned slice of
1099 * text. For the exact semantics of this function, see
1100 * atk_text_get_text_after_offset().
1101 *
1102 * Returns: a newly allocated string containing a slice of text
1103 * from layout. Free with g_free().
1104 */
1105gchar *
1106_ctk_pango_get_text_after (PangoLayout *layout,
1107 AtkTextBoundary boundary_type,
1108 gint offset,
1109 gint *start_offset,
1110 gint *end_offset)
1111{
1112 const gchar *text;
1113 gint start, end;
1114 const PangoLogAttr *attrs;
1115 gint n_attrs;
1116
1117 text = pango_layout_get_text (layout);
1118
1119 if (text[0] == 0)
1120 {
1121 *start_offset = 0;
1122 *end_offset = 0;
1123 return g_strdup ("")g_strdup_inline ("");
1124 }
1125
1126 attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs);
1127
1128 start = offset;
1129 end = start;
1130
1131 switch (boundary_type)
1132 {
1133 case ATK_TEXT_BOUNDARY_CHAR:
1134 start = _ctk_pango_move_chars (layout, start, 1);
1135 end = start;
1136 end = _ctk_pango_move_chars (layout, end, 1);
1137 break;
1138
1139 case ATK_TEXT_BOUNDARY_WORD_START:
1140 if (_ctk_pango_is_inside_word (layout, end))
1141 end = _ctk_pango_move_words (layout, end, 1);
1142 while (!attrs[end].is_word_start && end < n_attrs - 1)
1143 end = _ctk_pango_move_chars (layout, end, 1);
1144 start = end;
1145 if (end < n_attrs - 1)
1146 {
1147 end = _ctk_pango_move_words (layout, end, 1);
1148 while (!attrs[end].is_word_start && end < n_attrs - 1)
1149 end = _ctk_pango_move_chars (layout, end, 1);
1150 }
1151 break;
1152
1153 case ATK_TEXT_BOUNDARY_WORD_END:
1154 end = _ctk_pango_move_words (layout, end, 1);
1155 start = end;
1156 if (end < n_attrs - 1)
1157 end = _ctk_pango_move_words (layout, end, 1);
1158 break;
1159
1160 case ATK_TEXT_BOUNDARY_SENTENCE_START:
1161 if (_ctk_pango_is_inside_sentence (layout, end))
1162 end = _ctk_pango_move_sentences (layout, end, 1);
1163 while (!attrs[end].is_sentence_start && end < n_attrs - 1)
1164 end = _ctk_pango_move_chars (layout, end, 1);
1165 start = end;
1166 if (end < n_attrs - 1)
1167 {
1168 end = _ctk_pango_move_sentences (layout, end, 1);
1169 while (!attrs[end].is_sentence_start && end < n_attrs - 1)
1170 end = _ctk_pango_move_chars (layout, end, 1);
1171 }
1172 break;
1173
1174 case ATK_TEXT_BOUNDARY_SENTENCE_END:
1175 end = _ctk_pango_move_sentences (layout, end, 1);
1176 start = end;
1177 if (end < n_attrs - 1)
1178 end = _ctk_pango_move_sentences (layout, end, 1);
1179 break;
1180
1181 case ATK_TEXT_BOUNDARY_LINE_START:
1182 case ATK_TEXT_BOUNDARY_LINE_END:
1183 pango_layout_get_line_after (layout, boundary_type, offset, &start, &end);
1184 break;
1185 }
1186
1187 *start_offset = start;
1188 *end_offset = end;
1189
1190 g_assert (start <= end)do { if (start <= end) ; else g_assertion_message_expr ("Ctk"
, "ctkpango.c", 1190, ((const char*) (__func__)), "start <= end"
); } while (0)
;
1191
1192 return g_utf8_substring (text, start, end);
1193}
1194
1195/*
1196 * _ctk_pango_get_text_at:
1197 * @layout: a #PangoLayout
1198 * @boundary_type: a #AtkTextBoundary
1199 * @offset: a character offset in @layout
1200 * @start_offset: return location for the start of the returned text
1201 * @end_offset: return location for the end of the return text
1202 *
1203 * Gets a slice of the text from @layout at @offset.
1204 *
1205 * The @boundary_type determines the size of the returned slice of
1206 * text. For the exact semantics of this function, see
1207 * atk_text_get_text_after_offset().
1208 *
1209 * Returns: a newly allocated string containing a slice of text
1210 * from layout. Free with g_free().
1211 */
1212gchar *
1213_ctk_pango_get_text_at (PangoLayout *layout,
1214 AtkTextBoundary boundary_type,
1215 gint offset,
1216 gint *start_offset,
1217 gint *end_offset)
1218{
1219 const gchar *text;
1220 gint start, end;
1221 const PangoLogAttr *attrs;
1222 gint n_attrs;
1223
1224 text = pango_layout_get_text (layout);
1225
1226 if (text[0] == 0)
1227 {
1228 *start_offset = 0;
1229 *end_offset = 0;
1230 return g_strdup ("")g_strdup_inline ("");
1231 }
1232
1233 attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs);
1234
1235 start = offset;
1236 end = start;
1237
1238 switch (boundary_type)
1239 {
1240 case ATK_TEXT_BOUNDARY_CHAR:
1241 end = _ctk_pango_move_chars (layout, end, 1);
1242 break;
1243
1244 case ATK_TEXT_BOUNDARY_WORD_START:
1245 if (!attrs[start].is_word_start)
1246 start = _ctk_pango_move_words (layout, start, -1);
1247 if (_ctk_pango_is_inside_word (layout, end))
1248 end = _ctk_pango_move_words (layout, end, 1);
1249 while (!attrs[end].is_word_start && end < n_attrs - 1)
1250 end = _ctk_pango_move_chars (layout, end, 1);
1251 break;
1252
1253 case ATK_TEXT_BOUNDARY_WORD_END:
1254 if (_ctk_pango_is_inside_word (layout, start) &&
1255 !attrs[start].is_word_start)
1256 start = _ctk_pango_move_words (layout, start, -1);
1257 while (!attrs[start].is_word_end && start > 0)
1258 start = _ctk_pango_move_chars (layout, start, -1);
1259 end = _ctk_pango_move_words (layout, end, 1);
1260 break;
1261
1262 case ATK_TEXT_BOUNDARY_SENTENCE_START:
1263 if (!attrs[start].is_sentence_start)
1264 start = _ctk_pango_move_sentences (layout, start, -1);
1265 if (_ctk_pango_is_inside_sentence (layout, end))
1266 end = _ctk_pango_move_sentences (layout, end, 1);
1267 while (!attrs[end].is_sentence_start && end < n_attrs - 1)
1268 end = _ctk_pango_move_chars (layout, end, 1);
1269 break;
1270
1271 case ATK_TEXT_BOUNDARY_SENTENCE_END:
1272 if (_ctk_pango_is_inside_sentence (layout, start) &&
1273 !attrs[start].is_sentence_start)
1274 start = _ctk_pango_move_sentences (layout, start, -1);
1275 while (!attrs[start].is_sentence_end && start > 0)
1276 start = _ctk_pango_move_chars (layout, start, -1);
1277 end = _ctk_pango_move_sentences (layout, end, 1);
1278 break;
1279
1280 case ATK_TEXT_BOUNDARY_LINE_START:
1281 case ATK_TEXT_BOUNDARY_LINE_END:
1282 pango_layout_get_line_at (layout, boundary_type, offset, &start, &end);
1283 break;
1284 }
1285
1286 *start_offset = start;
1287 *end_offset = end;
1288
1289 g_assert (start <= end)do { if (start <= end) ; else g_assertion_message_expr ("Ctk"
, "ctkpango.c", 1289, ((const char*) (__func__)), "start <= end"
); } while (0)
;
1290
1291 return g_utf8_substring (text, start, end);
1292}
1293
1294static gboolean
1295attr_list_merge_filter (PangoAttribute *attribute,
1296 gpointer list)
1297{
1298 pango_attr_list_change (list, pango_attribute_copy (attribute));
1299 return FALSE(0);
1300}
1301
1302/*
1303 * _ctk_pango_attr_list_merge:
1304 * @into: a #PangoAttrList where attributes are merged or %NULL
1305 * @from: a #PangoAttrList with the attributes to merge or %NULL
1306 *
1307 * Merges attributes from @from into @into.
1308 *
1309 * Returns: the merged list.
1310 */
1311PangoAttrList *
1312_ctk_pango_attr_list_merge (PangoAttrList *into,
1313 PangoAttrList *from)
1314{
1315 if (from)
1316 {
1317 if (into)
1318 pango_attr_list_filter (from, attr_list_merge_filter, into);
1319 else
1320 return pango_attr_list_ref (from);
1321 }
1322
1323 return into;
1324}
1325
1326PangoDirection
1327_ctk_pango_unichar_direction (gunichar ch)
1328{
1329 FriBidiCharType fribidi_ch_type;
1330
1331 G_STATIC_ASSERT (sizeof (FriBidiChar) == sizeof (gunichar))_Static_assert (sizeof (FriBidiChar) == sizeof (gunichar), "Expression evaluates to false"
)
;
1332
1333 fribidi_ch_type = fribidi_get_bidi_type (ch);
1334
1335 if (!FRIBIDI_IS_STRONG (fribidi_ch_type)((fribidi_ch_type) & 0x00000010L))
1336 return PANGO_DIRECTION_NEUTRAL;
1337 else if (FRIBIDI_IS_RTL (fribidi_ch_type)((fribidi_ch_type) & 0x00000001L))
1338 return PANGO_DIRECTION_RTL;
1339 else
1340 return PANGO_DIRECTION_LTR;
1341}
1342
1343PangoDirection
1344_ctk_pango_find_base_dir (const gchar *text,
1345 gint length)
1346{
1347 PangoDirection dir = PANGO_DIRECTION_NEUTRAL;
1348 const gchar *p;
1349
1350 g_return_val_if_fail (text != NULL || length == 0, PANGO_DIRECTION_NEUTRAL)do { if ((text != ((void*)0) || length == 0)) { } else { g_return_if_fail_warning
("Ctk", ((const char*) (__func__)), "text != NULL || length == 0"
); return (PANGO_DIRECTION_NEUTRAL); } } while (0)
;
1351
1352 p = text;
1353 while ((length < 0 || p < text + length) && *p)
1354 {
1355 gunichar wc = g_utf8_get_char (p);
1356
1357 dir = _ctk_pango_unichar_direction (wc);
1358
1359 if (dir != PANGO_DIRECTION_NEUTRAL)
1360 break;
1361
1362 p = g_utf8_next_char (p)((p) + g_utf8_skip[*(const guchar *)(p)]);
1363 }
1364
1365 return dir;
1366}
1367