1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <vcl/accessibility/AccessibleTextAttributeHelper.hxx>
22 #include <com/sun/star/accessibility/AccessibleTextType.hpp>
23 #include <com/sun/star/accessibility/XAccessibleTextMarkup.hpp>
24 #include <com/sun/star/awt/FontSlant.hpp>
25 #include <com/sun/star/awt/FontStrikeout.hpp>
26 #include <com/sun/star/awt/FontUnderline.hpp>
27 #include <com/sun/star/awt/FontWeight.hpp>
28 #include <com/sun/star/style/ParagraphAdjust.hpp>
29 #include <com/sun/star/text/TextMarkupType.hpp>
30 #include <o3tl/any.hxx>
31 #include <tools/color.hxx>
35 OUString
lcl_ConvertCharEscapement(sal_Int16 nEscapement
)
45 OUString
lcl_ConverCharStrikeout(sal_Int16 nStrikeout
)
47 OUString sTextLineThroughStyle
;
48 OUString sTextLineThroughText
;
49 OUString sTextLineThroughType
;
50 OUString sTextLineThroughWidth
;
54 case css::awt::FontStrikeout::BOLD
:
55 sTextLineThroughType
= "single";
56 sTextLineThroughWidth
= "bold";
58 case css::awt::FontStrikeout::DONTKNOW
:
60 case css::awt::FontStrikeout::DOUBLE
:
61 sTextLineThroughType
= "double";
63 case css::awt::FontStrikeout::NONE
:
64 sTextLineThroughStyle
= "none";
66 case css::awt::FontStrikeout::SINGLE
:
67 sTextLineThroughType
= "single";
69 case css::awt::FontStrikeout::SLASH
:
70 sTextLineThroughText
= u
"/"_ustr
;
72 case css::awt::FontStrikeout::X
:
73 sTextLineThroughText
= u
"X"_ustr
;
76 assert(false && "Unhandled strikeout type");
80 if (!sTextLineThroughStyle
.isEmpty())
81 sResult
+= u
"text-line-through-style:"_ustr
+ sTextLineThroughStyle
+ ";";
82 if (!sTextLineThroughText
.isEmpty())
83 sResult
+= u
"text-line-through-text:"_ustr
+ sTextLineThroughText
+ ";";
84 if (!sTextLineThroughType
.isEmpty())
85 sResult
+= u
"text-line-through-type:"_ustr
+ sTextLineThroughType
+ ";";
86 if (!sTextLineThroughWidth
.isEmpty())
87 sResult
+= u
"text-line-through-width:"_ustr
+ sTextLineThroughWidth
+ ";";
92 OUString
lcl_convertFontWeight(double fontWeight
)
94 if (fontWeight
== css::awt::FontWeight::THIN
|| fontWeight
== css::awt::FontWeight::ULTRALIGHT
)
96 if (fontWeight
== css::awt::FontWeight::LIGHT
)
98 if (fontWeight
== css::awt::FontWeight::SEMILIGHT
)
100 if (fontWeight
== css::awt::FontWeight::NORMAL
)
102 if (fontWeight
== css::awt::FontWeight::SEMIBOLD
)
104 if (fontWeight
== css::awt::FontWeight::BOLD
)
106 if (fontWeight
== css::awt::FontWeight::ULTRABOLD
)
108 if (fontWeight
== css::awt::FontWeight::BLACK
)
111 // awt::FontWeight::DONTKNOW || fontWeight == awt::FontWeight::NORMAL
115 OUString
lcl_ConvertFontSlant(css::awt::FontSlant eFontSlant
)
119 case css::awt::FontSlant::FontSlant_NONE
:
121 case css::awt::FontSlant::FontSlant_OBLIQUE
:
122 case css::awt::FontSlant::FontSlant_REVERSE_OBLIQUE
:
124 case css::awt::FontSlant::FontSlant_ITALIC
:
125 case css::awt::FontSlant::FontSlant_REVERSE_ITALIC
:
127 case css::awt::FontSlant::FontSlant_DONTKNOW
:
128 case css::awt::FontSlant::FontSlant_MAKE_FIXED_SIZE
:
134 // s. https://wiki.linuxfoundation.org/accessibility/iaccessible2/textattributes
136 void lcl_ConvertFontUnderline(sal_Int16 nFontUnderline
, OUString
& rUnderlineStyle
,
137 OUString
& rUnderlineType
, OUString
& rUnderlineWidth
)
139 rUnderlineStyle
= u
""_ustr
;
140 rUnderlineType
= u
"single"_ustr
;
141 rUnderlineWidth
= u
"auto"_ustr
;
143 switch (nFontUnderline
)
145 case css::awt::FontUnderline::BOLD
:
146 rUnderlineWidth
= u
"bold"_ustr
;
148 case css::awt::FontUnderline::BOLDDASH
:
149 rUnderlineWidth
= u
"bold"_ustr
;
150 rUnderlineStyle
= u
"dash"_ustr
;
152 case css::awt::FontUnderline::BOLDDASHDOT
:
153 rUnderlineWidth
= u
"bold"_ustr
;
154 rUnderlineStyle
= u
"dot-dash"_ustr
;
156 case css::awt::FontUnderline::BOLDDASHDOTDOT
:
157 rUnderlineWidth
= u
"bold"_ustr
;
158 rUnderlineStyle
= u
"dot-dot-dash"_ustr
;
160 case css::awt::FontUnderline::BOLDDOTTED
:
161 rUnderlineWidth
= u
"bold"_ustr
;
162 rUnderlineStyle
= u
"dotted"_ustr
;
164 case css::awt::FontUnderline::BOLDLONGDASH
:
165 rUnderlineWidth
= u
"bold"_ustr
;
166 rUnderlineStyle
= u
"long-dash"_ustr
;
168 case css::awt::FontUnderline::BOLDWAVE
:
169 rUnderlineWidth
= u
"bold"_ustr
;
170 rUnderlineStyle
= u
"wave"_ustr
;
172 case css::awt::FontUnderline::DASH
:
173 rUnderlineStyle
= u
"dash"_ustr
;
175 case css::awt::FontUnderline::DASHDOT
:
176 rUnderlineStyle
= u
"dot-dash"_ustr
;
178 case css::awt::FontUnderline::DASHDOTDOT
:
179 rUnderlineStyle
= u
"dot-dot-dash"_ustr
;
181 case css::awt::FontUnderline::DONTKNOW
:
182 rUnderlineWidth
= u
""_ustr
;
183 rUnderlineStyle
= u
""_ustr
;
184 rUnderlineType
= u
""_ustr
;
186 case css::awt::FontUnderline::DOTTED
:
187 rUnderlineStyle
= u
"dotted"_ustr
;
189 case css::awt::FontUnderline::DOUBLE
:
190 rUnderlineType
= u
"double"_ustr
;
192 case css::awt::FontUnderline::DOUBLEWAVE
:
193 rUnderlineStyle
= u
"wave"_ustr
;
194 rUnderlineType
= u
"double"_ustr
;
196 case css::awt::FontUnderline::LONGDASH
:
197 rUnderlineStyle
= u
"long-dash"_ustr
;
199 case css::awt::FontUnderline::NONE
:
200 rUnderlineWidth
= u
"none"_ustr
;
201 rUnderlineStyle
= u
"none"_ustr
;
202 rUnderlineType
= u
"none"_ustr
;
204 case css::awt::FontUnderline::SINGLE
:
205 rUnderlineType
= u
"single"_ustr
;
207 case css::awt::FontUnderline::SMALLWAVE
:
208 case css::awt::FontUnderline::WAVE
:
209 rUnderlineStyle
= u
"wave"_ustr
;
212 assert(false && "Unhandled font underline type");
216 /** Converts Color to "rgb(r,g,b)" as specified in https://wiki.linuxfoundation.org/accessibility/iaccessible2/textattributes. */
217 OUString
lcl_ConvertColor(Color aColor
)
219 return u
"rgb(" + OUString::number(aColor
.GetRed()) + u
"\\,"
220 + OUString::number(aColor
.GetGreen()) + u
"\\," + OUString::number(aColor
.GetBlue())
224 OUString
lcl_ConvertParagraphAdjust(css::style::ParagraphAdjust eParaAdjust
)
228 case css::style::ParagraphAdjust_LEFT
:
230 case css::style::ParagraphAdjust_RIGHT
:
231 return u
"right"_ustr
;
232 case css::style::ParagraphAdjust_BLOCK
:
233 case css::style::ParagraphAdjust_STRETCH
:
234 return u
"justify"_ustr
;
235 case css::style::ParagraphAdjust_CENTER
:
236 return u
"center"_ustr
;
238 assert(false && "Unhandled ParagraphAdjust value");
244 static OUString
ConvertUnoToIAccessible2TextAttributes(
245 const css::uno::Sequence
<css::beans::PropertyValue
>& rUnoAttributes
,
246 IA2AttributeType eAttributeType
)
249 for (css::beans::PropertyValue
const& prop
: rUnoAttributes
)
254 if (eAttributeType
& IA2AttributeType::TextAttributes
)
256 if (prop
.Name
== "CharBackColor")
258 sAttribute
= "background-color";
259 sValue
= lcl_ConvertColor(
260 Color(ColorTransparency
, *o3tl::doAccess
<sal_Int32
>(prop
.Value
)));
262 else if (prop
.Name
== "CharColor")
264 sAttribute
= "color";
265 sValue
= lcl_ConvertColor(
266 Color(ColorTransparency
, *o3tl::doAccess
<sal_Int32
>(prop
.Value
)));
268 else if (prop
.Name
== "CharEscapement")
270 sAttribute
= "text-position";
271 const sal_Int16 nEscapement
= *o3tl::doAccess
<sal_Int16
>(prop
.Value
);
272 sValue
= lcl_ConvertCharEscapement(nEscapement
);
274 else if (prop
.Name
== "CharFontName")
276 sAttribute
= "font-family";
277 sValue
= *o3tl::doAccess
<OUString
>(prop
.Value
);
279 else if (prop
.Name
== "CharHeight")
281 sAttribute
= "font-size";
282 sValue
= OUString::number(*o3tl::doAccess
<double>(prop
.Value
)) + "pt";
284 else if (prop
.Name
== "CharPosture")
286 sAttribute
= "font-style";
287 const css::awt::FontSlant eFontSlant
288 = *o3tl::doAccess
<css::awt::FontSlant
>(prop
.Value
);
289 sValue
= lcl_ConvertFontSlant(eFontSlant
);
291 else if (prop
.Name
== "CharStrikeout")
293 const sal_Int16 nStrikeout
= *o3tl::doAccess
<sal_Int16
>(prop
.Value
);
294 aRet
+= lcl_ConverCharStrikeout(nStrikeout
);
296 else if (prop
.Name
== "CharUnderline")
298 OUString sUnderlineStyle
;
299 OUString sUnderlineType
;
300 OUString sUnderlineWidth
;
301 const sal_Int16 nUnderline
= *o3tl::doAccess
<sal_Int16
>(prop
.Value
);
302 lcl_ConvertFontUnderline(nUnderline
, sUnderlineStyle
, sUnderlineType
,
305 // leave 'sAttribute' and 'sName' empty, set all attributes here
306 if (!sUnderlineStyle
.isEmpty())
307 aRet
+= u
"text-underline-style:" + sUnderlineStyle
+ ";";
308 if (!sUnderlineType
.isEmpty())
309 aRet
+= u
"text-underline-type:" + sUnderlineType
+ ";";
310 if (!sUnderlineWidth
.isEmpty())
311 aRet
+= u
"text-underline-width:" + sUnderlineWidth
+ ";";
313 else if (prop
.Name
== "CharWeight")
315 sAttribute
= "font-weight";
316 sValue
= lcl_convertFontWeight(*o3tl::doAccess
<double>(prop
.Value
));
320 // so far, "ParaAdjust" is the only UNO text attribute that
321 // maps to an object attribute for IAccessible2 ("text-align")
322 if (sAttribute
.isEmpty() && (eAttributeType
& IA2AttributeType::ObjectAttributes
)
323 && prop
.Name
== "ParaAdjust")
325 sAttribute
= "text-align";
326 const css::style::ParagraphAdjust eParaAdjust
327 = static_cast<css::style::ParagraphAdjust
>(*o3tl::doAccess
<sal_Int16
>(prop
.Value
));
328 sValue
= lcl_ConvertParagraphAdjust(eParaAdjust
);
331 if (!sAttribute
.isEmpty() && !sValue
.isEmpty())
332 aRet
+= sAttribute
+ ":" + sValue
+ ";";
338 OUString
AccessibleTextAttributeHelper::GetIAccessible2TextAttributes(
339 const css::uno::Reference
<css::accessibility::XAccessibleText
>& xText
,
340 IA2AttributeType eAttributeType
, sal_Int32 nOffset
, sal_Int32
& rStartOffset
,
341 sal_Int32
& rEndOffset
)
345 const css::uno::Sequence
<css::beans::PropertyValue
> attribs
346 = xText
->getCharacterAttributes(nOffset
, css::uno::Sequence
<OUString
>());
347 OUString sAttributes
= ConvertUnoToIAccessible2TextAttributes(attribs
, eAttributeType
);
349 css::accessibility::TextSegment aAttributeRun
350 = xText
->getTextAtIndex(nOffset
, css::accessibility::AccessibleTextType::ATTRIBUTE_RUN
);
351 rStartOffset
= aAttributeRun
.SegmentStart
;
352 rEndOffset
= aAttributeRun
.SegmentEnd
;
354 // report spelling error as "invalid:spelling;" IA2 text attribute and
355 // adapt start/end index as necessary
356 css::uno::Reference
<css::accessibility::XAccessibleTextMarkup
> xTextMarkup(xText
,
357 css::uno::UNO_QUERY
);
358 if ((eAttributeType
& IA2AttributeType::TextAttributes
) && xTextMarkup
.is())
360 bool bInvalidSpelling
= false;
361 const sal_Int32
nMarkupCount(
362 xTextMarkup
->getTextMarkupCount(css::text::TextMarkupType::SPELLCHECK
));
363 for (sal_Int32 nMarkupIndex
= 0; nMarkupIndex
< nMarkupCount
; ++nMarkupIndex
)
365 const css::accessibility::TextSegment aTextSegment
366 = xTextMarkup
->getTextMarkup(nMarkupIndex
, css::text::TextMarkupType::SPELLCHECK
);
367 const sal_Int32 nStartOffsetTextMarkup
= aTextSegment
.SegmentStart
;
368 const sal_Int32 nEndOffsetTextMarkup
= aTextSegment
.SegmentEnd
;
369 if (nStartOffsetTextMarkup
<= nOffset
)
371 if (nOffset
< nEndOffsetTextMarkup
)
373 // offset is inside invalid spelling
374 rStartOffset
= ::std::max(rStartOffset
, nStartOffsetTextMarkup
);
375 rEndOffset
= ::std::min(rEndOffset
, nEndOffsetTextMarkup
);
376 bInvalidSpelling
= true;
381 rStartOffset
= ::std::max(rStartOffset
, nEndOffsetTextMarkup
);
386 rEndOffset
= ::std::min(rEndOffset
, nStartOffsetTextMarkup
);
390 if (bInvalidSpelling
)
391 sAttributes
+= u
"invalid:spelling;"_ustr
;
397 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */