blob: 9de1f7291e95e6697a5fbe8889d198006cbee6b6 [file] [log] [blame]
/**************************************************************
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*************************************************************/
// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_vcl.hxx"
#include "atktextattributes.hxx"
#include <com/sun/star/awt/FontSlant.hpp>
#include <com/sun/star/awt/FontStrikeout.hpp>
#include <com/sun/star/awt/FontUnderline.hpp>
#include <com/sun/star/style/CaseMap.hpp>
#include <com/sun/star/style/LineSpacing.hpp>
#include <com/sun/star/style/LineSpacingMode.hpp>
#include <com/sun/star/style/ParagraphAdjust.hpp>
#include <com/sun/star/style/TabAlign.hpp>
#include <com/sun/star/style/TabStop.hpp>
#include <com/sun/star/text/WritingMode2.hpp>
#include "atkwrapper.hxx"
#include <com/sun/star/accessibility/XAccessibleComponent.hpp>
#include <vcl/svapp.hxx>
#include <vcl/outdev.hxx>
#include <stdio.h>
#include <string.h>
using namespace ::com::sun::star;
typedef gchar* (* AtkTextAttrFunc) ( const uno::Any& rAny );
typedef bool (* TextPropertyValueFunc) ( uno::Any& rAny, const gchar * value );
#define STRNCMP_PARAM( s ) s,sizeof( s )-1
/*****************************************************************************/
static AtkTextAttribute atk_text_attribute_paragraph_style = ATK_TEXT_ATTR_INVALID;
static AtkTextAttribute atk_text_attribute_font_effect = ATK_TEXT_ATTR_INVALID;
static AtkTextAttribute atk_text_attribute_decoration = ATK_TEXT_ATTR_INVALID;
static AtkTextAttribute atk_text_attribute_line_height = ATK_TEXT_ATTR_INVALID;
static AtkTextAttribute atk_text_attribute_rotation = ATK_TEXT_ATTR_INVALID;
static AtkTextAttribute atk_text_attribute_shadow = ATK_TEXT_ATTR_INVALID;
static AtkTextAttribute atk_text_attribute_tab_interval = ATK_TEXT_ATTR_INVALID;
static AtkTextAttribute atk_text_attribute_tab_stops = ATK_TEXT_ATTR_INVALID;
static AtkTextAttribute atk_text_attribute_writing_mode = ATK_TEXT_ATTR_INVALID;
static AtkTextAttribute atk_text_attribute_vertical_align = ATK_TEXT_ATTR_INVALID;
static AtkTextAttribute atk_text_attribute_misspelled = ATK_TEXT_ATTR_INVALID;
// --> OD 2010-03-01 #i92232#
static AtkTextAttribute atk_text_attribute_tracked_change = ATK_TEXT_ATTR_INVALID;
// <--
// --> OD 2010-03-05 #i92233#
static AtkTextAttribute atk_text_attribute_mm_to_pixel_ratio = ATK_TEXT_ATTR_INVALID;
// <--
/*****************************************************************************/
/**
* !! IMPORTANT NOTE !! : when adding items to this list, KEEP THE LIST SORTED
* and re-arrange the enum values accordingly.
*/
enum ExportedAttribute
{
TEXT_ATTRIBUTE_BACKGROUND_COLOR = 0,
TEXT_ATTRIBUTE_CASEMAP,
TEXT_ATTRIBUTE_FOREGROUND_COLOR,
TEXT_ATTRIBUTE_CONTOURED,
TEXT_ATTRIBUTE_CHAR_ESCAPEMENT,
TEXT_ATTRIBUTE_BLINKING,
TEXT_ATTRIBUTE_FONT_NAME,
TEXT_ATTRIBUTE_HEIGHT,
TEXT_ATTRIBUTE_HIDDEN,
TEXT_ATTRIBUTE_KERNING,
TEXT_ATTRIBUTE_LOCALE,
TEXT_ATTRIBUTE_POSTURE,
TEXT_ATTRIBUTE_RELIEF,
TEXT_ATTRIBUTE_ROTATION,
TEXT_ATTRIBUTE_SCALE,
TEXT_ATTRIBUTE_SHADOWED,
TEXT_ATTRIBUTE_STRIKETHROUGH,
TEXT_ATTRIBUTE_UNDERLINE,
TEXT_ATTRIBUTE_WEIGHT,
// --> OD 2010-03-05 #i92233#
TEXT_ATTRIBUTE_MM_TO_PIXEL_RATIO,
// <--
TEXT_ATTRIBUTE_JUSTIFICATION,
TEXT_ATTRIBUTE_BOTTOM_MARGIN,
TEXT_ATTRIBUTE_FIRST_LINE_INDENT,
TEXT_ATTRIBUTE_LEFT_MARGIN,
TEXT_ATTRIBUTE_LINE_SPACING,
TEXT_ATTRIBUTE_RIGHT_MARGIN,
TEXT_ATTRIBUTE_STYLE_NAME,
TEXT_ATTRIBUTE_TAB_STOPS,
TEXT_ATTRIBUTE_TOP_MARGIN,
TEXT_ATTRIBUTE_WRITING_MODE,
TEXT_ATTRIBUTE_LAST
};
static const char * ExportedTextAttributes[TEXT_ATTRIBUTE_LAST] =
{
"CharBackColor", // TEXT_ATTRIBUTE_BACKGROUND_COLOR
"CharCaseMap", // TEXT_ATTRIBUTE_CASEMAP
"CharColor", // TEXT_ATTRIBUTE_FOREGROUND_COLOR
"CharContoured", // TEXT_ATTRIBUTE_CONTOURED
"CharEscapement", // TEXT_ATTRIBUTE_CHAR_ESCAPEMENT
"CharFlash", // TEXT_ATTRIBUTE_BLINKING
"CharFontName", // TEXT_ATTRIBUTE_FONT_NAME
"CharHeight", // TEXT_ATTRIBUTE_HEIGHT
"CharHidden", // TEXT_ATTRIBUTE_HIDDEN
"CharKerning", // TEXT_ATTRIBUTE_KERNING
"CharLocale", // TEXT_ATTRIBUTE_LOCALE
"CharPosture", // TEXT_ATTRIBUTE_POSTURE
"CharRelief", // TEXT_ATTRIBUTE_RELIEF
"CharRotation", // TEXT_ATTRIBUTE_ROTATION
"CharScaleWidth", // TEXT_ATTRIBUTE_SCALE
"CharShadowed", // TEXT_ATTRIBUTE_SHADOWED
"CharStrikeout", // TEXT_ATTRIBUTE_STRIKETHROUGH
"CharUnderline", // TEXT_ATTRIBUTE_UNDERLINE
"CharWeight", // TEXT_ATTRIBUTE_WEIGHT
// --> OD 2010-03-05 #i92233#
"MMToPixelRatio", // TEXT_ATTRIBUTE_MM_TO_PIXEL_RATIO
// <--
"ParaAdjust", // TEXT_ATTRIBUTE_JUSTIFICATION
"ParaBottomMargin", // TEXT_ATTRIBUTE_BOTTOM_MARGIN
"ParaFirstLineIndent", // TEXT_ATTRIBUTE_FIRST_LINE_INDENT
"ParaLeftMargin", // TEXT_ATTRIBUTE_LEFT_MARGIN
"ParaLineSpacing", // TEXT_ATTRIBUTE_LINE_SPACING
"ParaRightMargin", // TEXT_ATTRIBUTE_RIGHT_MARGIN
"ParaStyleName", // TEXT_ATTRIBUTE_STYLE_NAME
"ParaTabStops", // TEXT_ATTRIBUTE_TAB_STOPS
"ParaTopMargin", // TEXT_ATTRIBUTE_TOP_MARGIN
"WritingMode" // TEXT_ATTRIBUTE_WRITING_MODE
};
/*****************************************************************************/
static gchar*
get_value( const uno::Sequence< beans::PropertyValue >& rAttributeList,
sal_Int32 nIndex, AtkTextAttrFunc func )
{
if( nIndex != -1 )
return func(rAttributeList[nIndex].Value);
return NULL;
}
#define get_bool_value( list, index ) get_value( list, index, Bool2String )
#define get_short_value( list, index ) get_value( list, index, Short2String )
//#define get_long_value( list, index ) get_value( list, index, Long2String ) pb: not used (warning on linux)
#define get_height_value( list, index ) get_value( list, index, Float2String )
#define get_justification_value( list, index ) get_value( list, index, Adjust2Justification )
#define get_cmm_value( list, index ) get_value( list, index, CMM2UnitString )
#define get_scale_width( list, index ) get_value( list, index, Scale2String )
#define get_strikethrough_value( list, index ) get_value( list, index, Strikeout2String )
#define get_string_value( list, index ) get_value( list, index, GetString )
#define get_style_value( list, index ) get_value( list, index, FontSlant2Style )
#define get_underline_value( list, index ) get_value( list, index, Underline2String )
#define get_variant_value( list, index ) get_value( list, index, CaseMap2String )
#define get_weight_value( list, index ) get_value( list, index, Weight2String )
#define get_language_string( list, index ) get_value( list, index, Locale2String )
/*
static gchar*
dump_value( const uno::Sequence< beans::PropertyValue >& rAttributeList, sal_Int32 nIndex )
{
if( nIndex != -1 )
{
rtl::OString aName = rtl::OUStringToOString(rAttributeList[nIndex].Name, RTL_TEXTENCODING_UTF8);
if( rAttributeList[nIndex].Value.has<sal_Int16> () )
OSL_TRACE( "%s = %d (short value)", aName.getStr(),
rAttributeList[nIndex].Value.get<sal_Int16> () );
else if( rAttributeList[nIndex].Value.has<sal_Int8> () )
OSL_TRACE( "%s = %d (byte value)", aName.getStr(),
rAttributeList[nIndex].Value.get<sal_Int8> () );
else if( rAttributeList[nIndex].Value.has<sal_Bool> () )
OSL_TRACE( "%s = %s (bool value)", aName.getStr(),
rAttributeList[nIndex].Value.get<sal_Bool> () ? "true" : "false" );
else if( rAttributeList[nIndex].Value.has<rtl::OUString> () )
OSL_TRACE( "%s = %s", aName.getStr(),
rtl::OUStringToOString(rAttributeList[nIndex].Value.get<rtl::OUString> (),
RTL_TEXTENCODING_UTF8).getStr() );
}
return NULL;
}
*/
static inline
double toPoint(sal_Int16 n)
{
// 100th mm -> pt
return (double) (n * 72) / 2540;
}
/*****************************************************************************/
/*
static gchar*
NullString(const uno::Any&)
{
return NULL;
}
*/
static bool
InvalidValue( uno::Any&, const gchar * )
{
return false;
}
/*****************************************************************************/
static gchar*
Float2String(const uno::Any& rAny)
{
return g_strdup_printf( "%g", rAny.get<float>() );
}
static bool
String2Float( uno::Any& rAny, const gchar * value )
{
float fval;
if( 1 != sscanf( value, "%g", &fval ) )
return false;
rAny = uno::makeAny( fval );
return true;
}
/*****************************************************************************/
/*
static gchar*
Short2String(const uno::Any& rAny)
{
return g_strdup_printf( "%d", rAny.get<sal_Int16>() );
}
static bool
String2Short( uno::Any& rAny, const gchar * value )
{
sal_Int32 lval;
if( 1 != sscanf( value, "%d", &lval ) )
return false;
rAny = uno::makeAny( (sal_Int16) lval );
return true;
}
*/
/*****************************************************************************/
/* pb: not used (warning on linux)
static gchar*
Long2String(const uno::Any& rAny)
{
return g_strdup_printf( "%ld", rAny.get<sal_Int32>() );
}
static bool
String2Long( uno::Any& rAny, const gchar * value )
{
sal_Int32 lval;
if( 1 != sscanf( value, "%ld", &lval ) )
return false;
rAny = uno::makeAny( lval );
return true;
}
*/
/*****************************************************************************/
static accessibility::XAccessibleComponent*
getComponent( AtkText *pText ) throw (uno::RuntimeException)
{
AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pText );
if( pWrap )
{
if( !pWrap->mpComponent && pWrap->mpContext )
{
uno::Any any = pWrap->mpContext->queryInterface( accessibility::XAccessibleComponent::static_type(NULL) );
pWrap->mpComponent = reinterpret_cast< accessibility::XAccessibleComponent * > (any.pReserved);
pWrap->mpComponent->acquire();
}
return pWrap->mpComponent;
}
return NULL;
}
static gchar*
get_color_value(const uno::Sequence< beans::PropertyValue >& rAttributeList,
const sal_Int32 * pIndexArray,
ExportedAttribute attr,
AtkText * text)
{
sal_Int32 nColor = -1; // AUTOMATIC
sal_Int32 nIndex = pIndexArray[attr];
if( nIndex != -1 )
nColor = rAttributeList[nIndex].Value.get<sal_Int32>();
/*
* Check for color value for 100% alpha white, which means
* "automatic". Grab the RGB value from XAccessibleComponent
* in this case.
*/
if( (nColor == -1) && text )
{
try
{
accessibility::XAccessibleComponent *pComponent = getComponent( text );
if( pComponent )
{
switch( attr )
{
case TEXT_ATTRIBUTE_BACKGROUND_COLOR:
nColor = pComponent->getBackground();
break;
case TEXT_ATTRIBUTE_FOREGROUND_COLOR:
nColor = pComponent->getForeground();
break;
default:
break;
}
}
}
catch(const uno::Exception& e) {
g_warning( "Exception in get[Fore|Back]groundColor()" );
}
}
if( nColor != -1 )
{
sal_uInt8 blue = nColor & 0xFF;
sal_uInt8 green = (nColor >> 8) & 0xFF;
sal_uInt8 red = (nColor >> 16) & 0xFF;
return g_strdup_printf( "%u,%u,%u", red, green, blue );
}
return NULL;
}
static bool
String2Color( uno::Any& rAny, const gchar * value )
{
int red, green, blue;
if( 3 != sscanf( value, "%d,%d,%d", &red, &green, &blue ) )
return false;
sal_Int32 nColor = (sal_Int32) blue | ( (sal_Int32) green << 8 ) | ( ( sal_Int32 ) red << 16 );
rAny = uno::makeAny( nColor );
return true;
}
/*****************************************************************************/
static gchar*
FontSlant2Style(const uno::Any& rAny)
{
const gchar * value = NULL;
switch( rAny.get<awt::FontSlant>() )
{
case awt::FontSlant_NONE:
value = "normal";
break;
case awt::FontSlant_OBLIQUE:
value = "oblique";
break;
case awt::FontSlant_ITALIC:
value = "italic";
break;
case awt::FontSlant_REVERSE_OBLIQUE:
value = "reverse oblique";
break;
case awt::FontSlant_REVERSE_ITALIC:
value = "reverse italic";
break;
default:
break;
}
if( value )
return g_strdup( value );
return NULL;
}
static bool
Style2FontSlant( uno::Any& rAny, const gchar * value )
{
awt::FontSlant aFontSlant;
if( strncmp( value, STRNCMP_PARAM( "normal" ) ) )
aFontSlant = awt::FontSlant_NONE;
else if( strncmp( value, STRNCMP_PARAM( "oblique" ) ) )
aFontSlant = awt::FontSlant_OBLIQUE;
else if( strncmp( value, STRNCMP_PARAM( "italic" ) ) )
aFontSlant = awt::FontSlant_ITALIC;
else if( strncmp( value, STRNCMP_PARAM( "reverse oblique" ) ) )
aFontSlant = awt::FontSlant_REVERSE_OBLIQUE;
else if( strncmp( value, STRNCMP_PARAM( "reverse italic" ) ) )
aFontSlant = awt::FontSlant_REVERSE_ITALIC;
else
return false;
rAny = uno::makeAny( aFontSlant );
return true;
}
/*****************************************************************************/
static gchar*
Weight2String(const uno::Any& rAny)
{
return g_strdup_printf( "%g", rAny.get<float>() * 4 );
}
static bool
String2Weight( uno::Any& rAny, const gchar * value )
{
float weight;
if( 1 != sscanf( value, "%g", &weight ) )
return false;
rAny = uno::makeAny( weight / 4 );
return true;
}
/*****************************************************************************/
static gchar*
Adjust2Justification(const uno::Any& rAny)
{
const gchar * value = NULL;
switch( rAny.get<short>() )
{
case style::ParagraphAdjust_LEFT:
value = "left";
break;
case style::ParagraphAdjust_RIGHT:
value = "right";
break;
case style::ParagraphAdjust_BLOCK:
case style::ParagraphAdjust_STRETCH:
value = "fill";
break;
case style::ParagraphAdjust_CENTER:
value = "center";
break;
default:
break;
}
if( value )
return g_strdup( value );
return NULL;
}
static bool
Justification2Adjust( uno::Any& rAny, const gchar * value )
{
short nParagraphAdjust;
if( strncmp( value, STRNCMP_PARAM( "left" ) ) )
nParagraphAdjust = style::ParagraphAdjust_LEFT;
else if( strncmp( value, STRNCMP_PARAM( "right" ) ) )
nParagraphAdjust = style::ParagraphAdjust_RIGHT;
else if( strncmp( value, STRNCMP_PARAM( "fill" ) ) )
nParagraphAdjust = style::ParagraphAdjust_BLOCK;
else if( strncmp( value, STRNCMP_PARAM( "center" ) ) )
nParagraphAdjust = style::ParagraphAdjust_CENTER;
else
return false;
rAny = uno::makeAny( nParagraphAdjust );
return true;
}
/*****************************************************************************/
const gchar * font_strikethrough[] = {
"none", // FontStrikeout::NONE
"single", // FontStrikeout::SINGLE
"double", // FontStrikeout::DOUBLE
NULL, // FontStrikeout::DONTKNOW
"bold", // FontStrikeout::BOLD
"with /", // FontStrikeout::SLASH
"with X" // FontStrikeout::X
};
const sal_Int16 n_strikeout_constants = sizeof(font_strikethrough) / sizeof(gchar*);
static gchar*
Strikeout2String(const uno::Any& rAny)
{
sal_Int16 n = rAny.get<sal_Int16>();
if( n >= 0 && n < n_strikeout_constants )
return g_strdup( font_strikethrough[n] );
return NULL;
}
static bool
String2Strikeout( uno::Any& rAny, const gchar * value )
{
for( sal_Int16 n=0; n < n_strikeout_constants; ++n )
{
if( ( NULL != font_strikethrough[n] ) &&
0 == strncmp( value, font_strikethrough[n], strlen( font_strikethrough[n] ) ) )
{
rAny = uno::makeAny( n );
return true;
}
}
return false;
}
/*****************************************************************************/
static gchar*
Underline2String(const uno::Any& rAny)
{
const gchar * value = NULL;
switch( rAny.get<sal_Int16>() )
{
case awt::FontUnderline::NONE:
value = "none";
break;
case awt::FontUnderline::SINGLE:
value = "single";
break;
case awt::FontUnderline::DOUBLE:
value = "double";
break;
default:
break;
}
if( value )
return g_strdup( value );
return NULL;
}
static bool
String2Underline( uno::Any& rAny, const gchar * value )
{
short nUnderline;
if( strncmp( value, STRNCMP_PARAM( "none" ) ) )
nUnderline = awt::FontUnderline::NONE;
else if( strncmp( value, STRNCMP_PARAM( "single" ) ) )
nUnderline = awt::FontUnderline::SINGLE;
else if( strncmp( value, STRNCMP_PARAM( "double" ) ) )
nUnderline = awt::FontUnderline::DOUBLE;
else
return false;
rAny = uno::makeAny( nUnderline );
return true;
}
/*****************************************************************************/
static gchar*
GetString(const uno::Any& rAny)
{
rtl::OString aFontName = rtl::OUStringToOString( rAny.get< rtl::OUString > (), RTL_TEXTENCODING_UTF8 );
if( aFontName.getLength() )
return g_strdup( aFontName.getStr() );
return NULL;
}
static bool
SetString( uno::Any& rAny, const gchar * value )
{
rtl::OString aFontName( value );
if( aFontName.getLength() )
{
rAny = uno::makeAny( rtl::OStringToOUString( aFontName, RTL_TEXTENCODING_UTF8 ) );
return true;
}
return false;
}
/*****************************************************************************/
// @see http://developer.gnome.org/doc/API/2.0/atk/AtkText.html#AtkTextAttribute
// CMM = 100th of mm
static gchar*
CMM2UnitString(const uno::Any& rAny)
{
double fValue = rAny.get<sal_Int32>();
fValue = fValue * 0.01;
return g_strdup_printf( "%gmm", fValue );
}
static bool
UnitString2CMM( uno::Any& rAny, const gchar * value )
{
float fValue = 0.0; // pb: dont use double here because of warning on linux
if( 1 != sscanf( value, "%gmm", &fValue ) )
return false;
fValue = fValue * 100;
rAny = uno::makeAny( (sal_Int32) fValue);
return true;
}
/*****************************************************************************/
static const gchar * bool_values[] = { "true", "false" };
static gchar *
Bool2String( const uno::Any& rAny )
{
int n = 1;
if( rAny.get<sal_Bool>() )
n = 0;
return g_strdup( bool_values[n] );
}
static bool
String2Bool( uno::Any& rAny, const gchar * value )
{
sal_Bool bValue;
if( strncmp( value, STRNCMP_PARAM( "true" ) ) )
bValue = sal_True;
else if( strncmp( value, STRNCMP_PARAM( "false" ) ) )
bValue = sal_False;
else
return false;
rAny = uno::makeAny(bValue);
return true;
}
/*****************************************************************************/
static gchar*
Scale2String( const uno::Any& rAny )
{
return g_strdup_printf( "%g", (double) (rAny.get< sal_Int16 > ()) / 100 );
}
static bool
String2Scale( uno::Any& rAny, const gchar * value )
{
double dval;
if( 1 != sscanf( value, "%lg", &dval ) )
return false;
rAny = uno::makeAny((sal_Int16) (dval * 100));
return true;
}
/*****************************************************************************/
static gchar *
CaseMap2String( const uno::Any& rAny )
{
const gchar * value = NULL;
switch( rAny.get<short>() )
{
case style::CaseMap::SMALLCAPS:
value = "small_caps";
break;
default:
value = "normal";
break;
}
if( value )
return g_strdup( value );
return NULL;
}
static bool
String2CaseMap( uno::Any& rAny, const gchar * value )
{
short nCaseMap;
if( strncmp( value, STRNCMP_PARAM( "normal" ) ) )
nCaseMap = style::CaseMap::NONE;
else if( strncmp( value, STRNCMP_PARAM( "small_caps" ) ) )
nCaseMap = style::CaseMap::SMALLCAPS;
else
return false;
rAny = uno::makeAny( nCaseMap );
return true;
}
/*****************************************************************************/
const gchar * font_stretch[] = {
"ultra_condensed",
"extra_condensed",
"condensed",
"semi_condensed",
"normal",
"semi_expanded",
"expanded",
"extra_expanded",
"ultra_expanded"
};
static gchar*
Kerning2Stretch(const uno::Any& rAny)
{
sal_Int16 n = rAny.get<sal_Int16>();
int i = 4;
// No good idea for a mapping - just return the basic info
if( n < 0 )
i=2;
else if( n > 0 )
i=6;
return g_strdup(font_stretch[i]);
}
/*****************************************************************************/
static gchar*
Locale2String(const uno::Any& rAny)
{
lang::Locale aLocale = rAny.get<lang::Locale> ();
return g_strdup_printf( "%s-%s",
rtl::OUStringToOString( aLocale.Language, RTL_TEXTENCODING_ASCII_US).getStr(),
rtl::OUStringToOString( aLocale.Country, RTL_TEXTENCODING_ASCII_US).toAsciiLowerCase().getStr() );
}
static bool
String2Locale( uno::Any& rAny, const gchar * value )
{
bool ret = false;
gchar ** str_array = g_strsplit_set( value, "-.@", -1 );
if( str_array[0] != NULL )
{
ret = true;
lang::Locale aLocale;
aLocale.Language = rtl::OUString::createFromAscii(str_array[0]);
if( str_array[1] != NULL )
{
gchar * country = g_ascii_strup(str_array[1], -1);
aLocale.Country = rtl::OUString::createFromAscii(country);
g_free(country);
}
rAny = uno::makeAny(aLocale);
}
g_strfreev(str_array);
return ret;
}
/*****************************************************************************/
// @see http://www.w3.org/TR/2002/WD-css3-fonts-20020802/#font-effect-prop
static const gchar * relief[] = { "none", "emboss", "engrave" };
static const gchar * outline = "outline";
static gchar *
get_font_effect(const uno::Sequence< beans::PropertyValue >& rAttributeList,
sal_Int32 nContourIndex, sal_Int32 nReliefIndex)
{
if( nContourIndex != -1 )
{
if( rAttributeList[nContourIndex].Value.get<sal_Bool>() )
return g_strdup(outline);
}
if( nReliefIndex != -1 )
{
sal_Int16 n = rAttributeList[nReliefIndex].Value.get<sal_Int16>();
if( n < 3)
return g_strdup(relief[n]);
}
return NULL;
}
/*****************************************************************************/
// @see http://www.w3.org/TR/REC-CSS2/text.html#lining-striking-props
enum
{
DECORATION_NONE = 0,
DECORATION_BLINK,
DECORATION_UNDERLINE,
DECORATION_LINE_THROUGH
};
static const gchar * decorations[] = { "none", "blink", "underline", "line-through" };
static gchar *
get_text_decoration(const uno::Sequence< beans::PropertyValue >& rAttributeList,
sal_Int32 nBlinkIndex, sal_Int32 nUnderlineIndex,
sal_Int16 nStrikeoutIndex)
{
gchar * value_list[4] = { NULL, NULL, NULL, NULL };
gint count = 0;
// no property value found
if( ( nBlinkIndex == -1 ) && (nUnderlineIndex == -1 ) && (nStrikeoutIndex == -1))
return NULL;
if( nBlinkIndex != -1 )
{
if( rAttributeList[nBlinkIndex].Value.get<sal_Bool>() )
value_list[count++] = const_cast <gchar *> (decorations[DECORATION_BLINK]);
}
if( nUnderlineIndex != -1 )
{
sal_Int16 n = rAttributeList[nUnderlineIndex].Value.get<sal_Int16> ();
if( n != awt::FontUnderline::NONE )
value_list[count++] = const_cast <gchar *> (decorations[DECORATION_UNDERLINE]);
}
if( nStrikeoutIndex != -1 )
{
sal_Int16 n = rAttributeList[nStrikeoutIndex].Value.get<sal_Int16> ();
if( n != awt::FontStrikeout::NONE && n != awt::FontStrikeout::DONTKNOW )
value_list[count++] = const_cast <gchar *> (decorations[DECORATION_LINE_THROUGH]);
}
if( count == 0 )
value_list[count++] = const_cast <gchar *> (decorations[DECORATION_NONE]);
return g_strjoinv(" ", value_list);
}
/*****************************************************************************/
// @see http://www.w3.org/TR/REC-CSS2/text.html#propdef-text-shadow
static const gchar * shadow_values[] = { "none", "black" };
static gchar *
Bool2Shadow( const uno::Any& rAny )
{
int n = 0;
if( rAny.get<sal_Bool>() )
n = 1;
return g_strdup( shadow_values[n] );
}
/*****************************************************************************/
static gchar *
Short2Degree( const uno::Any& rAny )
{
float f = rAny.get<sal_Int16>() / 10;
return g_strdup_printf( "%g", f );
}
/*****************************************************************************/
const gchar * directions[] = { "ltr", "rtl", "rtl", "ltr", "none" };
static gchar *
WritingMode2Direction( const uno::Any& rAny )
{
sal_Int16 n = rAny.get<sal_Int16>();
if( 0 <= n && n <= text::WritingMode2::PAGE )
return g_strdup(directions[n]);
return NULL;
}
// @see http://www.w3.org/TR/2001/WD-css3-text-20010517/#PrimaryTextAdvanceDirection
const gchar * writing_modes[] = { "lr-tb", "rl-tb", "tb-rl", "tb-lr", "none" };
static gchar *
WritingMode2String( const uno::Any& rAny )
{
sal_Int16 n = rAny.get<sal_Int16>();
if( 0 <= n && n <= text::WritingMode2::PAGE )
return g_strdup(writing_modes[n]);
return NULL;
}
/*****************************************************************************/
const char * baseline_values[] = { "baseline", "sub", "super" };
// @see http://www.w3.org/TR/REC-CSS2/visudet.html#propdef-vertical-align
static gchar *
Escapement2VerticalAlign( const uno::Any& rAny )
{
sal_Int16 n = rAny.get<sal_Int16>();
gchar * ret = NULL;
// Values are in %, 101% means "automatic"
if( n == 0 )
ret = g_strdup(baseline_values[0]);
else if( n == 101 )
ret = g_strdup(baseline_values[2]);
else if( n == -101 )
ret = g_strdup(baseline_values[1]);
else
ret = g_strdup_printf( "%d%%", n );
return ret;
}
/*****************************************************************************/
// @see http://www.w3.org/TR/REC-CSS2/visudet.html#propdef-line-height
static gchar *
LineSpacing2LineHeight( const uno::Any& rAny )
{
style::LineSpacing ls;
gchar * ret = NULL;
if( rAny >>= ls )
{
if( ls.Mode == style::LineSpacingMode::PROP )
ret = g_strdup_printf( "%d%%", ls.Height );
else if( ls.Mode == style::LineSpacingMode::FIX )
ret = g_strdup_printf( "%.3gpt", toPoint(ls.Height) );
}
return ret;
}
/*****************************************************************************/
// @see http://www.w3.org/People/howcome/t/970224HTMLERB-CSS/WD-tabs-970117.html
static gchar *
TabStopList2String( const uno::Any& rAny, bool default_tabs )
{
uno::Sequence< style::TabStop > theTabStops;
gchar * ret = NULL;
if( rAny >>= theTabStops)
{
sal_Int32 indexOfTab = 0;
sal_Int32 numberOfTabs = theTabStops.getLength();
sal_Unicode lastFillChar = (sal_Unicode) ' ';
for( ; indexOfTab < numberOfTabs; ++indexOfTab )
{
bool is_default_tab = (style::TabAlign_DEFAULT == theTabStops[indexOfTab].Alignment);
if( is_default_tab != default_tabs )
continue;
double fValue = theTabStops[indexOfTab].Position;
fValue = fValue * 0.01;
const gchar * tab_align = "";
switch( theTabStops[indexOfTab].Alignment )
{
case style::TabAlign_LEFT :
tab_align = "left ";
break;
case style::TabAlign_CENTER :
tab_align = "center ";
break;
case style::TabAlign_RIGHT :
tab_align = "right ";
break;
case style::TabAlign_DECIMAL :
tab_align = "decimal ";
break;
default:
break;
}
const gchar * lead_char = "";
if( theTabStops[indexOfTab].FillChar != lastFillChar )
{
lastFillChar = theTabStops[indexOfTab].FillChar;
switch (lastFillChar)
{
case (sal_Unicode) ' ':
lead_char = "blank ";
break;
case (sal_Unicode) '.':
lead_char = "dotted ";
break;
case (sal_Unicode) '-':
lead_char = "dashed ";
break;
case (sal_Unicode) '_':
lead_char = "lined ";
break;
default:
lead_char = "custom ";
break;
}
}
gchar * tab_str = g_strdup_printf( "%s%s%gmm", lead_char, tab_align, fValue );
if( ret )
{
gchar * old_tab_str = ret;
ret = g_strconcat(old_tab_str, " ", tab_str, NULL /* terminated */);
g_free( old_tab_str );
}
else
ret = tab_str;
}
}
return ret;
}
static gchar *
TabStops2String( const uno::Any& rAny )
{
return TabStopList2String(rAny, false);
}
static gchar *
DefaultTabStops2String( const uno::Any& rAny )
{
return TabStopList2String(rAny, true);
}
/*****************************************************************************/
extern "C" int
attr_compare(const void *p1,const void *p2)
{
const rtl_uString * pustr = (const rtl_uString *) p1;
const char * pc = *((const char **) p2);
return rtl_ustr_ascii_compare_WithLength(pustr->buffer, pustr->length, pc);
}
static void
find_exported_attributes( sal_Int32 *pArray,
const com::sun::star::uno::Sequence< com::sun::star::beans::PropertyValue >& rAttributeList )
{
for( sal_Int32 i = 0; i < rAttributeList.getLength(); i++ )
{
const char ** pAttr = (const char **) bsearch(rAttributeList[i].Name.pData,
ExportedTextAttributes, TEXT_ATTRIBUTE_LAST, sizeof(const char *),
attr_compare);
if( pAttr )
{
sal_Int32 nIndex = pAttr - ExportedTextAttributes;
pArray[nIndex] = i;
}
}
}
/*****************************************************************************/
static AtkAttributeSet*
attribute_set_prepend( AtkAttributeSet* attribute_set,
AtkTextAttribute attribute,
gchar * value )
{
if( value )
{
AtkAttribute *at = (AtkAttribute *) g_malloc( sizeof (AtkAttribute) );
at->name = g_strdup( atk_text_attribute_get_name( attribute ) );
at->value = value;
return g_slist_prepend(attribute_set, at);
}
return attribute_set;
}
/*****************************************************************************/
AtkAttributeSet*
attribute_set_new_from_property_values(
const uno::Sequence< beans::PropertyValue >& rAttributeList,
bool run_attributes_only,
AtkText *text)
{
AtkAttributeSet* attribute_set = NULL;
sal_Int32 aIndexList[TEXT_ATTRIBUTE_LAST] = { -1 };
// Initialize index array with -1
for( sal_Int32 attr = 0; attr < TEXT_ATTRIBUTE_LAST; ++attr )
aIndexList[attr] = -1;
find_exported_attributes(aIndexList, rAttributeList);
attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_BG_COLOR,
get_color_value(rAttributeList, aIndexList, TEXT_ATTRIBUTE_BACKGROUND_COLOR, run_attributes_only ? NULL : text ) );
attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_FG_COLOR,
get_color_value(rAttributeList, aIndexList, TEXT_ATTRIBUTE_FOREGROUND_COLOR, run_attributes_only ? NULL : text) );
attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_INVISIBLE,
get_bool_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_HIDDEN]));
attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_UNDERLINE,
get_underline_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_UNDERLINE]));
attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_STRIKETHROUGH,
get_strikethrough_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_STRIKETHROUGH]));
attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_SIZE,
get_height_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_HEIGHT]));
attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_WEIGHT,
get_weight_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_WEIGHT]));
attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_FAMILY_NAME,
get_string_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_FONT_NAME]));
attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_VARIANT,
get_variant_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_CASEMAP]));
attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_STYLE,
get_style_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_POSTURE]));
attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_SCALE,
get_scale_width(rAttributeList, aIndexList[TEXT_ATTRIBUTE_SCALE]));
attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_LANGUAGE,
get_language_string(rAttributeList, aIndexList[TEXT_ATTRIBUTE_LOCALE]));
attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_DIRECTION,
get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_WRITING_MODE], WritingMode2Direction));
attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_STRETCH,
get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_KERNING], Kerning2Stretch));
if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_font_effect )
atk_text_attribute_font_effect = atk_text_attribute_register("font-effect");
attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_font_effect,
get_font_effect(rAttributeList, aIndexList[TEXT_ATTRIBUTE_CONTOURED], aIndexList[TEXT_ATTRIBUTE_RELIEF]));
if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_decoration )
atk_text_attribute_decoration = atk_text_attribute_register("text-decoration");
attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_decoration,
get_text_decoration(rAttributeList, aIndexList[TEXT_ATTRIBUTE_BLINKING],
aIndexList[TEXT_ATTRIBUTE_UNDERLINE], aIndexList[TEXT_ATTRIBUTE_STRIKETHROUGH]));
if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_rotation )
atk_text_attribute_rotation = atk_text_attribute_register("text-rotation");
attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_rotation,
get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_ROTATION], Short2Degree));
if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_shadow )
atk_text_attribute_shadow = atk_text_attribute_register("text-shadow");
attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_shadow,
get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_SHADOWED], Bool2Shadow));
if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_writing_mode )
atk_text_attribute_writing_mode = atk_text_attribute_register("writing-mode");
attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_writing_mode,
get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_WRITING_MODE], WritingMode2String));
if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_vertical_align )
atk_text_attribute_vertical_align = atk_text_attribute_register("vertical-align");
attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_vertical_align,
get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_CHAR_ESCAPEMENT], Escapement2VerticalAlign));
if( run_attributes_only )
return attribute_set;
attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_LEFT_MARGIN,
get_cmm_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_LEFT_MARGIN]));
attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_RIGHT_MARGIN,
get_cmm_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_RIGHT_MARGIN]));
attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_INDENT,
get_cmm_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_FIRST_LINE_INDENT]));
attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_PIXELS_ABOVE_LINES,
get_cmm_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_TOP_MARGIN]));
attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_PIXELS_BELOW_LINES,
get_cmm_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_BOTTOM_MARGIN]));
attribute_set = attribute_set_prepend(attribute_set, ATK_TEXT_ATTR_JUSTIFICATION,
get_justification_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_JUSTIFICATION]));
if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_paragraph_style )
atk_text_attribute_paragraph_style = atk_text_attribute_register("paragraph-style");
attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_paragraph_style,
get_string_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_STYLE_NAME]));
if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_line_height )
atk_text_attribute_line_height = atk_text_attribute_register("line-height");
attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_line_height,
get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_LINE_SPACING], LineSpacing2LineHeight));
if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_tab_interval )
atk_text_attribute_tab_interval = atk_text_attribute_register("tab-interval");
attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_tab_interval,
get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_TAB_STOPS], DefaultTabStops2String));
if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_tab_stops )
atk_text_attribute_tab_stops = atk_text_attribute_register("tab-stops");
attribute_set = attribute_set_prepend(attribute_set, atk_text_attribute_tab_stops,
get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_TAB_STOPS], TabStops2String));
// --> OD 2010-03-05 #i92233#
if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_mm_to_pixel_ratio )
atk_text_attribute_mm_to_pixel_ratio = atk_text_attribute_register("mm-to-pixel-ratio");
attribute_set = attribute_set_prepend( attribute_set, atk_text_attribute_mm_to_pixel_ratio,
get_value(rAttributeList, aIndexList[TEXT_ATTRIBUTE_MM_TO_PIXEL_RATIO], Float2String));
// <--
return attribute_set;
}
AtkAttributeSet* attribute_set_prepend_misspelled( AtkAttributeSet* attribute_set )
{
if( ATK_TEXT_ATTR_INVALID == atk_text_attribute_misspelled )
atk_text_attribute_misspelled = atk_text_attribute_register( "text-spelling" );
attribute_set = attribute_set_prepend( attribute_set, atk_text_attribute_misspelled,
g_strdup_printf( "misspelled" ) );
return attribute_set;
}
// --> OD 2010-03-01 #i92232#
AtkAttributeSet* attribute_set_prepend_tracked_change_insertion( AtkAttributeSet* attribute_set )
{
if ( ATK_TEXT_ATTR_INVALID == atk_text_attribute_tracked_change )
{
atk_text_attribute_tracked_change = atk_text_attribute_register( "text-tracked-change" );
}
attribute_set = attribute_set_prepend( attribute_set,
atk_text_attribute_tracked_change,
g_strdup_printf( "insertion" ) );
return attribute_set;
}
AtkAttributeSet* attribute_set_prepend_tracked_change_deletion( AtkAttributeSet* attribute_set )
{
if ( ATK_TEXT_ATTR_INVALID == atk_text_attribute_tracked_change )
{
atk_text_attribute_tracked_change = atk_text_attribute_register( "text-tracked-change" );
}
attribute_set = attribute_set_prepend( attribute_set,
atk_text_attribute_tracked_change,
g_strdup_printf( "deletion" ) );
return attribute_set;
}
AtkAttributeSet* attribute_set_prepend_tracked_change_formatchange( AtkAttributeSet* attribute_set )
{
if ( ATK_TEXT_ATTR_INVALID == atk_text_attribute_tracked_change )
{
atk_text_attribute_tracked_change = atk_text_attribute_register( "text-tracked-change" );
}
attribute_set = attribute_set_prepend( attribute_set,
atk_text_attribute_tracked_change,
g_strdup_printf( "attribute-change" ) );
return attribute_set;
}
// <--
/*****************************************************************************/
struct AtkTextAttrMapping
{
const char * name;
TextPropertyValueFunc toPropertyValue;
};
const AtkTextAttrMapping g_TextAttrMap[] =
{
{ "", InvalidValue }, // ATK_TEXT_ATTR_INVALID = 0
{ "ParaLeftMargin", UnitString2CMM }, // ATK_TEXT_ATTR_LEFT_MARGIN
{ "ParaRightMargin", UnitString2CMM }, // ATK_TEXT_ATTR_RIGHT_MARGIN
{ "ParaFirstLineIndent", UnitString2CMM }, // ATK_TEXT_ATTR_INDENT
{ "CharHidden", String2Bool }, // ATK_TEXT_ATTR_INVISIBLE
{ "", InvalidValue }, // ATK_TEXT_ATTR_EDITABLE
{ "ParaTopMargin", UnitString2CMM }, // ATK_TEXT_ATTR_PIXELS_ABOVE_LINES
{ "ParaBottomMargin", UnitString2CMM }, // ATK_TEXT_ATTR_PIXELS_BELOW_LINES
{ "", InvalidValue }, // ATK_TEXT_ATTR_PIXELS_INSIDE_WRAP
{ "", InvalidValue }, // ATK_TEXT_ATTR_BG_FULL_HEIGHT
{ "", InvalidValue }, // ATK_TEXT_ATTR_RISE
{ "CharUnderline", String2Underline }, // ATK_TEXT_ATTR_UNDERLINE
{ "CharStrikeout", String2Strikeout }, // ATK_TEXT_ATTR_STRIKETHROUGH
{ "CharHeight", String2Float }, // ATK_TEXT_ATTR_SIZE
{ "CharScaleWidth", String2Scale }, // ATK_TEXT_ATTR_SCALE
{ "CharWeight", String2Weight }, // ATK_TEXT_ATTR_WEIGHT
{ "CharLocale", String2Locale }, // ATK_TEXT_ATTR_LANGUAGE
{ "CharFontName", SetString }, // ATK_TEXT_ATTR_FAMILY_NAME
{ "CharBackColor", String2Color }, // ATK_TEXT_ATTR_BG_COLOR
{ "CharColor", String2Color }, // ATK_TEXT_ATTR_FG_COLOR
{ "", InvalidValue }, // ATK_TEXT_ATTR_BG_STIPPLE
{ "", InvalidValue }, // ATK_TEXT_ATTR_FG_STIPPLE
{ "", InvalidValue }, // ATK_TEXT_ATTR_WRAP_MODE
{ "", InvalidValue }, // ATK_TEXT_ATTR_DIRECTION
{ "ParaAdjust", Justification2Adjust }, // ATK_TEXT_ATTR_JUSTIFICATION
{ "", InvalidValue }, // ATK_TEXT_ATTR_STRETCH
{ "CharCaseMap", String2CaseMap }, // ATK_TEXT_ATTR_VARIANT
{ "CharPosture", Style2FontSlant } // ATK_TEXT_ATTR_STYLE
};
static const sal_Int32 g_TextAttrMapSize = sizeof( g_TextAttrMap ) / sizeof( AtkTextAttrMapping );
/*****************************************************************************/
bool
attribute_set_map_to_property_values(
AtkAttributeSet* attribute_set,
uno::Sequence< beans::PropertyValue >& rValueList )
{
// Ensure enough space ..
uno::Sequence< beans::PropertyValue > aAttributeList (g_TextAttrMapSize);
sal_Int32 nIndex = 0;
for( GSList * item = attribute_set; item != NULL; item = g_slist_next( item ) )
{
AtkAttribute* attribute = (AtkAttribute *) item;
AtkTextAttribute text_attr = atk_text_attribute_for_name( attribute->name );
if( text_attr < g_TextAttrMapSize )
{
if( g_TextAttrMap[text_attr].name[0] != '\0' )
{
if( ! g_TextAttrMap[text_attr].toPropertyValue( aAttributeList[nIndex].Value, attribute->value) )
return false;
aAttributeList[nIndex].Name = rtl::OUString::createFromAscii( g_TextAttrMap[text_attr].name );
aAttributeList[nIndex].State = beans::PropertyState_DIRECT_VALUE;
++nIndex;
}
}
else
{
// Unsupported text attribute
return false;
}
}
aAttributeList.realloc( nIndex );
rValueList = aAttributeList;
return true;
}