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