Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/vcl/unx/gtk3/a11y/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 44 kB image not shown  

Quelle  atktextattributes.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   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 .
 */


#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 <i18nlangtag/languagetag.hxx>
#include <tools/UnitConversion.hxx>
#include <o3tl/safeint.hxx>
#include <o3tl/string_view.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;
// #i92232#
static AtkTextAttribute atk_text_attribute_tracked_change = ATK_TEXT_ATTR_INVALID;
// #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.
  */


namespace {

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,
    // #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
    // #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 nullptr;
}

#define get_bool_value( list, index ) get_value( list, index, Bool2String )
#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 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 <<= fval;
    return true;
}

/*****************************************************************************/

/// @throws uno::RuntimeException
static css::uno::Reference<css::accessibility::XAccessibleComponent>
    getComponent( AtkText *pText )
{
    AtkObjectWrapper *pWrap = ATK_OBJECT_WRAPPER( pText );
    if( pWrap )
    {
        if( !pWrap->mpComponent.is() )
        {
            pWrap->mpComponent.set(pWrap->mpContext, css::uno::UNO_QUERY);
        }

        return pWrap->mpComponent;
    }

    return css::uno::Reference<css::accessibility::XAccessibleComponent>();
}

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
        {
            css::uno::Reference<css::accessibility::XAccessibleComponent>
                pComponent = getComponent( text );
            if( pComponent.is() )
            {
                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&) {
            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 nullptr;
}

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 = static_cast<sal_Int32>(blue) | ( static_cast<sal_Int32>(green) << 8 ) | ( static_cast<sal_Int32>(red) << 16 );
    rAny <<= nColor;
    return true;
}

/*****************************************************************************/

static gchar*
FontSlant2Style(const uno::Any& rAny)
{
    const gchar * value = nullptr;

    awt::FontSlant aFontSlant;
    if(!(rAny >>= aFontSlant))
        return nullptr;

    switch( aFontSlant )
    {
        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 nullptr;
}

static bool
Style2FontSlant( uno::Any& rAny, const gchar * value )
{
    awt::FontSlant aFontSlant;

    if( strncmp( value, STRNCMP_PARAM( "normal" ) ) == 0 )
        aFontSlant = awt::FontSlant_NONE;
    else if( strncmp( value, STRNCMP_PARAM( "oblique" ) ) == 0 )
        aFontSlant = awt::FontSlant_OBLIQUE;
    else if( strncmp( value, STRNCMP_PARAM( "italic" ) ) == 0 )
        aFontSlant = awt::FontSlant_ITALIC;
    else if( strncmp( value, STRNCMP_PARAM( "reverse oblique" ) ) == 0 )
        aFontSlant = awt::FontSlant_REVERSE_OBLIQUE;
    else if( strncmp( value, STRNCMP_PARAM( "reverse italic" ) ) == 0 )
        aFontSlant = awt::FontSlant_REVERSE_ITALIC;
    else
        return false;

    rAny <<= 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 <<= weight / 4;
    return true;
}

/*****************************************************************************/

static gchar*
Adjust2Justification(const uno::Any& rAny)
{
    const gchar * value = nullptr;

    switchstatic_cast<style::ParagraphAdjust>(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 nullptr;
}

static bool
Justification2Adjust( uno::Any& rAny, const gchar * value )
{
    style::ParagraphAdjust nParagraphAdjust;

    if( strncmp( value, STRNCMP_PARAM( "left" ) ) == 0 )
        nParagraphAdjust = style::ParagraphAdjust_LEFT;
    else if( strncmp( value, STRNCMP_PARAM( "right" ) ) == 0 )
        nParagraphAdjust = style::ParagraphAdjust_RIGHT;
    else if( strncmp( value, STRNCMP_PARAM( "fill" ) ) == 0 )
        nParagraphAdjust = style::ParagraphAdjust_BLOCK;
    else if( strncmp( value, STRNCMP_PARAM( "center" ) ) == 0 )
        nParagraphAdjust = style::ParagraphAdjust_CENTER;
    else
        return false;

    rAny <<= static_cast<short>(nParagraphAdjust);
    return true;
}

/*****************************************************************************/

const gchar * const font_strikethrough[] = {
    "none",   // FontStrikeout::NONE
    "single"// FontStrikeout::SINGLE
    "double"// FontStrikeout::DOUBLE
    nullptr,     // FontStrikeout::DONTKNOW
    "bold",   // FontStrikeout::BOLD
    "with /"// FontStrikeout::SLASH
    "with X"  // FontStrikeout::X
};

static gchar*
Strikeout2String(const uno::Any& rAny)
{
    sal_Int16 n = rAny.get<sal_Int16>();

    if( n >= 0 && o3tl::make_unsigned(n) < SAL_N_ELEMENTS(font_strikethrough) )
        return g_strdup( font_strikethrough[n] );

    return nullptr;
}

static bool
String2Strikeout( uno::Any& rAny, const gchar * value )
{
    for( sal_Int16 n=0; n < sal_Int16(SAL_N_ELEMENTS(font_strikethrough)); ++n )
    {
        if( ( nullptr != font_strikethrough[n] ) &&
            0 == strncmp( value, font_strikethrough[n], strlen( font_strikethrough[n] ) ) )
        {
            rAny <<= n;
            return true;
        }
    }

    return false;
}

/*****************************************************************************/

static gchar*
Underline2String(const uno::Any& rAny)
{
    const gchar * value = nullptr;

    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 nullptr;
}

static bool
String2Underline( uno::Any& rAny, const gchar * value )
{
    short nUnderline;

    if( strncmp( value, STRNCMP_PARAM( "none" ) ) == 0 )
        nUnderline = awt::FontUnderline::NONE;
    else if( strncmp( value, STRNCMP_PARAM( "single" ) ) == 0 )
        nUnderline = awt::FontUnderline::SINGLE;
    else if( strncmp( value, STRNCMP_PARAM( "double" ) ) == 0 )
        nUnderline = awt::FontUnderline::DOUBLE;
    else
        return false;

    rAny <<= nUnderline;
    return true;
}

/*****************************************************************************/

static gchar*
GetString(const uno::Any& rAny)
{
    OString aFontName = OUStringToOString( rAny.get< OUString > (), RTL_TEXTENCODING_UTF8 );

    if( !aFontName.isEmpty() )
        return g_strdup( aFontName.getStr() );

    return nullptr;
}

static bool
SetString( uno::Any& rAny, const gchar * value )
{
    OString aFontName( value );

    if( !aFontName.isEmpty() )
    {
        rAny <<= 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: don't use double here because of warning on linux

    if( 1 != sscanf( value, "%gmm", &fValue ) )
        return false;

    fValue = fValue * 100;

    rAny <<= static_cast<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<bool>() )
        n = 0;

    return g_strdup( bool_values[n] );
}

static bool
String2Bool( uno::Any& rAny, const gchar * value )
{
    bool bValue;

    if( strncmp( value, STRNCMP_PARAM( "true" ) ) == 0 )
        bValue = true;
    else if( strncmp( value, STRNCMP_PARAM( "false" ) ) == 0 )
        bValue = false;
    else
        return false;

    rAny <<= bValue;
    return true;
}

/*****************************************************************************/

static gchar*
Scale2String( const uno::Any& rAny )
{
    return g_strdup_printf( "%g"static_cast<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 <<= static_cast<sal_Int16>(dval * 100);
    return true;
}

/*****************************************************************************/

static gchar *
CaseMap2String( const uno::Any& rAny )
{
    const gchar * value;

    switch( rAny.get<short>() )
    {
        case style::CaseMap::SMALLCAPS:
            value = "small_caps";
            break;

        default:
            value = "normal";
            break;
    }

    return g_strdup(value);
}

static bool
String2CaseMap( uno::Any& rAny, const gchar * value )
{
    short nCaseMap;

    if( strncmp( value, STRNCMP_PARAM( "normal" ) ) == 0 )
        nCaseMap = style::CaseMap::NONE;
    else if( strncmp( value, STRNCMP_PARAM( "small_caps" ) ) == 0 )
        nCaseMap = style::CaseMap::SMALLCAPS;
    else
        return false;

    rAny <<= nCaseMap;
    return true;
}

/*****************************************************************************/

const gchar * const 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)
{
    /* FIXME-BCP47: support language tags? And why is country lowercase? */
    lang::Locale aLocale = rAny.get<lang::Locale> ();
    LanguageTag aLanguageTag( aLocale);
    return g_strdup_printf( "%s-%s",
        OUStringToOString( aLanguageTag.getLanguage(), RTL_TEXTENCODING_ASCII_US).getStr(),
        OUStringToOString( aLanguageTag.getCountry(), RTL_TEXTENCODING_ASCII_US).toAsciiLowerCase().getStr() );
}

static bool
String2Locale( uno::Any& rAny, const gchar * value )
{
    /* FIXME-BCP47: support language tags? */
    bool ret = false;

    gchar ** str_array = g_strsplit_set( value, "-.@", -1 );
    if( str_array[0] != nullptr )
    {
        ret = true;

        lang::Locale aLocale;

        aLocale.Language = OUString::createFromAscii(str_array[0]);
        if( str_array[1] != nullptr )
        {
            gchar * country = g_ascii_strup(str_array[1], -1);
            aLocale.Country = OUString::createFromAscii(country);
            g_free(country);
        }

        rAny <<= 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" };
const gchar * const 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<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 nullptr;
}

/*****************************************************************************/

// @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] = { nullptr, nullptr, nullptr, nullptr };
    gint count = 0;

    // no property value found
    if( ( nBlinkIndex == -1 ) && (nUnderlineIndex == -1 ) && (nStrikeoutIndex == -1))
        return nullptr;

    if( nBlinkIndex != -1 )
    {
        if( rAttributeList[nBlinkIndex].Value.get<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<bool>() )
        n = 1;

    return g_strdup( shadow_values[n] );
}

/*****************************************************************************/

static gchar *
Short2Degree( const uno::Any& rAny )
{
    float f = rAny.get<sal_Int16>() / 10.0;
    return g_strdup_printf( "%g", f );
}

/*****************************************************************************/

const gchar * const 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 nullptr;
}

// @see http://www.w3.org/TR/2001/WD-css3-text-20010517/#PrimaryTextAdvanceDirection

const gchar * const 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 nullptr;
}

/*****************************************************************************/

const char * const 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 = nullptr;

    // 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 = nullptr;

    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", convertMm100ToPoint<double>(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 = nullptr;

    if( rAny >>= theTabStops)
    {
        sal_Unicode lastFillChar = ' ';

        for (const auto& rTabStop : theTabStops)
        {
            bool is_default_tab = (style::TabAlign_DEFAULT == rTabStop.Alignment);

            if( is_default_tab != default_tabs )
                continue;

            double fValue = rTabStop.Position;
            fValue = fValue * 0.01;

            const gchar * tab_align = "";
            switch( rTabStop.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( rTabStop.FillChar != lastFillChar )
            {
                lastFillChar = rTabStop.FillChar;
                switch (lastFillChar)
                {
                    case ' ':
                        lead_char = "blank ";
                        break;

                    case '.':
                        lead_char = "dotted ";
                        break;

                    case '-':
                        lead_char = "dashed ";
                        break;

                    case '_':
                        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, nullptr);
                g_free( tab_str );
                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" {

static int
attr_compare(const void *p1,const void *p2)
{
    const rtl_uString * pustr = static_cast<const rtl_uString *>(p1);
    const char * pc = *static_cast<const char * const *>(p2);

    return rtl_ustr_ascii_compare_WithLength(pustr->buffer, pustr->length, pc);
}

}

static void
find_exported_attributes( sal_Int32 *pArray,
    const css::uno::Sequence< css::beans::PropertyValue >& rAttributeList )
{
    for( sal_Int32 i = 0; i < rAttributeList.getLength(); i++ )
    {
        const char ** pAttr = static_cast<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 = static_cast<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 = nullptr;

    sal_Int32 aIndexList[TEXT_ATTRIBUTE_LAST] = { -1 };

    // Initialize index array with -1
    for(sal_Int32 & rn : aIndexList)
        rn = -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 ? nullptr : 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 ? nullptr : 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));

    // #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_new_from_extended_attributes(
    const css::uno::Reference< css::accessibility::XAccessibleExtendedAttributes >& rExtendedAttributes )
{
    AtkAttributeSet *pSet = nullptr;

    // extended attributes is a string of colon-separated pairs of property and value,
    // with pairs separated by semicolons. Example: "heading-level:2;weight:bold;"
    const OUString sExtendedAttrs = rExtendedAttributes->getExtendedAttributes();
    sal_Int32 nIndex = 0;
    do
    {
        OUString sProperty = sExtendedAttrs.getToken( 0, ';', nIndex );

        sal_Int32 nColonPos = 0;
        OString sPropertyName = OUStringToOString( o3tl::getToken(sProperty, 0, ':', nColonPos ),
                                                   RTL_TEXTENCODING_UTF8 );
        OString sPropertyValue = OUStringToOString( o3tl::getToken(sProperty, 0, ':', nColonPos ),
                                                    RTL_TEXTENCODING_UTF8 );

        pSet = attribute_set_prepend( pSet,
                                      atk_text_attribute_register( sPropertyName.getStr() ),
                                      g_strdup_printf( "%s", sPropertyValue.getStr() ) );
    }
    while ( nIndex >= 0 && nIndex < sExtendedAttrs.getLength() );

    return pSet;
}

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;
}

// #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;
}

/*****************************************************************************/

namespace {

struct AtkTextAttrMapping
{
    OUString          name;
    TextPropertyValueFunc toPropertyValue;
};

}

constexpr AtkTextAttrMapping g_TextAttrMap[]
{
    { u""_ustr, InvalidValue },                       // ATK_TEXT_ATTR_INVALID = 0
    { u"ParaLeftMargin"_ustr, UnitString2CMM },       // ATK_TEXT_ATTR_LEFT_MARGIN
    { u"ParaRightMargin"_ustr, UnitString2CMM },      // ATK_TEXT_ATTR_RIGHT_MARGIN
    { u"ParaFirstLineIndent"_ustr, UnitString2CMM },  // ATK_TEXT_ATTR_INDENT
    { u"CharHidden"_ustr, String2Bool },              // ATK_TEXT_ATTR_INVISIBLE
    { u""_ustr, InvalidValue },                       // ATK_TEXT_ATTR_EDITABLE
    { u"ParaTopMargin"_ustr, UnitString2CMM },        // ATK_TEXT_ATTR_PIXELS_ABOVE_LINES
    { u"ParaBottomMargin"_ustr, UnitString2CMM },     // ATK_TEXT_ATTR_PIXELS_BELOW_LINES
    { u""_ustr, InvalidValue },                       // ATK_TEXT_ATTR_PIXELS_INSIDE_WRAP
    { u""_ustr, InvalidValue },                       // ATK_TEXT_ATTR_BG_FULL_HEIGHT
    { u""_ustr, InvalidValue },                       // ATK_TEXT_ATTR_RISE
    { u"CharUnderline"_ustr, String2Underline },      // ATK_TEXT_ATTR_UNDERLINE
    { u"CharStrikeout"_ustr, String2Strikeout },      // ATK_TEXT_ATTR_STRIKETHROUGH
    { u"CharHeight"_ustr, String2Float },             // ATK_TEXT_ATTR_SIZE
    { u"CharScaleWidth"_ustr, String2Scale },         // ATK_TEXT_ATTR_SCALE
    { u"CharWeight"_ustr, String2Weight },            // ATK_TEXT_ATTR_WEIGHT
    { u"CharLocale"_ustr, String2Locale },            // ATK_TEXT_ATTR_LANGUAGE
    { u"CharFontName"_ustr,  SetString },             // ATK_TEXT_ATTR_FAMILY_NAME
    { u"CharBackColor"_ustr, String2Color },          // ATK_TEXT_ATTR_BG_COLOR
    { u"CharColor"_ustr, String2Color },              // ATK_TEXT_ATTR_FG_COLOR
    { u""_ustr, InvalidValue },                       // ATK_TEXT_ATTR_BG_STIPPLE
    { u""_ustr, InvalidValue },                       // ATK_TEXT_ATTR_FG_STIPPLE
    { u""_ustr, InvalidValue },                       // ATK_TEXT_ATTR_WRAP_MODE
    { u""_ustr, InvalidValue },                       // ATK_TEXT_ATTR_DIRECTION
    { u"ParaAdjust"_ustr, Justification2Adjust },     // ATK_TEXT_ATTR_JUSTIFICATION
    { u""_ustr, InvalidValue },                       // ATK_TEXT_ATTR_STRETCH
    { u"CharCaseMap"_ustr, String2CaseMap },          // ATK_TEXT_ATTR_VARIANT
    { u"CharPosture"_ustr, Style2FontSlant }          // ATK_TEXT_ATTR_STYLE
};

/*****************************************************************************/

bool
attribute_set_map_to_property_values(
    AtkAttributeSet* attribute_set,
    uno::Sequence< beans::PropertyValue >& rValueList )
{
    // Ensure enough space ..
    uno::Sequence< beans::PropertyValue > aAttributeList (SAL_N_ELEMENTS(g_TextAttrMap));
    auto pAttributeList = aAttributeList.getArray();

    sal_Int32 nIndex = 0;
    for( GSList * item = attribute_set; item != nullptr; item = g_slist_next( item ) )
    {
        AtkAttribute* attribute = reinterpret_cast<AtkAttribute *>(item);

        AtkTextAttribute text_attr = atk_text_attribute_for_name( attribute->name );
        if( text_attr < SAL_N_ELEMENTS(g_TextAttrMap) )
        {
            if( g_TextAttrMap[text_attr].name[0] != '\0' )
            {
                if( ! g_TextAttrMap[text_attr].toPropertyValue( pAttributeList[nIndex].Value, attribute->value) )
                    return false;

                pAttributeList[nIndex].Name = g_TextAttrMap[text_attr].name;
                pAttributeList[nIndex].State = beans::PropertyState_DIRECT_VALUE;
                ++nIndex;
            }
        }
        else
        {
            // Unsupported text attribute
            return false;
        }
    }

    aAttributeList.realloc( nIndex );
    rValueList = std::move(aAttributeList);
    return true;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

Messung V0.5
C=96 H=95 G=95

¤ Dauer der Verarbeitung: 0.26 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.