merge the formfield patch from ooo-build
[ooovba.git] / vcl / unx / gtk / a11y / atktext.cxx
blob201c651254a8e0c57566de1f8c6604bb50106f3a
1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: atktext.cxx,v $
10 * $Revision: 1.10 $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
31 // MARKER(update_precomp.py): autogen include statement, do not remove
32 #include "precompiled_vcl.hxx"
34 #include "atkwrapper.hxx"
35 #include "atktextattributes.hxx"
36 #include <algorithm>
38 #include <com/sun/star/accessibility/AccessibleTextType.hpp>
39 #include <com/sun/star/accessibility/TextSegment.hpp>
40 #include <com/sun/star/accessibility/XAccessibleMultiLineText.hpp>
41 #include <com/sun/star/accessibility/XAccessibleText.hpp>
42 #include <com/sun/star/accessibility/XAccessibleTextAttributes.hpp>
43 #include <com/sun/star/accessibility/XAccessibleTextMarkup.hpp>
44 #include <com/sun/star/text/TextMarkupType.hpp>
46 // #define ENABLE_TRACING
48 #ifdef ENABLE_TRACING
49 #include <stdio.h>
50 #endif
52 using namespace ::com::sun::star;
54 static sal_Int16
55 text_type_from_boundary(AtkTextBoundary boundary_type)
57 switch(boundary_type)
59 case ATK_TEXT_BOUNDARY_CHAR:
60 return accessibility::AccessibleTextType::CHARACTER;
61 case ATK_TEXT_BOUNDARY_WORD_START:
62 case ATK_TEXT_BOUNDARY_WORD_END:
63 return accessibility::AccessibleTextType::WORD;
64 case ATK_TEXT_BOUNDARY_SENTENCE_START:
65 case ATK_TEXT_BOUNDARY_SENTENCE_END:
66 return accessibility::AccessibleTextType::SENTENCE;
67 case ATK_TEXT_BOUNDARY_LINE_START:
68 case ATK_TEXT_BOUNDARY_LINE_END:
69 return accessibility::AccessibleTextType::LINE;
70 default:
71 return -1;
75 /*****************************************************************************/
77 static gchar *
78 adjust_boundaries( accessibility::XAccessibleText* pText,
79 accessibility::TextSegment& rTextSegment,
80 AtkTextBoundary boundary_type,
81 gint * start_offset, gint * end_offset )
83 accessibility::TextSegment aTextSegment;
84 rtl::OUString aString;
85 gint start = 0, end = 0;
87 if( rTextSegment.SegmentText.getLength() > 0 )
89 switch(boundary_type)
91 case ATK_TEXT_BOUNDARY_CHAR:
92 case ATK_TEXT_BOUNDARY_LINE_START:
93 case ATK_TEXT_BOUNDARY_LINE_END:
94 case ATK_TEXT_BOUNDARY_SENTENCE_START:
95 start = rTextSegment.SegmentStart;
96 end = rTextSegment.SegmentEnd;
97 aString = rTextSegment.SegmentText;
98 break;
100 // the OOo break iterator behaves as SENTENCE_START
101 case ATK_TEXT_BOUNDARY_SENTENCE_END:
102 start = rTextSegment.SegmentStart;
103 end = rTextSegment.SegmentEnd;
105 if( start > 0 )
106 --start;
107 if( end > 0 && end < pText->getCharacterCount() - 1 )
108 --end;
110 aString = pText->getTextRange(start, end);
111 break;
113 case ATK_TEXT_BOUNDARY_WORD_START:
114 start = rTextSegment.SegmentStart;
116 // Determine the start index of the next segment
117 aTextSegment = pText->getTextBehindIndex(rTextSegment.SegmentEnd,
118 text_type_from_boundary(boundary_type));
119 if( aTextSegment.SegmentText.getLength() > 0 )
120 end = aTextSegment.SegmentStart;
121 else
122 end = pText->getCharacterCount();
124 aString = pText->getTextRange(start, end);
125 break;
127 case ATK_TEXT_BOUNDARY_WORD_END:
128 end = rTextSegment.SegmentEnd;
130 // Determine the end index of the previous segment
131 aTextSegment = pText->getTextBeforeIndex(rTextSegment.SegmentStart,
132 text_type_from_boundary(boundary_type));
133 if( aTextSegment.SegmentText.getLength() > 0 )
134 start = aTextSegment.SegmentEnd;
135 else
136 start = 0;
138 aString = pText->getTextRange(start, end);
139 break;
141 default:
142 return NULL;
146 *start_offset = start;
147 *end_offset = end;
149 #ifdef ENABLE_TRACING
150 fprintf(stderr, "adjust_boundaries( %d, %d, %d ) returns %d, %d\n",
151 rTextSegment.SegmentStart, rTextSegment.SegmentEnd, boundary_type,
152 start, end);
153 #endif
155 return OUStringToGChar(aString);
158 /*****************************************************************************/
160 static accessibility::XAccessibleText*
161 getText( AtkText *pText ) throw (uno::RuntimeException)
163 AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pText );
164 if( pWrap )
166 if( !pWrap->mpText && pWrap->mpContext )
168 uno::Any any = pWrap->mpContext->queryInterface( accessibility::XAccessibleText::static_type(NULL) );
169 pWrap->mpText = reinterpret_cast< accessibility::XAccessibleText * > (any.pReserved);
170 pWrap->mpText->acquire();
173 return pWrap->mpText;
176 return NULL;
179 /*****************************************************************************/
181 static accessibility::XAccessibleTextMarkup*
182 getTextMarkup( AtkText *pText ) throw (uno::RuntimeException)
184 AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pText );
185 if( pWrap )
187 if( !pWrap->mpTextMarkup && pWrap->mpContext )
189 uno::Any any = pWrap->mpContext->queryInterface( accessibility::XAccessibleTextMarkup::static_type(NULL) );
190 pWrap->mpTextMarkup = reinterpret_cast< accessibility::XAccessibleTextMarkup * > (any.pReserved);
191 pWrap->mpTextMarkup->acquire();
194 return pWrap->mpTextMarkup;
197 return NULL;
200 /*****************************************************************************/
202 static accessibility::XAccessibleTextAttributes*
203 getTextAttributes( AtkText *pText ) throw (uno::RuntimeException)
205 AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pText );
206 if( pWrap )
208 if( !pWrap->mpTextAttributes && pWrap->mpContext )
210 uno::Any any = pWrap->mpContext->queryInterface( accessibility::XAccessibleTextAttributes::static_type(NULL) );
211 /* Since this not a dedicated interface in Atk and thus has not
212 * been queried during wrapper initialization, we need to check
213 * the return value here.
215 if( typelib_TypeClass_INTERFACE == any.pType->eTypeClass )
217 pWrap->mpTextAttributes = reinterpret_cast< accessibility::XAccessibleTextAttributes * > (any.pReserved);
218 pWrap->mpTextAttributes->acquire();
222 return pWrap->mpTextAttributes;
225 return NULL;
228 /*****************************************************************************/
230 static accessibility::XAccessibleMultiLineText*
231 getMultiLineText( AtkText *pText ) throw (uno::RuntimeException)
233 AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pText );
234 if( pWrap )
236 if( !pWrap->mpMultiLineText && pWrap->mpContext )
238 uno::Any any = pWrap->mpContext->queryInterface( accessibility::XAccessibleMultiLineText::static_type(NULL) );
239 /* Since this not a dedicated interface in Atk and thus has not
240 * been queried during wrapper initialization, we need to check
241 * the return value here.
243 if( typelib_TypeClass_INTERFACE == any.pType->eTypeClass )
245 pWrap->mpMultiLineText = reinterpret_cast< accessibility::XAccessibleMultiLineText * > (any.pReserved);
246 pWrap->mpMultiLineText->acquire();
250 return pWrap->mpMultiLineText;
253 return NULL;
256 /*****************************************************************************/
258 extern "C" {
260 static gchar *
261 text_wrapper_get_text (AtkText *text,
262 gint start_offset,
263 gint end_offset)
265 gchar * ret = NULL;
267 g_return_val_if_fail( (end_offset == -1) || (end_offset >= start_offset), NULL );
269 /* at-spi expects the delete event to be send before the deletion happened
270 * so we save the deleted string object in the UNO event notification and
271 * fool libatk-bridge.so here ..
273 void * pData = g_object_get_data( G_OBJECT(text), "ooo::text_changed::delete" );
274 if( pData != NULL )
276 accessibility::TextSegment * pTextSegment =
277 reinterpret_cast <accessibility::TextSegment *> (pData);
279 if( pTextSegment->SegmentStart == start_offset &&
280 pTextSegment->SegmentEnd == end_offset )
282 rtl::OString aUtf8 = rtl::OUStringToOString( pTextSegment->SegmentText, RTL_TEXTENCODING_UTF8 );
283 return g_strdup( aUtf8.getStr() );
287 try {
288 accessibility::XAccessibleText* pText = getText( text );
289 if( pText )
291 rtl::OUString aText;
292 sal_Int32 n = pText->getCharacterCount();
294 if( -1 == end_offset )
295 aText = pText->getText();
296 else if( start_offset < n )
297 aText = pText->getTextRange(start_offset, end_offset);
299 ret = g_strdup( rtl::OUStringToOString(aText, RTL_TEXTENCODING_UTF8 ).getStr() );
302 catch(const uno::Exception& e) {
303 g_warning( "Exception in getText()" );
306 #ifdef ENABLE_TRACING
307 fprintf(stderr, "text_wrapper_get_text( %d,%d ) returns %s\n", start_offset, end_offset, ret ? ret : "null" );
308 #endif
309 return ret;
312 static gchar *
313 text_wrapper_get_text_after_offset (AtkText *text,
314 gint offset,
315 AtkTextBoundary boundary_type,
316 gint *start_offset,
317 gint *end_offset)
319 try {
320 accessibility::XAccessibleText* pText = getText( text );
321 if( pText )
323 accessibility::TextSegment aTextSegment = pText->getTextBehindIndex(offset, text_type_from_boundary(boundary_type));
324 return adjust_boundaries(pText, aTextSegment, boundary_type, start_offset, end_offset);
327 catch(const uno::Exception& e) {
328 g_warning( "Exception in get_text_after_offset()" );
331 return NULL;
334 static gchar *
335 text_wrapper_get_text_at_offset (AtkText *text,
336 gint offset,
337 AtkTextBoundary boundary_type,
338 gint *start_offset,
339 gint *end_offset)
341 try {
342 accessibility::XAccessibleText* pText = getText( text );
343 if( pText )
345 /* If the user presses the 'End' key, the caret will be placed behind the last character,
346 * which is the same index as the first character of the next line. In atk the magic offset
347 * '-2' is used to cover this special case.
349 if (
350 -2 == offset &&
351 (ATK_TEXT_BOUNDARY_LINE_START == boundary_type ||
352 ATK_TEXT_BOUNDARY_LINE_END == boundary_type)
355 accessibility::XAccessibleMultiLineText* pMultiLineText = getMultiLineText( text );
356 if( pMultiLineText )
358 accessibility::TextSegment aTextSegment = pMultiLineText->getTextAtLineWithCaret();
359 return adjust_boundaries(pText, aTextSegment, boundary_type, start_offset, end_offset);
363 accessibility::TextSegment aTextSegment = pText->getTextAtIndex(offset, text_type_from_boundary(boundary_type));
364 return adjust_boundaries(pText, aTextSegment, boundary_type, start_offset, end_offset);
367 catch(const uno::Exception& e) {
368 g_warning( "Exception in get_text_at_offset()" );
371 return NULL;
374 static gunichar
375 text_wrapper_get_character_at_offset (AtkText *text,
376 gint offset)
378 gint start, end;
379 gunichar uc = 0;
381 gchar * char_as_string =
382 text_wrapper_get_text_at_offset(text, offset, ATK_TEXT_BOUNDARY_CHAR,
383 &start, &end);
384 if( char_as_string )
386 uc = g_utf8_get_char( char_as_string );
387 g_free( char_as_string );
390 return uc;
393 static gchar *
394 text_wrapper_get_text_before_offset (AtkText *text,
395 gint offset,
396 AtkTextBoundary boundary_type,
397 gint *start_offset,
398 gint *end_offset)
400 try {
401 accessibility::XAccessibleText* pText = getText( text );
402 if( pText )
404 accessibility::TextSegment aTextSegment = pText->getTextBeforeIndex(offset, text_type_from_boundary(boundary_type));
405 return adjust_boundaries(pText, aTextSegment, boundary_type, start_offset, end_offset);
408 catch(const uno::Exception& e) {
409 g_warning( "Exception in text_before_offset()" );
412 return NULL;
415 static gint
416 text_wrapper_get_caret_offset (AtkText *text)
418 gint offset = -1;
420 try {
421 accessibility::XAccessibleText* pText = getText( text );
422 if( pText )
423 offset = pText->getCaretPosition();
425 catch(const uno::Exception& e) {
426 g_warning( "Exception in getCaretPosition()" );
429 #ifdef ENABLE_TRACING
430 fprintf(stderr, "get_caret_offset(%p) returns %d\n", text, offset);
431 #endif
433 return offset;
436 static gboolean
437 text_wrapper_set_caret_offset (AtkText *text,
438 gint offset)
440 try {
441 accessibility::XAccessibleText* pText = getText( text );
442 if( pText )
443 return pText->setCaretPosition( offset );
445 catch(const uno::Exception& e) {
446 g_warning( "Exception in setCaretPosition()" );
449 return FALSE;
452 static AtkAttributeSet *
453 text_wrapper_get_run_attributes( AtkText *text,
454 gint offset,
455 gint *start_offset,
456 gint *end_offset)
458 AtkAttributeSet *pSet = NULL;
460 try {
461 bool bOffsetsAreValid = false;
463 accessibility::XAccessibleText* pText = getText( text );
464 accessibility::XAccessibleTextAttributes* pTextAttributes = getTextAttributes( text );
465 if( pText && pTextAttributes )
467 uno::Sequence< beans::PropertyValue > aAttributeList =
468 pTextAttributes->getRunAttributes( offset, uno::Sequence< rtl::OUString > () );
470 pSet = attribute_set_new_from_property_values( aAttributeList, true, text );
471 // --> OD 2009-06-22 #i100938#
472 // - always provide start_offset and end_offset
473 // if( pSet )
474 // <--
476 accessibility::TextSegment aTextSegment =
477 pText->getTextAtIndex(offset, accessibility::AccessibleTextType::ATTRIBUTE_RUN);
479 *start_offset = aTextSegment.SegmentStart;
480 // --> OD 2009-06-22 #i100938#
481 // Do _not_ increment the end_offset provide by <accessibility::TextSegment> instance
482 // *end_offset = aTextSegment.SegmentEnd + 1; // FIXME: TESTME
483 *end_offset = aTextSegment.SegmentEnd;
484 // <--
485 bOffsetsAreValid = true;
489 // Special handling for missspelled
490 accessibility::XAccessibleTextMarkup* pTextMarkup = getTextMarkup( text );
491 if( pTextMarkup )
493 uno::Sequence< accessibility::TextSegment > aTextSegmentSeq =
494 pTextMarkup->getTextMarkupAtIndex( offset, com::sun::star::text::TextMarkupType::SPELLCHECK );
495 if( aTextSegmentSeq.getLength() > 0 )
497 accessibility::TextSegment aTextSegment = aTextSegmentSeq[0];
498 gint nStartOffsetMisspelled = aTextSegment.SegmentStart;
499 gint nEndOffsetMisspelled = aTextSegment.SegmentEnd;
501 // Get attribute run here if it hasn't been done before
502 if( !bOffsetsAreValid )
504 accessibility::TextSegment aAttributeTextSegment =
505 pText->getTextAtIndex(offset, accessibility::AccessibleTextType::ATTRIBUTE_RUN);
506 *start_offset = aAttributeTextSegment.SegmentStart;
507 *end_offset = aAttributeTextSegment.SegmentEnd;
510 if( nEndOffsetMisspelled <= offset )
511 *start_offset = ::std::max( *start_offset, nEndOffsetMisspelled );
512 else if( nStartOffsetMisspelled <= offset )
513 *start_offset = ::std::max( *start_offset, nStartOffsetMisspelled );
515 if( nStartOffsetMisspelled > offset )
516 *end_offset = ::std::min( *end_offset, nStartOffsetMisspelled );
517 else if( nEndOffsetMisspelled > offset )
518 *end_offset = ::std::min( *end_offset, nEndOffsetMisspelled );
520 if( nStartOffsetMisspelled <= offset && nEndOffsetMisspelled > offset )
521 pSet = attribute_set_prepend_misspelled( pSet );
525 catch(const uno::Exception& e){
527 g_warning( "Exception in get_run_attributes()" );
529 if( pSet )
531 atk_attribute_set_free( pSet );
532 pSet = NULL;
536 return pSet;
539 /*****************************************************************************/
541 static AtkAttributeSet *
542 text_wrapper_get_default_attributes( AtkText *text )
544 AtkAttributeSet *pSet = NULL;
546 try {
547 accessibility::XAccessibleTextAttributes* pTextAttributes = getTextAttributes( text );
548 if( pTextAttributes )
550 uno::Sequence< beans::PropertyValue > aAttributeList =
551 pTextAttributes->getDefaultAttributes( uno::Sequence< rtl::OUString > () );
553 pSet = attribute_set_new_from_property_values( aAttributeList, false, text );
556 catch(const uno::Exception& e) {
558 g_warning( "Exception in get_default_attributes()" );
560 if( pSet )
562 atk_attribute_set_free( pSet );
563 pSet = NULL;
567 return pSet;
570 /*****************************************************************************/
572 static void
573 text_wrapper_get_character_extents( AtkText *text,
574 gint offset,
575 gint *x,
576 gint *y,
577 gint *width,
578 gint *height,
579 AtkCoordType coords )
581 try {
582 accessibility::XAccessibleText* pText = getText( text );
583 if( pText )
585 *x = *y = *width = *height = 0;
586 awt::Rectangle aRect = pText->getCharacterBounds( offset );
588 gint origin_x = 0;
589 gint origin_y = 0;
591 if( coords == ATK_XY_SCREEN )
593 g_return_if_fail( ATK_IS_COMPONENT( text ) );
594 atk_component_get_position( ATK_COMPONENT( text ), &origin_x, &origin_y, coords);
597 *x = aRect.X + origin_x;
598 *y = aRect.Y + origin_y;
599 *width = aRect.Width;
600 *height = aRect.Height;
602 #ifdef ENABLE_TRACING
603 fprintf(stderr, "get_character_extents(%d, %d) returns: %d,%d,%d,%d ",
604 offset, coords, *x, *y, *width, *height);
605 #endif
608 catch(const uno::Exception& e) {
609 g_warning( "Exception in getCharacterBounds" );
613 static gint
614 text_wrapper_get_character_count (AtkText *text)
616 gint rv = 0;
618 try {
619 accessibility::XAccessibleText* pText = getText( text );
620 if( pText )
621 rv = pText->getCharacterCount();
623 catch(const uno::Exception& e) {
624 g_warning( "Exception in getCharacterCount" );
627 #ifdef ENABLE_TRACING
628 fprintf(stderr, "get_character_count(%p) returns: %d\n", text, rv);
629 #endif
631 return rv;
634 static gint
635 text_wrapper_get_offset_at_point (AtkText *text,
636 gint x,
637 gint y,
638 AtkCoordType coords)
640 try {
641 accessibility::XAccessibleText* pText = getText( text );
642 if( pText )
644 gint origin_x = 0;
645 gint origin_y = 0;
647 if( coords == ATK_XY_SCREEN )
649 g_return_val_if_fail( ATK_IS_COMPONENT( text ), -1 );
650 atk_component_get_position( ATK_COMPONENT( text ), &origin_x, &origin_y, coords);
653 return pText->getIndexAtPoint( awt::Point(x - origin_x, y - origin_y) );
656 catch(const uno::Exception& e) {
657 g_warning( "Exception in getIndexAtPoint" );
660 return -1;
663 // FIXME: the whole series of selections API is problematic ...
665 static gint
666 text_wrapper_get_n_selections (AtkText *text)
668 gint rv = 0;
670 try {
671 accessibility::XAccessibleText* pText = getText( text );
672 if( pText )
673 rv = ( pText->getSelectionEnd() > pText->getSelectionStart() ) ? 1 : 0;
675 catch(const uno::Exception& e) {
676 g_warning( "Exception in getSelectionEnd() or getSelectionStart()" );
679 #ifdef ENABLE_TRACING
680 fprintf(stderr, "get_n_selections(%p) returns %d\n", text, rv);
681 #endif
683 return rv;
686 static gchar *
687 text_wrapper_get_selection (AtkText *text,
688 gint selection_num,
689 gint *start_offset,
690 gint *end_offset)
692 g_return_val_if_fail( selection_num == 0, FALSE );
694 try {
695 accessibility::XAccessibleText* pText = getText( text );
696 if( pText )
698 *start_offset = pText->getSelectionStart();
699 *end_offset = pText->getSelectionEnd();
701 return OUStringToGChar( pText->getSelectedText() );
704 catch(const uno::Exception& e) {
705 g_warning( "Exception in getSelectionEnd(), getSelectionStart() or getSelectedText()" );
708 return NULL;
711 static gboolean
712 text_wrapper_add_selection (AtkText *text,
713 gint start_offset,
714 gint end_offset)
716 // FIXME: can we try to be more compatible by expanding an
717 // existing adjacent selection ?
719 try {
720 accessibility::XAccessibleText* pText = getText( text );
721 if( pText )
722 return pText->setSelection( start_offset, end_offset ); // ?
724 catch(const uno::Exception& e) {
725 g_warning( "Exception in setSelection()" );
728 return FALSE;
731 static gboolean
732 text_wrapper_remove_selection (AtkText *text,
733 gint selection_num)
735 g_return_val_if_fail( selection_num == 0, FALSE );
737 try {
738 accessibility::XAccessibleText* pText = getText( text );
739 if( pText )
740 return pText->setSelection( 0, 0 ); // ?
742 catch(const uno::Exception& e) {
743 g_warning( "Exception in setSelection()" );
746 return FALSE;
749 static gboolean
750 text_wrapper_set_selection (AtkText *text,
751 gint selection_num,
752 gint start_offset,
753 gint end_offset)
755 g_return_val_if_fail( selection_num == 0, FALSE );
757 try {
758 accessibility::XAccessibleText* pText = getText( text );
759 if( pText )
760 return pText->setSelection( start_offset, end_offset );
762 catch(const uno::Exception& e) {
763 g_warning( "Exception in setSelection()" );
766 return FALSE;
769 } // extern "C"
771 void
772 textIfaceInit (AtkTextIface *iface)
774 g_return_if_fail (iface != NULL);
776 iface->get_text = text_wrapper_get_text;
777 iface->get_character_at_offset = text_wrapper_get_character_at_offset;
778 iface->get_text_before_offset = text_wrapper_get_text_before_offset;
779 iface->get_text_at_offset = text_wrapper_get_text_at_offset;
780 iface->get_text_after_offset = text_wrapper_get_text_after_offset;
781 iface->get_caret_offset = text_wrapper_get_caret_offset;
782 iface->set_caret_offset = text_wrapper_set_caret_offset;
783 iface->get_character_count = text_wrapper_get_character_count;
784 iface->get_n_selections = text_wrapper_get_n_selections;
785 iface->get_selection = text_wrapper_get_selection;
786 iface->add_selection = text_wrapper_add_selection;
787 iface->remove_selection = text_wrapper_remove_selection;
788 iface->set_selection = text_wrapper_set_selection;
789 iface->get_run_attributes = text_wrapper_get_run_attributes;
790 iface->get_default_attributes = text_wrapper_get_default_attributes;
791 iface->get_character_extents = text_wrapper_get_character_extents;
792 iface->get_offset_at_point = text_wrapper_get_offset_at_point;