include/vcl/accessibility/AccessibleTextAttributeHelper.hxx |   32 ++
 vcl/qt5/QtAccessibleWidget.cxx                              |    6 
 vcl/source/accessibility/AccessibleTextAttributeHelper.cxx  |  158 +++++++-----
 winaccessibility/source/UAccCOM/AccTextBase.cxx             |    4 
 4 files changed, 135 insertions(+), 65 deletions(-)

New commits:
commit b0bf4043d320f69d1cf9dfcbcd6a8eac4df94015
Author:     Michael Weghorn <m.wegh...@posteo.de>
AuthorDate: Fri Oct 20 13:09:39 2023 +0200
Commit:     Michael Weghorn <m.wegh...@posteo.de>
CommitDate: Sat Oct 21 13:48:31 2023 +0200

    tdf#135922 a11y: Report IA2 "text-align" attr
    
    Map the "ParaAlign" UNO text attribute to the
    IAccessibl2 *object* attribute "text-align",
    as specified in the IAccessible2 object
    attributes specification. [1]
    
    This causes the corresponding "justification" AT-SPI text
    attribute to be reported when using the qt6 VCL
    plugin on Linux.
    
    For Windows, bridging this to the platform a11y
    will be implemented in an upcoming change.
    
    See the commmit message of the previous change
    
        Change-Id Ief7c840d3c5274714a914ca0e56df0c5eaffb06d
        tdf#135922 a11y: Prepare reporting text attrs as IA2 obj attrs
    
    for more details.
    
    [1] 
https://wiki.linuxfoundation.org/accessibility/iaccessible2/objectattributes
    
    Change-Id: I6a0478a4ac37f69393e995602ed698b5a78488e5
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/158256
    Tested-by: Jenkins
    Reviewed-by: Michael Weghorn <m.wegh...@posteo.de>

diff --git a/vcl/source/accessibility/AccessibleTextAttributeHelper.cxx 
b/vcl/source/accessibility/AccessibleTextAttributeHelper.cxx
index ebce733fa491..5647dc972ba5 100644
--- a/vcl/source/accessibility/AccessibleTextAttributeHelper.cxx
+++ b/vcl/source/accessibility/AccessibleTextAttributeHelper.cxx
@@ -25,6 +25,7 @@
 #include <com/sun/star/awt/FontStrikeout.hpp>
 #include <com/sun/star/awt/FontUnderline.hpp>
 #include <com/sun/star/awt/FontWeight.hpp>
+#include <com/sun/star/style/ParagraphAdjust.hpp>
 #include <com/sun/star/text/TextMarkupType.hpp>
 #include <o3tl/any.hxx>
 #include <tools/color.hxx>
@@ -219,6 +220,25 @@ OUString lcl_ConvertColor(Color aColor)
            + OUString::number(aColor.GetGreen()) + u"\\," + 
OUString::number(aColor.GetBlue())
            + u")";
 }
+
+OUString lcl_ConvertParagraphAdjust(css::style::ParagraphAdjust eParaAdjust)
+{
+    switch (eParaAdjust)
+    {
+        case css::style::ParagraphAdjust_LEFT:
+            return u"left"_ustr;
+        case css::style::ParagraphAdjust_RIGHT:
+            return u"right"_ustr;
+        case css::style::ParagraphAdjust_BLOCK:
+        case css::style::ParagraphAdjust_STRETCH:
+            return u"justify"_ustr;
+        case css::style::ParagraphAdjust_CENTER:
+            return u"center"_ustr;
+        default:
+            assert(false && "Unhandled ParagraphAdjust value");
+            return u""_ustr;
+    }
+}
 }
 
 OUString AccessibleTextAttributeHelper::ConvertUnoToIAccessible2TextAttributes(
@@ -297,6 +317,17 @@ OUString 
AccessibleTextAttributeHelper::ConvertUnoToIAccessible2TextAttributes(
             }
         }
 
+        // so far, "ParaAdjust" is the only UNO text attribute that
+        // maps to an object attribute for IAccessible2 ("text-align")
+        if (sAttribute.isEmpty() && (eAttributeType & 
IA2AttributeType::ObjectAttributes)
+            && prop.Name == "ParaAdjust")
+        {
+            sAttribute = "text-align";
+            const css::style::ParagraphAdjust eParaAdjust
+                = 
static_cast<css::style::ParagraphAdjust>(*o3tl::doAccess<sal_Int16>(prop.Value));
+            sValue = lcl_ConvertParagraphAdjust(eParaAdjust);
+        }
+
         if (!sAttribute.isEmpty() && !sValue.isEmpty())
             aRet += sAttribute + ":" + sValue + ";";
     }
commit 9362766b94a4627db0d8adfac4285388970d1fba
Author:     Michael Weghorn <m.wegh...@posteo.de>
AuthorDate: Fri Oct 20 12:41:15 2023 +0200
Commit:     Michael Weghorn <m.wegh...@posteo.de>
CommitDate: Sat Oct 21 13:48:22 2023 +0200

    tdf#135922 a11y: Prepare reporting text attrs as IA2 obj attrs
    
    According to the IAccessible2 specification, some
    of the attributes that LibreOffice handles as text
    attributes are mapped to IAccessible2 text attributes
    as well [1], but others should be reported as object
    attributes [2], e.g. text alignment
    is reported via the "text-align" object attribute on
    the paragraph object.
    
    So far, `AccessibleTextAttributeHelper` was only handling
    attributes that are mapped to IAccessible2 text attributes.
    
    Prepare for reporting object attributes as well, which
    will be required to report text alignment on Windows
    in a compliant way (s. tdf#135922).
    
    On the other hand, Qt also expects
    `QAccessibleTextInterface::attributes` to return
    text formatting using the attributes specified in the
    IAccessible2 attribute specifications and maps that to the
    platform-specific attributes (AT-SPI text attributes
    on Linux), but currently does not provide any way
    to report object attributes in addition to text
    attributes. It however supports e.g. the
    "text-align" attribute mentioned in the
    IAccessible2 object attribute specification
    when it's reported as a text attribute [3].
    
    Therefore, add a new `IA2AttributeType` enum
    that can be used to specify what kind of
    IAccessible2 attributes (text attributes,
    object attributes) to report.
    
    Only request IA2 text attributes on Windows
    when text attributes are requested, but
    both types for Qt.
    
    So far, support for none of the object attributes
    has been implemented, but an upcoming change
    will do that.
    
    [1] 
https://wiki.linuxfoundation.org/accessibility/iaccessible2/textattributes
    [2] 
https://wiki.linuxfoundation.org/accessibility/iaccessible2/objectattributes
    [3] 
https://code.qt.io/cgit/qt/qtbase.git/tree/src/gui/accessible/linux/atspiadaptor.cpp?id=546208f0ff23819d216cbb5bf0b5daded79b454e#n2193
    
    Change-Id: Ief7c840d3c5274714a914ca0e56df0c5eaffb06d
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/158255
    Tested-by: Jenkins
    Reviewed-by: Michael Weghorn <m.wegh...@posteo.de>

diff --git a/include/vcl/accessibility/AccessibleTextAttributeHelper.hxx 
b/include/vcl/accessibility/AccessibleTextAttributeHelper.hxx
index c5e02a04e85c..d0d49c3209b3 100644
--- a/include/vcl/accessibility/AccessibleTextAttributeHelper.hxx
+++ b/include/vcl/accessibility/AccessibleTextAttributeHelper.hxx
@@ -22,23 +22,49 @@
 #include <com/sun/star/accessibility/XAccessibleText.hpp>
 #include <com/sun/star/beans/PropertyValue.hdl>
 #include <com/sun/star/uno/Sequence.hxx>
+#include <o3tl/typed_flags_set.hxx>
 #include <rtl/ustring.hxx>
 #include <vcl/dllapi.h>
 
+/**
+ * According to the IAccessible2 specification, some of the attributes that 
LibreOffice
+ * handles as text attributes are mapped to IAccessible2 text attributes as 
well,
+ * but others should be reported as object attributes (e.g. text alignment is 
reported
+ * via the "text-align" object attribute on the paragraph object).
+ *
+ * https://wiki.linuxfoundation.org/accessibility/iaccessible2/textattributes
+ * https://wiki.linuxfoundation.org/accessibility/iaccessible2/objectattributes
+ *
+ * This enum class is used to specify the type(s) of attributes of interest.
+ */
+enum class IA2AttributeType
+{
+    None = 0x0000,
+    ObjectAttributes = 0x0001,
+    TextAttributes = 0x0002
+};
+
+template <> struct o3tl::typed_flags<IA2AttributeType> : 
is_typed_flags<IA2AttributeType, 0x003>
+{
+};
+
 class VCL_DLLPUBLIC AccessibleTextAttributeHelper
 {
 public:
     /** Converts UNO text attribute properties to a string holding
      *  the corresponding IAccessible2 text attributes.
      * @param rUnoAttributes A sequence holding the UNO text attributes.
+     * @param eAttributeType: Thy type(s) of attributes of interest.
      * @returns String holding the corresponding IAccessible2 text properties.
      */
     static OUString ConvertUnoToIAccessible2TextAttributes(
-        const css::uno::Sequence<css::beans::PropertyValue>& rUnoAttributes);
+        const css::uno::Sequence<css::beans::PropertyValue>& rUnoAttributes,
+        IA2AttributeType eAttributeType);
 
     /**
      * Get the IAccessible2 text attributes and the span of the attributes at 
the given index.
      * @param xText The interface to query for the information.
+     * @param eAttributeType: Thy type(s) of attributes of interest.
      * @param nOffset Character offset for which to retrieve the information.
      * @param rStartOffset Out param that is set to the start index of the 
attribute run.
      * @param rEndOffset Out param that is set to the end index of the 
attribute run.
@@ -46,8 +72,8 @@ public:
      */
     static OUString
     
GetIAccessible2TextAttributes(css::uno::Reference<css::accessibility::XAccessibleText>
 xText,
-                                  sal_Int32 nOffset, sal_Int32& rStartOffset,
-                                  sal_Int32& rEndOffset);
+                                  IA2AttributeType eAttributeType, sal_Int32 
nOffset,
+                                  sal_Int32& rStartOffset, sal_Int32& 
rEndOffset);
 };
 
 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s 
cinkeys+=0=break: */
diff --git a/vcl/qt5/QtAccessibleWidget.cxx b/vcl/qt5/QtAccessibleWidget.cxx
index 02a46147ce99..b1b8f4574c78 100644
--- a/vcl/qt5/QtAccessibleWidget.cxx
+++ b/vcl/qt5/QtAccessibleWidget.cxx
@@ -882,8 +882,12 @@ QString QtAccessibleWidget::attributes(int offset, int* 
startOffset, int* endOff
         return QString();
     }
 
+    // Qt doesn't have the strict separation into text and object attributes, 
but also
+    // suppports text-specific attributes that are object attributes according 
to the
+    // IAccessible2 spec.
     const OUString aRet = 
AccessibleTextAttributeHelper::GetIAccessible2TextAttributes(
-        xText, offset, *startOffset, *endOffset);
+        xText, IA2AttributeType::TextAttributes | 
IA2AttributeType::ObjectAttributes, offset,
+        *startOffset, *endOffset);
     return toQString(aRet);
 }
 
diff --git a/vcl/source/accessibility/AccessibleTextAttributeHelper.cxx 
b/vcl/source/accessibility/AccessibleTextAttributeHelper.cxx
index f69b7483d9e5..ebce733fa491 100644
--- a/vcl/source/accessibility/AccessibleTextAttributeHelper.cxx
+++ b/vcl/source/accessibility/AccessibleTextAttributeHelper.cxx
@@ -222,72 +222,79 @@ OUString lcl_ConvertColor(Color aColor)
 }
 
 OUString AccessibleTextAttributeHelper::ConvertUnoToIAccessible2TextAttributes(
-    const css::uno::Sequence<css::beans::PropertyValue>& rUnoAttributes)
+    const css::uno::Sequence<css::beans::PropertyValue>& rUnoAttributes,
+    IA2AttributeType eAttributeType)
 {
     OUString aRet;
     for (css::beans::PropertyValue const& prop : rUnoAttributes)
     {
         OUString sAttribute;
         OUString sValue;
-        if (prop.Name == "CharBackColor")
-        {
-            sAttribute = "background-color";
-            sValue = lcl_ConvertColor(
-                Color(ColorTransparency, 
*o3tl::doAccess<sal_Int32>(prop.Value)));
-        }
-        else if (prop.Name == "CharColor")
-        {
-            sAttribute = "color";
-            sValue = lcl_ConvertColor(
-                Color(ColorTransparency, 
*o3tl::doAccess<sal_Int32>(prop.Value)));
-        }
-        else if (prop.Name == "CharEscapement")
-        {
-            sAttribute = "text-position";
-            const sal_Int16 nEscapement = 
*o3tl::doAccess<sal_Int16>(prop.Value);
-            sValue = lcl_ConvertCharEscapement(nEscapement);
-        }
-        else if (prop.Name == "CharFontName")
-        {
-            sAttribute = "font-family";
-            sValue = *o3tl::doAccess<OUString>(prop.Value);
-        }
-        else if (prop.Name == "CharHeight")
-        {
-            sAttribute = "font-size";
-            sValue = OUString::number(*o3tl::doAccess<double>(prop.Value)) + 
"pt";
-        }
-        else if (prop.Name == "CharPosture")
-        {
-            sAttribute = "font-style";
-            const css::awt::FontSlant eFontSlant = 
*o3tl::doAccess<css::awt::FontSlant>(prop.Value);
-            sValue = lcl_ConvertFontSlant(eFontSlant);
-        }
-        else if (prop.Name == "CharStrikeout")
-        {
-            const sal_Int16 nStrikeout = 
*o3tl::doAccess<sal_Int16>(prop.Value);
-            aRet += lcl_ConverCharStrikeout(nStrikeout);
-        }
-        else if (prop.Name == "CharUnderline")
-        {
-            OUString sUnderlineStyle;
-            OUString sUnderlineType;
-            OUString sUnderlineWidth;
-            const sal_Int16 nUnderline = 
*o3tl::doAccess<sal_Int16>(prop.Value);
-            lcl_ConvertFontUnderline(nUnderline, sUnderlineStyle, 
sUnderlineType, sUnderlineWidth);
 
-            // leave 'sAttribute' and 'sName' empty, set all attributes here
-            if (!sUnderlineStyle.isEmpty())
-                aRet += u"text-underline-style:" + sUnderlineStyle + ";";
-            if (!sUnderlineType.isEmpty())
-                aRet += u"text-underline-type:" + sUnderlineType + ";";
-            if (!sUnderlineWidth.isEmpty())
-                aRet += u"text-underline-width:" + sUnderlineWidth + ";";
-        }
-        else if (prop.Name == "CharWeight")
+        if (eAttributeType & IA2AttributeType::TextAttributes)
         {
-            sAttribute = "font-weight";
-            sValue = 
lcl_convertFontWeight(*o3tl::doAccess<double>(prop.Value));
+            if (prop.Name == "CharBackColor")
+            {
+                sAttribute = "background-color";
+                sValue = lcl_ConvertColor(
+                    Color(ColorTransparency, 
*o3tl::doAccess<sal_Int32>(prop.Value)));
+            }
+            else if (prop.Name == "CharColor")
+            {
+                sAttribute = "color";
+                sValue = lcl_ConvertColor(
+                    Color(ColorTransparency, 
*o3tl::doAccess<sal_Int32>(prop.Value)));
+            }
+            else if (prop.Name == "CharEscapement")
+            {
+                sAttribute = "text-position";
+                const sal_Int16 nEscapement = 
*o3tl::doAccess<sal_Int16>(prop.Value);
+                sValue = lcl_ConvertCharEscapement(nEscapement);
+            }
+            else if (prop.Name == "CharFontName")
+            {
+                sAttribute = "font-family";
+                sValue = *o3tl::doAccess<OUString>(prop.Value);
+            }
+            else if (prop.Name == "CharHeight")
+            {
+                sAttribute = "font-size";
+                sValue = OUString::number(*o3tl::doAccess<double>(prop.Value)) 
+ "pt";
+            }
+            else if (prop.Name == "CharPosture")
+            {
+                sAttribute = "font-style";
+                const css::awt::FontSlant eFontSlant
+                    = *o3tl::doAccess<css::awt::FontSlant>(prop.Value);
+                sValue = lcl_ConvertFontSlant(eFontSlant);
+            }
+            else if (prop.Name == "CharStrikeout")
+            {
+                const sal_Int16 nStrikeout = 
*o3tl::doAccess<sal_Int16>(prop.Value);
+                aRet += lcl_ConverCharStrikeout(nStrikeout);
+            }
+            else if (prop.Name == "CharUnderline")
+            {
+                OUString sUnderlineStyle;
+                OUString sUnderlineType;
+                OUString sUnderlineWidth;
+                const sal_Int16 nUnderline = 
*o3tl::doAccess<sal_Int16>(prop.Value);
+                lcl_ConvertFontUnderline(nUnderline, sUnderlineStyle, 
sUnderlineType,
+                                         sUnderlineWidth);
+
+                // leave 'sAttribute' and 'sName' empty, set all attributes 
here
+                if (!sUnderlineStyle.isEmpty())
+                    aRet += u"text-underline-style:" + sUnderlineStyle + ";";
+                if (!sUnderlineType.isEmpty())
+                    aRet += u"text-underline-type:" + sUnderlineType + ";";
+                if (!sUnderlineWidth.isEmpty())
+                    aRet += u"text-underline-width:" + sUnderlineWidth + ";";
+            }
+            else if (prop.Name == "CharWeight")
+            {
+                sAttribute = "font-weight";
+                sValue = 
lcl_convertFontWeight(*o3tl::doAccess<double>(prop.Value));
+            }
         }
 
         if (!sAttribute.isEmpty() && !sValue.isEmpty())
@@ -298,14 +305,14 @@ OUString 
AccessibleTextAttributeHelper::ConvertUnoToIAccessible2TextAttributes(
 }
 
 OUString AccessibleTextAttributeHelper::GetIAccessible2TextAttributes(
-    css::uno::Reference<css::accessibility::XAccessibleText> xText, sal_Int32 
nOffset,
-    sal_Int32& rStartOffset, sal_Int32& rEndOffset)
+    css::uno::Reference<css::accessibility::XAccessibleText> xText, 
IA2AttributeType eAttributeType,
+    sal_Int32 nOffset, sal_Int32& rStartOffset, sal_Int32& rEndOffset)
 {
     assert(xText.is());
 
     const css::uno::Sequence<css::beans::PropertyValue> attribs
         = xText->getCharacterAttributes(nOffset, 
css::uno::Sequence<OUString>());
-    OUString sAttributes = ConvertUnoToIAccessible2TextAttributes(attribs);
+    OUString sAttributes = ConvertUnoToIAccessible2TextAttributes(attribs, 
eAttributeType);
 
     css::accessibility::TextSegment aAttributeRun
         = xText->getTextAtIndex(nOffset, 
css::accessibility::AccessibleTextType::ATTRIBUTE_RUN);
@@ -316,7 +323,7 @@ OUString 
AccessibleTextAttributeHelper::GetIAccessible2TextAttributes(
     // adapt start/end index as necessary
     css::uno::Reference<css::accessibility::XAccessibleTextMarkup> 
xTextMarkup(xText,
                                                                                
css::uno::UNO_QUERY);
-    if (xTextMarkup.is())
+    if ((eAttributeType & IA2AttributeType::TextAttributes) && 
xTextMarkup.is())
     {
         bool bInvalidSpelling = false;
         const sal_Int32 nMarkupCount(
diff --git a/winaccessibility/source/UAccCOM/AccTextBase.cxx 
b/winaccessibility/source/UAccCOM/AccTextBase.cxx
index 858504395a6f..a50cee9dd41c 100644
--- a/winaccessibility/source/UAccCOM/AccTextBase.cxx
+++ b/winaccessibility/source/UAccCOM/AccTextBase.cxx
@@ -142,7 +142,9 @@ COM_DECLSPEC_NOTHROW STDMETHODIMP 
CAccTextBase::get_attributes(long offset, long
         return E_FAIL;
 
 
-    const OUString sAttrs = 
AccessibleTextAttributeHelper::GetIAccessible2TextAttributes(pRXText, offset, 
*startOffset, *endOffset);
+    const OUString sAttrs = 
AccessibleTextAttributeHelper::GetIAccessible2TextAttributes(pRXText,
+                                                                               
          IA2AttributeType::TextAttributes,
+                                                                               
          offset, *startOffset, *endOffset);
 
     if(*textAttributes)
         SysFreeString(*textAttributes);

Reply via email to