1 /*************************************************************************
2  *
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * Copyright 2000, 2010 Oracle and/or its affiliates.
6  *
7  * OpenOffice.org - a multi-platform office productivity suite
8  *
9  * This file is part of OpenOffice.org.
10  *
11  * OpenOffice.org is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU Lesser General Public License version 3
13  * only, as published by the Free Software Foundation.
14  *
15  * OpenOffice.org is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU Lesser General Public License version 3 for more details
19  * (a copy is included in the LICENSE file that accompanied this code).
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * version 3 along with OpenOffice.org.  If not, see
23  * <http://www.openoffice.org/license.html>
24  * for a copy of the LGPLv3 License.
25  *
26  ************************************************************************/
27 
28 // MARKER(update_precomp.py): autogen include statement, do not remove
29 #include "precompiled_vcl.hxx"
30 
31 #include "atktextattributes.hxx"
32 
33 #include <com/sun/star/awt/FontSlant.hpp>
34 #include <com/sun/star/awt/FontStrikeout.hpp>
35 #include <com/sun/star/awt/FontUnderline.hpp>
36 
37 #include <com/sun/star/style/CaseMap.hpp>
38 #include <com/sun/star/style/LineSpacing.hpp>
39 #include <com/sun/star/style/LineSpacingMode.hpp>
40 #include <com/sun/star/style/ParagraphAdjust.hpp>
41 #include <com/sun/star/style/TabAlign.hpp>
42 #include <com/sun/star/style/TabStop.hpp>
43 
44 #include <com/sun/star/text/WritingMode2.hpp>
45 
46 #include "atkwrapper.hxx"
47 
48 #include <com/sun/star/accessibility/XAccessibleComponent.hpp>
49 
50 #include <vcl/svapp.hxx>
51 #include <vcl/outdev.hxx>
52 
53 #include <stdio.h>
54 #include <string.h>
55 
56 using namespace ::com::sun::star;
57 
58 typedef gchar* (* AtkTextAttrFunc)       ( const uno::Any& rAny );
59 typedef bool   (* TextPropertyValueFunc) ( uno::Any& rAny, const gchar * value );
60 
61 #define STRNCMP_PARAM( s )  s,sizeof( s )-1
62 
63 
64 /*****************************************************************************/
65 
66 static AtkTextAttribute atk_text_attribute_paragraph_style = ATK_TEXT_ATTR_INVALID;
67 static AtkTextAttribute atk_text_attribute_font_effect = ATK_TEXT_ATTR_INVALID;
68 static AtkTextAttribute atk_text_attribute_decoration = ATK_TEXT_ATTR_INVALID;
69 static AtkTextAttribute atk_text_attribute_line_height = ATK_TEXT_ATTR_INVALID;
70 static AtkTextAttribute atk_text_attribute_rotation = ATK_TEXT_ATTR_INVALID;
71 static AtkTextAttribute atk_text_attribute_shadow = ATK_TEXT_ATTR_INVALID;
72 static AtkTextAttribute atk_text_attribute_tab_interval = ATK_TEXT_ATTR_INVALID;
73 static AtkTextAttribute atk_text_attribute_tab_stops = ATK_TEXT_ATTR_INVALID;
74 static AtkTextAttribute atk_text_attribute_writing_mode = ATK_TEXT_ATTR_INVALID;
75 static AtkTextAttribute atk_text_attribute_vertical_align = ATK_TEXT_ATTR_INVALID;
76 static AtkTextAttribute atk_text_attribute_misspelled = ATK_TEXT_ATTR_INVALID;
77 // --> OD 2010-03-01 #i92232#
78 static AtkTextAttribute atk_text_attribute_tracked_change = ATK_TEXT_ATTR_INVALID;
79 // <--
80 // --> OD 2010-03-05 #i92233#
81 static AtkTextAttribute atk_text_attribute_mm_to_pixel_ratio = ATK_TEXT_ATTR_INVALID;
82 // <--
83 
84 /*****************************************************************************/
85 
86 /**
87   * !! IMPORTANT NOTE !! : when adding items to this list, KEEP THE LIST SORTED
88   *                        and re-arrange the enum values accordingly.
89   */
90 
91 enum ExportedAttribute
92 {
93     TEXT_ATTRIBUTE_BACKGROUND_COLOR = 0,
94     TEXT_ATTRIBUTE_CASEMAP,
95     TEXT_ATTRIBUTE_FOREGROUND_COLOR,
96     TEXT_ATTRIBUTE_CONTOURED,
97     TEXT_ATTRIBUTE_CHAR_ESCAPEMENT,
98     TEXT_ATTRIBUTE_BLINKING,
99     TEXT_ATTRIBUTE_FONT_NAME,
100     TEXT_ATTRIBUTE_HEIGHT,
101     TEXT_ATTRIBUTE_HIDDEN,
102     TEXT_ATTRIBUTE_KERNING,
103     TEXT_ATTRIBUTE_LOCALE,
104     TEXT_ATTRIBUTE_POSTURE,
105     TEXT_ATTRIBUTE_RELIEF,
106     TEXT_ATTRIBUTE_ROTATION,
107     TEXT_ATTRIBUTE_SCALE,
108     TEXT_ATTRIBUTE_SHADOWED,
109     TEXT_ATTRIBUTE_STRIKETHROUGH,
110     TEXT_ATTRIBUTE_UNDERLINE,
111     TEXT_ATTRIBUTE_WEIGHT,
112     // --> OD 2010-03-05 #i92233#
113     TEXT_ATTRIBUTE_MM_TO_PIXEL_RATIO,
114     // <--
115     TEXT_ATTRIBUTE_JUSTIFICATION,
116     TEXT_ATTRIBUTE_BOTTOM_MARGIN,
117     TEXT_ATTRIBUTE_FIRST_LINE_INDENT,
118     TEXT_ATTRIBUTE_LEFT_MARGIN,
119     TEXT_ATTRIBUTE_LINE_SPACING,
120     TEXT_ATTRIBUTE_RIGHT_MARGIN,
121     TEXT_ATTRIBUTE_STYLE_NAME,
122     TEXT_ATTRIBUTE_TAB_STOPS,
123     TEXT_ATTRIBUTE_TOP_MARGIN,
124     TEXT_ATTRIBUTE_WRITING_MODE,
125     TEXT_ATTRIBUTE_LAST
126 };
127 
128 static const char * ExportedTextAttributes[TEXT_ATTRIBUTE_LAST] =
129 {
130     "CharBackColor",        // TEXT_ATTRIBUTE_BACKGROUND_COLOR
131     "CharCaseMap",          // TEXT_ATTRIBUTE_CASEMAP
132     "CharColor",            // TEXT_ATTRIBUTE_FOREGROUND_COLOR
133     "CharContoured",        // TEXT_ATTRIBUTE_CONTOURED
134     "CharEscapement",       // TEXT_ATTRIBUTE_CHAR_ESCAPEMENT
135     "CharFlash",            // TEXT_ATTRIBUTE_BLINKING
136     "CharFontName",         // TEXT_ATTRIBUTE_FONT_NAME
137     "CharHeight",           // TEXT_ATTRIBUTE_HEIGHT
138     "CharHidden",           // TEXT_ATTRIBUTE_HIDDEN
139     "CharKerning",          // TEXT_ATTRIBUTE_KERNING
140     "CharLocale",           // TEXT_ATTRIBUTE_LOCALE
141     "CharPosture",          // TEXT_ATTRIBUTE_POSTURE
142     "CharRelief",           // TEXT_ATTRIBUTE_RELIEF
143     "CharRotation",         // TEXT_ATTRIBUTE_ROTATION
144     "CharScaleWidth",       // TEXT_ATTRIBUTE_SCALE
145     "CharShadowed",         // TEXT_ATTRIBUTE_SHADOWED
146     "CharStrikeout",        // TEXT_ATTRIBUTE_STRIKETHROUGH
147     "CharUnderline",        // TEXT_ATTRIBUTE_UNDERLINE
148     "CharWeight",           // TEXT_ATTRIBUTE_WEIGHT
149     // --> OD 2010-03-05 #i92233#
150     "MMToPixelRatio",       // TEXT_ATTRIBUTE_MM_TO_PIXEL_RATIO
151     // <--
152     "ParaAdjust",           // TEXT_ATTRIBUTE_JUSTIFICATION
153     "ParaBottomMargin",     // TEXT_ATTRIBUTE_BOTTOM_MARGIN
154     "ParaFirstLineIndent",  // TEXT_ATTRIBUTE_FIRST_LINE_INDENT
155     "ParaLeftMargin",       // TEXT_ATTRIBUTE_LEFT_MARGIN
156     "ParaLineSpacing",      // TEXT_ATTRIBUTE_LINE_SPACING
157     "ParaRightMargin",      // TEXT_ATTRIBUTE_RIGHT_MARGIN
158     "ParaStyleName",        // TEXT_ATTRIBUTE_STYLE_NAME
159     "ParaTabStops",         // TEXT_ATTRIBUTE_TAB_STOPS
160     "ParaTopMargin",        // TEXT_ATTRIBUTE_TOP_MARGIN
161     "WritingMode"           // TEXT_ATTRIBUTE_WRITING_MODE
162 };
163 
164 
165 /*****************************************************************************/
166 
167 static gchar*
168 get_value( const uno::Sequence< beans::PropertyValue >& rAttributeList,
169            sal_Int32 nIndex, AtkTextAttrFunc func )
170 {
171     if( nIndex != -1 )
172         return func(rAttributeList[nIndex].Value);
173 
174     return NULL;
175 }
176 
177 #define get_bool_value( list, index ) get_value( list, index, Bool2String )
178 #define get_short_value( list, index ) get_value( list, index, Short2String )
179 //#define get_long_value( list, index ) get_value( list, index, Long2String ) pb: not used (warning on linux)
180 #define get_height_value( list, index ) get_value( list, index, Float2String )
181 #define get_justification_value( list, index ) get_value( list, index, Adjust2Justification )
182 #define get_cmm_value( list, index ) get_value( list, index, CMM2UnitString )
183 #define get_scale_width( list, index ) get_value( list, index, Scale2String )
184 #define get_strikethrough_value( list, index ) get_value( list, index, Strikeout2String )
185 #define get_string_value( list, index ) get_value( list, index, GetString )
186 #define get_style_value( list, index ) get_value( list, index, FontSlant2Style )
187 #define get_underline_value( list, index ) get_value( list, index, Underline2String )
188 #define get_variant_value( list, index ) get_value( list, index, CaseMap2String )
189 #define get_weight_value( list, index ) get_value( list, index, Weight2String )
190 #define get_language_string( list, index ) get_value( list, index, Locale2String )
191 
192 /*
193 static gchar*
194 dump_value( const uno::Sequence< beans::PropertyValue >& rAttributeList, sal_Int32 nIndex )
195 {
196     if( nIndex != -1 )
197     {
198         rtl::OString aName = rtl::OUStringToOString(rAttributeList[nIndex].Name, RTL_TEXTENCODING_UTF8);
199 
200         if( rAttributeList[nIndex].Value.has<sal_Int16> () )
201             OSL_TRACE( "%s = %d (short value)", aName.getStr(),
202                 rAttributeList[nIndex].Value.get<sal_Int16> () );
203 
204         else if( rAttributeList[nIndex].Value.has<sal_Int8> () )
205             OSL_TRACE( "%s = %d (byte value)", aName.getStr(),
206                 rAttributeList[nIndex].Value.get<sal_Int8> () );
207 
208         else if( rAttributeList[nIndex].Value.has<sal_Bool> () )
209             OSL_TRACE( "%s = %s (bool value)", aName.getStr(),
210                 rAttributeList[nIndex].Value.get<sal_Bool> () ? "true" : "false" );
211 
212         else if( rAttributeList[nIndex].Value.has<rtl::OUString> () )
213             OSL_TRACE( "%s = %s", aName.getStr(),
214                 rtl::OUStringToOString(rAttributeList[nIndex].Value.get<rtl::OUString> (),
215                     RTL_TEXTENCODING_UTF8).getStr() );
216     }
217 
218     return NULL;
219 }
220 */
221 
222 static inline
223 double toPoint(sal_Int16 n)
224 {
225     // 100th mm -> pt
226     return (double) (n * 72) / 2540;
227 }
228 
229 
230 /*****************************************************************************/
231 
232 /*
233 static gchar*
234 NullString(const uno::Any&)
235 {
236     return NULL;
237 }
238 */
239 
240 static bool
241 InvalidValue( uno::Any&, const gchar * )
242 {
243     return false;
244 }
245 
246 /*****************************************************************************/
247 
248 static gchar*
249 Float2String(const uno::Any& rAny)
250 {
251     return g_strdup_printf( "%g", rAny.get<float>() );
252 }
253 
254 static bool
255 String2Float( uno::Any& rAny, const gchar * value )
256 {
257     float fval;
258 
259     if( 1 != sscanf( value, "%g", &fval ) )
260         return false;
261 
262     rAny = uno::makeAny( fval );
263     return true;
264 }
265 
266 /*****************************************************************************/
267 
268 /*
269 static gchar*
270 Short2String(const uno::Any& rAny)
271 {
272     return g_strdup_printf( "%d", rAny.get<sal_Int16>() );
273 }
274 
275 static bool
276 String2Short( uno::Any& rAny, const gchar * value )
277 {
278     sal_Int32 lval;
279 
280     if( 1 != sscanf( value, "%d", &lval ) )
281         return false;
282 
283     rAny = uno::makeAny( (sal_Int16) lval );
284     return true;
285 }
286 */
287 
288 /*****************************************************************************/
289 /* pb: not used (warning on linux)
290 static gchar*
291 Long2String(const uno::Any& rAny)
292 {
293     return g_strdup_printf( "%ld", rAny.get<sal_Int32>() );
294 }
295 
296 static bool
297 String2Long( uno::Any& rAny, const gchar * value )
298 {
299     sal_Int32 lval;
300 
301     if( 1 != sscanf( value, "%ld", &lval ) )
302         return false;
303 
304     rAny = uno::makeAny( lval );
305     return true;
306 }
307 */
308 /*****************************************************************************/
309 
310 static accessibility::XAccessibleComponent*
311     getComponent( AtkText *pText ) throw (uno::RuntimeException)
312 {
313     AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pText );
314     if( pWrap )
315     {
316         if( !pWrap->mpComponent && pWrap->mpContext )
317         {
318             uno::Any any = pWrap->mpContext->queryInterface( accessibility::XAccessibleComponent::static_type(NULL) );
319             pWrap->mpComponent = reinterpret_cast< accessibility::XAccessibleComponent * > (any.pReserved);
320             pWrap->mpComponent->acquire();
321         }
322 
323         return pWrap->mpComponent;
324     }
325 
326     return NULL;
327 }
328 
329 static gchar*
330 get_color_value(const uno::Sequence< beans::PropertyValue >& rAttributeList,
331                 const sal_Int32 * pIndexArray,
332                 ExportedAttribute attr,
333                 AtkText * text)
334 {
335     sal_Int32 nColor = -1; // AUTOMATIC
336     sal_Int32 nIndex = pIndexArray[attr];
337 
338     if( nIndex != -1 )
339         nColor = rAttributeList[nIndex].Value.get<sal_Int32>();
340 
341     /*
342      * Check for color value for 100% alpha white, which means
343      * "automatic". Grab the RGB value from XAccessibleComponent
344      * in this case.
345      */
346 
347     if( (nColor == -1) && text )
348     {
349         try
350         {
351             accessibility::XAccessibleComponent *pComponent = getComponent( text );
352             if( pComponent )
353             {
354                 switch( attr )
355                 {
356                     case TEXT_ATTRIBUTE_BACKGROUND_COLOR:
357                         nColor = pComponent->getBackground();
358                         break;
359                     case TEXT_ATTRIBUTE_FOREGROUND_COLOR:
360                         nColor = pComponent->getForeground();
361                         break;
362                     default:
363                         break;
364                 }
365             }
366         }
367 
368         catch(const uno::Exception& e) {
369             g_warning( "Exception in get[Fore|Back]groundColor()" );
370         }
371     }
372 
373     if( nColor != -1 )
374     {
375         sal_uInt8 blue  = nColor & 0xFF;
376         sal_uInt8 green = (nColor >> 8) & 0xFF;
377         sal_uInt8 red   = (nColor >> 16) & 0xFF;
378 
379         return g_strdup_printf( "%u,%u,%u", red, green, blue );
380     }
381 
382     return NULL;
383 }
384 
385 static bool
386 String2Color( uno::Any& rAny, const gchar * value )
387 {
388     int red, green, blue;
389 
390     if( 3 != sscanf( value, "%d,%d,%d", &red, &green, &blue ) )
391         return false;
392 
393     sal_Int32 nColor = (sal_Int32) blue | ( (sal_Int32) green << 8 ) | ( ( sal_Int32 ) red << 16 );
394     rAny = uno::makeAny( nColor );
395     return true;
396 }
397 
398 /*****************************************************************************/
399 
400 static gchar*
401 FontSlant2Style(const uno::Any& rAny)
402 {
403     const gchar * value = NULL;
404 
405     switch( rAny.get<awt::FontSlant>() )
406     {
407         case awt::FontSlant_NONE:
408             value = "normal";
409             break;
410 
411         case awt::FontSlant_OBLIQUE:
412             value = "oblique";
413             break;
414 
415         case awt::FontSlant_ITALIC:
416             value = "italic";
417             break;
418 
419         case awt::FontSlant_REVERSE_OBLIQUE:
420             value = "reverse oblique";
421             break;
422 
423         case awt::FontSlant_REVERSE_ITALIC:
424             value = "reverse italic";
425             break;
426 
427         default:
428             break;
429     }
430 
431     if( value )
432          return g_strdup( value );
433 
434     return NULL;
435 }
436 
437 static bool
438 Style2FontSlant( uno::Any& rAny, const gchar * value )
439 {
440     awt::FontSlant aFontSlant;
441 
442     if( strncmp( value, STRNCMP_PARAM( "normal" ) ) )
443         aFontSlant = awt::FontSlant_NONE;
444     else if( strncmp( value, STRNCMP_PARAM( "oblique" ) ) )
445         aFontSlant = awt::FontSlant_OBLIQUE;
446     else if( strncmp( value, STRNCMP_PARAM( "italic" ) ) )
447         aFontSlant = awt::FontSlant_ITALIC;
448     else if( strncmp( value, STRNCMP_PARAM( "reverse oblique" ) ) )
449         aFontSlant = awt::FontSlant_REVERSE_OBLIQUE;
450     else if( strncmp( value, STRNCMP_PARAM( "reverse italic" ) ) )
451         aFontSlant = awt::FontSlant_REVERSE_ITALIC;
452     else
453         return false;
454 
455     rAny = uno::makeAny( aFontSlant );
456     return true;
457 }
458 
459 /*****************************************************************************/
460 
461 static gchar*
462 Weight2String(const uno::Any& rAny)
463 {
464     return g_strdup_printf( "%g", rAny.get<float>() * 4 );
465 }
466 
467 static bool
468 String2Weight( uno::Any& rAny, const gchar * value )
469 {
470     float weight;
471 
472     if( 1 != sscanf( value, "%g", &weight ) )
473         return false;
474 
475     rAny = uno::makeAny( weight / 4 );
476     return true;
477 }
478 
479 
480 /*****************************************************************************/
481 
482 static gchar*
483 Adjust2Justification(const uno::Any& rAny)
484 {
485     const gchar * value = NULL;
486 
487     switch( rAny.get<short>() )
488     {
489         case style::ParagraphAdjust_LEFT:
490             value = "left";
491             break;
492 
493         case style::ParagraphAdjust_RIGHT:
494             value = "right";
495             break;
496 
497         case style::ParagraphAdjust_BLOCK:
498         case style::ParagraphAdjust_STRETCH:
499             value = "fill";
500             break;
501 
502         case style::ParagraphAdjust_CENTER:
503             value = "center";
504             break;
505 
506         default:
507             break;
508     }
509 
510     if( value )
511         return g_strdup( value );
512 
513     return NULL;
514 }
515 
516 static bool
517 Justification2Adjust( uno::Any& rAny, const gchar * value )
518 {
519     short nParagraphAdjust;
520 
521     if( strncmp( value, STRNCMP_PARAM( "left" ) ) )
522         nParagraphAdjust = style::ParagraphAdjust_LEFT;
523     else if( strncmp( value, STRNCMP_PARAM( "right" ) ) )
524         nParagraphAdjust = style::ParagraphAdjust_RIGHT;
525     else if( strncmp( value, STRNCMP_PARAM( "fill" ) ) )
526         nParagraphAdjust = style::ParagraphAdjust_BLOCK;
527     else if( strncmp( value, STRNCMP_PARAM( "center" ) ) )
528         nParagraphAdjust = style::ParagraphAdjust_CENTER;
529     else
530         return false;
531 
532     rAny = uno::makeAny( nParagraphAdjust );
533     return true;
534 }
535 
536 /*****************************************************************************/
537 
538 const gchar * font_strikethrough[] = {
539     "none",   // FontStrikeout::NONE
540     "single", // FontStrikeout::SINGLE
541     "double", // FontStrikeout::DOUBLE
542     NULL,     // FontStrikeout::DONTKNOW
543     "bold",   // FontStrikeout::BOLD
544     "with /", // FontStrikeout::SLASH
545     "with X"  // FontStrikeout::X
546 };
547 
548 const sal_Int16 n_strikeout_constants = sizeof(font_strikethrough) / sizeof(gchar*);
549 
550 static gchar*
551 Strikeout2String(const uno::Any& rAny)
552 {
553     sal_Int16 n = rAny.get<sal_Int16>();
554 
555     if( n >= 0 && n < n_strikeout_constants )
556         return g_strdup( font_strikethrough[n] );
557 
558     return NULL;
559 }
560 
561 static bool
562 String2Strikeout( uno::Any& rAny, const gchar * value )
563 {
564     for( sal_Int16 n=0; n < n_strikeout_constants; ++n )
565     {
566         if( ( NULL != font_strikethrough[n] ) &&
567             0 == strncmp( value, font_strikethrough[n], strlen( font_strikethrough[n] ) ) )
568         {
569             rAny = uno::makeAny( n );
570             return true;
571         }
572     }
573 
574     return false;
575 }
576 
577 /*****************************************************************************/
578 
579 static gchar*
580 Underline2String(const uno::Any& rAny)
581 {
582     const gchar * value = NULL;
583 
584     switch( rAny.get<sal_Int16>() )
585     {
586         case awt::FontUnderline::NONE:
587             value = "none";
588             break;
589 
590         case awt::FontUnderline::SINGLE:
591             value = "single";
592             break;
593 
594         case awt::FontUnderline::DOUBLE:
595             value = "double";
596             break;
597 
598         default:
599             break;
600     }
601 
602     if( value )
603         return g_strdup( value );
604 
605     return NULL;
606 }
607 
608 static bool
609 String2Underline( uno::Any& rAny, const gchar * value )
610 {
611     short nUnderline;
612 
613     if( strncmp( value, STRNCMP_PARAM( "none" ) ) )
614         nUnderline = awt::FontUnderline::NONE;
615     else if( strncmp( value, STRNCMP_PARAM( "single" ) ) )
616         nUnderline = awt::FontUnderline::SINGLE;
617     else if( strncmp( value, STRNCMP_PARAM( "double" ) ) )
618         nUnderline = awt::FontUnderline::DOUBLE;
619     else
620         return false;
621 
622     rAny = uno::makeAny( nUnderline );
623     return true;
624 }
625 
626 /*****************************************************************************/
627 
628 static gchar*
629 GetString(const uno::Any& rAny)
630 {
631     rtl::OString aFontName = rtl::OUStringToOString( rAny.get< rtl::OUString > (), RTL_TEXTENCODING_UTF8 );
632 
633     if( aFontName.getLength() )
634         return g_strdup( aFontName.getStr() );
635 
636     return NULL;
637 }
638 
639 static bool
640 SetString( uno::Any& rAny, const gchar * value )
641 {
642     rtl::OString aFontName( value );
643 
644     if( aFontName.getLength() )
645     {
646         rAny = uno::makeAny( rtl::OStringToOUString( aFontName, RTL_TEXTENCODING_UTF8 ) );
647         return true;
648     }
649 
650     return false;
651 }
652 
653 /*****************************************************************************/
654 
655 // @see http://developer.gnome.org/doc/API/2.0/atk/AtkText.html#AtkTextAttribute
656 
657 // CMM = 100th of mm
658 static gchar*
659 CMM2UnitString(const uno::Any& rAny)
660 {
661     double fValue = rAny.get<sal_Int32>();
662     fValue = fValue * 0.01;
663 
664     return g_strdup_printf( "%gmm", fValue );
665 }
666 
667 static bool
668 UnitString2CMM( uno::Any& rAny, const gchar * value )
669 {
670     float fValue = 0.0; // pb: dont use double here because of warning on linux
671 
672     if( 1 != sscanf( value, "%gmm", &fValue ) )
673         return false;
674 
675     fValue = fValue * 100;
676 
677     rAny = uno::makeAny( (sal_Int32) fValue);
678     return true;
679 }
680 
681 /*****************************************************************************/
682 
683 static const gchar * bool_values[] = { "true", "false" };
684 
685 static gchar *
686 Bool2String( const uno::Any& rAny )
687 {
688     int n = 1;
689 
690     if( rAny.get<sal_Bool>() )
691         n = 0;
692 
693     return g_strdup( bool_values[n] );
694 }
695 
696 static bool
697 String2Bool( uno::Any& rAny, const gchar * value )
698 {
699     sal_Bool bValue;
700 
701     if( strncmp( value, STRNCMP_PARAM( "true" ) ) )
702         bValue = sal_True;
703     else if( strncmp( value, STRNCMP_PARAM( "false" ) ) )
704         bValue = sal_False;
705     else
706         return false;
707 
708     rAny = uno::makeAny(bValue);
709     return true;
710 }
711 
712 /*****************************************************************************/
713 
714 static gchar*
715 Scale2String( const uno::Any& rAny )
716 {
717     return g_strdup_printf( "%g", (double) (rAny.get< sal_Int16 > ()) / 100 );
718 }
719 
720 static bool
721 String2Scale( uno::Any& rAny, const gchar * value )
722 {
723     double dval;
724 
725     if( 1 != sscanf( value, "%lg", &dval ) )
726         return false;
727 
728     rAny = uno::makeAny((sal_Int16) (dval * 100));
729     return true;
730 }
731 
732 /*****************************************************************************/
733 
734 static gchar *
735 CaseMap2String( const uno::Any& rAny )
736 {
737     const gchar * value = NULL;
738 
739     switch( rAny.get<short>() )
740     {
741         case style::CaseMap::SMALLCAPS:
742             value = "small_caps";
743             break;
744 
745         default:
746             value = "normal";
747             break;
748     }
749 
750     if( value )
751         return g_strdup( value );
752 
753     return NULL;
754 }
755 
756 static bool
757 String2CaseMap( uno::Any& rAny, const gchar * value )
758 {
759     short nCaseMap;
760 
761     if( strncmp( value, STRNCMP_PARAM( "normal" ) ) )
762         nCaseMap = style::CaseMap::NONE;
763     else if( strncmp( value, STRNCMP_PARAM( "small_caps" ) ) )
764         nCaseMap = style::CaseMap::SMALLCAPS;
765     else
766         return false;
767 
768     rAny = uno::makeAny( nCaseMap );
769     return true;
770 }
771 
772 /*****************************************************************************/
773 
774 const gchar * font_stretch[] = {
775     "ultra_condensed",
776     "extra_condensed",
777     "condensed",
778     "semi_condensed",
779     "normal",
780     "semi_expanded",
781     "expanded",
782     "extra_expanded",
783     "ultra_expanded"
784 };
785 
786 static gchar*
787 Kerning2Stretch(const uno::Any& rAny)
788 {
789     sal_Int16 n = rAny.get<sal_Int16>();
790     int i = 4;
791 
792     // No good idea for a mapping - just return the basic info
793     if( n < 0 )
794         i=2;
795     else if( n > 0 )
796         i=6;
797 
798     return g_strdup(font_stretch[i]);
799 }
800 
801 /*****************************************************************************/
802 
803 static gchar*
804 Locale2String(const uno::Any& rAny)
805 {
806     lang::Locale aLocale = rAny.get<lang::Locale> ();
807     return g_strdup_printf( "%s-%s",
808         rtl::OUStringToOString( aLocale.Language, RTL_TEXTENCODING_ASCII_US).getStr(),
809         rtl::OUStringToOString( aLocale.Country, RTL_TEXTENCODING_ASCII_US).toAsciiLowerCase().getStr() );
810 }
811 
812 static bool
813 String2Locale( uno::Any& rAny, const gchar * value )
814 {
815     bool ret = false;
816 
817     gchar ** str_array = g_strsplit_set( value, "-.@", -1 );
818     if( str_array[0] != NULL )
819     {
820         ret = true;
821 
822         lang::Locale aLocale;
823 
824         aLocale.Language = rtl::OUString::createFromAscii(str_array[0]);
825         if( str_array[1] != NULL )
826         {
827             gchar * country = g_ascii_strup(str_array[1], -1);
828             aLocale.Country = rtl::OUString::createFromAscii(country);
829             g_free(country);
830         }
831 
832         rAny = uno::makeAny(aLocale);
833     }
834 
835     g_strfreev(str_array);
836     return ret;
837 }
838 
839 /*****************************************************************************/
840 
841 // @see http://www.w3.org/TR/2002/WD-css3-fonts-20020802/#font-effect-prop
842 static const gchar * relief[] = { "none", "emboss", "engrave" };
843 static const gchar * outline  = "outline";
844 
845 static gchar *
846 get_font_effect(const uno::Sequence< beans::PropertyValue >& rAttributeList,
847                 sal_Int32 nContourIndex, sal_Int32 nReliefIndex)
848 {
849     if( nContourIndex != -1 )
850     {
851         if( rAttributeList[nContourIndex].Value.get<sal_Bool>() )
852             return g_strdup(outline);
853     }
854 
855     if( nReliefIndex != -1 )
856     {
857         sal_Int16 n = rAttributeList[nReliefIndex].Value.get<sal_Int16>();
858         if( n <  3)
859             return g_strdup(relief[n]);
860     }
861 
862     return NULL;
863 }
864 
865 /*****************************************************************************/
866 
867 // @see http://www.w3.org/TR/REC-CSS2/text.html#lining-striking-props
868 
869 
870 enum
871 {
872     DECORATION_NONE = 0,
873     DECORATION_BLINK,
874     DECORATION_UNDERLINE,
875     DECORATION_LINE_THROUGH
876 };
877 
878 
879 static const gchar * decorations[] = { "none", "blink", "underline", "line-through" };
880 
881 static gchar *
882 get_text_decoration(const uno::Sequence< beans::PropertyValue >& rAttributeList,
883                     sal_Int32 nBlinkIndex, sal_Int32 nUnderlineIndex,
884                     sal_Int16 nStrikeoutIndex)
885 {
886     gchar * value_list[4] = { NULL, NULL, NULL, NULL };
887     gint count = 0;
888 
889     // no property value found
890     if( ( nBlinkIndex == -1 ) && (nUnderlineIndex == -1 ) && (nStrikeoutIndex == -1))
891         return NULL;
892 
893     if( nBlinkIndex != -1 )
894     {
895         if( rAttributeList[nBlinkIndex].Value.get<sal_Bool>() )
896             value_list[count++] = const_cast <gchar *> (decorations[DECORATION_BLINK]);
897     }
898     if( nUnderlineIndex != -1 )
899     {
900         sal_Int16 n = rAttributeList[nUnderlineIndex].Value.get<sal_Int16> ();
901         if( n != awt::FontUnderline::NONE )
902             value_list[count++] = const_cast <gchar *> (decorations[DECORATION_UNDERLINE]);
903     }
904     if( nStrikeoutIndex != -1 )
905     {
906         sal_Int16 n = rAttributeList[nStrikeoutIndex].Value.get<sal_Int16> ();
907         if( n != awt::FontStrikeout::NONE && n != awt::FontStrikeout::DONTKNOW )
908             value_list[count++] = const_cast <gchar *> (decorations[DECORATION_LINE_THROUGH]);
909     }
910 
911     if( count == 0 )
912         value_list[count++] = const_cast <gchar *> (decorations[DECORATION_NONE]);
913 
914     return g_strjoinv(" ", value_list);
915 }
916 
917 
918 /*****************************************************************************/
919 
920 // @see http://www.w3.org/TR/REC-CSS2/text.html#propdef-text-shadow
921 
922 static const gchar * shadow_values[] = { "none", "black" };
923 
924 static gchar *
925 Bool2Shadow( const uno::Any& rAny )
926 {
927     int n = 0;
928 
929     if( rAny.get<sal_Bool>() )
930         n = 1;
931 
932     return g_strdup( shadow_values[n] );
933 }
934 
935 /*****************************************************************************/
936 
937 static gchar *
938 Short2Degree( const uno::Any& rAny )
939 {
940     float f = rAny.get<sal_Int16>() / 10;
941     return g_strdup_printf( "%g", f );
942 }
943 
944 /*****************************************************************************/
945 
946 const gchar * directions[] = { "ltr", "rtl", "rtl", "ltr", "none" };
947 
948 static gchar *
949 WritingMode2Direction( const uno::Any& rAny )
950 {
951     sal_Int16 n = rAny.get<sal_Int16>();
952 
953     if( 0 <= n && n <= text::WritingMode2::PAGE )
954         return g_strdup(directions[n]);
955 
956     return NULL;
957 }
958 
959 // @see http://www.w3.org/TR/2001/WD-css3-text-20010517/#PrimaryTextAdvanceDirection
960 
961 const gchar * writing_modes[] = { "lr-tb", "rl-tb", "tb-rl", "tb-lr", "none" };
962 static gchar *
963 WritingMode2String( const uno::Any& rAny )
964 {
965     sal_Int16 n = rAny.get<sal_Int16>();
966 
967     if( 0 <= n && n <= text::WritingMode2::PAGE )
968         return g_strdup(writing_modes[n]);
969 
970     return NULL;
971 }
972 
973 /*****************************************************************************/
974 
975 const char * baseline_values[] = { "baseline", "sub", "super" };
976 
977 // @see http://www.w3.org/TR/REC-CSS2/visudet.html#propdef-vertical-align
978 static gchar *
979 Escapement2VerticalAlign( const uno::Any& rAny )
980 {
981     sal_Int16 n = rAny.get<sal_Int16>();
982     gchar * ret = NULL;
983 
984     // Values are in %, 101% means "automatic"
985     if( n == 0 )
986         ret = g_strdup(baseline_values[0]);
987     else if( n == 101 )
988         ret = g_strdup(baseline_values[2]);
989     else if( n == -101 )
990         ret = g_strdup(baseline_values[1]);
991     else
992         ret = g_strdup_printf( "%d%%", n );
993 
994     return ret;
995 }
996 
997 /*****************************************************************************/
998 
999 // @see http://www.w3.org/TR/REC-CSS2/visudet.html#propdef-line-height
1000 static gchar *
1001 LineSpacing2LineHeight( const uno::Any& rAny )
1002 {
1003     style::LineSpacing ls;
1004     gchar * ret = NULL;
1005 
1006     if( rAny >>= ls )
1007     {
1008         if( ls.Mode == style::LineSpacingMode::PROP )
1009             ret = g_strdup_printf( "%d%%", ls.Height );
1010         else if( ls.Mode == style::LineSpacingMode::FIX )
1011             ret = g_strdup_printf( "%.3gpt", toPoint(ls.Height) );
1012     }
1013 
1014     return ret;
1015 }
1016 
1017 /*****************************************************************************/
1018 
1019 // @see http://www.w3.org/People/howcome/t/970224HTMLERB-CSS/WD-tabs-970117.html
1020 static gchar *
1021 TabStopList2String( const uno::Any& rAny, bool default_tabs )
1022 {
1023     uno::Sequence< style::TabStop > theTabStops;
1024     gchar * ret = NULL;
1025 
1026     if( rAny >>= theTabStops)
1027     {
1028         sal_Int32 indexOfTab = 0;
1029         sal_Int32 numberOfTabs = theTabStops.getLength();
1030         sal_Unicode lastFillChar = (sal_Unicode) ' ';
1031 
1032         for( ; indexOfTab < numberOfTabs; ++indexOfTab )
1033         {
1034             bool is_default_tab = (style::TabAlign_DEFAULT == theTabStops[indexOfTab].Alignment);
1035 
1036             if( is_default_tab != default_tabs )
1037                 continue;
1038 
1039             double fValue = theTabStops[indexOfTab].Position;
1040             fValue = fValue * 0.01;
1041 
1042             const gchar * tab_align = "";
1043             switch( theTabStops[indexOfTab].Alignment )
1044             {
1045                 case style::TabAlign_LEFT :
1046                     tab_align = "left ";
1047                     break;
1048                 case style::TabAlign_CENTER :
1049                     tab_align = "center ";
1050                     break;
1051                 case style::TabAlign_RIGHT :
1052                     tab_align = "right ";
1053                     break;
1054                 case style::TabAlign_DECIMAL :
1055                     tab_align = "decimal ";
1056                     break;
1057                 default:
1058                     break;
1059             }
1060 
1061             const gchar * lead_char = "";
1062 
1063             if( theTabStops[indexOfTab].FillChar != lastFillChar )
1064             {
1065                 lastFillChar = theTabStops[indexOfTab].FillChar;
1066                 switch (lastFillChar)
1067                 {
1068                     case (sal_Unicode) ' ':
1069                         lead_char = "blank ";
1070                         break;
1071 
1072                     case (sal_Unicode) '.':
1073                         lead_char = "dotted ";
1074                         break;
1075 
1076                     case (sal_Unicode) '-':
1077                         lead_char = "dashed ";
1078                         break;
1079 
1080                     case (sal_Unicode) '_':
1081                         lead_char = "lined ";
1082                         break;
1083 
1084                     default:
1085                         lead_char = "custom ";
1086                         break;
1087                 }
1088             }
1089 
1090             gchar * tab_str = g_strdup_printf( "%s%s%gmm", lead_char, tab_align, fValue );
1091 
1092             if( ret )
1093             {
1094                 gchar * old_tab_str = ret;
1095                 ret = g_strconcat(old_tab_str, " ", tab_str, NULL /* terminated */);
1096                 g_free( old_tab_str );
1097             }
1098             else
1099                 ret = tab_str;
1100         }
1101     }
1102 
1103     return ret;
1104 }
1105 
1106 static gchar *
1107 TabStops2String( const uno::Any& rAny )
1108 {
1109     return TabStopList2String(rAny, false);
1110 }
1111 
1112 static gchar *
1113 DefaultTabStops2String( const uno::Any& rAny )
1114 {
1115     return TabStopList2String(rAny, true);
1116 }
1117 
1118 /*****************************************************************************/
1119 
1120 extern "C" int
1121 attr_compare(const void *p1,const void *p2)
1122 {
1123     const rtl_uString * pustr = (const rtl_uString *) p1;
1124     const char * pc = *((const char **) p2);
1125 
1126     return rtl_ustr_ascii_compare_WithLength(pustr->buffer, pustr->length, pc);
1127 }
1128 
1129 static void
1130 find_exported_attributes( sal_Int32 *pArray,
1131     const com::sun::star::uno::Sequence< com::sun::star::beans::PropertyValue >& rAttributeList )
1132 {
1133     for( sal_Int32 i = 0; i < rAttributeList.getLength(); i++ )
1134     {
1135         const char ** pAttr = (const char **) bsearch(rAttributeList[i].Name.pData,
1136             ExportedTextAttributes, TEXT_ATTRIBUTE_LAST, sizeof(const char *),
1137             attr_compare);
1138 
1139         if( pAttr )
1140         {
1141             sal_Int32 nIndex = pAttr - ExportedTextAttributes;
1142             pArray[nIndex] = i;
1143         }
1144     }
1145 }
1146 
1147 /*****************************************************************************/
1148 
1149 static AtkAttributeSet*
1150 attribute_set_prepend( AtkAttributeSet* attribute_set,
1151                        AtkTextAttribute attribute,
1152                        gchar * value )
1153 {
1154     if( value )
1155     {
1156         AtkAttribute *at = (AtkAttribute *) g_malloc( sizeof (AtkAttribute) );
1157         at->name  = g_strdup( atk_text_attribute_get_name( attribute ) );
1158         at->value = value;
1159 
1160         return g_slist_prepend(attribute_set, at);
1161     }
1162 
1163     return attribute_set;
1164 }
1165 
1166 /*****************************************************************************/
1167 
1168 AtkAttributeSet*
1169 attribute_set_new_from_property_values(
1170     const uno::Sequence< beans::PropertyValue >& rAttributeList,
1171     bool run_attributes_only,
1172     AtkText *text)
1173 {
1174     AtkAttributeSet* attribute_set = NULL;
1175 
1176     sal_Int32 aIndexList[TEXT_ATTRIBUTE_LAST] = { -1 };
1177 
1178     // Initialize index array with -1
1179     for( sal_Int32 attr = 0; attr < TEXT_ATTRIBUTE_LAST; ++attr )
1180         aIndexList[attr] = -1;
1181 
1182     find_exported_attributes(aIndexList, rAttributeList);
1183 
1184     attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_BG_COLOR,
1185         get_color_value(rAttributeList, aIndexList, TEXT_ATTRIBUTE_BACKGROUND_COLOR, run_attributes_only ? NULL : text ) );
1186 
1187     attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_FG_COLOR,
1188         get_color_value(rAttributeList, aIndexList, TEXT_ATTRIBUTE_FOREGROUND_COLOR, run_attributes_only ? NULL : text) );
1189 
1190     attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_INVISIBLE,
1191         get_bool_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_HIDDEN]));
1192 
1193     attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_UNDERLINE,
1194         get_underline_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_UNDERLINE]));
1195 
1196     attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_STRIKETHROUGH,
1197         get_strikethrough_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_STRIKETHROUGH]));
1198 
1199     attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_SIZE,
1200         get_height_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_HEIGHT]));
1201 
1202     attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_WEIGHT,
1203         get_weight_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_WEIGHT]));
1204 
1205     attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_FAMILY_NAME,
1206         get_string_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_FONT_NAME]));
1207 
1208     attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_VARIANT,
1209         get_variant_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_CASEMAP]));
1210 
1211     attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_STYLE,
1212         get_style_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_POSTURE]));
1213 
1214     attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_SCALE,
1215         get_scale_width(rAttributeList, aIndexList[TEXT_ATTRIBUTE_SCALE]));
1216 
1217     attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_LANGUAGE,
1218         get_language_string(rAttributeList, aIndexList[TEXT_ATTRIBUTE_LOCALE]));
1219 
1220     attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_DIRECTION,
1221         get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_WRITING_MODE], WritingMode2Direction));
1222 
1223     attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_STRETCH,
1224         get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_KERNING], Kerning2Stretch));
1225 
1226     if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_font_effect )
1227         atk_text_attribute_font_effect = atk_text_attribute_register("font-effect");
1228 
1229     attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_font_effect,
1230         get_font_effect(rAttributeList, aIndexList[TEXT_ATTRIBUTE_CONTOURED], aIndexList[TEXT_ATTRIBUTE_RELIEF]));
1231 
1232     if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_decoration )
1233         atk_text_attribute_decoration = atk_text_attribute_register("text-decoration");
1234 
1235     attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_decoration,
1236         get_text_decoration(rAttributeList, aIndexList[TEXT_ATTRIBUTE_BLINKING],
1237             aIndexList[TEXT_ATTRIBUTE_UNDERLINE], aIndexList[TEXT_ATTRIBUTE_STRIKETHROUGH]));
1238 
1239     if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_rotation )
1240         atk_text_attribute_rotation = atk_text_attribute_register("text-rotation");
1241 
1242     attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_rotation,
1243         get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_ROTATION], Short2Degree));
1244 
1245     if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_shadow )
1246         atk_text_attribute_shadow = atk_text_attribute_register("text-shadow");
1247 
1248     attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_shadow,
1249         get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_SHADOWED], Bool2Shadow));
1250 
1251     if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_writing_mode )
1252         atk_text_attribute_writing_mode = atk_text_attribute_register("writing-mode");
1253 
1254     attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_writing_mode,
1255         get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_WRITING_MODE], WritingMode2String));
1256 
1257     if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_vertical_align )
1258         atk_text_attribute_vertical_align = atk_text_attribute_register("vertical-align");
1259 
1260     attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_vertical_align,
1261         get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_CHAR_ESCAPEMENT], Escapement2VerticalAlign));
1262 
1263     if( run_attributes_only )
1264         return attribute_set;
1265 
1266     attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_LEFT_MARGIN,
1267         get_cmm_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_LEFT_MARGIN]));
1268 
1269     attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_RIGHT_MARGIN,
1270         get_cmm_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_RIGHT_MARGIN]));
1271 
1272     attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_INDENT,
1273         get_cmm_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_FIRST_LINE_INDENT]));
1274 
1275     attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_PIXELS_ABOVE_LINES,
1276         get_cmm_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_TOP_MARGIN]));
1277 
1278     attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_PIXELS_BELOW_LINES,
1279         get_cmm_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_BOTTOM_MARGIN]));
1280 
1281     attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_JUSTIFICATION,
1282         get_justification_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_JUSTIFICATION]));
1283 
1284     if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_paragraph_style )
1285         atk_text_attribute_paragraph_style = atk_text_attribute_register("paragraph-style");
1286 
1287     attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_paragraph_style,
1288         get_string_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_STYLE_NAME]));
1289 
1290     if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_line_height )
1291         atk_text_attribute_line_height = atk_text_attribute_register("line-height");
1292 
1293     attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_line_height,
1294         get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_LINE_SPACING], LineSpacing2LineHeight));
1295 
1296     if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_tab_interval )
1297         atk_text_attribute_tab_interval = atk_text_attribute_register("tab-interval");
1298 
1299     attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_tab_interval,
1300         get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_TAB_STOPS], DefaultTabStops2String));
1301 
1302     if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_tab_stops )
1303         atk_text_attribute_tab_stops = atk_text_attribute_register("tab-stops");
1304 
1305     attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_tab_stops,
1306         get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_TAB_STOPS], TabStops2String));
1307 
1308     // --> OD 2010-03-05 #i92233#
1309     if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_mm_to_pixel_ratio )
1310         atk_text_attribute_mm_to_pixel_ratio = atk_text_attribute_register("mm-to-pixel-ratio");
1311 
1312     attribute_set = attribute_set_prepend( attribute_set, atk_text_attribute_mm_to_pixel_ratio,
1313         get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_MM_TO_PIXEL_RATIO], Float2String));
1314     // <--
1315 
1316     return attribute_set;
1317 }
1318 
1319 
1320 AtkAttributeSet* attribute_set_prepend_misspelled( AtkAttributeSet* attribute_set )
1321 {
1322 	if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_misspelled )
1323 		atk_text_attribute_misspelled = atk_text_attribute_register( "text-spelling" );
1324 
1325 	attribute_set = attribute_set_prepend( attribute_set, atk_text_attribute_misspelled,
1326 		g_strdup_printf( "misspelled" ) );
1327 
1328 	return attribute_set;
1329 }
1330 
1331 // --> OD 2010-03-01 #i92232#
1332 AtkAttributeSet* attribute_set_prepend_tracked_change_insertion( AtkAttributeSet* attribute_set )
1333 {
1334     if ( ATK_TEXT_ATTR_INVALID == atk_text_attribute_tracked_change )
1335     {
1336         atk_text_attribute_tracked_change = atk_text_attribute_register( "text-tracked-change" );
1337     }
1338 
1339     attribute_set = attribute_set_prepend( attribute_set,
1340                                            atk_text_attribute_tracked_change,
1341                                            g_strdup_printf( "insertion" ) );
1342 
1343     return attribute_set;
1344 }
1345 
1346 AtkAttributeSet* attribute_set_prepend_tracked_change_deletion( AtkAttributeSet* attribute_set )
1347 {
1348     if ( ATK_TEXT_ATTR_INVALID == atk_text_attribute_tracked_change )
1349     {
1350         atk_text_attribute_tracked_change = atk_text_attribute_register( "text-tracked-change" );
1351     }
1352 
1353     attribute_set = attribute_set_prepend( attribute_set,
1354                                            atk_text_attribute_tracked_change,
1355                                            g_strdup_printf( "deletion" ) );
1356 
1357     return attribute_set;
1358 }
1359 
1360 AtkAttributeSet* attribute_set_prepend_tracked_change_formatchange( AtkAttributeSet* attribute_set )
1361 {
1362     if ( ATK_TEXT_ATTR_INVALID == atk_text_attribute_tracked_change )
1363     {
1364         atk_text_attribute_tracked_change = atk_text_attribute_register( "text-tracked-change" );
1365     }
1366 
1367     attribute_set = attribute_set_prepend( attribute_set,
1368                                            atk_text_attribute_tracked_change,
1369                                            g_strdup_printf( "attribute-change" ) );
1370 
1371     return attribute_set;
1372 }
1373 // <--
1374 
1375 /*****************************************************************************/
1376 
1377 struct AtkTextAttrMapping
1378 {
1379     const char *          name;
1380     TextPropertyValueFunc toPropertyValue;
1381 };
1382 
1383 const AtkTextAttrMapping g_TextAttrMap[] =
1384 {
1385     { "", InvalidValue },                       // ATK_TEXT_ATTR_INVALID = 0
1386     { "ParaLeftMargin", UnitString2CMM },       // ATK_TEXT_ATTR_LEFT_MARGIN
1387     { "ParaRightMargin", UnitString2CMM },      // ATK_TEXT_ATTR_RIGHT_MARGIN
1388     { "ParaFirstLineIndent", UnitString2CMM },  // ATK_TEXT_ATTR_INDENT
1389     { "CharHidden", String2Bool },              // ATK_TEXT_ATTR_INVISIBLE
1390     { "", InvalidValue },                       // ATK_TEXT_ATTR_EDITABLE
1391     { "ParaTopMargin", UnitString2CMM },        // ATK_TEXT_ATTR_PIXELS_ABOVE_LINES
1392     { "ParaBottomMargin", UnitString2CMM },     // ATK_TEXT_ATTR_PIXELS_BELOW_LINES
1393     { "", InvalidValue },                       // ATK_TEXT_ATTR_PIXELS_INSIDE_WRAP
1394     { "", InvalidValue },                       // ATK_TEXT_ATTR_BG_FULL_HEIGHT
1395     { "", InvalidValue },                       // ATK_TEXT_ATTR_RISE
1396     { "CharUnderline", String2Underline },      // ATK_TEXT_ATTR_UNDERLINE
1397     { "CharStrikeout", String2Strikeout },      // ATK_TEXT_ATTR_STRIKETHROUGH
1398     { "CharHeight", String2Float },             // ATK_TEXT_ATTR_SIZE
1399     { "CharScaleWidth", String2Scale },         // ATK_TEXT_ATTR_SCALE
1400     { "CharWeight", String2Weight },            // ATK_TEXT_ATTR_WEIGHT
1401     { "CharLocale", String2Locale },            // ATK_TEXT_ATTR_LANGUAGE
1402     { "CharFontName",  SetString },             // ATK_TEXT_ATTR_FAMILY_NAME
1403     { "CharBackColor", String2Color },          // ATK_TEXT_ATTR_BG_COLOR
1404     { "CharColor", String2Color },              // ATK_TEXT_ATTR_FG_COLOR
1405     { "", InvalidValue },                       // ATK_TEXT_ATTR_BG_STIPPLE
1406     { "", InvalidValue },                       // ATK_TEXT_ATTR_FG_STIPPLE
1407     { "", InvalidValue },                       // ATK_TEXT_ATTR_WRAP_MODE
1408     { "", InvalidValue },                       // ATK_TEXT_ATTR_DIRECTION
1409     { "ParaAdjust", Justification2Adjust },     // ATK_TEXT_ATTR_JUSTIFICATION
1410     { "", InvalidValue },                       // ATK_TEXT_ATTR_STRETCH
1411     { "CharCaseMap", String2CaseMap },          // ATK_TEXT_ATTR_VARIANT
1412     { "CharPosture", Style2FontSlant }          // ATK_TEXT_ATTR_STYLE
1413 };
1414 
1415 static const sal_Int32 g_TextAttrMapSize = sizeof( g_TextAttrMap ) / sizeof( AtkTextAttrMapping );
1416 
1417 /*****************************************************************************/
1418 
1419 bool
1420 attribute_set_map_to_property_values(
1421     AtkAttributeSet* attribute_set,
1422     uno::Sequence< beans::PropertyValue >& rValueList )
1423 {
1424     // Ensure enough space ..
1425     uno::Sequence< beans::PropertyValue > aAttributeList (g_TextAttrMapSize);
1426 
1427     sal_Int32 nIndex = 0;
1428     for( GSList * item = attribute_set; item != NULL; item = g_slist_next( item ) )
1429     {
1430         AtkAttribute* attribute = (AtkAttribute *) item;
1431 
1432         AtkTextAttribute text_attr = atk_text_attribute_for_name( attribute->name );
1433         if( text_attr < g_TextAttrMapSize )
1434         {
1435             if( g_TextAttrMap[text_attr].name[0] != '\0' )
1436             {
1437                 if( ! g_TextAttrMap[text_attr].toPropertyValue( aAttributeList[nIndex].Value, attribute->value) )
1438                     return false;
1439 
1440                 aAttributeList[nIndex].Name = rtl::OUString::createFromAscii( g_TextAttrMap[text_attr].name );
1441                 aAttributeList[nIndex].State = beans::PropertyState_DIRECT_VALUE;
1442                 ++nIndex;
1443             }
1444         }
1445         else
1446         {
1447             // Unsupported text attribute
1448             return false;
1449         }
1450     }
1451 
1452     aAttributeList.realloc( nIndex );
1453     rValueList = aAttributeList;
1454     return true;
1455 }
1456 
1457